diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000000..f7a5a06250ec --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,138 @@ +# Code Review Checklist + + + +## Purpose + + +## Ticket Number + + +## Requirements +- [ ] Have the requirements been met? +- [ ] Have stakeholder(s) approved the change? + +## Implementation +- [ ] Does this code change accomplish what it is supposed to do? +- [ ] Can this solution be simplified? +- [ ] Does this change add unwanted compile-time or run-time dependencies? +- [ ] Could an additional framework, API, library, or service improve the solution? +- [ ] Could we reuse part of LLVM instead of implementing the patch or a part of it? +- [ ] Is the code at the right abstraction level? +- [ ] Is the code modular enough? +- [ ] Can a better solution be found in terms of maintainability, readability, performance, or security? +- [ ] Does similar functionality already exist in the codebase? If yes, why isn’t it reused? +- [ ] Are there any best practices, design patterns or language-specific patterns that could substantially improve this code? + +## Logic Errors and Bugs +- [ ] Can you think of any use case in which the +code does not behave as intended? +- [ ] Can you think of any inputs or external events +that could break the code? + +## Error Handling and Logging +- [ ] Is error handling done the correct way? +- [ ] Should any logging or debugging information +be added or removed? +- [ ] Are error messages user-friendly? +- [ ] Are there enough log events and are they +written in a way that allows for easy +debugging? + +## Maintainability +- [ ] Is the code easy to read? +- [ ] Is the code not repeated (DRY Principle)? +- [ ] Is the code method/class not too long? + +## Dependencies +- [ ] Were updates to documentation, configuration, or readme files made as required by this change? +- [ ] Are there any potential impacts on other parts of the system or backward compatibility? + +## Security +- [ ] Does the code introduce any security vulnerabilities? + +## Performance +- [ ] Do you think this code change decreases +system performance? +- [ ] Do you see any potential to improve the +performance of the code significantly? + +## Testing and Testability +- [ ] Is the code testable? +- [ ] Have automated tests been added, or have related ones been updated to cover the change? + - [ ] For changes to mutable state +- [ ] Do tests reasonably cover the code change (unit/integration/system tests)? + - [ ] Line Coverage + - [ ] Region Coverage + - [ ] Branch Coverage +- [ ] Are there some test cases, input or edge cases +that should be tested in addition? + +## Readability +- [ ] Is the code easy to understand? +- [ ] Which parts were confusing to you and why? +- [ ] Can the readability of the code be improved by +smaller methods? +- [ ] Can the readability of the code be improved by +different function, method or variable names? +- [ ] Is the code located in the right +file/folder/package? +- [ ] Do you think certain methods should be +restructured to have a more intuitive control +flow? +- [ ] Is the data flow understandable? +- [ ] Are there redundant or outdated comments? +- [ ] Could some comments convey the message +better? +- [ ] Would more comments make the code more +understandable? +- [ ] Could some comments be removed by making the code itself more readable? +- [ ] Is there any commented-out code? +- [ ] Have you run a spelling and grammar checker? + +## Documentation +- [ ] Is there sufficient documentation? +- [ ] Is the ReadMe.md file up to date? + +## Best Practices +- [ ] Follow Single Responsibility principle? +- [ ] Are different errors handled correctly? +- [ ] Are errors and warnings logged? +- [ ] Magic values avoided? +- [ ] No unnecessary comments? +- [ ] Minimal nesting used? + +## Experts' Opinion +- [ ] Do you think a specific expert, like a security +expert or a usability expert, should look over +the code before it can be accepted? +- [ ] Will this code change impact different teams, and should they review the change as well? + + diff --git a/.github/lnt-benchmarks.yml b/.github/lnt-benchmarks.yml new file mode 100644 index 000000000000..8d28caa4767e --- /dev/null +++ b/.github/lnt-benchmarks.yml @@ -0,0 +1,25 @@ +name: LNT benchmarks + +on: + push: + branches: + - main + workflow_dispatch: + pull_request: + +concurrency: + group: ${{ github.repository_id }}-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + + lnt-benchmarks: + # Run only for matter-labs pull requests, ignore for forks + if: ${{ github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'push' }} + uses: matter-labs/era-compiler-ci/.github/workflows/lnt.yml@v1 + secrets: inherit + with: + compiler_llvm_branch: ${{ github.head_ref }} + ccache-key: 'llvm-Linux-X64-gnu' + compiler-llvm-repo: ${{ github.event.pull_request.head.repo.full_name }} + use-dev-machine: ${{ github.event_name != 'push' }} diff --git a/.github/workflows/README.md b/.github/workflows/README.md deleted file mode 100644 index ce34d2337e9c..000000000000 --- a/.github/workflows/README.md +++ /dev/null @@ -1 +0,0 @@ -Github action workflows should be stored in this directory. diff --git a/.github/workflows/benchmarks-integration-tests.yml b/.github/workflows/benchmarks-integration-tests.yml new file mode 100644 index 000000000000..c620354bfe77 --- /dev/null +++ b/.github/workflows/benchmarks-integration-tests.yml @@ -0,0 +1,142 @@ +name: Tests + +on: + pull_request: + workflow_dispatch: + inputs: + compiler_tester_reference_rev: + description: "compiler-tester revision to use as a benchmark reference" + required: true + default: "main" + compiler_tester_candidate_rev: + description: "compiler-tester revision to use as a benchmark candidate. Defaults to `main` branch if the `era-compiler-llvm-test` tag doesn't exist" + required: true + default: "era-compiler-llvm-test" + compiler_llvm_reference_branch: + description: "compiler-llvm branch to use as a benchmark reference" + required: true + default: "main" + compiler_llvm_candidate_branch: + description: "compiler-llvm branch to use as a benchmark candidate" + required: true + default: "main" + compiler_llvm_benchmark_mode: + description: "Mode filter for compiler-llvm benchmarks" + required: false + default: "^M^B3" + compiler_llvm_benchmark_path: + description: "Path filter for compiler-llvm benchmarks" + required: false + default: "" + +concurrency: + group: ${{ github.repository_id }}-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + + # Check for secrets leak in the repository + secrets-scanner: + uses: matter-labs/era-compiler-ci/.github/workflows/secrets-scanner.yaml@v1 + secrets: inherit + + compiler-tester-ref: + runs-on: ubuntu-latest + outputs: + reference-ref: ${{ steps.compiler_tester_ref.outputs.reference-ref }} + candidate-ref: ${{ steps.compiler_tester_ref.outputs.candidate-ref }} + env: + ERA_COMPILER_LLVM_TEST_TAG: era-compiler-llvm-test + ERA_COMPILER_LLVM_REF_DEFAULT: main + steps: + + - name: Checkout + uses: actions/checkout@v4 + with: + repository: matter-labs/era-compiler-tester + + - name: Define compiler tester ref + id: compiler_tester_ref + shell: bash + run: | + REFERENCE_REF=${{ inputs.compiler_tester_reference_rev || env.ERA_COMPILER_LLVM_REF_DEFAULT }} + if [ -n "$(git ls-remote --tags --heads --refs origin ${REFERENCE_REF})" ]; then + echo "reference-ref=${REFERENCE_REF}" | tee -a "${GITHUB_OUTPUT}" + else + echo "reference-ref=${{ env.ERA_COMPILER_LLVM_REF_DEFAULT }}" | tee -a "${GITHUB_OUTPUT}" + fi + CANDIDATE_REF=${{ inputs.compiler_tester_candidate_rev || env.ERA_COMPILER_LLVM_TEST_TAG }} + if [ -n "$(git ls-remote --tags --heads --refs origin ${CANDIDATE_REF})" ]; then + echo "candidate-ref=${CANDIDATE_REF}" | tee -a "${GITHUB_OUTPUT}" + else + echo "candidate-ref=${{ env.ERA_COMPILER_LLVM_REF_DEFAULT }}" | tee -a "${GITHUB_OUTPUT}" + fi + + target-machine: + runs-on: ubuntu-latest + outputs: + evm: ${{ steps.evm.outputs.machine || steps.default.outputs.evm }} + eravm: ${{ steps.eravm.outputs.machine || steps.default.outputs.eravm }} + steps: + + - name: Check for EraVM target + id: eravm + if: contains(github.event.pull_request.title, '[eravm]') || contains(github.event.pull_request.title, '[EraVM]') + run: echo "machine=eravm" | tee -a "${GITHUB_OUTPUT}" + + - name: Check for EVM target + id: evm + if: contains(github.event.pull_request.title, '[evm]') || contains(github.event.pull_request.title, '[EVM]') + run: echo "machine=evm" | tee -a "${GITHUB_OUTPUT}" + + - name: Check for default target + id: default + shell: bash -ex {0} + run: | + if [[ "${{ join(steps.*.outputs.*) }}" == "" ]]; then + echo "eravm=eravm" | tee -a "${GITHUB_OUTPUT}" + echo "evm=evm" | tee -a "${GITHUB_OUTPUT}" + fi + + # Benchmarks workflow call from the era-compiler-ci repository + # This is a common part of the benchmarks workflow for all repositories + # If you would like to make a change to the benchmarks workflow, please do it in the era-compiler-ci repository + benchmarks: + needs: [compiler-tester-ref, target-machine] + uses: matter-labs/era-compiler-ci/.github/workflows/benchmarks.yml@v1 + secrets: inherit + strategy: + fail-fast: false + matrix: + target: '${{ needs.target-machine.outputs.* }}' + toolchain: [ 'ir-llvm' ] + with: + compiler_tester_reference_branch: ${{ needs.compiler-tester-ref.outputs.reference-ref }} + compiler_tester_candidate_branch: ${{ needs.compiler-tester-ref.outputs.candidate-ref }} + compiler_llvm_reference_branch: ${{ github.event.inputs.compiler_llvm_reference_branch || github.event.repository.default_branch }} + compiler_llvm_candidate_branch: ${{ github.event.inputs.compiler_llvm_candidate_branch || github.head_ref }} + compiler_llvm_benchmark_mode: ${{ github.event.inputs.compiler_llvm_benchmark_mode || '^M^B3' }} + compiler_llvm_benchmark_path: ${{ github.event.inputs.compiler_llvm_benchmark_path || '' }} + compiler-llvm-repo: ${{ github.event.pull_request.head.repo.full_name }} # required to properly test forks + target-machine: ${{ matrix.target }} + toolchain: ${{ matrix.toolchain }} + environment: ${{ matrix.target == 'eravm' && 'zk_evm' || 'EVMInterpreter' }} + ccache-key: 'llvm-Linux-X64-gnu' + + # Integration tests workflow call from the era-compiler-ci repository + # This is a common part of the integration tests workflow for all repositories + # If you would like to make a change to the integration tests workflow, please do it in the era-compiler-ci repository + integration-tests: + needs: [compiler-tester-ref, target-machine] + uses: matter-labs/era-compiler-ci/.github/workflows/integration-tests.yaml@v1 + secrets: inherit + strategy: + fail-fast: false + matrix: + target: ${{ needs.target-machine.outputs.* }} + with: + compiler-tester-ref: ${{ needs.compiler-tester-ref.outputs.candidate-ref }} + llvm-ref: ${{ github.event.inputs.compiler_llvm_candidate_branch || github.head_ref || github.event.repository.default_branch }} + compiler-llvm-repo: ${{ github.event.pull_request.head.repo.full_name }} # required to properly test forks + target-machine: ${{ matrix.target }} + ccache-key: 'llvm-Linux-X64-gnu' diff --git a/.github/workflows/build-ci-container.yml b/.github/workflows/build-ci-container.yml deleted file mode 100644 index 28fc7de2ee06..000000000000 --- a/.github/workflows/build-ci-container.yml +++ /dev/null @@ -1,112 +0,0 @@ -name: Build CI Container - -permissions: - contents: read - -on: - push: - branches: - - main - paths: - - .github/workflows/build-ci-container.yml - - '.github/workflows/containers/github-action-ci/**' - pull_request: - branches: - - main - paths: - - .github/workflows/build-ci-container.yml - - '.github/workflows/containers/github-action-ci/**' - -jobs: - # TODO(boomanaiden154): Switch this back to a single stage build when we can - # run this on the self-hosted runners and don't have to do it this way to - # avoid timeouts. - build-ci-container-stage1: - if: github.repository_owner == 'llvm' - runs-on: ubuntu-latest - steps: - - name: Checkout LLVM - uses: actions/checkout@v4 - with: - sparse-checkout: .github/workflows/containers/github-action-ci/ - - name: Change podman Root Direcotry - run: | - mkdir -p ~/.config/containers - sudo mkdir -p /mnt/podman - sudo chown `whoami`:`whoami` /mnt/podman - cp ./.github/workflows/containers/github-action-ci/storage.conf ~/.config/containers/storage.conf - podman info - - name: Build container stage1 - working-directory: ./.github/workflows/containers/github-action-ci/ - run: | - podman build -t stage1-toolchain --target stage1-toolchain -f stage1.Dockerfile . - - name: Save container image - run: | - podman save stage1-toolchain > stage1-toolchain.tar - - name: Upload container image - uses: actions/upload-artifact@v4 - with: - name: stage1-toolchain - path: stage1-toolchain.tar - retention-days: 1 - build-ci-container-stage2: - if: github.repository_owner == 'llvm' - runs-on: ubuntu-latest - needs: build-ci-container-stage1 - permissions: - packages: write - steps: - - name: Write Variables - id: vars - run: | - tag=`date +%s` - container_name="ghcr.io/$GITHUB_REPOSITORY_OWNER/ci-ubuntu-22.04" - echo "container-name=$container_name" >> $GITHUB_OUTPUT - echo "container-name-tag=$container_name:$tag" >> $GITHUB_OUTPUT - - - name: Checkout LLVM - uses: actions/checkout@v4 - with: - sparse-checkout: .github/workflows/containers/github-action-ci/ - - - name: Change podman Root Direcotry - run: | - mkdir -p ~/.config/containers - sudo mkdir -p /mnt/podman - sudo chown `whoami`:`whoami` /mnt/podman - cp ./.github/workflows/containers/github-action-ci/storage.conf ~/.config/containers/storage.conf - podman info - - # Download the container image into /mnt/podman rather than - # $GITHUB_WORKSPACE to avoid space limitations on the default drive - # and use the permissions setup for /mnt/podman. - - name: Download stage1-toolchain - uses: actions/download-artifact@v4 - with: - name: stage1-toolchain - path: /mnt/podman - - - name: Load stage1-toolchain - run: | - podman load -i /mnt/podman/stage1-toolchain.tar - - - name: Build Container - working-directory: ./.github/workflows/containers/github-action-ci/ - run: | - podman build -t ${{ steps.vars.outputs.container-name-tag }} -f stage2.Dockerfile . - podman tag ${{ steps.vars.outputs.container-name-tag }} ${{ steps.vars.outputs.container-name }}:latest - - - name: Test Container - run: | - for image in ${{ steps.vars.outputs.container-name-tag }} ${{ steps.vars.outputs.container-name }}; do - podman run --rm -it $image /usr/bin/bash -x -c 'printf '\''#include \nint main(int argc, char **argv) { std::cout << "Hello\\n"; }'\'' | clang++ -x c++ - && ./a.out | grep Hello' - done - - - name: Push Container - if: github.event_name == 'push' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - podman login -u ${{ github.actor }} -p $GITHUB_TOKEN ghcr.io - podman push ${{ steps.vars.outputs.container-name-tag }} - podman push ${{ steps.vars.outputs.container-name }}:latest diff --git a/.github/workflows/cache-regen.yml b/.github/workflows/cache-regen.yml new file mode 100644 index 000000000000..360ba41b8c49 --- /dev/null +++ b/.github/workflows/cache-regen.yml @@ -0,0 +1,68 @@ +name: Cache regen + +on: + workflow_dispatch: + schedule: + - cron: '0 20 * * *' # each day at 8 PM GMT + +# Cancel the workflow if any new changes pushed to a feature branch or the trunk +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + + cleanup-ccache: + runs-on: ubuntu-latest + steps: + - name: Checkout source + uses: actions/checkout@v4 + + - name: Clean up cache + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh cache list + gh cache delete --all || true + + regen-ccache: + needs: cleanup-ccache + strategy: + fail-fast: false # finalize testing of all targets even if one failed + matrix: + include: + - name: "Linux x86 gnu" + runner: matterlabs-ci-runner-high-performance + image: ghcr.io/matter-labs/zksync-llvm-runner:latest + target: "x86_64-unknown-linux-gnu" + runs-on: ${{ matrix.runner }} + container: + image: ${{ matrix.image || '' }} # Special workaround to allow matrix builds with optional container + name: ${{ matrix.name }} + steps: + + - name: Checkout + uses: actions/checkout@v4 + with: + path: "llvm" + + - name: Build LLVM for tests + uses: matter-labs/era-compiler-ci/.github/actions/build-llvm@v1 + with: + target-env: ${{ contains(matrix.target, 'musl') && 'musl' || 'gnu' }} + build-type: RelWithDebInfo + save-ccache: 'false' + enable-tests: 'true' + enable-assertions: 'true' + clone-llvm: 'false' + ccache-key: ${{ format('llvm-{0}-{1}-{2}', runner.os, runner.arch, 'gnu') }} + + - name: Build LLVM for benchmarks + uses: matter-labs/era-compiler-ci/.github/actions/build-llvm@v1 + with: + target-env: ${{ contains(matrix.target, 'musl') && 'musl' || 'gnu' }} + enable-assertions: 'false' + build-type: Release + save-ccache: 'true' + clone-llvm: 'false' + ccache-key: ${{ format('llvm-{0}-{1}-{2}', runner.os, runner.arch, 'gnu') }} diff --git a/.github/workflows/ci-post-commit-analyzer-run.py b/.github/workflows/ci-post-commit-analyzer-run.py deleted file mode 100644 index e5f52d3b2fa6..000000000000 --- a/.github/workflows/ci-post-commit-analyzer-run.py +++ /dev/null @@ -1,34 +0,0 @@ -import json -import multiprocessing -import os -import re -import subprocess -import sys - - -def run_analyzer(data): - os.chdir(data["directory"]) - command = ( - data["command"] - + f" --analyze --analyzer-output html -o analyzer-results -Xclang -analyzer-config -Xclang max-nodes=75000" - ) - print(command) - subprocess.run(command, shell=True, check=True) - - -def pool_error(e): - print("Error analyzing file:", e) - - -def main(): - db_path = sys.argv[1] - database = json.load(open(db_path)) - - with multiprocessing.Pool() as pool: - pool.map_async(run_analyzer, [k for k in database], error_callback=pool_error) - pool.close() - pool.join() - - -if __name__ == "__main__": - main() diff --git a/.github/workflows/ci-post-commit-analyzer.yml b/.github/workflows/ci-post-commit-analyzer.yml deleted file mode 100644 index d614dd07b3a4..000000000000 --- a/.github/workflows/ci-post-commit-analyzer.yml +++ /dev/null @@ -1,95 +0,0 @@ -name: Post-Commit Static Analyzer - -permissions: - contents: read - -on: - push: - branches: - - 'release/**' - paths: - - 'clang/**' - - 'llvm/**' - - '.github/workflows/ci-post-commit-analyzer.yml' - pull_request: - types: - - opened - - synchronize - - reopened - - closed - paths: - - '.github/workflows/ci-post-commit-analyzer.yml' - - '.github/workflows/ci-post-commit-analyzer-run.py' - schedule: - - cron: '30 0 * * *' - -concurrency: - group: >- - llvm-project-${{ github.workflow }}-${{ github.event_name == 'pull_request' && - ( github.event.pull_request.number || github.ref) }} - cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} - -jobs: - post-commit-analyzer: - if: >- - github.repository_owner == 'llvm' && - github.event.action != 'closed' - runs-on: ubuntu-22.04 - container: - image: 'ghcr.io/llvm/ci-ubuntu-22.04:latest' - env: - LLVM_VERSION: 18 - steps: - - name: Checkout Source - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - - name: Setup ccache - uses: hendrikmuhs/ccache-action@v1 - with: - # A full build of llvm, clang, lld, and lldb takes about 250MB - # of ccache space. There's not much reason to have more than this, - # because we usually won't need to save cache entries from older - # builds. Also, there is an overall 10GB cache limit, and each - # run creates a new cache entry so we want to ensure that we have - # enough cache space for all the tests to run at once and still - # fit under the 10 GB limit. - # Default to 2G to workaround: https://github.com/hendrikmuhs/ccache-action/issues/174 - max-size: 2G - key: post-commit-analyzer - variant: sccache - - - name: Configure - run: | - cmake -B build -S llvm -G Ninja \ - -DLLVM_ENABLE_ASSERTIONS=ON \ - -DLLVM_ENABLE_PROJECTS=clang \ - -DLLVM_BUILD_LLVM_DYLIB=ON \ - -DLLVM_LINK_LLVM_DYLIB=ON \ - -DCMAKE_CXX_COMPILER=clang++ \ - -DCMAKE_C_COMPILER=clang \ - -DCMAKE_CXX_COMPILER_LAUNCHER=sccache \ - -DCMAKE_C_COMPILER_LAUNCHER=sccache \ - -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ - -DLLVM_INCLUDE_TESTS=OFF \ - -DCLANG_INCLUDE_TESTS=OFF \ - -DCMAKE_BUILD_TYPE=Release - - - name: Build - run: | - # FIXME: We need to build all the generated header files in order to be able to run - # the analyzer on every file. Building libLLVM and libclang is probably overkill for - # this, but it's better than building every target. - ninja -v -C build libLLVM.so libclang.so - - # Run the analyzer. - python3 .github/workflows/ci-post-commit-analyzer-run.py build/compile_commands.json - - scan-build --generate-index-only build/analyzer-results - - - name: Upload Results - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 - if: always() - with: - name: analyzer-results - path: 'build/analyzer-results/*' - diff --git a/.github/workflows/clang-tests.yml b/.github/workflows/clang-tests.yml deleted file mode 100644 index 2569ce19518e..000000000000 --- a/.github/workflows/clang-tests.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: Clang Tests - -permissions: - contents: read - -on: - workflow_dispatch: - push: - branches: - - 'release/**' - paths: - - 'clang/**' - - '.github/workflows/clang-tests.yml' - - '.github/workflows/llvm-project-tests.yml' - - '!llvm/**' - pull_request: - branches: - - 'release/**' - paths: - - 'clang/**' - - '.github/workflows/clang-tests.yml' - - '.github/workflows/llvm-project-tests.yml' - - '!llvm/**' - -concurrency: - # Skip intermediate builds: always. - # Cancel intermediate builds: only if it is a pull request build. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} - -jobs: - check_clang: - if: github.repository_owner == 'llvm' - name: Test clang,lldb,libclc - uses: ./.github/workflows/llvm-project-tests.yml - with: - build_target: check-clang - projects: clang;lldb;libclc diff --git a/.github/workflows/containers/github-action-ci/bootstrap.patch b/.github/workflows/containers/github-action-ci/bootstrap.patch deleted file mode 100644 index 55631c54a396..000000000000 --- a/.github/workflows/containers/github-action-ci/bootstrap.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/clang/cmake/caches/BOLT-PGO.cmake b/clang/cmake/caches/BOLT-PGO.cmake -index 1a04ca9a74e5..d092820e4115 100644 ---- a/clang/cmake/caches/BOLT-PGO.cmake -+++ b/clang/cmake/caches/BOLT-PGO.cmake -@@ -4,6 +4,8 @@ set(CLANG_BOOTSTRAP_TARGETS - stage2-clang-bolt - stage2-distribution - stage2-install-distribution -+ clang -+ lld - CACHE STRING "") - set(BOOTSTRAP_CLANG_BOOTSTRAP_TARGETS - clang-bolt diff --git a/.github/workflows/containers/github-action-ci/stage1.Dockerfile b/.github/workflows/containers/github-action-ci/stage1.Dockerfile deleted file mode 100644 index 8c6bcf463841..000000000000 --- a/.github/workflows/containers/github-action-ci/stage1.Dockerfile +++ /dev/null @@ -1,44 +0,0 @@ -FROM docker.io/library/ubuntu:22.04 as base -ENV LLVM_SYSROOT=/opt/llvm - -FROM base as stage1-toolchain -ENV LLVM_VERSION=17.0.6 - -RUN apt-get update && \ - apt-get install -y \ - wget \ - gcc \ - g++ \ - cmake \ - ninja-build \ - python3 \ - git \ - curl - -RUN curl -O -L https://github.com/llvm/llvm-project/archive/refs/tags/llvmorg-$LLVM_VERSION.tar.gz && tar -xf llvmorg-$LLVM_VERSION.tar.gz - -WORKDIR /llvm-project-llvmorg-$LLVM_VERSION - -COPY bootstrap.patch / - -# TODO(boomanaiden154): Remove the patch pulled from a LLVM PR once we bump -# the toolchain to version 18 and the patch is in-tree. -# TODO(boomanaiden154): Remove the bootstrap patch once we unsplit the build -# and no longer need to explicitly build the stage2 dependencies. -RUN curl https://github.com/llvm/llvm-project/commit/dd0356d741aefa25ece973d6cc4b55dcb73b84b4.patch | patch -p1 && cat /bootstrap.patch | patch -p1 - -RUN mkdir build - -RUN cmake -B ./build -G Ninja ./llvm \ - -C ./clang/cmake/caches/BOLT-PGO.cmake \ - -DBOOTSTRAP_LLVM_ENABLE_LLD=ON \ - -DBOOTSTRAP_BOOTSTRAP_LLVM_ENABLE_LLD=ON \ - -DPGO_INSTRUMENT_LTO=Thin \ - -DLLVM_ENABLE_RUNTIMES="compiler-rt" \ - -DCMAKE_INSTALL_PREFIX="$LLVM_SYSROOT" \ - -DLLVM_ENABLE_PROJECTS="bolt;clang;lld;clang-tools-extra" \ - -DLLVM_DISTRIBUTION_COMPONENTS="lld;compiler-rt;clang-format;scan-build" \ - -DCLANG_DEFAULT_LINKER="lld" \ - -DBOOTSTRAP_CLANG_PGO_TRAINING_DATA_SOURCE_DIR=/llvm-project-llvmorg-$LLVM_VERSION/llvm - -RUN ninja -C ./build stage2-instrumented-clang stage2-instrumented-lld diff --git a/.github/workflows/containers/github-action-ci/stage2.Dockerfile b/.github/workflows/containers/github-action-ci/stage2.Dockerfile deleted file mode 100644 index 0ca0da87734c..000000000000 --- a/.github/workflows/containers/github-action-ci/stage2.Dockerfile +++ /dev/null @@ -1,29 +0,0 @@ -FROM docker.io/library/ubuntu:22.04 as base -ENV LLVM_SYSROOT=/opt/llvm - -FROM stage1-toolchain AS stage2-toolchain - -RUN ninja -C ./build stage2-clang-bolt stage2-install-distribution && ninja -C ./build install-distribution && rm -rf ./build - -FROM base - -COPY --from=stage2-toolchain $LLVM_SYSROOT $LLVM_SYSROOT - -# Need to install curl for hendrikmuhs/ccache-action -# Need nodejs for some of the GitHub actions. -# Need perl-modules for clang analyzer tests. -# Need git for SPIRV-Tools tests. -RUN apt-get update && \ - apt-get install -y \ - binutils \ - cmake \ - curl \ - git \ - libstdc++-11-dev \ - ninja-build \ - nodejs \ - perl-modules \ - python3-psutil - -ENV LLVM_SYSROOT=$LLVM_SYSROOT -ENV PATH=${LLVM_SYSROOT}/bin:${PATH} diff --git a/.github/workflows/containers/github-action-ci/storage.conf b/.github/workflows/containers/github-action-ci/storage.conf deleted file mode 100644 index 60f295ff1e96..000000000000 --- a/.github/workflows/containers/github-action-ci/storage.conf +++ /dev/null @@ -1,4 +0,0 @@ -[storage] - driver = "overlay" - runroot = "/mnt/podman/container" - graphroot = "/mnt/podman/image" diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 000000000000..6f0f8ef7d775 --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,237 @@ +name: Code coverage + +on: + push: + branches: + - main + pull_request: + schedule: + - cron: '0 0 * * 0' # Weekly on Sunday at 00:00 + workflow_dispatch: + +defaults: + run: + shell: bash -ex {0} + +concurrency: + group: ${{ github.repository_id }}-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +env: + PROFDATA_FILE: llvm.profdata + LCOV_FILE: codecov.lcov + OUTPUT_HTML_DIR: COVERAGE + +jobs: + + run-with-coverage: + if: ${{ github.event_name == 'pull_request' || github.event_name == 'push' }} + runs-on: matterlabs-ci-runner-high-performance + container: + image: ghcr.io/matter-labs/zksync-llvm-runner:latest + steps: + + - name: Checkout solx + uses: actions/checkout@v4 + with: + ref: 'main' + repository: matter-labs/solx + submodules: recursive + + # This step is required to checkout submodules + # that are disabled in .gitmodules config + - name: Checkout submodules + run: | + git config --global --add safe.directory '*' + git submodule update --force --depth=1 --recursive --checkout + + - name: Remove llvm submodule + run: rm -rf llvm + + - name: Checkout llvm + uses: actions/checkout@v4 + with: + path: llvm + + - name: Building solc + uses: matter-labs/era-compiler-ci/.github/actions/build-solc@v1 + with: + cmake-build-type: 'Release' + working-dir: 'era-solidity' + upload-testing-binary: false + + - name: Build LLVM with coverage + uses: matter-labs/era-compiler-ci/.github/actions/build-llvm@v1 + with: + clone-llvm: 'false' + enable-coverage: true + enable-tests: true + enable-assertions: 'false' + ccache-key: ${{ format('llvm-{0}-{1}', runner.os, runner.arch) }} + + - name: Build solx with coverage + uses: matter-labs/era-compiler-ci/.github/actions/build-rust@v1 + env: + BOOST_PREFIX: ${{ github.workspace }}/era-solidity/boost/lib + SOLC_PREFIX: ${{ github.workspace }}/era-solidity/build + with: + exec_name: 'solx' + target: 'x86_64-unknown-linux-gnu' + release-suffix: test + enable-coverage: true + + - name: Running Lit tests + run: | + ninja -C './target-llvm/build-final' verify-llvm -v || true + ninja -C './target-llvm/build-final' check-llvm-codegen-evm -v || true + ninja -C './target-llvm/build-final' check-llvm-codegen-generic -v || true + ninja -C './target-llvm/build-final' check-llvm-mc-evm -v || true + ninja -C './target-llvm/build-final' check-llvm-unit -v || true + ninja -C './target-llvm/build-final' check-lld-unit -v || true + + - name: Generate coverage reports + run: | + llvm-profdata merge -sparse -o ${PROFDATA_FILE} ./target-llvm/build-final/profiles/*.profraw + llvm-cov show --show-directory-coverage \ + --format=html --output-dir=${OUTPUT_HTML_DIR} \ + -instr-profile=${PROFDATA_FILE} ./releases/test/solx-test + llvm-cov export --format=lcov -instr-profile=${PROFDATA_FILE} \ + ./releases/test/solx-test > ./${LCOV_FILE} + + - name: Upload coverage artifacts + uses: actions/upload-artifact@v4 + with: + name: 'Coverage HTML' + path: ${{ env.OUTPUT_HTML_DIR }} + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + working-directory: ${{ github.workspace }}/llvm # Mandatory if main repo is in different directory + directory: ${{ github.workspace }} # Mandatory if main repo is in different directory + token: ${{ secrets.CODECOV_TOKEN }} + file: ${{ env.LCOV_FILE }} + slug: ${{ github.repository }} + + # Calculates full code coverage for LLVM + # based on regression AND integration tests together + # Runs for `main` branch by schedule or by request as it takes >3 hours to complete + integration-tests-coverage: + if: ${{ github.event_name != 'pull_request' && github.event_name != 'push' }} + runs-on: matterlabs-ci-runner-highdisk + container: + image: ghcr.io/matter-labs/zksync-llvm-runner:main + env: + BOOST_PREFIX: ${{ github.workspace }}/era-solidity/boost/lib + SOLC_PREFIX: ${{ github.workspace }}/era-solidity/build + TARGET: 'x86_64-unknown-linux-gnu' + steps: + + - name: Checkout compiler-tester + uses: actions/checkout@v4 + with: + ref: 'main' + repository: matter-labs/era-compiler-tester + submodules: recursive + + # This step is required to checkout submodules + # that are disabled in .gitmodules config + - name: Checkout submodules + run: | + git config --global --add safe.directory '*' + git submodule update --init --force --depth=1 --recursive --checkout + + - name: Remove llvm submodule + run: rm -rf llvm + + - name: Checkout llvm + uses: actions/checkout@v4 + with: + path: llvm + + - name: Build LLVM for tester + uses: matter-labs/era-compiler-ci/.github/actions/build-llvm@v1 + with: + clone-llvm: false + + - name: Build compiler-tester + run: | + cargo build --release --target ${TARGET} --bin 'compiler-tester' + # Clean-up LLVM for the coverage build + rm -rf target-llvm + + - name: Building solc + uses: matter-labs/era-compiler-ci/.github/actions/build-solc@v1 + with: + cmake-build-type: 'Release' + working-dir: 'era-solidity' + upload-testing-binary: false + + - name: Build LLVM with coverage + uses: matter-labs/era-compiler-ci/.github/actions/build-llvm@v1 + with: + clone-llvm: 'false' + enable-coverage: true + enable-tests: true + enable-assertions: 'false' + ccache-key: ${{ format('llvm-{0}-{1}', runner.os, runner.arch) }} + + - name: Build solx with coverage + env: + CARGO_CHECKOUT_DIR: /usr/local/cargo/git/checkouts + RUSTFLAGS: "-C instrument-coverage" + run: | + cargo build --target ${TARGET} \ + --manifest-path ${CARGO_CHECKOUT_DIR}/solx-*/*/Cargo.toml \ + --target-dir './target-solx/' + + - name: Running Lit tests + run: | + ninja -C './target-llvm/build-final' verify-llvm -v || true + ninja -C './target-llvm/build-final' check-llvm-codegen-evm -v || true + ninja -C './target-llvm/build-final' check-llvm-codegen-generic -v || true + ninja -C './target-llvm/build-final' check-llvm-mc-evm -v || true + ninja -C './target-llvm/build-final' check-llvm-unit -v || true + ninja -C './target-llvm/build-final' check-lld-unit -v || true + llvm-profdata merge -sparse -o ${PROFDATA_FILE} ./target-llvm/build-final/profiles/*.profraw + + - name: Run integration tests with coverage + run: | + TMP="${PROFDATA_FILE}.tmp" + for TEST_PATH in tests/solidity/complex/* tests/solidity/simple/* tests/solidity/ethereum/*; do + WORKDIR=$(basename ${TEST_PATH}) + mkdir -p "${GITHUB_WORKSPACE}/${WORKDIR}" + ./target/${TARGET}/release/compiler-tester \ + --target evm \ + --toolchain ir-llvm \ + --solx "./target-solx/${TARGET}/debug/solx" \ + --load-system-contracts ./system-contracts-stable-build \ + --path ${TEST_PATH} \ + --workflow build \ + --verbose + + mv *.profraw "${GITHUB_WORKSPACE}/${WORKDIR}/" || true + du -hs "${GITHUB_WORKSPACE}/${WORKDIR}" + find ${GITHUB_WORKSPACE}/${WORKDIR} -type f -name '*.profraw' -print > profiles.lst + if [[ -f "${PROFDATA_FILE}" ]]; then + llvm-profdata merge -sparse -num-threads="$(nproc)" -o "${TMP}" "${PROFDATA_FILE}" @profiles.lst + else + llvm-profdata merge -sparse -num-threads="$(nproc)" -o "${TMP}" @profiles.lst + fi + mv -f "${TMP}" "${PROFDATA_FILE}" + rm -rf "${GITHUB_WORKSPACE}/${WORKDIR}" + done + + - name: Generate coverage reports + run: | + llvm-cov show --show-directory-coverage \ + --format=html --output-dir=${OUTPUT_HTML_DIR} \ + -instr-profile=${PROFDATA_FILE} ./target-solx/${TARGET}/debug/solx + llvm-cov export --format=lcov -instr-profile=${PROFDATA_FILE} \ + ./target-solx/${TARGET}/debug/solx > ./llvm/${LCOV_FILE} + + - name: Upload coverage artifacts + uses: actions/upload-artifact@v4 + with: + name: 'Coverage integration tests HTML' + path: ${{ env.OUTPUT_HTML_DIR }} diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml deleted file mode 100644 index 800e92915735..000000000000 --- a/.github/workflows/docs.yml +++ /dev/null @@ -1,169 +0,0 @@ -# LLVM Documentation CI -# 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 - -name: "Test documentation build" - -permissions: - contents: read - -on: - push: - branches: - - 'main' - paths: - - 'llvm/docs/**' - - 'clang/docs/**' - - 'clang/include/clang/Basic/AttrDocs.td' - - 'clang/include/clang/Driver/ClangOptionDocs.td' - - 'clang/include/clang/Basic/DiagnosticDocs.td' - - 'clang-tools-extra/docs/**' - - 'lldb/docs/**' - - 'libunwind/docs/**' - - 'libcxx/docs/**' - - 'libc/docs/**' - - 'lld/docs/**' - - 'openmp/docs/**' - - 'polly/docs/**' - - 'flang/docs/**' - - 'flang/include/flang/Optimizer/Dialect/FIROps.td' - - '.github/workflows/docs.yml' - pull_request: - paths: - - 'llvm/docs/**' - - 'clang/docs/**' - - 'clang/include/clang/Basic/AttrDocs.td' - - 'clang/include/clang/Driver/ClangOptionDocs.td' - - 'clang/include/clang/Basic/DiagnosticDocs.td' - - 'clang-tools-extra/docs/**' - - 'lldb/docs/**' - - 'libunwind/docs/**' - - 'libcxx/docs/**' - - 'libc/docs/**' - - 'lld/docs/**' - - 'openmp/docs/**' - - 'polly/docs/**' - - 'flang/docs/**' - - 'flang/include/flang/Optimizer/Dialect/FIROps.td' - - '.github/workflows/docs.yml' - -jobs: - check-docs-build: - name: "Test documentation build" - runs-on: ubuntu-latest - if: github.repository == 'llvm/llvm-project' - steps: - # Don't fetch before checking for file changes to force the file changes - # action to use the Github API in pull requests. If it's a push to a - # branch we can't use the Github API to get the diff, so we need to have - # a local checkout beforehand. - - name: Fetch LLVM sources (Push) - if: ${{ github.event_name == 'push' }} - uses: actions/checkout@v4 - with: - fetch-depth: 1 - - name: Get subprojects that have doc changes - id: docs-changed-subprojects - uses: tj-actions/changed-files@v39 - with: - files_yaml: | - llvm: - - 'llvm/docs/**' - clang: - - 'clang/docs/**' - - 'clang/include/clang/Basic/AttrDocs.td' - - 'clang/include/clang/Driver/ClangOptionDocs.td' - - 'clang/include/clang/Basic/DiagnosticDocs.td' - clang-tools-extra: - - 'clang-tools-extra/docs/**' - lldb: - - 'lldb/docs/**' - libunwind: - - 'libunwind/docs/**' - libcxx: - - 'libcxx/docs/**' - libc: - - 'libc/docs/**' - lld: - - 'lld/docs/**' - openmp: - - 'openmp/docs/**' - polly: - - 'polly/docs/**' - flang: - - 'flang/docs/**' - - 'flang/include/flang/Optimizer/Dialect/FIROps.td' - - name: Fetch LLVM sources (PR) - if: ${{ github.event_name == 'pull_request' }} - uses: actions/checkout@v4 - with: - fetch-depth: 1 - - name: Setup Python env - uses: actions/setup-python@v5 - with: - python-version: '3.11' - cache: 'pip' - cache-dependency-path: 'llvm/docs/requirements.txt' - - name: Install python dependencies - run: pip install -r llvm/docs/requirements.txt - - name: Install system dependencies - run: | - sudo apt-get update - # swig and graphviz are lldb specific dependencies - sudo apt-get install -y cmake ninja-build swig graphviz - - name: Build LLVM docs - if: steps.docs-changed-subprojects.outputs.llvm_any_changed == 'true' - run: | - cmake -B llvm-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_SPHINX=ON ./llvm - TZ=UTC ninja -C llvm-build docs-llvm-html docs-llvm-man - - name: Build Clang docs - if: steps.docs-changed-subprojects.outputs.clang_any_changed == 'true' - run: | - cmake -B clang-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang" -DLLVM_ENABLE_SPHINX=ON ./llvm - TZ=UTC ninja -C clang-build docs-clang-html docs-clang-man - - name: Build clang-tools-extra docs - if: steps.docs-changed-subprojects.outputs.clang-tools-extra_any_changed == 'true' - run: | - cmake -B clang-tools-extra-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" -DLLVM_ENABLE_SPHINX=ON ./llvm - TZ=UTC ninja -C clang-tools-extra-build docs-clang-tools-html docs-clang-tools-man - - name: Build LLDB docs - if: steps.docs-changed-subprojects.outputs.lldb_any_changed == 'true' - run: | - cmake -B lldb-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;lldb" -DLLVM_ENABLE_SPHINX=ON ./llvm - TZ=UTC ninja -C lldb-build docs-lldb-html docs-lldb-man - - name: Build libunwind docs - if: steps.docs-changed-subprojects.outputs.libunwind_any_changed == 'true' - run: | - cmake -B libunwind-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_RUNTIMES="libunwind" -DLLVM_ENABLE_SPHINX=ON ./runtimes - TZ=UTC ninja -C libunwind-build docs-libunwind-html - - name: Build libcxx docs - if: steps.docs-changed-subprojects.outputs.libcxx_any_changed == 'true' - run: | - cmake -B libcxx-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_RUNTIMES="libcxxabi;libcxx;libunwind" -DLLVM_ENABLE_SPHINX=ON ./runtimes - TZ=UTC ninja -C libcxx-build docs-libcxx-html - - name: Build libc docs - if: steps.docs-changed-subprojects.outputs.libc_any_changed == 'true' - run: | - cmake -B libc-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_RUNTIMES="libc" -DLLVM_ENABLE_SPHINX=ON ./runtimes - TZ=UTC ninja -C libc-build docs-libc-html - - name: Build LLD docs - if: steps.docs-changed-subprojects.outputs.lld_any_changed == 'true' - run: | - cmake -B lld-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="lld" -DLLVM_ENABLE_SPHINX=ON ./llvm - TZ=UTC ninja -C lld-build docs-lld-html - - name: Build OpenMP docs - if: steps.docs-changed-subprojects.outputs.openmp_any_changed == 'true' - run: | - cmake -B openmp-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;openmp" -DLLVM_ENABLE_SPHINX=ON ./llvm - TZ=UTC ninja -C openmp-build docs-openmp-html - - name: Build Polly docs - if: steps.docs-changed-subprojects.outputs.polly_any_changed == 'true' - run: | - cmake -B polly-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="polly" -DLLVM_ENABLE_SPHINX=ON ./llvm - TZ=UTC ninja -C polly-build docs-polly-html docs-polly-man - - name: Build Flang docs - if: steps.docs-changed-subprojects.outputs.flang_any_changed == 'true' - run: | - cmake -B flang-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;mlir;flang" -DLLVM_ENABLE_SPHINX=ON ./llvm - TZ=UTC ninja -C flang-build docs-flang-html diff --git a/.github/workflows/email-check.yaml b/.github/workflows/email-check.yaml deleted file mode 100644 index 8f32d020975f..000000000000 --- a/.github/workflows/email-check.yaml +++ /dev/null @@ -1,46 +0,0 @@ -name: "Check for private emails used in PRs" - -on: - pull_request: - types: - - opened - -permissions: - contents: read - -jobs: - validate_email: - runs-on: ubuntu-latest - if: github.repository == 'llvm/llvm-project' - steps: - - name: Fetch LLVM sources - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.sha }} - - - name: Extract author email - id: author - run: | - git log -1 - echo "EMAIL=$(git show -s --format='%ae' HEAD~0)" >> $GITHUB_OUTPUT - # Create empty comment file - echo "[]" > comments - - - name: Validate author email - if: ${{ endsWith(steps.author.outputs.EMAIL, 'noreply.github.com') }} - env: - COMMENT: >- - ⚠️ We detected that you are using a GitHub private e-mail address to contribute to the repo.
- Please turn off [Keep my email addresses private](https://github.com/settings/emails) setting in your account.
- See [LLVM Discourse](https://discourse.llvm.org/t/hidden-emails-on-github-should-we-do-something-about-it) for more information. - run: | - cat << EOF > comments - [{"body" : "$COMMENT"}] - EOF - - - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 - if: always() - with: - name: workflow-args - path: | - comments diff --git a/.github/workflows/forge-benchmarks.yaml b/.github/workflows/forge-benchmarks.yaml new file mode 100644 index 000000000000..6c39025fef2a --- /dev/null +++ b/.github/workflows/forge-benchmarks.yaml @@ -0,0 +1,43 @@ +name: Forge benchmarks + +on: + workflow_dispatch: + inputs: + solx_candidate_branch: + description: 'Solidity candidate branch to use. If empty, the current branch will be used.' + required: false + default: '' + solx_reference_branch: + description: 'Solidity reference branch to use. If empty, the current branch will be used.' + required: false + default: '' + compiler_llvm_candidate_branch: + description: 'LLVM candidate branch to use. If empty, the current branch will be used.' + required: false + default: '' + compiler_llvm_reference_branch: + description: 'LLVM reference branch to use. If empty, the default repository branch will be used.' + required: false + default: 'main' + pull_request: + +permissions: + contents: read + pull-requests: write + +concurrency: + group: ${{ github.repository_id }}-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + forge-benchmarks: + uses: matter-labs/era-compiler-ci/.github/workflows/forge-benchmarks.yaml@v1 + secrets: inherit + with: + print-markdown-tables: 'false' # do not print markdown tables to PRs + upload-reports: ${{ github.event_name == 'pull_request' }} + solx_candidate_branch: ${{ inputs.solx_candidate_branch || 'main' }} + solx_reference_branch: ${{ inputs.solx_reference_branch || 'main' }} + compiler_llvm_candidate_branch: ${{ inputs.compiler_llvm_candidate_branch || github.head_ref }} + compiler_llvm_reference_branch: ${{ inputs.compiler_llvm_reference_branch || github.base_ref || github.event.repository.default_branch }} + compiler-llvm-repo: ${{ github.event.pull_request.head.repo.full_name || 'matter-labs/era-compiler-llvm' }} # required to properly test forks diff --git a/.github/workflows/get-llvm-version/action.yml b/.github/workflows/get-llvm-version/action.yml deleted file mode 100644 index 2218d926fc13..000000000000 --- a/.github/workflows/get-llvm-version/action.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Get LLVM Version -description: >- - Get the LLVM version from the llvm-project source tree. This action assumes - the llvm-project sources have already been checked out into GITHUB_WORKSPACE. - -outputs: - major: - description: LLVM major version - value: ${{ steps.version.outputs.major }} - minor: - description: LLVM minor version - value: ${{ steps.version.outputs.minor }} - patch: - description: LLVM patch version - value: ${{ steps.version.outputs.patch }} - -runs: - using: "composite" - steps: - - name: Get Version - shell: bash - id: version - run: | - for v in major minor patch; do - echo "$v=`llvm/utils/release/get-llvm-version.sh --$v`" >> $GITHUB_OUTPUT - done diff --git a/.github/workflows/issue-release-workflow.yml b/.github/workflows/issue-release-workflow.yml deleted file mode 100644 index 5027d4f3ea6f..000000000000 --- a/.github/workflows/issue-release-workflow.yml +++ /dev/null @@ -1,69 +0,0 @@ -# This contains the workflow definitions that allow users to test backports -# to the release branch using comments on issues. -# -# /cherry-pick <...> -# -# This comment will attempt to cherry-pick the given commits to the latest -# release branch (release/Y.x) and if successful, push the result to a branch -# on github. -# -# /branch // -# -# This comment will create a pull request from to the latest release -# branch. - -name: Issue Release Workflow - -permissions: - contents: read - -on: - issue_comment: - types: - - created - - edited - issues: - types: - - opened - -env: - COMMENT_BODY: ${{ github.event.action == 'opened' && github.event.issue.body || github.event.comment.body }} - -jobs: - backport-commits: - name: Backport Commits - runs-on: ubuntu-latest - permissions: - issues: write - pull-requests: write - if: >- - (github.repository == 'llvm/llvm-project') && - !startswith(github.event.comment.body, '') && - contains(github.event.action == 'opened' && github.event.issue.body || github.event.comment.body, '/cherry-pick') - steps: - - name: Fetch LLVM sources - uses: actions/checkout@v4 - with: - repository: llvm/llvm-project - # GitHub stores the token used for checkout and uses it for pushes - # too, but we want to use a different token for pushing, so we need - # to disable persist-credentials here. - persist-credentials: false - fetch-depth: 0 - - - name: Setup Environment - run: | - pip install --require-hashes -r ./llvm/utils/git/requirements.txt - ./llvm/utils/git/github-automation.py --token ${{ github.token }} setup-llvmbot-git - - - name: Backport Commits - run: | - printf "%s" "$COMMENT_BODY" | - ./llvm/utils/git/github-automation.py \ - --repo "$GITHUB_REPOSITORY" \ - --token "${{ secrets.RELEASE_WORKFLOW_PR_CREATE }}" \ - release-workflow \ - --branch-repo-token ${{ secrets.RELEASE_WORKFLOW_PUSH_SECRET }} \ - --issue-number ${{ github.event.issue.number }} \ - --requested-by ${{ (github.event.action == 'opened' && github.event.issue.user.login) || github.event.comment.user.login }} \ - auto diff --git a/.github/workflows/issue-subscriber.yml b/.github/workflows/issue-subscriber.yml deleted file mode 100644 index ef4fdf441819..000000000000 --- a/.github/workflows/issue-subscriber.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Issue Subscriber - -on: - issues: - types: - - labeled - -permissions: - contents: read - -jobs: - auto-subscribe: - runs-on: ubuntu-latest - if: github.repository == 'llvm/llvm-project' - steps: - - name: Checkout Automation Script - uses: actions/checkout@v4 - with: - sparse-checkout: llvm/utils/git/ - ref: main - - - name: Setup Automation Script - working-directory: ./llvm/utils/git/ - run: | - pip install --require-hashes -r requirements.txt - - - name: Update watchers - working-directory: ./llvm/utils/git/ - # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable - env: - LABEL_NAME: ${{ github.event.label.name }} - run: | - python3 ./github-automation.py \ - --token '${{ secrets.ISSUE_SUBSCRIBER_TOKEN }}' \ - issue-subscriber \ - --issue-number '${{ github.event.issue.number }}' \ - --label-name "$LABEL_NAME" diff --git a/.github/workflows/issue-write.yml b/.github/workflows/issue-write.yml deleted file mode 100644 index 5334157a7fd2..000000000000 --- a/.github/workflows/issue-write.yml +++ /dev/null @@ -1,157 +0,0 @@ -name: Comment on an issue - -on: - workflow_run: - workflows: - - "Check code formatting" - - "Check for private emails used in PRs" - - "PR Request Release Note" - types: - - completed - -permissions: - contents: read - -jobs: - pr-comment: - runs-on: ubuntu-latest - permissions: - pull-requests: write - if: > - github.event.workflow_run.event == 'pull_request' && - ( - github.event.workflow_run.conclusion == 'success' || - github.event.workflow_run.conclusion == 'failure' - ) - steps: - - name: Fetch Sources - uses: actions/checkout@v4 - with: - sparse-checkout: | - .github/workflows/unprivileged-download-artifact/action.yml - sparse-checkout-cone-mode: false - - name: 'Download artifact' - uses: ./.github/workflows/unprivileged-download-artifact - id: download-artifact - with: - run-id: ${{ github.event.workflow_run.id }} - artifact-name: workflow-args - - - name: 'Comment on PR' - if: steps.download-artifact.outputs.artifact-id != '' - uses: actions/github-script@v3 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - var fs = require('fs'); - const comments = JSON.parse(fs.readFileSync('./comments')); - if (!comments || comments.length == 0) { - return; - } - - let runInfo = await github.actions.getWorkflowRun({ - owner: context.repo.owner, - repo: context.repo.repo, - run_id: context.payload.workflow_run.id - }); - - console.log(runInfo); - - - // Query to find the number of the pull request that triggered this job. - // The associated pull requests are based off of the branch name, so if - // you create a pull request for a branch, close it, and then create - // another pull request with the same branch, then this query will return - // two associated pull requests. This is why we have to fetch all the - // associated pull requests and then iterate through them to find the - // one that is open. - const gql_query = ` - query($repo_owner : String!, $repo_name : String!, $branch: String!) { - repository(owner: $repo_owner, name: $repo_name) { - ref (qualifiedName: $branch) { - associatedPullRequests(first: 100) { - nodes { - baseRepository { - owner { - login - } - } - number - state - } - } - } - } - } - ` - const gql_variables = { - repo_owner: runInfo.data.head_repository.owner.login, - repo_name: runInfo.data.head_repository.name, - branch: runInfo.data.head_branch - } - const gql_result = await github.graphql(gql_query, gql_variables); - console.log(gql_result); - // If the branch for the PR was deleted before this job has a chance - // to run, then the ref will be null. This can happen if someone: - // 1. Rebase the PR, which triggers some workflow. - // 2. Immediately merges the PR and deletes the branch. - // 3. The workflow finishes and triggers this job. - if (!gql_result.repository.ref) { - console.log("Ref has been deleted"); - return; - } - console.log(gql_result.repository.ref.associatedPullRequests.nodes); - - var pr_number = 0; - gql_result.repository.ref.associatedPullRequests.nodes.forEach((pr) => { - - // The largest PR number is the one we care about. The only way - // to have more than one associated pull requests is if all the - // old pull requests are in the closed state. - if (pr.baseRepository.owner.login = context.repo.owner && pr.number > pr_number) { - pr_number = pr.number; - } - }); - if (pr_number == 0) { - console.log("Error retrieving pull request number"); - return; - } - - await comments.forEach(function (comment) { - if (comment.id) { - // Security check: Ensure that this comment was created by - // the github-actions bot, so a malicious input won't overwrite - // a user's comment. - github.issues.getComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: comment.id - }).then((old_comment) => { - console.log(old_comment); - if (old_comment.data.user.login != "github-actions[bot]") { - console.log("Invalid comment id: " + comment.id); - return; - } - github.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr_number, - comment_id: comment.id, - body: comment.body - }); - }); - } else { - github.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr_number, - body: comment.body - }); - } - }); - - - name: Dump comments file - if: >- - always() && - steps.download-artifact.outputs.artifact-id != '' - run: cat comments diff --git a/.github/workflows/libclang-abi-tests.yml b/.github/workflows/libclang-abi-tests.yml deleted file mode 100644 index 9e839ff49e28..000000000000 --- a/.github/workflows/libclang-abi-tests.yml +++ /dev/null @@ -1,168 +0,0 @@ -name: libclang ABI Tests - -permissions: - contents: read - -on: - workflow_dispatch: - push: - branches: - - 'release/**' - paths: - - 'clang/**' - - '.github/workflows/libclang-abi-tests.yml' - pull_request: - branches: - - 'release/**' - paths: - - 'clang/**' - - '.github/workflows/libclang-abi-tests.yml' - -concurrency: - # Skip intermediate builds: always. - # Cancel intermediate builds: only if it is a pull request build. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} - -jobs: - abi-dump-setup: - if: github.repository_owner == 'llvm' - runs-on: ubuntu-latest - outputs: - BASELINE_REF: ${{ steps.vars.outputs.BASELINE_REF }} - ABI_HEADERS: ${{ steps.vars.outputs.ABI_HEADERS }} - ABI_LIBS: ${{ steps.vars.outputs.ABI_LIBS }} - BASELINE_VERSION_MAJOR: ${{ steps.vars.outputs.BASELINE_VERSION_MAJOR }} - LLVM_VERSION_MAJOR: ${{ steps.version.outputs.major }} - LLVM_VERSION_MINOR: ${{ steps.version.outputs.minor }} - LLVM_VERSION_PATCH: ${{ steps.version.outputs.patch }} - steps: - - name: Checkout source - uses: actions/checkout@v4 - with: - fetch-depth: 250 - - - name: Get LLVM version - id: version - uses: ./.github/workflows/get-llvm-version - - - name: Setup Variables - id: vars - run: | - remote_repo='https://github.com/llvm/llvm-project' - if [ ${{ steps.version.outputs.patch }} -eq 0 ]; then - major_version=$(( ${{ steps.version.outputs.major }} - 1)) - baseline_ref="llvmorg-$major_version.1.0" - - # If there is a minor release, we want to use that as the base line. - minor_ref=$(git ls-remote --refs -t "$remote_repo" llvmorg-"$major_version".[1-9].[0-9] | tail -n1 | grep -o 'llvmorg-.\+' || true) - if [ -n "$minor_ref" ]; then - baseline_ref="$minor_ref" - else - # Check if we have a release candidate - rc_ref=$(git ls-remote --refs -t "$remote_repo" llvmorg-"$major_version".[1-9].[0-9]-rc* | tail -n1 | grep -o 'llvmorg-.\+' || true) - if [ -n "$rc_ref" ]; then - baseline_ref="$rc_ref" - fi - fi - { - echo "BASELINE_VERSION_MAJOR=$major_version" - echo "BASELINE_REF=$baseline_ref" - echo "ABI_HEADERS=clang-c" - echo "ABI_LIBS=libclang.so" - } >> "$GITHUB_OUTPUT" - else - { - echo "BASELINE_VERSION_MAJOR=${{ steps.version.outputs.major }}" - echo "BASELINE_REF=llvmorg-${{ steps.version.outputs.major }}.1.0" - echo "ABI_HEADERS=." - echo "ABI_LIBS=libclang.so libclang-cpp.so" - } >> "$GITHUB_OUTPUT" - fi - - abi-dump: - if: github.repository_owner == 'llvm' - needs: abi-dump-setup - runs-on: ubuntu-latest - strategy: - matrix: - name: - - build-baseline - - build-latest - include: - - name: build-baseline - llvm_version_major: ${{ needs.abi-dump-setup.outputs.BASELINE_VERSION_MAJOR }} - ref: ${{ needs.abi-dump-setup.outputs.BASELINE_REF }} - repo: llvm/llvm-project - - name: build-latest - llvm_version_major: ${{ needs.abi-dump-setup.outputs.LLVM_VERSION_MAJOR }} - ref: ${{ github.sha }} - repo: ${{ github.repository }} - steps: - - name: Install Ninja - uses: llvm/actions/install-ninja@main - - name: Install abi-compliance-checker - run: | - sudo apt-get install abi-dumper autoconf pkg-config - - name: Install universal-ctags - run: | - git clone https://github.com/universal-ctags/ctags.git - cd ctags - ./autogen.sh - ./configure - sudo make install - - name: Download source code - uses: llvm/actions/get-llvm-project-src@main - with: - ref: ${{ matrix.ref }} - repo: ${{ matrix.repo }} - - name: Configure - run: | - mkdir install - cmake -B build -S llvm -G Ninja -DLLVM_ENABLE_PROJECTS=clang -DCMAKE_BUILD_TYPE=Debug -DLLVM_TARGETS_TO_BUILD="" -DLLVM_BUILD_LLVM_DYLIB=ON -DLLVM_LINK_LLVM_DYLIB=ON -DCMAKE_C_FLAGS_DEBUG="-g1 -Og" -DCMAKE_CXX_FLAGS_DEBUG="-g1 -Og" -DCMAKE_INSTALL_PREFIX="$(pwd)"/install llvm - - name: Build - run: ninja -C build/ ${{ needs.abi-dump-setup.outputs.ABI_LIBS }} install-clang-headers - - name: Dump ABI - run: | - parallel abi-dumper -lver ${{ matrix.ref }} -skip-cxx -public-headers ./install/include/${{ needs.abi-dump-setup.outputs.ABI_HEADERS }} -o {}-${{ matrix.ref }}.abi ./build/lib/{} ::: ${{ needs.abi-dump-setup.outputs.ABI_LIBS }} - for lib in ${{ needs.abi-dump-setup.outputs.ABI_LIBS }}; do - # Remove symbol versioning from dumps, so we can compare across major versions. - sed -i 's/LLVM_[0-9]\+/LLVM_NOVERSION/' $lib-${{ matrix.ref }}.abi - done - - name: Upload ABI file - uses: actions/upload-artifact@v3 - with: - name: ${{ matrix.name }} - path: '*${{ matrix.ref }}.abi' - - abi-compare: - if: github.repository_owner == 'llvm' - runs-on: ubuntu-latest - needs: - - abi-dump-setup - - abi-dump - steps: - - name: Download baseline - uses: actions/download-artifact@v3 - with: - name: build-baseline - path: build-baseline - - name: Download latest - uses: actions/download-artifact@v3 - with: - name: build-latest - path: build-latest - - - name: Install abi-compliance-checker - run: sudo apt-get install abi-compliance-checker - - name: Compare ABI - run: | - for lib in ${{ needs.abi-dump-setup.outputs.ABI_LIBS }}; do - abi-compliance-checker -lib $lib -old build-baseline/$lib*.abi -new build-latest/$lib*.abi - done - - name: Upload ABI Comparison - if: always() - uses: actions/upload-artifact@v3 - with: - name: compat-report-${{ github.sha }} - path: compat_reports/ diff --git a/.github/workflows/libclang-python-tests.yml b/.github/workflows/libclang-python-tests.yml deleted file mode 100644 index 43ded0af3ac2..000000000000 --- a/.github/workflows/libclang-python-tests.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: Libclang Python Binding Tests - -permissions: - contents: read - -on: - workflow_dispatch: - push: - branches: - - 'main' - paths: - - 'clang/bindings/python/**' - - 'clang/tools/libclang/**' - - 'clang/CMakeList.txt' - - '.github/workflows/libclang-python-tests.yml' - - '.github/workflows/llvm-project-tests.yml' - pull_request: - paths: - - 'clang/bindings/python/**' - - 'clang/tools/libclang/**' - - 'clang/CMakeList.txt' - - '.github/workflows/libclang-python-tests.yml' - - '.github/workflows/llvm-project-tests.yml' - -concurrency: - # Skip intermediate builds: always. - # Cancel intermediate builds: only if it is a pull request build. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} - -jobs: - check-clang-python: - # Build libclang and then run the libclang Python binding's unit tests. - name: Build and run Python unit tests - if: github.repository == 'llvm/llvm-project' - strategy: - fail-fast: false - matrix: - python-version: ["3.8", "3.11"] - uses: ./.github/workflows/llvm-project-tests.yml - with: - build_target: check-clang-python - projects: clang - # There is an issue running on "windows-2019". - # See https://github.com/llvm/llvm-project/issues/76601#issuecomment-1873049082. - os_list: '["ubuntu-latest"]' - python_version: ${{ matrix.python-version }} diff --git a/.github/workflows/libclc-tests.yml b/.github/workflows/libclc-tests.yml deleted file mode 100644 index 23192f776a98..000000000000 --- a/.github/workflows/libclc-tests.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: libclc Tests - -permissions: - contents: read - -on: - workflow_dispatch: - push: - branches: - - 'release/**' - paths: - - 'libclc/**' - - '.github/workflows/libclc-tests.yml' - - '.github/workflows/llvm-project-tests.yml' - - '!clang/**' - - '!llvm/**' - pull_request: - branches: - - 'release/**' - paths: - - 'libclc/**' - - '.github/workflows/libclc-tests.yml' - - '.github/workflows/llvm-project-tests.yml' - - '!clang/**' - - '!llvm/**' - -concurrency: - # Skip intermediate builds: always. - # Cancel intermediate builds: only if it is a pull request build. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} - -jobs: - check_libclc: - if: github.repository_owner == 'llvm' - name: Test libclc - uses: ./.github/workflows/llvm-project-tests.yml - with: - projects: clang;libclc diff --git a/.github/workflows/libcxx-build-and-test.yaml b/.github/workflows/libcxx-build-and-test.yaml deleted file mode 100644 index 1456f245cf7c..000000000000 --- a/.github/workflows/libcxx-build-and-test.yaml +++ /dev/null @@ -1,234 +0,0 @@ -# This file defines pre-commit CI for libc++, libc++abi, and libunwind (on Github). -# -# We split the configurations in multiple stages with the intent of saving compute time -# when a job fails early in the pipeline. This is why the jobs are marked as `continue-on-error: false`. -# We try to run the CI configurations with the most signal in the first stage. -# -# Stages 1 & 2 are meant to be "smoke tests", and are meant to catch most build/test failures quickly and without using -# too many resources. -# Stage 3 is "everything else", and is meant to catch breakages on more niche or unique configurations. -# -# Therefore, we "fail-fast" for any failures during stages 1 & 2, meaning any job failing cancels all other running jobs, -# under the assumption that if the "smoke tests" fail, then the other configurations will likely fail in the same way. -# However, stage 3 does not fail fast, as it's more likely that any one job failing is a flake or a configuration-specific -# -name: Build and Test libc++ -on: - pull_request: - paths: - - 'libcxx/**' - - 'libcxxabi/**' - - 'libunwind/**' - - 'runtimes/**' - - 'cmake/**' - - '.github/workflows/libcxx-build-and-test.yaml' - schedule: - # Run nightly at 08:00 UTC (aka 00:00 Pacific, aka 03:00 Eastern) - - cron: '0 8 * * *' - -permissions: - contents: read # Default everything to read-only - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number }} - cancel-in-progress: true - - -env: - # LLVM POST-BRANCH bump version - # LLVM POST-BRANCH add compiler test for ToT - 1, e.g. "Clang 17" - # LLVM RELEASE bump remove compiler ToT - 3, e.g. "Clang 15" - LLVM_HEAD_VERSION: "19" # Used compiler, update POST-BRANCH. - LLVM_PREVIOUS_VERSION: "18" - LLVM_OLDEST_VERSION: "17" - GCC_STABLE_VERSION: "13" - LLVM_SYMBOLIZER_PATH: "/usr/bin/llvm-symbolizer-19" - CLANG_CRASH_DIAGNOSTICS_DIR: "crash_diagnostics" - - -jobs: - stage1: - if: github.repository_owner == 'llvm' - runs-on: libcxx-runners-8-set - continue-on-error: false - strategy: - fail-fast: false - matrix: - config: [ - 'generic-cxx03', - 'generic-cxx26', - 'generic-modules' - ] - cc: [ 'clang-19' ] - cxx: [ 'clang++-19' ] - include: - - config: 'generic-gcc' - cc: 'gcc-14' - cxx: 'g++-14' - steps: - - uses: actions/checkout@v4 - - name: ${{ matrix.config }}.${{ matrix.cxx }} - run: libcxx/utils/ci/run-buildbot ${{ matrix.config }} - env: - CC: ${{ matrix.cc }} - CXX: ${{ matrix.cxx }} - - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 - if: always() - with: - name: ${{ matrix.config }}-${{ matrix.cxx }}-results - path: | - **/test-results.xml - **/*.abilist - **/CMakeError.log - **/CMakeOutput.log - **/crash_diagnostics/* - stage2: - if: github.repository_owner == 'llvm' - runs-on: libcxx-runners-8-set - needs: [ stage1 ] - continue-on-error: false - strategy: - fail-fast: false - matrix: - config: [ - 'generic-cxx11', - 'generic-cxx14', - 'generic-cxx17', - 'generic-cxx20', - 'generic-cxx23' - ] - cc: [ 'clang-19' ] - cxx: [ 'clang++-19' ] - include: - - config: 'generic-gcc-cxx11' - cc: 'gcc-14' - cxx: 'g++-14' - - config: 'generic-cxx23' - cc: 'clang-17' - cxx: 'clang++-17' - - config: 'generic-cxx26' - cc: 'clang-18' - cxx: 'clang++-18' - steps: - - uses: actions/checkout@v4 - - name: ${{ matrix.config }} - run: libcxx/utils/ci/run-buildbot ${{ matrix.config }} - env: - CC: ${{ matrix.cc }} - CXX: ${{ matrix.cxx }} - - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 - if: always() # Upload artifacts even if the build or test suite fails - with: - name: ${{ matrix.config }}-${{ matrix.cxx }}-results - path: | - **/test-results.xml - **/*.abilist - **/CMakeError.log - **/CMakeOutput.log - **/crash_diagnostics/* - stage3: - if: github.repository_owner == 'llvm' - needs: [ stage1, stage2 ] - continue-on-error: false - strategy: - fail-fast: false - max-parallel: 8 - matrix: - config: [ - 'generic-abi-unstable', - 'generic-hardening-mode-debug', - 'generic-hardening-mode-extensive', - 'generic-hardening-mode-fast', - 'generic-hardening-mode-fast-with-abi-breaks', - 'generic-merged', - 'generic-modules-lsv', - 'generic-no-exceptions', - 'generic-no-experimental', - 'generic-no-filesystem', - 'generic-no-localization', - 'generic-no-random_device', - 'generic-no-threads', - 'generic-no-tzdb', - 'generic-no-unicode', - 'generic-no-wide-characters', - 'generic-no-rtti', - 'generic-optimized-speed', - 'generic-static', - # TODO Find a better place for the benchmark and bootstrapping builds to live. They're either very expensive - # or don't provide much value since the benchmark run results are too noise on the bots. - 'benchmarks', - 'bootstrapping-build' - ] - machine: [ 'libcxx-runners-8-set' ] - include: - - config: 'generic-cxx26' - machine: libcxx-runners-8-set - - config: 'generic-asan' - machine: libcxx-runners-8-set - - config: 'generic-tsan' - machine: libcxx-runners-8-set - - config: 'generic-ubsan' - machine: libcxx-runners-8-set - # Use a larger machine for MSAN to avoid timeout and memory allocation issues. - - config: 'generic-msan' - machine: libcxx-runners-8-set - runs-on: ${{ matrix.machine }} - steps: - - uses: actions/checkout@v4 - - name: ${{ matrix.config }} - run: libcxx/utils/ci/run-buildbot ${{ matrix.config }} - env: - CC: clang-19 - CXX: clang++-19 - - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 - if: always() - with: - name: ${{ matrix.config }}-results - path: | - **/test-results.xml - **/*.abilist - **/CMakeError.log - **/CMakeOutput.log - **/crash_diagnostics/* - windows: - runs-on: windows-2022 - needs: [ stage1 ] - strategy: - fail-fast: false - matrix: - include: - - { config: clang-cl-dll, mingw: false } - - { config: clang-cl-static, mingw: false } - - { config: clang-cl-no-vcruntime, mingw: false } - - { config: clang-cl-debug, mingw: false } - - { config: clang-cl-static-crt, mingw: false } - - { config: mingw-dll, mingw: true } - - { config: mingw-static, mingw: true } - - { config: mingw-dll-i686, mingw: true } - steps: - - uses: actions/checkout@v4 - - name: Install dependencies - run: | - choco install -y ninja - pip install psutil - - name: Install a current LLVM - if: ${{ matrix.mingw != true }} - run: | - choco install -y llvm --version=18.1.6 --allow-downgrade - - name: Install llvm-mingw - if: ${{ matrix.mingw == true }} - run: | - curl -LO https://github.com/mstorsjo/llvm-mingw/releases/download/20240606/llvm-mingw-20240606-ucrt-x86_64.zip - powershell Expand-Archive llvm-mingw*.zip -DestinationPath . - del llvm-mingw*.zip - mv llvm-mingw* c:\llvm-mingw - echo "c:\llvm-mingw\bin" | Out-File -FilePath $Env:GITHUB_PATH -Encoding utf8 -Append - - name: Add Git Bash to the path - run: | - echo "c:\Program Files\Git\usr\bin" | Out-File -FilePath $Env:GITHUB_PATH -Encoding utf8 -Append - - name: Set up the MSVC dev environment - if: ${{ matrix.mingw != true }} - uses: ilammy/msvc-dev-cmd@v1 - - name: Build and test - run: | - bash libcxx/utils/ci/run-buildbot ${{ matrix.config }} diff --git a/.github/workflows/libcxx-check-generated-files.yml b/.github/workflows/libcxx-check-generated-files.yml deleted file mode 100644 index 570055624b2a..000000000000 --- a/.github/workflows/libcxx-check-generated-files.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: "Check libc++ generated files" -on: - pull_request: - paths: - - 'libcxx/**' - -permissions: - contents: read - -jobs: - check_generated_files: - runs-on: ubuntu-latest - steps: - - name: Fetch LLVM sources - uses: actions/checkout@v4 - - - name: Install dependencies - uses: aminya/setup-cpp@v1 - with: - clangformat: 17.0.1 - ninja: true - - - name: Check generated files - run: libcxx/utils/ci/run-buildbot check-generated-output diff --git a/.github/workflows/libcxx-restart-preempted-jobs.yaml b/.github/workflows/libcxx-restart-preempted-jobs.yaml deleted file mode 100644 index 21879ce19c27..000000000000 --- a/.github/workflows/libcxx-restart-preempted-jobs.yaml +++ /dev/null @@ -1,132 +0,0 @@ -name: Restart Preempted Libc++ Workflow - -# The libc++ builders run on preemptable VMs, which can be shutdown at any time. -# This workflow identifies when a workflow run was canceled due to the VM being preempted, -# and restarts the workflow run. - -# We identify a canceled workflow run by checking the annotations of the check runs in the check suite, -# which should contain the message "The runner has received a shutdown signal." - -# Note: If a job is both preempted and also contains a non-preemption failure, we do not restart the workflow. - -on: - workflow_run: - workflows: [Build and Test libc\+\+] - types: - - completed - -permissions: - contents: read - -jobs: - restart: - if: github.repository_owner == 'llvm' && (github.event.workflow_run.conclusion == 'failure' || github.event.workflow_run.conclusion == 'cancelled') - name: "Restart Job" - permissions: - statuses: read - checks: write - actions: write - runs-on: ubuntu-latest - steps: - - name: "Restart Job" - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea #v7.0.1 - with: - script: | - const failure_regex = /Process completed with exit code 1./ - const preemption_regex = /The runner has received a shutdown signal/ - - const wf_run = context.payload.workflow_run - core.notice(`Running on "${wf_run.display_title}" by @${wf_run.actor.login} (event: ${wf_run.event})\nWorkflow run URL: ${wf_run.html_url}`) - - - async function create_check_run(conclusion, message) { - // Create a check run on the given workflow run to indicate if - // we are restarting the workflow or not. - if (conclusion != 'success' && conclusion != 'skipped' && conclusion != 'neutral') { - core.setFailed('Invalid conclusion: ' + conclusion) - } - await github.rest.checks.create({ - owner: context.repo.owner, - repo: context.repo.repo, - name: 'Restart Preempted Job', - head_sha: wf_run.head_sha, - status: 'completed', - conclusion: conclusion, - output: { - title: 'Restarted Preempted Job', - summary: message - } - }) - } - - console.log('Listing check runs for suite') - const check_suites = await github.rest.checks.listForSuite({ - owner: context.repo.owner, - repo: context.repo.repo, - check_suite_id: context.payload.workflow_run.check_suite_id, - per_page: 100 // FIXME: We don't have 100 check runs yet, but we should handle this better. - }) - - check_run_ids = []; - for (check_run of check_suites.data.check_runs) { - console.log('Checking check run: ' + check_run.id); - if (check_run.status != 'completed') { - console.log('Check run was not completed. Skipping.'); - continue; - } - if (check_run.conclusion != 'failure' && check_run.conclusion != 'cancelled') { - console.log('Check run had conclusion: ' + check_run.conclusion + '. Skipping.'); - continue; - } - check_run_ids.push(check_run.id); - } - - has_preempted_job = false; - - for (check_run_id of check_run_ids) { - console.log('Listing annotations for check run: ' + check_run_id); - - annotations = await github.rest.checks.listAnnotations({ - owner: context.repo.owner, - repo: context.repo.repo, - check_run_id: check_run_id - }) - - for (annotation of annotations.data) { - if (annotation.annotation_level != 'failure') { - continue; - } - - const preemption_match = annotation.message.match(preemption_regex); - - if (preemption_match != null) { - console.log('Found preemption message: ' + annotation.message); - has_preempted_job = true; - } - - const failure_match = annotation.message.match(failure_regex); - if (failure_match != null) { - // We only want to restart the workflow if all of the failures were due to preemption. - // We don't want to restart the workflow if there were other failures. - core.notice('Choosing not to rerun workflow because we found a non-preemption failure' + - 'Failure message: "' + annotation.message + '"'); - await create_check_run('skipped', 'Choosing not to rerun workflow because we found a non-preemption failure\n' - + 'Failure message: ' + annotation.message) - return; - } - } - } - - if (!has_preempted_job) { - core.notice('No preempted jobs found. Not restarting workflow.'); - await create_check_run('neutral', 'No preempted jobs found. Not restarting workflow.') - return; - } - - core.notice("Restarted workflow: " + context.payload.workflow_run.id); - await github.rest.actions.reRunWorkflowFailedJobs({ - owner: context.repo.owner, - repo: context.repo.repo, - run_id: context.payload.workflow_run.id - }) - await create_check_run('success', 'Restarted workflow run due to preempted job') diff --git a/.github/workflows/lld-tests.yml b/.github/workflows/lld-tests.yml deleted file mode 100644 index 599c0975fa68..000000000000 --- a/.github/workflows/lld-tests.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: LLD Tests - -permissions: - contents: read - -on: - workflow_dispatch: - push: - branches: - - 'release/**' - paths: - - 'lld/**' - - '.github/workflows/lld-tests.yml' - - '.github/workflows/llvm-project-tests.yml' - - '!llvm/**' - pull_request: - branches: - - 'release/**' - paths: - - 'lld/**' - - '.github/workflows/lld-tests.yml' - - '.github/workflows/llvm-project-tests.yml' - - '!llvm/**' - -concurrency: - # Skip intermediate builds: always. - # Cancel intermediate builds: only if it is a pull request build. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} - -jobs: - check_lld: - if: github.repository_owner == 'llvm' - name: Test lld - uses: ./.github/workflows/llvm-project-tests.yml - with: - build_target: check-lld - projects: lld diff --git a/.github/workflows/lldb-tests.yml b/.github/workflows/lldb-tests.yml deleted file mode 100644 index 6bb972195625..000000000000 --- a/.github/workflows/lldb-tests.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: lldb Tests - -permissions: - contents: read - -on: - workflow_dispatch: - push: - branches: - - 'release/**' - paths: - - 'lldb/**' - - '.github/workflows/lldb-tests.yml' - - '.github/workflows/llvm-project-tests.yml' - - '!clang/**' - - '!llvm/**' - pull_request: - branches: - - 'release/**' - paths: - - 'lldb/**' - - '.github/workflows/lldb-tests.yml' - - '.github/workflows/llvm-project-tests.yml' - - '!clang/**' - - '!llvm/**' - -concurrency: - # Skip intermediate builds: always. - # Cancel intermediate builds: only if it is a pull request build. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} - -jobs: - build_lldb: - if: github.repository_owner == 'llvm' - name: Build lldb - uses: ./.github/workflows/llvm-project-tests.yml - with: - projects: clang;lldb diff --git a/.github/workflows/llvm-bugs.yml b/.github/workflows/llvm-bugs.yml deleted file mode 100644 index c392078fa452..000000000000 --- a/.github/workflows/llvm-bugs.yml +++ /dev/null @@ -1,63 +0,0 @@ -name: LLVM Bugs notifier - -permissions: - contents: read - issues: read - -on: - issues: - types: - - opened - -jobs: - auto-subscribe: - runs-on: ubuntu-latest - if: github.repository == 'llvm/llvm-project' - steps: - - uses: actions/setup-node@v4 - with: - node-version: 18 - check-latest: true - - run: npm install mailgun.js form-data - - name: Send notification - uses: actions/github-script@v6 - env: - MAILGUN_API_KEY: ${{ secrets.LLVM_BUGS_KEY }} - with: - script: | - const Mailgun = require('mailgun.js'); - const formData = require('form-data'); - - const mailgun = new Mailgun(formData); - const DOMAIN = 'email.llvm.org'; - - const mg = mailgun.client({ username: 'api', key: process.env.MAILGUN_API_KEY }); - - github.rest.issues.get({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo - }) - .then((issue) => { - const payload = { - author : issue.data.user.login, - issue : issue.data.number, - title : issue.data.title, - url : issue.data.html_url, - labels : issue.data.labels.map((label) => label.name), - assignee : issue.data.assignees.map((assignee) => assignee.login), - body : issue.data.body - }; - - const data = { - from: 'LLVM Bugs ', - to: 'llvm-bugs@lists.llvm.org', - subject: `[Bug ${issue.data.number}] ${issue.data.title}`, - template: 'new-github-issue', - 'o:tracking-clicks': 'no', - 'h:X-Mailgun-Variables': JSON.stringify(payload) - }; - - return mg.messages.create(DOMAIN, data); - }) - .then((msg) => console.log(msg)); diff --git a/.github/workflows/llvm-project-tests.yml b/.github/workflows/llvm-project-tests.yml deleted file mode 100644 index 17a54be16bad..000000000000 --- a/.github/workflows/llvm-project-tests.yml +++ /dev/null @@ -1,148 +0,0 @@ -name: LLVM Project Tests - -permissions: - contents: read - -on: - workflow_dispatch: - inputs: - build_target: - required: false - projects: - required: false - extra_cmake_args: - required: false - os_list: - required: false - default: '["ubuntu-latest", "windows-2019", "macOS-13"]' - python_version: - required: false - type: string - default: '3.11' - workflow_call: - inputs: - build_target: - required: false - type: string - default: "all" - - projects: - required: true - type: string - - extra_cmake_args: - required: false - type: string - - os_list: - required: false - type: string - # Use windows-2019 due to: - # https://developercommunity.visualstudio.com/t/Prev-Issue---with-__assume-isnan-/1597317 - default: '["ubuntu-latest", "windows-2019", "macOS-13"]' - - python_version: - required: false - type: string - default: '3.11' - -concurrency: - # Skip intermediate builds: always. - # Cancel intermediate builds: only if it is a pull request build. - # If the group name here is the same as the group name in the workflow that includes - # this one, then the action will try to wait on itself and get stuck. - group: llvm-project-${{ github.workflow }}-${{ inputs.projects }}${{ github.ref }} - cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} - -jobs: - lit-tests: - name: Lit Tests - runs-on: ${{ matrix.os }} - container: - image: ${{(startsWith(matrix.os, 'ubuntu') && 'ghcr.io/llvm/ci-ubuntu-22.04:latest') || null}} - volumes: - - /mnt/:/mnt/ - strategy: - fail-fast: false - matrix: - os: ${{ fromJSON(inputs.os_list) }} - steps: - - name: Setup Windows - if: startsWith(matrix.os, 'windows') - uses: llvm/actions/setup-windows@main - with: - arch: amd64 - # On Windows, starting with win19/20220814.1, cmake choose the 32-bit - # python3.10.6 libraries instead of the 64-bit libraries when building - # lldb. Using this setup-python action to make 3.10 the default - # python fixes this. - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: ${{ inputs.python_version }} - - name: Install Ninja - if: runner.os != 'Linux' - uses: llvm/actions/install-ninja@main - # actions/checkout deletes any existing files in the new git directory, - # so this needs to either run before ccache-action or it has to use - # clean: false. - - uses: actions/checkout@v4 - with: - fetch-depth: 250 - - name: Setup ccache - uses: hendrikmuhs/ccache-action@v1 - with: - # A full build of llvm, clang, lld, and lldb takes about 250MB - # of ccache space. There's not much reason to have more than this, - # because we usually won't need to save cache entries from older - # builds. Also, there is an overall 10GB cache limit, and each - # run creates a new cache entry so we want to ensure that we have - # enough cache space for all the tests to run at once and still - # fit under the 10 GB limit. - # Default to 2G to workaround: https://github.com/hendrikmuhs/ccache-action/issues/174 - max-size: 2G - key: ${{ matrix.os }} - variant: sccache - - name: Build and Test - env: - # Workaround for https://github.com/actions/virtual-environments/issues/5900. - # This should be a no-op for non-mac OSes - PKG_CONFIG_PATH: /usr/local/Homebrew/Library/Homebrew/os/mac/pkgconfig//12 - shell: bash - id: build-llvm - run: | - if [ "${{ runner.os }}" == "Linux" ]; then - builddir="/mnt/build/" - mkdir -p $builddir - extra_cmake_args="-DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang" - else - builddir="$(pwd)"/build - fi - if [ "${{ runner.os }}" == "macOS" ]; then - # Workaround test failure on some lld tests on MacOS - # https://github.com/llvm/llvm-project/issues/81967 - extra_cmake_args="-DLLVM_DISABLE_ASSEMBLY_FILES=ON" - fi - echo "llvm-builddir=$builddir" >> "$GITHUB_OUTPUT" - cmake -G Ninja \ - -B "$builddir" \ - -S llvm \ - -DLLVM_ENABLE_PROJECTS="${{ inputs.projects }}" \ - -DCMAKE_BUILD_TYPE=Release \ - -DLLVM_ENABLE_ASSERTIONS=ON \ - -DLLDB_INCLUDE_TESTS=OFF \ - -DLIBCLC_TARGETS_TO_BUILD="amdgcn--;amdgcn--amdhsa;r600--;nvptx--;nvptx64--;nvptx--nvidiacl;nvptx64--nvidiacl" \ - -DCMAKE_C_COMPILER_LAUNCHER=sccache \ - -DCMAKE_CXX_COMPILER_LAUNCHER=sccache \ - $extra_cmake_args \ - ${{ inputs.extra_cmake_args }} - ninja -C "$builddir" '${{ inputs.build_target }}' - - - name: Build and Test libclc - if: "!startsWith(matrix.os, 'windows') && contains(inputs.projects, 'libclc')" - env: - LLVM_BUILDDIR: ${{ steps.build-llvm.outputs.llvm-builddir }} - run: | - # The libclc tests don't have a generated check target so all we can - # do is build it. - ninja -C "$LLVM_BUILDDIR" diff --git a/.github/workflows/llvm-project-workflow-tests.yml b/.github/workflows/llvm-project-workflow-tests.yml deleted file mode 100644 index a2539b279be0..000000000000 --- a/.github/workflows/llvm-project-workflow-tests.yml +++ /dev/null @@ -1,32 +0,0 @@ -# This workflow will test the llvm-project-tests workflow in PRs -# targetting the main branch. Since this workflow doesn't normally -# run on main PRs, we need some way to test it to ensure new updates -# don't break it. - -name: LLVM Workflow Test - -permissions: - contents: read - -on: - pull_request: - branches: - - 'main' - paths: - - '.github/workflows/llvm-project-tests.yml' - - '.github/workflows/llvm-project-workflow-tests.yml' - -concurrency: - # Skip intermediate builds: always. - # Cancel intermediate builds: only if it is a pull request build. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} - -jobs: - llvm-test: - if: github.repository_owner == 'llvm' - name: Build and Test - uses: ./.github/workflows/llvm-project-tests.yml - with: - build_target: check-all - projects: clang;lld;libclc;lldb diff --git a/.github/workflows/llvm-tests.yml b/.github/workflows/llvm-tests.yml deleted file mode 100644 index 26e644229aaa..000000000000 --- a/.github/workflows/llvm-tests.yml +++ /dev/null @@ -1,192 +0,0 @@ -name: LLVM Tests - -permissions: - contents: read - -on: - workflow_dispatch: - push: - branches: - - 'release/**' - paths: - - 'llvm/**' - - '.github/workflows/llvm-tests.yml' - - '.github/workflows/llvm-project-tests.yml' - pull_request: - branches: - - 'release/**' - paths: - - 'llvm/**' - - '.github/workflows/llvm-tests.yml' - - '.github/workflows/llvm-project-tests.yml' - -concurrency: - # Skip intermediate builds: always. - # Cancel intermediate builds: only if it is a pull request build. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} - -jobs: - check-all: - if: github.repository_owner == 'llvm' - name: Build and Test - uses: ./.github/workflows/llvm-project-tests.yml - with: - build_target: check-all - projects: clang;lld;libclc;lldb - - abi-dump-setup: - if: github.repository_owner == 'llvm' - runs-on: ubuntu-latest - outputs: - BASELINE_REF: ${{ steps.vars.outputs.BASELINE_REF }} - ABI_HEADERS: ${{ steps.vars.outputs.ABI_HEADERS }} - BASELINE_VERSION_MAJOR: ${{ steps.vars.outputs.BASELINE_VERSION_MAJOR }} - BASELINE_VERSION_MINOR: ${{ steps.vars.outputs.BASELINE_VERSION_MINOR }} - LLVM_VERSION_MAJOR: ${{ steps.version.outputs.major }} - LLVM_VERSION_MINOR: ${{ steps.version.outputs.minor }} - LLVM_VERSION_PATCH: ${{ steps.version.outputs.patch }} - steps: - - name: Checkout source - uses: actions/checkout@v4 - with: - fetch-depth: 250 - - - name: Get LLVM version - id: version - uses: ./.github/workflows/get-llvm-version - - - name: Setup Variables - id: vars - run: | - # C++ ABI: - # 18.1.0 we aren't doing ABI checks. - # 18.1.1 We want to check 18.1.0. - # C ABI: - # 18.1.0 We want to check 17.0.x - # 18.1.1 We want to check 18.1.0 - echo "BASELINE_VERSION_MINOR=1" >> "$GITHUB_OUTPUT" - if [ ${{ steps.version.outputs.patch }} -eq 0 ]; then - { - echo "BASELINE_VERSION_MAJOR=$(( ${{ steps.version.outputs.major }} - 1))" - echo "ABI_HEADERS=llvm-c" - } >> "$GITHUB_OUTPUT" - else - { - echo "BASELINE_VERSION_MAJOR=${{ steps.version.outputs.major }}" - echo "ABI_HEADERS=." - } >> "$GITHUB_OUTPUT" - fi - - abi-dump: - if: github.repository_owner == 'llvm' - needs: abi-dump-setup - runs-on: ubuntu-latest - strategy: - matrix: - name: - - build-baseline - - build-latest - include: - - name: build-baseline - llvm_version_major: ${{ needs.abi-dump-setup.outputs.BASELINE_VERSION_MAJOR }} - ref: llvmorg-${{ needs.abi-dump-setup.outputs.BASELINE_VERSION_MAJOR }}.${{ needs.abi-dump-setup.outputs.BASELINE_VERSION_MINOR }}.0 - repo: llvm/llvm-project - - name: build-latest - llvm_version_major: ${{ needs.abi-dump-setup.outputs.LLVM_VERSION_MAJOR }} - ref: ${{ github.sha }} - repo: ${{ github.repository }} - steps: - - name: Install Ninja - uses: llvm/actions/install-ninja@main - - name: Install abi-compliance-checker - run: | - sudo apt-get install abi-dumper autoconf pkg-config - - name: Install universal-ctags - run: | - git clone https://github.com/universal-ctags/ctags.git - cd ctags - ./autogen.sh - ./configure - sudo make install - - name: Download source code - uses: llvm/actions/get-llvm-project-src@main - with: - ref: ${{ matrix.ref }} - repo: ${{ matrix.repo }} - - name: Configure - run: | - mkdir install - cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DLLVM_TARGETS_TO_BUILD="" -DLLVM_BUILD_LLVM_DYLIB=ON -DCMAKE_C_FLAGS_DEBUG="-g1 -Og" -DCMAKE_CXX_FLAGS_DEBUG="-g1 -Og" -DCMAKE_INSTALL_PREFIX="$(pwd)"/install llvm - - name: Build - # Need to run install-LLVM twice to ensure the symlink is installed (this is a bug). - run: | - ninja -C build install-LLVM - ninja -C build install-LLVM - ninja -C build install-llvm-headers - - name: Dump ABI - run: | - if [ "${{ needs.abi-dump-setup.outputs.ABI_HEADERS }}" = "llvm-c" ]; then - nm ./install/lib/libLLVM.so | awk "/T _LLVM/ || /T LLVM/ { print $3 }" | sort -u | sed -e "s/^_//g" | cut -d ' ' -f 3 > llvm.symbols - # Even though the -symbols-list option doesn't seem to filter out the symbols, I believe it speeds up processing, so I'm leaving it in. - export EXTRA_ARGS="-symbols-list llvm.symbols" - else - touch llvm.symbols - fi - abi-dumper $EXTRA_ARGS -lver ${{ matrix.ref }} -skip-cxx -public-headers ./install/include/${{ needs.abi-dump-setup.outputs.ABI_HEADERS }} -o ${{ matrix.ref }}.abi ./install/lib/libLLVM.so - # Remove symbol versioning from dumps, so we can compare across major versions. - sed -i 's/LLVM_${{ matrix.llvm_version_major }}/LLVM_NOVERSION/' ${{ matrix.ref }}.abi - - name: Upload ABI file - uses: actions/upload-artifact@v3 - with: - name: ${{ matrix.name }} - path: ${{ matrix.ref }}.abi - - - name: Upload symbol list file - if: matrix.name == 'build-baseline' - uses: actions/upload-artifact@v3 - with: - name: symbol-list - path: llvm.symbols - - abi-compare: - if: github.repository_owner == 'llvm' - runs-on: ubuntu-latest - needs: - - abi-dump-setup - - abi-dump - steps: - - name: Download baseline - uses: actions/download-artifact@v3 - with: - name: build-baseline - path: build-baseline - - name: Download latest - uses: actions/download-artifact@v3 - with: - name: build-latest - path: build-latest - - name: Download symbol list - uses: actions/download-artifact@v3 - with: - name: symbol-list - path: symbol-list - - - name: Install abi-compliance-checker - run: sudo apt-get install abi-compliance-checker - - name: Compare ABI - run: | - if [ -s symbol-list/llvm.symbols ]; then - # This option doesn't seem to work with the ABI dumper, so passing it here. - export EXTRA_ARGS="-symbols-list symbol-list/llvm.symbols" - fi - # FIXME: Reading of gzip'd abi files on the GitHub runners stop - # working some time in March of 2021, likely due to a change in the - # runner's environment. - abi-compliance-checker $EXTRA_ARGS -l libLLVM.so -old build-baseline/*.abi -new build-latest/*.abi || test "${{ needs.abi-dump-setup.outputs.ABI_HEADERS }}" = "llvm-c" - - name: Upload ABI Comparison - if: always() - uses: actions/upload-artifact@v3 - with: - name: compat-report-${{ github.sha }} - path: compat_reports/ diff --git a/.github/workflows/merged-prs.yml b/.github/workflows/merged-prs.yml deleted file mode 100644 index e29afd4097f9..000000000000 --- a/.github/workflows/merged-prs.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: "Add buildbot information to first PRs from new contributors" - -permissions: - contents: read - -on: - # It's safe to use pull_request_target here, because we aren't checking out - # code from the pull request branch. - # See https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ - pull_request_target: - types: - - closed - -jobs: - buildbot_comment: - runs-on: ubuntu-latest - permissions: - pull-requests: write - if: >- - (github.repository == 'llvm/llvm-project') && - (github.event.pull_request.merged == true) - steps: - - name: Checkout Automation Script - uses: actions/checkout@v4 - with: - sparse-checkout: llvm/utils/git/ - ref: main - - - name: Setup Automation Script - working-directory: ./llvm/utils/git/ - run: | - pip install --require-hashes -r requirements.txt - - - name: Add Buildbot information comment - working-directory: ./llvm/utils/git/ - run: | - python3 ./github-automation.py \ - --token '${{ secrets.GITHUB_TOKEN }}' \ - pr-buildbot-information \ - --issue-number "${{ github.event.pull_request.number }}" \ - --author "${{ github.event.pull_request.user.login }}" diff --git a/.github/workflows/new-issues.yml b/.github/workflows/new-issues.yml deleted file mode 100644 index ed15fdb9fba6..000000000000 --- a/.github/workflows/new-issues.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Labeling new issues -on: - issues: - types: ['opened'] - -permissions: - contents: read - -jobs: - automate-issues-labels: - permissions: - issues: write - runs-on: ubuntu-latest - if: github.repository == 'llvm/llvm-project' - steps: - - uses: llvm/actions/issue-labeler@main - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - configuration-path: .github/new-issues-labeler.yml - include-title: 1 - include-body: 0 - sync-labels: 0 - enable-versioned-regex: 0 diff --git a/.github/workflows/new-prs.yml b/.github/workflows/new-prs.yml deleted file mode 100644 index 88175d6f8d64..000000000000 --- a/.github/workflows/new-prs.yml +++ /dev/null @@ -1,75 +0,0 @@ -name: "Labelling new pull requests" - -permissions: - contents: read - -on: - # It's safe to use pull_request_target here, because we aren't checking out - # code from the pull request branch. - # See https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ - pull_request_target: - types: - - opened - - reopened - - ready_for_review - - synchronize - -jobs: - greeter: - runs-on: ubuntu-latest - permissions: - pull-requests: write - # Only comment on PRs that have been opened for the first time, by someone - # new to LLVM or to GitHub as a whole. Ideally we'd look for FIRST_TIMER - # or FIRST_TIME_CONTRIBUTOR, but this does not appear to work. Instead check - # that we do not have any of the other author associations. - # See https://docs.github.com/en/webhooks/webhook-events-and-payloads?actionType=opened#pull_request - # for all the possible values. - if: >- - (github.repository == 'llvm/llvm-project') && - (github.event.action == 'opened') && - (github.event.pull_request.author_association != 'COLLABORATOR') && - (github.event.pull_request.author_association != 'CONTRIBUTOR') && - (github.event.pull_request.author_association != 'MANNEQUIN') && - (github.event.pull_request.author_association != 'MEMBER') && - (github.event.pull_request.author_association != 'OWNER') - steps: - - name: Checkout Automation Script - uses: actions/checkout@v4 - with: - sparse-checkout: llvm/utils/git/ - ref: main - - - name: Setup Automation Script - working-directory: ./llvm/utils/git/ - run: | - pip install --require-hashes -r requirements.txt - - - name: Greet Author - working-directory: ./llvm/utils/git/ - run: | - python3 ./github-automation.py \ - --token '${{ secrets.GITHUB_TOKEN }}' \ - pr-greeter \ - --issue-number "${{ github.event.pull_request.number }}" - - automate-prs-labels: - # Greet first so that only the author gets that notification. - needs: greeter - runs-on: ubuntu-latest - # Ignore PRs with more than 10 commits. Pull requests with a lot of - # commits tend to be accidents usually when someone made a mistake while trying - # to rebase. We want to ignore these pull requests to avoid excessive - # notifications. - # always() means that even if greeter is skipped, this job will run. - if: > - always() && github.repository == 'llvm/llvm-project' && - github.event.pull_request.draft == false && - github.event.pull_request.commits < 10 - steps: - - uses: actions/labeler@v4 - with: - configuration-path: .github/new-prs-labeler.yml - # workaround for https://github.com/actions/labeler/issues/112 - sync-labels: '' - repo-token: ${{ secrets.ISSUE_SUBSCRIBER_TOKEN }} diff --git a/.github/workflows/pr-code-format.yml b/.github/workflows/pr-code-format.yml deleted file mode 100644 index 22357e5d99e4..000000000000 --- a/.github/workflows/pr-code-format.yml +++ /dev/null @@ -1,96 +0,0 @@ -name: "Check code formatting" - -permissions: - contents: read - -on: - pull_request: - branches: - - main - -jobs: - code_formatter: - runs-on: ubuntu-latest - if: github.repository == 'llvm/llvm-project' - steps: - - name: Fetch LLVM sources - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.sha }} - - - name: Checkout through merge base - uses: rmacklin/fetch-through-merge-base@v0 - with: - base_ref: ${{ github.event.pull_request.base.ref }} - head_ref: ${{ github.event.pull_request.head.sha }} - deepen_length: 500 - - - name: Get changed files - id: changed-files - uses: tj-actions/changed-files@v39 - with: - separator: "," - skip_initial_fetch: true - - # We need to pull the script from the main branch, so that we ensure - # we get the latest version of this script. - - name: Fetch code formatting utils - uses: actions/checkout@v4 - with: - repository: ${{ github.repository }} - ref: ${{ github.base_ref }} - sparse-checkout: | - llvm/utils/git/requirements_formatting.txt - llvm/utils/git/code-format-helper.py - sparse-checkout-cone-mode: false - path: code-format-tools - - - name: "Listed files" - env: - CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }} - run: | - echo "Formatting files:" - echo "$CHANGED_FILES" - - - name: Install clang-format - uses: aminya/setup-cpp@v1 - with: - clangformat: 18.1.7 - - - name: Setup Python env - uses: actions/setup-python@v5 - with: - python-version: '3.11' - cache: 'pip' - cache-dependency-path: 'code-format-tools/llvm/utils/git/requirements_formatting.txt' - - - name: Install python dependencies - run: pip install -r code-format-tools/llvm/utils/git/requirements_formatting.txt - - - name: Run code formatter - env: - GITHUB_PR_NUMBER: ${{ github.event.pull_request.number }} - START_REV: ${{ github.event.pull_request.base.sha }} - END_REV: ${{ github.event.pull_request.head.sha }} - CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }} - # TODO(boomanaiden154): Once clang v18 is released, we should be able - # to take advantage of the new --diff_from_common_commit option - # explicitly in code-format-helper.py and not have to diff starting at - # the merge base. - # Create an empty comments file so the pr-write job doesn't fail. - run: | - echo "[]" > comments && - python ./code-format-tools/llvm/utils/git/code-format-helper.py \ - --write-comment-to-file \ - --token ${{ secrets.GITHUB_TOKEN }} \ - --issue-number $GITHUB_PR_NUMBER \ - --start-rev $(git merge-base $START_REV $END_REV) \ - --end-rev $END_REV \ - --changed-files "$CHANGED_FILES" - - - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 - if: always() - with: - name: workflow-args - path: | - comments diff --git a/.github/workflows/pr-request-release-note.yml b/.github/workflows/pr-request-release-note.yml deleted file mode 100644 index 2fa501dda16b..000000000000 --- a/.github/workflows/pr-request-release-note.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: PR Request Release Note - -permissions: - contents: read - -on: - pull_request: - types: - - closed - -jobs: - request-release-note: - if: >- - github.repository_owner == 'llvm' && - startsWith(github.ref, 'refs/heads/release') - - runs-on: ubuntu-latest - steps: - # We need to pull the script from the main branch, so that we ensure - # we get the latest version of this script. - - name: Checkout Scripts - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - sparse-checkout: | - llvm/utils/git/requirements.txt - llvm/utils/git/github-automation.py - sparse-checkout-cone-mode: false - - - name: Install Dependencies - run: | - pip install --require-hashes -r llvm/utils/git/requirements.txt - - - name: Request Release Note - env: - # We need to use an llvmbot token here, because we are mentioning a user. - GITHUB_TOKEN: ${{ github.token }} - run: | - python3 llvm/utils/git/github-automation.py \ - --repo "$GITHUB_REPOSITORY" \ - --token "$GITHUB_TOKEN" \ - request-release-note \ - --pr-number ${{ github.event.pull_request.number}} - - - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 - if: always() - with: - name: workflow-args - path: | - comments diff --git a/.github/workflows/pr-subscriber.yml b/.github/workflows/pr-subscriber.yml deleted file mode 100644 index 272d3e2f9ef8..000000000000 --- a/.github/workflows/pr-subscriber.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: PR Subscriber - -on: - pull_request_target: - types: - - labeled - -permissions: - contents: read - -jobs: - auto-subscribe: - runs-on: ubuntu-latest - if: github.repository == 'llvm/llvm-project' - steps: - - name: Checkout Automation Script - uses: actions/checkout@v4 - with: - sparse-checkout: llvm/utils/git/ - ref: main - - - name: Setup Automation Script - working-directory: ./llvm/utils/git/ - run: | - pip install --require-hashes -r requirements.txt - - - name: Update watchers - working-directory: ./llvm/utils/git/ - run: | - python3 ./github-automation.py \ - --token '${{ secrets.ISSUE_SUBSCRIBER_TOKEN }}' \ - pr-subscriber \ - --issue-number "${{ github.event.number }}" \ - --label-name "${{ github.event.label.name }}" diff --git a/.github/workflows/regression-tests.yml b/.github/workflows/regression-tests.yml new file mode 100644 index 000000000000..f3c2fd6ce63b --- /dev/null +++ b/.github/workflows/regression-tests.yml @@ -0,0 +1,197 @@ +name: Regression tests + +on: + pull_request: + branches: + - main + workflow_dispatch: + inputs: + commits-to-test: + description: "Number of commits to test" + required: true + default: '1' + # For more information about the supported sanitizers in LLVM, see `LLVM_USE_SANITIZER` option in: + # https://www.llvm.org/docs/CMake.html + llvm-sanitizer: + required: false + default: 'Address' + type: string + description: 'A sanitizer to build LLVM with. Possible values are Address, Memory, MemoryWithOrigins, Undefined, Thread, DataFlow, and Address;Undefined' + enable-valgrind: + required: false + default: 'false' + type: string + description: 'Run LLVM regression tests under valgrind.' + +defaults: + run: + shell: bash + +concurrency: + group: ${{ github.repository_id }}-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + + target-machine: + runs-on: ubuntu-latest + outputs: + evm: ${{ steps.evm.outputs.machine }} + eravm: ${{ steps.eravm.outputs.machine }} + default: ${{ steps.default.outputs.machine }} + steps: + + - name: Check for EraVM target + id: eravm + if: contains(github.event.pull_request.title, '[eravm]') || contains(github.event.pull_request.title, '[EraVM]') + run: echo "machine=eravm" | tee -a "${GITHUB_OUTPUT}" + + - name: Check for EVM target + id: evm + if: contains(github.event.pull_request.title, '[evm]') || contains(github.event.pull_request.title, '[EVM]') + run: echo "machine=evm" | tee -a "${GITHUB_OUTPUT}" + + - name: Check for default target + id: default + shell: bash -ex {0} + run: | + if [[ "${{ join(steps.*.outputs.*) }}" == "" ]]; then + echo "machine=default" | tee -a "${GITHUB_OUTPUT}" + fi + + prepare-test-matrix: + needs: target-machine + runs-on: ubuntu-latest + outputs: + matrix-formatting: ${{ steps.prepare.outputs.matrix-formatting }} + matrix-tests: ${{ steps.prepare.outputs.matrix-tests }} + fetch_depth: ${{ steps.extract_branch.outputs.fetch_depth }} + + steps: + + - name: Extract branch name + id: extract_branch + run: | + echo "head_ref=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" | tee -a "${GITHUB_OUTPUT}" + echo "base_ref=${{ github.base_ref || github.event.repository.default_branch }}" | tee -a "${GITHUB_OUTPUT}" + echo "fetch_depth=$(( ${{ github.event.pull_request.commits || github.event.inputs.commits-to-test }} + 1 ))" | tee -a "${GITHUB_OUTPUT}" + + - uses: actions/checkout@v4 + with: + ref: ${{ steps.extract_branch.outputs.head_ref }} + fetch-depth: ${{ github.event.pull_request.commits || github.event.inputs.commits-to-test }} + repository: ${{ github.event.pull_request.head.repo.full_name }} + + - name: Prepare commits + id: prepare + shell: bash -ex {0} + env: + TEST_COMMITS_LIMIT: 25 # Limit the number of commits to test + run: | + COMMITS_TO_TEST=$(git log -n ${{ github.event.pull_request.commits || github.event.inputs.commits-to-test }} --pretty='"%H"' | head -n ${TEST_COMMITS_LIMIT}) + echo "matrix-formatting={ \"commit\": [${COMMITS_TO_TEST//$'\n'/, }] }" | tee -a "${GITHUB_OUTPUT}" + TARGETS=$(echo '${{ toJSON(needs.target-machine.outputs) }}' | jq '.[]') + echo "matrix-tests={ \"commit\": [${COMMITS_TO_TEST//$'\n'/, }], \"target\": [${TARGETS//$'\n'/, }] }" | tee -a "${GITHUB_OUTPUT}" + + check-formatting: + if: github.event_name == 'pull_request' + runs-on: [ci-runner-compiler, Linux] + container: + image: ghcr.io/matter-labs/zksync-llvm-runner:latest + options: -m 110g + needs: prepare-test-matrix + strategy: + fail-fast: false + max-parallel: 3 + matrix: ${{ fromJson(needs.prepare-test-matrix.outputs.matrix-formatting) }} + steps: + + - uses: actions/checkout@v4 + with: + ref: ${{ matrix.commit }} + fetch-depth: ${{ needs.prepare-test-matrix.outputs.fetch_depth }} + repository: ${{ github.event.pull_request.head.repo.full_name }} + + - name: Define previous commit + id: previous-commit + run: echo "sha=$(git show --quiet --pretty='%H' ${{ matrix.commit }}^1)" | tee -a "${GITHUB_OUTPUT}" + + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@2f7c5bfce28377bc069a65ba478de0a74aa0ca32 # v46.0.1 + with: + separator: "," + sha: ${{ matrix.commit }} + base_sha: ${{ steps.previous-commit.outputs.sha }} + + - name: Install formatting requirements + run: python3 -m pip install -r ./llvm/utils/git/requirements_formatting_era_llvm.txt + + - name: Run code formatter + run: | + set -x + if [ '${{ github.event.pull_request.head.repo.fork }}' = 'true' ]; then + TOKEN_PARAM="" + else + TOKEN_PARAM="--token ${{ secrets.GITHUB_TOKEN }}" + fi + python3 ./llvm/utils/git/code-format-helper-era-llvm.py ${TOKEN_PARAM} \ + --issue-number ${{ github.event.pull_request.number }} \ + --start-rev ${{ steps.previous-commit.outputs.sha }} \ + --end-rev ${{ matrix.commit }} \ + --changed-files ${{ steps.changed-files.outputs.all_changed_files }} + + regression-tests: + runs-on: [ci-runner-compiler, Linux] + container: + image: ghcr.io/matter-labs/zksync-llvm-runner:latest + options: -m 110g + needs: prepare-test-matrix + strategy: + fail-fast: false + max-parallel: 3 + matrix: ${{ fromJson(needs.prepare-test-matrix.outputs.matrix-tests) }} + steps: + + - uses: actions/checkout@v4 + with: + ref: ${{ matrix.commit }} + fetch-depth: ${{ needs.prepare-test-matrix.outputs.fetch_depth }} + path: "llvm" + repository: ${{ github.event.pull_request.head.repo.full_name }} + + - name: Define previous commit + if: github.event_name == 'pull_request' + working-directory: llvm + id: previous-commit + run: echo "sha=$(git show --quiet --pretty='%H' ${{ matrix.commit }}^1)" | tee -a "${GITHUB_OUTPUT}" + + - name: Build LLVM + uses: matter-labs/era-compiler-ci/.github/actions/build-llvm@v1 + with: + default-target-triple: ${{ matrix.target }} + enable-tests: true + enable-assertions: true + clone-llvm: false + ccache-key-type: 'static' # rotate ccache key every month + sanitizer: ${{ inputs.llvm-sanitizer }} + enable-valgrind: ${{ inputs.enable-valgrind }} + + # `verify-llvm` is a special target that runs all the regression tests + # it includes `check-llvm` and special codegen tests + # called via `verify-llvm-codegen-opt` and `verify-llvm-codegen-llc` targets + - name: Running Lit tests + run: | + ninja -C './target-llvm/build-final' verify-llvm -v + ninja -C './target-llvm/build-final' check-lld-unit -v + + - name: clang-tidy + if: github.event_name == 'pull_request' + working-directory: llvm + run: | + CHANGES=$(git diff -U0 ${{ steps.previous-commit.outputs.sha }}) + echo "Running clang-tidy on the following changes: ${CHANGES}" + echo "${CHANGES}" | \ + ./clang-tools-extra/clang-tidy/tool/clang-tidy-diff_Zegar.py \ + -p1 -clang-tidy-binary $(which clang-tidy) \ + -path ../target-llvm/build-final/compile_commands.json diff --git a/.github/workflows/release-binaries-all.yml b/.github/workflows/release-binaries-all.yml deleted file mode 100644 index 394b0c74d24e..000000000000 --- a/.github/workflows/release-binaries-all.yml +++ /dev/null @@ -1,98 +0,0 @@ -name: Release Binaries All - -permissions: - contents: read # Default everything to read-only - -on: - workflow_dispatch: - inputs: - release-version: - description: 'Release Version' - required: true - type: string - upload: - description: 'Upload binaries to the release page' - required: true - default: false - type: boolean - - workflow_call: - inputs: - release-version: - description: 'Release Version' - required: true - type: string - upload: - description: 'Upload binaries to the release page' - required: true - default: false - type: boolean - - pull_request: - types: - - opened - - synchronize - - reopened - # When a PR is closed, we still start this workflow, but then skip - # all the jobs, which makes it effectively a no-op. The reason to - # do this is that it allows us to take advantage of concurrency groups - # to cancel in progress CI jobs whenever the PR is closed. - - closed - paths: - - '.github/workflows/release-binaries-all.yml' - - '.github/workflows/release-binaries.yml' - - '.github/workflows/release-binaries-setup-stage/*' - - '.github/workflows/release-binaries-save-stage/*' - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || 'dispatch' }} - cancel-in-progress: True - -jobs: - setup-variables: - if: >- - (github.event_name != 'pull_request' || github.event.action != 'closed') - runs-on: ubuntu-22.04 - outputs: - release-version: ${{ steps.vars.outputs.release-version }} - upload: ${{ steps.vars.outputs.upload }} - steps: - - shell: bash - id: vars - run: | - upload="${{ inputs.upload }}" - release_version="${{ inputs.release-version }}" - if [ "${{ github.event_name }}" = "pull_request" ]; then - upload="false" - release_version="" - fi - echo "release-version=$release_version" >> "$GITHUB_OUTPUT" - echo "upload=$upload" >> "$GITHUB_OUTPUT" - - release-binaries-all: - name: Build Release Binaries - needs: - - setup-variables - permissions: - contents: write # For release uploads - id-token: write # For artifact attestations - attestations: write # For artifact attestations - strategy: - fail-fast: false - matrix: - runs-on: - - ubuntu-22.04 - - windows-2022 - - macos-13 - - macos-14 - - uses: ./.github/workflows/release-binaries.yml - with: - release-version: "${{ needs.setup-variables.outputs.release-version }}" - upload: ${{ needs.setup-variables.outputs.upload == 'true'}} - runs-on: "${{ matrix.runs-on }}" - secrets: - # This will be empty for pull_request events, but that's fine, because - # the release-binaries workflow does not use this secret for the - # pull_request event. - RELEASE_TASKS_USER_TOKEN: ${{ secrets.RELEASE_TASKS_USER_TOKEN }} diff --git a/.github/workflows/release-binaries-save-stage/action.yml b/.github/workflows/release-binaries-save-stage/action.yml deleted file mode 100644 index f08088c7bc56..000000000000 --- a/.github/workflows/release-binaries-save-stage/action.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: Save Stage -description: >- - Upload the source and binary directories from a build stage so that they - can be re-used in the next stage. This action is used to the release - binaries workflow into multiple stages to avoid the 6 hour timeout on - the GitHub hosted runners. -inputs: - build-prefix: - description: "Directory containing the build directory." - required: true - type: 'string' - -permissions: - contents: read - -runs: - using: "composite" - steps: - # We need to create an archive of the build directory, because it has too - # many files to upload. - - name: Package Build and Source Directories - shell: bash - run: | - # Remove .git/config to avoid leaking GITHUB_TOKEN stored there. - # See https://unit42.paloaltonetworks.com/github-repo-artifacts-leak-tokens/ - rm -Rf .git/config - # Windows does not support symlinks, so we need to dereference them. - tar --exclude build/ ${{ (runner.os == 'Windows' && '-h') || '' }} -c . | zstd -T0 -c > ../llvm-project.tar.zst - mv ../llvm-project.tar.zst . - tar -C ${{ inputs.build-prefix }} -c build/ | zstd -T0 -c > build.tar.zst - - - name: Upload Stage 1 Source - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 - with: - name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-source - path: llvm-project.tar.zst - retention-days: 2 - - - name: Upload Stage 1 Build Dir - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 - with: - name: ${{ runner.os}}-${{ runner.arch }}-${{ github.job }}-build - path: build.tar.zst - retention-days: 2 diff --git a/.github/workflows/release-binaries-setup-stage/action.yml b/.github/workflows/release-binaries-setup-stage/action.yml deleted file mode 100644 index f5e5db27e659..000000000000 --- a/.github/workflows/release-binaries-setup-stage/action.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: Setup Stage -description: >- - Setup the next stage of the release binaries workflow. This sets up the - environment correctly for a new stage of the release binaries workflow - and also restores the source and build directory from the previous stage. - -inputs: - previous-artifact: - description: >- - A unique descriptor for the artifact from the previous stage. This will - be used to construct the final artifact pattern, which is: - $RUNNER_OS-$RUNNER_ARCH-$PREVIOUS_ARTIFACT-* - required: false - type: 'string' - -outputs: - build-prefix: - description: "Directory containing the build directory." - value: ${{ steps.build-prefix.outputs.build-prefix }} - -runs: - using: "composite" - steps: - - name: Install Ninja - uses: llvm/actions/install-ninja@22e9f909d35b50bd1181709564bfe816eaeaae81 # main - - - name: Setup Windows - if: startsWith(runner.os, 'Windows') - uses: llvm/actions/setup-windows@main - with: - arch: amd64 - - - name: Set Build Prefix - id: build-prefix - shell: bash - run: | - build_prefix=`pwd` - if [ "${{ runner.os }}" = "Linux" ]; then - sudo chown $USER:$USER /mnt/ - build_prefix=/mnt/ - fi - echo "build-prefix=$build_prefix" >> $GITHUB_OUTPUT - - - name: Download Previous Stage Artifact - if: ${{ inputs.previous-artifact }} - id: download - uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1 - with: - pattern: ${{ runner.os }}-${{ runner.arch }}-${{ inputs.previous-artifact }}-* - merge-multiple: true - - - name: Unpack Artifact - if: ${{ steps.download.outputs.download-path }} - shell: bash - run: | - tar --zstd -xf llvm-project.tar.zst - rm llvm-project.tar.zst - tar --zstd -C ${{ steps.build-prefix.outputs.build-prefix}} -xf build.tar.zst - rm build.tar.zst diff --git a/.github/workflows/release-binaries.yml b/.github/workflows/release-binaries.yml deleted file mode 100644 index f24e25879b96..000000000000 --- a/.github/workflows/release-binaries.yml +++ /dev/null @@ -1,492 +0,0 @@ -name: Release Binaries - -on: - workflow_dispatch: - inputs: - release-version: - description: 'Release Version' - required: false - type: string - upload: - description: 'Upload binaries to the release page' - required: true - default: false - type: boolean - runs-on: - description: "Runner to use for the build" - required: true - type: choice - options: - - ubuntu-22.04 - - windows-2022 - - macos-13 - - macos-14 - - workflow_call: - inputs: - release-version: - description: 'Release Version' - required: false - type: string - upload: - description: 'Upload binaries to the release page' - required: true - default: false - type: boolean - runs-on: - description: "Runner to use for the build" - required: true - type: string - secrets: - RELEASE_TASKS_USER_TOKEN: - description: "Secret used to check user permissions." - required: false - - -permissions: - contents: read # Default everything to read-only - -jobs: - prepare: - name: Prepare to build binaries - runs-on: ${{ inputs.runs-on }} - if: github.repository == 'llvm/llvm-project' - outputs: - release-version: ${{ steps.vars.outputs.release-version }} - ref: ${{ steps.vars.outputs.ref }} - upload: ${{ steps.vars.outputs.upload }} - target-cmake-flags: ${{ steps.vars.outputs.target-cmake-flags }} - build-flang: ${{ steps.vars.outputs.build-flang }} - enable-pgo: ${{ steps.vars.outputs.enable-pgo }} - release-binary-basename: ${{ steps.vars.outputs.release-binary-basename }} - release-binary-filename: ${{ steps.vars.outputs.release-binary-filename }} - - steps: - # It's good practice to use setup-python, but this is also required on macos-14 - # due to https://github.com/actions/runner-images/issues/10385 - - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f - with: - python-version: '3.12' - - - name: Checkout LLVM - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - - name: Install Dependencies - shell: bash - run: | - pip install --require-hashes -r ./llvm/utils/git/requirements.txt - - - name: Check Permissions - if: github.event_name != 'pull_request' - env: - GITHUB_TOKEN: ${{ github.token }} - USER_TOKEN: ${{ secrets.RELEASE_TASKS_USER_TOKEN }} - shell: bash - run: | - ./llvm/utils/release/./github-upload-release.py --token "$GITHUB_TOKEN" --user ${{ github.actor }} --user-token "$USER_TOKEN" check-permissions - - - name: Collect Variables - id: vars - shell: bash - # In order for the test-release.sh script to run correctly, the LLVM - # source needs to be at the following location relative to the build dir: - # | X.Y.Z-rcN | ./rcN/llvm-project - # | X.Y.Z | ./final/llvm-project - # - # We also need to set divergent flags based on the release version: - # | X.Y.Z-rcN | -rc N -test-asserts - # | X.Y.Z | -final - run: | - trimmed=$(echo ${{ inputs.release-version }} | xargs) - if [ -n "$trimmed" ]; then - release_version="$trimmed" - ref="llvmorg-$release_version" - else - release_version="${{ (github.event_name == 'pull_request' && format('PR{0}', github.event.pull_request.number)) || 'CI'}}-${{ github.sha }}" - ref=${{ github.sha }} - fi - if [ -n "${{ inputs.upload }}" ]; then - upload="${{ inputs.upload }}" - else - upload="false" - fi - echo "release-version=$release_version">> $GITHUB_OUTPUT - echo "ref=$ref" >> $GITHUB_OUTPUT - echo "upload=$upload" >> $GITHUB_OUTPUT - - release_binary_basename="LLVM-$release_version-${{ runner.os }}-${{ runner.arch }}" - echo "release-binary-basename=$release_binary_basename" >> $GITHUB_OUTPUT - echo "release-binary-filename=$release_binary_basename.tar.xz" >> $GITHUB_OUTPUT - - # Detect necessary CMake flags - target="${{ runner.os }}-${{ runner.arch }}" - echo "enable-pgo=false" >> $GITHUB_OUTPUT - target_cmake_flags="-DLLVM_RELEASE_ENABLE_PGO=OFF" - # The macOS builds try to cross compile some libraries so we need to - # add extra CMake args to disable them. - # See https://github.com/llvm/llvm-project/issues/99767 - if [ "${{ runner.os }}" = "macOS" ]; then - target_cmake_flags="$target_cmake_flags -DBOOTSTRAP_COMPILER_RT_ENABLE_IOS=OFF" - if [ "${{ runner.arch }}" = "ARM64" ]; then - arches=arm64 - else - arches=x86_64 - fi - target_cmake_flags="$target_cmake_flags -DBOOTSTRAP_DARWIN_osx_ARCHS=$arches -DBOOTSTRAP_DARWIN_osx_BUILTIN_ARCHS=$arches" - fi - - build_flang="true" - - if [ "${{ runner.os }}" = "Windows" ]; then - # The build times out on Windows, so we need to disable LTO. - target_cmake_flags="$target_cmake_flags -DLLVM_RELEASE_ENABLE_LTO=OFF" - fi - - echo "target-cmake-flags=$target_cmake_flags" >> $GITHUB_OUTPUT - echo "build-flang=$build_flang" >> $GITHUB_OUTPUT - - build-stage1: - name: "Build Stage 1" - needs: prepare - if: github.repository == 'llvm/llvm-project' - runs-on: ${{ inputs.runs-on }} - steps: - - - name: Checkout Actions - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - ref: ${{ (github.event_name == 'pull_request' && github.sha) || 'main' }} - sparse-checkout: | - .github/workflows/ - sparse-checkout-cone-mode: false - # Check out outside of working directory so the source checkout doesn't - # remove it. - path: workflows - - # actions/checkout does not support paths outside of the GITHUB_WORKSPACE. - # Also, anything that we put inside of GITHUB_WORKSPACE will be overwritten - # by future actions/checkout steps. Therefore, in order to checkout the - # latest actions from main, we need to first checkout out the actions inside of - # GITHUB_WORKSPACE (see previous step), then use actions/checkout to checkout - # the code being built and the move the actions from main back into GITHUB_WORKSPACE, - # becasue the uses on composite actions only reads workflows from inside GITHUB_WORKSPACE. - - shell: bash - run: mv workflows ../workflows-main - - - name: Checkout LLVM - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - ref: ${{ needs.prepare.outputs.ref }} - - - name: Copy main workflows - shell: bash - run: | - mv ../workflows-main . - - - name: Setup Stage - id: setup-stage - uses: ./workflows-main/.github/workflows/release-binaries-setup-stage - - - name: Setup sccache - uses: hendrikmuhs/ccache-action@ca3acd2731eef11f1572ccb126356c2f9298d35e # v1.2.9 - with: - # Default to 2G to workaround: https://github.com/hendrikmuhs/ccache-action/issues/174 - max-size: 2G - key: sccache-${{ runner.os }}-${{ runner.arch }}-release - variant: sccache - - - name: Build Stage 1 Clang - id: build - shell: bash - run: | - # There were some issues on the ARM64 MacOS runners with trying to build x86 object, - # so we need to set some extra cmake flags to disable this. - cmake -G Ninja -S llvm -B ${{ steps.setup-stage.outputs.build-prefix }}/build \ - ${{ needs.prepare.outputs.target-cmake-flags }} \ - -C clang/cmake/caches/Release.cmake \ - -DBOOTSTRAP_LLVM_PARALLEL_LINK_JOBS=1 \ - -DBOOTSTRAP_CPACK_PACKAGE_FILE_NAME="${{ needs.prepare.outputs.release-binary-basename }}" \ - -DCMAKE_C_COMPILER_LAUNCHER=sccache \ - -DCMAKE_CXX_COMPILER_LAUNCHER=sccache - ninja -v -C ${{ steps.setup-stage.outputs.build-prefix }}/build - # There is a race condition on the MacOS builders and this command is here - # to help debug that when it happens. - ls -ltr ${{ steps.setup-stage.outputs.build-prefix }}/build - - - name: Save Stage - uses: ./workflows-main/.github/workflows/release-binaries-save-stage - with: - build-prefix: ${{ steps.setup-stage.outputs.build-prefix }} - - build-stage2: - name: "Build Stage 2" - needs: - - prepare - - build-stage1 - if: github.repository == 'llvm/llvm-project' - runs-on: ${{ inputs.runs-on }} - steps: - - name: Checkout Actions - uses: actions/checkout@v4 - with: - ref: ${{ (github.event_name == 'pull_request' && github.sha) || 'main' }} - sparse-checkout: | - .github/workflows/ - sparse-checkout-cone-mode: false - path: workflows - - name: Setup Stage - id: setup-stage - uses: ./workflows/.github/workflows/release-binaries-setup-stage - with: - previous-artifact: build-stage1 - - - name: Build Stage 2 - # Re-enable once PGO builds are supported. - if: needs.prepare.outputs.enable-pgo == 'true' - shell: bash - run: | - ninja -C ${{ steps.setup-stage.outputs.build-prefix}}/build stage2-instrumented - - - name: Save Stage - uses: ./workflows/.github/workflows/release-binaries-save-stage - with: - build-prefix: ${{ steps.setup-stage.outputs.build-prefix }} - - build-stage3-clang: - name: "Build Stage 3 LLVM/Clang" - needs: - - prepare - - build-stage2 - if: github.repository == 'llvm/llvm-project' - runs-on: ${{ inputs.runs-on }} - steps: - - name: Checkout Actions - uses: actions/checkout@v4 - with: - ref: ${{ (github.event_name == 'pull_request' && github.sha) || 'main' }} - sparse-checkout: | - .github/workflows/ - sparse-checkout-cone-mode: false - path: workflows - - name: Setup Stage - id: setup-stage - uses: ./workflows/.github/workflows/release-binaries-setup-stage - with: - previous-artifact: build-stage2 - - - name: Build LLVM/Clang - shell: bash - run: | - # There is a race condition on the MacOS builders and this command is here - # to help debug that when it happens. - ls -ltr ${{ steps.setup-stage.outputs.build-prefix }}/build - ninja -C ${{ steps.setup-stage.outputs.build-prefix }}/build stage2-clang - # Build some of the larger binaries here too. - ninja -C ${{ steps.setup-stage.outputs.build-prefix }}/build/tools/clang/stage2-bins/ \ - clang-scan-deps \ - modularize clangd \ - clangd-indexer \ - clang-check \ - ${{ (runner.os == 'Linux' && 'clangd-fuzzer') || '' }} \ - clang-tidy \ - llc \ - lli \ - llvm-exegesis \ - llvm-opt-fuzzer \ - llvm-reduce \ - llvm-lto \ - dsymutil - - - name: Save Stage - uses: ./workflows/.github/workflows/release-binaries-save-stage - with: - build-prefix: ${{ steps.setup-stage.outputs.build-prefix }} - - build-stage3-flang: - name: "Build Stage 3 Flang/MLIR/Bolt" - needs: - - prepare - - build-stage3-clang - runs-on: ${{ inputs.runs-on }} - steps: - - name: Checkout Actions - uses: actions/checkout@v4 - with: - ref: ${{ (github.event_name == 'pull_request' && github.sha) || 'main' }} - sparse-checkout: | - .github/workflows/ - sparse-checkout-cone-mode: false - path: workflows - - name: Setup Stage - id: setup-stage - uses: ./workflows/.github/workflows/release-binaries-setup-stage - with: - previous-artifact: build-stage3-clang - - - name: Build Flang / MLIR / Bolt - shell: bash - run: | - # Build some of the mlir tools that take a long time to link - if [ "${{ needs.prepare.outputs.build-flang }}" = "true" ]; then - ninja -C ${{ steps.setup-stage.outputs.build-prefix }}/build/tools/clang/stage2-bins/ -j2 flang-new bbc - fi - ninja -C ${{ steps.setup-stage.outputs.build-prefix }}/build/tools/clang/stage2-bins/ \ - mlir-bytecode-parser-fuzzer \ - mlir-cpu-runner \ - mlir-lsp-server \ - mlir-opt \ - mlir-query \ - mlir-reduce \ - mlir-text-parser-fuzzer \ - mlir-translate \ - mlir-transform-opt \ - mlir-cat \ - mlir-minimal-opt \ - mlir-minimal-opt-canonicalize \ - mlir-pdll-lsp-server \ - llvm-bolt \ - llvm-bolt-heatmap - - - name: Save Stage - uses: ./workflows/.github/workflows/release-binaries-save-stage - with: - build-prefix: ${{ steps.setup-stage.outputs.build-prefix }} - - build-stage3-all: - name: "Build Stage 3" - needs: - - prepare - - build-stage3-flang - runs-on: ${{ inputs.runs-on }} - steps: - - name: Checkout Actions - uses: actions/checkout@v4 - with: - ref: ${{ (github.event_name == 'pull_request' && github.sha) || 'main' }} - sparse-checkout: | - .github/workflows/ - sparse-checkout-cone-mode: false - path: workflows - - name: Setup Stage - id: setup-stage - uses: ./workflows/.github/workflows/release-binaries-setup-stage - with: - previous-artifact: build-stage3-flang - - - name: Build Release Package - shell: bash - run: | - which cmake - ninja -C ${{ steps.setup-stage.outputs.build-prefix }}/build stage2-package - # Copy Release artifact to the workspace so it is easier to upload. - # This is necessary, because on Windows, the build-prefix path can - # only be used on bash steps, because it uses the form of /d/files/ - # and other steps expect D:\files. - mv ${{ steps.setup-stage.outputs.build-prefix }}/build/tools/clang/stage2-bins/${{ needs.prepare.outputs.release-binary-filename }} . - - - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 - with: - name: ${{ runner.os }}-${{ runner.arch }}-release-binary - # Due to path differences on Windows when running in bash vs running on node, - # we need to search for files in the current workspace. - path: | - ${{ needs.prepare.outputs.release-binary-filename }} - - # Clean up some build files to reduce size of artifact. - - name: Clean Up Build Directory - shell: bash - run: | - find ${{ steps.setup-stage.outputs.build-prefix }}/build -iname ${{ needs.prepare.outputs.release-binary-filename }} -delete - rm -Rf ${{ steps.setup-stage.outputs.build-prefix }}/build/tools/clang/stage2-bins/_CPack_Packages - - - name: Save Stage - uses: ./workflows/.github/workflows/release-binaries-save-stage - with: - build-prefix: ${{ steps.setup-stage.outputs.build-prefix }} - - upload-release-binaries: - name: "Upload Release Binaries" - needs: - - prepare - - build-stage3-all - if: >- - always() && - github.event_name != 'pull_request' && - needs.prepare.outputs.upload == 'true' - runs-on: ubuntu-22.04 - permissions: - contents: write # For release uploads - id-token: write # For artifact attestations - attestations: write # For artifact attestations - - steps: - - name: Checkout Release Scripts - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - sparse-checkout: | - llvm/utils/release/github-upload-release.py - llvm/utils/git/requirements.txt - sparse-checkout-cone-mode: false - - - name: 'Download artifact' - uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1 - with: - pattern: '*-release-binary' - merge-multiple: true - - - name: Attest Build Provenance - id: provenance - uses: actions/attest-build-provenance@897ed5eab6ed058a474202017ada7f40bfa52940 # v1.0.0 - with: - subject-path: ${{ needs.prepare.outputs.release-binary-filename }} - - - name: Rename attestation file - run: - mv ${{ steps.provenance.outputs.bundle-path }} ${{ needs.prepare.outputs.release-binary-filename }}.jsonl - - - name: Upload Build Provenance - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 #v4.3.3 - with: - name: ${{ needs.prepare.outputs.release-binary-filename }}-attestation - path: ${{ needs.prepare.outputs.release-binary-filename }}.jsonl - - - name: Install Python Requirements - run: | - pip install --require-hashes -r ./llvm/utils/git/requirements.txt - - - name: Upload Release - shell: bash - run: | - ./llvm/utils/release/github-upload-release.py \ - --token ${{ github.token }} \ - --release ${{ needs.prepare.outputs.release-version }} \ - upload \ - --files ${{ needs.prepare.outputs.release-binary-filename }}* - - test-stage3: - name: "Test Stage 3" - needs: - - prepare - - build-stage3-all - if: >- - github.repository == 'llvm/llvm-project' - runs-on: ${{ inputs.runs-on }} - steps: - - name: Checkout Actions - uses: actions/checkout@v4 - with: - ref: ${{ (github.event_name == 'pull_request' && github.sha) || 'main' }} - sparse-checkout: | - .github/workflows/ - sparse-checkout-cone-mode: false - path: workflows - - name: Setup Stage - id: setup-stage - uses: ./workflows/.github/workflows/release-binaries-setup-stage - with: - previous-artifact: build-stage3-all - - - name: Run Tests - shell: bash - run: | - ninja -C ${{ steps.setup-stage.outputs.build-prefix }}/build stage2-check-all diff --git a/.github/workflows/release-documentation.yml b/.github/workflows/release-documentation.yml deleted file mode 100644 index 922c5093f135..000000000000 --- a/.github/workflows/release-documentation.yml +++ /dev/null @@ -1,91 +0,0 @@ -name: Release Documentation - -permissions: - contents: read - -on: - workflow_dispatch: - inputs: - release-version: - description: 'Release Version' - required: true - type: string - upload: - description: 'Upload documentation' - required: false - type: boolean - - workflow_call: - inputs: - release-version: - description: 'Release Version' - required: true - type: string - upload: - description: 'Upload documentation' - required: false - type: boolean - -jobs: - release-documentation: - name: Build and Upload Release Documentation - runs-on: ubuntu-latest - env: - upload: ${{ inputs.upload && !contains(inputs.release-version, 'rc') }} - steps: - - name: Checkout LLVM - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - - name: Setup Python env - uses: actions/setup-python@v5 - with: - cache: 'pip' - cache-dependency-path: './llvm/docs/requirements.txt' - - - name: Install Dependencies - run: | - sudo apt-get update - sudo apt-get install -y \ - graphviz \ - python3-github \ - ninja-build \ - texlive-font-utils - pip3 install --user -r ./llvm/docs/requirements.txt - - - name: Build Documentation - env: - GITHUB_TOKEN: ${{ github.token }} - run: | - ./llvm/utils/release/build-docs.sh -release "${{ inputs.release-version }}" -no-doxygen - - - name: Create Release Notes Artifact - uses: actions/upload-artifact@v3 - with: - name: release-notes - path: docs-build/html-export/ - - - name: Clone www-releases - if: env.upload - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - repository: ${{ github.repository_owner }}/www-releases - ref: main - fetch-depth: 0 - path: www-releases - persist-credentials: false - - - name: Upload Release Notes - if: env.upload - env: - GH_TOKEN: ${{ secrets.WWW_RELEASES_TOKEN }} - run: | - mkdir -p www-releases/${{ inputs.release-version }} - mv ./docs-build/html-export/* www-releases/${{ inputs.release-version }} - cd www-releases - git checkout -b ${{ inputs.release-version }} - git add ${{ inputs.release-version }} - git config user.email "llvmbot@llvm.org" - git config user.name "llvmbot" - git commit -a -m "Add ${{ inputs.release-version }} documentation" - git push --force "https://$GH_TOKEN@github.com/llvmbot/www-releases.git" HEAD:refs/heads/${{ inputs.release-version }} - gh pr create -f -B main -H ${{ inputs.release-version }} -R llvmbot/www-releases diff --git a/.github/workflows/release-doxygen.yml b/.github/workflows/release-doxygen.yml deleted file mode 100644 index ea95e5bb12b2..000000000000 --- a/.github/workflows/release-doxygen.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: Release Doxygen - -permissions: - contents: read - -on: - workflow_dispatch: - inputs: - release-version: - description: 'Release Version' - required: true - type: string - upload: - description: 'Upload documentation' - required: false - type: boolean - - workflow_call: - inputs: - release-version: - description: 'Release Version' - required: true - type: string - upload: - description: 'Upload documentation' - required: false - type: boolean - secrets: - RELEASE_TASKS_USER_TOKEN: - description: "Secret used to check user permissions." - required: false - -jobs: - release-doxygen: - name: Build and Upload Release Doxygen - runs-on: ubuntu-latest - permissions: - contents: write - env: - upload: ${{ inputs.upload && !contains(inputs.release-version, 'rc') }} - steps: - - name: Checkout LLVM - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - - name: Setup Python env - uses: actions/setup-python@v5 - with: - cache: 'pip' - cache-dependency-path: './llvm/docs/requirements.txt' - - - name: Install Dependencies - run: | - sudo apt-get update - sudo apt-get install -y \ - doxygen \ - graphviz \ - python3-github \ - ninja-build \ - texlive-font-utils - pip3 install --user -r ./llvm/docs/requirements.txt - - - name: Build Doxygen - run: | - ./llvm/utils/release/build-docs.sh -release "${{ inputs.release-version }}" -no-sphinx - - - name: Upload Doxygen - if: env.upload - env: - GITHUB_TOKEN: ${{ github.token }} - USER_TOKEN: ${{ secrets.RELEASE_TASKS_USER_TOKEN }} - run: | - ./llvm/utils/release/github-upload-release.py --token "$GITHUB_TOKEN" --release "${{ inputs.release-version }}" --user "${{ github.actor }}" --user-token "$USER_TOKEN" upload --files ./*doxygen*.tar.xz diff --git a/.github/workflows/release-lit.yml b/.github/workflows/release-lit.yml deleted file mode 100644 index 9d6f3140e688..000000000000 --- a/.github/workflows/release-lit.yml +++ /dev/null @@ -1,79 +0,0 @@ -name: Release Lit - -permissions: - contents: read - -on: - workflow_dispatch: - inputs: - release-version: - description: 'Release Version' - required: true - type: string - - workflow_call: - inputs: - release-version: - description: 'Release Version' - required: true - type: string - secrets: - RELEASE_TASKS_USER_TOKEN: - description: "Secret used to check user permissions." - required: false - -jobs: - release-lit: - name: Release Lit - runs-on: ubuntu-latest - steps: - - name: Checkout LLVM - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - ref: "llvmorg-${{ inputs.release-version }}" - - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y python3-setuptools python3-psutil python3-github - - - name: Check Permissions - env: - GITHUB_TOKEN: ${{ github.token }} - USER_TOKEN: ${{ secrets.RELEASE_TASKS_USER_TOKEN }} - run: | - ./llvm/utils/release/./github-upload-release.py --token "$GITHUB_TOKEN" --user ${{ github.actor }} --user-token "$USER_TOKEN" check-permissions - - - name: Setup Cpp - uses: aminya/setup-cpp@v1 - with: - compiler: llvm-16.0.6 - cmake: true - ninja: true - - - name: Test lit - run: | - mkdir build && cd build - export FILECHECK_OPTS='-dump-input-filter=all -vv -color' - cmake ../llvm -DCMAKE_BUILD_TYPE=Release -G Ninja - ninja -v -j $(nproc) check-lit - - - name: Package lit - run: | - cd llvm/utils/lit - # Remove 'dev' suffix from lit version. - sed -i 's/ + "dev"//g' lit/__init__.py - python3 setup.py sdist bdist_wheel - - - name: Upload lit to test.pypi.org - uses: pypa/gh-action-pypi-publish@release/v1 - with: - password: ${{ secrets.LLVM_LIT_TEST_PYPI_API_TOKEN }} - repository-url: https://test.pypi.org/legacy/ - packages-dir: llvm/utils/lit/dist/ - - - name: Upload lit to pypi.org - uses: pypa/gh-action-pypi-publish@release/v1 - with: - password: ${{ secrets.LLVM_LIT_PYPI_API_TOKEN }} - packages-dir: llvm/utils/lit/dist/ diff --git a/.github/workflows/release-sources.yml b/.github/workflows/release-sources.yml deleted file mode 100644 index a6c86823f99d..000000000000 --- a/.github/workflows/release-sources.yml +++ /dev/null @@ -1,108 +0,0 @@ -name: Release Sources - -permissions: - contents: read - -on: - workflow_dispatch: - inputs: - release-version: - description: Release Version - required: true - type: string - workflow_call: - inputs: - release-version: - description: Release Version - required: true - type: string - secrets: - RELEASE_TASKS_USER_TOKEN: - description: "Secret used to check user permissions." - required: false - # Run on pull_requests for testing purposes. - pull_request: - paths: - - '.github/workflows/release-sources.yml' - types: - - opened - - synchronize - - reopened - # When a PR is closed, we still start this workflow, but then skip - # all the jobs, which makes it effectively a no-op. The reason to - # do this is that it allows us to take advantage of concurrency groups - # to cancel in progress CI jobs whenever the PR is closed. - - closed - -concurrency: - group: ${{ github.workflow }}-${{ inputs.release-version || github.event.pull_request.number }} - cancel-in-progress: True - -jobs: - inputs: - name: Collect Job Inputs - if: >- - github.repository_owner == 'llvm' && - github.event.action != 'closed' - outputs: - ref: ${{ steps.inputs.outputs.ref }} - export-args: ${{ steps.inputs.outputs.export-args }} - runs-on: ubuntu-latest - steps: - - id: inputs - run: | - ref=${{ (inputs.release-version && format('llvmorg-{0}', inputs.release-version)) || github.sha }} - if [ -n "${{ inputs.release-version }}" ]; then - export_args="-release ${{ inputs.release-version }} -final" - else - export_args="-git-ref ${{ github.sha }}" - fi - echo "ref=$ref" >> $GITHUB_OUTPUT - echo "export-args=$export_args" >> $GITHUB_OUTPUT - - release-sources: - name: Package Release Sources - if: github.repository_owner == 'llvm' - runs-on: ubuntu-latest - needs: - - inputs - permissions: - id-token: write - attestations: write - steps: - - name: Checkout LLVM - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - ref: ${{ needs.inputs.outputs.ref }} - fetch-tags: true - - name: Install Dependencies - run: | - pip install --require-hashes -r ./llvm/utils/git/requirements.txt - - - name: Check Permissions - if: github.event_name != 'pull_request' - env: - GITHUB_TOKEN: ${{ github.token }} - USER_TOKEN: ${{ secrets.RELEASE_TASKS_USER_TOKEN }} - run: | - ./llvm/utils/release/./github-upload-release.py --token "$GITHUB_TOKEN" --user ${{ github.actor }} --user-token "$USER_TOKEN" check-permissions - - name: Create Tarballs - run: | - ./llvm/utils/release/export.sh ${{ needs.inputs.outputs.export-args }} - - name: Attest Build Provenance - if: github.event_name != 'pull_request' - id: provenance - uses: actions/attest-build-provenance@897ed5eab6ed058a474202017ada7f40bfa52940 # v1.0.0 - with: - subject-path: "*.xz" - - if: github.event_name != 'pull_request' - run: | - mv ${{ steps.provenance.outputs.bundle-path }} . - - name: Create Tarball Artifacts - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 #v4.3.3 - with: - path: | - *.xz - attestation.jsonl - - diff --git a/.github/workflows/release-tasks.yml b/.github/workflows/release-tasks.yml deleted file mode 100644 index 780dd0ff6325..000000000000 --- a/.github/workflows/release-tasks.yml +++ /dev/null @@ -1,123 +0,0 @@ -name: Release Task - -permissions: - contents: read - -on: - push: - tags: - # The regex support here is limited, so just match everything that starts with llvmorg- and filter later. - - 'llvmorg-*' - -jobs: - validate-tag: - name: Validate Tag - runs-on: ubuntu-latest - if: github.repository == 'llvm/llvm-project' - outputs: - release-version: ${{ steps.validate-tag.outputs.release-version }} - steps: - - name: Validate Tag - id: validate-tag - run: | - echo "${{ github.ref_name }}" | grep -e '^llvmorg-[0-9]\+\.[0-9]\+\.[0-9]\+\(-rc[0-9]\+\)\?$' - release_version=$(echo "${{ github.ref_name }}" | sed 's/llvmorg-//g') - echo "release-version=$release_version" >> "$GITHUB_OUTPUT" - - release-create: - name: Create a New Release - runs-on: ubuntu-latest - permissions: - contents: write # For creating the release. - needs: validate-tag - - steps: - - name: Install Dependencies - run: | - sudo apt-get update - sudo apt-get install python3-github - - - name: Checkout LLVM - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - - name: Create Release - env: - GITHUB_TOKEN: ${{ github.token }} - USER_TOKEN: ${{ secrets.RELEASE_TASKS_USER_TOKEN }} - run: | - ./llvm/utils/release/./github-upload-release.py --token "$GITHUB_TOKEN" --release ${{ needs.validate-tag.outputs.release-version }} --user ${{ github.actor }} --user-token "$USER_TOKEN" create - release-documentation: - name: Build and Upload Release Documentation - needs: - - validate-tag - uses: ./.github/workflows/release-documentation.yml - with: - release-version: ${{ needs.validate-tag.outputs.release-version }} - upload: true - - release-doxygen: - name: Build and Upload Release Doxygen - permissions: - contents: write - needs: - - validate-tag - - release-create - uses: ./.github/workflows/release-doxygen.yml - with: - release-version: ${{ needs.validate-tag.outputs.release-version }} - upload: true - # Called workflows don't have access to secrets by default, so we need to explicitly pass secrets that we use. - secrets: - RELEASE_TASKS_USER_TOKEN: ${{ secrets.RELEASE_TASKS_USER_TOKEN }} - - release-lit: - name: Release Lit - needs: validate-tag - uses: ./.github/workflows/release-lit.yml - with: - release-version: ${{ needs.validate-tag.outputs.release-version }} - # Called workflows don't have access to secrets by default, so we need to explicitly pass secrets that we use. - secrets: - RELEASE_TASKS_USER_TOKEN: ${{ secrets.RELEASE_TASKS_USER_TOKEN }} - - release-binaries: - name: Build Release Binaries - permissions: - contents: write - id-token: write - attestations: write - needs: - - validate-tag - - release-create - strategy: - fail-fast: false - matrix: - runs-on: - - ubuntu-22.04 - - windows-2022 - - macos-13 - - macos-14 - - uses: ./.github/workflows/release-binaries.yml - with: - release-version: ${{ needs.validate-tag.outputs.release-version }} - upload: true - runs-on: ${{ matrix.runs-on }} - # Called workflows don't have access to secrets by default, so we need to explicitly pass secrets that we use. - secrets: - RELEASE_TASKS_USER_TOKEN: ${{ secrets.RELEASE_TASKS_USER_TOKEN }} - - release-sources: - name: Package Release Sources - permissions: - contents: read - id-token: write - attestations: write - needs: - - validate-tag - uses: ./.github/workflows/release-sources.yml - with: - release-version: ${{ needs.validate-tag.outputs.release-version }} - # Called workflows don't have access to secrets by default, so we need to explicitly pass secrets that we use. - secrets: - RELEASE_TASKS_USER_TOKEN: ${{ secrets.RELEASE_TASKS_USER_TOKEN }} diff --git a/.github/workflows/scheduled.yml b/.github/workflows/scheduled.yml new file mode 100644 index 000000000000..9d9e59a67531 --- /dev/null +++ b/.github/workflows/scheduled.yml @@ -0,0 +1,185 @@ +name: Scheduled build + +# This workflow is triggered by a schedule or manually +# It allows to build LLVM, run regression tests and integration tests +# for all supported platforms by user's choice +# It also allows to enable assertions and/or valgrind for regression tests +# and regenerates ccache + +on: + schedule: + - cron: '0 0 * * 0' # every week + workflow_dispatch: + inputs: + ref: + description: "Git REF to use for the build" + required: false + type: string + build_macos: + description: "Build for MacOS?" + required: false + type: boolean + default: true + build_linux_amd64: + description: "Build for Linux amd64?" + required: false + type: boolean + default: true + build_linux_arm64: + description: "Build for Linux arm64?" + required: false + type: boolean + default: true + build_windows: + description: "Build for Windows?" + required: false + type: boolean + default: true + run_regression_tests: + description: "Run regression tests?" + required: false + type: boolean + default: false + run_integration_tests: + description: "Run integration tests?" + required: false + type: boolean + default: false + enable-valgrind: + required: false + default: false + type: boolean + description: 'Enable valgrind for regression tests?' + valgrind-options: + required: false + default: '' + type: string + description: 'Space-separated list of additional valgrind options.' + enable_assertions: + description: "Enable assertions?" + required: false + type: boolean + default: true + + +jobs: + + prepare-matrix: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.prepare-matrix.outputs.matrix }} + steps: + - name: Prepare matrix + id: prepare-matrix + run: | + # Define general matrix parameters + # Windows is not supported yet on era-compiler-tester side + # WINDOWS='{"name":"Windows-x86","runner":"windows-2019-github-hosted-64core"}' + MACOS_AMD64='{"name":"MacOS-x86","runner":"macos-13-large"}' + MACOS_ARM64='{"name":"MacOS-arm64","runner":["self-hosted","macOS","ARM64"]}' + LINUX_AMD64='{"name":"Linux-AMD64","runner":"matterlabs-ci-runner-high-performance","image":"ghcr.io/matter-labs/zksync-llvm-runner:latest"}' + LINUX_ARM64='{"name":"Linux-ARM64","runner":"matterlabs-ci-runner-arm","image":"ghcr.io/matter-labs/zksync-llvm-runner:latest"}' + # Disable platforms for non-tag builds if user requested + if [ ${GITHUB_EVENT_NAME} = workflow_dispatch ]; then + [ ${{ github.event.inputs.build_windows }} != true ] && WINDOWS= + [ ${{ github.event.inputs.build_macos }} != true ] && MACOS_AMD64= + [ ${{ github.event.inputs.build_macos }} != true ] && MACOS_ARM64= + [ ${{ github.event.inputs.build_linux_amd64 }} != true ] && LINUX_AMD64= + [ ${{ github.event.inputs.build_linux_arm64 }} != true ] && LINUX_ARM64= + fi + PLATFORMS=(${WINDOWS} ${MACOS_AMD64} ${MACOS_ARM64} ${LINUX_AMD64} ${LINUX_ARM64}) + echo "matrix={ \"include\": [ $(IFS=, ; echo "${PLATFORMS[*]}") ] }" | tee -a "${GITHUB_OUTPUT}" + + + integration-tests: + needs: prepare-matrix + uses: matter-labs/era-compiler-ci/.github/workflows/integration-tests.yaml@main + secrets: inherit + if: ${{ inputs.run_integration_tests || github.event_name == 'schedule' }} + with: + ccache-key-type: 'static' # rotate ccache key every month + llvm-ref: ${{ inputs.ref }} + target-machine: 'eravm' # TODO: add `evm` target when LLVM EVM BE is ready + platforms-matrix: ${{ needs.prepare-matrix.outputs.matrix }} + + + build: + needs: prepare-matrix + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.prepare-matrix.outputs.matrix) }} + runs-on: ${{ matrix.runner }} + container: + image: ${{ matrix.image || '' }} # Special workaround to allow matrix builds with optional container + name: ${{ matrix.name }} + steps: + - name: Checkout source + uses: actions/checkout@v4 + with: + ref: ${{ inputs.ref }} + path: "llvm" + + - name: Prepare Windows env + if: runner.os == 'Windows' + uses: matter-labs/era-compiler-ci/.github/actions/prepare-msys@v1 + + - name: Build LLVM + uses: matter-labs/era-compiler-ci/.github/actions/build-llvm@v1 + with: + enable-tests: true + enable-assertions: ${{ inputs.enable_assertions }} + clone-llvm: false + ccache-key-type: 'static' + save-ccache: ${{ matrix.name == 'Linux x86' }} + enable-valgrind: ${{ inputs.enable-valgrind }} + valgrind-options: ${{ inputs.valgrind-options }} + + # Required to keep executable permissions for binaries + - name: Prepare tarball + run: tar -czf ${{ runner.os }}-${{ runner.arch }}-target-final.tar.gz ./target-llvm/target-final + + - name: Upload LLVM binaries + uses: actions/upload-artifact@v4 + with: + name: llvm-bins-${{ runner.os }}-${{ runner.arch }} + path: ./${{ runner.os }}-${{ runner.arch }}-target-final.tar.gz + + # On Windows, run `llvm-lit` directly with xfail parameter + # due to different incompatibilities in tests and MSYS2 environment + - name: Lit tests (Windows) + if: ${{ (inputs.run_regression_tests || github.event_name == 'schedule') && runner.os == 'Windows' }} + shell: 'msys2 {0}' + env: + LLVM_XFAILS: + "Object/archive-big-read.test;\ + Object/archive-extract.test;\ + Object/archive-toc.test;\ + TableGen/x86-fold-tables.td;\ + tools/llvm-ar/empty-uid-gid.test;\ + tools/llvm-cov/native_separators.c;\ + tools/llvm-libtool-darwin/deterministic-library.test;\ + tools/llvm-nm/invalid-tapi-files.test;\ + tools/llvm-nm/tapi-files.test;\ + tools/llvm-objcopy/ELF/deterministic-archive.test;\ + tools/llvm-ranlib/D-flag.test;\ + tools/llvm-tapi-diff/v5.test" + LLD_XFAILS: + "MachO/double-unwind-info.s;\ + MachO/error-limit.test;\ + MachO/local-private-extern.yaml" + run: | + ./target-llvm/build-final/bin/llvm-lit.py -sv ./target-llvm/build-final/test --xfail ${LLVM_XFAILS} + ./target-llvm/build-final/bin/llvm-lit.py -sv ./target-llvm/build-final/tools/lld/test --xfail ${LLD_XFAILS} + + - name: Lit tests (MacOS/Linux) + if: ${{ (inputs.run_regression_tests || github.event_name == 'schedule') && runner.os != 'Windows' }} + run: ninja -C './target-llvm/build-final' verify-llvm -v + + - name: Send Slack notification + uses: 8398a7/action-slack@v3 + if: failure() && github.event_name == 'schedule' + with: + status: ${{ job.status }} + fields: repo,commit,author,action,eventName,ref,workflow,job,took + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml deleted file mode 100644 index ff61cf83a6af..000000000000 --- a/.github/workflows/scorecard.yml +++ /dev/null @@ -1,62 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. They are provided -# by a third-party and are governed by separate terms of service, privacy -# policy, and support documentation. - -# Check current LLVM-Project results here: https://securityscorecards.dev/viewer/?uri=github.com/llvm/llvm-project - -name: Scorecard supply-chain security -on: - # For Branch-Protection check. Only the default branch is supported. See - # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection - branch_protection_rule: - # To guarantee Maintained check is occasionally updated. See - # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained - schedule: - - cron: '38 20 * * *' - -# Declare default permissions as read only. -permissions: - contents: read - -jobs: - analysis: - name: Scorecard analysis - runs-on: ubuntu-latest - if: github.repository == 'llvm/llvm-project' - permissions: - # Needed to upload the results to code-scanning dashboard. - security-events: write - # Needed to publish results and get a badge (see publish_results below). - id-token: write - - steps: - - name: "Checkout code" - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0 - with: - persist-credentials: false - - - name: "Run analysis" - uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1 - with: - results_file: results.sarif - results_format: sarif - - # - Publish results to OpenSSF REST API for easy access by consumers - # - Allows the repository to include the Scorecard badge. - # - See https://github.com/ossf/scorecard-action#publishing-results. - publish_results: true - - # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF - # format to the repository Actions tab. - - name: "Upload artifact" - uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # v3.1.0 - with: - name: SARIF file - path: results.sarif - retention-days: 5 - - # Upload the results to GitHub's code scanning dashboard. - - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@17573ee1cc1b9d061760f3a006fc4aac4f944fd5 # v2.2.4 - with: - sarif_file: results.sarif diff --git a/.github/workflows/set-release-binary-outputs.sh b/.github/workflows/set-release-binary-outputs.sh deleted file mode 100644 index 14d0798364e9..000000000000 --- a/.github/workflows/set-release-binary-outputs.sh +++ /dev/null @@ -1,34 +0,0 @@ -# Usage: set-release-binary-outputs.sh - -set -e - -if [ -z "$GITHUB_OUTPUT" ]; then - export GITHUB_OUTPUT=`mktemp` - echo "Warning: Environment variable GITHUB_OUTPUT is not set." - echo "Writing output variables to $GITHUB_OUTPUT" -fi - -tag=$1 -upload=$2 - -if echo $tag | grep -e '^[0-9a-f]\+$'; then - # This is a plain commit. - # TODO: Don't hardcode this. - release_version="18" - upload='false' - ref="$tag" - -else - - pattern='^llvmorg-[0-9]\+\.[0-9]\+\.[0-9]\+\(-rc[0-9]\+\)\?$' - echo "$tag" | grep -e $pattern - if [ $? != 0 ]; then - echo "ERROR: Tag '$tag' doesn't match pattern: $pattern" - exit 1 - fi - release_version=`echo "$tag" | sed 's/llvmorg-//g'` - release=`echo "$release_version" | sed 's/-.*//g'` -fi -echo "release-version=$release_version" >> $GITHUB_OUTPUT -echo "upload=$upload" >> $GITHUB_OUTPUT -echo "ref=$tag" >> $GITHUB_OUTPUT diff --git a/.github/workflows/spirv-tests.yml b/.github/workflows/spirv-tests.yml deleted file mode 100644 index 75918e73e897..000000000000 --- a/.github/workflows/spirv-tests.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: SPIR-V Tests - -permissions: - contents: read - -on: - workflow_dispatch: - pull_request: - paths: - - 'llvm/lib/Target/SPIRV/**' - - 'llvm/test/CodeGen/SPIRV/**' - - '.github/workflows/spirv-tests.yml' - -concurrency: - # Skip intermediate builds: always. - # Cancel intermediate builds: only if it is a pull request build. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} - -jobs: - check_spirv: - if: github.repository_owner == 'llvm' - name: Test SPIR-V - uses: ./.github/workflows/llvm-project-tests.yml - with: - build_target: check-llvm-codegen-spirv - projects: - extra_cmake_args: '-DLLVM_TARGETS_TO_BUILD="" -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="SPIRV" -DLLVM_INCLUDE_SPIRV_TOOLS_TESTS=ON' - os_list: '["ubuntu-latest"]' diff --git a/.github/workflows/unprivileged-download-artifact/action.yml b/.github/workflows/unprivileged-download-artifact/action.yml deleted file mode 100644 index 9d8fb59a67c0..000000000000 --- a/.github/workflows/unprivileged-download-artifact/action.yml +++ /dev/null @@ -1,81 +0,0 @@ -name: Unprivileged Download Artifact -description: >- - Download artifacts from another workflow run without using an access token. -inputs: - run-id: - description: >- - The run-id for the workflow run that you want to download the artifact - from. If ommitted it will download the most recently created artifact - from the repo with the artifact-name. - required: false - artifact-name: - desciption: The name of the artifact to download. - required: true - - -outputs: - filename: - description: >- - The filename of the downloaded artifact or the empty string if the - artifact was not found. - value: ${{ steps.download-artifact.outputs.filename }} - artifact-id: - description: "The id of the artifact being downloaded." - value: ${{ steps.artifact-url.outputs.id }} - - -runs: - using: "composite" - steps: - - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea #v7.0.1 - id: artifact-url - with: - script: | - var response; - if (!"${{ inputs.run-id }}") { - response = await github.rest.actions.listArtifactsForRepo({ - owner: context.repo.owner, - repo: context.repo.repo, - name: "${{ inputs.artifact-name }}" - }) - } else { - response = await github.rest.actions.listWorkflowRunArtifacts({ - owner: context.repo.owner, - repo: context.repo.repo, - run_id: "${{ inputs.run-id }}", - name: "${{ inputs.artifact-name }}" - }) - } - - console.log(response) - - for (artifact of response.data.artifacts) { - console.log(artifact); - } - - if (response.data.artifacts.length == 0) { - console.log("Could not find artifact ${{ inputs.artifact-name }} for workflow run ${{ inputs.run-id }}") - return; - } - - const url_response = await github.rest.actions.downloadArtifact({ - owner: context.repo.owner, - repo: context.repo.repo, - artifact_id: response.data.artifacts[0].id, - archive_format: "zip" - }) - - core.setOutput("url", url_response.url); - core.setOutput("id", response.data.artifacts[0].id); - - - shell: bash - if: steps.artifact-url.outputs.url != '' - id: download-artifact - run: | - curl -L -o ${{ inputs.artifact-name }}.zip "${{ steps.artifact-url.outputs.url }}" - echo "filename=${{ inputs.artifact-name }}.zip" >> $GITHUB_OUTPUT - - - shell: bash - if: steps.download-artifact.outputs.filename != '' - run: | - unzip ${{ steps.download-artifact.outputs.filename }} diff --git a/.github/workflows/version-check.py b/.github/workflows/version-check.py deleted file mode 100755 index f75fd5030088..000000000000 --- a/.github/workflows/version-check.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/python3 - -from git import Repo -import re -import sys - - -def get_version_from_tag(tag): - m = re.match("llvmorg-([0-9]+)\.([0-9]+)\.([0-9]+)(-rc[0-9]+)?$", tag) - if m: - if m.lastindex == 4: - # We have an rc tag. - return m.group(1, 2, 3) - # We have a final release tag. - return (m.group(1), m.group(2), str(int(m.group(3)) + 1)) - - m = re.match("llvmorg-([0-9]+)-init", tag) - if m: - return (m.group(1), "1", "0") - - raise Exception(f"error: Tag is not valid: {tag}") - - -version = sys.argv[1] - -repo = Repo() - -tag = repo.git.describe(tags=True, abbrev=0) -expected_version = ".".join(get_version_from_tag(tag)) - -if version != expected_version: - print("error: Expected version", expected_version, "but found version", version) - sys.exit(1) - -print("Versions match:", version, expected_version) -sys.exit(0) diff --git a/.github/workflows/version-check.yml b/.github/workflows/version-check.yml deleted file mode 100644 index 894e07d323ca..000000000000 --- a/.github/workflows/version-check.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: LLVM Project Version Check - -on: - push: - branches: - - 'release/**' - pull_request: - branches: - - 'release/**' - -permissions: - contents: read - -jobs: - version_check: - if: github.repository_owner == 'llvm' - runs-on: ubuntu-latest - steps: - - name: Fetch LLVM sources - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Install dependencies - run: | - pip install --require-hashes -r ./llvm/utils/git/requirements.txt - - - name: Version Check - run: | - version=$(grep -o 'LLVM_VERSION_\(MAJOR\|MINOR\|PATCH\) [0-9]\+' cmake/Modules/LLVMVersion.cmake | cut -d ' ' -f 2 | tr "\n" "." | sed 's/.$//g') - .github/workflows/version-check.py "$version" diff --git a/.gitignore b/.gitignore index 20c4f52cd378..e249915a801c 100644 --- a/.gitignore +++ b/.gitignore @@ -26,8 +26,8 @@ # Ignore the user specified CMake presets in subproject directories. /*/CMakeUserPresets.json -# Nested build directory -/build* +# Nested build directories +/build*/ #==============================================================================# # Explicit files to ignore (only matches one). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6424f9b26a9d..f129e606f7a5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,17 +1,9 @@ -# Contributing to LLVM +# Contribution Guidelines -Thank you for your interest in contributing to LLVM! There are many ways to -contribute, and we appreciate all contributions. +Thank you for considering helping out with the source code! We are extremely grateful for any consideration of +contributions to this repository. However, at this time, we generally do not accept external contributions. This policy +will change in the future, so please check back regularly for updates. -To get started with contributing, please take a look at the -[Contributing to LLVM](https://llvm.org/docs/Contributing.html) guide. It -describes how to get involved, raise issues and submit patches. +For security issues, please contact us at [security@matterlabs.dev](mailto:security@matterlabs.dev). -## Getting in touch - -Join the [LLVM Discourse forums](https://discourse.llvm.org/), [Discord -chat](https://discord.gg/xS7Z362), or #llvm IRC channel on -[OFTC](https://oftc.net/). - -The LLVM project has adopted a [code of conduct](https://llvm.org/docs/CodeOfConduct.html) for -participants to all modes of communication within the project. +Thank you for your support in accelerating the mass adoption of crypto for personal sovereignty! diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000000..e7ac8892af2b --- /dev/null +++ b/LICENSE @@ -0,0 +1,278 @@ +============================================================================== +The LLVM Project is under the Apache License v2.0 with LLVM Exceptions: +============================================================================== + + 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 2019 Matter Labs + + 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. + + +---- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + +============================================================================== +Software from third parties included in the LLVM Project: +============================================================================== +The LLVM Project contains third party software which is under different license +terms. All such code will be identified clearly using at least one of two +mechanisms: +1) It will be in a separate directory tree with its own `LICENSE.txt` or + `LICENSE` file at the top containing the specific license and restrictions + which apply to that software, or +2) It will contain specific license and restriction terms at the top of every + file. + +============================================================================== +Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy): +============================================================================== +University of Illinois/NCSA +Open Source License + +Copyright (c) 2003-2019 University of Illinois at Urbana-Champaign. +All rights reserved. + +Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at + Urbana-Champaign, nor the names of its contributors may be used to + endorse or promote products derived from this Software without specific + prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. diff --git a/README.md b/README.md index a9b29ecbc1a3..02b40eb1cb5a 100644 --- a/README.md +++ b/README.md @@ -1,44 +1,138 @@ -# The LLVM Compiler Infrastructure +# ZKsync Era: The ZKsync LLVM Framework -[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/llvm/llvm-project/badge)](https://securityscorecards.dev/viewer/?uri=github.com/llvm/llvm-project) -[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/8273/badge)](https://www.bestpractices.dev/projects/8273) -[![libc++](https://github.com/llvm/llvm-project/actions/workflows/libcxx-build-and-test.yaml/badge.svg?branch=main&event=schedule)](https://github.com/llvm/llvm-project/actions/workflows/libcxx-build-and-test.yaml?query=event%3Aschedule) +[![Logo](eraLogo.svg)](https://zksync.io/) -Welcome to the LLVM project! +ZKsync Era is a layer 2 rollup that uses zero-knowledge proofs to scale Ethereum without compromising on security +or decentralization. As it's EVM-compatible (with Solidity/Vyper), 99% of Ethereum projects can redeploy without +needing to refactor or re-audit any code. ZKsync Era also uses an LLVM-based compiler that will eventually enable +developers to write smart contracts in popular languages such as C++ and Rust. -This repository contains the source code for LLVM, a toolkit for the -construction of highly optimized compilers, optimizers, and run-time -environments. +This directory and its sub-directories contain the source code for the ZKsync fork of the [LLVM](https://llvm.org) framework, +a toolkit for the construction of highly optimized compilers, optimizers, and run-time environments +used by the Solidity and Vyper compilers developed by Matter Labs. -The LLVM project has multiple components. The core of the project is -itself called "LLVM". This contains all of the tools, libraries, and header +## Overview + +Welcome to the ZKsync LLVM project! + +The project has multiple components. The core of the project is +the `llvm` directory. This contains all of the tools, libraries, and header files needed to process intermediate representations and convert them into object files. Tools include an assembler, disassembler, bitcode analyzer, and -bitcode optimizer. +bitcode optimizer. These tools are not yet officially supported for third-party front-ends. +It also contains ZKsync modifications of the standard [LLVM regression tests](https://llvm.org/docs/TestingGuide.html#regression-tests). + +## Building + +The ZKsync LLVM framework must be built with our tool called `zksync-llvm`: + +
+1. Install the system prerequisites. + + * Linux (Debian): + + Install the following packages: + ```shell + apt install cmake ninja-build curl git libssl-dev pkg-config clang lld + ``` + * Linux (Arch): + + Install the following packages: + ```shell + pacman -Syu which cmake ninja curl git pkg-config clang lld + ``` + + * MacOS: + + * Install the [HomeBrew](https://brew.sh) package manager. + * Install the following packages: + + ```shell + brew install cmake ninja coreutils + ``` + + * Install your choice of a recent LLVM/[Clang](https://clang.llvm.org) compiler, e.g. via [Xcode](https://developer.apple.com/xcode/), [Apple’s Command Line Tools](https://developer.apple.com/library/archive/technotes/tn2339/_index.html), or your preferred package manager. +
+ +
+2. Install Rust. + + * Follow the latest [official instructions](https://www.rust-lang.org/tools/install: + ```shell + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + . ${HOME}/.cargo/env + ``` + + > Currently we are not pinned to any specific version of Rust, so just install the latest stable build for your platform. +
+ +
+3. Install the ZKsync LLVM framework builder. + + * Install the builder using `cargo`: + ```shell + cargo install compiler-llvm-builder + ``` + + > The builder is not the ZKsync LLVM framework itself, but a tool that clones its repository and runs a sequence of build commands. By default it is installed in `~/.cargo/bin/`, which is recommended to be added to your `$PATH`. + +
+ +
+4. Create the `LLVM.lock` file. + + * In a directory in which you want the `llvm` directory, create an `LLVM.lock` file with the URL and branch or tag you want to build, for example: + + ```properties + url = "https://github.com/matter-labs/era-compiler-llvm" + branch = "main" + ``` + +
+ +
+5. Build LLVM. + + * Clone and build the ZKsync LLVM framework using the `zksync-llvm` tool: + ```shell + zksync-llvm clone + zksync-llvm build + ``` + + The build artifacts will end up in the `./target-llvm/target-final/` directory. + You may point your `LLVM_SYS_170_PREFIX` to that directory to use this build as a compiler dependency. + If built with the `--enable-tests` option, test tools will be in the `./target-llvm/build-final/` directory, along with copies of the build artifacts. For all supported build options, run `zksync-llvm build --help`. + +
+ +## Troubleshooting + +- Make sure your system is up-to-date. +- Make sure you have the required system prerequisites installed. +- Unset any LLVM-related environment variables you may have set. +- If you encounter any problems with the building process, please open an issue in the [era-compiler-llvm](https://github.com/matter-labs/era-compiler-llvm/issues) repository. -C-like languages use the [Clang](https://clang.llvm.org/) frontend. This -component compiles C, C++, Objective-C, and Objective-C++ code into LLVM bitcode --- and from there into object files, using LLVM. +## License -Other components include: -the [libc++ C++ standard library](https://libcxx.llvm.org), -the [LLD linker](https://lld.llvm.org), and more. +The ZKsync fork of the LLVM framework is distributed under the terms of +Apache License, Version 2.0 with LLVM Exceptions, ([LICENSE](LICENSE) or ) -## Getting the Source Code and Building LLVM +## Resources -Consult the -[Getting Started with LLVM](https://llvm.org/docs/GettingStarted.html#getting-the-source-code-and-building-llvm) -page for information on building and running LLVM. +[Official LLVM documentation](https://llvm.org/docs/GettingStarted.html) -For information on how to contribute to the LLVM project, please take a look at -the [Contributing to LLVM](https://llvm.org/docs/Contributing.html) guide. +## Official Links -## Getting in touch +- [Website](https://zksync.io/) +- [GitHub](https://github.com/matter-labs) +- [Twitter](https://twitter.com/zksync) +- [Twitter for Devs](https://twitter.com/zkSyncDevs) +- [Discord](https://join.zksync.dev/) -Join the [LLVM Discourse forums](https://discourse.llvm.org/), [Discord -chat](https://discord.gg/xS7Z362), -[LLVM Office Hours](https://llvm.org/docs/GettingInvolved.html#office-hours) or -[Regular sync-ups](https://llvm.org/docs/GettingInvolved.html#online-sync-ups). +## Disclaimer -The LLVM project has adopted a [code of conduct](https://llvm.org/docs/CodeOfConduct.html) for -participants to all modes of communication within the project. +ZKsync Era has been through extensive testing and audits, and although it is live, it is still in alpha state and +will undergo further audits and bug bounty programs. We would love to hear our community's thoughts and suggestions +about it! +It's important to note that forking it now could potentially lead to missing important +security updates, critical features, and performance improvements. diff --git a/SECURITY.md b/SECURITY.md index f6a5e6c01629..d77060818a22 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,5 +1,74 @@ -# Reporting LLVM Security Issues +# Security Policy -To report security issues in LLVM, please follow the steps outlined on the -[LLVM Security Group](https://llvm.org/docs/Security.html#how-to-report-a-security-issue) -page. +We truly appreciate efforts to discover and disclose security issues responsibly! + +## Vulnerabilities + +If you'd like to report a security issue in the repositories of matter-labs organization, please proceed to our +[Bug Bounty Program on Immunefi](https://docs.zksync.io/build/resources/audit-bug-bounty#bug-bounty-program). + +## Other Security Issues + +We take an impact-first approach instead of a rules-first approach. Therefore, if you believe you found the impactful +issue but can't report it via the Bug Bounty, please email us at +[security@matterlabs.dev](mailto:security@matterlabs.dev). + +### PGP Key + +The following PGP key may be used to communicate sensitive information to developers: + +Fingerprint: `5FED B2D0 EA2C 4906 DD66 71D7 A2C5 0B40 CE3C F297` + +``` +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBGEBmQkBEAD6tlkBEZFMvR8kOgxXX857nC2+oTik6TopJz4uCskuqDaeldMy +l+26BBzLkIeO1loS+bzVgnNFJRrGt9gv98MzNEHJVv6D7GsSLlUX/pz7Lxn0J4ry +o5XIk3MQTCUBdaXGs6GBLl5Xe8o+zNj4MKd4zjgDLinITNlE/YZCDsXyvYS3YFTQ +cwaUTNlawkKgw4BLaEqwB2JuyEhI9wx5X7ibjFL32sWMolYsNAlzFQzM09HCurTn +q0DYau9kPJARcEk9/DK2iq0z3gMCQ8iRTDaOWd8IbSP3HxcEoM5j5ZVAlULmjmUE +StDaMPLj0Kh01Tesh/j+vjchPXHT0n4zqi1+KOesAOk7SIwLadHfQMTpkU7G2fR1 +BrA5MtlzY+4Rm6o7qu3dpZ+Nc4iM3FUnaQRpvn4g5nTh8vjG94OCzX8DXWrCKyxx +amCs9PLDYOpx84fXYv4frkWpKh2digDSUGKhoHaOSnqyyvu3BNWXBCQZJ20rqEIu +sXOQMxWIoWCOOPRRvrHrKDA2hpoKjs3pGsProfpVRzb9702jhWpTfbDp9WjQlFtX +2ZIDxlwAxcugClgrp5JiUxvhg2A9lDNwCF7r1e68uNv5usBZQVKPJmnvS2nWgKy8 +x9oJsnwrEjxwiRHd34UvfMkwY9RENSJ+NoXqBdS7Lwz4m6vgbzq6K56WPQARAQAB +tCRaa1N5bmMgU2VjdXJpdHkgPHNlY3VyaXR5QHprc3luYy5pbz6JAk4EEwEKADgW +IQRf7bLQ6ixJBt1mcdeixQtAzjzylwUCYQGZCQIbAwULCQgHAgYVCgkICwIEFgID +AQIeAQIXgAAKCRCixQtAzjzyl5y8EAC/T3oq88Dak2b+5TlWdU2Gpm6924eAqlMt +y1KksDezzNQUlPiCUVllpin2PIjU/S+yzMWKXJA04LoVkEPfPOWjAaavLOjRumxu +MR6P2dVUg1InqzYVsJuRhKSpeexzNA5qO2BPM7/I2Iea1IoJPjogGbfXCo0r5kne +KU7a5GEa9eDHxpHTsbphQe2vpQ1239mUJrFpzAvILn6jV1tawMn5pNCXbsa8l6l2 +gtlyQPdOQECy77ZJxrgzaUBcs/RPzUGhwA/qNuvpF0whaCvZuUFMVuCTEu5LZka2 +I9Rixy+3jqBeONBgb+Fiz5phbiMX33M9JQwGONFaxdvpFTerLwPK2N1T8zcufa01 +ypzkWGheScFZemBxUwXwK4x579wjsnfrY11w0p1jtDgPTnLlXUA2mom4+7MyXPg0 +F75qh6vU1pdXaCVkruFgPVtIw+ccw2AxD50iZQ943ZERom9k165dR9+QxOVMXQ4P +VUxsFZWvK70/s8TLjsGljvSdSOa85iEUqSqh0AlCwIAxLMiDwh5s/ZgiHoIM6Xih +oCpuZyK9p0dn+DF/XkgAZ/S91PesMye3cGm6M5r0tS26aoc2Pk6X37Hha1pRALwo +MOHyaGjc/jjcXXxv6o55ALrOrzS0LQmLZ+EHuteCT15kmeY3kqYJ3og62KgiDvew +dKHENvg7d7kCDQRhAZleARAA6uD6WfdqGeKV5i170+kLsxR3QGav0qGNAbxpSJyn +iHQ8u7mQk3S+ziwN2AAopfBk1je+vCWtEGC3+DWRRfJSjLbtaBG8e6kLP3/cGA75 +qURz6glTG4nl5fcEAa6B1st0OxjVWiSLX3g/yjz8lznQb9awuRjdeHMnyx5DsJUN +d+Iu5KxGupQvKGOMKivSvC8VWk9taaQRpRF+++6stLCDk3ZtlxiopMs3X2jAp6xG +sOBbix1cv9BTsfaiL7XDL/gviqBPXYY5L42x6+jnPo5lROfnlLYkWrv6KZr7HD4k +tRXeaSwxLD2EkUyb16Jpp0be/ofvBtITGUDDLCGBiaXtx/v8d52MARjsyLJSYloj +1yiW01LfAiWHUC4z5jl2T7E7sicrlLH1M8Z6WbuqjdeaYwtfyPA2YCKr/3fn6pIo +D+pYaBSESmhA92P+XVaf5y2BZ6Qf8LveDpWwsVGdBGh9T0raA1ooe1GESLjmIjUa +z5AeQ/uXL5Md9I6bpMUUJYQiH19RPcFlJriI3phXyyf6Wlkk8oVEeCWyzcmw+x1V +deRTvE2x4WIwKGLXRNjin2j1AP7vU2HaNwlPrLijqdyi68+0irRQONoH7Qonr4ca +xWgL+pAaa3dWxf0xqK7uZFp4aTVWlr2uXtV/eaUtLmGMCU0jnjb109wg5L0F7WRT +PfEAEQEAAYkCNgQYAQoAIBYhBF/tstDqLEkG3WZx16LFC0DOPPKXBQJhAZleAhsM +AAoJEKLFC0DOPPKXAAEP/jK7ch9GkoaYlsuqY/aHtxEwVddUDOxjyn3FMDoln85L +/n8AmLQb2bcpKSqpaJwMbmfEyr5MDm8xnsBTfx3u6kgaLOWfKxjLQ6PM7kgIMdi4 +bfaRRuSEI1/R6c/hNpiGnzAeeexldH1we+eH1IVmh4crdat49S2xh7Qlv9ahvgsP +LfKl3rJ+aaX/Ok0AHzhvSfhFpPr1gAaGeaRt+rhlZsx2QyG4Ez8p2nDAcAzPiB3T +73ENoBIX6mTPfPm1UgrRyFKBqtUzAodz66j3r6ebBlWzIRg8iZenVMAxzjINAsxN +w1Bzfgsi5ZespfsSlmEaa7jJkqqDuEcLa2YuiFAue7Euqwz1aGeq1GfTicQioSCb +Ur/LGyz2Mj3ykbaP8p5mFVcUN51yQy6OcpvR/W1DfRT9SHFT/bCf9ixsjB2HlZGo +uxPJowwqmMgHd755ZzPDUM9YDgLI1yXdcYshObv3Wq537JAxnZJCGRK4Y8SwrMSh +8WRxlaM0AGWXiJFIDD4bQPIdnF3X8w0cGWE5Otkb8mMHOT+rFTVlDODwm1zF6oIG +PTwfVrpiZBwiUtfJol1exr/MzSPyGoJnYs3cRf2E3O+D1LbcR8w0LbjGuUy38Piz +ZO/vCeyJ3JZC5kE8nD+XBA4idwzh0BKEfH9t+WchQ3Up9rxyzLyQamoqt5Xby4pY +=xkM3 +-----END PGP PUBLIC KEY BLOCK----- +``` diff --git a/clang-tools-extra/clang-tidy/tool/clang-tidy-diff_Zegar.py b/clang-tools-extra/clang-tidy/tool/clang-tidy-diff_Zegar.py new file mode 100755 index 000000000000..a5f21a380e0b --- /dev/null +++ b/clang-tools-extra/clang-tidy/tool/clang-tidy-diff_Zegar.py @@ -0,0 +1,347 @@ +#!/usr/bin/env python3 +# +# ===- clang-tidy-diff.py - ClangTidy Diff Checker -----------*- python -*--===# +# +# 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 is a proposed revision of clang-tidy-diff.py by Piotr Zegar: https://reviews.llvm.org/D158929 +# for https://github.com/llvm/llvm-project/issues/65000, eventually to be committed upstream. +# It is being committed to the Matter Labs fork of LLVM, and might be further modified +# by its employees. + +r""" +ClangTidy Diff Checker +====================== + +This script reads input from a unified diff, runs clang-tidy on all changed +files and outputs clang-tidy warnings in changed lines only. This is useful to +detect clang-tidy regressions in the lines touched by a specific patch. +Example usage for git/svn users: + + git diff -U0 HEAD^ | clang-tidy-diff.py -p1 + svn diff --diff-cmd=diff -x-U0 | \ + clang-tidy-diff.py -fix -checks=-*,modernize-use-override + +""" + +import argparse +import glob +import json +import multiprocessing +import os +import re +import shutil +import subprocess +import sys +import tempfile +import threading +import traceback + +try: + import yaml +except ImportError: + yaml = None + +is_py2 = sys.version[0] == "2" + +if is_py2: + import Queue as queue +else: + import queue as queue + + +def run_tidy(task_queue, lock, timeout, failed_files): + watchdog = None + while True: + command = task_queue.get() + try: + proc = subprocess.Popen( + command, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + + if timeout is not None: + watchdog = threading.Timer(timeout, proc.kill) + watchdog.start() + + stdout, stderr = proc.communicate() + if proc.returncode != 0: + if proc.returncode < 0: + msg = "Terminated by signal %d : %s\n" % ( + -proc.returncode, + " ".join(command), + ) + stderr += msg.encode("utf-8") + failed_files.append(command) + + with lock: + sys.stdout.write(stdout.decode("utf-8") + "\n") + sys.stdout.flush() + if stderr: + sys.stderr.write(stderr.decode("utf-8") + "\n") + sys.stderr.flush() + except Exception as e: + with lock: + sys.stderr.write("Failed: " + str(e) + ": ".join(command) + "\n") + finally: + with lock: + if not (timeout is None or watchdog is None): + if not watchdog.is_alive(): + sys.stderr.write( + "Terminated by timeout: " + " ".join(command) + "\n" + ) + watchdog.cancel() + task_queue.task_done() + + +def start_workers(max_tasks, tidy_caller, arguments): + for _ in range(max_tasks): + t = threading.Thread(target=tidy_caller, args=arguments) + t.daemon = True + t.start() + + +def merge_replacement_files(tmpdir, mergefile): + """Merge all replacement files in a directory into a single file""" + # The fixes suggested by clang-tidy >= 4.0.0 are given under + # the top level key 'Diagnostics' in the output yaml files + mergekey = "Diagnostics" + merged = [] + for replacefile in glob.iglob(os.path.join(tmpdir, "*.yaml")): + content = yaml.safe_load(open(replacefile, "r")) + if not content: + continue # Skip empty files. + merged.extend(content.get(mergekey, [])) + + if merged: + # MainSourceFile: The key is required by the definition inside + # include/clang/Tooling/ReplacementsYaml.h, but the value + # is actually never used inside clang-apply-replacements, + # so we set it to '' here. + output = {"MainSourceFile": "", mergekey: merged} + with open(mergefile, "w") as out: + yaml.safe_dump(output, out) + else: + # Empty the file: + open(mergefile, "w").close() + + +def main(): + parser = argparse.ArgumentParser( + description="Run clang-tidy against changed files, and " + "output diagnostics only for modified " + "lines." + ) + parser.add_argument( + "-clang-tidy-binary", + metavar="PATH", + default="clang-tidy", + help="path to clang-tidy binary", + ) + parser.add_argument( + "-p", + metavar="NUM", + default=0, + help="strip the smallest prefix containing P slashes", + ) + parser.add_argument( + "-regex", + metavar="PATTERN", + default=None, + help="custom pattern selecting file paths to check " + "(case sensitive, overrides -iregex)", + ) + parser.add_argument( + "-iregex", + metavar="PATTERN", + default=r".*\.(cpp|cc|c\+\+|cxx|c|cl|h|hpp|m|mm|inc)", + help="custom pattern selecting file paths to check " + "(case insensitive, overridden by -regex)", + ) + parser.add_argument( + "-j", + type=int, + default=1, + help="number of tidy instances to be run in parallel.", + ) + parser.add_argument( + "-timeout", type=int, default=None, help="timeout per each file in seconds." + ) + parser.add_argument( + "-fix", action="store_true", default=False, help="apply suggested fixes" + ) + parser.add_argument( + "-checks", + help="checks filter, when not specified, use clang-tidy " "default", + default="", + ) + parser.add_argument("-use-color", action="store_true", help="Use colors in output") + parser.add_argument( + "-path", dest="build_path", help="Path used to read a compile command database." + ) + if yaml: + parser.add_argument( + "-export-fixes", + metavar="FILE", + dest="export_fixes", + help="Create a yaml file to store suggested fixes in, " + "which can be applied with clang-apply-replacements.", + ) + parser.add_argument( + "-extra-arg", + dest="extra_arg", + action="append", + default=[], + help="Additional argument to append to the compiler " "command line.", + ) + parser.add_argument( + "-extra-arg-before", + dest="extra_arg_before", + action="append", + default=[], + help="Additional argument to prepend to the compiler " "command line.", + ) + parser.add_argument( + "-quiet", + action="store_true", + default=False, + help="Run clang-tidy in quiet mode", + ) + parser.add_argument( + "-load", + dest="plugins", + action="append", + default=[], + help="Load the specified plugin in clang-tidy.", + ) + + clang_tidy_args = [] + argv = sys.argv[1:] + if "--" in argv: + clang_tidy_args.extend(argv[argv.index("--") :]) + argv = argv[: argv.index("--")] + + args = parser.parse_args(argv) + + # Extract changed lines for each file. + filename = None + lines_by_file = {} + for line in sys.stdin: + match = re.search('^\+\+\+\ "?(.*?/){%s}([^ \t\n"]*)' % args.p, line) + if match: + filename = match.group(2) + if filename is None: + continue + + if args.regex is not None: + if not re.match("^%s$" % args.regex, filename): + continue + else: + if not re.match("^%s$" % args.iregex, filename, re.IGNORECASE): + continue + + match = re.search("^@@.*\+(\d+)(,(\d+))?", line) + if match: + start_line = int(match.group(1)) + line_count = 1 + if match.group(3): + line_count = int(match.group(3)) + if line_count == 0: + continue + end_line = start_line + line_count - 1 + lines_by_file.setdefault(filename, []).append([start_line, end_line]) + + if not any(lines_by_file): + print("No relevant changes found.") + sys.exit(0) + + max_task_count = args.j + if max_task_count == 0: + max_task_count = multiprocessing.cpu_count() + max_task_count = min(len(lines_by_file), max_task_count) + + tmpdir = None + if yaml and args.export_fixes: + tmpdir = tempfile.mkdtemp() + + # Tasks for clang-tidy. + task_queue = queue.Queue(max_task_count) + # A lock for console output. + lock = threading.Lock() + + # List of files with a non-zero return code. + failed_files = [] + + # Run a pool of clang-tidy workers. + start_workers( + max_task_count, run_tidy, (task_queue, lock, args.timeout, failed_files) + ) + + # Form the common args list. + common_clang_tidy_args = [] + if args.fix: + common_clang_tidy_args.append("-fix") + if args.checks != "": + common_clang_tidy_args.append("-checks=" + args.checks) + if args.quiet: + common_clang_tidy_args.append("-quiet") + if args.build_path is not None: + common_clang_tidy_args.append("-p=%s" % args.build_path) + if args.use_color: + common_clang_tidy_args.append("--use-color") + for arg in args.extra_arg: + common_clang_tidy_args.append("-extra-arg=%s" % arg) + for arg in args.extra_arg_before: + common_clang_tidy_args.append("-extra-arg-before=%s" % arg) + for plugin in args.plugins: + common_clang_tidy_args.append("-load=%s" % plugin) + + for name in lines_by_file: + line_filter_json = json.dumps( + [{"name": name, "lines": lines_by_file[name]}], separators=(",", ":") + ) + + # Run clang-tidy on files containing changes. + command = [args.clang_tidy_binary] + command.append("-line-filter=" + line_filter_json) + if yaml and args.export_fixes: + # Get a temporary file. We immediately close the handle so clang-tidy can + # overwrite it. + (handle, tmp_name) = tempfile.mkstemp(suffix=".yaml", dir=tmpdir) + os.close(handle) + command.append("-export-fixes=" + tmp_name) + command.extend(common_clang_tidy_args) + command.append(name) + command.extend(clang_tidy_args) + + task_queue.put(command) + + # Application return code + return_code = 0 + + # Wait for all threads to be done. + task_queue.join() + # Application return code + return_code = 0 + if failed_files: + return_code = 1 + + if yaml and args.export_fixes: + print("Writing fixes to " + args.export_fixes + " ...") + try: + merge_replacement_files(tmpdir, args.export_fixes) + except: + sys.stderr.write("Error exporting fixes.\n") + traceback.print_exc() + return_code = 1 + + if tmpdir: + shutil.rmtree(tmpdir) + sys.exit(return_code) + + +if __name__ == "__main__": + main() diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 7bfdaaae45a9..fe1927ac8a32 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -6792,7 +6792,7 @@ class Sema final : public SemaBase { ExprResult BuildPredefinedExpr(SourceLocation Loc, PredefinedIdentKind IK); ExprResult ActOnPredefinedExpr(SourceLocation Loc, tok::TokenKind Kind); - ExprResult ActOnIntegerConstant(SourceLocation Loc, uint64_t Val); + ExprResult ActOnIntegerConstant(SourceLocation Loc, int64_t Val); bool CheckLoopHintExpr(Expr *E, SourceLocation Loc, bool AllowZero); diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index e95992b99f7e..853492964c8b 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -371,6 +371,9 @@ namespace clang { #define TYPE(Class, Base) \ ExpectedType Visit##Class##Type(const Class##Type *T); #include "clang/AST/TypeNodes.inc" + // EVM local begin + ExpectedType VisitBitIntType(const BitIntType *T); + // EVM local end // Importing declarations Error ImportDeclParts(NamedDecl *D, DeclarationName &Name, NamedDecl *&ToD, @@ -1073,6 +1076,13 @@ ExpectedType ASTNodeImporter::VisitAtomicType(const AtomicType *T){ return Importer.getToContext().getAtomicType(*UnderlyingTypeOrErr); } +// EVM local begin +ExpectedType ASTNodeImporter::VisitBitIntType(const BitIntType *T) { + return Importer.getToContext().getBitIntType(T->isUnsigned(), + T->getNumBits()); +} +// EVM local end + ExpectedType ASTNodeImporter::VisitBuiltinType(const BuiltinType *T) { switch (T->getKind()) { #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \ diff --git a/clang/lib/AST/Interp/IntegralAP.h b/clang/lib/AST/Interp/IntegralAP.h index b8aa21038256..8f6e9b7bfc0f 100644 --- a/clang/lib/AST/Interp/IntegralAP.h +++ b/clang/lib/AST/Interp/IntegralAP.h @@ -61,7 +61,7 @@ template class IntegralAP final { IntegralAP(APInt V) : V(V) {} /// Arbitrary value for uninitialized variables. - IntegralAP() : IntegralAP(-1, 3) {} + IntegralAP() : IntegralAP(Signed ? -1 : 7, 3) {} IntegralAP operator-() const { return IntegralAP(-V); } IntegralAP operator-(const IntegralAP &Other) const { @@ -112,7 +112,10 @@ template class IntegralAP final { template static IntegralAP from(Integral I, unsigned BitWidth) { - APInt Copy = APInt(BitWidth, static_cast(I), InputSigned); + // TODO: Avoid implicit trunc? + // See https://github.com/llvm/llvm-project/issues/112510. + APInt Copy = APInt(BitWidth, static_cast(I), InputSigned, + /*implicitTrunc=*/true); return IntegralAP(Copy); } diff --git a/clang/lib/CodeGen/CGVTT.cpp b/clang/lib/CodeGen/CGVTT.cpp index 20bd2c2fc2c6..989a07d09d50 100644 --- a/clang/lib/CodeGen/CGVTT.cpp +++ b/clang/lib/CodeGen/CGVTT.cpp @@ -85,8 +85,9 @@ CodeGenVTables::EmitVTTDefinition(llvm::GlobalVariable *VTT, cast(VTable->getValueType()) ->getElementType(AddressPoint.VTableIndex)); unsigned Offset = ComponentSize * AddressPoint.AddressPointIndex; - llvm::ConstantRange InRange(llvm::APInt(32, -Offset, true), - llvm::APInt(32, VTableSize - Offset, true)); + llvm::ConstantRange InRange( + llvm::APInt(32, (int)-Offset, true), + llvm::APInt(32, (int)(VTableSize - Offset), true)); llvm::Constant *Init = llvm::ConstantExpr::getGetElementPtr( VTable->getValueType(), VTable, Idxs, /*InBounds=*/true, InRange); diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 0be92fb2e275..e9853116ddab 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -2101,8 +2101,9 @@ ItaniumCXXABI::getVTableAddressPoint(BaseSubobject Base, unsigned VTableSize = ComponentSize * Layout.getVTableSize(AddressPoint.VTableIndex); unsigned Offset = ComponentSize * AddressPoint.AddressPointIndex; - llvm::ConstantRange InRange(llvm::APInt(32, -Offset, true), - llvm::APInt(32, VTableSize - Offset, true)); + llvm::ConstantRange InRange( + llvm::APInt(32, (int)-Offset, true), + llvm::APInt(32, (int)(VTableSize - Offset), true)); return llvm::ConstantExpr::getGetElementPtr( VTable->getValueType(), VTable, Indices, /*InBounds=*/true, InRange); } diff --git a/clang/lib/Parse/ParseInit.cpp b/clang/lib/Parse/ParseInit.cpp index 0a9a359cdaf9..dd59cb23236d 100644 --- a/clang/lib/Parse/ParseInit.cpp +++ b/clang/lib/Parse/ParseInit.cpp @@ -436,9 +436,9 @@ ExprResult Parser::createEmbedExpr() { ASTContext &Context = Actions.getASTContext(); SourceLocation StartLoc = ConsumeAnnotationToken(); if (Data->BinaryData.size() == 1) { - Res = IntegerLiteral::Create(Context, - llvm::APInt(CHAR_BIT, Data->BinaryData.back()), - Context.UnsignedCharTy, StartLoc); + Res = IntegerLiteral::Create( + Context, llvm::APInt(CHAR_BIT, (unsigned char)Data->BinaryData.back()), + Context.UnsignedCharTy, StartLoc); } else { auto CreateStringLiteralFromStringRef = [&](StringRef Str, QualType Ty) { llvm::APSInt ArraySize = diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 687b1be94592..b1a2718dee39 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -3575,9 +3575,10 @@ ExprResult Sema::ActOnCharacterConstant(const Token &Tok, Scope *UDLScope) { Lit, Tok.getLocation()); } -ExprResult Sema::ActOnIntegerConstant(SourceLocation Loc, uint64_t Val) { +ExprResult Sema::ActOnIntegerConstant(SourceLocation Loc, int64_t Val) { unsigned IntSize = Context.getTargetInfo().getIntWidth(); - return IntegerLiteral::Create(Context, llvm::APInt(IntSize, Val), + return IntegerLiteral::Create(Context, + llvm::APInt(IntSize, Val, /*isSigned=*/true), Context.IntTy, Loc); } diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index 6cbc075302eb..9b9115c9f2fe 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -5712,7 +5712,9 @@ StmtResult SemaOpenMP::ActOnOpenMPCanonicalLoop(Stmt *AStmt) { llvm_unreachable("unhandled unary increment operator"); } Step = IntegerLiteral::Create( - Ctx, llvm::APInt(Ctx.getIntWidth(LogicalTy), Direction), LogicalTy, {}); + Ctx, + llvm::APInt(Ctx.getIntWidth(LogicalTy), Direction, /*isSigned=*/true), + LogicalTy, {}); } else if (auto *IncBin = dyn_cast(Inc)) { if (IncBin->getOpcode() == BO_AddAssign) { Step = IncBin->getRHS(); diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 000000000000..c139341dc999 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,9 @@ +codecov: + require_ci_to_pass: no + +coverage: + status: + project: + default: false # disable the default status that measures entire project + patch: + default: false # disable the patch status that measures patch changes diff --git a/infra/docker-compose.yml b/infra/docker-compose.yml new file mode 100644 index 000000000000..4bff98ba6c75 --- /dev/null +++ b/infra/docker-compose.yml @@ -0,0 +1,10 @@ +version: '3.2' +services: + zk: + image: "matterlabs/llvm_runner:latest" + working_dir: /usr/src/zksync + command: "tail -f /dev/null" + volumes: + - ${GITHUB_WORKSPACE}:/usr/src/zksync + environment: + - SSH_PRIVATE_KEY=${SSH_PRIVATE_KEY} diff --git a/lld/CMakeLists.txt b/lld/CMakeLists.txt index 64c9f2380550..97add9ffecdd 100644 --- a/lld/CMakeLists.txt +++ b/lld/CMakeLists.txt @@ -188,6 +188,7 @@ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) ) endif() +add_subdirectory(lld-c) add_subdirectory(Common) add_subdirectory(tools/lld) diff --git a/lld/Common/DriverDispatcher.cpp b/lld/Common/DriverDispatcher.cpp index f5c8bcdef4e0..e4b1f423392e 100644 --- a/lld/Common/DriverDispatcher.cpp +++ b/lld/Common/DriverDispatcher.cpp @@ -150,6 +150,22 @@ static Driver whichDriver(llvm::SmallVectorImpl &argsV, return it->d; } +// EVM local begin +static DriverMemBuf whichDriver(llvm::SmallVectorImpl &argsV, + llvm::ArrayRef drivers) { + Flavor f = parseFlavor(argsV); + const auto *const it = + llvm::find_if(drivers, [=](auto &driverdef) { return driverdef.f == f; }); + if (it == drivers.end()) { + // Driver is invalid or not available in this build. + return [](llvm::ArrayRef, llvm::raw_pwrite_stream *, + llvm::ArrayRef, llvm::raw_ostream &, + llvm::raw_ostream &, bool, bool) { return false; }; + } + return it->d; +} +// EVM local end + namespace lld { bool inTestOutputDisabled = false; @@ -174,6 +190,35 @@ int unsafeLldMain(llvm::ArrayRef args, return r; } + +// EVM local begin +/// Universal linker main(). This linker emulates the gnu, darwin, or +/// windows linker based on the argv[0] or -flavor option. +int unsafeLldMainMemBuf(llvm::ArrayRef inBuffers, + llvm::raw_pwrite_stream *outBuffer, + llvm::ArrayRef args, + llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS, + llvm::ArrayRef drivers, + bool exitEarly) { + SmallVector argsV(make_range(args.begin(), args.end())); + DriverMemBuf d = whichDriver(argsV, drivers); + // Run the driver. If an error occurs, false will be returned. + const int r = !d(inBuffers, outBuffer, argsV, stdoutOS, stderrOS, exitEarly, + inTestOutputDisabled); + // At this point 'r' is either 1 for error, and 0 for no error. + + // Call exit() if we can to avoid calling destructors. + if (exitEarly) + exitLld(r); + + // Delete the global context and clear the global context pointer, so that it + // cannot be accessed anymore. + CommonLinkerContext::destroy(); + + return r; +} +// EVM local end } // namespace lld Result lld::lldMain(llvm::ArrayRef args, @@ -201,3 +246,34 @@ Result lld::lldMain(llvm::ArrayRef args, } return {r, /*canRunAgain=*/true}; } + +// EVM local begin +Result lld::lldMainMemBuf(llvm::ArrayRef inBuffers, + llvm::raw_pwrite_stream *outBuffer, + llvm::ArrayRef args, + llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS, + llvm::ArrayRef drivers) { + int r = 0; + { + // The crash recovery is here only to be able to recover from arbitrary + // control flow when fatal() is called (through setjmp/longjmp or + // __try/__except). + llvm::CrashRecoveryContext crc; + if (!crc.RunSafely([&]() { + r = unsafeLldMainMemBuf(inBuffers, outBuffer, args, stdoutOS, + stderrOS, drivers, /*exitEarly=*/false); + })) + return {crc.RetCode, /*canRunAgain=*/false}; + } + + // Cleanup memory and reset everything back in pristine condition. This path + // is only taken when LLD is in test, or when it is used as a library. + llvm::CrashRecoveryContext crc; + if (!crc.RunSafely([]() { CommonLinkerContext::destroy(); })) { + // The memory is corrupted beyond any possible recovery. + return {r, /*canRunAgain=*/false}; + } + return {r, /*canRunAgain=*/true}; +} +// EVM local end diff --git a/lld/ELF/Arch/ARM.cpp b/lld/ELF/Arch/ARM.cpp index 07a7535c4a23..bbe4012930a6 100644 --- a/lld/ELF/Arch/ARM.cpp +++ b/lld/ELF/Arch/ARM.cpp @@ -1195,7 +1195,7 @@ template void ObjFile::importCmseSymbols() { Defined *sym = reinterpret_cast(make()); // Initialize symbol fields. - memset(sym, 0, sizeof(Symbol)); + memset(static_cast(sym), 0, sizeof(Symbol)); sym->setName(CHECK(eSyms[i].getName(stringTable), this)); sym->value = eSym.st_value; sym->size = eSym.st_size; diff --git a/lld/ELF/Arch/EVM.cpp b/lld/ELF/Arch/EVM.cpp new file mode 100644 index 000000000000..9c083f590069 --- /dev/null +++ b/lld/ELF/Arch/EVM.cpp @@ -0,0 +1,94 @@ +//===- EVM.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 +// +//===----------------------------------------------------------------------===// +// +// EVM is a stack-based virtual machine with a word size of 256 bits intendent +// for execution of smart contracts in Ethereum blockchain environment. +// +// Since it is baremetal programming, there's usually no loader to load +// ELF files on EVMs. You are expected to link your program against address +// 0 and pull out a .text section from the result using objcopy, so that you +// can write the linked code to on-chip flush memory. You can do that with +// the following commands: +// +// ld.lld -Ttext=0 -o foo foo.o +// objcopy -O binary --only-section=.text foo output.bin +// +//===----------------------------------------------------------------------===// + +#include "InputFiles.h" +#include "Symbols.h" +#include "Target.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::support::endian; +using namespace llvm::ELF; +using namespace lld; +using namespace lld::elf; + +namespace { +class EVM final : public TargetInfo { +public: + uint32_t calcEFlags() const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + void relocate(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; +}; +} // namespace + +RelExpr EVM::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { + case R_EVM_DATA: + return R_ABS; + case R_EVM_NONE: + return R_NONE; + default: + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; + } +} + +void EVM::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { + switch (rel.type) { + case R_EVM_DATA: { + write32be(loc, static_cast(val)); + break; + } + default: + llvm_unreachable("unknown relocation"); + } +} + +TargetInfo *elf::getEVMTargetInfo() { + static EVM target; + return ⌖ +} + +static uint32_t getEFlags(InputFile *file) { + return cast>(file)->getObj().getHeader().e_flags; +} + +uint32_t EVM::calcEFlags() const { + assert(!ctx.objectFiles.empty()); + + const uint32_t flags = getEFlags(ctx.objectFiles[0]); + if (auto it = std::find_if_not( + ctx.objectFiles.begin(), ctx.objectFiles.end(), + [flags](InputFile *f) { return flags == getEFlags(f); }); + it != ctx.objectFiles.end()) + error(toString(*it) + + ": cannot link object files with incompatible target ISA"); + + return flags; +} diff --git a/lld/ELF/CMakeLists.txt b/lld/ELF/CMakeLists.txt index 83d816ddb060..764cac75a78a 100644 --- a/lld/ELF/CMakeLists.txt +++ b/lld/ELF/CMakeLists.txt @@ -24,6 +24,7 @@ add_lld_library(lldELF Arch/AMDGPU.cpp Arch/ARM.cpp Arch/AVR.cpp + Arch/EVM.cpp Arch/Hexagon.cpp Arch/LoongArch.cpp Arch/Mips.cpp diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index 28726d48e428..4072bfde42eb 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -157,6 +157,11 @@ struct Config { llvm::CachePruningPolicy thinLTOCachePolicy; llvm::SetVector dependencyFiles; // for --dependency-file llvm::StringMap sectionStartMap; + // EVM local begin + llvm::ArrayRef inBuffers; + llvm::raw_pwrite_stream *outBuffer; + bool useIOMemoryBuffers = false; + // EVM local end llvm::StringRef bfdname; llvm::StringRef chroot; llvm::StringRef dependencyFile; @@ -288,6 +293,9 @@ struct Config { bool relaxGP; bool relocatable; bool resolveGroups; + // EVM local begin + bool evmAssembly; + // EVM local end bool relrGlibc = false; bool relrPackDynRelocs = false; llvm::DenseSet saveTempsArgs; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index eb6734dfd458..1fb6612b3b37 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -53,6 +53,9 @@ #include "llvm/Config/llvm-config.h" #include "llvm/LTO/LTO.h" #include "llvm/Object/Archive.h" +// EVM local begin +#include "llvm/Object/ELF.h" +// EVM local end #include "llvm/Object/IRObjectFile.h" #include "llvm/Remarks/HotnessThresholdParser.h" #include "llvm/Support/CommandLine.h" @@ -164,6 +167,53 @@ bool link(ArrayRef args, llvm::raw_ostream &stdoutOS, return errorCount() == 0; } + +// EVM local begin +bool linkMemBuf(ArrayRef inBuffers, + raw_pwrite_stream *outBuffer, ArrayRef args, + llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS, + bool exitEarly, bool disableOutput) { + // This driver-specific context will be freed later by unsafeLldMainMemBuf(). + auto *ctx = new CommonLinkerContext; + + ctx->e.initialize(stdoutOS, stderrOS, exitEarly, disableOutput); + ctx->e.cleanupCallback = []() { + elf::ctx.reset(); + symtab = SymbolTable(); + + outputSections.clear(); + symAux.clear(); + + tar = nullptr; + in.reset(); + + partitions.clear(); + partitions.emplace_back(); + + SharedFile::vernauxNum = 0; + }; + ctx->e.logName = args::getFilenameWithoutExe(args[0]); + ctx->e.errorLimitExceededMsg = "too many errors emitted, stopping now (use " + "--error-limit=0 to see all errors)"; + + config = ConfigWrapper(); + script = ScriptWrapper(); + + symAux.emplace_back(); + + partitions.clear(); + partitions.emplace_back(); + + config->progName = args[0]; + config->inBuffers = inBuffers; + config->outBuffer = outBuffer; + config->useIOMemoryBuffers = true; + + elf::ctx.driver.linkerMain(args); + + return errorCount() == 0; +} +// EVM local end } // namespace elf } // namespace lld @@ -1796,6 +1846,14 @@ static void readConfigs(opt::InputArgList &args) { } else { error(Twine("cannot find version script ") + arg->getValue()); } + + // EVM local begin + if (args.hasArg(OPT_evm_assembly)) { + config->evmAssembly = true; + config->unresolvedSymbols = UnresolvedPolicy::Ignore; + config->emitRelocs = true; + } + // EVM local end } // Some Config members do not directly correspond to any particular @@ -1868,13 +1926,19 @@ static void setConfigs(opt::InputArgList &args) { (!config->entry.empty() || (!config->shared && !config->relocatable)); if (config->entry.empty() && !config->relocatable) config->entry = config->emachine == EM_MIPS ? "__start" : "_start"; - if (config->outputFile.empty()) + if (!config->useIOMemoryBuffers && config->outputFile.empty()) // EVM local config->outputFile = "a.out"; + // EVM local begin + if (config->useIOMemoryBuffers && !config->outputFile.empty()) + error("specification of an out file name has no effect, as the memory " + "buffer will be used instead"); + // EVM local end + // Fail early if the output file or map file is not writable. If a user has a // long link, e.g. due to a large LTO link, they do not wish to run it and // find that it failed because there was a mistake in their command-line. - { + if (!config->useIOMemoryBuffers) { // EVM local llvm::TimeTraceScope timeScope("Create output files"); if (auto e = tryCreateFile(config->outputFile)) error("cannot open output file " + config->outputFile + ": " + @@ -1931,7 +1995,14 @@ void LinkerDriver::createFiles(opt::InputArgList &args) { } case OPT_script: case OPT_default_script: - if (std::optional path = searchScript(arg->getValue())) { + // EVM local begin + if (config->useIOMemoryBuffers) { + if (std::optional mb = readFile(arg->getValue())) + readLinkerScript(*mb); + break; + // EVM local end + } else if (std::optional path = + searchScript(arg->getValue())) { if (std::optional mb = readFile(*path)) { if (arg->getOption().matches(OPT_default_script)) { defaultScript = mb; diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index 48f5a9609ecf..14d2ddc7344f 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -249,15 +249,31 @@ std::optional elf::readFile(StringRef path) { log(path); config->dependencyFiles.insert(llvm::CachedHashString(path)); - auto mbOrErr = MemoryBuffer::getFile(path, /*IsText=*/false, - /*RequiresNullTerminator=*/false); - if (auto ec = mbOrErr.getError()) { - error("cannot open " + path + ": " + ec.message()); - return std::nullopt; + // EVM local begin + MemoryBufferRef mbref; + if (config->useIOMemoryBuffers) { + unsigned idx = 0; + if (path.getAsInteger(10, idx)) { + error(path + ": path should be an index"); + return std::nullopt; + } + if (idx >= config->inBuffers.size()) { + error("memory buffer index is out of range"); + return std::nullopt; + } + mbref = config->inBuffers[idx]; + // Don't take MB ownership, because it's owned externally. + } else { + auto mbOrErr = MemoryBuffer::getFile(path, /*IsText=*/false, + /*RequiresNullTerminator=*/false); + if (auto ec = mbOrErr.getError()) { + error("cannot open " + path + ": " + ec.message()); + return std::nullopt; + } + mbref = (*mbOrErr)->getMemBufferRef(); + ctx.memoryBuffers.push_back(std::move(*mbOrErr)); // take MB ownership } - - MemoryBufferRef mbref = (*mbOrErr)->getMemBufferRef(); - ctx.memoryBuffers.push_back(std::move(*mbOrErr)); // take MB ownership + // EVM local end if (tar) tar->append(relativeToRoot(path), mbref.getBuffer()); @@ -1678,6 +1694,10 @@ static uint16_t getBitcodeMachineKind(StringRef path, const Triple &t) { return t.isOSIAMCU() ? EM_IAMCU : EM_386; case Triple::x86_64: return EM_X86_64; + // EVM local begin + case Triple::evm: + return EM_EVM; + // EVM local end default: error(path + ": could not infer e_machine from bitcode target triple " + t.str()); diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index a165c813d425..aedc14a19bc9 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -446,6 +446,45 @@ void InputSection::copyRelocations(uint8_t *buf, auto *p = reinterpret_cast(buf); buf += sizeof(RelTy); + // EVM local begin + // The --evm-assembly mode implies the --emit-relocs option, which + // instructs the linker to emit both applied relocations + // (for defined symbols) and unresolved relocations + // (for undefined symbols). We do not need to retain the applied + // relocations, as their symbols will be removed from the output file. + // To remove them from the '.rela' section, we would need to create a + // new synthetic section, copy only the necessary relocations there, + // and map that section to the output file. However, a simpler approach + // seems to be to use the original relocation sections and change the type + // of unnecessary relocations to 'NONE'. Please note that we cannot simply + // omit them from the output file, as by the time 'copyRelocations' is + // called, the ELF header (which describes section sizes) has already been + // emitted. Therefore, we cannot alter the number of relocations in the + // output + // '.rela' section. + // For example, the resulting '.rela' sections might look like: + // + // Relocation section '.rela.text' at offset 0x1038 contains 9 entries: + // + // Offset Info Type Sym. Value Symbol's Name + Addend + // 00000000 00000000 R_EVM_NONE 0 + // 00000000 00000000 R_EVM_NONE 0 + // 00000000 00000000 R_EVM_NONE 0 + // 00000000 00000000 R_EVM_NONE 0 + // 0000001f 00000601 R_EVM_DATA 00000000 __linker_symbol__0 + 0 + // 00000023 00000701 R_EVM_DATA 00000000 __linker_symbol__1 + 0 + // 00000027 00000801 R_EVM_DATA 00000000 __linker_symbol__2 + 0 + // 0000002b 00000901 R_EVM_DATA 00000000 __linker_symbol__3 + 0 + // 0000002f 00000a01 R_EVM_DATA 00000000 __linker_symbol__4 + 0 + // + if (config->evmAssembly && sym.isDefined()) { + p->r_offset = 0; + p->r_addend = 0; + p->setSymbolAndType(0, 0, false); + continue; + } + // EVM local end + if (RelTy::HasAddend) p->r_addend = rel.addend; diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td index 74733efb28ff..4a76409cf09f 100644 --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -423,6 +423,8 @@ defm rpath: Eq<"rpath", "Add a DT_RUNPATH to the output">; def relocatable: F<"relocatable">, HelpText<"Create relocatable object file">; +def evm_assembly: F<"evm-assembly">, HelpText<"Create evm assembly object file">; + defm retain_symbols_file: Eq<"retain-symbols-file", "Retain only the symbols listed in the file">, MetaVarName<"">; diff --git a/lld/ELF/SymbolTable.cpp b/lld/ELF/SymbolTable.cpp index 258a78ab40bb..1a8d14536e09 100644 --- a/lld/ELF/SymbolTable.cpp +++ b/lld/ELF/SymbolTable.cpp @@ -58,7 +58,7 @@ void SymbolTable::wrap(Symbol *sym, Symbol *real, Symbol *wrap) { // alias for sym, but that could degrade the user experience of some tools // that can print out only one symbol for each location: sym is a preferred // name than real, but they might print out real instead. - memcpy(real, sym, sizeof(SymbolUnion)); + memcpy(static_cast(real), sym, sizeof(SymbolUnion)); real->isUsedInRegularObj = false; } @@ -89,7 +89,7 @@ Symbol *SymbolTable::insert(StringRef name) { symVector.push_back(sym); // *sym was not initialized by a constructor. Initialize all Symbol fields. - memset(sym, 0, sizeof(Symbol)); + memset(static_cast(sym), 0, sizeof(Symbol)); sym->setName(name); sym->partition = 1; sym->versionId = VER_NDX_GLOBAL; diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h index e764fe8d7363..d7bf74115bae 100644 --- a/lld/ELF/Symbols.h +++ b/lld/ELF/Symbols.h @@ -86,7 +86,7 @@ class Symbol { // The default copy constructor is deleted due to atomic flags. Define one for // places where no atomic is needed. - Symbol(const Symbol &o) { memcpy(this, &o, sizeof(o)); } + Symbol(const Symbol &o) { memcpy(static_cast(this), &o, sizeof(o)); } protected: const char *nameData; diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp index 3e221646ce24..66ef4e0f97c1 100644 --- a/lld/ELF/Target.cpp +++ b/lld/ELF/Target.cpp @@ -91,6 +91,10 @@ TargetInfo *elf::getTarget() { return getSystemZTargetInfo(); case EM_X86_64: return getX86_64TargetInfo(); + // EVM local begin + case EM_EVM: + return getEVMTargetInfo(); + // EVM local end default: fatal("unsupported e_machine value: " + Twine(config->emachine)); } diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h index 0cefa3181356..ac5bbc0d67f5 100644 --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -191,6 +191,9 @@ TargetInfo *getSPARCV9TargetInfo(); TargetInfo *getSystemZTargetInfo(); TargetInfo *getX86TargetInfo(); TargetInfo *getX86_64TargetInfo(); +// EVM local begin +TargetInfo *getEVMTargetInfo(); +// EVM local end template TargetInfo *getMipsTargetInfo(); struct ErrorPlace { diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index 8e3a746a08eb..b8c728dacd4f 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -2702,6 +2702,10 @@ static uint16_t getELFType() { return ET_DYN; if (config->relocatable) return ET_REL; + // EVM local begin + if (config->evmAssembly) + return ET_REL; + // EVM local end return ET_EXEC; } @@ -2756,6 +2760,28 @@ template void Writer::openFile() { return; } + // EVM local begin + if (config->useIOMemoryBuffers) { + // Create a special buffer that outputs its content to the + // raw memory stream (config->outBuffer) instead of a file. + // The ugly point in this design is that the buffer type is derived + // from FileOutputBuffer, but the file management functionality + // is not used. Probably this is fine, as otherwise we will need + // much more changes in the lld code. + Expected> bufferOrErr = + FileOutputBuffer::create(fileSize, *config->outBuffer); + + if (!bufferOrErr) { + error("failed to create memory buffer for the result: " + + llvm::toString(bufferOrErr.takeError())); + return; + } + + buffer = std::move(*bufferOrErr); + Out::bufferStart = buffer->getBufferStart(); + return; + } + // EVM local end unlinkAsync(config->outputFile); unsigned flags = 0; if (!config->relocatable) diff --git a/lld/include/lld-c/LLDAsLibraryC.h b/lld/include/lld-c/LLDAsLibraryC.h new file mode 100644 index 000000000000..ec3144ca14b5 --- /dev/null +++ b/lld/include/lld-c/LLDAsLibraryC.h @@ -0,0 +1,142 @@ +//==--------- LLDAsLibraryC.h - LLD Public 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 header declares the following EVM C interfaces: +// - 'LLVMLinkEVM' +// The interface to the LLD linker functionality (via lld-as-a-library) +// +// - 'LLVMIsELF' +// Checks if the given memory buffer contains an ELF EVM object file +// +// - 'LLVMGetUndefinedSymbolsEVM' +// Returns an array of undefined linker symbols +// +// - 'LLVMDisposeUndefinedSymbolsEVM' +// Disposes an array returned by the 'LLVMGetUndefinedSymbolsEVM' +// +// These functions use a notion of the 'Linker Symbol' which is generalization +// of a usual ELF global symbol. The main difference is that 'Linker Symbol' +// has a 20-byte value, whereas the maximum value width of a usual ELF symbol +// is just 8 bytes. In order to represent a 20-byte symbol value with its +// relocation, initial 'Linker Symbol' is split into five sub-symbols +// which are usual 32-bit ELF symbols. This split is performed by the LLVM MC +// layer. For example, if the codegen needs to represent a 20-byte relocatable +// value associated with the symbol 'symbol_id', the MC layer sequentially +// (in the binary layout) emits the following undefined symbols: +// +// '__linker_symbol_id_0' +// '__linker_symbol_id_1' +// '__linker_symbol_id_2' +// '__linker_symbol_id_3' +// '__linker_symbol_id_4' +// +// with associated 32-bit relocations. Each sub-symbol name is formed by +// prepending '__linker' and appending '_[0-4]'. MC layer also sets the +// ELF::STO_EVM_LINKER_SYMBOL flag in the 'st_other' field in the symbol +// table entry to distinguish such symbols from all others. +// In EVM, only these symbols are allowed to be undefined in an object +// code. All other cases must be treated as unreachable and denote a bug +// in the FE/LLVM codegen/Linker implementation. +// 'Linker Symbols' are resolved, i.e they receive their final 20-byte +// values, at the linkage stage when calling LLVMLinkEVM. +// For this, the 'LLVMLinkEVM' has parameters: +// - \p linkerSymbolNames, array of null-terminated linker symbol names +// - \p linkerSymbolValues, array of symbol values +// +// For example, if linkerSymbolNames[0] points to a string 'symbol_id', +// it takes the linkerSymbolValues[0] value which is 20-byte array +// 0xAAAAAAAABB.....EEEEEEEE) and creates five symbol definitions in +// a linker script: +// +// "__linker_symbol_id_0" = 0xAAAAAAAA +// "__linker_symbol_id_1" = 0xBBBBBBBB +// "__linker_symbol_id_2" = 0xCCCCCCCC +// "__linker_symbol_id_3" = 0xDDDDDDDD +// "__linker_symbol_id_4" = 0xEEEEEEEE +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_C_LLDASLIBRARYC_H +#define LLD_C_LLDASLIBRARYC_H + +#include "llvm-c/ExternC.h" +#include "llvm-c/Types.h" + +LLVM_C_EXTERN_C_BEGIN + +// Currently, the size of a linker symbol is limited to 20 bytes, as its the +// only usage is to represent Ethereum addresses which are of 160 bit width. +#define LINKER_SYMBOL_SIZE 20 + +/** Returns true if the \p inBuffer contains an EVM ELF object file. */ +LLVMBool LLVMIsELFEVM(LLVMMemoryBufferRef inBuffer); + +/** + * Performs the following steps, which are based on Ethereum's + * Assembly::assemble() logic: + * - Concatenates the .text sections of input ELF files referenced + * by __dataoffset* symbols from the first file. + * - Resolves undefined __dataoffset* and __datasize* symbols. + * - Gathers all undefined linker symbols (library references) from + * all files. + * - Ensures that the first file does not load and set + * immutables simultaneously. + * + * \p codeSegment, 0 means the first file has a deploy code, + * 1 - runtime code; + * \p inBuffers - relocatable ELF files to be assembled + * \p inBuffersIDs - their IDs + * \p outBuffer - resulting relocatable object file */ +LLVMBool LLVMAssembleEVM(uint64_t codeSegment, + const LLVMMemoryBufferRef inBuffers[], + const char *const inBuffersIDs[], + uint64_t inBuffersNum, LLVMMemoryBufferRef *outBuffer, + char **errorMessage); + +/** Returns an array of undefined linker symbol names + * from the ELF object provided in \p inBuffer. */ +void LLVMGetUndefinedReferencesEVM(LLVMMemoryBufferRef inBuffer, + char ***linkerSymbols, + uint64_t *numLinkerSymbols); + +/** Disposes of the array of symbols returned by the + * 'LLVMGetUndefinedReferences' function. */ +void LLVMDisposeUndefinedReferences(char *symbolNames[], uint64_t numSymbols); + +/** Resolves undefined linker symbols in the ELF object file \p inBuffer. + * Returns the ELF object file if any linker symbols remain unresolved; + * otherwise, returns the bytecode. */ +LLVMBool LLVMLinkEVM(LLVMMemoryBufferRef inBuffer, + LLVMMemoryBufferRef *outBuffer, + const char *const *linkerSymbolNames, + const char linkerSymbolValues[][LINKER_SYMBOL_SIZE], + uint64_t numLinkerSymbols, char **errorMessage); + +/** Returns the immutable symbol names and their offsets from the ELF + * object file provided in \p inBuffer. */ +uint64_t LLVMGetImmutablesEVM(LLVMMemoryBufferRef inBuffer, + char ***immutableIDs, + uint64_t **immutableOffsets); + +/** Returns an array of offsets of the linker symbol relocations form the + * ELF object file provided in \p inBuffer. */ +uint64_t LLVMGetSymbolOffsetsEVM(LLVMMemoryBufferRef inBuffer, + const char *symbolName, + uint64_t **symbolOffsets); + +/** Disposes of the immutable names and their offsets returned by + * 'LLVMGetImmutablesEVM'. */ +void LLVMDisposeImmutablesEVM(char **immutableIDs, uint64_t *immutableOffsets, + uint64_t numOfImmutables); + +/** Releases the array of linker symbol offsets. */ +void LLVMDisposeSymbolOffsetsEVM(uint64_t *offsets); + +LLVM_C_EXTERN_C_END + +#endif // LLD_C_LLDASLIBRARYC_H diff --git a/lld/include/lld/Common/Driver.h b/lld/include/lld/Common/Driver.h index 8520e6e7e257..35792ad4cbe7 100644 --- a/lld/include/lld/Common/Driver.h +++ b/lld/include/lld/Common/Driver.h @@ -10,6 +10,7 @@ #define LLD_COMMON_DRIVER_H #include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/MemoryBufferRef.h" #include "llvm/Support/raw_ostream.h" namespace lld { @@ -25,11 +26,24 @@ enum Flavor { using Driver = bool (*)(llvm::ArrayRef, llvm::raw_ostream &, llvm::raw_ostream &, bool, bool); +// EVM local begin +using DriverMemBuf = bool (*)(llvm::ArrayRef inBuffers, + llvm::raw_pwrite_stream *outBuffer, + llvm::ArrayRef, llvm::raw_ostream &, + llvm::raw_ostream &, bool, bool); +// EVM local end + struct DriverDef { Flavor f; Driver d; }; +// EVM local begin +struct DriverDefMemBuf { + Flavor f; + DriverMemBuf d; +}; +// EVM local begin struct Result { int retCode; bool canRunAgain; @@ -43,6 +57,13 @@ struct Result { // by cleanup. Result lldMain(llvm::ArrayRef args, llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS, llvm::ArrayRef drivers); +// EVM local begin +Result lldMainMemBuf(llvm::ArrayRef inBuffers, + llvm::raw_pwrite_stream *outBuffer, + llvm::ArrayRef args, + llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS, + llvm::ArrayRef drivers); +// EVM local end } // namespace lld // With this macro, library users must specify which drivers they use, provide @@ -56,6 +77,19 @@ Result lldMain(llvm::ArrayRef args, llvm::raw_ostream &stdoutOS, } \ } +// EVM local begin +#define LLD_HAS_DRIVER_MEM_BUF(name) \ + namespace lld { \ + namespace name { \ + bool linkMemBuf(llvm::ArrayRef inBuffers, \ + llvm::raw_pwrite_stream *outBuffer, \ + llvm::ArrayRef args, \ + llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS, \ + bool exitEarly, bool disableOutput); \ + } \ + } +// EVM local end + // An array which declares that all LLD drivers are linked in your executable. // Must be used along with LLD_HAS_DRIVERS. See examples in LLD unittests. #define LLD_ALL_DRIVERS \ @@ -66,4 +100,10 @@ Result lldMain(llvm::ArrayRef args, llvm::raw_ostream &stdoutOS, } \ } +// EVM local begin +#define LLD_ALL_DRIVERS_MEM_BUF \ + { \ + { lld::Gnu, &lld::elf::linkMemBuf } \ + } +// EVM local end #endif diff --git a/lld/lld-c/CMakeLists.txt b/lld/lld-c/CMakeLists.txt new file mode 100644 index 000000000000..1ab04cd41b0b --- /dev/null +++ b/lld/lld-c/CMakeLists.txt @@ -0,0 +1,19 @@ +add_lld_library(lldC + LLDAsLibraryC.cpp + + ${LLD_INCLUDE_DIR}/lld/Common + + LINK_COMPONENTS + Core + EVMDesc + ObjCopy + Object + Support + + LINK_LIBS + lldCommon + lldELF + + DEPENDS + intrinsics_gen + ) diff --git a/lld/lld-c/LLDAsLibraryC.cpp b/lld/lld-c/LLDAsLibraryC.cpp new file mode 100644 index 000000000000..20311d08cb50 --- /dev/null +++ b/lld/lld-c/LLDAsLibraryC.cpp @@ -0,0 +1,862 @@ +#include "lld-c/LLDAsLibraryC.h" +#include "lld/Common/Driver.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm-c/Core.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/ObjCopy/CommonConfig.h" +#include "llvm/ObjCopy/ConfigManager.h" +#include "llvm/ObjCopy/ObjCopy.h" +#include "llvm/Object/Binary.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/Alignment.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/MemoryBuffer.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace llvm; +using namespace object; +using namespace objcopy; + +LLD_HAS_DRIVER_MEM_BUF(elf) + +namespace llvm { +namespace EVM { +std::string getLinkerSymbolHash(StringRef SymName); +bool isLinkerSymbolName(StringRef Name); +std::string getLinkerSymbolName(StringRef SymName); +bool isSymbolSectionName(StringRef Name); +std::string getSymbolSectionName(StringRef Name); +std::string getSymbolIndexedName(StringRef Name, unsigned SubIdx); +std::string getDataSizeSymbol(StringRef SymbolName); +std::string getNonIndexedSymbolName(StringRef Name); +bool isDataOffsetSymbolName(StringRef Name); +std::string getDataOffsetSymbol(StringRef Name); +std::string extractDataOffseteName(StringRef SymbolName); +bool isLoadImmutableSymbolName(StringRef Name); +std::string getImmutableId(StringRef Name); +} // namespace EVM +} // namespace llvm + +enum class ReferenceSymbolType { Linker }; + +constexpr static unsigned subSymbolRelocSize = sizeof(uint32_t); + +static std::mutex lldMutex; + +/// Returns reference to the section content. The section is expected +/// the be present in the file. +static StringRef getSectionContent(const ObjectFile &file, + StringRef sectionName) { + section_iterator si = std::find_if(file.section_begin(), file.section_end(), + [§ionName](const SectionRef &sec) { + StringRef curSecName = + cantFail(sec.getName()); + return curSecName == sectionName; + }); + if (si == file.section_end()) + report_fatal_error(Twine("lld: expected ") + sectionName + + " in object file"); + + return cantFail(si->getContents()); +} + +static std::string getLinkerSubSymbolName(StringRef name, unsigned idx) { + return EVM::getSymbolIndexedName( + EVM::getLinkerSymbolName(EVM::getLinkerSymbolHash(name)), idx); +} + +/// Returns true if the object file \p file contains any other undefined +/// linker dependency symbols besides those passed in \p linkerSymbolNames. +static bool hasUndefinedReferenceSymbols(ObjectFile &file, + const char *const *linkerSymbolNames, + uint64_t numLinkerSymbols) { + StringSet<> symbolsToBeDefined; + // Create a set of possible symbols from the 'linkerSymbolNames' array. + for (unsigned symIdx = 0; symIdx < numLinkerSymbols; ++symIdx) { + for (unsigned subSymIdx = 0; + subSymIdx < LINKER_SYMBOL_SIZE / subSymbolRelocSize; ++subSymIdx) { + std::string subSymName = + getLinkerSubSymbolName(linkerSymbolNames[symIdx], subSymIdx); + if (!symbolsToBeDefined.insert(subSymName).second) + report_fatal_error(Twine("lld: duplicating reference symbol ") + + subSymName + "in object file"); + } + } + + for (const SymbolRef &sym : file.symbols()) { + uint32_t symFlags = cantFail(sym.getFlags()); + uint8_t other = ELFSymbolRef(sym).getOther(); + if ((other == ELF::STO_EVM_REFERENCE_SYMBOL) && + (symFlags & object::SymbolRef::SF_Undefined)) { + StringRef symName = cantFail(sym.getName()); + if (!symbolsToBeDefined.contains(symName)) + return true; + } + } + return false; +} + +/// Returns a string with the symbol definitions passed in +/// \p linkerSymbolValues. For each name from the \p linkerSymbolNames array it +/// creates five symbol definitions. For example, if the linkerSymbolNames[0] +/// points to a string 'symbol_id', it takes the linkerSymbolValues[0] value +/// (which is 20 byte array: 0xAAAAAAAABB.....EEEEEEEE) and creates five symbol +/// definitions: +/// +/// __linker_symbol_id_0 = 0xAAAAAAAA +/// __linker_symbol_id_1 = 0xBBBBBBBB +/// __linker_symbol_id_2 = 0xCCCCCCCC +/// __linker_symbol_id_3 = 0xDDDDDDDD +/// __linker_symbol_id_4 = 0xEEEEEEEE +/// +static std::string +createSymbolDefinitions(const char *const *linkerSymbolNames, + const char linkerSymbolValues[][LINKER_SYMBOL_SIZE], + uint64_t numLinkerSymbols) { + auto getSymbolDef = + [](StringRef symName, const char *symVal, size_t symSize, + std::function subSymNameFunc) + -> std::string { + std::string symbolDefBuf; + raw_string_ostream symbolDef(symbolDefBuf); + SmallString<128> hexStrSymbolVal; + toHex(ArrayRef(reinterpret_cast(symVal), symSize), + /*LowerCase*/ false, hexStrSymbolVal); + for (unsigned idx = 0; idx < symSize / subSymbolRelocSize; ++idx) { + symbolDef << subSymNameFunc(symName, idx); + symbolDef << " = 0x"; + symbolDef << hexStrSymbolVal + .substr(2 * subSymbolRelocSize * idx, + 2 * subSymbolRelocSize) + .str(); + symbolDef << ";\n"; + } + return symbolDef.str(); + }; + + std::string symbolsDefBuf; + raw_string_ostream symbolsDef(symbolsDefBuf); + for (uint64_t symNum = 0; symNum < numLinkerSymbols; ++symNum) + symbolsDef << getSymbolDef(linkerSymbolNames[symNum], + linkerSymbolValues[symNum], LINKER_SYMBOL_SIZE, + &getLinkerSubSymbolName); + + return symbolsDef.str(); +} + +/// Returns an array of names of undefined reference symbols in the +/// ELF object. +static char **LLVMGetUndefinedSymbols(const ObjectFile *oFile, + uint64_t *numSymbols, + ReferenceSymbolType symbolType) { + StringSet<> undefSymbols; + StringSet<> undefSubSymbols; + for (const SymbolRef &sym : oFile->symbols()) { + uint32_t symFlags = cantFail(sym.getFlags()); + uint8_t other = ELFSymbolRef(sym).getOther(); + if ((other == ELF::STO_EVM_REFERENCE_SYMBOL) && + (symFlags & object::SymbolRef::SF_Undefined)) { + StringRef subName = cantFail(sym.getName()); + if (symbolType == ReferenceSymbolType::Linker && + EVM::isLinkerSymbolName(subName)) { + undefSubSymbols.insert(subName); + std::string symName = EVM::getNonIndexedSymbolName(subName); + undefSymbols.insert(symName); + } + } + } + + *numSymbols = undefSymbols.size(); + if (!undefSymbols.size()) + return nullptr; + + char **undefSymbolNames = reinterpret_cast( + std::malloc(undefSymbols.size() * sizeof(char *))); + unsigned undefSymIdx = 0; + for (const StringSet<>::value_type &entry : undefSymbols) { + StringRef symName = entry.first(); + // Check that 'undefSubSymbols' forms a set of groups each consisting of + // five or eight sub-symbols depending on the symbol type. + for (unsigned idx = 0; idx < LINKER_SYMBOL_SIZE / subSymbolRelocSize; idx++) { + std::string subSymName = EVM::getSymbolIndexedName(symName, idx); + if (!undefSubSymbols.contains(subSymName)) { + report_fatal_error(Twine("lld: missing reference symbol ") + + subSymName); + } + } + std::string secName = EVM::getSymbolSectionName(symName); + undefSymbolNames[undefSymIdx++] = + strdup(getSectionContent(*oFile, secName).str().c_str()); + } + + // Sort the returned names in lexicographical order. + std::sort( + undefSymbolNames, undefSymbolNames + *numSymbols, + [](const char *s1, const char *s2) { return std::strcmp(s1, s2) < 0; }); + + return undefSymbolNames; +} + +/// Disposes an array with symbols returned by the +/// LLVMGetUndefinedReferences* functions. +void LLVMDisposeUndefinedReferences(char *symbolNames[], uint64_t numSymbols) { + for (unsigned idx = 0; idx < numSymbols; ++idx) + std::free(symbolNames[idx]); + std::free(symbolNames); +} + +//----------------------------------------------------------------------------// + +/// Create linker script with linker symbol definitions. +static std::string createEVMLinkerSymbolsDefinitions( + const char *const *linkerSymbolNames, + const char linkerSymbolValues[][LINKER_SYMBOL_SIZE], + uint64_t numLinkerSymbols) { + return createSymbolDefinitions(linkerSymbolNames, linkerSymbolValues, + numLinkerSymbols); +} + +/// Returns true if the \p inBuffer contains an EVM ELF object file. +LLVMBool LLVMIsELFEVM(LLVMMemoryBufferRef inBuffer) { + Expected> inBinaryOrErr = + ELFObjectFile::create(unwrap(inBuffer)->getMemBufferRef()); + if (!inBinaryOrErr) { + handleAllErrors(inBinaryOrErr.takeError(), [](const ErrorInfoBase &EI) {}); + return false; + } + return inBinaryOrErr.get().getArch() == Triple::evm; +} + +/// Returns an array of undefined linker symbol names +/// from the ELF object provided in \p inBuffer. +void LLVMGetUndefinedReferencesEVM(LLVMMemoryBufferRef inBuffer, + char ***linkerSymbols, + uint64_t *numLinkerSymbols) { + assert(linkerSymbols && numLinkerSymbols); + *linkerSymbols = nullptr; + *numLinkerSymbols = 0; + if (!LLVMIsELFEVM(inBuffer)) + return; + + std::unique_ptr inBinary = + cantFail(createBinary(unwrap(inBuffer)->getMemBufferRef())); + const auto *oFile = static_cast(inBinary.get()); + + *linkerSymbols = LLVMGetUndefinedSymbols(oFile, numLinkerSymbols, + ReferenceSymbolType::Linker); +} + +/// Resolves undefined linker symbols in the ELF object file \p inBuffer. +/// Returns the ELF object file if any linker symbols remain unresolved; +/// otherwise, returns the bytecode. +LLVMBool LLVMLinkEVM(LLVMMemoryBufferRef inBuffer, + LLVMMemoryBufferRef *outBuffer, + const char *const *linkerSymbolNames, + const char linkerSymbolValues[][LINKER_SYMBOL_SIZE], + uint64_t numLinkerSymbols, char **errorMessage) { + SmallVector localInMemBufRefs(2); + localInMemBufRefs[0] = *unwrap(inBuffer); + + *outBuffer = nullptr; + if (!LLVMIsELFEVM(inBuffer)) { + *errorMessage = strdup("Input binary is not an EVM ELF file"); + return true; + } + + std::unique_ptr inBinary = + cantFail(createBinary(localInMemBufRefs[0])); + assert(inBinary->isObject()); + bool shouldEmitRelocatable = + hasUndefinedReferenceSymbols(*static_cast(inBinary.get()), + linkerSymbolNames, numLinkerSymbols); + + std::string linkerScript = createEVMLinkerSymbolsDefinitions( + linkerSymbolNames, linkerSymbolValues, numLinkerSymbols); + + std::unique_ptr linkerScriptBuf = + MemoryBuffer::getMemBuffer(linkerScript, "1"); + + localInMemBufRefs[1] = linkerScriptBuf->getMemBufferRef(); + + SmallVector lldArgs; + lldArgs.push_back("ld.lld"); + + // Push the name of the linker script - '1'. + lldArgs.push_back("-T"); + lldArgs.push_back("1"); + + // Push the name of the input object file - '0'. + lldArgs.push_back("0"); + + // If all symbols are resolved, strip the ELF format and emit the final + // bytecode. Otherwise, emit an ELF relocatable file. + if (shouldEmitRelocatable) + lldArgs.push_back("--relocatable"); + else + lldArgs.push_back("--oformat=binary"); + + SmallString<0> codeString; + raw_svector_ostream ostream(codeString); + SmallString<0> errorString; + raw_svector_ostream errorOstream(errorString); + + // Lld-as-a-library is not thread-safe due to its global state, + // so we need to protect it from concurrent access by multiple threads. + std::unique_lock lock(lldMutex); + const lld::Result s = + lld::lldMainMemBuf(localInMemBufRefs, &ostream, lldArgs, outs(), + errorOstream, {{lld::Gnu, &lld::elf::linkMemBuf}}); + lock.unlock(); + + bool ret = !s.retCode && s.canRunAgain; + if (!ret) { + *errorMessage = strdup(errorString.c_str()); + return true; + } + + StringRef data = ostream.str(); + *outBuffer = LLVMCreateMemoryBufferWithMemoryRangeCopy(data.data(), + data.size(), "result"); + + return false; +} + +/// Finds symbol name sections and adds them to the \p namesMap. +/// A symbol name section has the following format: +/// +/// .symbol_name__linker_symbol__$[0-9a-f]{64}$__ +/// +static void getSymbolNameSections(const ObjectFile *oFile, const char *fileID, + StringMap> &namesMap) { + [[maybe_unused]] SmallVector sectionNames; + for (const SectionRef &sec : oFile->sections()) { + StringRef secName = cantFail(sec.getName()); + if (EVM::isSymbolSectionName(secName)) { + namesMap[secName].push_back(fileID); +#ifndef NDEBUG + sectionNames.push_back(secName); +#endif // NDEBUG + } + } + +#ifndef NDEBUG + // Verify that undefined linker symbols exist for the name sections. + StringMap expectedSymNames; + for (StringRef secName : sectionNames) { + StringRef refName = getSectionContent(*oFile, secName); + for (unsigned idx = 0; idx < LINKER_SYMBOL_SIZE / subSymbolRelocSize; + ++idx) { + std::string symName = getLinkerSubSymbolName(refName, idx); + expectedSymNames[symName]++; + } + } + + StringMap actualSymNames; + for (const SymbolRef &sym : oFile->symbols()) { + section_iterator symSec = cantFail(sym.getSection()); + // Undefined symbol has no related section. + if (symSec != oFile->section_end()) + continue; + + StringRef symName = cantFail(sym.getName()); + if (EVM::isLinkerSymbolName(symName)) + actualSymNames[symName]++; + } + assert(actualSymNames == expectedSymNames); +#endif // NDEBUG +} + +/// Creates a linker script to generate an 'assembly' ELF object file. +/// Here is an example of the script: +/// +/// ENTRY(0); +/// SECTIONS { +/// . = 0; +/// .text : SUBALIGN(1) { +/// __datasize__$12c297efd8baf$__ = 268; +/// __dataoffset__$b5e66d52578d$__ = ABSOLUTE(.); +/// __$b5e66d52578d$__(.text); +/// __dataoffset__$12c297efd8baf$__ = ABSOLUTE(.); +/// __$12c297efd8baf$__(.text); +/// __$b5e66d52578d$__(.metadata) +/// __datasize__$b5e66d52578d$__ = ABSOLUTE(.); +/// } +/// /DISCARD/ : { +/// } +/// } +/// VERSION { +/// { local: +/// __datasize__$12c297efd8baf$__; +/// __dataoffset__$b5e66d52578d$__; +/// __dataoffset__$12c297efd8baf$__; +/// __datasize__$b5e66d52578d$__; +/// }; +/// }; + +static std::string createEVMAssembeScript(ArrayRef memBufs, + ArrayRef depIDs, + const StringSet<> &depsToLink) { + assert(memBufs.size() == depIDs.size()); + + // Maps symbol name section to an array of object IDs that contain it. + StringMap> symbolNameSectionsMap; + + auto getDataSizeName = [](StringRef name) { + return EVM::getDataSizeSymbol(EVM::getLinkerSymbolHash(name)); + }; + + // A set of all the defined symbols in the script that should be removed + // from the symbol table. To achieve this, we set their visibility + // to 'local'. These symbols will later be removed using the 'objcopy' API. + std::string definedSymbolsBuf; + raw_string_ostream definedSymbols(definedSymbolsBuf); + + std::unique_ptr firstBinary = + cantFail(createBinary(unwrap(memBufs[0])->getMemBufferRef())); + const auto *firstObjFile = static_cast(firstBinary.get()); + getSymbolNameSections(firstObjFile, depIDs[0], symbolNameSectionsMap); + + std::string textSectionBuf; + raw_string_ostream textSection(textSectionBuf); + + // A set of all the defined symbols in the script that should be removed + // from the symbol table. To achieve this, we set their visibility to + // 'local'. These symbols will later be removed using the 'objcopy' API. + for (unsigned idx = 1; idx < memBufs.size(); ++idx) { + std::unique_ptr binary = + cantFail(createBinary(unwrap(memBufs[idx])->getMemBufferRef())); + const auto *objFile = static_cast(binary.get()); + std::string bufIdHash = EVM::getLinkerSymbolHash(depIDs[idx]); + if (depsToLink.count(bufIdHash)) + getSymbolNameSections(objFile, depIDs[idx], symbolNameSectionsMap); + + for (const SectionRef &sec : objFile->sections()) { + if (sec.isText()) { + assert(cantFail(sec.getName()) == ".text"); + std::string sym = getDataSizeName(depIDs[idx]); + definedSymbols << sym << ";\n"; + textSection << sym << " = " << sec.getSize() << ";\n"; + break; + } + } + } + + // __dataoffset_Id_1 = .; + // Id_1(.text); + // __dataoffset_Id_2 = .; + // Id_2(.text); + // ... + // __dataoffset_Id_N = .; + // Id_N(.text); + for (unsigned idx = 0; idx < memBufs.size(); ++idx) { + std::string bufIdHash = EVM::getLinkerSymbolHash(depIDs[idx]); + // Do not link the dependency if it's not referenced via + // __dataoffset. + if (idx != 0 && !depsToLink.count(bufIdHash)) + continue; + + std::string sym = EVM::getDataOffsetSymbol(bufIdHash); + definedSymbols << sym << ";\n"; + textSection << sym << " = ABSOLUTE(.);\n"; + textSection << bufIdHash << "(.text);\n"; + } + + // Append matadata section of the first object (if any). + textSection << EVM::getLinkerSymbolHash(depIDs[0]) << "(.metadata);\n"; + + // Define a symbol whose value is the total size of the output + // '.text' section. + std::string firstDataSizeSym = getDataSizeName(depIDs[0]); + definedSymbols << firstDataSizeSym << ";\n"; + textSection << firstDataSizeSym << " = ABSOLUTE(.);\n"; + + // When assembling multiple ELF files that reference the same library, + // the files will have identical (name and content) sections: + // + // .symbol_name__linker_symbol__$[0-9a-f]{64}$__ + // + // By default, the linker will concatenate the sections, which is not desired. + // We need to retain only one section with the original content in the output + // file. To achieve this, we discard all duplicate input sections and keep + // just one. + std::string discardSectionsBuf; + raw_string_ostream discardSections(discardSectionsBuf); + for (const auto &[secName, IDs] : symbolNameSectionsMap) { + // We need to remove all symbol name sections, retaining just one + // (regardless of which one). The sections to be removed should be placed + // in the /DISCARD/ output section. + assert(IDs.size() > 0); + for (StringRef id : drop_begin(IDs)) { + std::string idHash = EVM::getLinkerSymbolHash(id); + discardSections << idHash << '(' << secName << ");\n"; + } + } + + std::string script = + formatv(R"(ENTRY(0); +SECTIONS { + . = 0; + .text : SUBALIGN(1) { +{0} + } +/DISCARD/ : { +{2} +} +} +VERSION { + { local: +{1} + }; +};)", + textSection.str(), definedSymbols.str(), discardSections.str()); + + return script; +} + +/// Removes all local symbols from the object file, except for undefined +/// linker symbols and immutables from the first file. +/// This is done using the 'objcopy' API. +static LLVMMemoryBufferRef removeLocalSymbols(LLVMMemoryBufferRef inBuffer, + StringSet<> firstFileImmutables) { + std::unique_ptr inBinary = + cantFail(createBinary(unwrap(inBuffer)->getMemBufferRef())); + auto *oFile = static_cast(inBinary.get()); + + StringSet<> undefReferenceSymbols; + for (const SymbolRef &sym : oFile->symbols()) { + uint32_t symFlags = cantFail(sym.getFlags()); + uint8_t other = ELFSymbolRef(sym).getOther(); + if ((other == ELF::STO_EVM_REFERENCE_SYMBOL) && + (symFlags & object::SymbolRef::SF_Undefined)) { + undefReferenceSymbols.insert(cantFail(sym.getName())); + } + } + + auto errCallback = [](Error E) { + report_fatal_error(StringRef(toString(std::move(E)))); + return Error::success(); + }; + + // Create an 'objcopy' configuration that removes all local + // symbols while explicitly retaining the necessary ones. + ConfigManager Config; + Config.Common.DiscardMode = DiscardType::All; + for (const StringSet<>::value_type &entry : + llvm::concat::value_type>(undefReferenceSymbols, + firstFileImmutables)) { + if (Error E = Config.Common.SymbolsToKeep.addMatcher(NameOrPattern::create( + entry.first(), MatchStyle::Literal, errCallback))) + report_fatal_error(StringRef(toString(std::move(E)))); + } + + SmallString<0> dataVector; + raw_svector_ostream outStream(dataVector); + + if (Error Err = objcopy::executeObjcopyOnBinary(Config, *oFile, outStream)) + report_fatal_error(StringRef(toString(std::move(Err)))); + + MemoryBufferRef buffer(StringRef(dataVector.data(), dataVector.size()), ""); + Expected> result = createBinary(buffer); + + // Check the copied file. + if (!result) + report_fatal_error(StringRef(toString(result.takeError()))); + + StringRef data = outStream.str(); + StringRef bufName = unwrap(inBuffer)->getBufferIdentifier(); + return LLVMCreateMemoryBufferWithMemoryRangeCopy(data.data(), data.size(), + bufName.str().c_str()); +} + +/// Checks if the ELF file contains any undefined symbols, aside from +/// those used to represent library addresses. +static void getUndefinedNonRefSymbols(LLVMMemoryBufferRef inBuffer, + SmallVectorImpl &undefSyms) { + std::unique_ptr inBinary = + cantFail(createBinary(unwrap(inBuffer)->getMemBufferRef())); + auto *oFile = static_cast(inBinary.get()); + + for (const SymbolRef &sym : oFile->symbols()) { + uint32_t symFlags = cantFail(sym.getFlags()); + uint8_t other = ELFSymbolRef(sym).getOther(); + if ((other != ELF::STO_EVM_REFERENCE_SYMBOL) && + (symFlags & object::SymbolRef::SF_Undefined)) + undefSyms.push_back(cantFail(sym.getName())); + } +} + +/// Returns an array of offsets for the linker symbol relocations. +/// These are the symbol offsets in the generated bytecode. +uint64_t LLVMGetSymbolOffsetsEVM(LLVMMemoryBufferRef inBuffer, + const char *symbolName, + uint64_t **symbolOffsets) { + std::unique_ptr inBinary = + cantFail(createBinary(unwrap(inBuffer)->getMemBufferRef())); + auto *elfFile = static_cast(inBinary.get()); + + SmallVector Offsets; + // The relocation of a linker symbol is expressed as a sequence of + // five consecutive 32-bit relocations. We need only the first one. + std::string subSymName = getLinkerSubSymbolName(symbolName, 0); + for (const ELFSectionRef relocsSec : elfFile->sections()) { + // Find the relocation section. It should be .rela.text. + if (relocsSec.relocations().empty() || + !cantFail(relocsSec.getRelocatedSection())->isText()) + continue; + + // Check relocations of the specified type. + for (const ELFRelocationRef rel : relocsSec.relocations()) { + if (rel.getType() != ELF::R_EVM_DATA) + continue; + + elf_symbol_iterator sym = rel.getSymbol(); + uint32_t symFlags = cantFail(sym->getFlags()); + StringRef symName = cantFail(sym->getName()); + if ((sym->getOther() == llvm::ELF::STO_EVM_REFERENCE_SYMBOL) && + (symFlags & object::SymbolRef::SF_Undefined) && + (symName == subSymName)) + Offsets.push_back(rel.getOffset()); + } + } + + *symbolOffsets = reinterpret_cast( + std::malloc(Offsets.size() * sizeof(uint64_t))); + + sort(Offsets); + copy(Offsets, *symbolOffsets); + + return Offsets.size(); +} + +/// Performs the following steps, which are based on Ethereum's +/// Assembly::assemble() logic: +/// - Concatenates the .text sections of input ELF files referenced +/// by __dataoffset* symbols from the first file. +/// - Resolves undefined __dataoffset* and __datasize* symbols. +/// - Gathers all undefined linker symbols (library references) from +/// all files. +/// - Ensures that the first file does not load and set +/// immutables simultaneously. +/// +/// \p codeSegment, 0 means the first file has a deploy code, +/// 1 - runtime code; +/// \p inBuffers - relocatable ELF files to be assembled +/// \p inBuffersIDs - their IDs +/// \p outBuffer - resulting relocatable object file +LLVMBool LLVMAssembleEVM(uint64_t codeSegment, + const LLVMMemoryBufferRef inBuffers[], + const char *const inBuffersIDs[], + uint64_t inBuffersNum, LLVMMemoryBufferRef *outBuffer, + char **errorMessage) { + SmallVector localInMemBufRefs(inBuffersNum + 1); + SmallVector> localInMemBufs(inBuffersNum + 1); + + // TODO: #740. Verify that the object files contain sections with the original + // inBuffersIDs, i.e. before the hash is applied. + for (unsigned idx = 0; idx < inBuffersNum; ++idx) { + MemoryBufferRef ref = *unwrap(inBuffers[idx]); + // We need to copy the buffers to change their names, as the linking + // process depends on them. + localInMemBufs[idx] = MemoryBuffer::getMemBufferCopy( + ref.getBuffer(), EVM::getLinkerSymbolHash(inBuffersIDs[idx])); + localInMemBufRefs[idx] = localInMemBufs[idx]->getMemBufferRef(); + } + + std::unique_ptr firstBinary = + cantFail(createBinary(localInMemBufRefs[0])); + const auto *firstObjFile = static_cast(firstBinary.get()); + + // Retrieve the object names referenced by the first file. + // Retrieve the immutable symbols from the first file. + StringSet<> firstLoadImmutables; + StringSet<> firstDataOffsetRefs; + for (const SymbolRef &sym : firstObjFile->symbols()) { + StringRef symName = cantFail(sym.getName()); + if (EVM::isLoadImmutableSymbolName(symName)) + firstLoadImmutables.insert(symName); + else if (EVM::isDataOffsetSymbolName(symName)) { + std::string objName = EVM::extractDataOffseteName(symName); + firstDataOffsetRefs.insert(objName); + } + } + + // Check if the first (idx == 1) dependency file contains loadimmutable + // symbols. + // A 'codeSegment' value of 0 indicates that the first file contains a + // deploy code, which implies that the first dependency is the corresponding + // runtime code. + bool depHasLoadImmutable = false; + if (codeSegment == 0 && inBuffersNum > 1) { + std::unique_ptr binary = + cantFail(createBinary(localInMemBufRefs[1])); + const auto *oFile = static_cast(binary.get()); + + for (const SymbolRef &sym : oFile->symbols()) { + section_iterator symSec = cantFail(sym.getSection()); + if (symSec == oFile->section_end()) + continue; + + StringRef symName = cantFail(sym.getName()); + if (EVM::isLoadImmutableSymbolName(symName)) + depHasLoadImmutable = true; + } + } + + if (firstLoadImmutables.size() > 0 && depHasLoadImmutable) + report_fatal_error("lld: assembly both sets up and loads immutables"); + + std::string linkerScript = createEVMAssembeScript( + ArrayRef(inBuffers, inBuffersNum), ArrayRef(inBuffersIDs, inBuffersNum), + firstDataOffsetRefs); + + std::unique_ptr scriptBuf = + MemoryBuffer::getMemBuffer(linkerScript, "script.x"); + localInMemBufRefs[inBuffersNum] = scriptBuf->getMemBufferRef(); + + SmallVector lldArgs; + lldArgs.push_back("ld.lld"); + + // Use file name remapping (a linker feature) to replace file names with + // indexes in the array of memory buffers. + const std::string remapStr("--remap-inputs="); + SmallVector args; + for (unsigned idx = 0; idx < inBuffersNum; ++idx) { + std::string idHash = EVM::getLinkerSymbolHash(inBuffersIDs[idx]); + if (idx > 0 && !firstDataOffsetRefs.count(idHash)) + continue; + + args.emplace_back(remapStr + idHash + "=" + std::to_string(idx)); + args.emplace_back(std::to_string(idx)); + } + + args.emplace_back("-T"); + args.emplace_back(std::to_string(inBuffersNum)); + + for (const std::string &arg : args) + lldArgs.push_back(arg.c_str()); + + lldArgs.push_back("--evm-assembly"); + + SmallString<0> codeString; + raw_svector_ostream ostream(codeString); + SmallString<0> errorString; + raw_svector_ostream errorOstream(errorString); + + // Lld-as-a-library is not thread-safe due to its global state, + // so we need to protect it from concurrent access by multiple threads. + std::unique_lock lock(lldMutex); + const lld::Result s = + lld::lldMainMemBuf(localInMemBufRefs, &ostream, lldArgs, outs(), + errorOstream, {{lld::Gnu, &lld::elf::linkMemBuf}}); + lock.unlock(); + + bool ret = !s.retCode && s.canRunAgain; + if (!ret) { + *errorMessage = strdup(errorString.c_str()); + return true; + } + + StringRef data = ostream.str(); + LLVMMemoryBufferRef Tmp = LLVMCreateMemoryBufferWithMemoryRangeCopy( + data.data(), data.size(), + EVM::getLinkerSymbolHash(inBuffersIDs[0]).c_str()); + + SmallVector undefSyms; + getUndefinedNonRefSymbols(Tmp, undefSyms); + if (!undefSyms.empty()) { + std::string storage; + raw_string_ostream errMsg(storage); + for (StringRef name : undefSyms) + errMsg << "non-ref undefined symbol: " << name << '\n'; + + *errorMessage = strdup(errMsg.str().c_str()); + return true; + } + + *outBuffer = removeLocalSymbols(Tmp, firstLoadImmutables); + LLVMDisposeMemoryBuffer(Tmp); + + return false; +} + +/// Returns the immutable symbol names and their offsets from the ELF +/// object file provided in \p inBuffer. +uint64_t LLVMGetImmutablesEVM(LLVMMemoryBufferRef inBuffer, + char ***immutableIDs, + uint64_t **immutableOffsets) { + std::unique_ptr inBinary = + cantFail(createBinary(unwrap(inBuffer)->getMemBufferRef())); + const auto *objFile = static_cast(inBinary.get()); + + // Maps immutable IDs to their references in the object code. + StringMap> immutablesMap; + for (const SymbolRef &sym : objFile->symbols()) { + section_iterator symSec = cantFail(sym.getSection()); + if (symSec == objFile->section_end()) + continue; + + StringRef symName = cantFail(sym.getName()); + if (EVM::isLoadImmutableSymbolName(symName)) { + std::string Id = EVM::getImmutableId(symName); + uint64_t symOffset = cantFail(sym.getValue()); + // The symbol points to the beginning of a PUSH32 instruction. + // We have to add 1 (opcode size) to get offset to the PUSH32 + // instruction operand. + immutablesMap[Id].push_back(symOffset + 1); + } + } + + if (immutablesMap.empty()) { + *immutableIDs = nullptr; + *immutableOffsets = nullptr; + return 0; + } + + uint64_t numOfImmutables = 0; + for (const auto &[id, offsets] : immutablesMap) { + numOfImmutables += offsets.size(); + }; + + *immutableIDs = + reinterpret_cast(std::malloc(numOfImmutables * sizeof(char *))); + *immutableOffsets = reinterpret_cast( + std::malloc(numOfImmutables * sizeof(uint64_t))); + + unsigned idx = 0; + for (const auto &[id, offsets] : immutablesMap) { + for (uint64_t offset : offsets) { + assert(idx < numOfImmutables); + (*immutableIDs)[idx] = strdup(id.str().c_str()); + (*immutableOffsets)[idx++] = offset; + } + } + + return numOfImmutables; +} + +/// Disposes of the immutable names and their offsets returned by +/// 'LLVMGetImmutablesEVM'. +void LLVMDisposeImmutablesEVM(char **immutableIDs, uint64_t *immutableOffsets, + uint64_t numOfImmutables) { + for (unsigned idx = 0; idx < numOfImmutables; ++idx) + std::free(immutableIDs[idx]); + + std::free(immutableIDs); + std::free(immutableOffsets); +} + +void LLVMDisposeSymbolOffsetsEVM(uint64_t *offsets) { std::free(offsets); } diff --git a/lld/test/lit.cfg.py b/lld/test/lit.cfg.py index d309c2ad4ee2..177ceb6f58c4 100644 --- a/lld/test/lit.cfg.py +++ b/lld/test/lit.cfg.py @@ -171,3 +171,11 @@ # ELF tests expect the default target for ld.lld to be ELF. if config.ld_lld_default_mingw: config.excludes.append("ELF") + +# EVM local begin +if config.target_triple.startswith("evm"): + config.excludes.append("wasm") + config.excludes.append("MachO") + config.excludes.append("COFF") + config.excludes.append("MinGW") +# EVM local end diff --git a/lld/unittests/CMakeLists.txt b/lld/unittests/CMakeLists.txt index ffaea3f20783..25cdf788504c 100644 --- a/lld/unittests/CMakeLists.txt +++ b/lld/unittests/CMakeLists.txt @@ -4,3 +4,6 @@ endfunction() add_subdirectory(AsLibAll) add_subdirectory(AsLibELF) +# EVM local begin +add_subdirectory(EVM) +# EVM local End diff --git a/lld/unittests/EVM/CMakeLists.txt b/lld/unittests/EVM/CMakeLists.txt new file mode 100644 index 000000000000..6514cde4f0f4 --- /dev/null +++ b/lld/unittests/EVM/CMakeLists.txt @@ -0,0 +1,17 @@ +add_lld_unittests(EVMLLDTests + LLDTest.cpp +) + +target_link_libraries(EVMLLDTests + PRIVATE + lldCommon + lldC + lldELF + LLVMCore + LLVMEVMCodeGen + LLVMEVMInfo + LLVMEVMDesc + LLVMIRReader + LLVMObjCopyC + LLVMTarget +) diff --git a/lld/unittests/EVM/Inputs/A_deploy.ll b/lld/unittests/EVM/Inputs/A_deploy.ll new file mode 100644 index 000000000000..7d045d64faba --- /dev/null +++ b/lld/unittests/EVM/Inputs/A_deploy.ll @@ -0,0 +1,20 @@ +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" +declare i256 @llvm.evm.datasize(metadata) +declare i256 @llvm.evm.dataoffset(metadata) +declare i256 @llvm.evm.codesize() + +define i256 @init() { + %datasize = tail call i256 @llvm.evm.datasize(metadata !1) + %dataoffset = tail call i256 @llvm.evm.dataoffset(metadata !1) + %res = add i256 %datasize, %dataoffset + ret i256 %res +} +define i256 @args_len() { + %datasize = tail call i256 @llvm.evm.datasize(metadata !2) + %codesize = tail call i256 @llvm.evm.codesize() + %res = sub i256 %codesize, %datasize + ret i256 %res +} +!1 = !{!"A_38_deployed"} +!2 = !{!"A_38"} diff --git a/lld/unittests/EVM/Inputs/A_deployed.ll b/lld/unittests/EVM/Inputs/A_deployed.ll new file mode 100644 index 000000000000..134e349b975b --- /dev/null +++ b/lld/unittests/EVM/Inputs/A_deployed.ll @@ -0,0 +1,13 @@ +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" +declare i256 @llvm.evm.linkersymbol(metadata) +declare i256 @llvm.evm.datasize(metadata) + +define i256 @runtime() { + %lib = call i256 @llvm.evm.linkersymbol(metadata !2) + %datasize = tail call i256 @llvm.evm.datasize(metadata !1) + %res = add i256 %lib, %datasize + ret i256 %res +} +!1 = !{!"A_38_deployed"} +!2 = !{!"library_id"} diff --git a/lld/unittests/EVM/Inputs/D_deploy.ll b/lld/unittests/EVM/Inputs/D_deploy.ll new file mode 100644 index 000000000000..9eb4097fd5e6 --- /dev/null +++ b/lld/unittests/EVM/Inputs/D_deploy.ll @@ -0,0 +1,20 @@ +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" +declare i256 @llvm.evm.datasize(metadata) +declare i256 @llvm.evm.dataoffset(metadata) +declare i256 @llvm.evm.codesize() + +define i256 @init() { + %datasize = tail call i256 @llvm.evm.datasize(metadata !1) + %dataoffset = tail call i256 @llvm.evm.dataoffset(metadata !1) + %res = add i256 %datasize, %dataoffset + ret i256 %res +} +define i256 @args_len() { + %datasize = tail call i256 @llvm.evm.datasize(metadata !2) + %codesize = tail call i256 @llvm.evm.codesize() + %res = sub i256 %codesize, %datasize + ret i256 %res +} +!1 = !{!"D_51_deployed"} +!2 = !{!"D_51"} diff --git a/lld/unittests/EVM/Inputs/D_deployed.ll b/lld/unittests/EVM/Inputs/D_deployed.ll new file mode 100644 index 000000000000..cc0d99edb63a --- /dev/null +++ b/lld/unittests/EVM/Inputs/D_deployed.ll @@ -0,0 +1,9 @@ +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" +declare i256 @llvm.evm.loadimmutable(metadata) + +define i256 @runtime() { + %res = call i256 @llvm.evm.loadimmutable(metadata !1) + ret i256 %res +} +!1 = !{!"40"} diff --git a/lld/unittests/EVM/Inputs/R_deploy.ll b/lld/unittests/EVM/Inputs/R_deploy.ll new file mode 100644 index 000000000000..883d5146050c --- /dev/null +++ b/lld/unittests/EVM/Inputs/R_deploy.ll @@ -0,0 +1,34 @@ +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" +declare i256 @llvm.evm.datasize(metadata) +declare i256 @llvm.evm.dataoffset(metadata) +declare i256 @llvm.evm.codesize() + +define i256 @init() { + %datasize = tail call i256 @llvm.evm.datasize(metadata !1) + %dataoffset = tail call i256 @llvm.evm.dataoffset(metadata !1) + %res = add i256 %datasize, %dataoffset + ret i256 %res +} +define i256 @A_init() { + %datasize = tail call i256 @llvm.evm.datasize(metadata !4) + %dataoffset = tail call i256 @llvm.evm.dataoffset(metadata !4) + %res = add i256 %datasize, %dataoffset + ret i256 %res +} +define i256 @args_len() { + %datasize = tail call i256 @llvm.evm.datasize(metadata !2) + %codesize = tail call i256 @llvm.evm.codesize() + %res = sub i256 %codesize, %datasize + ret i256 %res +} +define i256 @A_runtime() { + %datasize = tail call i256 @llvm.evm.datasize(metadata !3) + %dataoffset = tail call i256 @llvm.evm.dataoffset(metadata !3) + %res = add i256 %datasize, %dataoffset + ret i256 %res +} +!1 = !{!"R_107_deployed"} +!2 = !{!"R_107"} +!3 = !{!"A_38.A_38_deployed"} +!4 = !{!"A_38"} diff --git a/lld/unittests/EVM/Inputs/R_deployed.ll b/lld/unittests/EVM/Inputs/R_deployed.ll new file mode 100644 index 000000000000..97b4b99057c0 --- /dev/null +++ b/lld/unittests/EVM/Inputs/R_deployed.ll @@ -0,0 +1,26 @@ +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" +declare i256 @llvm.evm.loadimmutable(metadata) +declare i256 @llvm.evm.datasize(metadata) +declare i256 @llvm.evm.dataoffset(metadata) + +define i256 @runtime() { + %res = tail call i256 @llvm.evm.loadimmutable(metadata !1) + ret i256 %res +} + +define i256 @get_runtimecode() { + %datasize = tail call i256 @llvm.evm.datasize(metadata !2) + %dataoffset = tail call i256 @llvm.evm.dataoffset(metadata !2) + %res = add i256 %datasize, %dataoffset + ret i256 %res +} +define i256 @get_initcode() { + %datasize = tail call i256 @llvm.evm.datasize(metadata !3) + %dataoffset = tail call i256 @llvm.evm.dataoffset(metadata !3) + %res = add i256 %datasize, %dataoffset + ret i256 %res +} +!1 = !{!"53"} +!2 = !{!"A_38.A_38_deployed"} +!3 = !{!"D_51"} diff --git a/lld/unittests/EVM/Inputs/deployIr.ll b/lld/unittests/EVM/Inputs/deployIr.ll new file mode 100644 index 000000000000..1ad574a4702a --- /dev/null +++ b/lld/unittests/EVM/Inputs/deployIr.ll @@ -0,0 +1,21 @@ +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" +declare i256 @llvm.evm.datasize(metadata) +declare i256 @llvm.evm.dataoffset(metadata) +declare i256 @llvm.evm.linkersymbol(metadata) + +define i256 @foo() { + %res = call i256 @llvm.evm.linkersymbol(metadata !1) + ret i256 %res +} + +define i256 @bar() { + %linkersym = call i256 @llvm.evm.linkersymbol(metadata !1) + %datasize = tail call i256 @llvm.evm.datasize(metadata !2) + %dataoffset = tail call i256 @llvm.evm.dataoffset(metadata !2) + %tmp = add i256 %datasize, %dataoffset + %res = add i256 %tmp, %linkersym + ret i256 %res +} +!1 = !{!"library_id"} +!2 = !{!"Test_26_deployed"} diff --git a/lld/unittests/EVM/Inputs/deployedIr.ll b/lld/unittests/EVM/Inputs/deployedIr.ll new file mode 100644 index 000000000000..e1ec57393ad1 --- /dev/null +++ b/lld/unittests/EVM/Inputs/deployedIr.ll @@ -0,0 +1,13 @@ +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" +declare i256 @llvm.evm.linkersymbol(metadata) +declare i256 @llvm.evm.loadimmutable(metadata) + +define i256 @foo() { + %res = call i256 @llvm.evm.linkersymbol(metadata !1) + %res2 = call i256 @llvm.evm.loadimmutable(metadata !2) + %res3 = add i256 %res, %res2 + ret i256 %res3 +} +!1 = !{!"library_id2"} +!2 = !{!"id"} diff --git a/lld/unittests/EVM/Inputs/undefDeployIr.ll b/lld/unittests/EVM/Inputs/undefDeployIr.ll new file mode 100644 index 000000000000..9b888a550783 --- /dev/null +++ b/lld/unittests/EVM/Inputs/undefDeployIr.ll @@ -0,0 +1,13 @@ +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" +declare i256 @llvm.evm.datasize(metadata) +declare i256 @llvm.evm.dataoffset(metadata) +declare i256 @llvm.evm.linkersymbol(metadata) + +define i256 @bar() { + %datasize = tail call i256 @llvm.evm.datasize(metadata !1) + %dataoffset = tail call i256 @llvm.evm.dataoffset(metadata !1) + %res = add i256 %datasize, %dataoffset + ret i256 %res +} +!1 = !{!"unknown"} diff --git a/lld/unittests/EVM/Inputs/undefDeployedIr.ll b/lld/unittests/EVM/Inputs/undefDeployedIr.ll new file mode 100644 index 000000000000..e1ec57393ad1 --- /dev/null +++ b/lld/unittests/EVM/Inputs/undefDeployedIr.ll @@ -0,0 +1,13 @@ +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" +declare i256 @llvm.evm.linkersymbol(metadata) +declare i256 @llvm.evm.loadimmutable(metadata) + +define i256 @foo() { + %res = call i256 @llvm.evm.linkersymbol(metadata !1) + %res2 = call i256 @llvm.evm.loadimmutable(metadata !2) + %res3 = add i256 %res, %res2 + ret i256 %res3 +} +!1 = !{!"library_id2"} +!2 = !{!"id"} diff --git a/lld/unittests/EVM/LLDTest.cpp b/lld/unittests/EVM/LLDTest.cpp new file mode 100644 index 000000000000..78d71e2969db --- /dev/null +++ b/lld/unittests/EVM/LLDTest.cpp @@ -0,0 +1,357 @@ +//===---------- llvm/unittests/MC/AssemblerTest.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 "lld-c/LLDAsLibraryC.h" +#include "llvm-c/Core.h" +#include "llvm-c/IRReader.h" +#include "llvm-c/ObjCopy.h" +#include "llvm-c/TargetMachine.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/KECCAK.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include "gtest/gtest.h" +#include + +using namespace llvm; + +static std::string expand(const char *Path) { + llvm::SmallString<256> ThisPath; + ThisPath.append(getenv("LLD_SRC_DIR")); + llvm::sys::path::append(ThisPath, "unittests", "EVM", "Inputs", Path); + std::cerr << "Full path: " << ThisPath.str().str() << '\n'; + return ThisPath.str().str(); +} + +class LLDCTest : public testing::Test { + void SetUp() override { + LLVMInitializeEVMTargetInfo(); + LLVMInitializeEVMTarget(); + LLVMInitializeEVMTargetMC(); + LLVMInitializeEVMAsmPrinter(); + + LLVMTargetRef Target = 0; + const char *Triple = "evm"; + char *ErrMsg = 0; + if (LLVMGetTargetFromTriple(Triple, &Target, &ErrMsg)) { + FAIL() << "Failed to create target from the triple (" << Triple + << "): " << ErrMsg; + return; + } + ASSERT_TRUE(Target); + + // Construct a TargetMachine. + TM = + LLVMCreateTargetMachine(Target, Triple, "", "", LLVMCodeGenLevelDefault, + LLVMRelocDefault, LLVMCodeModelDefault); + + Context = LLVMContextCreate(); + } + + void TearDown() override { + LLVMDisposeTargetMachine(TM); + LLVMContextDispose(Context); + } + +public: + LLVMMemoryBufferRef link(LLVMMemoryBufferRef InAssembly, const char *Name, + const char *LinkerSyms[], + const char LinkerSymVals[][20], uint64_t NumSyms) { + char *ErrMsg = nullptr; + LLVMMemoryBufferRef OutAssembly = nullptr; + if (LLVMLinkEVM(InAssembly, &OutAssembly, LinkerSyms, LinkerSymVals, + NumSyms, &ErrMsg)) { + LLVMDisposeMessage(ErrMsg); + exit(1); + } + return OutAssembly; + } + + LLVMMemoryBufferRef addmd(LLVMMemoryBufferRef InObj) { + char *ErrMsg = nullptr; + LLVMMemoryBufferRef Out; + if (LLVMAddMetadata(InObj, MD.data(), MD.size(), &Out, &ErrMsg)) { + LLVMDisposeMessage(ErrMsg); + exit(1); + } + + return Out; + } + + LLVMMemoryBufferRef assemble(uint64_t codeSegment, + const std::vector &Objs, + const std::vector &IDs) { + char *ErrMsg = nullptr; + LLVMMemoryBufferRef OutAssembly = nullptr; + if (LLVMAssembleEVM(codeSegment, Objs.data(), IDs.data(), Objs.size(), + &OutAssembly, &ErrMsg)) { + LLVMDisposeMessage(ErrMsg); + exit(1); + } + EXPECT_TRUE(LLVMIsELFEVM(OutAssembly)); + return OutAssembly; + } + + LLVMMemoryBufferRef compileIR(const char *FileName) { + auto Buf = MemoryBuffer::getFile(expand(FileName), /*IsText=*/false, + /*RequiresNullTerminator=*/false); + if (auto EC = Buf.getError()) + exit(1); + + LLVMMemoryBufferRef IrBuf = llvm::wrap(Buf.get().release()); + char *ErrMsg = nullptr; + LLVMModuleRef Module; + if (LLVMParseIRInContext(Context, IrBuf, &Module, &ErrMsg)) { + LLVMDisposeMessage(ErrMsg); + exit(1); + } + + // Run CodeGen to produce the buffers. + LLVMMemoryBufferRef Result; + if (LLVMTargetMachineEmitToMemoryBuffer(TM, Module, LLVMObjectFile, &ErrMsg, + &Result)) { + LLVMDisposeModule(Module); + LLVMDisposeMessage(ErrMsg); + exit(1); + } + LLVMDisposeModule(Module); + return Result; + } + + LLVMTargetMachineRef TM; + LLVMContextRef Context; + std::array MD = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20}; +}; + +TEST_F(LLDCTest, IterativeLinkage) { + LLVMMemoryBufferRef DeployObjMemBuffer = compileIR("deployIr.ll"); + LLVMMemoryBufferRef DeployedObjMemBuffer = compileIR("deployedIr.ll"); + + EXPECT_TRUE(LLVMIsELFEVM(DeployObjMemBuffer)); + EXPECT_TRUE(LLVMIsELFEVM(DeployedObjMemBuffer)); + + const char *LinkerSymbol[2] = {"library_id", "library_id2"}; + const char LinkerSymbolVal[2][20] = { + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {'\x9f', 0x1E, '\xBB', '\xF1', 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}}; + StringRef SymVal1(LinkerSymbolVal[0], 20); + StringRef SymVal2(LinkerSymbolVal[1], 20); + + const char *InIDs[] = {"Test_26", "Test_26_deployed"}; + std::array InData = {DeployObjMemBuffer, + DeployedObjMemBuffer}; + LLVMMemoryBufferRef InMemBuf = nullptr; + LLVMMemoryBufferRef OutMemBuf = nullptr; + char *ErrMsg = nullptr; + + // Assemble deploy with deployed. + { + if (LLVMAssembleEVM(/*codeSegment=*/0, InData.data(), InIDs, 2, &OutMemBuf, + &ErrMsg)) { + FAIL() << "Failed to assemble:" << ErrMsg; + LLVMDisposeMessage(ErrMsg); + return; + } + EXPECT_TRUE(LLVMIsELFEVM(OutMemBuf)); + std::swap(OutMemBuf, InMemBuf); + } + + // Check load immutable references. + { + char **ImmutableIDs = nullptr; + uint64_t *ImmutableOffsets = nullptr; + uint64_t ImmCount = + LLVMGetImmutablesEVM(InData[1], &ImmutableIDs, &ImmutableOffsets); + EXPECT_TRUE(ImmCount == 1); + EXPECT_TRUE(std::strcmp(ImmutableIDs[0], "id") == 0); + LLVMDisposeImmutablesEVM(ImmutableIDs, ImmutableOffsets, ImmCount); + } + + // No linker symbol definitions are provided, so we have to receive ELF + // object file. + if (LLVMLinkEVM(InMemBuf, &OutMemBuf, nullptr, nullptr, 0, &ErrMsg)) { + FAIL() << "Failed to link:" << ErrMsg; + LLVMDisposeMessage(ErrMsg); + return; + } + + EXPECT_TRUE(LLVMIsELFEVM(OutMemBuf)); + + char **UndefLinkerSyms = nullptr; + uint64_t NumLinkerUndefs = 0; + + LLVMGetUndefinedReferencesEVM(OutMemBuf, &UndefLinkerSyms, &NumLinkerUndefs); + + EXPECT_TRUE(NumLinkerUndefs == 2); + EXPECT_TRUE((std::strcmp(UndefLinkerSyms[0], LinkerSymbol[0]) == 0)); + EXPECT_TRUE((std::strcmp(UndefLinkerSyms[1], LinkerSymbol[1]) == 0)); + LLVMDisposeUndefinedReferences(UndefLinkerSyms, NumLinkerUndefs); + + std::swap(OutMemBuf, InMemBuf); + LLVMDisposeMemoryBuffer(OutMemBuf); + + // The first linker symbol definitions is provided, so we still have + // to receive an ELF object file + if (LLVMLinkEVM(InMemBuf, &OutMemBuf, LinkerSymbol, LinkerSymbolVal, 1, + &ErrMsg)) { + FAIL() << "Failed to link:" << ErrMsg; + LLVMDisposeMessage(ErrMsg); + return; + } + + EXPECT_TRUE(LLVMIsELFEVM(OutMemBuf)); + + LLVMGetUndefinedReferencesEVM(OutMemBuf, &UndefLinkerSyms, &NumLinkerUndefs); + EXPECT_TRUE(NumLinkerUndefs == 1); + EXPECT_TRUE((std::strcmp(UndefLinkerSyms[0], LinkerSymbol[1]) == 0)); + LLVMDisposeUndefinedReferences(UndefLinkerSyms, NumLinkerUndefs); + + std::swap(OutMemBuf, InMemBuf); + LLVMDisposeMemoryBuffer(OutMemBuf); + + // Both linker symbol definitions are provided, so we have to receive + // a bytecode. + if (LLVMLinkEVM(InMemBuf, &OutMemBuf, LinkerSymbol, LinkerSymbolVal, 2, + &ErrMsg)) { + FAIL() << "Failed to link:" << ErrMsg; + LLVMDisposeMessage(ErrMsg); + return; + } + + EXPECT_TRUE(!LLVMIsELFEVM(OutMemBuf)); + + LLVMGetUndefinedReferencesEVM(OutMemBuf, &UndefLinkerSyms, &NumLinkerUndefs); + EXPECT_TRUE(NumLinkerUndefs == 0); + LLVMDisposeUndefinedReferences(UndefLinkerSyms, NumLinkerUndefs); + + StringRef DeployBin(LLVMGetBufferStart(OutMemBuf), + LLVMGetBufferSize(OutMemBuf)); + + EXPECT_TRUE(DeployBin.find(SymVal1) != StringRef::npos); + EXPECT_TRUE(DeployBin.find(SymVal2) != StringRef::npos); + + LLVMDisposeMemoryBuffer(OutMemBuf); + LLVMDisposeMemoryBuffer(InMemBuf); +} + +TEST_F(LLDCTest, Assembly) { + const char *LinkerSymbol[2] = {"unused_library_id", "library_id"}; + const char LinkerSymbolVal[2][20] = { + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9}}; + + LLVMMemoryBufferRef A_deploy_obj = compileIR("A_deploy.ll"); + LLVMMemoryBufferRef A_deployed_obj = compileIR("A_deployed.ll"); + LLVMMemoryBufferRef D_deploy_obj = compileIR("D_deploy.ll"); + LLVMMemoryBufferRef D_deployed_obj = compileIR("D_deployed.ll"); + LLVMMemoryBufferRef R_deploy_obj = compileIR("R_deploy.ll"); + LLVMMemoryBufferRef R_deployed_obj = compileIR("R_deployed.ll"); + + // A assemble. + LLVMMemoryBufferRef A_deployed_obj_md = addmd(A_deployed_obj); + LLVMMemoryBufferRef A_assembly_deployed = + assemble(/*codeSegment=*/1, {A_deployed_obj_md}, {"A_38_deployed"}); + LLVMMemoryBufferRef A_assembly = + assemble(/*codeSegment=*/0, {A_deploy_obj, A_assembly_deployed}, + {"A_38", "A_38_deployed"}); + + // D assemble. + LLVMMemoryBufferRef D_deployed_obj_md = addmd(D_deployed_obj); + LLVMMemoryBufferRef D_assembly_deployed = + assemble(/*codeSegment=*/1, {D_deployed_obj_md}, {"D_38_deployed"}); + LLVMMemoryBufferRef D_assembly = + assemble(/*codeSegment=*/0, {D_deploy_obj, D_assembly_deployed}, + {"D_51", "D_51_deployed"}); + + // R_deployed assemble. + // A_assembly is not required here, but we add it intentionaly to check + // that it will be ignored (the total number of library reference is 3). + LLVMMemoryBufferRef R_deployed_obj_md = addmd(R_deployed_obj); + LLVMMemoryBufferRef R_deployed_assemble = + assemble(/*codeSegment=*/1, + {R_deployed_obj_md, D_assembly, A_assembly, A_assembly_deployed}, + {"R_107_deployed", "D_51", "A_38", "A_38.A_38_deployed"}); + + // R assemble. + LLVMMemoryBufferRef R_assembly = assemble( + /*codeSegment=*/0, + {R_deploy_obj, R_deployed_assemble, A_assembly, A_assembly_deployed}, + {"R_107", "R_107_deployed", "A_38", "A_38.A_38_deployed"}); + + // Get linker symbols offsets + uint64_t *SymOffsets = nullptr; + uint64_t NumOffsets = + LLVMGetSymbolOffsetsEVM(R_assembly, LinkerSymbol[1], &SymOffsets); + EXPECT_TRUE(NumOffsets == 3); + LLVMDisposeSymbolOffsetsEVM(SymOffsets); + + // Linking with no linker symbols. + LLVMMemoryBufferRef TmpAssembly = link(R_assembly, "R", nullptr, nullptr, 0); + EXPECT_TRUE(LLVMIsELFEVM(TmpAssembly)); + + // Linking with unused linker symbol. It has no effect. + LLVMMemoryBufferRef TmpAssembly2 = + link(TmpAssembly, "R", LinkerSymbol, LinkerSymbolVal, 1); + EXPECT_TRUE(LLVMIsELFEVM(TmpAssembly2)); + + // Linking with both linker symbols. The library reference should be resolved + // and resulting object is a final bytecode. + LLVMMemoryBufferRef Bytecode = + link(TmpAssembly2, "R", LinkerSymbol, LinkerSymbolVal, 2); + EXPECT_TRUE(!LLVMIsELFEVM(Bytecode)); + + char **UndefLinkerSyms = nullptr; + uint64_t NumLinkerUndefs = 0; + + LLVMGetUndefinedReferencesEVM(Bytecode, &UndefLinkerSyms, &NumLinkerUndefs); + EXPECT_TRUE(NumLinkerUndefs == 0); + LLVMDisposeUndefinedReferences(UndefLinkerSyms, NumLinkerUndefs); + + StringRef Binary(LLVMGetBufferStart(Bytecode), LLVMGetBufferSize(Bytecode)); + + StringRef LibAddr(LinkerSymbolVal[1], 20); + EXPECT_TRUE(Binary.count(LibAddr) == 3); + EXPECT_TRUE(Binary.count(StringRef(MD.data(), MD.size())) == 5); + + LLVMDisposeMemoryBuffer(A_deploy_obj); + LLVMDisposeMemoryBuffer(A_deployed_obj); + LLVMDisposeMemoryBuffer(D_deploy_obj); + LLVMDisposeMemoryBuffer(D_deployed_obj); + LLVMDisposeMemoryBuffer(R_deploy_obj); + LLVMDisposeMemoryBuffer(R_deployed_obj); + LLVMDisposeMemoryBuffer(TmpAssembly); + LLVMDisposeMemoryBuffer(TmpAssembly2); + LLVMDisposeMemoryBuffer(Bytecode); +} + +TEST_F(LLDCTest, UndefNonRefSymbols) { + LLVMMemoryBufferRef DeployObj = compileIR("undefDeployIr.ll"); + LLVMMemoryBufferRef DeployedObj = compileIR("undefDeployedIr.ll"); + + EXPECT_TRUE(LLVMIsELFEVM(DeployObj)); + EXPECT_TRUE(LLVMIsELFEVM(DeployedObj)); + + const std::array InObjs = {DeployObj, DeployedObj}; + const std::array IDs = {"Test_26", "Test_26_deployed"}; + LLVMMemoryBufferRef OutObj = nullptr; + char *ErrMsg = nullptr; + EXPECT_TRUE(LLVMAssembleEVM(/*codeSegment=*/0, InObjs.data(), IDs.data(), + IDs.size(), &OutObj, &ErrMsg)); + EXPECT_TRUE(StringRef(ErrMsg).contains("non-ref undefined symbol:")); + LLVMDisposeMessage(ErrMsg); + + LLVMDisposeMemoryBuffer(DeployObj); + LLVMDisposeMemoryBuffer(DeployedObj); +} diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp index 444e44b39289..dc6211d233f4 100644 --- a/lldb/source/Expression/DWARFExpression.cpp +++ b/lldb/source/Expression/DWARFExpression.cpp @@ -859,10 +859,12 @@ llvm::Expected DWARFExpression::Evaluate( // TODO: Implement a real typed stack, and store the genericness of the value // there. auto to_generic = [&](auto v) { + // TODO: Avoid implicit trunc? + // See https://github.com/llvm/llvm-project/issues/112510. bool is_signed = std::is_signed::value; - return Scalar(llvm::APSInt( - llvm::APInt(8 * opcodes.GetAddressByteSize(), v, is_signed), - !is_signed)); + return Scalar(llvm::APSInt(llvm::APInt(8 * opcodes.GetAddressByteSize(), v, + is_signed, /*implicitTrunc=*/true), + !is_signed)); }; // The default kind is a memory location. This is updated by any diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index f70efe5ed57e..9d08137046d2 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -764,7 +764,9 @@ TypeSystemClang::GetBuiltinTypeForEncodingAndBitSize(Encoding encoding, return GetType(ast.UnsignedLongLongTy); if (QualTypeMatchesBitSize(bit_size, ast, ast.UnsignedInt128Ty)) return GetType(ast.UnsignedInt128Ty); - break; + // EVM local begin + return GetType(ast.getBitIntType(true, bit_size)); + // EVM local end case eEncodingSint: if (QualTypeMatchesBitSize(bit_size, ast, ast.SignedCharTy)) @@ -779,7 +781,9 @@ TypeSystemClang::GetBuiltinTypeForEncodingAndBitSize(Encoding encoding, return GetType(ast.LongLongTy); if (QualTypeMatchesBitSize(bit_size, ast, ast.Int128Ty)) return GetType(ast.Int128Ty); - break; + // EVM local begin + return GetType(ast.getBitIntType(false, bit_size)); + // EVM local end case eEncodingIEEE754: if (QualTypeMatchesBitSize(bit_size, ast, ast.FloatTy)) @@ -1004,7 +1008,9 @@ CompilerType TypeSystemClang::GetBuiltinTypeForDWARFEncodingAndBitSize( return GetType(ast.LongLongTy); if (QualTypeMatchesBitSize(bit_size, ast, ast.Int128Ty)) return GetType(ast.Int128Ty); - break; + // EVM local begin + return GetType(ast.getBitIntType(false, bit_size)); + // EVM local end case DW_ATE_signed_char: if (type_name == "char") { @@ -1056,7 +1062,9 @@ CompilerType TypeSystemClang::GetBuiltinTypeForDWARFEncodingAndBitSize( return GetType(ast.UnsignedLongLongTy); if (QualTypeMatchesBitSize(bit_size, ast, ast.UnsignedInt128Ty)) return GetType(ast.UnsignedInt128Ty); - break; + // EVM local begin + return GetType(ast.getBitIntType(true, bit_size)); + // EVM local end case DW_ATE_unsigned_char: if (type_name == "char") { @@ -2369,7 +2377,9 @@ CompilerType TypeSystemClang::GetIntTypeFromBitSize(size_t bit_size, if (bit_size == ast.getTypeSize(ast.UnsignedInt128Ty)) return GetType(ast.UnsignedInt128Ty); } - return CompilerType(); + // EVM local begin + return GetType(ast.getBitIntType(!is_signed, bit_size)); + // EVM local end } CompilerType TypeSystemClang::GetPointerSizedIntType(bool is_signed) { @@ -3832,6 +3842,16 @@ TypeSystemClang::GetTypeInfo(lldb::opaque_compiler_type_t type, ->getModifiedType() .getAsOpaquePtr(), pointee_or_element_clang_type); + // EVM local begin + case clang::Type::BitInt: + case clang::Type::DependentBitInt: { + uint32_t bitint_type_flags = + eTypeIsBuiltIn | eTypeHasValue | eTypeIsScalar | eTypeIsInteger; + if (qual_type->isSignedIntegerType()) + bitint_type_flags |= eTypeIsSigned; + return bitint_type_flags; + } + // EVM local end case clang::Type::Builtin: { const clang::BuiltinType *builtin_type = llvm::cast(qual_type->getCanonicalTypeInternal()); diff --git a/llvm/.clang-tidy b/llvm/.clang-tidy index 47bc73c13f19..617d2d1b1b1b 100644 --- a/llvm/.clang-tidy +++ b/llvm/.clang-tidy @@ -1 +1,67 @@ InheritParentConfig: true +# EVM local begin +Checks: > + bugprone-*, + -bugprone-easily-swappable-parameters, + -bugprone-implicit-widening-of-multiplication-result, + -bugprone-narrowing-conversions, + -bugprone-unchecked-optional-access, + cert-*, + clang-analyzer-*, + -clang-analyzer-optin.cplusplus.UninitializedObject, + concurrency-*, + cppcoreguidelines-*, + -cppcoreguidelines-avoid-const-or-ref-data-members, + -cppcoreguidelines-avoid-do-while, + -cppcoreguidelines-avoid-magic-numbers, + -cppcoreguidelines-avoid-non-const-global-variables, + -cppcoreguidelines-macro-usage, + -cppcoreguidelines-narrowing-conversions, + -cppcoreguidelines-owning-memory, + -cppcoreguidelines-pro-bounds-array-to-pointer-decay, + -cppcoreguidelines-pro-bounds-constant-array-index, + -cppcoreguidelines-pro-bounds-pointer-arithmetic, + -cppcoreguidelines-pro-type-const-cast, + -cppcoreguidelines-pro-type-static-cast-downcast, + -cppcoreguidelines-pro-type-union-access, + hicpp-*, + -hicpp-braces-around-statements, + -hicpp-named-parameter, + -hicpp-no-array-decay, + -hicpp-signed-bitwise, + -hicpp-special-member-functions, + llvm-*, + misc-redundant-expression, + misc-static-assert, + misc-unused-using-decls, + -misc-const-correctness, + -misc-include-cleaner, + modernize-loop-convert, + modernize-make-unique, + modernize-raw-string-literal, + modernize-use-bool-literals, + modernize-use-default-member-init, + modernize-use-emplace, + modernize-use-equals-default, + modernize-use-nullptr, + modernize-use-override, + modernize-use-using, + performance-*, + portability-*, + readability-*, + -readability-braces-around-statements, + -readability-convert-member-functions-to-static, + -readability-function-cognitive-complexity, + -readability-identifier-length, + -readability-identifier-naming, + -readability-implicit-bool-conversion, + -readability-isolate-declaration, + -readability-magic-numbers, + -readability-named-parameter, + -readability-simplify-boolean-expr, + -readability-uppercase-literal-suffix, +CheckOptions: + - key: concurrency-mt-unsafe.FunctionSet + value: posix +WarningsAsErrors: '*' +# EVM local end diff --git a/llvm/CMakeLists.txt b/llvm/CMakeLists.txt index 12618966c4ad..a52d4e21f31b 100644 --- a/llvm/CMakeLists.txt +++ b/llvm/CMakeLists.txt @@ -473,6 +473,9 @@ set(LLVM_ALL_TARGETS ARM AVR BPF +# EVM local begin + EVM +# EVM local end Hexagon Lanai LoongArch @@ -498,6 +501,10 @@ set(LLVM_ALL_EXPERIMENTAL_TARGETS Xtensa ) +# EVM local begin +add_compile_definitions(_EVM) +# EVM local end + # List of targets with JIT support: set(LLVM_TARGETS_WITH_JIT X86 PowerPC AArch64 ARM Mips SystemZ) @@ -546,7 +553,10 @@ option(LLVM_ENABLE_LIBEDIT "Use libedit if available." ON) option(LLVM_ENABLE_LIBPFM "Use libpfm for performance counters if available." ON) # On z/OS, threads cannot be used because TLS is not supported. -if (CMAKE_SYSTEM_NAME MATCHES "OS390") +# EVM local begin +# TODO: #677, investigate hanging up of solidity-compiler tests on windows. +if (CMAKE_SYSTEM_NAME MATCHES "OS390" OR CMAKE_SYSTEM_NAME MATCHES "Windows") +# EVM local end option(LLVM_ENABLE_THREADS "Use threads if available." OFF) else() option(LLVM_ENABLE_THREADS "Use threads if available." ON) diff --git a/llvm/docs/CompilerWriterInfo.rst b/llvm/docs/CompilerWriterInfo.rst index 8b70dcb8b400..5e09295746a6 100644 --- a/llvm/docs/CompilerWriterInfo.rst +++ b/llvm/docs/CompilerWriterInfo.rst @@ -212,3 +212,10 @@ Miscellaneous Resources * `GCC prefetch project `_ page has a good survey of the prefetching capabilities of a variety of modern processors. + +EVM +=== + +* `https://www.evm.codes/about` + +* `https://github.com/ethereum/execution-specs/tree/master/src/ethereum/shanghai/vm` diff --git a/llvm/include/llvm-c/ErrorHandling.h b/llvm/include/llvm-c/ErrorHandling.h index d9b9f22752b8..529cab6b0c9f 100644 --- a/llvm/include/llvm-c/ErrorHandling.h +++ b/llvm/include/llvm-c/ErrorHandling.h @@ -15,6 +15,9 @@ #define LLVM_C_ERRORHANDLING_H #include "llvm-c/ExternC.h" +// EVM local beging +#include +// EVM local end LLVM_C_EXTERN_C_BEGIN @@ -26,6 +29,10 @@ LLVM_C_EXTERN_C_BEGIN typedef void (*LLVMFatalErrorHandler)(const char *Reason); +// EVM local begin +typedef void (*LLVMStackErrorHandlerEVM)(uint64_t SpillRegionSize); +// EVM local end + /** * 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, @@ -48,6 +55,19 @@ void LLVMResetFatalErrorHandler(void); */ void LLVMEnablePrettyStackTrace(void); +// EVM local begin +/** + * Register a handler that the stackification algorithm invokes when it + * requires a pre-allocated spill region. + */ +void LLVMInstallEVMStackErrorHandler(LLVMStackErrorHandlerEVM Handler); + +/** + * Reset the EVM stack error handler. + */ +void LLVMResetEVMStackErrorHandler(void); +// EVM local end + /** * @} */ diff --git a/llvm/include/llvm-c/ObjCopy.h b/llvm/include/llvm-c/ObjCopy.h new file mode 100644 index 000000000000..a48565cbcb0f --- /dev/null +++ b/llvm/include/llvm-c/ObjCopy.h @@ -0,0 +1,31 @@ +//==--------- ObjCopy.h - ObjCopy Public 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 header declares the C interface for the llvm-objcopy functionality. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_C_OBJCOPY_H +#define LLVM_C_OBJCOPY_H + +#include "llvm-c/ExternC.h" +#include "llvm-c/Types.h" + +LLVM_C_EXTERN_C_BEGIN + +/** Creates new section '.metadata' in the input object file \p InBuffer + * and puts there the data pointed to by the \p MetadataPtr. The result is + * returned via \p OutBuffer. In case of an error the function returns 'true' + * and an error message is passes via \p ErrorMessage. The message should be + * disposed by LLVMDisposeMessage. **/ +LLVMBool LLVMAddMetadata(LLVMMemoryBufferRef InBuffer, const char *MetadataPtr, + uint64_t MetadataSize, LLVMMemoryBufferRef *OutBuffer, + char **ErrorMessage); +LLVM_C_EXTERN_C_END + +#endif // LLVM_C_OBJCOPY_H diff --git a/llvm/include/llvm-c/Support.h b/llvm/include/llvm-c/Support.h index 17657861b32b..95a503190fab 100644 --- a/llvm/include/llvm-c/Support.h +++ b/llvm/include/llvm-c/Support.h @@ -63,6 +63,14 @@ void *LLVMSearchForAddressOfSymbol(const char *symbolName); */ void LLVMAddSymbol(const char *symbolName, void *symbolValue); +// EVM local begin +/** + * Print git commit id to Buf. + * Return sprintf exit code. + */ +int LLVMPrintCommitIDTo(char* Buf); +// EVM local end + /** * @} */ diff --git a/llvm/include/llvm/ADT/APFixedPoint.h b/llvm/include/llvm/ADT/APFixedPoint.h index 0c014e76aa71..9c21b1a055b5 100644 --- a/llvm/include/llvm/ADT/APFixedPoint.h +++ b/llvm/include/llvm/ADT/APFixedPoint.h @@ -160,7 +160,9 @@ class APFixedPoint { } APFixedPoint(uint64_t Val, const FixedPointSemantics &Sema) - : APFixedPoint(APInt(Sema.getWidth(), Val, Sema.isSigned()), Sema) {} + : APFixedPoint(APInt(Sema.getWidth(), Val, Sema.isSigned(), + /*implicitTrunc=*/true), + Sema) {} // Zero initialization. APFixedPoint(const FixedPointSemantics &Sema) : APFixedPoint(0, Sema) {} diff --git a/llvm/include/llvm/ADT/APInt.h b/llvm/include/llvm/ADT/APInt.h index 108df7e0eaea..9c29eae68647 100644 --- a/llvm/include/llvm/ADT/APInt.h +++ b/llvm/include/llvm/ADT/APInt.h @@ -16,6 +16,9 @@ #define LLVM_ADT_APINT_H #include "llvm/Support/Compiler.h" +// EVM local begin +#include "llvm/Support/ErrorHandling.h" +// EVM local end #include "llvm/Support/MathExtras.h" #include "llvm/Support/float128.h" #include @@ -108,11 +111,46 @@ class [[nodiscard]] APInt { /// \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) + /// \param implicitTrunc allow implicit truncation of non-zero/sign bits of + /// val beyond the range of numBits + APInt(unsigned numBits, uint64_t val, bool isSigned = false, + bool implicitTrunc = false) : BitWidth(numBits) { + if (!implicitTrunc) { + if (isSigned) { + if (BitWidth == 0) { + // EVM local begin + if (val != 0 && val != uint64_t(-1)) + report_fatal_error( + "APInt error: Value must be 0 or -1 for signed 0-bit APInt"); + // EVM local end + } else { + // EVM local begin + if (!llvm::isIntN(BitWidth, val)) + report_fatal_error( + "APInt error: Value is not an N-bit signed value"); + // EVM local end + } + } else { + if (BitWidth == 0) { + // EVM local begin + if (val != 0) + report_fatal_error( + "APInt error: Value must be zero for unsigned 0-bit APInt"); + // EVM local end + } else { + // EVM local begin + if (!llvm::isUIntN(BitWidth, val)) + report_fatal_error( + "APInt error: Value is not an N-bit unsigned value"); + // EVM local begin + } + } + } if (isSingleWord()) { U.VAL = val; - clearUnusedBits(); + if (implicitTrunc || isSigned) + clearUnusedBits(); } else { initSlowCase(val, isSigned); } @@ -169,7 +207,10 @@ class [[nodiscard]] APInt { /// Destructor. ~APInt() { if (needsCleanup()) + // EVM local begin + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDelete) delete[] U.pVal; + // EVM local end } /// @} diff --git a/llvm/include/llvm/Analysis/AliasAnalysis.h b/llvm/include/llvm/Analysis/AliasAnalysis.h index 1b5a6ee24b86..c009e0dd4180 100644 --- a/llvm/include/llvm/Analysis/AliasAnalysis.h +++ b/llvm/include/llvm/Analysis/AliasAnalysis.h @@ -998,6 +998,18 @@ struct ExternalAAWrapperPass : ImmutablePass { explicit ExternalAAWrapperPass(CallbackT CB); + /// Returns whether this external AA should run before Basic AA. + /// + /// By default, external AA passes are run after Basic AA. If this returns + /// true, the external AA will be run before Basic AA during alias analysis. + /// + /// For some targets, we prefer to run the external AA early to improve + /// compile time as it has more target-specific information. This is + /// particularly useful when the external AA can provide more precise results + /// than Basic AA so that Basic AA does not need to spend time recomputing + /// them. + virtual bool runEarly() { return false; } + void getAnalysisUsage(AnalysisUsage &AU) const override { AU.setPreservesAll(); } diff --git a/llvm/include/llvm/Analysis/LoopUnrollAnalyzer.h b/llvm/include/llvm/Analysis/LoopUnrollAnalyzer.h index eada6a647763..12b906ec9dd5 100644 --- a/llvm/include/llvm/Analysis/LoopUnrollAnalyzer.h +++ b/llvm/include/llvm/Analysis/LoopUnrollAnalyzer.h @@ -44,7 +44,7 @@ class UnrolledInstAnalyzer : private InstVisitor { friend class InstVisitor; struct SimplifiedAddress { Value *Base = nullptr; - ConstantInt *Offset = nullptr; + APInt Offset; }; public: diff --git a/llvm/include/llvm/Analysis/VMAliasAnalysis.h b/llvm/include/llvm/Analysis/VMAliasAnalysis.h new file mode 100644 index 000000000000..bedf209a1719 --- /dev/null +++ b/llvm/include/llvm/Analysis/VMAliasAnalysis.h @@ -0,0 +1,50 @@ +//===-- VMAliasAnalysis.h - VM alias 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 +// +//===----------------------------------------------------------------------===// +// +// This is the address space based alias analysis pass, that is shared between +// EVM and EVM backends. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_ANALYSIS_VMALIASANALYSIS_H +#define LLVM_ANALYSIS_VMALIASANALYSIS_H + +#include "llvm/Analysis/AliasAnalysis.h" + +namespace llvm { + +class DataLayout; +class MemoryLocation; + +/// A simple AA result that uses address space info to answer queries. +class VMAAResult : public AAResultBase { + const DataLayout &DL; + SmallDenseSet StorageAS; + SmallDenseSet HeapAS; + unsigned MaxAS; + +public: + explicit VMAAResult(const DataLayout &DL, + const SmallDenseSet &StorageAS, + const SmallDenseSet &HeapAS, unsigned MaxAS) + : DL(DL), StorageAS(StorageAS), HeapAS(HeapAS), MaxAS(MaxAS) {} + + /// Handle invalidation events from the new pass manager. + /// + /// By definition, this result is stateless and so remains valid. + bool invalidate(Function &, const PreservedAnalyses &, + FunctionAnalysisManager::Invalidator &) { + return false; + } + + AliasResult alias(const MemoryLocation &LocA, const MemoryLocation &LocB, + AAQueryInfo &AAQI, const Instruction *I); +}; +} // end namespace llvm + +#endif // LLVM_ANALYSIS_VMALIASANALYSIS_H diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h index 456cffff6b4a..a0ddd401f617 100644 --- a/llvm/include/llvm/BinaryFormat/ELF.h +++ b/llvm/include/llvm/BinaryFormat/ELF.h @@ -321,6 +321,9 @@ enum { EM_VE = 251, // NEC SX-Aurora VE EM_CSKY = 252, // C-SKY 32-bit processor EM_LOONGARCH = 258, // LoongArch + // EVM local begin + EM_EVM = 261 // EVM + // EVM local end }; // Object file classes. @@ -427,6 +430,13 @@ enum { #include "ELFRelocs/AArch64.def" }; +// EVM local begin +// ELF Relocation types for EVM +enum : uint8_t { +#include "ELFRelocs/EVM.def" +}; +// EVM local end + // Special values for the st_other field in the symbol table entry for AArch64. enum { // Symbol may follow different calling convention than base PCS. @@ -1018,6 +1028,13 @@ enum { #include "ELFRelocs/Xtensa.def" }; + +// Special values for the st_other field in the symbol table entry for EVM. +enum { + // Symbol denotes a sub symbol of a wide reference. + STO_EVM_REFERENCE_SYMBOL = 0x80, +}; + #undef ELF_RELOC // Section header. diff --git a/llvm/include/llvm/BinaryFormat/ELFRelocs/EVM.def b/llvm/include/llvm/BinaryFormat/ELFRelocs/EVM.def new file mode 100644 index 000000000000..003b02562b86 --- /dev/null +++ b/llvm/include/llvm/BinaryFormat/ELFRelocs/EVM.def @@ -0,0 +1,7 @@ + +#ifndef ELF_RELOC +#error "ELF_RELOC must be defined" +#endif + +ELF_RELOC(R_EVM_NONE, 0) +ELF_RELOC(R_EVM_DATA, 1) diff --git a/llvm/include/llvm/CodeGen/SelectionDAG.h b/llvm/include/llvm/CodeGen/SelectionDAG.h index 16ec65f2e7da..508685518338 100644 --- a/llvm/include/llvm/CodeGen/SelectionDAG.h +++ b/llvm/include/llvm/CodeGen/SelectionDAG.h @@ -670,6 +670,9 @@ class SelectionDAG { SDValue getConstant(const APInt &Val, const SDLoc &DL, EVT VT, bool isTarget = false, bool isOpaque = false); + SDValue getSignedConstant(int64_t Val, const SDLoc &DL, EVT VT, + bool isTarget = false, bool isOpaque = false); + SDValue getAllOnesConstant(const SDLoc &DL, EVT VT, bool IsTarget = false, bool IsOpaque = false) { return getConstant(APInt::getAllOnes(VT.getScalarSizeInBits()), DL, VT, diff --git a/llvm/include/llvm/CodeGen/TargetInstrInfo.h b/llvm/include/llvm/CodeGen/TargetInstrInfo.h index 649711d8faf6..d129034dd1c0 100644 --- a/llvm/include/llvm/CodeGen/TargetInstrInfo.h +++ b/llvm/include/llvm/CodeGen/TargetInstrInfo.h @@ -28,6 +28,9 @@ #include "llvm/CodeGen/MachineOutliner.h" #include "llvm/CodeGen/RegisterClassInfo.h" #include "llvm/CodeGen/VirtRegMap.h" +// EVM local begin +#include "llvm/IR/Constants.h" +// EVM local end #include "llvm/MC/MCInstrInfo.h" #include "llvm/Support/BranchProbability.h" #include "llvm/Support/ErrorHandling.h" @@ -156,6 +159,12 @@ class TargetInstrInfo : public MCInstrInfo { virtual bool isSafeToSink(MachineInstr &MI, MachineBasicBlock *SuccToSinkTo, MachineCycleInfo *CI) const { return true; +} + + /// For a "cheap" instruction which doesn't enable additional sinking, + /// should MachineSink break a critical edge to sink it anyways? + virtual bool shouldBreakCriticalEdgeToSink(MachineInstr &MI) const { + return false; } protected: @@ -231,8 +240,14 @@ class TargetInstrInfo : public MCInstrInfo { /// that is set up between the frame setup and destroy pseudo instructions. int64_t getFrameSize(const MachineInstr &I) const { assert(isFrameInstr(I) && "Not a frame instruction"); - assert(I.getOperand(0).getImm() >= 0); - return I.getOperand(0).getImm(); + // EVM local begin + if (I.getOperand(0).isImm()) { + assert(I.getOperand(0).getImm() >= 0); + return I.getOperand(0).getImm(); + } else { + return I.getOperand(0).getCImm()->getZExtValue(); + } + // EVM local end } /// Returns the total frame size, which is made up of the space set up inside @@ -1017,10 +1032,16 @@ class TargetInstrInfo : public MCInstrInfo { /// The source and destination registers may overlap, which may require a /// careful implementation when multiple copy instructions are required for /// large registers. See for example the ARM target. + /// + /// If RenamableDest is true, the copy instruction's destination operand is + /// marked renamable. + /// If RenamableSrc is true, the copy instruction's source operand is + /// marked renamable. virtual void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, - MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const { + MCRegister DestReg, MCRegister SrcReg, bool KillSrc, + bool RenamableDest = false, + bool RenamableSrc = false) const { llvm_unreachable("Target didn't implement TargetInstrInfo::copyPhysReg!"); } diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h index 9d9886f4920a..b54d8f0d57a8 100644 --- a/llvm/include/llvm/CodeGen/TargetLowering.h +++ b/llvm/include/llvm/CodeGen/TargetLowering.h @@ -5075,7 +5075,6 @@ class TargetLowering : public TargetLoweringBase { //===--------------------------------------------------------------------===// // Div utility functions // - SDValue BuildSDIV(SDNode *N, SelectionDAG &DAG, bool IsAfterLegalization, SmallVectorImpl &Created) const; SDValue BuildUDIV(SDNode *N, SelectionDAG &DAG, bool IsAfterLegalization, diff --git a/llvm/include/llvm/CodeGen/ValueTypes.td b/llvm/include/llvm/CodeGen/ValueTypes.td index 963b6a71de38..ca2ea8191868 100644 --- a/llvm/include/llvm/CodeGen/ValueTypes.td +++ b/llvm/include/llvm/CodeGen/ValueTypes.td @@ -70,223 +70,227 @@ def i16 : VTInt<16, 6>; // 16-bit integer value def i32 : VTInt<32, 7>; // 32-bit integer value def i64 : VTInt<64, 8>; // 64-bit integer value def i128 : VTInt<128, 9>; // 128-bit integer value - -def bf16 : VTFP<16, 10>; // 16-bit brain floating point value -def f16 : VTFP<16, 11>; // 16-bit floating point value -def f32 : VTFP<32, 12>; // 32-bit floating point value -def f64 : VTFP<64, 13>; // 64-bit floating point value -def f80 : VTFP<80, 14>; // 80-bit floating point value -def f128 : VTFP<128, 15>; // 128-bit floating point value -def ppcf128 : VTFP<128, 16>; // PPC 128-bit floating point value - -def v1i1 : VTVec<1, i1, 17>; // 1 x i1 vector value -def v2i1 : VTVec<2, i1, 18>; // 2 x i1 vector value -def v3i1 : VTVec<3, i1, 19>; // 3 x i1 vector value -def v4i1 : VTVec<4, i1, 20>; // 4 x i1 vector value -def v8i1 : VTVec<8, i1, 21>; // 8 x i1 vector value -def v16i1 : VTVec<16, i1, 22>; // 16 x i1 vector value -def v32i1 : VTVec<32, i1, 23>; // 32 x i1 vector value -def v64i1 : VTVec<64, i1, 24>; // 64 x i1 vector value -def v128i1 : VTVec<128, i1, 25>; // 128 x i1 vector value -def v256i1 : VTVec<256, i1, 26>; // 256 x i1 vector value -def v512i1 : VTVec<512, i1, 27>; // 512 x i1 vector value -def v1024i1 : VTVec<1024, i1, 28>; // 1024 x i1 vector value -def v2048i1 : VTVec<2048, i1, 29>; // 2048 x i1 vector value - -def v128i2 : VTVec<128, i2, 30>; // 128 x i2 vector value -def v256i2 : VTVec<256, i2, 31>; // 256 x i2 vector value - -def v64i4 : VTVec<64, i4, 32>; // 64 x i4 vector value -def v128i4 : VTVec<128, i4, 33>; // 128 x i4 vector value - -def v1i8 : VTVec<1, i8, 34>; // 1 x i8 vector value -def v2i8 : VTVec<2, i8, 35>; // 2 x i8 vector value -def v3i8 : VTVec<3, i8, 36>; // 3 x i8 vector value -def v4i8 : VTVec<4, i8, 37>; // 4 x i8 vector value -def v8i8 : VTVec<8, i8, 38>; // 8 x i8 vector value -def v16i8 : VTVec<16, i8, 39>; // 16 x i8 vector value -def v32i8 : VTVec<32, i8, 40>; // 32 x i8 vector value -def v64i8 : VTVec<64, i8, 41>; // 64 x i8 vector value -def v128i8 : VTVec<128, i8, 42>; // 128 x i8 vector value -def v256i8 : VTVec<256, i8, 43>; // 256 x i8 vector value -def v512i8 : VTVec<512, i8, 44>; // 512 x i8 vector value -def v1024i8 : VTVec<1024, i8, 45>; // 1024 x i8 vector value - -def v1i16 : VTVec<1, i16, 46>; // 1 x i16 vector value -def v2i16 : VTVec<2, i16, 47>; // 2 x i16 vector value -def v3i16 : VTVec<3, i16, 48>; // 3 x i16 vector value -def v4i16 : VTVec<4, i16, 49>; // 4 x i16 vector value -def v8i16 : VTVec<8, i16, 50>; // 8 x i16 vector value -def v16i16 : VTVec<16, i16, 51>; // 16 x i16 vector value -def v32i16 : VTVec<32, i16, 52>; // 32 x i16 vector value -def v64i16 : VTVec<64, i16, 53>; // 64 x i16 vector value -def v128i16 : VTVec<128, i16, 54>; // 128 x i16 vector value -def v256i16 : VTVec<256, i16, 55>; // 256 x i16 vector value -def v512i16 : VTVec<512, i16, 56>; // 512 x i16 vector value - -def v1i32 : VTVec<1, i32, 57>; // 1 x i32 vector value -def v2i32 : VTVec<2, i32, 58>; // 2 x i32 vector value -def v3i32 : VTVec<3, i32, 59>; // 3 x i32 vector value -def v4i32 : VTVec<4, i32, 60>; // 4 x i32 vector value -def v5i32 : VTVec<5, i32, 61>; // 5 x i32 vector value -def v6i32 : VTVec<6, i32, 62>; // 6 x f32 vector value -def v7i32 : VTVec<7, i32, 63>; // 7 x f32 vector value -def v8i32 : VTVec<8, i32, 64>; // 8 x i32 vector value -def v9i32 : VTVec<9, i32, 65>; // 9 x i32 vector value -def v10i32 : VTVec<10, i32, 66>; // 10 x i32 vector value -def v11i32 : VTVec<11, i32, 67>; // 11 x i32 vector value -def v12i32 : VTVec<12, i32, 68>; // 12 x i32 vector value -def v16i32 : VTVec<16, i32, 69>; // 16 x i32 vector value -def v32i32 : VTVec<32, i32, 70>; // 32 x i32 vector value -def v64i32 : VTVec<64, i32, 71>; // 64 x i32 vector value -def v128i32 : VTVec<128, i32, 72>; // 128 x i32 vector value -def v256i32 : VTVec<256, i32, 73>; // 256 x i32 vector value -def v512i32 : VTVec<512, i32, 74>; // 512 x i32 vector value -def v1024i32 : VTVec<1024, i32, 75>; // 1024 x i32 vector value -def v2048i32 : VTVec<2048, i32, 76>; // 2048 x i32 vector value - -def v1i64 : VTVec<1, i64, 77>; // 1 x i64 vector value -def v2i64 : VTVec<2, i64, 78>; // 2 x i64 vector value -def v3i64 : VTVec<3, i64, 79>; // 3 x i64 vector value -def v4i64 : VTVec<4, i64, 80>; // 4 x i64 vector value -def v8i64 : VTVec<8, i64, 81>; // 8 x i64 vector value -def v16i64 : VTVec<16, i64, 82>; // 16 x i64 vector value -def v32i64 : VTVec<32, i64, 83>; // 32 x i64 vector value -def v64i64 : VTVec<64, i64, 84>; // 64 x i64 vector value -def v128i64 : VTVec<128, i64, 85>; // 128 x i64 vector value -def v256i64 : VTVec<256, i64, 86>; // 256 x i64 vector value - -def v1i128 : VTVec<1, i128, 87>; // 1 x i128 vector value - -def v1f16 : VTVec<1, f16, 88>; // 1 x f16 vector value -def v2f16 : VTVec<2, f16, 89>; // 2 x f16 vector value -def v3f16 : VTVec<3, f16, 90>; // 3 x f16 vector value -def v4f16 : VTVec<4, f16, 91>; // 4 x f16 vector value -def v8f16 : VTVec<8, f16, 92>; // 8 x f16 vector value -def v16f16 : VTVec<16, f16, 93>; // 16 x f16 vector value -def v32f16 : VTVec<32, f16, 94>; // 32 x f16 vector value -def v64f16 : VTVec<64, f16, 95>; // 64 x f16 vector value -def v128f16 : VTVec<128, f16, 96>; // 128 x f16 vector value -def v256f16 : VTVec<256, f16, 97>; // 256 x f16 vector value -def v512f16 : VTVec<512, f16, 98>; // 512 x f16 vector value - -def v2bf16 : VTVec<2, bf16, 99>; // 2 x bf16 vector value -def v3bf16 : VTVec<3, bf16, 100>; // 3 x bf16 vector value -def v4bf16 : VTVec<4, bf16, 101>; // 4 x bf16 vector value -def v8bf16 : VTVec<8, bf16, 102>; // 8 x bf16 vector value -def v16bf16 : VTVec<16, bf16, 103>; // 16 x bf16 vector value -def v32bf16 : VTVec<32, bf16, 104>; // 32 x bf16 vector value -def v64bf16 : VTVec<64, bf16, 105>; // 64 x bf16 vector value -def v128bf16 : VTVec<128, bf16, 106>; // 128 x bf16 vector value - -def v1f32 : VTVec<1, f32, 107>; // 1 x f32 vector value -def v2f32 : VTVec<2, f32, 108>; // 2 x f32 vector value -def v3f32 : VTVec<3, f32, 109>; // 3 x f32 vector value -def v4f32 : VTVec<4, f32, 110>; // 4 x f32 vector value -def v5f32 : VTVec<5, f32, 111>; // 5 x f32 vector value -def v6f32 : VTVec<6, f32, 112>; // 6 x f32 vector value -def v7f32 : VTVec<7, f32, 113>; // 7 x f32 vector value -def v8f32 : VTVec<8, f32, 114>; // 8 x f32 vector value -def v9f32 : VTVec<9, f32, 115>; // 9 x f32 vector value -def v10f32 : VTVec<10, f32, 116>; // 10 x f32 vector value -def v11f32 : VTVec<11, f32, 117>; // 11 x f32 vector value -def v12f32 : VTVec<12, f32, 118>; // 12 x f32 vector value -def v16f32 : VTVec<16, f32, 119>; // 16 x f32 vector value -def v32f32 : VTVec<32, f32, 120>; // 32 x f32 vector value -def v64f32 : VTVec<64, f32, 121>; // 64 x f32 vector value -def v128f32 : VTVec<128, f32, 122>; // 128 x f32 vector value -def v256f32 : VTVec<256, f32, 123>; // 256 x f32 vector value -def v512f32 : VTVec<512, f32, 124>; // 512 x f32 vector value -def v1024f32 : VTVec<1024, f32, 125>; // 1024 x f32 vector value -def v2048f32 : VTVec<2048, f32, 126>; // 2048 x f32 vector value - -def v1f64 : VTVec<1, f64, 127>; // 1 x f64 vector value -def v2f64 : VTVec<2, f64, 128>; // 2 x f64 vector value -def v3f64 : VTVec<3, f64, 129>; // 3 x f64 vector value -def v4f64 : VTVec<4, f64, 130>; // 4 x f64 vector value -def v8f64 : VTVec<8, f64, 131>; // 8 x f64 vector value -def v16f64 : VTVec<16, f64, 132>; // 16 x f64 vector value -def v32f64 : VTVec<32, f64, 133>; // 32 x f64 vector value -def v64f64 : VTVec<64, f64, 134>; // 64 x f64 vector value -def v128f64 : VTVec<128, f64, 135>; // 128 x f64 vector value -def v256f64 : VTVec<256, f64, 136>; // 256 x f64 vector value - -def nxv1i1 : VTScalableVec<1, i1, 137>; // n x 1 x i1 vector value -def nxv2i1 : VTScalableVec<2, i1, 138>; // n x 2 x i1 vector value -def nxv4i1 : VTScalableVec<4, i1, 139>; // n x 4 x i1 vector value -def nxv8i1 : VTScalableVec<8, i1, 140>; // n x 8 x i1 vector value -def nxv16i1 : VTScalableVec<16, i1, 141>; // n x 16 x i1 vector value -def nxv32i1 : VTScalableVec<32, i1, 142>; // n x 32 x i1 vector value -def nxv64i1 : VTScalableVec<64, i1, 143>; // n x 64 x i1 vector value - -def nxv1i8 : VTScalableVec<1, i8, 144>; // n x 1 x i8 vector value -def nxv2i8 : VTScalableVec<2, i8, 145>; // n x 2 x i8 vector value -def nxv4i8 : VTScalableVec<4, i8, 146>; // n x 4 x i8 vector value -def nxv8i8 : VTScalableVec<8, i8, 147>; // n x 8 x i8 vector value -def nxv16i8 : VTScalableVec<16, i8, 148>; // n x 16 x i8 vector value -def nxv32i8 : VTScalableVec<32, i8, 149>; // n x 32 x i8 vector value -def nxv64i8 : VTScalableVec<64, i8, 150>; // n x 64 x i8 vector value - -def nxv1i16 : VTScalableVec<1, i16, 151>; // n x 1 x i16 vector value -def nxv2i16 : VTScalableVec<2, i16, 152>; // n x 2 x i16 vector value -def nxv4i16 : VTScalableVec<4, i16, 153>; // n x 4 x i16 vector value -def nxv8i16 : VTScalableVec<8, i16, 154>; // n x 8 x i16 vector value -def nxv16i16 : VTScalableVec<16, i16, 155>; // n x 16 x i16 vector value -def nxv32i16 : VTScalableVec<32, i16, 156>; // n x 32 x i16 vector value - -def nxv1i32 : VTScalableVec<1, i32, 157>; // n x 1 x i32 vector value -def nxv2i32 : VTScalableVec<2, i32, 158>; // n x 2 x i32 vector value -def nxv4i32 : VTScalableVec<4, i32, 159>; // n x 4 x i32 vector value -def nxv8i32 : VTScalableVec<8, i32, 160>; // n x 8 x i32 vector value -def nxv16i32 : VTScalableVec<16, i32, 161>; // n x 16 x i32 vector value -def nxv32i32 : VTScalableVec<32, i32, 162>; // n x 32 x i32 vector value - -def nxv1i64 : VTScalableVec<1, i64, 163>; // n x 1 x i64 vector value -def nxv2i64 : VTScalableVec<2, i64, 164>; // n x 2 x i64 vector value -def nxv4i64 : VTScalableVec<4, i64, 165>; // n x 4 x i64 vector value -def nxv8i64 : VTScalableVec<8, i64, 166>; // n x 8 x i64 vector value -def nxv16i64 : VTScalableVec<16, i64, 167>; // n x 16 x i64 vector value -def nxv32i64 : VTScalableVec<32, i64, 168>; // n x 32 x i64 vector value - -def nxv1f16 : VTScalableVec<1, f16, 169>; // n x 1 x f16 vector value -def nxv2f16 : VTScalableVec<2, f16, 170>; // n x 2 x f16 vector value -def nxv4f16 : VTScalableVec<4, f16, 171>; // n x 4 x f16 vector value -def nxv8f16 : VTScalableVec<8, f16, 172>; // n x 8 x f16 vector value -def nxv16f16 : VTScalableVec<16, f16, 173>; // n x 16 x f16 vector value -def nxv32f16 : VTScalableVec<32, f16, 174>; // n x 32 x f16 vector value - -def nxv1bf16 : VTScalableVec<1, bf16, 175>; // n x 1 x bf16 vector value -def nxv2bf16 : VTScalableVec<2, bf16, 176>; // n x 2 x bf16 vector value -def nxv4bf16 : VTScalableVec<4, bf16, 177>; // n x 4 x bf16 vector value -def nxv8bf16 : VTScalableVec<8, bf16, 178>; // n x 8 x bf16 vector value -def nxv16bf16 : VTScalableVec<16, bf16, 179>; // n x 16 x bf16 vector value -def nxv32bf16 : VTScalableVec<32, bf16, 180>; // n x 32 x bf16 vector value - -def nxv1f32 : VTScalableVec<1, f32, 181>; // n x 1 x f32 vector value -def nxv2f32 : VTScalableVec<2, f32, 182>; // n x 2 x f32 vector value -def nxv4f32 : VTScalableVec<4, f32, 183>; // n x 4 x f32 vector value -def nxv8f32 : VTScalableVec<8, f32, 184>; // n x 8 x f32 vector value -def nxv16f32 : VTScalableVec<16, f32, 185>; // n x 16 x f32 vector value - -def nxv1f64 : VTScalableVec<1, f64, 186>; // n x 1 x f64 vector value -def nxv2f64 : VTScalableVec<2, f64, 187>; // n x 2 x f64 vector value -def nxv4f64 : VTScalableVec<4, f64, 188>; // n x 4 x f64 vector value -def nxv8f64 : VTScalableVec<8, f64, 189>; // n x 8 x f64 vector value - -def x86mmx : ValueType<64, 190>; // X86 MMX value -def Glue : ValueType<0, 191>; // Pre-RA sched glue -def isVoid : ValueType<0, 192>; // Produces no value -def untyped : ValueType<8, 193> { // Produces an untyped value +// EVM local begin +def i256 : VTInt<256, 10>; // 256-bit integer value +def i512 : VTInt<512, 11>; // 512-bit integer value + +def bf16 : VTFP<16, 12>; // 16-bit brain floating point value +def f16 : VTFP<16, 13>; // 16-bit floating point value +def f32 : VTFP<32, 14>; // 32-bit floating point value +def f64 : VTFP<64, 15>; // 64-bit floating point value +def f80 : VTFP<80, 16>; // 80-bit floating point value +def f128 : VTFP<128, 17>; // 128-bit floating point value +def ppcf128 : VTFP<128, 18>; // PPC 128-bit floating point value + +def v1i1 : VTVec<1, i1, 19>; // 1 x i1 vector value +def v2i1 : VTVec<2, i1, 20>; // 2 x i1 vector value +def v3i1 : VTVec<3, i1, 21>; // 3 x i1 vector value +def v4i1 : VTVec<4, i1, 22>; // 4 x i1 vector value +def v8i1 : VTVec<8, i1, 23>; // 8 x i1 vector value +def v16i1 : VTVec<16, i1, 24>; // 16 x i1 vector value +def v32i1 : VTVec<32, i1, 25>; // 32 x i1 vector value +def v64i1 : VTVec<64, i1, 26>; // 64 x i1 vector value +def v128i1 : VTVec<128, i1, 27>; // 128 x i1 vector value +def v256i1 : VTVec<256, i1, 28>; // 256 x i1 vector value +def v512i1 : VTVec<512, i1, 29>; // 512 x i1 vector value +def v1024i1 : VTVec<1024, i1, 30>; // 1024 x i1 vector value +def v2048i1 : VTVec<2048, i1, 31>; // 2048 x i1 vector value + +def v128i2 : VTVec<128, i2, 32>; // 128 x i2 vector value +def v256i2 : VTVec<256, i2, 33>; // 256 x i2 vector value + +def v64i4 : VTVec<64, i4, 34>; // 64 x i4 vector value +def v128i4 : VTVec<128, i4, 35>; // 128 x i4 vector value + +def v1i8 : VTVec<1, i8, 36>; // 1 x i8 vector value +def v2i8 : VTVec<2, i8, 37>; // 2 x i8 vector value +def v3i8 : VTVec<3, i8, 38>; // 3 x i8 vector value +def v4i8 : VTVec<4, i8, 39>; // 4 x i8 vector value +def v8i8 : VTVec<8, i8, 40>; // 8 x i8 vector value +def v16i8 : VTVec<16, i8, 41>; // 16 x i8 vector value +def v32i8 : VTVec<32, i8, 42>; // 32 x i8 vector value +def v64i8 : VTVec<64, i8, 43>; // 64 x i8 vector value +def v128i8 : VTVec<128, i8, 44>; // 128 x i8 vector value +def v256i8 : VTVec<256, i8, 45>; // 256 x i8 vector value +def v512i8 : VTVec<512, i8, 46>; // 512 x i8 vector value +def v1024i8 : VTVec<1024, i8, 47>; // 1024 x i8 vector value + +def v1i16 : VTVec<1, i16, 48>; // 1 x i16 vector value +def v2i16 : VTVec<2, i16, 49>; // 2 x i16 vector value +def v3i16 : VTVec<3, i16, 50>; // 3 x i16 vector value +def v4i16 : VTVec<4, i16, 51>; // 4 x i16 vector value +def v8i16 : VTVec<8, i16, 52>; // 8 x i16 vector value +def v16i16 : VTVec<16, i16, 53>; // 16 x i16 vector value +def v32i16 : VTVec<32, i16, 54>; // 32 x i16 vector value +def v64i16 : VTVec<64, i16, 55>; // 64 x i16 vector value +def v128i16 : VTVec<128, i16, 56>; // 128 x i16 vector value +def v256i16 : VTVec<256, i16, 57>; // 256 x i16 vector value +def v512i16 : VTVec<512, i16, 58>; // 512 x i16 vector value + +def v1i32 : VTVec<1, i32, 59>; // 1 x i32 vector value +def v2i32 : VTVec<2, i32, 60>; // 2 x i32 vector value +def v3i32 : VTVec<3, i32, 61>; // 3 x i32 vector value +def v4i32 : VTVec<4, i32, 62>; // 4 x i32 vector value +def v5i32 : VTVec<5, i32, 63>; // 5 x i32 vector value +def v6i32 : VTVec<6, i32, 64>; // 6 x f32 vector value +def v7i32 : VTVec<7, i32, 65>; // 7 x f32 vector value +def v8i32 : VTVec<8, i32, 66>; // 8 x i32 vector value +def v9i32 : VTVec<9, i32, 67>; // 9 x i32 vector value +def v10i32 : VTVec<10, i32, 68>; // 10 x i32 vector value +def v11i32 : VTVec<11, i32, 69>; // 11 x i32 vector value +def v12i32 : VTVec<12, i32, 70>; // 12 x i32 vector value +def v16i32 : VTVec<16, i32, 71>; // 16 x i32 vector value +def v32i32 : VTVec<32, i32, 72>; // 32 x i32 vector value +def v64i32 : VTVec<64, i32, 73>; // 64 x i32 vector value +def v128i32 : VTVec<128, i32, 74>; // 128 x i32 vector value +def v256i32 : VTVec<256, i32, 75>; // 256 x i32 vector value +def v512i32 : VTVec<512, i32, 76>; // 512 x i32 vector value +def v1024i32 : VTVec<1024, i32, 77>; // 1024 x i32 vector value +def v2048i32 : VTVec<2048, i32, 78>; // 2048 x i32 vector value + +def v1i64 : VTVec<1, i64, 79>; // 1 x i64 vector value +def v2i64 : VTVec<2, i64, 80>; // 2 x i64 vector value +def v3i64 : VTVec<3, i64, 81>; // 3 x i64 vector value +def v4i64 : VTVec<4, i64, 82>; // 4 x i64 vector value +def v8i64 : VTVec<8, i64, 83>; // 8 x i64 vector value +def v16i64 : VTVec<16, i64, 84>; // 16 x i64 vector value +def v32i64 : VTVec<32, i64, 85>; // 32 x i64 vector value +def v64i64 : VTVec<64, i64, 86>; // 64 x i64 vector value +def v128i64 : VTVec<128, i64, 87>; // 128 x i64 vector value +def v256i64 : VTVec<256, i64, 88>; // 256 x i64 vector value + +def v1i128 : VTVec<1, i128, 89>; // 1 x i128 vector value + +def v1f16 : VTVec<1, f16, 90>; // 1 x f16 vector value +def v2f16 : VTVec<2, f16, 91>; // 2 x f16 vector value +def v3f16 : VTVec<3, f16, 92>; // 3 x f16 vector value +def v4f16 : VTVec<4, f16, 93>; // 4 x f16 vector value +def v8f16 : VTVec<8, f16, 94>; // 8 x f16 vector value +def v16f16 : VTVec<16, f16, 95>; // 16 x f16 vector value +def v32f16 : VTVec<32, f16, 96>; // 32 x f16 vector value +def v64f16 : VTVec<64, f16, 97>; // 64 x f16 vector value +def v128f16 : VTVec<128, f16, 98>; // 128 x f16 vector value +def v256f16 : VTVec<256, f16, 99>; // 256 x f16 vector value +def v512f16 : VTVec<512, f16, 100>; // 512 x f16 vector value + +def v2bf16 : VTVec<2, bf16, 101>; // 2 x bf16 vector value +def v3bf16 : VTVec<3, bf16, 102>; // 3 x bf16 vector value +def v4bf16 : VTVec<4, bf16, 103>; // 4 x bf16 vector value +def v8bf16 : VTVec<8, bf16, 104>; // 8 x bf16 vector value +def v16bf16 : VTVec<16, bf16, 105>; // 16 x bf16 vector value +def v32bf16 : VTVec<32, bf16, 106>; // 32 x bf16 vector value +def v64bf16 : VTVec<64, bf16, 107>; // 64 x bf16 vector value +def v128bf16 : VTVec<128, bf16, 108>; // 128 x bf16 vector value + +def v1f32 : VTVec<1, f32, 109>; // 1 x f32 vector value +def v2f32 : VTVec<2, f32, 110>; // 2 x f32 vector value +def v3f32 : VTVec<3, f32, 111>; // 3 x f32 vector value +def v4f32 : VTVec<4, f32, 112>; // 4 x f32 vector value +def v5f32 : VTVec<5, f32, 113>; // 5 x f32 vector value +def v6f32 : VTVec<6, f32, 114>; // 6 x f32 vector value +def v7f32 : VTVec<7, f32, 115>; // 7 x f32 vector value +def v8f32 : VTVec<8, f32, 116>; // 8 x f32 vector value +def v9f32 : VTVec<9, f32, 117>; // 9 x f32 vector value +def v10f32 : VTVec<10, f32, 118>; // 10 x f32 vector value +def v11f32 : VTVec<11, f32, 119>; // 11 x f32 vector value +def v12f32 : VTVec<12, f32, 120>; // 12 x f32 vector value +def v16f32 : VTVec<16, f32, 121>; // 16 x f32 vector value +def v32f32 : VTVec<32, f32, 122>; // 32 x f32 vector value +def v64f32 : VTVec<64, f32, 123>; // 64 x f32 vector value +def v128f32 : VTVec<128, f32, 124>; // 128 x f32 vector value +def v256f32 : VTVec<256, f32, 125>; // 256 x f32 vector value +def v512f32 : VTVec<512, f32, 126>; // 512 x f32 vector value +def v1024f32 : VTVec<1024, f32, 127>; // 1024 x f32 vector value +def v2048f32 : VTVec<2048, f32, 128>; // 2048 x f32 vector value + +def v1f64 : VTVec<1, f64, 129>; // 1 x f64 vector value +def v2f64 : VTVec<2, f64, 130>; // 2 x f64 vector value +def v3f64 : VTVec<3, f64, 131>; // 3 x f64 vector value +def v4f64 : VTVec<4, f64, 132>; // 4 x f64 vector value +def v8f64 : VTVec<8, f64, 133>; // 8 x f64 vector value +def v16f64 : VTVec<16, f64, 134>; // 16 x f64 vector value +def v32f64 : VTVec<32, f64, 135>; // 32 x f64 vector value +def v64f64 : VTVec<64, f64, 136>; // 64 x f64 vector value +def v128f64 : VTVec<128, f64, 137>; // 128 x f64 vector value +def v256f64 : VTVec<256, f64, 138>; // 256 x f64 vector value + +def nxv1i1 : VTScalableVec<1, i1, 139>; // n x 1 x i1 vector value +def nxv2i1 : VTScalableVec<2, i1, 140>; // n x 2 x i1 vector value +def nxv4i1 : VTScalableVec<4, i1, 141>; // n x 4 x i1 vector value +def nxv8i1 : VTScalableVec<8, i1, 142>; // n x 8 x i1 vector value +def nxv16i1 : VTScalableVec<16, i1, 143>; // n x 16 x i1 vector value +def nxv32i1 : VTScalableVec<32, i1, 144>; // n x 32 x i1 vector value +def nxv64i1 : VTScalableVec<64, i1, 145>; // n x 64 x i1 vector value + +def nxv1i8 : VTScalableVec<1, i8, 146>; // n x 1 x i8 vector value +def nxv2i8 : VTScalableVec<2, i8, 147>; // n x 2 x i8 vector value +def nxv4i8 : VTScalableVec<4, i8, 148>; // n x 4 x i8 vector value +def nxv8i8 : VTScalableVec<8, i8, 149>; // n x 8 x i8 vector value +def nxv16i8 : VTScalableVec<16, i8, 150>; // n x 16 x i8 vector value +def nxv32i8 : VTScalableVec<32, i8, 151>; // n x 32 x i8 vector value +def nxv64i8 : VTScalableVec<64, i8, 152>; // n x 64 x i8 vector value + +def nxv1i16 : VTScalableVec<1, i16, 153>; // n x 1 x i16 vector value +def nxv2i16 : VTScalableVec<2, i16, 154>; // n x 2 x i16 vector value +def nxv4i16 : VTScalableVec<4, i16, 155>; // n x 4 x i16 vector value +def nxv8i16 : VTScalableVec<8, i16, 156>; // n x 8 x i16 vector value +def nxv16i16 : VTScalableVec<16, i16, 157>; // n x 16 x i16 vector value +def nxv32i16 : VTScalableVec<32, i16, 158>; // n x 32 x i16 vector value + +def nxv1i32 : VTScalableVec<1, i32, 159>; // n x 1 x i32 vector value +def nxv2i32 : VTScalableVec<2, i32, 160>; // n x 2 x i32 vector value +def nxv4i32 : VTScalableVec<4, i32, 161>; // n x 4 x i32 vector value +def nxv8i32 : VTScalableVec<8, i32, 162>; // n x 8 x i32 vector value +def nxv16i32 : VTScalableVec<16, i32, 163>; // n x 16 x i32 vector value +def nxv32i32 : VTScalableVec<32, i32, 164>; // n x 32 x i32 vector value + +def nxv1i64 : VTScalableVec<1, i64, 165>; // n x 1 x i64 vector value +def nxv2i64 : VTScalableVec<2, i64, 166>; // n x 2 x i64 vector value +def nxv4i64 : VTScalableVec<4, i64, 167>; // n x 4 x i64 vector value +def nxv8i64 : VTScalableVec<8, i64, 168>; // n x 8 x i64 vector value +def nxv16i64 : VTScalableVec<16, i64, 169>; // n x 16 x i64 vector value +def nxv32i64 : VTScalableVec<32, i64, 170>; // n x 32 x i64 vector value + +def nxv1f16 : VTScalableVec<1, f16, 171>; // n x 1 x f16 vector value +def nxv2f16 : VTScalableVec<2, f16, 172>; // n x 2 x f16 vector value +def nxv4f16 : VTScalableVec<4, f16, 173>; // n x 4 x f16 vector value +def nxv8f16 : VTScalableVec<8, f16, 174>; // n x 8 x f16 vector value +def nxv16f16 : VTScalableVec<16, f16, 175>; // n x 16 x f16 vector value +def nxv32f16 : VTScalableVec<32, f16, 176>; // n x 32 x f16 vector value + +def nxv1bf16 : VTScalableVec<1, bf16, 177>; // n x 1 x bf16 vector value +def nxv2bf16 : VTScalableVec<2, bf16, 178>; // n x 2 x bf16 vector value +def nxv4bf16 : VTScalableVec<4, bf16, 179>; // n x 4 x bf16 vector value +def nxv8bf16 : VTScalableVec<8, bf16, 180>; // n x 8 x bf16 vector value +def nxv16bf16 : VTScalableVec<16, bf16, 181>; // n x 16 x bf16 vector value +def nxv32bf16 : VTScalableVec<32, bf16, 182>; // n x 32 x bf16 vector value + +def nxv1f32 : VTScalableVec<1, f32, 183>; // n x 1 x f32 vector value +def nxv2f32 : VTScalableVec<2, f32, 184>; // n x 2 x f32 vector value +def nxv4f32 : VTScalableVec<4, f32, 185>; // n x 4 x f32 vector value +def nxv8f32 : VTScalableVec<8, f32, 186>; // n x 8 x f32 vector value +def nxv16f32 : VTScalableVec<16, f32, 187>; // n x 16 x f32 vector value + +def nxv1f64 : VTScalableVec<1, f64, 188>; // n x 1 x f64 vector value +def nxv2f64 : VTScalableVec<2, f64, 189>; // n x 2 x f64 vector value +def nxv4f64 : VTScalableVec<4, f64, 190>; // n x 4 x f64 vector value +def nxv8f64 : VTScalableVec<8, f64, 191>; // n x 8 x f64 vector value + +def x86mmx : ValueType<64, 192>; // X86 MMX value +def Glue : ValueType<0, 193>; // Pre-RA sched glue +def isVoid : ValueType<0, 194>; // Produces no value +def untyped : ValueType<8, 195> { // Produces an untyped value let LLVMName = "Untyped"; } -def funcref : ValueType<0, 194>; // WebAssembly's funcref type -def externref : ValueType<0, 195>; // WebAssembly's externref type -def exnref : ValueType<0, 196>; // WebAssembly's exnref type -def x86amx : ValueType<8192, 197>; // X86 AMX value -def i64x8 : ValueType<512, 198>; // 8 Consecutive GPRs (AArch64) +def funcref : ValueType<0, 196>; // WebAssembly's funcref type +def externref : ValueType<0, 197>; // WebAssembly's externref type +def exnref : ValueType<0, 198>; // WebAssembly's exnref type +def x86amx : ValueType<8192, 199>; // X86 AMX value +def i64x8 : ValueType<512, 200>; // 8 Consecutive GPRs (AArch64) def aarch64svcount - : ValueType<16, 199>; // AArch64 predicate-as-counter -def spirvbuiltin : ValueType<0, 200>; // SPIR-V's builtin type + : ValueType<16, 201>; // AArch64 predicate-as-counter +def spirvbuiltin : ValueType<0, 202>; // SPIR-V's builtin type +// EVM local end let isNormalValueType = false in { def token : ValueType<0, 248>; // TokenTy diff --git a/llvm/include/llvm/IR/CMakeLists.txt b/llvm/include/llvm/IR/CMakeLists.txt index 468d663796ed..71dcca28d054 100644 --- a/llvm/include/llvm/IR/CMakeLists.txt +++ b/llvm/include/llvm/IR/CMakeLists.txt @@ -22,4 +22,7 @@ tablegen(LLVM IntrinsicsWebAssembly.h -gen-intrinsic-enums -intrinsic-prefix=was tablegen(LLVM IntrinsicsX86.h -gen-intrinsic-enums -intrinsic-prefix=x86) tablegen(LLVM IntrinsicsXCore.h -gen-intrinsic-enums -intrinsic-prefix=xcore) tablegen(LLVM IntrinsicsVE.h -gen-intrinsic-enums -intrinsic-prefix=ve) +# EVM local begin +tablegen(LLVM IntrinsicsEVM.h -gen-intrinsic-enums -intrinsic-prefix=evm) +# EVM local end add_public_tablegen_target(intrinsics_gen) diff --git a/llvm/include/llvm/IR/IRBuilder.h b/llvm/include/llvm/IR/IRBuilder.h index 31a1fef32199..b96eb987136a 100644 --- a/llvm/include/llvm/IR/IRBuilder.h +++ b/llvm/include/llvm/IR/IRBuilder.h @@ -532,6 +532,11 @@ class IRBuilderBase { /// Fetch the type representing a 128-bit integer. IntegerType *getInt128Ty() { return Type::getInt128Ty(Context); } + // EVM local begin + /// Fetch the type representing a 256-bit integer. + IntegerType *getInt256Ty() { return Type::getInt256Ty(Context); } + // EVM local end + /// Fetch the type representing an N-bit integer. IntegerType *getIntNTy(unsigned N) { return Type::getIntNTy(Context, N); diff --git a/llvm/include/llvm/IR/Instructions.h b/llvm/include/llvm/IR/Instructions.h index c07fee58e4bd..c93e7fe665a7 100644 --- a/llvm/include/llvm/IR/Instructions.h +++ b/llvm/include/llvm/IR/Instructions.h @@ -48,6 +48,7 @@ class APInt; class BasicBlock; class ConstantInt; class DataLayout; +struct KnownBits; class StringRef; class Type; class Value; @@ -1279,6 +1280,11 @@ class ICmpInst: public CmpInst { static bool compare(const APInt &LHS, const APInt &RHS, ICmpInst::Predicate Pred); + /// Return result of `LHS Pred RHS`, if it can be determined from the + /// KnownBits. Otherwise return nullopt. + static std::optional compare(const KnownBits &LHS, const KnownBits &RHS, + ICmpInst::Predicate Pred); + // Methods for support type inquiry through isa, cast, and dyn_cast: static bool classof(const Instruction *I) { return I->getOpcode() == Instruction::ICmp; diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td index b4e758136b39..dee43218b12b 100644 --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -321,6 +321,9 @@ def IIT_I4 : IIT_Int<4, 58>; def IIT_AARCH64_SVCOUNT : IIT_VT; def IIT_V6 : IIT_Vec<6, 60>; def IIT_V10 : IIT_Vec<10, 61>; +// EVM local begin +def IIT_I256 : IIT_Int<256, 62>; +// EVM local end } defvar IIT_all_FixedTypes = !filter(iit, IIT_all, @@ -483,6 +486,10 @@ def llvm_i16_ty : LLVMType; def llvm_i32_ty : LLVMType; def llvm_i64_ty : LLVMType; def llvm_i128_ty : LLVMType; +// EVM local begin +def llvm_i256_ty : LLVMType; +def llvm_i512_ty : LLVMType; +// EVM local end def llvm_half_ty : LLVMType; def llvm_bfloat_ty : LLVMType; def llvm_float_ty : LLVMType; @@ -2764,5 +2771,7 @@ include "llvm/IR/IntrinsicsSPIRV.td" include "llvm/IR/IntrinsicsVE.td" include "llvm/IR/IntrinsicsDirectX.td" include "llvm/IR/IntrinsicsLoongArch.td" - +// EVM local begin +include "llvm/IR/IntrinsicsEVM.td" +// EVM local end #endif // TEST_INTRINSICS_SUPPRESS_DEFS diff --git a/llvm/include/llvm/IR/IntrinsicsEVM.td b/llvm/include/llvm/IR/IntrinsicsEVM.td new file mode 100644 index 000000000000..e70e6ea9f824 --- /dev/null +++ b/llvm/include/llvm/IR/IntrinsicsEVM.td @@ -0,0 +1,400 @@ +//===---- IntrinsicsEVM.td - Defines EVM intrinsics --------*- tablegen -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Defines all EVM intrinsics. +// +//===----------------------------------------------------------------------===// + +// This must be in sync with the declaration of address spaces in EVM.h +def AS { + int STACK = 0; + int HEAP = 1; + int CALL_DATA = 2; + int RETURN_DATA = 3; + int CODE = 4; + int STORAGE = 5; + int TSTORAGE = 6; +} + +let TargetPrefix = "evm" in { + +// Ethereum Virtual Machine (EVM) opcodes and corresponding intrinsics can be +// grouped by the runtime effects i.e., how their behavior or outputs are +// affected by state changes during a transaction: +// +// - 'Readnone' +// These do not read or write any persistent world-state once execution +// begins. Their results depend only on provided inputs or fixed +// environment data (constant during the transaction). They have no +// lasting side effects and return the same result given the same inputs. +// +// - 'Volatile (State-Dependent)' +// These read data from the world state or execution environment that may +// change within the same transaction (for example, due to prior storage +// writes, value transfers, or external calls). Re-invoking them after a +// state mutation can yield a different result. They themselves do not +// permanently modify state. Such intrinsics have +// 'IntrInaccessibleMemOnly' attribute, or 'IntrReadMem, +// IntrInaccessibleMemOnly' attributes to model dependency from the State. +// NOTE: we assume (to be confirmed) that the heap is not part of the +// State; therefore, it cannot be modified in a volatile manner. +// +// - 'Side-Effecting (State-Changing)' +// These opcodes cause persistent changes in world state - writing to +// storage, emitting logs, creating or destroying contracts, or +// transferring Ether. Their execution produces lasting side effects +// recorded on-chain (storage updates, logs, new accounts, etc.). Such +// intrinsics have no declared memory attributes, but their behavior is +// refined through EVM alias analysis.. + +// Arithmetical operations. +def int_evm_div : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], + [IntrNoMem, IntrWillReturn]>; + +def int_evm_sdiv : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], + [IntrNoMem, IntrWillReturn]>; + +def int_evm_mod : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], + [IntrNoMem, IntrWillReturn]>; + +def int_evm_smod : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], + [IntrNoMem, IntrWillReturn]>; + +def int_evm_shl : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], + [IntrNoMem, IntrWillReturn]>; + +def int_evm_shr : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], + [IntrNoMem, IntrWillReturn]>; + +def int_evm_sar : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], + [IntrNoMem, IntrWillReturn]>; + +def int_evm_addmod + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty, llvm_i256_ty], + [IntrNoMem, IntrWillReturn]>; + +def int_evm_mulmod + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty, llvm_i256_ty], + [IntrNoMem, IntrWillReturn]>; + +def int_evm_exp : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], + [IntrNoMem, IntrWillReturn]>; + +def int_evm_sha3 + : Intrinsic<[llvm_i256_ty], [LLVMQualPointerType, llvm_i256_ty], + [IntrReadMem, IntrArgMemOnly, IntrNoCallback, + NoCapture>, IntrWillReturn]>; + +def int_evm_signextend + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], + [IntrNoMem, IntrWillReturn]>; + +def int_evm_byte : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], + [IntrNoMem, IntrWillReturn]>; + +// Memory operations. +def int_evm_mstore8 + : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty], + [IntrWriteMem, IntrArgMemOnly, NoCapture>, + IntrNoCallback, IntrWillReturn]>; + +// Special getters that must not be relocated. +def int_evm_msize : Intrinsic<[llvm_i256_ty], [], [IntrWillReturn]>; + +def int_evm_pc : Intrinsic<[llvm_i256_ty], [], [IntrWillReturn]>; + +// TODO: #904: Set IntrInaccessibleMemOnly attribute to gas intrinsic. +// Currently, it is not set because benchmark numbers showed that it is not +// profitable to do that for heap address space. Revisit this decision once +// optimization for free ptr updates is implemented. +def int_evm_gas : Intrinsic<[llvm_i256_ty], [], [IntrWillReturn]>; + +// Getting values from the context. +def int_evm_address + : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; + +def int_evm_origin + : Intrinsic<[llvm_i256_ty], [], + [IntrReadMem, IntrInaccessibleMemOnly, IntrWillReturn]>; + +def int_evm_caller + : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; + +def int_evm_balance : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; + +def int_evm_callvalue + : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; + +def int_evm_calldatasize + : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; + +def int_evm_calldataload + : Intrinsic<[llvm_i256_ty], [LLVMQualPointerType], + [IntrReadMem, IntrArgMemOnly, IntrNoCallback, + IntrWillReturn]>; + +def int_evm_codesize : Intrinsic<[llvm_i256_ty], [], + [IntrNoMem, IntrNoCallback, IntrWillReturn]>; + +def int_evm_gasprice : Intrinsic<[llvm_i256_ty], [], + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; + +def int_evm_extcodesize : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; + +// TODO: #878. This intrinsic’s declaration implies that it reads/writes to +// inaccessible memory (a limitation of the TableGen declaration format). +// Actually, it only reads from inaccessible memory. Its memory properties +// can be refined in the custom implementation of EVM AA. +def int_evm_extcodecopy + : Intrinsic<[], + [llvm_i256_ty, LLVMQualPointerType, + LLVMQualPointerType, llvm_i256_ty], + [IntrInaccessibleMemOrArgMemOnly, WriteOnly>, + ReadOnly>, NoCapture>, + NoCapture>, IntrNoCallback, IntrWillReturn]>; + +def int_evm_returndatasize : Intrinsic<[llvm_i256_ty], [], + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; + +def int_evm_extcodehash : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; + +def int_evm_blockhash : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; + +def int_evm_blobhash : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; + +def int_evm_coinbase : Intrinsic<[llvm_i256_ty], [], + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; + +def int_evm_timestamp : Intrinsic<[llvm_i256_ty], [], + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; + +def int_evm_number : Intrinsic<[llvm_i256_ty], [], + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; + +def int_evm_difficulty : Intrinsic<[llvm_i256_ty], [], + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; + +def int_evm_gaslimit : Intrinsic<[llvm_i256_ty], [], + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; + +def int_evm_chainid : Intrinsic<[llvm_i256_ty], [], + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; + +def int_evm_selfbalance : Intrinsic<[llvm_i256_ty], [], + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; + +def int_evm_basefee : Intrinsic<[llvm_i256_ty], [], + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; + +def int_evm_blobbasefee : Intrinsic<[llvm_i256_ty], [], + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; + +// Logging +def int_evm_log0 + : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty], + [IntrInaccessibleMemOrArgMemOnly, ReadOnly>, + NoCapture>, IntrNoCallback, IntrWillReturn]>; + +def int_evm_log1 + : Intrinsic<[], + [LLVMQualPointerType, llvm_i256_ty, llvm_i256_ty], + [IntrInaccessibleMemOrArgMemOnly, ReadOnly>, + NoCapture>, IntrNoCallback, IntrWillReturn]>; + +def int_evm_log2 + : Intrinsic<[], + [LLVMQualPointerType, llvm_i256_ty, llvm_i256_ty, + llvm_i256_ty], + [IntrInaccessibleMemOrArgMemOnly, ReadOnly>, + NoCapture>, IntrNoCallback, IntrWillReturn]>; + +def int_evm_log3 + : Intrinsic<[], + [LLVMQualPointerType, llvm_i256_ty, llvm_i256_ty, + llvm_i256_ty, llvm_i256_ty], + [IntrInaccessibleMemOrArgMemOnly, ReadOnly>, + NoCapture>, IntrNoCallback, IntrWillReturn]>; + +def int_evm_log4 + : Intrinsic<[], + [LLVMQualPointerType, llvm_i256_ty, llvm_i256_ty, + llvm_i256_ty, llvm_i256_ty, llvm_i256_ty], + [IntrInaccessibleMemOrArgMemOnly, ReadOnly>, + NoCapture>, IntrNoCallback, IntrWillReturn]>; + +// System calls. +// The following intrinsics do not explicitly specify memory attributes, +// which by default implies arbitrary memory effects. Their actual +// (refined) memory effects are determined in EVMAAResult::getModRefInfo(). +// Here, we only indicate which pointer arguments read from or write to +// heap memory, simplifying the implementation of +// EVMAAResult::getArgModRefInfo(). +def int_evm_create + : Intrinsic<[llvm_i256_ty], + [llvm_i256_ty, LLVMQualPointerType, llvm_i256_ty], + [ReadOnly>, NoCapture>, + IntrNoCallback, IntrWillReturn]>; + +def int_evm_call : Intrinsic<[llvm_i256_ty], + [llvm_i256_ty, llvm_i256_ty, llvm_i256_ty, + LLVMQualPointerType, llvm_i256_ty, + LLVMQualPointerType, llvm_i256_ty], + [ReadOnly>, WriteOnly>, + NoCapture>, NoCapture>, + IntrNoCallback, IntrWillReturn]>; + +def int_evm_callcode + : Intrinsic<[llvm_i256_ty], + [llvm_i256_ty, llvm_i256_ty, llvm_i256_ty, + LLVMQualPointerType, llvm_i256_ty, + LLVMQualPointerType, llvm_i256_ty], + [ReadOnly>, WriteOnly>, + NoCapture>, NoCapture>, + IntrNoCallback, IntrWillReturn]>; + +def int_evm_delegatecall + : Intrinsic<[llvm_i256_ty], + [llvm_i256_ty, llvm_i256_ty, LLVMQualPointerType, + llvm_i256_ty, LLVMQualPointerType, llvm_i256_ty], + [ReadOnly>, WriteOnly>, + NoCapture>, NoCapture>, + IntrNoCallback, IntrWillReturn]>; + +def int_evm_create2 + : Intrinsic<[llvm_i256_ty], + [llvm_i256_ty, LLVMQualPointerType, llvm_i256_ty, + llvm_i256_ty], + [ReadOnly>, NoCapture>, + IntrNoCallback, IntrWillReturn]>; + +def int_evm_staticcall + : Intrinsic<[llvm_i256_ty], + [llvm_i256_ty, llvm_i256_ty, LLVMQualPointerType, + llvm_i256_ty, LLVMQualPointerType, llvm_i256_ty], + [ReadOnly>, WriteOnly>, + NoCapture>, NoCapture>, + IntrNoCallback, IntrWillReturn]>; + +def int_evm_selfdestruct : Intrinsic<[], [llvm_i256_ty], []>; + +def int_evm_stop : Intrinsic<[], [], [IntrNoReturn]>; + +def int_evm_invalid : Intrinsic<[], [], [IntrNoReturn]>; + +// Return with error. +// 'Return' and 'Revert' exhibit slightly different behaviors. +// In EVM AA, 'Return' is treated as aliasing with the Storage/Transient +// address spaces, whereas 'Revert' is not. This distinction allows the +// elimination of stores to Storage that occur before a 'Revert'. +def int_evm_return + : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty], + [IntrReadMem, ReadOnly>, IntrNoReturn]>; + +def int_evm_revert + : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty], + [IntrReadMem, IntrArgMemOnly, ReadOnly>, + IntrNoReturn]>; + +def int_evm_memmoveas1as1 + : Intrinsic<[], + [LLVMQualPointerType, LLVMQualPointerType, + llvm_i256_ty, llvm_i1_ty], + [IntrArgMemOnly, IntrWillReturn, IntrNoFree, IntrNoCallback, + NoCapture>, NoCapture>, + WriteOnly>, ReadOnly>, + ImmArg>]>; + +def int_evm_memcpyas1as2 + : Intrinsic<[], + [LLVMQualPointerType, + LLVMQualPointerType, llvm_i256_ty, llvm_i1_ty], + [IntrArgMemOnly, IntrWillReturn, IntrNoFree, IntrNoCallback, + NoCapture>, NoCapture>, + NoAlias>, NoAlias>, + WriteOnly>, ReadOnly>, + ImmArg>]>; + +def int_evm_memcpyas1as3 + : Intrinsic<[], + [LLVMQualPointerType, + LLVMQualPointerType, llvm_i256_ty, + llvm_i1_ty], + [IntrArgMemOnly, IntrWillReturn, IntrNoFree, IntrNoCallback, + NoCapture>, NoCapture>, + NoAlias>, NoAlias>, + WriteOnly>, ReadOnly>, + ImmArg>]>; + +def int_evm_memcpyas1as4 + : Intrinsic<[], + [LLVMQualPointerType, LLVMQualPointerType, + llvm_i256_ty, llvm_i1_ty], + [IntrArgMemOnly, IntrWillReturn, IntrNoFree, IntrNoCallback, + NoCapture>, NoCapture>, + NoAlias>, NoAlias>, + WriteOnly>, ReadOnly>, + ImmArg>]>; + +// The following intrinsics are used for linking purposes. + +// Returns a code size of the Yul object with the name +// passed in the metadata. +def int_evm_datasize + : DefaultAttrsIntrinsic<[llvm_i256_ty], [llvm_metadata_ty], + [IntrNoMem, IntrWillReturn]>; + +// Returns a code offset of the Yul object with the name +// passed in the metadata. +def int_evm_dataoffset + : DefaultAttrsIntrinsic<[llvm_i256_ty], [llvm_metadata_ty], + [IntrNoMem, IntrWillReturn]>; + +// Linking of libraries. + +// Inserts a library address placeholder to be replaced with +// a library address at linkage time. +def int_evm_linkersymbol + : DefaultAttrsIntrinsic<[llvm_i256_ty], [llvm_metadata_ty], + [IntrNoMem, IntrWillReturn]>; + +// The intrinsic call should not be optimized out, even though +// its result is not used. For this, IntrInaccessibleMemOnly is +// used. +def int_evm_pushdeployaddress + : DefaultAttrsIntrinsic<[llvm_i256_ty], [], + [IntrInaccessibleMemOnly, IntrWillReturn, + IntrNoDuplicate]>; + +// Immutables support. + +// Loads immutable value. Should be used only in runtime code. +def int_evm_loadimmutable + : DefaultAttrsIntrinsic<[llvm_i256_ty], [llvm_metadata_ty], + [IntrNoMem, IntrWillReturn]>; +} // TargetPrefix = "evm" diff --git a/llvm/include/llvm/IR/PassManagerInternal.h b/llvm/include/llvm/IR/PassManagerInternal.h index 4ada6ee5dd68..62bede206da5 100644 --- a/llvm/include/llvm/IR/PassManagerInternal.h +++ b/llvm/include/llvm/IR/PassManagerInternal.h @@ -22,6 +22,7 @@ #include "llvm/IR/Analysis.h" #include "llvm/Support/raw_ostream.h" #include +#include #include namespace llvm { @@ -167,7 +168,7 @@ template class ResultHasInvalidateMethod { // ambiguous if there were an invalidate member in the result type. template static DisabledType NonceFunction(T U::*); struct CheckerBase { int invalidate; }; - template struct Checker : CheckerBase, T {}; + template struct Checker : CheckerBase, std::remove_cv_t {}; template static decltype(NonceFunction(&Checker::invalidate)) check(rank<1>); diff --git a/llvm/include/llvm/IR/Type.h b/llvm/include/llvm/IR/Type.h index 1f0133c08e7d..636ac022b4c7 100644 --- a/llvm/include/llvm/IR/Type.h +++ b/llvm/include/llvm/IR/Type.h @@ -463,6 +463,10 @@ class Type { static IntegerType *getInt32Ty(LLVMContext &C); static IntegerType *getInt64Ty(LLVMContext &C); static IntegerType *getInt128Ty(LLVMContext &C); + // EVM local begin + static IntegerType *getInt256Ty(LLVMContext &C); + static IntegerType *getInt512Ty(LLVMContext &C); + // EVM local end template static Type *getScalarTy(LLVMContext &C) { int noOfBits = sizeof(ScalarTy) * CHAR_BIT; if (std::is_integral::value) { @@ -484,6 +488,9 @@ class Type { // static Type *getWasm_ExternrefTy(LLVMContext &C); static Type *getWasm_FuncrefTy(LLVMContext &C); + // EVM local begin + static PointerType *getInt256PtrTy(LLVMContext &C, unsigned AS = 0); + // EVM local end /// Return a pointer to the current type. This is equivalent to /// PointerType::get(Foo, AddrSpace). diff --git a/llvm/include/llvm/MC/MCAsmInfo.h b/llvm/include/llvm/MC/MCAsmInfo.h index add60ef7006b..0334fa74a762 100644 --- a/llvm/include/llvm/MC/MCAsmInfo.h +++ b/llvm/include/llvm/MC/MCAsmInfo.h @@ -495,6 +495,11 @@ class MCAsmInfo { /// '$' character to distinguish them from absolute names. bool UseParensForDollarSignNames = true; + // EVM local begin + /// True if target uses @symbol syntax. + bool PrependSymbolRefWithAt = false; + // EVM local end + /// True if the target supports flags in ".loc" directive, false if only /// location is allowed. bool SupportsExtendedDwarfLocDirective = true; @@ -806,6 +811,9 @@ class MCAsmInfo { bool useParensForDollarSignNames() const { return UseParensForDollarSignNames; } + // EVM local begin + bool prependSymbolRefWithAt() const { return PrependSymbolRefWithAt; } + // EVM local end bool supportsExtendedDwarfLocDirective() const { return SupportsExtendedDwarfLocDirective; } diff --git a/llvm/include/llvm/MC/MCDecoderOps.h b/llvm/include/llvm/MC/MCDecoderOps.h index 3c0b68101e34..2b2467e4148c 100644 --- a/llvm/include/llvm/MC/MCDecoderOps.h +++ b/llvm/include/llvm/MC/MCDecoderOps.h @@ -15,9 +15,13 @@ namespace llvm { namespace MCD { // Disassembler state machine opcodes. enum DecoderOps { - OPC_ExtractField = 1, // OPC_ExtractField(uleb128 Start, uint8_t Len) + // EVM local begin + OPC_ExtractField = 1, // OPC_ExtractField(uleb128 Start, uint16_t Len) + // EVM local end OPC_FilterValue, // OPC_FilterValue(uleb128 Val, uint16_t NumToSkip) - OPC_CheckField, // OPC_CheckField(uleb128 Start, uint8_t Len, + // EVM local begin + OPC_CheckField, // OPC_CheckField(uleb128 Start, uint16_t Len, + // EVM local end // uleb128 Val, uint16_t NumToSkip) OPC_CheckPredicate, // OPC_CheckPredicate(uleb128 PIdx, uint16_t NumToSkip) OPC_Decode, // OPC_Decode(uleb128 Opcode, uleb128 DIdx) diff --git a/llvm/include/llvm/MC/MCExpr.h b/llvm/include/llvm/MC/MCExpr.h index 118b1dd88525..aa275fc61d96 100644 --- a/llvm/include/llvm/MC/MCExpr.h +++ b/llvm/include/llvm/MC/MCExpr.h @@ -362,7 +362,10 @@ class MCSymbolRefExpr : public MCExpr { VK_VE_TPOFF_LO32, // symbol@tpoff_lo VK_TPREL, - VK_DTPREL + VK_DTPREL, + // EVM local begin + VK_EVM_DATA + // EVM local end }; private: diff --git a/llvm/include/llvm/Object/ELFObjectFile.h b/llvm/include/llvm/Object/ELFObjectFile.h index 811943dcd708..f76fc20e6570 100644 --- a/llvm/include/llvm/Object/ELFObjectFile.h +++ b/llvm/include/llvm/Object/ELFObjectFile.h @@ -1452,6 +1452,11 @@ template Triple::ArchType ELFObjectFile::getArch() const { case ELF::EM_XTENSA: return Triple::xtensa; + // EVM local begin + case ELF::EM_EVM: + return Triple::evm; + // EVM local end + default: return Triple::UnknownArch; } diff --git a/llvm/include/llvm/Passes/PassBuilder.h b/llvm/include/llvm/Passes/PassBuilder.h index 474a19531ff5..1841939d5b44 100644 --- a/llvm/include/llvm/Passes/PassBuilder.h +++ b/llvm/include/llvm/Passes/PassBuilder.h @@ -483,6 +483,44 @@ class PassBuilder { PipelineEarlySimplificationEPCallbacks.push_back(C); } + // EVM local begin + /// Register a callback for a default optimizer pipeline extension point + /// + /// This extension point allows adding optimizations before the inliner + /// and it differs from PipelineEarlySimplificationEPCallbacks, because some + /// passes are called in between, that in some cases can create opportunities + /// for CSE-like and memory based optimizations, since the input code is + /// better optimized. + /// EVM adds passes to remove repetitive computation of read only library + /// function calls (__sha3, __system_request, etc.). These passes must work + /// before the inliner, but to simplify inner pattern matching logic and make + /// it work more frequently, we found it better to put it right before the + /// inliner. Since InstCombinePass is called before this extension point, + /// it can simplify code and enable easier elimination of repetitive function + /// calls. + /// + /// Example that shows how InstCombinePass enables elimination of repetitive + /// function calls based on the input data that is passed: + /// Before InstCombinePass: + /// %_118 = load i256, ptr %_1, align 32 + /// store i256 %_118, ptr addrspace(1) %ptr1, align 1 + /// %_119 = load i256, ptr %_1, align 32 + /// store i256 %_119, ptr addrspace(1) %ptr2, align 1 + /// After InstCombinePass: + /// %_118 = load i256, ptr %_1, align 32 + /// store i256 %_118, ptr addrspace(1) %ptr1, align 4294967296 + /// store i256 %_118, ptr addrspace(1) %ptr2, align 32 + /// + /// In case there are two consecutive calls to the same library function, + /// but only different pointers are passed (in this case %ptr1 and %ptr2), + /// we can inspect these pointers and see that the same input data is written, + /// so in that case, it's possible to safely eliminate the second call. + void registerPreInlinerOptimizationsEPCallback( + const std::function &C) { + PreInlinerOptimizationsEPCallbacks.push_back(C); + } + // EVM local end + /// Register a callback for a default optimizer pipeline extension point /// /// This extension point allows adding optimizations before the function @@ -753,6 +791,10 @@ class PassBuilder { SmallVector, 2> VectorizerStartEPCallbacks; // Module callbacks + // EVM local begin + SmallVector, 2> + PreInlinerOptimizationsEPCallbacks; + // EVM local end SmallVector, 2> OptimizerEarlyEPCallbacks; SmallVector, 2> diff --git a/llvm/include/llvm/Support/Endian.h b/llvm/include/llvm/Support/Endian.h index 5831fe66a1f7..f86ea8901ae4 100644 --- a/llvm/include/llvm/Support/Endian.h +++ b/llvm/include/llvm/Support/Endian.h @@ -58,7 +58,7 @@ template [[nodiscard]] inline value_type read(const void *memory, endianness endian) { value_type ret; - memcpy(&ret, + memcpy(static_cast(&ret), LLVM_ASSUME_ALIGNED( memory, (detail::PickAlignment::value)), sizeof(value_type)); diff --git a/llvm/include/llvm/Support/ErrorHandling.h b/llvm/include/llvm/Support/ErrorHandling.h index 9c8e3448f3a0..764e3e43789c 100644 --- a/llvm/include/llvm/Support/ErrorHandling.h +++ b/llvm/include/llvm/Support/ErrorHandling.h @@ -15,6 +15,9 @@ #define LLVM_SUPPORT_ERRORHANDLING_H #include "llvm/Support/Compiler.h" +// EVM local begin +#include +// EVM local end namespace llvm { class StringRef; @@ -25,6 +28,11 @@ namespace llvm { const char *reason, bool gen_crash_diag); + // EVM local begin + typedef void (*evm_stack_error_handler_t)(void *user_data, + uint64_t spill_region_size); + // EVM local end + /// install_fatal_error_handler - Installs a new error handler to be used /// whenever a serious (non-recoverable) error is encountered by LLVM. /// @@ -75,6 +83,11 @@ namespace llvm { [[noreturn]] void report_fatal_error(const Twine &reason, bool gen_crash_diag = true); +// EVM local begin +/// Report a stackification error and request a spill region. +void report_evm_stack_error(const Twine &reason, uint64_t spill_region_size); +// EVM local end + /// Installs a new bad alloc error handler that should be used whenever a /// bad alloc error, e.g. failing malloc/calloc, is encountered by LLVM. /// diff --git a/llvm/include/llvm/Support/FileOutputBuffer.h b/llvm/include/llvm/Support/FileOutputBuffer.h index d4b73522115d..02d2919ccde2 100644 --- a/llvm/include/llvm/Support/FileOutputBuffer.h +++ b/llvm/include/llvm/Support/FileOutputBuffer.h @@ -48,6 +48,17 @@ class FileOutputBuffer { static Expected> create(StringRef FilePath, size_t Size, unsigned Flags = 0); + // EVM local begin + /// Factory method to create an OutputBuffer object which manages a read/write + /// buffer of the specified size. When committed, the buffer will be written + /// to the stream p\ Out. + /// Please note, even though the returned buffer type is derived from + /// FileOutputBuffer, the functionality related to file management + /// is not used and completely ignored. + static Expected> + create(size_t Size, raw_pwrite_stream &Out); + // EVM local end + /// Returns a pointer to the start of the buffer. virtual uint8_t *getBufferStart() const = 0; diff --git a/llvm/include/llvm/Support/KECCAK.h b/llvm/include/llvm/Support/KECCAK.h new file mode 100644 index 000000000000..42230cf31f35 --- /dev/null +++ b/llvm/include/llvm/Support/KECCAK.h @@ -0,0 +1,42 @@ +//===-- KECCAK.h - KECCAK 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 +// +//===----------------------------------------------------------------------===// +// +// Implementation the Keccak-f[1600] permutation approved in the FIPS 202 +// standard, which is used for instantiation of the KECCAK-256 hash function. +// +// [FIPS PUB 202] +// https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf +// [Keccak Reference] +// https://keccak.team/files/Keccak-reference-3.0.pdf +// [Keccak Specifications Summary] +// https://keccak.team/keccak_specs_summary.html +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_KECCAK_H +#define LLVM_SUPPORT_KECCAK_H + +#include +#include + +namespace llvm { + +template class ArrayRef; +class StringRef; + +namespace KECCAK { +/// Returns a raw 256-bit KECCAK-256 hash for the given data. +std::array KECCAK_256(ArrayRef Data); + +/// Returns a raw 256-bit KECCAK-256 hash for the given string. +std::array KECCAK_256(StringRef Str); +} // namespace KECCAK + +} // namespace llvm + +#endif // LLVM_SUPPORT_KECCAK_H diff --git a/llvm/include/llvm/Target/TargetMachine.h b/llvm/include/llvm/Target/TargetMachine.h index b8e56c755fbd..963bbba44bb6 100644 --- a/llvm/include/llvm/Target/TargetMachine.h +++ b/llvm/include/llvm/Target/TargetMachine.h @@ -371,6 +371,11 @@ class TargetMachine { // TODO: Populate all pass names by using PassRegistry.def. virtual void registerPassBuilderCallbacks(PassBuilder &) {} + /// Allow the target to register early alias analyses (AA before BasicAA) with + /// the AAManager for use with the new pass manager. Only affects the + /// "default" AAManager. + virtual void registerEarlyDefaultAliasAnalyses(AAManager &) {} + /// Allow the target to register alias analyses with the AAManager for use /// with the new pass manager. Only affects the "default" AAManager. virtual void registerDefaultAliasAnalyses(AAManager &) {} diff --git a/llvm/include/llvm/TargetParser/Triple.h b/llvm/include/llvm/TargetParser/Triple.h index d2126a03db90..f843fc2f016b 100644 --- a/llvm/include/llvm/TargetParser/Triple.h +++ b/llvm/include/llvm/TargetParser/Triple.h @@ -106,6 +106,9 @@ class Triple { wasm64, // WebAssembly with 64-bit pointers renderscript32, // 32-bit RenderScript renderscript64, // 64-bit RenderScript + // EVM local begin + evm, // EVM: evm + // EVM local end ve, // NEC SX-Aurora Vector Engine LastArchType = ve }; @@ -1069,6 +1072,12 @@ class Triple { Env == llvm::Triple::EABIHF; } + // EVM local begin + bool isEVM() const { + return getArch() == Triple::evm; + } + // EVM local end + /// Tests whether the target supports comdat bool supportsCOMDAT() const { return !(isOSBinFormatMachO() || isOSBinFormatXCOFF() || diff --git a/llvm/include/llvm/Transforms/Scalar/MergeIdenticalBB.h b/llvm/include/llvm/Transforms/Scalar/MergeIdenticalBB.h new file mode 100644 index 000000000000..dabdaf1f1558 --- /dev/null +++ b/llvm/include/llvm/Transforms/Scalar/MergeIdenticalBB.h @@ -0,0 +1,28 @@ +//===----- MergeSimilarBB.h - Merge identical basic blocks ------*- C++ -*-===// +/// \file +/// This file provides the interface for the pass responsible for merging +/// indentical basic blocks. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_SCALAR_MERGEIDENTICALBB_H +#define LLVM_TRANSFORMS_SCALAR_MERGEIDENTICALBB_H + +#include "llvm/IR/Function.h" +#include "llvm/IR/PassManager.h" + +namespace llvm { + +/// A pass to merge similar basic blocks. +/// +/// The pass identifies isomorphic basic blocks by hashing its content, then it +/// attempt to merge BBs with the same hash. +class MergeIdenticalBBPass : public PassInfoMixin { +public: + /// Run the pass over the function. + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); +}; + +} // namespace llvm + +#endif diff --git a/llvm/include/llvm/Transforms/Scalar/SHA3ConstFolding.h b/llvm/include/llvm/Transforms/Scalar/SHA3ConstFolding.h new file mode 100644 index 000000000000..be77e6b2251b --- /dev/null +++ b/llvm/include/llvm/Transforms/Scalar/SHA3ConstFolding.h @@ -0,0 +1,36 @@ +//===-- SHA3ConstFolding.h - Const fold calls to sha3 -----------*- 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 the interface for the SHA3 const folding pass. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_SCALAR_SHA3CONSTFOLDING_H +#define LLVM_TRANSFORMS_SCALAR_SHA3CONSTFOLDING_H + +#include "llvm/Analysis/AliasAnalysis.h" + +namespace llvm { + +class Function; +class AssumptionCache; +class MemorySSA; +class DominatorTree; +class TargetLibraryInfo; +class LoopInfo; +class Instruction; + +bool runSHA3ConstFolding( + Function &F, AliasAnalysis &AA, AssumptionCache &AC, MemorySSA &MSSA, + DominatorTree &DT, const TargetLibraryInfo &TLI, const LoopInfo &LI, + const std::function &IsSha3Call, + unsigned HeapAS); + +} // end namespace llvm + +#endif // LLVM_TRANSFORMS_SCALAR_SHA3CONSTFOLDING_H diff --git a/llvm/include/module.modulemap b/llvm/include/module.modulemap index b00da6d7cd28..5ff2382f5a38 100644 --- a/llvm/include/module.modulemap +++ b/llvm/include/module.modulemap @@ -103,6 +103,7 @@ module LLVM_BinaryFormat { textual header "llvm/BinaryFormat/ELFRelocs/Xtensa.def" textual header "llvm/BinaryFormat/WasmRelocs.def" textual header "llvm/BinaryFormat/MsgPack.def" + textual header "llvm/BinaryFormat/EVM.def" } module LLVM_Config { diff --git a/llvm/lib/Analysis/AliasAnalysis.cpp b/llvm/lib/Analysis/AliasAnalysis.cpp index 9cdb315b6088..edc5c33d4764 100644 --- a/llvm/lib/Analysis/AliasAnalysis.cpp +++ b/llvm/lib/Analysis/AliasAnalysis.cpp @@ -788,28 +788,49 @@ bool AAResultsWrapperPass::runOnFunction(Function &F) { AAR.reset( new AAResults(getAnalysis().getTLI(F))); + // Add any target-specific alias analyses that should be run early. + auto *ExtWrapperPass = getAnalysisIfAvailable(); + if (ExtWrapperPass && ExtWrapperPass->runEarly() && ExtWrapperPass->CB) { + LLVM_DEBUG(dbgs() << "AAResults register Early ExternalAA: " + << ExtWrapperPass->getPassName() << "\n"); + ExtWrapperPass->CB(*this, F, *AAR); + } + // BasicAA is always available for function analyses. Also, we add it first // so that it can trump TBAA results when it proves MustAlias. // FIXME: TBAA should have an explicit mode to support this and then we // should reconsider the ordering here. - if (!DisableBasicAA) + if (!DisableBasicAA) { + LLVM_DEBUG(dbgs() << "AAResults register BasicAA\n"); AAR->addAAResult(getAnalysis().getResult()); + } // Populate the results with the currently available AAs. - if (auto *WrapperPass = getAnalysisIfAvailable()) + if (auto *WrapperPass = + getAnalysisIfAvailable()) { + LLVM_DEBUG(dbgs() << "AAResults register ScopedNoAliasAA\n"); AAR->addAAResult(WrapperPass->getResult()); - if (auto *WrapperPass = getAnalysisIfAvailable()) + } + if (auto *WrapperPass = getAnalysisIfAvailable()) { + LLVM_DEBUG(dbgs() << "AAResults register TypeBasedAA\n"); AAR->addAAResult(WrapperPass->getResult()); - if (auto *WrapperPass = getAnalysisIfAvailable()) + } + if (auto *WrapperPass = getAnalysisIfAvailable()) { + LLVM_DEBUG(dbgs() << "AAResults register GlobalsAA\n"); AAR->addAAResult(WrapperPass->getResult()); - if (auto *WrapperPass = getAnalysisIfAvailable()) + } + if (auto *WrapperPass = getAnalysisIfAvailable()) { + LLVM_DEBUG(dbgs() << "AAResults register SCEVAA\n"); AAR->addAAResult(WrapperPass->getResult()); + } // If available, run an external AA providing callback over the results as // well. - if (auto *WrapperPass = getAnalysisIfAvailable()) - if (WrapperPass->CB) - WrapperPass->CB(*this, F, *AAR); + if (ExtWrapperPass && !ExtWrapperPass->runEarly() && ExtWrapperPass->CB) { + LLVM_DEBUG(dbgs() << "AAResults register Late ExternalAA: " + << ExtWrapperPass->getPassName() << "\n"); + ExtWrapperPass->CB(*this, F, *AAR); + } // Analyses don't mutate the IR, so return false. return false; diff --git a/llvm/lib/Analysis/CMakeLists.txt b/llvm/lib/Analysis/CMakeLists.txt index 74476cb5440c..bccdaef24be5 100644 --- a/llvm/lib/Analysis/CMakeLists.txt +++ b/llvm/lib/Analysis/CMakeLists.txt @@ -139,6 +139,9 @@ add_llvm_component_library(LLVMAnalysis ValueLatticeUtils.cpp ValueTracking.cpp VectorUtils.cpp + # EVM local begin + VMAliasAnalysis.cpp + # EVM local end ${GeneratedMLSources} ADDITIONAL_HEADER_DIRS diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp index ff30fece5fce..a03f82d682be 100644 --- a/llvm/lib/Analysis/ConstantFolding.cpp +++ b/llvm/lib/Analysis/ConstantFolding.cpp @@ -45,6 +45,9 @@ #include "llvm/IR/IntrinsicsAArch64.h" #include "llvm/IR/IntrinsicsAMDGPU.h" #include "llvm/IR/IntrinsicsARM.h" +// EVM local begin +#include "llvm/IR/IntrinsicsEVM.h" +// EVM local end #include "llvm/IR/IntrinsicsWebAssembly.h" #include "llvm/IR/IntrinsicsX86.h" #include "llvm/IR/Operator.h" @@ -886,10 +889,15 @@ Constant *SymbolicallyEvaluateGEP(const GEPOperator *GEP, return nullptr; unsigned BitWidth = DL.getTypeSizeInBits(IntIdxTy); - APInt Offset = APInt( - BitWidth, - DL.getIndexedOffsetInType( - SrcElemTy, ArrayRef((Value *const *)Ops.data() + 1, Ops.size() - 1))); + // EVM local begin + APInt Offset = + APInt(BitWidth, + DL.getIndexedOffsetInType( + SrcElemTy, + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) + ArrayRef((Value *const *)Ops.data() + 1, Ops.size() - 1)), + /*isSigned=*/true, /*implicitTrunc=*/true); + // EVM local end std::optional InRange = GEP->getInRange(); if (InRange) @@ -924,7 +932,7 @@ Constant *SymbolicallyEvaluateGEP(const GEPOperator *GEP, Ptr = cast(GEP->getOperand(0)); SrcElemTy = GEP->getSourceElementType(); Offset = Offset.sadd_ov( - APInt(BitWidth, DL.getIndexedOffsetInType(SrcElemTy, NestedOps)), + APInt(BitWidth, DL.getIndexedOffsetInType(SrcElemTy, NestedOps), true), // EVM local Overflow); } @@ -1546,6 +1554,13 @@ bool llvm::canConstantFoldCallTo(const CallBase *Call, const Function *F) { case Intrinsic::arm_mve_vctp32: case Intrinsic::arm_mve_vctp64: case Intrinsic::aarch64_sve_convert_from_svbool: + // EVM local begin + case Intrinsic::evm_addmod: + case Intrinsic::evm_mulmod: + case Intrinsic::evm_byte: + case Intrinsic::evm_exp: + case Intrinsic::evm_signextend: + // EVM local end // WebAssembly float semantics are always known case Intrinsic::wasm_trunc_signed: case Intrinsic::wasm_trunc_unsigned: @@ -2572,6 +2587,91 @@ static Constant *evaluateCompare(const APFloat &Op1, const APFloat &Op2, return nullptr; } +// EVM local begin +/// Extending length of two’s complement signed integer. +/// NumByte: the size in bytes minus one of the integer to sign extend. +/// Val: the integer being extended. +static Constant *ConstantFoldSignextendCall(Type *Ty, const APInt &NumByte, + const APInt &Val) { + unsigned BitWidth = Ty->getIntegerBitWidth(); + + // Signextend operation returns original value if the extension + // overflows the type width. + if (NumByte.uge(APInt(BitWidth, (BitWidth / 8) - 1))) + return ConstantInt::get(Ty, Val); + + const APInt NumBits = NumByte * 8 + 7; + const APInt NumBitInv = APInt(BitWidth, BitWidth) - NumBits; + const APInt SignMask = APInt(BitWidth, 1).shl(NumBits); + const APInt ValMask = APInt::getAllOnes(BitWidth).lshr(NumBitInv); + const APInt Ext1 = APInt::getAllOnes(BitWidth).shl(NumBits); + const APInt SignVal = SignMask & Val; + const APInt ValClean = Val & ValMask; + const APInt Sext = SignVal.isZero() ? APInt::getZero(BitWidth) : Ext1; + const APInt Result = Sext | ValClean; + return ConstantInt::get(Ty, Result); +} + +/// Exponential operation. +/// Calculates: +/// (base ** exp) (mod 2**256) +/// This is an implementation of the binary exponentiation algorithm. +static Constant *ConstantFoldExpCall(Type *Ty, const APInt &Base, + const APInt &Exp) { + const unsigned BitWidth = Ty->getIntegerBitWidth(); + if (Base == 0 && Exp != 0) + return Constant::getNullValue(Ty); + if (Base == 1 || (Base == 0 && Exp == 0)) + return ConstantInt::get(Ty, APInt(BitWidth, 1)); + if (Base == 2 && Exp.ule(255)) + return ConstantInt::get(Ty, APInt(BitWidth, 1).shl(Exp)); + + // The target algorithm is an implementation of the following C code, + // where m = 2 ** 256 and 'long long' type is replaced with i256 one. + // long long binpow(long long a, long long b, long long m) { + // a %= m; + // long long res = 1; + // while (b > 0) { + // if (b & 1) + // res = res * a % m; + // a = a * a % m; + // b >>= 1; + // } + // return res; + // } + + // Perform calculations in double bit-width, as we need to + // multiply. + const unsigned ExtBitWidth = BitWidth * 2; + const APInt ModExt = APInt(ExtBitWidth, 1).shl(BitWidth); + APInt ResExt(ExtBitWidth, 1); + APInt BaseExt = Base.zext(ExtBitWidth); + APInt CurrExp = Exp; + while (CurrExp.ugt(0)) { + if (CurrExp[0]) + ResExt = (ResExt * BaseExt).urem(ModExt); + + BaseExt = (BaseExt * BaseExt).urem(ModExt); + CurrExp.lshrInPlace(1); + } + return ConstantInt::get(Ty, ResExt.trunc(BitWidth)); +} + +/// Retrieve single byte from i256 word. +/// Returns: +/// (Val << ByteIdx * 8) >> 248. +/// For the Nth byte, we count from the left +/// (i.e. N=0 would be the most significant in big endian), +/// 0, if ByteIdx > 255. +static Constant *ConstantFoldByteCall(Type *Ty, const APInt &ByteIdx, + const APInt &Val) { + // Please, note the case ByteIdx > 31 is properly handled by the shl + // implementation, see the comments for ConstantFoldSHRCall. + unsigned BitWidth = Ty->getIntegerBitWidth(); + return ConstantInt::get(Ty, Val.shl(ByteIdx * 8).lshr(BitWidth - 8)); +} +// EVM local end + static Constant *ConstantFoldLibCall2(StringRef Name, Type *Ty, ArrayRef Operands, const TargetLibraryInfo *TLI) { @@ -2641,6 +2741,33 @@ static Constant *ConstantFoldIntrinsicCall2(Intrinsic::ID IntrinsicID, Type *Ty, const CallBase *Call) { assert(Operands.size() == 2 && "Wrong number of operands."); + // EVM local begin + if (Operands[0]->getType()->isIntegerTy() && + Operands[1]->getType()->isIntegerTy()) { + const APInt *C0 = nullptr, *C1 = nullptr; + if (!getConstIntOrUndef(Operands[0], C0) || + !getConstIntOrUndef(Operands[1], C1)) + return nullptr; + + if (IntrinsicID == Intrinsic::evm_signextend || + IntrinsicID == Intrinsic::evm_exp || + IntrinsicID == Intrinsic::evm_byte) { + if (isa(Operands[0]) || isa(Operands[1]) || + !C0 || !C1) + return PoisonValue::get(Ty); + + assert(Ty->getIntegerBitWidth() == 256); + + if (IntrinsicID == Intrinsic::evm_signextend) + return ConstantFoldSignextendCall(Ty, *C0, *C1); + if (IntrinsicID == Intrinsic::evm_exp) + return ConstantFoldExpCall(Ty, *C0, *C1); + if (IntrinsicID == Intrinsic::evm_byte) + return ConstantFoldByteCall(Ty, *C0, *C1); + } + } + // EVM local end + if (Ty->isFloatingPointTy()) { // TODO: We should have undef handling for all of the FP intrinsics that // are attempted to be folded in this function. @@ -3092,6 +3219,46 @@ static Constant *ConstantFoldAMDGCNPermIntrinsic(ArrayRef Operands, return ConstantInt::get(Ty, Val); } +// EVM local begin +/// Unsigned modulo addition operation. +/// Returns: +/// 0, if Mod == 0 +/// (Arg1 + Arg2) (mod Mod), if Mod != 0 +/// All intermediate calculations of this operation are not subject +/// to the 2 ** 256 modulo. +static Constant *ConstantFoldAddModCall(Type *Ty, const APInt &Arg1, + const APInt &Arg2, const APInt &Mod) { + const unsigned BitWidth = Ty->getIntegerBitWidth(); + if (Mod.isZero()) + return Constant::getNullValue(Ty); + + // Sum representation of two N-bit values takes up to N + 1 bits. + const unsigned ExtBitWidth = BitWidth + 1; + const APInt Sum = Arg1.zext(ExtBitWidth) + Arg2.zext(ExtBitWidth); + const APInt Rem = Sum.urem(Mod.zext(ExtBitWidth)); + return ConstantInt::get(Ty, Rem.trunc(BitWidth)); +} + +/// Unsigned modulo multiplication operation. +/// Returns: +/// 0, if Mod == 0 +/// (Arg1 * Arg2) (mod Mod), if Mod != 0 +/// All intermediate calculations of this operation are not subject +/// to the 2**256 modulo. +static Constant *ConstantFoldMulModCall(Type *Ty, const APInt &Arg1, + const APInt &Arg2, const APInt &Mod) { + const unsigned BitWidth = Ty->getIntegerBitWidth(); + if (Mod.isZero()) + return Constant::getNullValue(Ty); + + // Multiplication representation of two N-bit values takes up to 2 * N bits. + const unsigned ExtBitWidth = BitWidth * 2; + const APInt Product = Arg1.zext(ExtBitWidth) * Arg2.zext(ExtBitWidth); + const APInt Rem = Product.urem(Mod.zext(ExtBitWidth)); + return ConstantInt::get(Ty, Rem.trunc(BitWidth)); +} +// EVM local end + static Constant *ConstantFoldScalarCall3(StringRef Name, Intrinsic::ID IntrinsicID, Type *Ty, @@ -3226,6 +3393,28 @@ static Constant *ConstantFoldScalarCall3(StringRef Name, if (IntrinsicID == Intrinsic::amdgcn_perm) return ConstantFoldAMDGCNPermIntrinsic(Operands, Ty); + // EVM local begin + if (IntrinsicID == Intrinsic::evm_addmod || + IntrinsicID == Intrinsic::evm_mulmod) { + const APInt *C0, *C1, *C2; + if (!getConstIntOrUndef(Operands[0], C0) || + !getConstIntOrUndef(Operands[1], C1) || + !getConstIntOrUndef(Operands[2], C2)) + return nullptr; + + if (isa(Operands[0]) || isa(Operands[1]) || + isa(Operands[2]) || !C0 || !C1 || !C2) + return PoisonValue::get(Ty); + + assert(Ty->getIntegerBitWidth() == 256); + + if (IntrinsicID == Intrinsic::evm_addmod) + return ConstantFoldAddModCall(Ty, *C0, *C1, *C2); + if (IntrinsicID == Intrinsic::evm_mulmod) + return ConstantFoldMulModCall(Ty, *C0, *C1, *C2); + } + // EVM local end + return nullptr; } @@ -3402,8 +3591,9 @@ ConstantFoldScalarFrexpCall(Constant *Op, Type *IntTy) { // The exponent is an "unspecified value" for inf/nan. We use zero to avoid // using undef. - Constant *Result1 = FrexpMant.isFinite() ? ConstantInt::get(IntTy, FrexpExp) - : ConstantInt::getNullValue(IntTy); + Constant *Result1 = FrexpMant.isFinite() + ? ConstantInt::getSigned(IntTy, FrexpExp) + : ConstantInt::getNullValue(IntTy); return {Result0, Result1}; } diff --git a/llvm/lib/Analysis/InlineCost.cpp b/llvm/lib/Analysis/InlineCost.cpp index 345e5a019520..cb9e700b6a85 100644 --- a/llvm/lib/Analysis/InlineCost.cpp +++ b/llvm/lib/Analysis/InlineCost.cpp @@ -1911,8 +1911,15 @@ InlineCostCallAnalyzer::getHotCallSiteThreshold(CallBase &Call, void InlineCostCallAnalyzer::updateThreshold(CallBase &Call, Function &Callee) { // If no size growth is allowed for this inlining, set Threshold to 0. if (!allowSizeGrowth(Call)) { - Threshold = 0; - return; + // EVM local change begin + if (Triple(Call.getFunction()->getParent()->getTargetTriple()).isEVM()) { + constexpr int UnreachableThreshold = 10; + Threshold = std::min(Threshold, UnreachableThreshold); + } else { + Threshold = 0; + return; + } + // EVM local change end } Function *Caller = Call.getCaller(); diff --git a/llvm/lib/Analysis/Loads.cpp b/llvm/lib/Analysis/Loads.cpp index 61c6aa5e5a3e..8644c2148ebb 100644 --- a/llvm/lib/Analysis/Loads.cpp +++ b/llvm/lib/Analysis/Loads.cpp @@ -94,10 +94,8 @@ static bool isDereferenceableAndAlignedPointer( } bool CheckForNonNull, CheckForFreed; - APInt KnownDerefBytes(Size.getBitWidth(), - V->getPointerDereferenceableBytes(DL, CheckForNonNull, - CheckForFreed)); - if (KnownDerefBytes.getBoolValue() && KnownDerefBytes.uge(Size) && + if (Size.ule(V->getPointerDereferenceableBytes(DL, CheckForNonNull, + CheckForFreed)) && !CheckForFreed) if (!CheckForNonNull || isKnownNonZero(V, SimplifyQuery(DL, DT, AC, CtxI))) { diff --git a/llvm/lib/Analysis/LoopUnrollAnalyzer.cpp b/llvm/lib/Analysis/LoopUnrollAnalyzer.cpp index 7b6ca4d711fc..ff3bb0596c4a 100644 --- a/llvm/lib/Analysis/LoopUnrollAnalyzer.cpp +++ b/llvm/lib/Analysis/LoopUnrollAnalyzer.cpp @@ -64,7 +64,7 @@ bool UnrolledInstAnalyzer::simplifyInstWithSCEV(Instruction *I) { return false; SimplifiedAddress Address; Address.Base = Base->getValue(); - Address.Offset = Offset->getValue(); + Address.Offset = Offset->getAPInt(); SimplifiedAddresses[I] = Address; return false; } @@ -105,7 +105,7 @@ bool UnrolledInstAnalyzer::visitLoad(LoadInst &I) { auto AddressIt = SimplifiedAddresses.find(AddrOp); if (AddressIt == SimplifiedAddresses.end()) return false; - ConstantInt *SimplifiedAddrOp = AddressIt->second.Offset; + const APInt &SimplifiedAddrOp = AddressIt->second.Offset; auto *GV = dyn_cast(AddressIt->second.Base); // We're only interested in loads that can be completely folded to a @@ -125,9 +125,9 @@ bool UnrolledInstAnalyzer::visitLoad(LoadInst &I) { return false; unsigned ElemSize = CDS->getElementType()->getPrimitiveSizeInBits() / 8U; - if (SimplifiedAddrOp->getValue().getActiveBits() > 64) + if (SimplifiedAddrOp.getActiveBits() > 64) return false; - int64_t SimplifiedAddrOpV = SimplifiedAddrOp->getSExtValue(); + int64_t SimplifiedAddrOpV = SimplifiedAddrOp.getSExtValue(); if (SimplifiedAddrOpV < 0) { // FIXME: For now we conservatively ignore out of bound accesses, but // we're allowed to perform the optimization in this case. @@ -179,7 +179,7 @@ bool UnrolledInstAnalyzer::visitCmpInst(CmpInst &I) { if (Value *SimpleRHS = SimplifiedValues.lookup(RHS)) RHS = SimpleRHS; - if (!isa(LHS) && !isa(RHS)) { + if (!isa(LHS) && !isa(RHS) && !I.isSigned()) { auto SimplifiedLHS = SimplifiedAddresses.find(LHS); if (SimplifiedLHS != SimplifiedAddresses.end()) { auto SimplifiedRHS = SimplifiedAddresses.find(RHS); @@ -187,8 +187,15 @@ bool UnrolledInstAnalyzer::visitCmpInst(CmpInst &I) { SimplifiedAddress &LHSAddr = SimplifiedLHS->second; SimplifiedAddress &RHSAddr = SimplifiedRHS->second; if (LHSAddr.Base == RHSAddr.Base) { - LHS = LHSAddr.Offset; - RHS = RHSAddr.Offset; + // FIXME: This is only correct for equality predicates. For + // unsigned predicates, this only holds if we have nowrap flags, + // which we don't track (for nuw it's valid as-is, for nusw it + // requires converting the predicated to signed). As this is used only + // for cost modelling, this is not a correctness issue. + bool Res = ICmpInst::compare(LHSAddr.Offset, RHSAddr.Offset, + I.getPredicate()); + SimplifiedValues[&I] = ConstantInt::getBool(I.getType(), Res); + return true; } } } diff --git a/llvm/lib/Analysis/MemoryBuiltins.cpp b/llvm/lib/Analysis/MemoryBuiltins.cpp index 1edc51e9ce5d..50b8a774cb30 100644 --- a/llvm/lib/Analysis/MemoryBuiltins.cpp +++ b/llvm/lib/Analysis/MemoryBuiltins.cpp @@ -675,7 +675,8 @@ Value *llvm::lowerObjectSizeCall( if (!MustSucceed) return nullptr; - return ConstantInt::get(ResultType, MaxVal ? -1ULL : 0); + return MaxVal ? Constant::getAllOnesValue(ResultType) + : Constant::getNullValue(ResultType); } STATISTIC(ObjectVisitorArgument, @@ -781,6 +782,8 @@ SizeOffsetAPInt ObjectSizeOffsetVisitor::visitAllocaInst(AllocaInst &I) { TypeSize ElemSize = DL.getTypeAllocSize(I.getAllocatedType()); if (ElemSize.isScalable() && Options.EvalMode != ObjectSizeOpts::Mode::Min) return ObjectSizeOffsetVisitor::unknown(); + if (!isUIntN(IntTyBits, ElemSize.getKnownMinValue())) + return ObjectSizeOffsetVisitor::unknown(); APInt Size(IntTyBits, ElemSize.getKnownMinValue()); if (!I.isArrayAllocation()) return SizeOffsetAPInt(align(Size, I.getAlign()), Zero); diff --git a/llvm/lib/Analysis/MemoryLocation.cpp b/llvm/lib/Analysis/MemoryLocation.cpp index e0cd320e946a..75a1786b1c65 100644 --- a/llvm/lib/Analysis/MemoryLocation.cpp +++ b/llvm/lib/Analysis/MemoryLocation.cpp @@ -14,6 +14,10 @@ #include "llvm/IR/IntrinsicsARM.h" #include "llvm/IR/Module.h" #include "llvm/IR/Type.h" +// EVM local begin +#include "llvm/IR/IntrinsicsEVM.h" +#include "llvm/TargetParser/Triple.h" +// EVM local end #include using namespace llvm; @@ -167,6 +171,69 @@ MemoryLocation MemoryLocation::getForArgument(const CallBase *Call, if (const IntrinsicInst *II = dyn_cast(Call)) { const DataLayout &DL = II->getDataLayout(); + // EVM local begin + // Check if the length is within i64 range. If not, return imprecise + // location. + auto T = Call->getModule()->getTargetTriple(); + if (Triple(T).isEVM()) { + auto GetMemLocation = [Call, Arg, &AATags](unsigned MemSizeArgIdx) { + const auto *LenCI = + dyn_cast(Call->getArgOperand(MemSizeArgIdx)); + if (LenCI && LenCI->getValue().getActiveBits() <= 64) + return MemoryLocation( + Arg, LocationSize::precise(LenCI->getZExtValue()), AATags); + return MemoryLocation::getAfter(Arg, AATags); + }; + + switch (II->getIntrinsicID()) { + case Intrinsic::memcpy: + case Intrinsic::memcpy_inline: + case Intrinsic::memmove: + case Intrinsic::memset: + if (ConstantInt *LenCI = dyn_cast(II->getArgOperand(2))) + if (LenCI->getValue().getActiveBits() > 64) + return MemoryLocation::getAfter(Arg, AATags); + break; + case Intrinsic::lifetime_start: + case Intrinsic::lifetime_end: + // it is okay to have lifetime intrinsic + break; + case Intrinsic::evm_sha3: { + assert((ArgIdx == 0) && "Invalid argument index for sha3"); + return GetMemLocation(1); + } + case Intrinsic::evm_mstore8: { + assert((ArgIdx == 0) && "Invalid argument index for mstore8"); + return MemoryLocation(Arg, LocationSize::precise(1), AATags); + } + case Intrinsic::evm_calldataload: { + assert((ArgIdx == 0) && "Invalid argument index for calldataload"); + return MemoryLocation(Arg, LocationSize::precise(32), AATags); + } + case Intrinsic::evm_revert: { + assert((ArgIdx == 0) && "Invalid argument index for revert"); + return GetMemLocation(ArgIdx + 1); + } + case Intrinsic::evm_extcodecopy: { + assert((ArgIdx == 1 || ArgIdx == 2) && + "Invalid argument index for extcodecopy"); + return GetMemLocation(3); + } + case Intrinsic::evm_log0: + case Intrinsic::evm_log1: + case Intrinsic::evm_log2: + case Intrinsic::evm_log3: + case Intrinsic::evm_log4: { + assert((ArgIdx == 0) && "Invalid argument index for log"); + return GetMemLocation(ArgIdx + 1); + } + default: + llvm_unreachable("Unexpected intrinsic for EVM target"); + break; + } + } + // EVM local end + switch (II->getIntrinsicID()) { default: break; diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp index 412cfe73d3e5..c57e12336256 100644 --- a/llvm/lib/Analysis/ScalarEvolution.cpp +++ b/llvm/lib/Analysis/ScalarEvolution.cpp @@ -11947,8 +11947,9 @@ ScalarEvolution::computeConstantDifference(const SCEV *More, const SCEV *Less) { // fairly deep in the call stack (i.e. is called many times). // X - X = 0. + unsigned BW = getTypeSizeInBits(More->getType()); if (More == Less) - return APInt(getTypeSizeInBits(More->getType()), 0); + return APInt(BW, 0); if (isa(Less) && isa(More)) { const auto *LAR = cast(Less); @@ -11971,33 +11972,36 @@ ScalarEvolution::computeConstantDifference(const SCEV *More, const SCEV *Less) { // fall through } - if (isa(Less) && isa(More)) { - const auto &M = cast(More)->getAPInt(); - const auto &L = cast(Less)->getAPInt(); - return M - L; - } - - SCEV::NoWrapFlags Flags; - const SCEV *LLess = nullptr, *RLess = nullptr; - const SCEV *LMore = nullptr, *RMore = nullptr; - const SCEVConstant *C1 = nullptr, *C2 = nullptr; - // Compare (X + C1) vs X. - if (splitBinaryAdd(Less, LLess, RLess, Flags)) - if ((C1 = dyn_cast(LLess))) - if (RLess == More) - return -(C1->getAPInt()); - - // Compare X vs (X + C2). - if (splitBinaryAdd(More, LMore, RMore, Flags)) - if ((C2 = dyn_cast(LMore))) - if (RMore == Less) - return C2->getAPInt(); + // Try to cancel out common factors in two add expressions. + SmallDenseMap Multiplicity; + APInt Diff(BW, 0); + auto Add = [&](const SCEV *S, int Mul) { + if (auto *C = dyn_cast(S)) { + if (Mul == 1) { + Diff += C->getAPInt(); + } else { + assert(Mul == -1); + Diff -= C->getAPInt(); + } + } else + Multiplicity[S] += Mul; + }; + auto Decompose = [&](const SCEV *S, int Mul) { + if (isa(S)) { + for (const SCEV *Op : S->operands()) + Add(Op, Mul); + } else + Add(S, Mul); + }; + Decompose(More, 1); + Decompose(Less, -1); - // Compare (X + C1) vs (X + C2). - if (C1 && C2 && RLess == RMore) - return C2->getAPInt() - C1->getAPInt(); + // Check whether all the non-constants cancel out. + for (const auto &[_, Mul] : Multiplicity) + if (Mul != 0) + return std::nullopt; - return std::nullopt; + return Diff; } bool ScalarEvolution::isImpliedCondOperandsViaAddRecStart( diff --git a/llvm/lib/Analysis/VMAliasAnalysis.cpp b/llvm/lib/Analysis/VMAliasAnalysis.cpp new file mode 100644 index 000000000000..7c512631049f --- /dev/null +++ b/llvm/lib/Analysis/VMAliasAnalysis.cpp @@ -0,0 +1,178 @@ +//===-- VMAliasAnalysis.cpp - VM alias 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 +// +//===----------------------------------------------------------------------===// +// +// This is the address space based alias analysis pass, that is shared between +// EVM and EVM backends. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/VMAliasAnalysis.h" +#include "llvm/Analysis/ValueTracking.h" +#include "llvm/IR/Instructions.h" +#include + +using namespace llvm; + +// Get the base pointer and the offset by looking through the +// ptrtoint+arithmetic+inttoptr sequence. +static std::pair +getBaseWithOffset(const Value *V, unsigned BitWidth, unsigned MaxLookup = 6) { + auto Offset = APInt::getZero(BitWidth); + + // Bail out if this is not an inttoptr instruction. + if (!isa(V)) + return {nullptr, Offset}; + + V = cast(V)->getOperand(0); + + for (unsigned I = 0; I < MaxLookup; ++I) { + // If this is a ptrtoint, get the operand and stop the lookup. + if (const auto *PtrToInt = dyn_cast(V)) { + V = PtrToInt->getOperand(0); + break; + } + + // We only handle binary operations. + const auto *BOp = dyn_cast(V); + if (!BOp) + break; + + // With constant operand. + const auto *CI = dyn_cast(BOp->getOperand(1)); + if (!CI) + break; + + auto Val = CI->getValue(); + + // If the value is larger than the current bitwidth, extend the offset + // and remember the new bitwidth. + if (Val.getBitWidth() > BitWidth) { + BitWidth = Val.getBitWidth(); + Offset = Offset.sext(BitWidth); + } else { + // Otherwise, extend the value to the current bitwidth. + Val = Val.sext(BitWidth); + } + + // TODO: CPR-1652 Support more instructions. + if (BOp->getOpcode() == Instruction::Add) + Offset += Val; + else if (BOp->getOpcode() == Instruction::Sub) + Offset -= Val; + else + break; + + V = BOp->getOperand(0); + } + return {V, Offset}; +} + +static std::optional getConstStartLoc(const MemoryLocation &Loc, + unsigned BitWidth) { + if (isa(Loc.Ptr)) + return APInt::getZero(BitWidth); + + if (const auto *CE = dyn_cast(Loc.Ptr)) { + if (CE->getOpcode() == Instruction::IntToPtr) { + if (auto *CI = dyn_cast(CE->getOperand(0))) + return CI->getValue(); + } + } + + if (const auto *IntToPtr = dyn_cast(Loc.Ptr)) { + if (auto *CI = dyn_cast(IntToPtr->getOperand(0))) + return CI->getValue(); + } + + return std::nullopt; +} + +AliasResult VMAAResult::alias(const MemoryLocation &LocA, + const MemoryLocation &LocB, AAQueryInfo &AAQI, + const Instruction *I) { + const unsigned ASA = LocA.Ptr->getType()->getPointerAddressSpace(); + const unsigned ASB = LocB.Ptr->getType()->getPointerAddressSpace(); + + // If we don't know what this is, bail out. + if (ASA > MaxAS || ASB > MaxAS) + return AAResultBase::alias(LocA, LocB, AAQI, I); + + // Pointers can't alias if they are not in the same address space. + if (ASA != ASB) + return AliasResult::NoAlias; + + // Since pointers are in the same address space, handle only cases that are + // interesting to us. + if (!StorageAS.count(ASA) && !HeapAS.count(ASA)) + return AAResultBase::alias(LocA, LocB, AAQI, I); + + // Don't check unknown memory locations. + if (!LocA.Size.isPrecise() || !LocB.Size.isPrecise()) + return AAResultBase::alias(LocA, LocB, AAQI, I); + + // Only 256-bit keys are valid for storage. + if (StorageAS.count(ASA)) { + constexpr unsigned KeyByteWidth = 32; + if (LocA.Size != KeyByteWidth || LocB.Size != KeyByteWidth) + return AAResultBase::alias(LocA, LocB, AAQI, I); + } + + unsigned BitWidth = DL.getPointerSizeInBits(ASA); + auto StartA = getConstStartLoc(LocA, BitWidth); + auto StartB = getConstStartLoc(LocB, BitWidth); + + // If we don't have constant start locations, try to get the base pointer and + // the offset. In case we managed to find them and pointers have the same + // base, we can compare offsets to prove aliasing. Otherwise, forward the + // query to the next alias analysis. + if (!StartA || !StartB) { + auto [BaseA, OffsetA] = getBaseWithOffset(LocA.Ptr, BitWidth); + auto [BaseB, OffsetB] = getBaseWithOffset(LocB.Ptr, BitWidth); + if (!BaseA || !BaseB || BaseA != BaseB) + return AAResultBase::alias(LocA, LocB, AAQI, I); + + StartA = OffsetA; + StartB = OffsetB; + } + + // Extend start locations to the same bitwidth and not less than pointer size. + unsigned MaxBitWidth = std::max(StartA->getBitWidth(), StartB->getBitWidth()); + MaxBitWidth = std::max(MaxBitWidth, BitWidth); + const APInt StartAVal = StartA->sext(MaxBitWidth); + const APInt StartBVal = StartB->sext(MaxBitWidth); + + // Keys in storage can't overlap. + if (StorageAS.count(ASA)) { + if (StartAVal == StartBVal) + return AliasResult::MustAlias; + return AliasResult::NoAlias; + } + + // If heap locations are the same, they either must or partially alias based + // on the size of locations. + if (StartAVal == StartBVal) { + // If either of the memory references is empty, it doesn't matter what the + // pointer values are. + if (LocA.Size.isZero() || LocB.Size.isZero()) + return AliasResult::NoAlias; + + if (LocA.Size == LocB.Size) + return AliasResult::MustAlias; + return AliasResult::PartialAlias; + } + + auto DoesOverlap = [](const APInt &X, const APInt &XEnd, const APInt &Y) { + return Y.sge(X) && Y.slt(XEnd); + }; + + // For heap accesses, if locations don't overlap, they are not aliasing. + if (!DoesOverlap(StartAVal, StartAVal + LocA.Size.getValue(), StartBVal) && + !DoesOverlap(StartBVal, StartBVal + LocB.Size.getValue(), StartAVal)) + return AliasResult::NoAlias; + return AliasResult::PartialAlias; +} diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp index 4b77c0046cc7..16387b46569c 100644 --- a/llvm/lib/Analysis/ValueTracking.cpp +++ b/llvm/lib/Analysis/ValueTracking.cpp @@ -9518,7 +9518,7 @@ static ConstantRange getRangeForIntrinsic(const IntrinsicInst &II) { case Intrinsic::cttz: // Maximum of set/clear bits is the bit width. return ConstantRange::getNonEmpty(APInt::getZero(Width), - APInt(Width, Width + 1)); + APInt(Width, Width) + 1); case Intrinsic::uadd_sat: // uadd.sat(x, C) produces [C, UINT_MAX]. if (match(II.getOperand(0), m_APInt(C)) || @@ -9673,7 +9673,7 @@ static void setLimitForFPToI(const Instruction *I, APInt &Lower, APInt &Upper) { if (!I->getOperand(0)->getType()->getScalarType()->isHalfTy()) return; if (isa(I) && BitWidth >= 17) { - Lower = APInt(BitWidth, -65504); + Lower = APInt(BitWidth, -65504, true); Upper = APInt(BitWidth, 65505); } diff --git a/llvm/lib/BinaryFormat/ELF.cpp b/llvm/lib/BinaryFormat/ELF.cpp index 9878f5769087..519e417487eb 100644 --- a/llvm/lib/BinaryFormat/ELF.cpp +++ b/llvm/lib/BinaryFormat/ELF.cpp @@ -197,6 +197,9 @@ uint16_t ELF::convertArchNameToEMachine(StringRef Arch) { .Case("ve", EM_VE) .Case("csky", EM_CSKY) .Case("loongarch", EM_LOONGARCH) + // EVM local begin + .Case("evm", EM_EVM) + // EVM local end .Default(EM_NONE); } @@ -563,6 +566,10 @@ StringRef ELF::convertEMachineToArchName(uint16_t EMachine) { return "csky"; case EM_LOONGARCH: return "loongarch"; + // EVM local begin + case EM_EVM: + return "evm"; + // EVM local end default: return "None"; } diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp index 84d624f6cf8f..cb6874acfef3 100644 --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -858,7 +858,8 @@ class BitcodeReader : public BitcodeReaderBase, public GVMaterializer { } else { int64_t Start = BitcodeReader::decodeSignRotatedValue(Record[OpNum++]); int64_t End = BitcodeReader::decodeSignRotatedValue(Record[OpNum++]); - return ConstantRange(APInt(BitWidth, Start), APInt(BitWidth, End)); + return ConstantRange(APInt(BitWidth, Start, true), + APInt(BitWidth, End, true)); } } diff --git a/llvm/lib/CodeGen/CodeGenPrepare.cpp b/llvm/lib/CodeGen/CodeGenPrepare.cpp index 22d0708f5478..1fb37fb8406e 100644 --- a/llvm/lib/CodeGen/CodeGenPrepare.cpp +++ b/llvm/lib/CodeGen/CodeGenPrepare.cpp @@ -1643,7 +1643,7 @@ static bool matchUAddWithOverflowConstantEdgeCases(CmpInst *Cmp, if (Pred == ICmpInst::ICMP_EQ && match(B, m_AllOnes())) B = ConstantInt::get(B->getType(), 1); else if (Pred == ICmpInst::ICMP_NE && match(B, m_ZeroInt())) - B = ConstantInt::get(B->getType(), -1); + B = Constant::getAllOnesValue(B->getType()); else return false; diff --git a/llvm/lib/CodeGen/ExpandMemCmp.cpp b/llvm/lib/CodeGen/ExpandMemCmp.cpp index 2758f7be4d50..04222d5b4afd 100644 --- a/llvm/lib/CodeGen/ExpandMemCmp.cpp +++ b/llvm/lib/CodeGen/ExpandMemCmp.cpp @@ -590,7 +590,7 @@ void MemCmpExpansion::emitMemCmpResultBlock() { ResBlock.PhiSrc2); Value *Res = - Builder.CreateSelect(Cmp, ConstantInt::get(Builder.getInt32Ty(), -1), + Builder.CreateSelect(Cmp, Constant::getAllOnesValue(Builder.getInt32Ty()), ConstantInt::get(Builder.getInt32Ty(), 1)); PhiRes->addIncoming(Res, ResBlock.BB); diff --git a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp index e77ea3e76ad7..d70c2fe4f708 100644 --- a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp +++ b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp @@ -4369,40 +4369,7 @@ bool CombinerHelper::matchICmpToTrueFalseKnownBits(MachineInstr &MI, if (!KnownVal) { auto KnownLHS = KB->getKnownBits(MI.getOperand(2).getReg()); - switch (Pred) { - default: - llvm_unreachable("Unexpected G_ICMP predicate?"); - case CmpInst::ICMP_EQ: - KnownVal = KnownBits::eq(KnownLHS, KnownRHS); - break; - case CmpInst::ICMP_NE: - KnownVal = KnownBits::ne(KnownLHS, KnownRHS); - break; - case CmpInst::ICMP_SGE: - KnownVal = KnownBits::sge(KnownLHS, KnownRHS); - break; - case CmpInst::ICMP_SGT: - KnownVal = KnownBits::sgt(KnownLHS, KnownRHS); - break; - case CmpInst::ICMP_SLE: - KnownVal = KnownBits::sle(KnownLHS, KnownRHS); - break; - case CmpInst::ICMP_SLT: - KnownVal = KnownBits::slt(KnownLHS, KnownRHS); - break; - case CmpInst::ICMP_UGE: - KnownVal = KnownBits::uge(KnownLHS, KnownRHS); - break; - case CmpInst::ICMP_UGT: - KnownVal = KnownBits::ugt(KnownLHS, KnownRHS); - break; - case CmpInst::ICMP_ULE: - KnownVal = KnownBits::ule(KnownLHS, KnownRHS); - break; - case CmpInst::ICMP_ULT: - KnownVal = KnownBits::ult(KnownLHS, KnownRHS); - break; - } + KnownVal = ICmpInst::compare(KnownLHS, KnownRHS, Pred); } if (!KnownVal) diff --git a/llvm/lib/CodeGen/MachineBasicBlock.cpp b/llvm/lib/CodeGen/MachineBasicBlock.cpp index d681d00b5d8c..67f23fe7a8ec 100644 --- a/llvm/lib/CodeGen/MachineBasicBlock.cpp +++ b/llvm/lib/CodeGen/MachineBasicBlock.cpp @@ -708,8 +708,10 @@ void MachineBasicBlock::updateTerminator( if (TBB) { // The block has an unconditional branch. If its successor is now its // layout successor, delete the branch. - if (isLayoutSuccessor(TBB)) + // EVM local begin + if (isLayoutSuccessor(TBB) && !FBB) TII->removeBranch(*this); + // EVM local end } else { // The block has an unconditional fallthrough, or the end of the block is // unreachable. diff --git a/llvm/lib/CodeGen/MachineCopyPropagation.cpp b/llvm/lib/CodeGen/MachineCopyPropagation.cpp index b34e0939d1c7..8c48de890888 100644 --- a/llvm/lib/CodeGen/MachineCopyPropagation.cpp +++ b/llvm/lib/CodeGen/MachineCopyPropagation.cpp @@ -108,9 +108,13 @@ static std::optional isCopyInstr(const MachineInstr &MI, class CopyTracker { struct CopyInfo { - MachineInstr *MI, *LastSeenUseInCopy; + MachineInstr *MI = nullptr; + MachineInstr *LastSeenUseInCopy = nullptr; + // EVM local begin + SmallPtrSet SrcUsers; + // EVM local end SmallVector DefRegs; - bool Avail; + bool Avail = false; }; DenseMap Copies; @@ -223,6 +227,45 @@ class CopyTracker { } } + // EVM local begin + /// Track copy's src users, and return false if that can't be done. + /// We can only track if we have a COPY instruction which source is + /// the same as the Reg. + bool trackSrcUsers(MCRegister Reg, MachineInstr &MI, + const TargetRegisterInfo &TRI, const TargetInstrInfo &TII, + bool UseCopyInstr) { + MCRegUnit RU = *TRI.regunits(Reg).begin(); + MachineInstr *AvailCopy = findCopyDefViaUnit(RU, TRI); + if (!AvailCopy) + return false; + + std::optional CopyOperands = + isCopyInstr(*AvailCopy, TII, UseCopyInstr); + Register Src = CopyOperands->Source->getReg(); + + // Bail out, if the source of the copy is not the same as the Reg. + if (Src != Reg) + return false; + + auto I = Copies.find(RU); + if (I == Copies.end()) + return false; + + I->second.SrcUsers.insert(&MI); + return true; + } + + /// Return the users for a given register. + SmallPtrSet getSrcUsers(MCRegister Reg, + const TargetRegisterInfo &TRI) { + MCRegUnit RU = *TRI.regunits(Reg).begin(); + auto I = Copies.find(RU); + if (I == Copies.end()) + return {}; + return I->second.SrcUsers; + } + // EVM local end + /// Add this copy's registers into the tracker's copy maps. void trackCopy(MachineInstr *MI, const TargetRegisterInfo &TRI, const TargetInstrInfo &TII, bool UseCopyInstr) { @@ -235,13 +278,14 @@ class CopyTracker { // Remember Def is defined by the copy. for (MCRegUnit Unit : TRI.regunits(Def)) - Copies[Unit] = {MI, nullptr, {}, true}; + // EVM local begin + Copies[Unit] = {MI, nullptr, {}, {}, true}; + // EVM local end // Remember source that's copied to Def. Once it's clobbered, then // it's no longer available for copy propagation. for (MCRegUnit Unit : TRI.regunits(Src)) { - auto I = Copies.insert({Unit, {nullptr, nullptr, {}, false}}); - auto &Copy = I.first->second; + auto &Copy = Copies[Unit]; if (!is_contained(Copy.DefRegs, Def)) Copy.DefRegs.push_back(Def); Copy.LastSeenUseInCopy = MI; @@ -427,6 +471,10 @@ class MachineCopyPropagation : public MachineFunctionPass { bool hasImplicitOverlap(const MachineInstr &MI, const MachineOperand &Use); bool hasOverlappingMultipleDef(const MachineInstr &MI, const MachineOperand &MODef, Register Def); + // EVM local begin + bool canUpdateSrcUsers(const MachineInstr &Copy, + const MachineOperand &CopySrc); + // EVM local end /// Candidates for deletion. SmallSetVector MaybeDeadCopies; @@ -667,6 +715,28 @@ bool MachineCopyPropagation::hasOverlappingMultipleDef( return false; } +// EVM local begin +/// Return true if it is safe to update the users of the source register of the +/// copy. +bool MachineCopyPropagation::canUpdateSrcUsers(const MachineInstr &Copy, + const MachineOperand &CopySrc) { + for (auto *SrcUser : Tracker.getSrcUsers(CopySrc.getReg(), *TRI)) { + if (hasImplicitOverlap(*SrcUser, CopySrc)) + return false; + + for (MachineOperand &MO : SrcUser->uses()) { + if (!MO.isReg() || !MO.isUse() || MO.getReg() != CopySrc.getReg()) + continue; + if (MO.isTied() || !MO.isRenamable() || + !isBackwardPropagatableRegClassCopy(Copy, *SrcUser, + MO.getOperandNo())) + return false; + } + } + return true; +} +// EVM local end + /// Look for available copies whose destination register is used by \p MI and /// replace the use in \p MI with the copy's source register. void MachineCopyPropagation::forwardUses(MachineInstr &MI) { @@ -885,6 +955,12 @@ void MachineCopyPropagation::ForwardCopyPropagateBlock(MachineBasicBlock &MBB) { assert(!Reg.isVirtual() && "MachineCopyPropagation should be run after register allocation!"); + // EVM local begin + // Skip invalidating constant registers. + if (MRI->isReserved(Reg) && MRI->isConstantPhysReg(Reg)) + continue; + // EVM local end + if (MO.isDef() && !MO.isEarlyClobber()) { Defs.push_back(Reg.asMCReg()); continue; @@ -1030,6 +1106,11 @@ void MachineCopyPropagation::propagateDefs(MachineInstr &MI) { if (hasOverlappingMultipleDef(MI, MODef, Def)) continue; + // EVM local begin + if (!canUpdateSrcUsers(*Copy, *CopyOperands->Source)) + continue; + // EVM local end + LLVM_DEBUG(dbgs() << "MCP: Replacing " << printReg(MODef.getReg(), TRI) << "\n with " << printReg(Def, TRI) << "\n in " << MI << " from " << *Copy); @@ -1037,6 +1118,17 @@ void MachineCopyPropagation::propagateDefs(MachineInstr &MI) { MODef.setReg(Def); MODef.setIsRenamable(CopyOperands->Destination->isRenamable()); + // EVM local begin + for (auto *SrcUser : Tracker.getSrcUsers(Src, *TRI)) { + for (MachineOperand &MO : SrcUser->uses()) { + if (!MO.isReg() || !MO.isUse() || MO.getReg() != Src) + continue; + MO.setReg(Def); + MO.setIsRenamable(CopyOperands->Destination->isRenamable()); + } + } + // EVM local end + LLVM_DEBUG(dbgs() << "MCP: After replacement: " << MI << "\n"); MaybeDeadCopies.insert(Copy); Changed = true; @@ -1053,7 +1145,7 @@ void MachineCopyPropagation::BackwardCopyPropagateBlock( // Ignore non-trivial COPYs. std::optional CopyOperands = isCopyInstr(MI, *TII, UseCopyInstr); - if (CopyOperands && MI.getNumOperands() == 2) { + if (CopyOperands) { Register DefReg = CopyOperands->Destination->getReg(); Register SrcReg = CopyOperands->Source->getReg(); @@ -1102,7 +1194,11 @@ void MachineCopyPropagation::BackwardCopyPropagateBlock( CopyDbgUsers[Copy].insert(&MI); } } - } else { + // EVM local begin + } else if (!Tracker.trackSrcUsers(MO.getReg().asMCReg(), MI, *TRI, *TII, + UseCopyInstr)) { + // If we can't track the source users, invalidate the register. + // EVM local end Tracker.invalidateRegister(MO.getReg().asMCReg(), *TRI, *TII, UseCopyInstr); } diff --git a/llvm/lib/CodeGen/MachineSink.cpp b/llvm/lib/CodeGen/MachineSink.cpp index 4b3ff57fb478..764d4e5995e2 100644 --- a/llvm/lib/CodeGen/MachineSink.cpp +++ b/llvm/lib/CodeGen/MachineSink.cpp @@ -953,7 +953,9 @@ bool MachineSinking::isWorthBreakingCriticalEdge( } } - return false; + // Let the target decide if it's worth breaking this + // critical edge for a "cheap" instruction. + return TII->shouldBreakCriticalEdgeToSink(MI); } bool MachineSinking::isLegalToBreakCriticalEdge(MachineInstr &MI, diff --git a/llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp b/llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp index 19950f3eb67b..b7603854f951 100644 --- a/llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp +++ b/llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp @@ -414,6 +414,10 @@ class PreISelIntrinsicLoweringLegacyPass : public ModulePass { }; const auto &TM = getAnalysis().getTM(); + // EVM local begin + if (TM.getTargetTriple().isEVM()) + return false; + // EVM local end PreISelIntrinsicLowering Lowering(TM, LookupTTI); return Lowering.lowerIntrinsics(M); } @@ -438,6 +442,10 @@ ModulePass *llvm::createPreISelIntrinsicLoweringPass() { PreservedAnalyses PreISelIntrinsicLoweringPass::run(Module &M, ModuleAnalysisManager &AM) { + // EVM local begin + if (TM.getTargetTriple().isEVM()) + return PreservedAnalyses::all(); + // EVM local end auto &FAM = AM.getResult(M).getManager(); auto LookupTTI = [&FAM](Function &F) -> TargetTransformInfo & { diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp index 7b1f1dc40211..f6ac4b4ce217 100644 --- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp @@ -9582,7 +9582,7 @@ SDValue DAGCombiner::visitXOR(SDNode *N) { // A rotate left of ~1 is a nice way of achieving the desired result. if (TLI.isOperationLegalOrCustom(ISD::ROTL, VT) && N0Opcode == ISD::SHL && isAllOnesConstant(N1) && isOneConstant(N0.getOperand(0))) { - return DAG.getNode(ISD::ROTL, DL, VT, DAG.getConstant(~1, DL, VT), + return DAG.getNode(ISD::ROTL, DL, VT, DAG.getSignedConstant(~1, DL, VT), N0.getOperand(1)); } diff --git a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp index 4ce92e156cf8..334d307dc6c8 100644 --- a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp @@ -405,7 +405,12 @@ void InstrEmitter::AddOperand(MachineInstrBuilder &MIB, AddRegisterOperand(MIB, Op, IIOpNum, II, VRBaseMap, IsDebug, IsClone, IsCloned); } else if (ConstantSDNode *C = dyn_cast(Op)) { - MIB.addImm(C->getSExtValue()); + // EVM local begin + if (C->getConstantIntValue()->getBitWidth() > 64) + MIB.addCImm(C->getConstantIntValue()); + else + MIB.addImm(C->getSExtValue()); + // EVM local end } else if (ConstantFPSDNode *F = dyn_cast(Op)) { MIB.addFPImm(F->getConstantFPValue()); } else if (RegisterSDNode *R = dyn_cast(Op)) { diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp index 4b25f553ffae..ee7c146cdf1f 100644 --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp @@ -2592,7 +2592,7 @@ SDValue SelectionDAGLegalize::expandFrexp(SDNode *Node) const { SDValue IsDenormal = DAG.getSetCC(dl, SetCCVT, Abs, SmallestNormalizedInt, ISD::SETULT); - SDValue MinExp = DAG.getConstant(MinExpVal, dl, ExpVT); + SDValue MinExp = DAG.getSignedConstant(MinExpVal, dl, ExpVT); SDValue Zero = DAG.getConstant(0, dl, ExpVT); SDValue ScaledAsInt = DAG.getNode(ISD::BITCAST, dl, AsIntVT, ScaleUp); diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp index af77b0070df0..30bf62955104 100644 --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp @@ -3289,7 +3289,7 @@ void DAGTypeLegalizer::ExpandIntRes_MINMAX(SDNode *N, SDValue HiNeg = DAG.getSetCC(DL, CCT, LHSH, DAG.getConstant(0, DL, NVT), ISD::SETLT); if (N->getOpcode() == ISD::SMIN) { - Lo = DAG.getSelect(DL, NVT, HiNeg, LHSL, DAG.getConstant(-1, DL, NVT)); + Lo = DAG.getSelect(DL, NVT, HiNeg, LHSL, DAG.getAllOnesConstant(DL, NVT)); } else { Lo = DAG.getSelect(DL, NVT, HiNeg, DAG.getConstant(0, DL, NVT), LHSL); } @@ -4372,7 +4372,7 @@ void DAGTypeLegalizer::ExpandIntRes_MULFIX(SDNode *N, SDValue &Lo, SDValue SatMax, SatMin; SDValue NVTZero = DAG.getConstant(0, dl, NVT); - SDValue NVTNeg1 = DAG.getConstant(-1, dl, NVT); + SDValue NVTNeg1 = DAG.getAllOnesConstant(dl, NVT); EVT BoolNVT = getSetCCResultType(NVT); if (!Signed) { diff --git a/llvm/lib/CodeGen/SelectionDAG/ScheduleDAGRRList.cpp b/llvm/lib/CodeGen/SelectionDAG/ScheduleDAGRRList.cpp index e4ee3fd99f16..78721351107a 100644 --- a/llvm/lib/CodeGen/SelectionDAG/ScheduleDAGRRList.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/ScheduleDAGRRList.cpp @@ -525,7 +525,10 @@ FindCallSeqStart(SDNode *N, unsigned &NestLevel, unsigned &MaxNest, } // Otherwise, find the chain and continue climbing. for (const SDValue &Op : N->op_values()) - if (Op.getValueType() == MVT::Other) { + // EVM local begin + if (Op.getValueType() == MVT::Other + && Op.getOpcode() != ISD::BasicBlock) { + // EVM local end N = Op.getNode(); goto found_chain_operand; } diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp index 02d44cd36ae5..7a523144e633 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -1627,7 +1627,11 @@ SDValue SelectionDAG::getConstant(uint64_t Val, const SDLoc &DL, EVT VT, assert((EltVT.getSizeInBits() >= 64 || (uint64_t)((int64_t)Val >> EltVT.getSizeInBits()) + 1 < 2) && "getConstant with a uint64_t value that doesn't fit in the type!"); - return getConstant(APInt(EltVT.getSizeInBits(), Val), DL, VT, isT, isO); + // TODO: Avoid implicit trunc? + // See https://github.com/llvm/llvm-project/issues/112510. + return getConstant(APInt(EltVT.getSizeInBits(), Val, /*isSigned=*/false, + /*implicitTrunc=*/true), + DL, VT, isT, isO); } SDValue SelectionDAG::getConstant(const APInt &Val, const SDLoc &DL, EVT VT, @@ -1747,6 +1751,15 @@ SDValue SelectionDAG::getConstant(const ConstantInt &Val, const SDLoc &DL, return Result; } +SDValue SelectionDAG::getSignedConstant(int64_t Val, const SDLoc &DL, EVT VT, + bool isT, bool isO) { + unsigned Size = VT.getScalarSizeInBits(); + assert( + isIntN(Size, Val) && + "getSignedConstant with a int64_t value that doesn't fit in the type!"); + return getConstant(APInt(Size, Val, true), DL, VT, isT, isO); +} + SDValue SelectionDAG::getIntPtrConstant(uint64_t Val, const SDLoc &DL, bool isTarget) { return getConstant(Val, DL, TLI->getPointerTy(getDataLayout()), isTarget); @@ -2428,9 +2441,9 @@ SDValue SelectionDAG::expandVAArg(SDNode *Node) { VAList = getNode(ISD::ADD, dl, VAList.getValueType(), VAList, getConstant(MA->value() - 1, dl, VAList.getValueType())); - VAList = - getNode(ISD::AND, dl, VAList.getValueType(), VAList, - getConstant(-(int64_t)MA->value(), dl, VAList.getValueType())); + VAList = getNode( + ISD::AND, dl, VAList.getValueType(), VAList, + getSignedConstant(-(int64_t)MA->value(), dl, VAList.getValueType())); } // Increment the pointer, VAList, to the next vaarg @@ -12575,7 +12588,17 @@ MaybeAlign SelectionDAG::InferPtrAlign(SDValue Ptr) const { isa(Ptr.getOperand(0))) { // Handle FI+Cst FrameIdx = cast(Ptr.getOperand(0))->getIndex(); - FrameOffset = Ptr.getConstantOperandVal(1); + // EVM local begin + // In case the offset is negative, it overflows 64 bits with EVM's i256. + // Though the architecture guarantees the offset fits 64 bits wide + // signed integer, so it's ok to truncate. + if (Ptr.getOperand(1).getSimpleValueType() == MVT::i256) + FrameOffset = cast(Ptr.getOperand(1)) + ->getAPIntValue() + .trunc(64) + .getZExtValue(); + + // EVM local end } if (FrameIdx != INT_MIN) { diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGAddressAnalysis.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGAddressAnalysis.cpp index f2ab88851b78..10656ccebd73 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGAddressAnalysis.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGAddressAnalysis.cpp @@ -225,6 +225,10 @@ static BaseIndexOffset matchLSNode(const LSBaseSDNode *N, // Only consider ORs which act as adds. if (auto *C = dyn_cast(Base->getOperand(1))) if (DAG.MaskedValueIsZero(Base->getOperand(0), C->getAPIntValue())) { + // EVM local begin + if (C->getAPIntValue().getSignificantBits() > 64) + return BaseIndexOffset(); + // EVM local end Offset += C->getSExtValue(); Base = DAG.getTargetLoweringInfo().unwrapAddress(Base->getOperand(0)); continue; @@ -232,6 +236,10 @@ static BaseIndexOffset matchLSNode(const LSBaseSDNode *N, break; case ISD::ADD: if (auto *C = dyn_cast(Base->getOperand(1))) { + // EVM local begin + if (C->getAPIntValue().getSignificantBits() > 64) + return BaseIndexOffset(); + // EVM local end Offset += C->getSExtValue(); Base = DAG.getTargetLoweringInfo().unwrapAddress(Base->getOperand(0)); continue; @@ -243,6 +251,10 @@ static BaseIndexOffset matchLSNode(const LSBaseSDNode *N, unsigned int IndexResNo = (Base->getOpcode() == ISD::LOAD) ? 1 : 0; if (LSBase->isIndexed() && Base.getResNo() == IndexResNo) if (auto *C = dyn_cast(LSBase->getOffset())) { + // EVM local begin + if (C->getAPIntValue().getSignificantBits() > 64) + return BaseIndexOffset(); + // EVM local end auto Off = C->getSExtValue(); if (LSBase->getAddressingMode() == ISD::PRE_DEC || LSBase->getAddressingMode() == ISD::POST_DEC) @@ -286,6 +298,12 @@ static BaseIndexOffset matchLSNode(const LSBaseSDNode *N, !isa(Index->getOperand(1))) return BaseIndexOffset(PotentialBase, Index, Offset, IsIndexSignExt); + // EVM local begin + if (cast(Index->getOperand(1)) + ->getAPIntValue() + .getSignificantBits() > 64) + return BaseIndexOffset(); + // EVM local end Offset += cast(Index->getOperand(1))->getSExtValue(); Index = Index->getOperand(0); if (Index->getOpcode() == ISD::SIGN_EXTEND) { diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index 7fa3b8a73a41..199c864512e3 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -4334,7 +4334,8 @@ void SelectionDAGBuilder::visitGetElementPtr(const User &I) { GTI.getSequentialElementStride(DAG.getDataLayout()); // We intentionally mask away the high bits here; ElementSize may not // fit in IdxTy. - APInt ElementMul(IdxSize, ElementSize.getKnownMinValue()); + APInt ElementMul(IdxSize, ElementSize.getKnownMinValue(), + /*isSigned=*/false, /*implicitTrunc=*/true); bool ElementScalable = ElementSize.isScalable(); // If this is a scalar constant or a splat vector of constants, @@ -4474,7 +4475,7 @@ void SelectionDAGBuilder::visitAlloca(const AllocaInst &I) { // Mask out the low bits for alignment purposes. AllocSize = DAG.getNode(ISD::AND, dl, AllocSize.getValueType(), AllocSize, - DAG.getConstant(~StackAlignMask, dl, IntPtr)); + DAG.getSignedConstant(~StackAlignMask, dl, IntPtr)); SDValue Ops[] = { getRoot(), AllocSize, @@ -5229,7 +5230,8 @@ void SelectionDAGBuilder::visitTargetIntrinsic(const CallInst &I, // definition. const Function *F = I.getCalledFunction(); bool HasChain = !F->doesNotAccessMemory(); - bool OnlyLoad = HasChain && F->onlyReadsMemory(); + bool OnlyLoad = + HasChain && F->onlyReadsMemory() && F->willReturn() && F->doesNotThrow(); // Build the operand list. SmallVector Ops; @@ -12342,9 +12344,18 @@ void SelectionDAGBuilder::visitSwitch(const SwitchInst &SI) { return; } + // EVM local begin + // TODO: CPR-688 EVM can build jump tables, though the constants are 4 + // times as expensive as instructions in terms of code size. For hot pieces of + // code it still makes sense. + if (!TM.getTargetTriple().isEVM()) { + // EVM local end SL->findJumpTables(Clusters, &SI, getCurSDLoc(), DefaultMBB, DAG.getPSI(), DAG.getBFI()); SL->findBitTestClusters(Clusters, &SI); + // EVM local begin + } + // EVM local end LLVM_DEBUG({ dbgs() << "Case clusters: "; @@ -12507,8 +12518,10 @@ void SelectionDAGBuilder::visitVectorSplice(const CallInst &I) { // VECTOR_SHUFFLE doesn't support a scalable mask so use a dedicated node. if (VT.isScalableVector()) { - setValue(&I, DAG.getNode(ISD::VECTOR_SPLICE, DL, VT, V1, V2, - DAG.getVectorIdxConstant(Imm, DL))); + setValue( + &I, DAG.getNode(ISD::VECTOR_SPLICE, DL, VT, V1, V2, + DAG.getSignedConstant( + Imm, DL, TLI.getVectorIdxTy(DAG.getDataLayout())))); return; } diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp index b961d3bb1fec..3451087f08cd 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp @@ -2166,7 +2166,10 @@ ScheduleDAGSDNodes *SelectionDAGISel::CreateScheduler() { bool SelectionDAGISel::CheckAndMask(SDValue LHS, ConstantSDNode *RHS, int64_t DesiredMaskS) const { const APInt &ActualMask = RHS->getAPIntValue(); - const APInt &DesiredMask = APInt(LHS.getValueSizeInBits(), DesiredMaskS); + // TODO: Avoid implicit trunc? + // See https://github.com/llvm/llvm-project/issues/112510. + const APInt &DesiredMask = APInt(LHS.getValueSizeInBits(), DesiredMaskS, + /*isSigned=*/false, /*implicitTrunc=*/true); // If the actual mask exactly matches, success! if (ActualMask == DesiredMask) @@ -2195,7 +2198,10 @@ bool SelectionDAGISel::CheckAndMask(SDValue LHS, ConstantSDNode *RHS, bool SelectionDAGISel::CheckOrMask(SDValue LHS, ConstantSDNode *RHS, int64_t DesiredMaskS) const { const APInt &ActualMask = RHS->getAPIntValue(); - const APInt &DesiredMask = APInt(LHS.getValueSizeInBits(), DesiredMaskS); + // TODO: Avoid implicit trunc? + // See https://github.com/llvm/llvm-project/issues/112510. + const APInt &DesiredMask = APInt(LHS.getValueSizeInBits(), DesiredMaskS, + /*isSigned=*/false, /*implicitTrunc=*/true); // If the actual mask exactly matches, success! if (ActualMask == DesiredMask) @@ -2930,7 +2936,14 @@ CheckInteger(const unsigned char *MatcherTable, unsigned &MatcherIndex, Val = decodeSignRotatedValue(Val); ConstantSDNode *C = dyn_cast(N); - return C && C->getAPIntValue().trySExtValue() == Val; + // EVM local begin + if (C) { + const APInt &CVal = C->getAPIntValue(); + if (CVal == APInt(CVal.getBitWidth(), Val, /*isSigned=*/true)) + return true; + } + return false; + // EVM local end } LLVM_ATTRIBUTE_ALWAYS_INLINE static bool diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp index 140c97ccd90b..e7e40f954621 100644 --- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp @@ -4641,6 +4641,10 @@ SDValue TargetLowering::SimplifySetCC(EVT VT, SDValue N0, SDValue N1, } } + // EVM local begin + // Load narrowing is not profiable for EVM. + if (!DAG.getTarget().getTargetTriple().isEVM()) { + // EVM local end // If the LHS is '(and load, const)', the RHS is 0, the test is for // equality or unsigned, and all 1 bits of the const are in the same // partial word, see if we can shorten the load. @@ -4712,6 +4716,9 @@ SDValue TargetLowering::SimplifySetCC(EVT VT, SDValue N0, SDValue N1, return DAG.getSetCC(dl, VT, And, DAG.getConstant(0LL, dl, newVT), Cond); } } + // EVM local begin + } + // EVM local end // If the LHS is a ZERO_EXTEND, perform the comparison on the input. if (N0.getOpcode() == ISD::ZERO_EXTEND) { @@ -6342,9 +6349,9 @@ SDValue TargetLowering::BuildSDIV(SDNode *N, SelectionDAG &DAG, } MagicFactors.push_back(DAG.getConstant(magics.Magic, dl, SVT)); - Factors.push_back(DAG.getConstant(NumeratorFactor, dl, SVT)); + Factors.push_back(DAG.getSignedConstant(NumeratorFactor, dl, SVT)); Shifts.push_back(DAG.getConstant(magics.ShiftAmount, dl, ShSVT)); - ShiftMasks.push_back(DAG.getConstant(ShiftMask, dl, SVT)); + ShiftMasks.push_back(DAG.getSignedConstant(ShiftMask, dl, SVT)); return true; }; @@ -6789,7 +6796,9 @@ TargetLowering::prepareUREMEqFold(EVT SETCCVT, SDValue REMNode, PAmts.push_back(DAG.getConstant(P, DL, SVT)); KAmts.push_back( - DAG.getConstant(APInt(ShSVT.getSizeInBits(), K), DL, ShSVT)); + DAG.getConstant(APInt(ShSVT.getSizeInBits(), K, /*isSigned=*/false, + /*implicitTrunc=*/true), + DL, ShSVT)); QAmts.push_back(DAG.getConstant(Q, DL, SVT)); return true; }; @@ -7060,7 +7069,9 @@ TargetLowering::prepareSREMEqFold(EVT SETCCVT, SDValue REMNode, PAmts.push_back(DAG.getConstant(P, DL, SVT)); AAmts.push_back(DAG.getConstant(A, DL, SVT)); KAmts.push_back( - DAG.getConstant(APInt(ShSVT.getSizeInBits(), K), DL, ShSVT)); + DAG.getConstant(APInt(ShSVT.getSizeInBits(), K, /*isSigned=*/false, + /*implicitTrunc=*/true), + DL, ShSVT)); QAmts.push_back(DAG.getConstant(Q, DL, SVT)); return true; }; @@ -9045,8 +9056,8 @@ SDValue TargetLowering::expandVPCTLZ(SDNode *Node, SelectionDAG &DAG) const { DAG.getNode(ISD::VP_SRL, dl, VT, Op, Tmp, Mask, VL), Mask, VL); } - Op = DAG.getNode(ISD::VP_XOR, dl, VT, Op, DAG.getConstant(-1, dl, VT), Mask, - VL); + Op = DAG.getNode(ISD::VP_XOR, dl, VT, Op, DAG.getAllOnesConstant(dl, VT), + Mask, VL); return DAG.getNode(ISD::VP_CTPOP, dl, VT, Op, Mask, VL); } @@ -9159,7 +9170,7 @@ SDValue TargetLowering::expandVPCTTZ(SDNode *Node, SelectionDAG &DAG) const { // Same as the vector part of expandCTTZ, use: popcount(~x & (x - 1)) SDValue Not = DAG.getNode(ISD::VP_XOR, dl, VT, Op, - DAG.getConstant(-1, dl, VT), Mask, VL); + DAG.getAllOnesConstant(dl, VT), Mask, VL); SDValue MinusOne = DAG.getNode(ISD::VP_SUB, dl, VT, Op, DAG.getConstant(1, dl, VT), Mask, VL); SDValue Tmp = DAG.getNode(ISD::VP_AND, dl, VT, Not, MinusOne, Mask, VL); @@ -10028,6 +10039,7 @@ SDValue TargetLowering::expandUnalignedStore(StoreSDNode *ST, assert(StoreMemVT.isInteger() && !StoreMemVT.isVector() && "Unaligned store of unknown type."); + // Get the half-size VT EVT NewStoredVT = StoreMemVT.getHalfSizedIntegerVT(*DAG.getContext()); unsigned NumBits = NewStoredVT.getFixedSizeInBits(); diff --git a/llvm/lib/CodeGen/TargetInstrInfo.cpp b/llvm/lib/CodeGen/TargetInstrInfo.cpp index 3cd1bb296d28..54cd760f951d 100644 --- a/llvm/lib/CodeGen/TargetInstrInfo.cpp +++ b/llvm/lib/CodeGen/TargetInstrInfo.cpp @@ -823,7 +823,9 @@ void TargetInstrInfo::lowerCopy(MachineInstr *MI, } copyPhysReg(*MI->getParent(), MI, MI->getDebugLoc(), DstMO.getReg(), - SrcMO.getReg(), SrcMO.isKill()); + SrcMO.getReg(), SrcMO.isKill(), + DstMO.getReg().isPhysical() ? DstMO.isRenamable() : false, + SrcMO.getReg().isPhysical() ? SrcMO.isRenamable() : false); if (MI->getNumOperands() > 2) transferImplicitOperands(MI, TRI); diff --git a/llvm/lib/CodeGen/TargetPassConfig.cpp b/llvm/lib/CodeGen/TargetPassConfig.cpp index 3658e8320a0c..21cc13abb363 100644 --- a/llvm/lib/CodeGen/TargetPassConfig.cpp +++ b/llvm/lib/CodeGen/TargetPassConfig.cpp @@ -1063,8 +1063,12 @@ bool TargetPassConfig::addISelPasses() { PM->add(createTargetTransformInfoWrapperPass(TM->getTargetIRAnalysis())); addPass(createPreISelIntrinsicLoweringPass()); - addPass(createExpandLargeDivRemPass()); - addPass(createExpandLargeFpConvertPass()); + // EVM local begin + if (!TM->getTargetTriple().isEVM()) { + addPass(createExpandLargeDivRemPass()); + addPass(createExpandLargeFpConvertPass()); + } + // EVM local end addIRPasses(); addCodeGenPrepare(); addPassesToHandleExceptions(); diff --git a/llvm/lib/ExecutionEngine/ExecutionEngine.cpp b/llvm/lib/ExecutionEngine/ExecutionEngine.cpp index 8297d15b1580..38413fba4eab 100644 --- a/llvm/lib/ExecutionEngine/ExecutionEngine.cpp +++ b/llvm/lib/ExecutionEngine/ExecutionEngine.cpp @@ -1056,7 +1056,7 @@ void ExecutionEngine::StoreValueToMemory(const GenericValue &Val, *((double*)Ptr) = Val.DoubleVal; break; case Type::X86_FP80TyID: - memcpy(Ptr, Val.IntVal.getRawData(), 10); + memcpy(static_cast(Ptr), Val.IntVal.getRawData(), 10); break; case Type::PointerTyID: // Ensure 64 bit target pointers are fully initialized on 32 bit hosts. diff --git a/llvm/lib/ExecutionEngine/MCJIT/MCJIT.cpp b/llvm/lib/ExecutionEngine/MCJIT/MCJIT.cpp index 869b383dd064..f8f344ec38f5 100644 --- a/llvm/lib/ExecutionEngine/MCJIT/MCJIT.cpp +++ b/llvm/lib/ExecutionEngine/MCJIT/MCJIT.cpp @@ -590,7 +590,7 @@ GenericValue MCJIT::runFunction(Function *F, ArrayRef ArgValues) { return rv; } case Type::VoidTyID: - rv.IntVal = APInt(32, ((int(*)())(intptr_t)FPtr)()); + rv.IntVal = APInt(32, ((int (*)())(intptr_t)FPtr)(), true); return rv; case Type::FloatTyID: rv.FloatVal = ((float(*)())(intptr_t)FPtr)(); diff --git a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp index ab2482b91213..89509d2d8e7a 100644 --- a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp +++ b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp @@ -5905,7 +5905,7 @@ CallInst *OpenMPIRBuilder::createOMPInteropInit( Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize); Value *ThreadId = getOrCreateThreadID(Ident); if (Device == nullptr) - Device = ConstantInt::get(Int32, -1); + Device = Constant::getAllOnesValue(Int32); Constant *InteropTypeVal = ConstantInt::get(Int32, (int)InteropType); if (NumDependences == nullptr) { NumDependences = ConstantInt::get(Int32, 0); @@ -5933,7 +5933,7 @@ CallInst *OpenMPIRBuilder::createOMPInteropDestroy( Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize); Value *ThreadId = getOrCreateThreadID(Ident); if (Device == nullptr) - Device = ConstantInt::get(Int32, -1); + Device = Constant::getAllOnesValue(Int32); if (NumDependences == nullptr) { NumDependences = ConstantInt::get(Int32, 0); PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext()); @@ -5961,7 +5961,7 @@ CallInst *OpenMPIRBuilder::createOMPInteropUse(const LocationDescription &Loc, Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize); Value *ThreadId = getOrCreateThreadID(Ident); if (Device == nullptr) - Device = ConstantInt::get(Int32, -1); + Device = Constant::getAllOnesValue(Int32); if (NumDependences == nullptr) { NumDependences = ConstantInt::get(Int32, 0); PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext()); @@ -6098,7 +6098,7 @@ OpenMPIRBuilder::createTargetInit(const LocationDescription &Loc, bool IsSPMD, Builder.CreateCall(Fn, {KernelEnvironment, KernelLaunchEnvironment}); Value *ExecUserCode = Builder.CreateICmpEQ( - ThreadKind, ConstantInt::get(ThreadKind->getType(), -1), + ThreadKind, Constant::getAllOnesValue(ThreadKind->getType()), "exec_user_code"); // ThreadKind = __kmpc_target_init(...) diff --git a/llvm/lib/IR/ConstantRange.cpp b/llvm/lib/IR/ConstantRange.cpp index 0ead67742280..43cc8664866a 100644 --- a/llvm/lib/IR/ConstantRange.cpp +++ b/llvm/lib/IR/ConstantRange.cpp @@ -1836,7 +1836,7 @@ ConstantRange ConstantRange::ctlz(bool ZeroIsPoison) const { // Zero is either safe or not in the range. The output range is composed by // the result of countLeadingZero of the two extremes. return getNonEmpty(APInt(getBitWidth(), getUnsignedMax().countl_zero()), - APInt(getBitWidth(), getUnsignedMin().countl_zero() + 1)); + APInt(getBitWidth(), getUnsignedMin().countl_zero()) + 1); } static ConstantRange getUnsignedCountTrailingZerosRange(const APInt &Lower, @@ -1895,7 +1895,7 @@ ConstantRange ConstantRange::cttz(bool ZeroIsPoison) const { } if (isFullSet()) - return getNonEmpty(Zero, APInt(BitWidth, BitWidth + 1)); + return getNonEmpty(Zero, APInt(BitWidth, BitWidth) + 1); if (!isWrappedSet()) return getUnsignedCountTrailingZerosRange(Lower, Upper); // The range is wrapped. We decompose it into two ranges, [0, Upper) and @@ -1940,7 +1940,7 @@ ConstantRange ConstantRange::ctpop() const { unsigned BitWidth = getBitWidth(); APInt Zero = APInt::getZero(BitWidth); if (isFullSet()) - return getNonEmpty(Zero, APInt(BitWidth, BitWidth + 1)); + return getNonEmpty(Zero, APInt(BitWidth, BitWidth) + 1); if (!isWrappedSet()) return getUnsignedPopCountRange(Lower, Upper); // The range is wrapped. We decompose it into two ranges, [0, Upper) and diff --git a/llvm/lib/IR/Constants.cpp b/llvm/lib/IR/Constants.cpp index 70803c153d8c..510e15f348f8 100644 --- a/llvm/lib/IR/Constants.cpp +++ b/llvm/lib/IR/Constants.cpp @@ -933,7 +933,10 @@ Constant *ConstantInt::get(Type *Ty, uint64_t V, bool isSigned) { } ConstantInt *ConstantInt::get(IntegerType *Ty, uint64_t V, bool isSigned) { - return get(Ty->getContext(), APInt(Ty->getBitWidth(), V, isSigned)); + // TODO: Avoid implicit trunc? + // See https://github.com/llvm/llvm-project/issues/112510. + return get(Ty->getContext(), + APInt(Ty->getBitWidth(), V, isSigned, /*implicitTrunc=*/true)); } Constant *ConstantInt::get(Type *Ty, const APInt& V) { diff --git a/llvm/lib/IR/Core.cpp b/llvm/lib/IR/Core.cpp index cf7bbf6b2576..a051b333f40b 100644 --- a/llvm/lib/IR/Core.cpp +++ b/llvm/lib/IR/Core.cpp @@ -672,6 +672,14 @@ LLVMTypeRef LLVMInt64TypeInContext(LLVMContextRef C) { LLVMTypeRef LLVMInt128TypeInContext(LLVMContextRef C) { return (LLVMTypeRef) Type::getInt128Ty(*unwrap(C)); } +// EVM local begin +LLVMTypeRef LLVMInt256TypeInContext(LLVMContextRef C) { + return (LLVMTypeRef) Type::getInt256Ty(*unwrap(C)); +} +LLVMTypeRef LLVMInt512TypeInContext(LLVMContextRef C) { + return (LLVMTypeRef) Type::getInt512Ty(*unwrap(C)); +} +// EVM local end LLVMTypeRef LLVMIntTypeInContext(LLVMContextRef C, unsigned NumBits) { return wrap(IntegerType::get(*unwrap(C), NumBits)); } @@ -694,6 +702,14 @@ LLVMTypeRef LLVMInt64Type(void) { LLVMTypeRef LLVMInt128Type(void) { return LLVMInt128TypeInContext(LLVMGetGlobalContext()); } +// EVM local begin +LLVMTypeRef LLVMInt256Type(void) { + return LLVMInt256TypeInContext(LLVMGetGlobalContext()); +} +LLVMTypeRef LLVMInt512Type(void) { + return LLVMInt512TypeInContext(LLVMGetGlobalContext()); +} +// EVM local end LLVMTypeRef LLVMIntType(unsigned NumBits) { return LLVMIntTypeInContext(LLVMGetGlobalContext(), NumBits); } diff --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp index 20871982afb0..5cfa2de5b054 100644 --- a/llvm/lib/IR/Function.cpp +++ b/llvm/lib/IR/Function.cpp @@ -1346,7 +1346,13 @@ static void DecodeIITType(unsigned &NextElt, ArrayRef Infos, ArgInfo)); return; } + // EVM local begin + case IIT_I256: { + OutputTable.push_back(IITDescriptor::get(IITDescriptor::Integer, 256)); + return; + } } + // EVM local end llvm_unreachable("unhandled"); } diff --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp index 7a8cf8c23049..f271063d6796 100644 --- a/llvm/lib/IR/Instructions.cpp +++ b/llvm/lib/IR/Instructions.cpp @@ -39,6 +39,7 @@ #include "llvm/Support/Casting.h" #include "llvm/Support/CheckedArithmetic.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/KnownBits.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/ModRef.h" #include "llvm/Support/TypeSize.h" @@ -3826,6 +3827,35 @@ bool FCmpInst::compare(const APFloat &LHS, const APFloat &RHS, } } +std::optional ICmpInst::compare(const KnownBits &LHS, + const KnownBits &RHS, + ICmpInst::Predicate Pred) { + switch (Pred) { + case ICmpInst::ICMP_EQ: + return KnownBits::eq(LHS, RHS); + case ICmpInst::ICMP_NE: + return KnownBits::ne(LHS, RHS); + case ICmpInst::ICMP_UGE: + return KnownBits::uge(LHS, RHS); + case ICmpInst::ICMP_UGT: + return KnownBits::ugt(LHS, RHS); + case ICmpInst::ICMP_ULE: + return KnownBits::ule(LHS, RHS); + case ICmpInst::ICMP_ULT: + return KnownBits::ult(LHS, RHS); + case ICmpInst::ICMP_SGE: + return KnownBits::sge(LHS, RHS); + case ICmpInst::ICMP_SGT: + return KnownBits::sgt(LHS, RHS); + case ICmpInst::ICMP_SLE: + return KnownBits::sle(LHS, RHS); + case ICmpInst::ICMP_SLT: + return KnownBits::slt(LHS, RHS); + default: + llvm_unreachable("Unexpected non-integer predicate."); + } +} + CmpInst::Predicate CmpInst::getFlippedSignednessPredicate(Predicate pred) { assert(CmpInst::isRelational(pred) && "Call only with non-equality predicates!"); diff --git a/llvm/lib/IR/LLVMContextImpl.cpp b/llvm/lib/IR/LLVMContextImpl.cpp index 0a376179d609..db6d9fe1f207 100644 --- a/llvm/lib/IR/LLVMContextImpl.cpp +++ b/llvm/lib/IR/LLVMContextImpl.cpp @@ -42,7 +42,10 @@ LLVMContextImpl::LLVMContextImpl(LLVMContext &C) X86_FP80Ty(C, Type::X86_FP80TyID), FP128Ty(C, Type::FP128TyID), PPC_FP128Ty(C, Type::PPC_FP128TyID), X86_MMXTy(C, Type::X86_MMXTyID), X86_AMXTy(C, Type::X86_AMXTyID), Int1Ty(C, 1), Int8Ty(C, 8), - Int16Ty(C, 16), Int32Ty(C, 32), Int64Ty(C, 64), Int128Ty(C, 128) {} + Int16Ty(C, 16), Int32Ty(C, 32), Int64Ty(C, 64), Int128Ty(C, 128), + // EVM local begin + Int256Ty(C, 256), Int512Ty(C, 512) {} + // EVM local end LLVMContextImpl::~LLVMContextImpl() { #ifndef NDEBUG diff --git a/llvm/lib/IR/LLVMContextImpl.h b/llvm/lib/IR/LLVMContextImpl.h index 937a87d68617..c739fd12542a 100644 --- a/llvm/lib/IR/LLVMContextImpl.h +++ b/llvm/lib/IR/LLVMContextImpl.h @@ -1583,7 +1583,10 @@ class LLVMContextImpl { Type VoidTy, LabelTy, HalfTy, BFloatTy, FloatTy, DoubleTy, MetadataTy, TokenTy; Type X86_FP80Ty, FP128Ty, PPC_FP128Ty, X86_MMXTy, X86_AMXTy; - IntegerType Int1Ty, Int8Ty, Int16Ty, Int32Ty, Int64Ty, Int128Ty; + // EVM local begin + IntegerType Int1Ty, Int8Ty, Int16Ty, Int32Ty, Int64Ty, Int128Ty, Int256Ty, + Int512Ty; + // EVM local end std::unique_ptr TheNoneToken; diff --git a/llvm/lib/IR/Type.cpp b/llvm/lib/IR/Type.cpp index 5c61ad9f000b..d367154ebac2 100644 --- a/llvm/lib/IR/Type.cpp +++ b/llvm/lib/IR/Type.cpp @@ -254,6 +254,10 @@ IntegerType *Type::getInt16Ty(LLVMContext &C) { return &C.pImpl->Int16Ty; } IntegerType *Type::getInt32Ty(LLVMContext &C) { return &C.pImpl->Int32Ty; } IntegerType *Type::getInt64Ty(LLVMContext &C) { return &C.pImpl->Int64Ty; } IntegerType *Type::getInt128Ty(LLVMContext &C) { return &C.pImpl->Int128Ty; } +// EVM local begin +IntegerType *Type::getInt256Ty(LLVMContext &C) { return &C.pImpl->Int256Ty; } +IntegerType *Type::getInt512Ty(LLVMContext &C) { return &C.pImpl->Int512Ty; } +// EVM local end IntegerType *Type::getIntNTy(LLVMContext &C, unsigned N) { return IntegerType::get(C, N); @@ -271,6 +275,12 @@ Type *Type::getWasm_FuncrefTy(LLVMContext &C) { return Ty; } +// EVM local begin +PointerType *Type::getInt256PtrTy(LLVMContext &C, unsigned AS) { + return getInt256Ty(C)->getPointerTo(AS); +} +// EVM local end + //===----------------------------------------------------------------------===// // IntegerType Implementation //===----------------------------------------------------------------------===// @@ -287,6 +297,9 @@ IntegerType *IntegerType::get(LLVMContext &C, unsigned NumBits) { case 32: return cast(Type::getInt32Ty(C)); case 64: return cast(Type::getInt64Ty(C)); case 128: return cast(Type::getInt128Ty(C)); + // EVM local begin + case 256: return cast(Type::getInt256Ty(C)); + // EVM local end default: break; } diff --git a/llvm/lib/MC/ELFObjectWriter.cpp b/llvm/lib/MC/ELFObjectWriter.cpp index f958905a26aa..f29cc92394cf 100644 --- a/llvm/lib/MC/ELFObjectWriter.cpp +++ b/llvm/lib/MC/ELFObjectWriter.cpp @@ -984,6 +984,7 @@ uint64_t ELFWriter::writeObject(MCAssembler &Asm) { const uint64_t SecStart = align(Section.getAlign()); const MCSymbolELF *SignatureSymbol = Section.getGroup(); + writeSectionData(Asm, Section); uint64_t SecEnd = W.OS.tell(); @@ -1022,6 +1023,7 @@ uint64_t ELFWriter::writeObject(MCAssembler &Asm) { OWriter.TargetObjectWriter->addTargetSectionFlags(Ctx, Section); } + for (auto &[Group, Members] : Groups) { // Remember the offset into the file for this section. const uint64_t SecStart = align(Group->getAlign()); diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp index 9309d5987dc9..793dd5c4a3bc 100644 --- a/llvm/lib/MC/MCAsmStreamer.cpp +++ b/llvm/lib/MC/MCAsmStreamer.cpp @@ -1334,7 +1334,13 @@ void MCAsmStreamer::emitIntValueInHexWithPadding(uint64_t Value, void MCAsmStreamer::emitValueImpl(const MCExpr *Value, unsigned Size, SMLoc Loc) { + // EVM local begin +#if 0 + // EVM local end assert(Size <= 8 && "Invalid size"); + // EVM local begin +#endif + // EVM local end assert(getCurrentSectionOnly() && "Cannot emit contents before setting section!"); const char *Directive = nullptr; @@ -1346,6 +1352,11 @@ void MCAsmStreamer::emitValueImpl(const MCExpr *Value, unsigned Size, case 8: Directive = MAI->getData64bitsDirective(); break; } + // EVM local begin + if (Size == 32) + Directive = ".cell\t"; + // EVM local end + if (!Directive) { int64_t IntValue; if (!Value->evaluateAsAbsolute(IntValue)) diff --git a/llvm/lib/MC/MCExpr.cpp b/llvm/lib/MC/MCExpr.cpp index b42a668bce23..6f207bb23b9a 100644 --- a/llvm/lib/MC/MCExpr.cpp +++ b/llvm/lib/MC/MCExpr.cpp @@ -70,6 +70,10 @@ void MCExpr::print(raw_ostream &OS, const MCAsmInfo *MAI, bool InParens) const { return; } case MCExpr::SymbolRef: { + // EVM local begin + if (MAI && MAI->prependSymbolRefWithAt()) + // EVM local end + OS << '@'; const MCSymbolRefExpr &SRE = cast(*this); const MCSymbol &Sym = SRE.getSymbol(); // Parenthesize names that start with $ so that they don't look like @@ -396,6 +400,9 @@ StringRef MCSymbolRefExpr::getVariantKindName(VariantKind Kind) { case VK_VE_TLS_GD_LO32: return "tls_gd_lo"; case VK_VE_TPOFF_HI32: return "tpoff_hi"; case VK_VE_TPOFF_LO32: return "tpoff_lo"; + // EVM local begin + case VK_EVM_DATA: return "evm_data"; + // EVM local end // clang-format on } llvm_unreachable("Invalid variant kind"); @@ -534,6 +541,9 @@ MCSymbolRefExpr::getVariantKindForName(StringRef Name) { .Case("tls_gd_lo", VK_VE_TLS_GD_LO32) .Case("tpoff_hi", VK_VE_TPOFF_HI32) .Case("tpoff_lo", VK_VE_TPOFF_LO32) + // EVM local begin + .Case("evm_data", VK_EVM_DATA) + // EVM local end .Default(VK_Invalid); } diff --git a/llvm/lib/MC/MCObjectFileInfo.cpp b/llvm/lib/MC/MCObjectFileInfo.cpp index 6dadd9752646..bb7cc4e70b05 100644 --- a/llvm/lib/MC/MCObjectFileInfo.cpp +++ b/llvm/lib/MC/MCObjectFileInfo.cpp @@ -392,6 +392,10 @@ void MCObjectFileInfo::initELFMCObjectFileInfo(const Triple &T, bool Large) { TextSection = Ctx->getELFSection(".text", ELF::SHT_PROGBITS, ELF::SHF_EXECINSTR | ELF::SHF_ALLOC); + // EVM local begin + if (T.getArch() == Triple::evm) + TextSection->setAlignment(Align(1)); + // EVM local end DataSection = Ctx->getELFSection(".data", ELF::SHT_PROGBITS, ELF::SHF_WRITE | ELF::SHF_ALLOC); diff --git a/llvm/lib/ObjCopy/CMakeLists.txt b/llvm/lib/ObjCopy/CMakeLists.txt index 2d6bee94875f..ce9e6faaeb0d 100644 --- a/llvm/lib/ObjCopy/CMakeLists.txt +++ b/llvm/lib/ObjCopy/CMakeLists.txt @@ -81,3 +81,5 @@ add_llvm_component_library(LLVMObjCopy Support MC ) + +add_subdirectory(ObjCopyC) diff --git a/llvm/lib/ObjCopy/ObjCopyC/CMakeLists.txt b/llvm/lib/ObjCopy/ObjCopyC/CMakeLists.txt new file mode 100644 index 000000000000..271f8e40c26f --- /dev/null +++ b/llvm/lib/ObjCopy/ObjCopyC/CMakeLists.txt @@ -0,0 +1,14 @@ +add_llvm_component_library(LLVMObjCopyC + ObjCopyC.cpp + + DEPENDS + intrinsics_gen + + LINK_COMPONENTS + BinaryFormat + Core + MC + ObjCopy + Object + Support +) diff --git a/llvm/lib/ObjCopy/ObjCopyC/ObjCopyC.cpp b/llvm/lib/ObjCopy/ObjCopyC/ObjCopyC.cpp new file mode 100644 index 000000000000..20a0dae6d18e --- /dev/null +++ b/llvm/lib/ObjCopy/ObjCopyC/ObjCopyC.cpp @@ -0,0 +1,96 @@ +//===-------- ObjCopyC.cpp - ObjCopy Public 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 declares the C interface for the llv-objcopy functionality. +// +//===----------------------------------------------------------------------===// + +#include "llvm-c/Core.h" +#include "llvm-c/ObjCopy.h" +#include "llvm-c/Object.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ObjCopy/ConfigManager.h" +#include "llvm/ObjCopy/ObjCopy.h" +#include "llvm/Object/Binary.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/MemoryBuffer.h" + +#include +#include + +using namespace llvm; +using namespace object; +using namespace objcopy; + +#ifndef NDEBUG +static void checkSectionData(ObjectFile &File, StringRef SectionName, + StringRef SectionData) { + for (const object::SectionRef &Sec : File.sections()) { + StringRef CurSecName = cantFail(Sec.getName()); + if (CurSecName == SectionName) { + StringRef CurSecData = cantFail(Sec.getContents()); + assert(Sec.getSize() == SectionData.size()); + assert(memcmp(CurSecData.data(), SectionData.data(), + SectionData.size()) == 0); + } + } +} +#endif // NDEBUG + +LLVMBool LLVMAddMetadata(LLVMMemoryBufferRef InBuffer, const char *MetadataPtr, + uint64_t MetadataSize, LLVMMemoryBufferRef *OutBuffer, + char **ErrorMessage) { + if (!MetadataSize) { + *OutBuffer = nullptr; + return false; + } + + StringRef MDSectionName = ".metadata"; + + std::unique_ptr MDSectionBuffer = MemoryBuffer::getMemBuffer( + StringRef(MetadataPtr, MetadataSize), MDSectionName, false); + + Expected> InObjOrErr( + createBinary(unwrap(InBuffer)->getMemBufferRef())); + if (!InObjOrErr) + llvm_unreachable("Cannot create Binary object from the memory buffer"); + + ConfigManager Config; + Config.Common.AddSection.emplace_back(MDSectionName, + std::move(MDSectionBuffer)); + + // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) + SectionFlagsUpdate SFU = {MDSectionName, SecReadonly | SecNoload}; + [[maybe_unused]] auto It = + Config.Common.SetSectionFlags.try_emplace(SFU.Name, SFU); + assert(It.second); + + SmallString<0> BufferString; + raw_svector_ostream OutStream(BufferString); + if (Error Err = objcopy::executeObjcopyOnBinary(Config, *InObjOrErr.get(), + OutStream)) { + *ErrorMessage = strdup(toString(std::move(Err)).c_str()); + return true; + } + + // Create output buffer and copy there the object code. + llvm::StringRef Data = BufferString.str(); + *OutBuffer = LLVMCreateMemoryBufferWithMemoryRangeCopy(Data.data(), + Data.size(), "result"); + +#ifndef NDEBUG + // Check that copied file has the new section. + Expected> Result = + createBinary(llvm::unwrap(*OutBuffer)->getMemBufferRef()); + assert(Result && (*Result)->isObject()); + checkSectionData(*static_cast((*Result).get()), MDSectionName, + StringRef(MetadataPtr, MetadataSize)); +#endif // NDEBUG + + return false; +} diff --git a/llvm/lib/Object/ELF.cpp b/llvm/lib/Object/ELF.cpp index e47a40b8715d..226e45f1160d 100644 --- a/llvm/lib/Object/ELF.cpp +++ b/llvm/lib/Object/ELF.cpp @@ -181,6 +181,15 @@ StringRef llvm::object::getELFRelocationTypeName(uint32_t Machine, break; } break; + // EVM local begin + case ELF::EM_EVM: + switch (Type) { +#include "llvm/BinaryFormat/ELFRelocs/EVM.def" + default: + break; + } + break; + // EVM local end default: break; } diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp index 5dbb1e2f4987..610c4d0ef861 100644 --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -257,6 +257,9 @@ #include "llvm/Transforms/Scalar/MakeGuardsExplicit.h" #include "llvm/Transforms/Scalar/MemCpyOptimizer.h" #include "llvm/Transforms/Scalar/MergeICmps.h" +// EVM local begin +#include "llvm/Transforms/Scalar/MergeIdenticalBB.h" +// EVM local begin #include "llvm/Transforms/Scalar/MergedLoadStoreMotion.h" #include "llvm/Transforms/Scalar/NaryReassociate.h" #include "llvm/Transforms/Scalar/NewGVN.h" diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp index 6f36bdad780a..c0745590f1b0 100644 --- a/llvm/lib/Passes/PassBuilderPipelines.cpp +++ b/llvm/lib/Passes/PassBuilderPipelines.cpp @@ -1207,6 +1207,11 @@ PassBuilder::buildModuleSimplificationPipeline(OptimizationLevel Level, if (EnablePGOForceFunctionAttrs && PGOOpt) MPM.addPass(PGOForceFunctionAttrsPass(PGOOpt->ColdOptType)); + // EVM local begin + for (auto &C : PreInlinerOptimizationsEPCallbacks) + C(MPM, Level); + // EVM local end + MPM.addPass(AlwaysInlinerPass(/*InsertLifetimeIntrinsics=*/true)); if (EnableModuleInliner) @@ -2094,6 +2099,11 @@ ModulePassManager PassBuilder::buildO0DefaultPipeline(OptimizationLevel Level, invokePipelineEarlySimplificationEPCallbacks(MPM, Level); + // EVM local begin + for (auto &C : PreInlinerOptimizationsEPCallbacks) + C(MPM, Level); + // EVM local end + // Build a minimal pipeline based on the semantics required by LLVM, // which is just that always inlining occurs. Further, disable generating // lifetime intrinsics to avoid enabling further optimizations during @@ -2171,6 +2181,10 @@ AAManager PassBuilder::buildDefaultAAPipeline() { // The order in which these are registered determines their priority when // being queried. + // Add any target-specific alias analyses that should be run early. + if (TM) + TM->registerEarlyDefaultAliasAnalyses(AA); + // First we register the basic alias analysis that provides the majority of // per-function local AA logic. This is a stateless, on-demand local set of // AA techniques. diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def index 3b92823cd283..c66b9e2070fc 100644 --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -395,6 +395,9 @@ FUNCTION_PASS("make-guards-explicit", MakeGuardsExplicitPass()) FUNCTION_PASS("mem2reg", PromotePass()) FUNCTION_PASS("memcpyopt", MemCpyOptPass()) FUNCTION_PASS("memprof", MemProfilerPass()) +// EVM local begin +FUNCTION_PASS("mergebb", MergeIdenticalBBPass()) +// EVM local end FUNCTION_PASS("mergeicmps", MergeICmpsPass()) FUNCTION_PASS("mergereturn", UnifyFunctionExitNodesPass()) FUNCTION_PASS("move-auto-init", MoveAutoInitPass()) diff --git a/llvm/lib/Support/APInt.cpp b/llvm/lib/Support/APInt.cpp index 24e136fcb9c7..a5b1e8535418 100644 --- a/llvm/lib/Support/APInt.cpp +++ b/llvm/lib/Support/APInt.cpp @@ -234,7 +234,8 @@ APInt& APInt::operator-=(uint64_t RHS) { APInt APInt::operator*(const APInt& RHS) const { assert(BitWidth == RHS.BitWidth && "Bit widths must be the same"); if (isSingleWord()) - return APInt(BitWidth, U.VAL * RHS.U.VAL); + return APInt(BitWidth, U.VAL * RHS.U.VAL, /*isSigned=*/false, + /*implicitTrunc=*/true); APInt Result(getMemory(getNumWords()), getBitWidth()); tcMultiply(Result.U.pVal, U.pVal, RHS.U.pVal, getNumWords()); @@ -455,7 +456,8 @@ APInt APInt::extractBits(unsigned numBits, unsigned bitPosition) const { "Illegal bit extraction"); if (isSingleWord()) - return APInt(numBits, U.VAL >> bitPosition); + return APInt(numBits, U.VAL >> bitPosition, /*isSigned=*/false, + /*implicitTrunc=*/true); unsigned loBit = whichBit(bitPosition); unsigned loWord = whichWord(bitPosition); @@ -463,7 +465,8 @@ APInt APInt::extractBits(unsigned numBits, unsigned bitPosition) const { // Single word result extracting bits from a single word source. if (loWord == hiWord) - return APInt(numBits, U.pVal[loWord] >> loBit); + return APInt(numBits, U.pVal[loWord] >> loBit, /*isSigned=*/false, + /*implicitTrunc=*/true); // Extracting bits that start on a source word boundary can be done // as a fast memory copy. @@ -907,7 +910,8 @@ APInt APInt::trunc(unsigned width) const { assert(width <= BitWidth && "Invalid APInt Truncate request"); if (width <= APINT_BITS_PER_WORD) - return APInt(width, getRawData()[0]); + return APInt(width, getRawData()[0], /*isSigned=*/false, + /*implicitTrunc=*/true); if (width == BitWidth) return *this; @@ -955,7 +959,7 @@ APInt APInt::sext(unsigned Width) const { assert(Width >= BitWidth && "Invalid APInt SignExtend request"); if (Width <= APINT_BITS_PER_WORD) - return APInt(Width, SignExtend64(U.VAL, BitWidth)); + return APInt(Width, SignExtend64(U.VAL, BitWidth), /*isSigned=*/true); if (Width == BitWidth) return *this; diff --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt index f653379e3033..32887ce755f7 100644 --- a/llvm/lib/Support/CMakeLists.txt +++ b/llvm/lib/Support/CMakeLists.txt @@ -128,6 +128,16 @@ endif() add_subdirectory(BLAKE3) +# EVM local begin +find_package(Git REQUIRED) +execute_process(COMMAND "${GIT_EXECUTABLE}" rev-parse HEAD + WORKING_DIRECTORY "${LLVM_MAIN_SRC_DIR}" + OUTPUT_VARIABLE COMMIT_ID + RESULT_VARIABLE RES_ID + OUTPUT_STRIP_TRAILING_WHITESPACE) +set_source_files_properties(CommandLine.cpp PROPERTIES COMPILE_FLAGS -DEVM_LLVM_COMMIT_ID=\\"${COMMIT_ID}\\") +# EVM local end + add_llvm_component_library(LLVMSupport ABIBreak.cpp AMDGPUMetadata.cpp @@ -194,6 +204,7 @@ add_llvm_component_library(LLVMSupport IntEqClasses.cpp IntervalMap.cpp JSON.cpp + KECCAK.cpp KnownBits.cpp LEB128.cpp LineIterator.cpp diff --git a/llvm/lib/Support/CommandLine.cpp b/llvm/lib/Support/CommandLine.cpp index ecc487a17ccc..46f08d26537c 100644 --- a/llvm/lib/Support/CommandLine.cpp +++ b/llvm/lib/Support/CommandLine.cpp @@ -2537,6 +2537,11 @@ class VersionPrinter { OS << "LLVM (http://llvm.org/):\n "; #endif OS << PACKAGE_NAME << " version " << PACKAGE_VERSION << "\n "; +// EVM local begin +#ifdef EVM_LLVM_COMMIT_ID + OS << "Git: " << EVM_LLVM_COMMIT_ID << "\n "; +#endif +// EVM local end #if LLVM_IS_DEBUG_BUILD OS << "DEBUG build"; #else @@ -2842,3 +2847,10 @@ void LLVMParseCommandLineOptions(int argc, const char *const *argv, llvm::cl::ParseCommandLineOptions(argc, argv, StringRef(Overview), &llvm::nulls()); } + +// EVM local begin +int LLVMPrintCommitIDTo(char* Buf) { + return snprintf(Buf, sizeof(EVM_LLVM_COMMIT_ID), "%s", + EVM_LLVM_COMMIT_ID); +} +// EVM local end diff --git a/llvm/lib/Support/ErrorHandling.cpp b/llvm/lib/Support/ErrorHandling.cpp index cb42e28c04a8..59a60bbc9cef 100644 --- a/llvm/lib/Support/ErrorHandling.cpp +++ b/llvm/lib/Support/ErrorHandling.cpp @@ -45,6 +45,11 @@ static void *ErrorHandlerUserData = nullptr; static fatal_error_handler_t BadAllocErrorHandler = nullptr; static void *BadAllocErrorHandlerUserData = nullptr; +// EVM local begin +static evm_stack_error_handler_t EVMStackErrorHandler = nullptr; +static void *EVMStackErrorHandlerUserData = nullptr; +// EVM local end + #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 @@ -59,6 +64,9 @@ static void *BadAllocErrorHandlerUserData = nullptr; // builds. We can remove these ifdefs if that script goes away. static std::mutex ErrorHandlerMutex; static std::mutex BadAllocErrorHandlerMutex; +// EVM local begin +static std::mutex EVMStackErrorHandlerMutex; +// EVM local end #endif void llvm::install_fatal_error_handler(fatal_error_handler_t handler, @@ -234,6 +242,57 @@ void LLVMResetFatalErrorHandler() { remove_fatal_error_handler(); } +// EVM local begin +static void bindingsEVMStackErrorHandler(void *user_data, + uint64_t spill_region_size) { + LLVMStackErrorHandlerEVM handler = + LLVM_EXTENSION reinterpret_cast(user_data); + handler(spill_region_size); +} + +void LLVMInstallEVMStackErrorHandler(LLVMStackErrorHandlerEVM Handler) { +#if LLVM_ENABLE_THREADS == 1 + std::lock_guard Lock(EVMStackErrorHandlerMutex); +#endif + assert(!EVMStackErrorHandler && "Error handler already registered!\n"); + EVMStackErrorHandler = bindingsEVMStackErrorHandler; + EVMStackErrorHandlerUserData = + LLVM_EXTENSION reinterpret_cast(Handler); +} + +void LLVMResetEVMStackErrorHandler(void) { +#if LLVM_ENABLE_THREADS == 1 + std::lock_guard Lock(EVMStackErrorHandlerMutex); +#endif + EVMStackErrorHandler = nullptr; + EVMStackErrorHandlerUserData = nullptr; +} + +void llvm::report_evm_stack_error(const Twine &Reason, + uint64_t spillRegionSize) { + llvm::evm_stack_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(EVMStackErrorHandlerMutex); +#endif + handler = EVMStackErrorHandler; + handlerData = EVMStackErrorHandlerUserData; + } + + if (handler) + handler(handlerData, spillRegionSize); + + // If execution reaches this point, either the front-end handler wasn't + // set up, or it returned without calling abort()/exit(). + // In either case, fall back to the default error handler. + report_fatal_error(Reason); +} + +// EVM local end + #ifdef _WIN32 #define WIN32_NO_STATUS diff --git a/llvm/lib/Support/FileOutputBuffer.cpp b/llvm/lib/Support/FileOutputBuffer.cpp index 58a06a34e8cf..6611af22ee56 100644 --- a/llvm/lib/Support/FileOutputBuffer.cpp +++ b/llvm/lib/Support/FileOutputBuffer.cpp @@ -112,6 +112,43 @@ class InMemoryBuffer : public FileOutputBuffer { size_t BufferSize; unsigned Mode; }; + +// EVM local begin +// A FileOutputBuffer which keeps data in memory and writes to the final +// output stream on commit(). +// Note, the file management functionality, derived from FileOutputBuffer, +// is not supposed to be used. +class InMemoryStreamBuffer : public FileOutputBuffer { +public: + InMemoryStreamBuffer(MemoryBlock Buf, std::size_t BufSize, + raw_pwrite_stream &Out) + : FileOutputBuffer("<<"), Buffer(Buf), BufferSize(BufSize), + OutStream(Out) {} + + uint8_t *getBufferStart() const override { + return static_cast(Buffer.base()); + } + + uint8_t *getBufferEnd() const override { + return static_cast(Buffer.base()) + BufferSize; + } + + size_t getBufferSize() const override { return BufferSize; } + + Error commit() override { + OutStream << StringRef(static_cast(Buffer.base()), + BufferSize); + OutStream.flush(); + return Error::success(); + } + +private: + // Buffer may actually contain a larger memory block than BufferSize + OwningMemoryBlock Buffer; + size_t BufferSize; + raw_pwrite_stream &OutStream; +}; +// EVM local end } // namespace static Expected> @@ -154,6 +191,19 @@ createOnDiskBuffer(StringRef Path, size_t Size, unsigned Mode) { std::move(MappedFile)); } +// EVM local begin +// Create an instance of InMemoryStreamBuffer. +Expected> +FileOutputBuffer::create(size_t Size, raw_pwrite_stream &Out) { + std::error_code EC; + MemoryBlock MB = Memory::allocateMappedMemory( + Size, nullptr, sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC); + if (EC) + return errorCodeToError(EC); + return std::make_unique(MB, Size, Out); +} +// EVM local end + // Create an instance of FileOutputBuffer. Expected> FileOutputBuffer::create(StringRef Path, size_t Size, unsigned Flags) { diff --git a/llvm/lib/Support/KECCAK.cpp b/llvm/lib/Support/KECCAK.cpp new file mode 100644 index 000000000000..a2714286976b --- /dev/null +++ b/llvm/lib/Support/KECCAK.cpp @@ -0,0 +1,287 @@ +//===-- KECCAK.cpp - Private copy of the KECCAK 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 code was taken from public domain +// (https://github.com/XKCP/XKCP/blob/master/Standalone/CompactFIPS202/ +// C/Keccak-readable-and-compact.c), and modified by wrapping it in a +// C++ interface for LLVM, changing formatting, and removing +// unnecessary comments. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/KECCAK.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Endian.h" +#include "llvm/TargetParser/Host.h" +#include + +using namespace llvm; + +namespace { + +#if defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) && BYTE_ORDER == LITTLE_ENDIAN +#define KECCAK_LITTLE_ENDIAN +#endif + +#ifndef KECCAK_LITTLE_ENDIAN +/// +/// Function to load a 64-bit value using the little-endian (LE) convention. +/// On a LE platform, this could be greatly simplified using a cast. +/// +uint64_t load64(const uint8_t *x) { + int i; + uint64_t u = 0; + for (i = 7; i >= 0; --i) { + u <<= 8; + u |= x[i]; + } + return u; +} + +/// +/// Function to store a 64-bit value using the little-endian (LE) convention. +/// On a LE platform, this could be greatly simplified using a cast. +/// +void store64(uint8_t *x, uint64_t u) { + unsigned int i; + for (i = 0; i < 8; ++i) { + x[i] = u; + u >>= 8; + } +} + +/// +/// Function to XOR into a 64-bit value using the little-endian (LE) convention. +/// On a LE platform, this could be greatly simplified using a cast. +/// +void xor64(uint8_t *x, uint64_t u) { + unsigned int i; + for (i = 0; i < 8; ++i) { + x[i] ^= u; + u >>= 8; + } +} +#endif + +typedef uint64_t tKeccakLane; + +/// +/// A readable and compact implementation of the Keccak-f[1600] permutation. +/// +#define ROL64(a, offset) \ + ((((uint64_t)a) << offset) ^ (((uint64_t)a) >> (64 - offset))) +#define i(x, y) ((x) + 5 * (y)) + +#ifdef KECCAK_LITTLE_ENDIAN +#define readLane(x, y) (((tKeccakLane *)state)[i(x, y)]) +#define writeLane(x, y, lane) (((tKeccakLane *)state)[i(x, y)]) = (lane) +#define XORLane(x, y, lane) (((tKeccakLane *)state)[i(x, y)]) ^= (lane) +#else +#define readLane(x, y) load64((uint8_t *)state + sizeof(tKeccakLane) * i(x, y)) +#define writeLane(x, y, lane) \ + store64((uint8_t *)state + sizeof(tKeccakLane) * i(x, y), lane) +#define XORLane(x, y, lane) \ + xor64((uint8_t *)state + sizeof(tKeccakLane) * i(x, y), lane) +#endif + +/// +/// Function that computes the linear feedback shift register (LFSR) used to +/// define the round constants (see [Keccak Reference, Section 1.2]). +/// +int LFSR86540(uint8_t *LFSR) { + const int result = ((*LFSR) & 0x01) != 0; + if (((*LFSR) & 0x80) != 0) + // Primitive polynomial over GF(2): x^8+x^6+x^5+x^4+1 + (*LFSR) = ((*LFSR) << 1) ^ 0x71; + else + (*LFSR) <<= 1; + return result; +} + +/// +/// Function that computes the Keccak-f[1600] permutation on the given state. +/// +void KeccakF1600_StatePermute(void *state) { + unsigned int round, x, y, j, t; + uint8_t LFSRstate = 0x01; + + for (round = 0; round < 24; round++) { + { + // θ step (see [Keccak Reference, Section 2.3.2]) + tKeccakLane C[5], D; + // Compute the parity of the columns + for (x = 0; x < 5; x++) + C[x] = readLane(x, 0) ^ readLane(x, 1) ^ readLane(x, 2) ^ + readLane(x, 3) ^ readLane(x, 4); + for (x = 0; x < 5; x++) { + /* Compute the θ effect for a given column */ + D = C[(x + 4) % 5] ^ ROL64(C[(x + 1) % 5], 1); + /* Add the θ effect to the whole column */ + for (y = 0; y < 5; y++) + XORLane(x, y, D); + } + } + + { + // ρ and π steps (see [Keccak Reference, Sections 2.3.3 and 2.3.4]) + tKeccakLane current, temp; + // Start at coordinates (1 0) + x = 1; + y = 0; + current = readLane(x, y); + // Iterate over ((0 1)(2 3))^t * (1 0) for 0 ≤ t ≤ 23 + for (t = 0; t < 24; t++) { + // Compute the rotation constant r = (t+1)(t+2)/2 + const unsigned int r = ((t + 1) * (t + 2) / 2) % 64; + // Compute ((0 1)(2 3)) * (x y) + const unsigned int Y = (2 * x + 3 * y) % 5; + x = y; + y = Y; + // Swap current and state(x,y), and rotate + temp = readLane(x, y); + writeLane(x, y, ROL64(current, r)); + current = temp; + } + } + + { + // χ step (see [Keccak Reference, Section 2.3.1]) + tKeccakLane temp[5]; + for (y = 0; y < 5; y++) { + // Take a copy of the plane + for (x = 0; x < 5; x++) + temp[x] = readLane(x, y); + // Compute χ on the plane + for (x = 0; x < 5; x++) + writeLane(x, y, temp[x] ^ ((~temp[(x + 1) % 5]) & temp[(x + 2) % 5])); + } + } + + { + // ι step (see [Keccak Reference, Section 2.3.5]) + for (j = 0; j < 7; j++) { + const unsigned int bitPosition = (1 << j) - 1; // 2^j-1 + if (LFSR86540(&LFSRstate)) + XORLane(0, 0, (tKeccakLane)1 << bitPosition); + } + } + } +} + +/// +/// Function to compute the Keccak[r, c] sponge function over a given input +/// using the Keccak-f[1600] permutation. +/// +/// @param rate +/// The value of the rate r. +/// @param capacity +/// The value of the capacity c. +/// @param input +/// Pointer to the input message. +/// @param inputByteLen +/// The number of input bytes provided in the input message. +/// @param delimitedSuffix +/// Bits that will be automatically appended to the end of the input message, +/// as in domain separation. This is a byte containing from 0 to 7 bits. +/// These n bits must be in the least significant bit positions and +/// must be delimited with a bit 1 at position n +/// (counting from 0=LSB to 7=MSB) and followed by bits 0 from position +/// n+1 to position 7. +/// Some examples: +/// - If no bits are to be appended, then @a delimitedSuffix must be 0x01. +/// - If the 2-bit sequence 0,1 is to be appended (as for SHA3-*), +/// @a delimitedSuffix must be 0x06. +/// - If the 4-bit sequence 1,1,1,1 is to be appended (as for SHAKE*), +/// @a delimitedSuffix must be 0x1F. +/// - If the 7-bit sequence 1,1,0,1,0,0,0 is to be absorbed, +/// @a delimitedSuffix must be 0x8B. +/// @param output +/// Pointer to the buffer where to store the output. +/// @param outputByteLen +/// The number of output bytes desired. +/// @pre +/// One must have r+c=1600 and the rate a multiple of 8 bits in this +/// implementation. +/// +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +void KeccakSponge(unsigned int rate, unsigned int capacity, + const unsigned char *input, + unsigned long long int inputByteLen, + unsigned char delimitedSuffix, unsigned char *output, + unsigned long long int outputByteLen) { + uint8_t state[200]; + const unsigned int rateInBytes = rate / 8; + unsigned int blockSize = 0; + unsigned int i; + + if (((rate + capacity) != 1600) || ((rate % 8) != 0)) + return; + + // Initialize the state + std::memset(state, 0, sizeof(state)); + + // Absorb all the input blocks + while (inputByteLen > 0) { + blockSize = MIN(inputByteLen, rateInBytes); + for (i = 0; i < blockSize; i++) + state[i] ^= input[i]; + input += blockSize; + inputByteLen -= blockSize; + + if (blockSize == rateInBytes) { + KeccakF1600_StatePermute(state); + blockSize = 0; + } + } + + // Do the padding and switch to the squeezing phase + // Absorb the last few bits and add the first bit of padding + // (which coincides with the delimiter in delimitedSuffix) + state[blockSize] ^= delimitedSuffix; + // If the first bit of padding is at position rate-1, we need a whole new + // block for the second bit of padding + if (((delimitedSuffix & 0x80) != 0) && (blockSize == (rateInBytes - 1))) + KeccakF1600_StatePermute(state); + // Add the second bit of padding + state[rateInBytes - 1] ^= 0x80; + // Switch to the squeezing phase + KeccakF1600_StatePermute(state); + + // Squeeze out all the output blocks + while (outputByteLen > 0) { + blockSize = MIN(outputByteLen, rateInBytes); + std::memcpy(output, state, blockSize); + output += blockSize; + outputByteLen -= blockSize; + + if (outputByteLen > 0) + KeccakF1600_StatePermute(state); + } +} + +} // end anonymous namespace + +/// +/// Function to compute KECCAK-256 hash on the input message. The output length +/// is fixed to 32 bytes. +/// +std::array KECCAK::KECCAK_256(ArrayRef Data) { + std::array Result; + KeccakSponge(1088, 512, Data.data(), Data.size(), 0x01, Result.data(), 32); + return Result; +} + +/// +/// Function to compute KECCAK-256 hash on the input string message. The output +/// length is fixed to 32 bytes. +/// +std::array KECCAK::KECCAK_256(StringRef Str) { + return KECCAK_256( + ArrayRef(reinterpret_cast(Str.data()), Str.size())); +} diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp index ef2789e96213..9c57d8895ab5 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -2329,10 +2329,11 @@ void AArch64TargetLowering::computeKnownBitsForTargetNode( } case AArch64ISD::BICi: { // Compute the bit cleared value. - uint64_t Mask = - ~(Op->getConstantOperandVal(1) << Op->getConstantOperandVal(2)); + APInt Mask = + ~(Op->getConstantOperandAPInt(1) << Op->getConstantOperandAPInt(2)) + .trunc(Known.getBitWidth()); Known = DAG.computeKnownBits(Op->getOperand(0), Depth + 1); - Known &= KnownBits::makeConstant(APInt(Known.getBitWidth(), Mask)); + Known &= KnownBits::makeConstant(Mask); break; } case AArch64ISD::VLSHR: { @@ -12504,7 +12505,8 @@ static bool isEXTMask(ArrayRef M, EVT VT, bool &ReverseEXT, // Benefit form APInt to handle overflow when calculating expected element. unsigned NumElts = VT.getVectorNumElements(); unsigned MaskBits = APInt(32, NumElts * 2).logBase2(); - APInt ExpectedElt = APInt(MaskBits, *FirstRealElt + 1); + APInt ExpectedElt = APInt(MaskBits, *FirstRealElt + 1, /*isSigned=*/false, + /*implicitTrunc=*/true); // The following shuffle indices must be the successive elements after the // first real element. bool FoundWrongElt = std::any_of(FirstRealElt + 1, M.end(), [&](int Elt) { @@ -13851,9 +13853,9 @@ static SDValue NormalizeBuildVector(SDValue Op, // (with operands cast to integers), then the only possibilities // are constants and UNDEFs. if (auto *CstLane = dyn_cast(Lane)) { - APInt LowBits(EltTy.getSizeInBits(), - CstLane->getZExtValue()); - Lane = DAG.getConstant(LowBits.getZExtValue(), dl, MVT::i32); + Lane = DAG.getConstant( + CstLane->getAPIntValue().trunc(EltTy.getSizeInBits()).getZExtValue(), + dl, MVT::i32); } else if (Lane.getNode()->isUndef()) { Lane = DAG.getUNDEF(MVT::i32); } else { @@ -23140,7 +23142,7 @@ static bool findMoreOptimalIndexType(const MaskedGatherScatterSDNode *N, EVT NewIndexVT = IndexVT.changeVectorElementType(MVT::i32); // Stride does not scale explicitly by 'Scale', because it happens in // the gather/scatter addressing mode. - Index = DAG.getStepVector(SDLoc(N), NewIndexVT, APInt(32, Stride)); + Index = DAG.getStepVector(SDLoc(N), NewIndexVT, APInt(32, Stride, true)); return true; } @@ -28084,7 +28086,7 @@ static SDValue GenerateFixedLengthSVETBL(SDValue Op, SDValue Op1, SDValue Op2, unsigned BitsPerElt = VTOp1.getVectorElementType().getSizeInBits(); unsigned IndexLen = MinSVESize / BitsPerElt; unsigned ElementsPerVectorReg = VTOp1.getVectorNumElements(); - uint64_t MaxOffset = APInt(BitsPerElt, -1, false).getZExtValue(); + uint64_t MaxOffset = maxUIntN(BitsPerElt); EVT MaskEltType = VTOp1.getVectorElementType().changeTypeToInteger(); EVT MaskType = EVT::getVectorVT(*DAG.getContext(), MaskEltType, IndexLen); bool MinMaxEqual = (MinSVESize == MaxSVESize); @@ -28427,16 +28429,14 @@ bool AArch64TargetLowering::SimplifyDemandedBitsForTargetNode( KnownBits KnownOp0 = TLO.DAG.computeKnownBits(Op0, OriginalDemandedElts, Depth + 1); // Op0 &= ~(ConstantOperandVal(1) << ConstantOperandVal(2)) - uint64_t BitsToClear = Op->getConstantOperandVal(1) - << Op->getConstantOperandVal(2); + APInt BitsToClear = + (Op->getConstantOperandAPInt(1) << Op->getConstantOperandAPInt(2)) + .trunc(KnownOp0.getBitWidth()); APInt AlreadyZeroedBitsToClear = BitsToClear & KnownOp0.Zero; - if (APInt(Known.getBitWidth(), BitsToClear) - .isSubsetOf(AlreadyZeroedBitsToClear)) + if (BitsToClear.isSubsetOf(AlreadyZeroedBitsToClear)) return TLO.CombineTo(Op, Op0); - Known = KnownOp0 & - KnownBits::makeConstant(APInt(Known.getBitWidth(), ~BitsToClear)); - + Known = KnownOp0 & KnownBits::makeConstant(~BitsToClear); return false; } case ISD::INTRINSIC_WO_CHAIN: { diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp index 805684ef69a5..243fcffb43eb 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp @@ -4451,7 +4451,9 @@ void AArch64InstrInfo::copyGPRRegTuple(MachineBasicBlock &MBB, void AArch64InstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, + bool RenamableSrc) const { if (AArch64::GPR32spRegClass.contains(DestReg) && (AArch64::GPR32spRegClass.contains(SrcReg) || SrcReg == AArch64::WZR)) { const TargetRegisterInfo *TRI = &getRegisterInfo(); diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.h b/llvm/lib/Target/AArch64/AArch64InstrInfo.h index 69ee0a70765e..f217f2d32c4a 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.h +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.h @@ -338,7 +338,8 @@ class AArch64InstrInfo final : public AArch64GenInstrInfo { llvm::ArrayRef Indices) const; void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, Register SrcReg, diff --git a/llvm/lib/Target/AMDGPU/R600InstrInfo.cpp b/llvm/lib/Target/AMDGPU/R600InstrInfo.cpp index a3159944a2ad..f6ca15eefd70 100644 --- a/llvm/lib/Target/AMDGPU/R600InstrInfo.cpp +++ b/llvm/lib/Target/AMDGPU/R600InstrInfo.cpp @@ -40,7 +40,8 @@ bool R600InstrInfo::isVector(const MachineInstr &MI) const { void R600InstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { unsigned VectorComponents = 0; if ((R600::R600_Reg128RegClass.contains(DestReg) || R600::R600_Reg128VerticalRegClass.contains(DestReg)) && diff --git a/llvm/lib/Target/AMDGPU/R600InstrInfo.h b/llvm/lib/Target/AMDGPU/R600InstrInfo.h index f720e4656348..c767ecb24590 100644 --- a/llvm/lib/Target/AMDGPU/R600InstrInfo.h +++ b/llvm/lib/Target/AMDGPU/R600InstrInfo.h @@ -73,7 +73,8 @@ class R600InstrInfo final : public R600GenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; bool isLegalToSplitMBBAt(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI) const override; diff --git a/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp b/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp index 27b8c1b17422..ad2efd7e108d 100644 --- a/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp +++ b/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp @@ -794,7 +794,8 @@ static void expandSGPRCopy(const SIInstrInfo &TII, MachineBasicBlock &MBB, void SIInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { const TargetRegisterClass *RC = RI.getPhysRegBaseClass(DestReg); unsigned Size = RI.getRegSizeInBits(*RC); const TargetRegisterClass *SrcRC = RI.getPhysRegBaseClass(SrcReg); @@ -3461,7 +3462,8 @@ bool SIInstrInfo::foldImmediate(MachineInstr &UseMI, MachineInstr &DefMI, : AMDGPU::V_MOV_B32_e32 : Is64Bit ? AMDGPU::S_MOV_B64_IMM_PSEUDO : AMDGPU::S_MOV_B32; - APInt Imm(Is64Bit ? 64 : 32, getImmFor(UseMI.getOperand(1))); + APInt Imm(Is64Bit ? 64 : 32, getImmFor(UseMI.getOperand(1)), + /*isSigned=*/true, /*implicitTrunc=*/true); if (RI.isAGPR(*MRI, DstReg)) { if (Is64Bit || !isInlineConstant(Imm)) diff --git a/llvm/lib/Target/AMDGPU/SIInstrInfo.h b/llvm/lib/Target/AMDGPU/SIInstrInfo.h index 91855fb14f6f..d8eb38b2ea3a 100644 --- a/llvm/lib/Target/AMDGPU/SIInstrInfo.h +++ b/llvm/lib/Target/AMDGPU/SIInstrInfo.h @@ -255,7 +255,8 @@ class SIInstrInfo final : public AMDGPUGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void materializeImmediate(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, diff --git a/llvm/lib/Target/AMDGPU/SIShrinkInstructions.cpp b/llvm/lib/Target/AMDGPU/SIShrinkInstructions.cpp index 79bcf5e8cd30..610226cdd49b 100644 --- a/llvm/lib/Target/AMDGPU/SIShrinkInstructions.cpp +++ b/llvm/lib/Target/AMDGPU/SIShrinkInstructions.cpp @@ -203,12 +203,12 @@ static unsigned canModifyToInlineImmOp32(const SIInstrInfo *TII, // that SCC is not live as S_NOT_B32 clobbers it. It's probably not worth // it, as the reasonable values are already covered by s_movk_i32. ModifiedImm = ~SrcImm; - if (TII->isInlineConstant(APInt(32, ModifiedImm))) + if (TII->isInlineConstant(APInt(32, ModifiedImm, true))) return AMDGPU::V_NOT_B32_e32; } ModifiedImm = reverseBits(SrcImm); - if (TII->isInlineConstant(APInt(32, ModifiedImm))) + if (TII->isInlineConstant(APInt(32, ModifiedImm, true))) return Scalar ? AMDGPU::S_BREV_B32 : AMDGPU::V_BFREV_B32_e32; return 0; diff --git a/llvm/lib/Target/ARC/ARCInstrInfo.cpp b/llvm/lib/Target/ARC/ARCInstrInfo.cpp index 9b5e45cb5fe9..78db68fca305 100644 --- a/llvm/lib/Target/ARC/ARCInstrInfo.cpp +++ b/llvm/lib/Target/ARC/ARCInstrInfo.cpp @@ -281,7 +281,8 @@ unsigned ARCInstrInfo::removeBranch(MachineBasicBlock &MBB, void ARCInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { assert(ARC::GPR32RegClass.contains(SrcReg) && "Only GPR32 src copy supported."); assert(ARC::GPR32RegClass.contains(DestReg) && diff --git a/llvm/lib/Target/ARC/ARCInstrInfo.h b/llvm/lib/Target/ARC/ARCInstrInfo.h index 1875aafbde82..e25f99025226 100644 --- a/llvm/lib/Target/ARC/ARCInstrInfo.h +++ b/llvm/lib/Target/ARC/ARCInstrInfo.h @@ -65,7 +65,8 @@ class ARCInstrInfo : public ARCGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, Register SrcReg, diff --git a/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp b/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp index bf6bb8c295ef..0eaf2f167d51 100644 --- a/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp +++ b/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp @@ -892,7 +892,9 @@ void llvm::addPredicatedMveVpredROp(MachineInstrBuilder &MIB, void ARMBaseInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, + bool RenamableSrc) const { bool GPRDest = ARM::GPRRegClass.contains(DestReg); bool GPRSrc = ARM::GPRRegClass.contains(SrcReg); diff --git a/llvm/lib/Target/ARM/ARMBaseInstrInfo.h b/llvm/lib/Target/ARM/ARMBaseInstrInfo.h index a3c2684ac1fb..2ae00dc18c4f 100644 --- a/llvm/lib/Target/ARM/ARMBaseInstrInfo.h +++ b/llvm/lib/Target/ARM/ARMBaseInstrInfo.h @@ -209,7 +209,8 @@ class ARMBaseInstrInfo : public ARMGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, Register SrcReg, diff --git a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp index e54314cc7d00..aadbdeb88344 100644 --- a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp +++ b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp @@ -1158,7 +1158,8 @@ class ARMOperand : public MCParsedAsmOperand { bool isFPImm() const { if (!isImm()) return false; const MCConstantExpr *CE = dyn_cast(getImm()); - if (!CE) return false; + if (!CE || !isUInt<32>(CE->getValue())) + return false; int Val = ARM_AM::getFP32Imm(APInt(32, CE->getValue())); return Val != -1; } diff --git a/llvm/lib/Target/ARM/Thumb1InstrInfo.cpp b/llvm/lib/Target/ARM/Thumb1InstrInfo.cpp index 396328e958d1..a38aa3de40d9 100644 --- a/llvm/lib/Target/ARM/Thumb1InstrInfo.cpp +++ b/llvm/lib/Target/ARM/Thumb1InstrInfo.cpp @@ -42,7 +42,8 @@ unsigned Thumb1InstrInfo::getUnindexedOpcode(unsigned Opc) const { void Thumb1InstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { // Need to check the arch. MachineFunction &MF = *MBB.getParent(); const ARMSubtarget &st = MF.getSubtarget(); diff --git a/llvm/lib/Target/ARM/Thumb1InstrInfo.h b/llvm/lib/Target/ARM/Thumb1InstrInfo.h index 984bec4e6449..84241fb8a9a6 100644 --- a/llvm/lib/Target/ARM/Thumb1InstrInfo.h +++ b/llvm/lib/Target/ARM/Thumb1InstrInfo.h @@ -39,7 +39,8 @@ class Thumb1InstrInfo : public ARMBaseInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, Register SrcReg, bool isKill, int FrameIndex, diff --git a/llvm/lib/Target/ARM/Thumb2InstrInfo.cpp b/llvm/lib/Target/ARM/Thumb2InstrInfo.cpp index 09bcd3109f2b..d1e07b6703a5 100644 --- a/llvm/lib/Target/ARM/Thumb2InstrInfo.cpp +++ b/llvm/lib/Target/ARM/Thumb2InstrInfo.cpp @@ -151,7 +151,8 @@ Thumb2InstrInfo::optimizeSelect(MachineInstr &MI, void Thumb2InstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { // Handle SPR, DPR, and QPR copies. if (!ARM::GPRRegClass.contains(DestReg, SrcReg)) return ARMBaseInstrInfo::copyPhysReg(MBB, I, DL, DestReg, SrcReg, KillSrc); diff --git a/llvm/lib/Target/ARM/Thumb2InstrInfo.h b/llvm/lib/Target/ARM/Thumb2InstrInfo.h index 8915da8c5bf3..70ee3270e64a 100644 --- a/llvm/lib/Target/ARM/Thumb2InstrInfo.h +++ b/llvm/lib/Target/ARM/Thumb2InstrInfo.h @@ -39,7 +39,8 @@ class Thumb2InstrInfo : public ARMBaseInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, Register SrcReg, diff --git a/llvm/lib/Target/AVR/AVRInstrInfo.cpp b/llvm/lib/Target/AVR/AVRInstrInfo.cpp index 18b7365fc5aa..7b0f8d74e77c 100644 --- a/llvm/lib/Target/AVR/AVRInstrInfo.cpp +++ b/llvm/lib/Target/AVR/AVRInstrInfo.cpp @@ -42,7 +42,8 @@ AVRInstrInfo::AVRInstrInfo(AVRSubtarget &STI) void AVRInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { const AVRRegisterInfo &TRI = *STI.getRegisterInfo(); unsigned Opc; diff --git a/llvm/lib/Target/AVR/AVRInstrInfo.h b/llvm/lib/Target/AVR/AVRInstrInfo.h index 28c0e0319d46..8eb4292f2422 100644 --- a/llvm/lib/Target/AVR/AVRInstrInfo.h +++ b/llvm/lib/Target/AVR/AVRInstrInfo.h @@ -75,7 +75,8 @@ class AVRInstrInfo : public AVRGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, Register SrcReg, bool isKill, int FrameIndex, diff --git a/llvm/lib/Target/BPF/BPFInstrInfo.cpp b/llvm/lib/Target/BPF/BPFInstrInfo.cpp index 2209f1f1462b..1b07e7ffc0d3 100644 --- a/llvm/lib/Target/BPF/BPFInstrInfo.cpp +++ b/llvm/lib/Target/BPF/BPFInstrInfo.cpp @@ -31,7 +31,8 @@ BPFInstrInfo::BPFInstrInfo() void BPFInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { if (BPF::GPRRegClass.contains(DestReg, SrcReg)) BuildMI(MBB, I, DL, get(BPF::MOV_rr), DestReg) .addReg(SrcReg, getKillRegState(KillSrc)); diff --git a/llvm/lib/Target/BPF/BPFInstrInfo.h b/llvm/lib/Target/BPF/BPFInstrInfo.h index 354aca1bd2f9..a6b6fd7dc4d9 100644 --- a/llvm/lib/Target/BPF/BPFInstrInfo.h +++ b/llvm/lib/Target/BPF/BPFInstrInfo.h @@ -31,7 +31,8 @@ class BPFInstrInfo : public BPFGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; bool expandPostRAPseudo(MachineInstr &MI) const override; diff --git a/llvm/lib/Target/CSKY/CSKYInstrInfo.cpp b/llvm/lib/Target/CSKY/CSKYInstrInfo.cpp index 6baca84ab3d0..a2bb87bcaaf9 100644 --- a/llvm/lib/Target/CSKY/CSKYInstrInfo.cpp +++ b/llvm/lib/Target/CSKY/CSKYInstrInfo.cpp @@ -478,7 +478,8 @@ void CSKYInstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB, void CSKYInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { if (CSKY::GPRRegClass.contains(SrcReg) && CSKY::CARRYRegClass.contains(DestReg)) { if (STI.hasE2()) { diff --git a/llvm/lib/Target/CSKY/CSKYInstrInfo.h b/llvm/lib/Target/CSKY/CSKYInstrInfo.h index 4e3866b1188c..54c1106310d8 100644 --- a/llvm/lib/Target/CSKY/CSKYInstrInfo.h +++ b/llvm/lib/Target/CSKY/CSKYInstrInfo.h @@ -55,7 +55,8 @@ class CSKYInstrInfo : public CSKYGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; unsigned insertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB, MachineBasicBlock *FBB, ArrayRef Cond, diff --git a/llvm/lib/Target/EVM/CMakeLists.txt b/llvm/lib/Target/EVM/CMakeLists.txt new file mode 100644 index 000000000000..81036de5b922 --- /dev/null +++ b/llvm/lib/Target/EVM/CMakeLists.txt @@ -0,0 +1,104 @@ +add_llvm_component_group(EVM) + +set(LLVM_TARGET_DEFINITIONS EVM.td) + +tablegen(LLVM EVMGenAsmWriter.inc -gen-asm-writer) +tablegen(LLVM EVMGenDAGISel.inc -gen-dag-isel) +tablegen(LLVM EVMGenDisassemblerTables.inc -gen-disassembler) +tablegen(LLVM EVMGenInstrInfo.inc -gen-instr-info) +tablegen(LLVM EVMGenMCCodeEmitter.inc -gen-emitter) +tablegen(LLVM EVMGenRegisterInfo.inc -gen-register-info) +tablegen(LLVM EVMGenSubtargetInfo.inc -gen-subtarget) + +add_public_tablegen_target(EVMCommonTableGen) + +set(EVM_STDLIB_PATH_IN ${CMAKE_CURRENT_SOURCE_DIR}/evm-stdlib.ll) +set(EVM_STDLIB_PATH_OUT ${CMAKE_CURRENT_BINARY_DIR}/EVMStdLib.inc) + +file( + GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/EVMGenRuntime.cmake" + CONTENT [[ +file(READ ${RT_PATH_IN} RT_CONTENT) +set (RT_CONTENT "R\"(${RT_CONTENT})\"") +file(WRITE ${RT_PATH_OUT} ${RT_CONTENT}) + ]] +) + +add_custom_command( + OUTPUT "${EVM_STDLIB_PATH_OUT}" + COMMAND ${CMAKE_COMMAND} -DRT_PATH_IN=${EVM_STDLIB_PATH_IN} + -DRT_PATH_OUT=${EVM_STDLIB_PATH_OUT} + -P ${CMAKE_CURRENT_BINARY_DIR}/EVMGenRuntime.cmake + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/EVMGenRuntime.cmake + ${EVM_STDLIB_PATH_IN} +) +add_custom_target(EVMStdLib DEPENDS "${EVM_STDLIB_PATH_OUT}") + +add_llvm_target(EVMCodeGen + EVMAliasAnalysis.cpp + EVMAllocaHoisting.cpp + EVMAlwaysInline.cpp + EVMArgumentMove.cpp + EVMAsmPrinter.cpp + EVMBackwardPropagationStackification.cpp + EVMCalculateModuleSize.cpp + EVMConstantUnfolding.cpp + EVMCodegenPrepare.cpp + EVMFinalizeStackFrames.cpp + EVMFrameLowering.cpp + EVMISelDAGToDAG.cpp + EVMISelLowering.cpp + EVMInstrInfo.cpp + EVMLinkRuntime.cpp + EVMLowerIntrinsics.cpp + EVMLowerJumpUnless.cpp + EVMMachineFunctionInfo.cpp + EVMMarkRecursiveFunctions.cpp + EVMMCInstLower.cpp + EVMOptimizeLiveIntervals.cpp + EVMPeephole.cpp + EVMRegisterInfo.cpp + EVMSHA3ConstFolding.cpp + EVMSingleUseExpression.cpp + EVMSplitCriticalEdges.cpp + EVMStackSolver.cpp + EVMStackModel.cpp + EVMStackifyCodeEmitter.cpp + EVMStackShuffler.cpp + EVMSubtarget.cpp + EVMTargetMachine.cpp + EVMTargetTransformInfo.cpp + + LINK_COMPONENTS + Analysis + AsmPrinter + CodeGen + Core + EVMDesc + EVMInfo + IPO + IRReader + Linker + MC + Passes + Scalar + SelectionDAG + Support + Target + TargetParser + TransformUtils + EVMDesc + EVMInfo + + ADD_TO_COMPONENT + EVM + + DEPENDS + EVMStdLib + intrinsics_gen +) + +add_subdirectory(Disassembler) +add_subdirectory(MCTargetDesc) +add_subdirectory(TargetInfo) diff --git a/llvm/lib/Target/EVM/Disassembler/CMakeLists.txt b/llvm/lib/Target/EVM/Disassembler/CMakeLists.txt new file mode 100644 index 000000000000..9a5568b7f7b2 --- /dev/null +++ b/llvm/lib/Target/EVM/Disassembler/CMakeLists.txt @@ -0,0 +1,13 @@ +add_llvm_component_library(LLVMEVMDisassembler + EVMDisassembler.cpp + + LINK_COMPONENTS + EVMDesc + MCDisassembler + EVMInfo + Support + MC + + ADD_TO_COMPONENT + EVM + ) diff --git a/llvm/lib/Target/EVM/Disassembler/EVMDisassembler.cpp b/llvm/lib/Target/EVM/Disassembler/EVMDisassembler.cpp new file mode 100644 index 000000000000..d7274c17f3f3 --- /dev/null +++ b/llvm/lib/Target/EVM/Disassembler/EVMDisassembler.cpp @@ -0,0 +1,177 @@ +//==----------- EVMDisassembler.cpp - Disassembler for EVM -*- 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 is part of the EVM Disassembler. +// +// It contains code to translate the data produced by the decoder into +// MCInsts. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/EVMMCExpr.h" +#include "TargetInfo/EVMTargetInfo.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDecoderOps.h" +#include "llvm/MC/MCDisassembler/MCDisassembler.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/MC/TargetRegistry.h" + +using namespace llvm; + +#define DEBUG_TYPE "evm-disassembler" + +using DecodeStatus = MCDisassembler::DecodeStatus; + +namespace { + +class EVMDisassembler final : public MCDisassembler { + std::unique_ptr MCII; + + DecodeStatus getInstruction(MCInst &Instr, uint64_t &Size, + ArrayRef Bytes, uint64_t Address, + raw_ostream &CStream) const override; + +public: + EVMDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx, + std::unique_ptr MCII) + : MCDisassembler(STI, Ctx), MCII(std::move(MCII)) {} +}; + +} // end anonymous namespace + +// Decode PUSHN instructions, where N > 8, because target independent part +// of the Decoder can handle only 64-bit types. +template +static DecodeStatus decodePUSH(MCInst &Inst, APInt &Insn, uint64_t Address, + const MCDisassembler *Decoder) { + assert(ImmByteWidth > sizeof(uint64_t)); + assert(alignTo(Insn.getActiveBits(), 8) == (ImmByteWidth + 1) * 8); + MCContext &Ctx = Decoder->getContext(); + auto *Str = new (Ctx) SmallString<80>(); + Insn.extractBits(ImmByteWidth * 8, 0).toStringUnsigned(*Str); + Inst.addOperand(MCOperand::createExpr(EVMCImmMCExpr::create(*Str, Ctx))); + return MCDisassembler::Success; +} + +#include "EVMGenDisassemblerTables.inc" + +static const uint8_t *getDecoderTable(unsigned Size) { + switch (Size) { + case 1: + return static_cast(DecoderTable8); + case 2: + return static_cast(DecoderTable16); + case 3: + return static_cast(DecoderTable24); + case 4: + return static_cast(DecoderTable32); + case 5: + return static_cast(DecoderTable40); + case 6: + return static_cast(DecoderTable48); + case 7: + return static_cast(DecoderTable56); + case 8: + return static_cast(DecoderTable64); + case 9: + return static_cast(DecoderTable72); + case 10: + return static_cast(DecoderTable80); + case 11: + return static_cast(DecoderTable88); + case 12: + return static_cast(DecoderTable96); + case 13: + return static_cast(DecoderTable104); + case 14: + return static_cast(DecoderTable112); + case 15: + return static_cast(DecoderTable120); + case 16: + return static_cast(DecoderTable128); + case 17: + return static_cast(DecoderTable136); + case 18: + return static_cast(DecoderTable144); + case 19: + return static_cast(DecoderTable152); + case 20: + return static_cast(DecoderTable160); + case 21: + return static_cast(DecoderTable168); + case 22: + return static_cast(DecoderTable176); + case 23: + return static_cast(DecoderTable184); + case 24: + return static_cast(DecoderTable192); + case 25: + return static_cast(DecoderTable200); + case 26: + return static_cast(DecoderTable208); + case 27: + return static_cast(DecoderTable216); + case 28: + return static_cast(DecoderTable224); + case 29: + return static_cast(DecoderTable232); + case 30: + return static_cast(DecoderTable240); + case 31: + return static_cast(DecoderTable248); + case 32: + return static_cast(DecoderTable256); + case 33: + return static_cast(DecoderTable264); + default: + llvm_unreachable("Instructions must be from 1 to 33-bytes"); + } +} + +static MCDisassembler *createEVMDisassembler(const Target &T, + const MCSubtargetInfo &STI, + MCContext &Ctx) { + std::unique_ptr MCII(T.createMCInstrInfo()); + return new EVMDisassembler(STI, Ctx, std::move(MCII)); +} + +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeEVMDisassembler() { + // Register the disassembler. + TargetRegistry::RegisterMCDisassembler(getTheEVMTarget(), + createEVMDisassembler); +} + +MCDisassembler::DecodeStatus +EVMDisassembler::getInstruction(MCInst &Instr, uint64_t &Size, + ArrayRef Bytes, uint64_t Address, + raw_ostream &CStream) const { + Size = 0; + if (Bytes.empty()) + return Fail; + + const size_t BytesNum = Bytes.size(); + for (Size = 1; Size <= 33; ++Size) { + if (Size > BytesNum) + break; + + APInt Insn(33 * 8, toHex(ArrayRef(Bytes.begin(), Bytes.begin() + Size)), + 16); + DecodeStatus Result = decodeInstruction(getDecoderTable(Size), Instr, Insn, + Address, this, STI); + if (Result != Fail) + return Result; + } + + // Need to decrement after the loop. + --Size; + return Fail; +} diff --git a/llvm/lib/Target/EVM/EVM.h b/llvm/lib/Target/EVM/EVM.h new file mode 100644 index 000000000000..aef3f124cbe5 --- /dev/null +++ b/llvm/lib/Target/EVM/EVM.h @@ -0,0 +1,120 @@ +//==-------- EVM.h - Top-level interface for EVM representation --*- 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 the entry points for global functions defined in +// the LLVM EVM backend. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVM_H +#define LLVM_LIB_TARGET_EVM_EVM_H + +#include "llvm/IR/PassManager.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Pass.h" + +namespace llvm { +class EVMTargetMachine; +class FunctionPass; +class ModulePass; +class PassRegistry; + +namespace EVMAS { +// EVM address spaces +enum AddressSpaces { + AS_STACK = 0, + AS_HEAP = 1, + AS_CALL_DATA = 2, + AS_RETURN_DATA = 3, + AS_CODE = 4, + AS_STORAGE = 5, + AS_TSTORAGE = 6, + MAX_ADDRESS = AS_TSTORAGE +}; +} // namespace EVMAS + +namespace EVMCOST { +unsigned constexpr SWAP = 3; +unsigned constexpr DUP = 3; +unsigned constexpr POP = 2; +unsigned constexpr PUSH = 3; +unsigned constexpr MLOAD = 3; +} // namespace EVMCOST + +// LLVM IR passes. +ModulePass *createEVMLowerIntrinsicsPass(); +FunctionPass *createEVMCodegenPreparePass(); +ImmutablePass *createEVMAAWrapperPass(); +ImmutablePass *createEVMExternalAAWrapperPass(); +ModulePass *createEVMAlwaysInlinePass(); + +// ISel and immediate followup passes. +FunctionPass *createEVMISelDag(EVMTargetMachine &TM, + CodeGenOptLevel OptLevel); +FunctionPass *createEVMArgumentMove(); +FunctionPass *createEVMAllocaHoistingPass(); +ModulePass *createEVMLinkRuntimePass(); + +// Late passes. +FunctionPass *createEVMOptimizeLiveIntervals(); +FunctionPass *createEVMSingleUseExpression(); +FunctionPass *createEVMSplitCriticalEdges(); +FunctionPass *createEVMBPStackification(); +FunctionPass *createEVMLowerJumpUnless(); +FunctionPass *createEVMPeepholePass(); +ModulePass *createEVMFinalizeStackFrames(); +ModulePass *createEVMMarkRecursiveFunctionsPass(); +ModulePass *createEVMConstantUnfolding(); + +// PassRegistry initialization declarations. +void initializeEVMCodegenPreparePass(PassRegistry &); +void initializeEVMAllocaHoistingPass(PassRegistry &); +void initializeEVMLowerIntrinsicsPass(PassRegistry &); +void initializeEVMArgumentMovePass(PassRegistry &); +void initializeEVMLinkRuntimePass(PassRegistry &); +void initializeEVMOptimizeLiveIntervalsPass(PassRegistry &); +void initializeEVMSingleUseExpressionPass(PassRegistry &); +void initializeEVMSplitCriticalEdgesPass(PassRegistry &); +void initializeEVMBPStackificationPass(PassRegistry &); +void initializeEVMAAWrapperPassPass(PassRegistry &); +void initializeEVMExternalAAWrapperPass(PassRegistry &); +void initializeEVMAlwaysInlinePass(PassRegistry &); +void initializeEVMLowerJumpUnlessPass(PassRegistry &); +void initializeEVMFinalizeStackFramesPass(PassRegistry &); +void initializeEVMMarkRecursiveFunctionsPass(PassRegistry &); +void initializeEVMConstantUnfoldingPass(PassRegistry &); +void initializeEVMPeepholePass(PassRegistry &); + +struct EVMLinkRuntimePass : PassInfoMixin { + EVMLinkRuntimePass() = default; + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); +}; + +struct EVMAllocaHoistingPass : PassInfoMixin { + EVMAllocaHoistingPass() = default; + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); +}; + +struct EVMSHA3ConstFoldingPass : PassInfoMixin { + EVMSHA3ConstFoldingPass() = default; + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); +}; + +struct EVMMarkRecursiveFunctionsPass + : PassInfoMixin { + EVMMarkRecursiveFunctionsPass() = default; + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); +}; + +struct EVMAlwaysInlinePass : PassInfoMixin { + EVMAlwaysInlinePass() = default; + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); +}; + +} // namespace llvm +#endif // LLVM_LIB_TARGET_EVM_EVM_H diff --git a/llvm/lib/Target/EVM/EVM.td b/llvm/lib/Target/EVM/EVM.td new file mode 100644 index 000000000000..5852108fce25 --- /dev/null +++ b/llvm/lib/Target/EVM/EVM.td @@ -0,0 +1,57 @@ +//===---- EVM.td - Describe the EVM Target Machine -----*- tablegen -*-----===// +// +// 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 is the top level entry point for the EVM target. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Target-independent interfaces +//===----------------------------------------------------------------------===// + +include "llvm/Target/Target.td" + +class Proc Features> + : Processor; +def : Proc<"generic", []>; + +//===----------------------------------------------------------------------===// +// Register File Description +//===----------------------------------------------------------------------===// + +include "EVMRegisterInfo.td" + + +//===----------------------------------------------------------------------===// +// Instruction Descriptions +//===----------------------------------------------------------------------===// + +include "EVMInstrInfo.td" + +def EVMInstrInfo : InstrInfo; + + +//===----------------------------------------------------------------------===// +// Assembly Printers +//===----------------------------------------------------------------------===// + +def EVMAsmWriter : AsmWriter { + string AsmWriterClassName = "InstPrinter"; +} + + +def : ProcessorModel<"evm", NoSchedModel, []>; + +//===----------------------------------------------------------------------===// +// Target Declaration +//===----------------------------------------------------------------------===// + +def EVM : Target { + let InstructionSet = EVMInstrInfo; + let AssemblyWriters = [EVMAsmWriter]; +} diff --git a/llvm/lib/Target/EVM/EVMAliasAnalysis.cpp b/llvm/lib/Target/EVM/EVMAliasAnalysis.cpp new file mode 100644 index 000000000000..e2e01221e06d --- /dev/null +++ b/llvm/lib/Target/EVM/EVMAliasAnalysis.cpp @@ -0,0 +1,165 @@ +//===-- EVMAliasAnalysis.cpp - EVM alias 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 +// +//===----------------------------------------------------------------------===// +// +// This is the EVM address space based alias analysis pass. +// +//===----------------------------------------------------------------------===// + +#include "EVMAliasAnalysis.h" +#include "EVM.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/IntrinsicsEVM.h" +#include "llvm/IR/Module.h" + +using namespace llvm; + +#define DEBUG_TYPE "evm-aa" + +AnalysisKey EVMAA::Key; + +// Register this pass... +char EVMAAWrapperPass::ID = 0; +char EVMExternalAAWrapper::ID = 0; + +INITIALIZE_PASS(EVMAAWrapperPass, "evm-aa", + "EVM Address space based Alias Analysis", false, true) + +INITIALIZE_PASS(EVMExternalAAWrapper, "evm-aa-wrapper", + "EVM Address space based Alias Analysis Wrapper", false, true) + +ImmutablePass *llvm::createEVMAAWrapperPass() { return new EVMAAWrapperPass(); } + +ImmutablePass *llvm::createEVMExternalAAWrapperPass() { + return new EVMExternalAAWrapper(); +} + +EVMAAResult::EVMAAResult(const DataLayout &DL) + : VMAAResult(DL, {EVMAS::AS_STORAGE, EVMAS::AS_TSTORAGE}, {EVMAS::AS_HEAP}, + EVMAS::MAX_ADDRESS) {} + +ModRefInfo EVMAAResult::getArgModRefInfo(const CallBase *Call, + unsigned ArgIdx) { + if (Call->doesNotAccessMemory(ArgIdx)) + return ModRefInfo::NoModRef; + + if (Call->onlyWritesMemory(ArgIdx)) + return ModRefInfo::Mod; + + if (Call->onlyReadsMemory(ArgIdx)) + return ModRefInfo::Ref; + + return ModRefInfo::ModRef; +} + +static MemoryLocation getMemLocForArgument(const CallBase *Call, + unsigned ArgIdx) { + AAMDNodes AATags = Call->getAAMetadata(); + const Value *Arg = Call->getArgOperand(ArgIdx); + const auto *II = cast(Call); + + auto GetMemLocation = [Call, Arg, &AATags](unsigned MemSizeArgIdx) { + const auto *LenCI = + dyn_cast(Call->getArgOperand(MemSizeArgIdx)); + if (LenCI && LenCI->getValue().getActiveBits() <= 64) + return MemoryLocation(Arg, LocationSize::precise(LenCI->getZExtValue()), + AATags); + return MemoryLocation::getAfter(Arg, AATags); + }; + + switch (II->getIntrinsicID()) { + case Intrinsic::evm_return: { + assert((ArgIdx == 0) && "Invalid argument index for return"); + return GetMemLocation(ArgIdx + 1); + } + case Intrinsic::evm_create: + case Intrinsic::evm_create2: { + assert((ArgIdx == 1) && "Invalid argument index for create/create2"); + return GetMemLocation(ArgIdx + 1); + } + case Intrinsic::evm_call: + case Intrinsic::evm_callcode: { + assert((ArgIdx == 3 || ArgIdx == 5) && + "Invalid argument index for call/callcode"); + return GetMemLocation(ArgIdx + 1); + } + case Intrinsic::evm_delegatecall: + case Intrinsic::evm_staticcall: { + assert((ArgIdx == 2 || ArgIdx == 4) && + "Invalid argument index for delegatecall/staticcall"); + return GetMemLocation(ArgIdx + 1); + } + default: + llvm_unreachable("Unexpected intrinsic for EVM/EVM target"); + break; + } +} + +ModRefInfo EVMAAResult::getModRefInfo(const CallBase *Call, + const MemoryLocation &Loc, + AAQueryInfo &AAQI) { + const auto *II = dyn_cast(Call); + if (!II) + return AAResultBase::getModRefInfo(Call, Loc, AAQI); + + unsigned AS = Loc.Ptr->getType()->getPointerAddressSpace(); + switch (II->getIntrinsicID()) { + case Intrinsic::evm_gas: + // TODO: #904: Ideally, we would add IntrInaccessibleMemOnly attribute to + // gas intrinsic, but benchmark numbers showed that it is not profitable + // to do that for heap address space. + if (AS == EVMAS::AS_STORAGE || AS == EVMAS::AS_TSTORAGE) + return ModRefInfo::NoModRef; + return ModRefInfo::ModRef; + case Intrinsic::evm_return: + case Intrinsic::evm_staticcall: + if (AS == EVMAS::AS_STORAGE || AS == EVMAS::AS_TSTORAGE) + return ModRefInfo::Ref; + break; + case Intrinsic::evm_create: + case Intrinsic::evm_create2: + case Intrinsic::evm_call: + case Intrinsic::evm_callcode: + case Intrinsic::evm_delegatecall: + if (AS == EVMAS::AS_STORAGE || AS == EVMAS::AS_TSTORAGE) + return ModRefInfo::ModRef; + break; + default: + return AAResultBase::getModRefInfo(Call, Loc, AAQI); + } + + ModRefInfo Result = ModRefInfo::NoModRef; + for (const auto &I : llvm::enumerate(Call->args())) { + const Value *Arg = I.value(); + if (!Arg->getType()->isPointerTy()) + continue; + unsigned ArgIdx = I.index(); + MemoryLocation ArgLoc = getMemLocForArgument(Call, ArgIdx); + AliasResult ArgAlias = VMAAResult::alias(ArgLoc, Loc, AAQI, Call); + if (ArgAlias != AliasResult::NoAlias) + Result |= getArgModRefInfo(Call, ArgIdx); + } + + return Result; +} + +EVMAAWrapperPass::EVMAAWrapperPass() : ImmutablePass(ID) { + initializeEVMAAWrapperPassPass(*PassRegistry::getPassRegistry()); +} + +void EVMAAWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const { + AU.setPreservesAll(); +} + +bool EVMAAWrapperPass::doInitialization(Module &M) { + Result = std::make_unique(M.getDataLayout()); + return false; +} + +EVMAAResult EVMAA::run(Function &F, AnalysisManager &AM) { + return EVMAAResult(F.getParent()->getDataLayout()); +} diff --git a/llvm/lib/Target/EVM/EVMAliasAnalysis.h b/llvm/lib/Target/EVM/EVMAliasAnalysis.h new file mode 100644 index 000000000000..0f477f2ef0ad --- /dev/null +++ b/llvm/lib/Target/EVM/EVMAliasAnalysis.h @@ -0,0 +1,89 @@ +//===-- EVMAliasAnalysis.h - EVM alias 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 +// +//===----------------------------------------------------------------------===// +// +// This is the EVM address space based alias analysis pass. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVMALIASANALYSIS_H +#define LLVM_LIB_TARGET_EVM_EVMALIASANALYSIS_H + +#include "llvm/Analysis/VMAliasAnalysis.h" + +namespace llvm { + +/// EVM-specific AA result. Note that we override certain non-virtual methods +/// from AAResultBase, as clarified in its documentation. +class EVMAAResult : public VMAAResult { +public: + explicit EVMAAResult(const DataLayout &DL); + + ModRefInfo getModRefInfo(const CallBase *Call, const MemoryLocation &Loc, + AAQueryInfo &AAQI); + + ModRefInfo getArgModRefInfo(const CallBase *Call, unsigned ArgIdx); + + ModRefInfo getModRefInfo(const CallBase *Call1, const CallBase *Call2, + AAQueryInfo &AAQI) { + return AAResultBase::getModRefInfo(Call1, Call2, AAQI); + } +}; + +/// Analysis pass providing a never-invalidated alias analysis result. +class EVMAA : public AnalysisInfoMixin { + friend AnalysisInfoMixin; + + static AnalysisKey Key; + +public: + using Result = EVMAAResult; + EVMAAResult run(Function &F, AnalysisManager &AM); +}; + +/// Legacy wrapper pass to provide the EVMAAResult object. +class EVMAAWrapperPass : public ImmutablePass { + std::unique_ptr Result; + +public: + static char ID; + + EVMAAWrapperPass(); + + EVMAAResult &getResult() { return *Result; } + + bool doFinalization(Module &M) override { + Result.reset(); + return false; + } + + bool doInitialization(Module &M) override; + void getAnalysisUsage(AnalysisUsage &AU) const override; +}; + +// Wrapper around ExternalAAWrapperPass so that the default constructor gets the +// callback. +class EVMExternalAAWrapper : public ExternalAAWrapperPass { +public: + static char ID; + + bool runEarly() override { return true; } + + EVMExternalAAWrapper() + : ExternalAAWrapperPass([](Pass &P, Function &, AAResults &AAR) { + if (auto *WrapperPass = P.getAnalysisIfAvailable()) + AAR.addAAResult(WrapperPass->getResult()); + }) {} + + StringRef getPassName() const override { + return "EVM Address space based Alias Analysis Wrapper"; + } +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_EVMALIASANALYSIS_H diff --git a/llvm/lib/Target/EVM/EVMAllocaHoisting.cpp b/llvm/lib/Target/EVM/EVMAllocaHoisting.cpp new file mode 100644 index 000000000000..b427e2847903 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMAllocaHoisting.cpp @@ -0,0 +1,85 @@ +//===-- EVMAllocaHoisting.cpp - Hoist allocas to the entry ----*- 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 pass hoists the alloca instructions in the non-entry blocks to the +// entry blocks. +// Copied from NVPTX backend. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" + +#include "llvm/CodeGen/StackProtector.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Instructions.h" + +using namespace llvm; + +namespace { +// Hoisting the alloca instructions in the non-entry blocks to the entry +// block. +class EVMAllocaHoisting final : public FunctionPass { +public: + static char ID; // Pass ID + + EVMAllocaHoisting() : FunctionPass(ID) {} + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addPreserved(); + } + + StringRef getPassName() const override { + return "EVM specific alloca hoisting"; + } + + bool runOnFunction(Function &F) override; +}; + +} // end anonymous namespace + +char EVMAllocaHoisting::ID = 0; + +static bool runImpl(Function &F) { + bool Modified = false; + Function::iterator I = F.begin(); + Instruction *FirstTerminatorInst = (I++)->getTerminator(); + + for (auto E = F.end(); I != E; ++I) { + for (auto BI = I->begin(), BE = I->end(); BI != BE;) { + auto *Alloca = dyn_cast(BI++); + if (Alloca && isa(Alloca->getArraySize())) { + Alloca->moveBefore(FirstTerminatorInst); + Modified = true; + } + } + } + + return Modified; +} + +bool EVMAllocaHoisting::runOnFunction(Function &F) { + if (skipFunction(F)) + return false; + + return runImpl(F); +} + +INITIALIZE_PASS( + EVMAllocaHoisting, "evm-alloca-hoisting", + "Hoisting alloca instructions in non-entry blocks to the entry block", + false, false) + +FunctionPass *llvm::createEVMAllocaHoistingPass() { + return new EVMAllocaHoisting; +} + +PreservedAnalyses EVMAllocaHoistingPass::run(Function &F, + FunctionAnalysisManager &AM) { + return runImpl(F) ? PreservedAnalyses::none() : PreservedAnalyses::all(); +} diff --git a/llvm/lib/Target/EVM/EVMAlwaysInline.cpp b/llvm/lib/Target/EVM/EVMAlwaysInline.cpp new file mode 100644 index 000000000000..4243c049699e --- /dev/null +++ b/llvm/lib/Target/EVM/EVMAlwaysInline.cpp @@ -0,0 +1,78 @@ +//===---- EVMAlwaysInline.cpp - Add alwaysinline attribute ------*- 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 pass adds alwaysinline attribute to functions with one call site. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" + +#define DEBUG_TYPE "evm-always-inline" + +using namespace llvm; + +namespace { + +class EVMAlwaysInline final : public ModulePass { +public: + static char ID; // Pass ID + EVMAlwaysInline() : ModulePass(ID) {} + + StringRef getPassName() const override { return "EVM always inline"; } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + ModulePass::getAnalysisUsage(AU); + } + + bool runOnModule(Module &M) override; +}; + +} // end anonymous namespace + +static bool runImpl(Module &M) { + bool Changed = false; + for (auto &F : M) { + if (F.isDeclaration() || F.hasOptNone() || + F.hasFnAttribute(Attribute::NoInline) || !F.hasOneUse()) + continue; + + auto *Call = dyn_cast(*F.user_begin()); + + // Skip non call instructions, recursive calls, or calls with noinline + // attribute. + if (!Call || Call->getFunction() == &F || Call->isNoInline()) + continue; + + F.addFnAttr(Attribute::AlwaysInline); + Changed = true; + } + + return Changed; +} + +bool EVMAlwaysInline::runOnModule(Module &M) { + if (skipModule(M)) + return false; + return runImpl(M); +} + +char EVMAlwaysInline::ID = 0; + +INITIALIZE_PASS(EVMAlwaysInline, "evm-always-inline", + "Add alwaysinline attribute to functions with one call site", + false, false) + +ModulePass *llvm::createEVMAlwaysInlinePass() { return new EVMAlwaysInline; } + +PreservedAnalyses EVMAlwaysInlinePass::run(Module &M, + ModuleAnalysisManager &AM) { + runImpl(M); + return PreservedAnalyses::all(); +} diff --git a/llvm/lib/Target/EVM/EVMArgumentMove.cpp b/llvm/lib/Target/EVM/EVMArgumentMove.cpp new file mode 100644 index 000000000000..c60b497d6087 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMArgumentMove.cpp @@ -0,0 +1,109 @@ +//===---------- EVMArgumentMove.cpp - Argument instruction moving ---------===// +// +// 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 moves and orders ARGUMENT instructions after ScheduleDAG +// scheduling. +// +// Arguments are really live-in registers, however, since we use virtual +// registers and LLVM doesn't support live-in virtual registers, we're +// currently making do with ARGUMENT instructions which are placed at the top +// of the entry block. The trick is to get them to *stay* at the top of the +// entry block. +// +// The ARGUMENTS physical register keeps these instructions pinned in place +// during liveness-aware CodeGen passes, however one thing which does not +// respect this is the ScheduleDAG scheduler. This pass is therefore run +// immediately after that. +// +// This is all hopefully a temporary solution until we find a better solution +// for describing the live-in nature of arguments. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "EVMMachineFunctionInfo.h" +#include "EVMSubtarget.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm/CodeGen/MachineBlockFrequencyInfo.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/Support/raw_ostream.h" + +#include + +using namespace llvm; + +#define DEBUG_TYPE "evm-argument-move" + +namespace { +class EVMArgumentMove final : public MachineFunctionPass { +public: + static char ID; // Pass identification, replacement for typeid + EVMArgumentMove() : MachineFunctionPass(ID) {} + + StringRef getPassName() const override { return "EVM Argument Move"; } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesCFG(); + AU.addPreserved(); + AU.addPreservedID(MachineDominatorsID); + MachineFunctionPass::getAnalysisUsage(AU); + } + + bool runOnMachineFunction(MachineFunction &MF) override; +}; +} // end anonymous namespace + +char EVMArgumentMove::ID = 0; +INITIALIZE_PASS(EVMArgumentMove, DEBUG_TYPE, + "Move ARGUMENT instructions for EVM", false, false) + +FunctionPass *llvm::createEVMArgumentMove() { return new EVMArgumentMove(); } + +bool EVMArgumentMove::runOnMachineFunction(MachineFunction &MF) { + LLVM_DEBUG({ + dbgs() << "********** Argument Move **********\n" + << "********** Function: " << MF.getName() << '\n'; + }); + + bool Changed = false; + MachineBasicBlock &EntryMBB = MF.front(); + std::deque Args; + MachineInstr *PushDeployAddress = nullptr; + for (MachineBasicBlock &MBB : MF) { + for (MachineInstr &MI : MBB) { + if (EVM::ARGUMENT == MI.getOpcode()) + Args.push_back(&MI); + else if (EVM::PUSHDEPLOYADDRESS == MI.getOpcode()) { + assert(!PushDeployAddress && "Multiple PUSHDEPLOYADDRESS instructions"); + PushDeployAddress = &MI; + } + } + } + + // Sort ARGUMENT instructions in ascending order of their arguments. + std::sort(Args.begin(), Args.end(), + [](const MachineInstr *MI1, const MachineInstr *MI2) { + int64_t Arg1Idx = MI1->getOperand(1).getImm(); + int64_t Arg2Idx = MI2->getOperand(1).getImm(); + return Arg1Idx < Arg2Idx; + }); + + // Should be the first instruction at the MF's entry. + if (PushDeployAddress) { + auto *MFI = MF.getInfo(); + MFI->setHasPushDeployAddress(); + Args.push_front(PushDeployAddress); + } + + for (MachineInstr *MI : reverse(Args)) { + MachineInstr *Arg = MI->removeFromParent(); + EntryMBB.insert(EntryMBB.begin(), Arg); + Changed = true; + } + return Changed; +} diff --git a/llvm/lib/Target/EVM/EVMAsmPrinter.cpp b/llvm/lib/Target/EVM/EVMAsmPrinter.cpp new file mode 100644 index 000000000000..802edb1401eb --- /dev/null +++ b/llvm/lib/Target/EVM/EVMAsmPrinter.cpp @@ -0,0 +1,445 @@ +//===-------- EVMAsmPrinter.cpp - EVM LLVM assembly writer ----------------===// +// +// 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 a printer that converts from our internal representation +// of machine-dependent LLVM code to the EVM assembly language. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "EVMMCInstLower.h" +#include "EVMMachineFunctionInfo.h" +#include "EVMTargetMachine.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "MCTargetDesc/EVMTargetStreamer.h" +#include "TargetInfo/EVMTargetInfo.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/Module.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCSectionELF.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/MC/TargetRegistry.h" + +using namespace llvm; + +extern cl::opt EVMKeepRegisters; + +#define DEBUG_TYPE "asm-printer" + +namespace { +class EVMAsmPrinter : public AsmPrinter { + // For each register class we need the mapping from a virtual register + // to its number. + // TODO: Once stackification is implemented this should be removed. + using VRegMap = DenseMap; + using VRegRCMap = DenseMap; + VRegRCMap VRegMapping; + + // Maps a linker symbol name to corresponding MCSymbol. + StringSet<> WideRelocSymbolsSet; + StringMap ImmutablesMap; + + // Contains constant global variable initializers in address space AS_CODE, + // which are concatenated into a single block. Duplicate initializers and + // those that are substrings of others are removed. This "data section" + // is emitted at the end of the .text section. + std::string DataSectionBuffer; + MCSymbol *DataSectionSymbol = nullptr; + // Maps each global variable symbol to the offset within the data section + // where its corresponding initializer is located. + DenseMap GlobSymbolToOffsetMap; + + // True if there is a function that pushes deploy address. + bool ModuleHasPushDeployAddress = false; + + bool FirstFunctIsHandled = false; + +public: + EVMAsmPrinter(TargetMachine &TM, std::unique_ptr Streamer) + : AsmPrinter(TM, std::move(Streamer)) {} + + StringRef getPassName() const override { return "EVM Assembly "; } + + void emitInstruction(const MachineInstr *MI) override; + + void emitBasicBlockStart(const MachineBasicBlock &MBB) override; + + void emitFunctionEntryLabel() override; + + void emitEndOfAsmFile(Module &) override; + + void emitStartOfAsmFile(Module &) override; + + void emitFunctionBodyStart() override; + + void emitFunctionBodyEnd() override; + + void emitGlobalVariable(const GlobalVariable *GV) override; + +private: + void emitAssemblySymbol(const MachineInstr *MI); + void emitWideRelocatableSymbol(const MachineInstr *MI); + void emitLoadImmutableLabel(const MachineInstr *MI); + void emitJumpDest(); + void createDataSectionBuffer(const Module &M); +}; +} // end of anonymous namespace + +void EVMAsmPrinter::emitFunctionEntryLabel() { + AsmPrinter::emitFunctionEntryLabel(); + + VRegMapping.clear(); + + // Go through all virtual registers of the MF to establish the mapping + // between the global virtual register number and the per-class virtual + // register number (though we have only one GPR register class). + // We use the per-class virtual register number in the asm output. + // TODO: This is a temporary solution while EVM-backend outputs virtual + // registers. Once stackification is implemented this should be removed. + const MachineRegisterInfo &MRI = MF->getRegInfo(); + const unsigned NumVRs = MRI.getNumVirtRegs(); + for (unsigned I = 0; I < NumVRs; I++) { + const Register Vr = Register::index2VirtReg(I); + const TargetRegisterClass *RC = MRI.getRegClass(Vr); + DenseMap &VRegMap = VRegMapping[RC]; + const unsigned N = VRegMap.size(); + VRegMap.insert(std::make_pair(Vr, N + 1)); + } +} + +void EVMAsmPrinter::emitFunctionBodyStart() { + if (MF->getFunction().hasFnAttribute("evm-entry-function") && + FirstFunctIsHandled) + report_fatal_error("Entry function '" + MF->getName() + + "' isn't the first function in the module."); + + if (const auto *MFI = MF->getInfo(); + MFI->getHasPushDeployAddress()) { + // TODO: #778. Move the function with PUSHDEPLOYADDRESS to the + // beginning of the module layout. + if (FirstFunctIsHandled) + report_fatal_error("Function with PUSHDEPLOYADDRESS isn't first."); + + assert(!ModuleHasPushDeployAddress && + "Multiple functions with PUSHDEPLOYADDRESS."); + + // Deploy address is represented by a PUSH20 instruction at the + // start of the bytecode. + MCInst MCI; + MCI.setOpcode(EVM::PUSH20_S); + MCI.addOperand(MCOperand::createImm(0)); + EmitToStreamer(*OutStreamer, MCI); + ModuleHasPushDeployAddress = true; + } +} + +void EVMAsmPrinter::emitFunctionBodyEnd() { FirstFunctIsHandled = true; } + +void EVMAsmPrinter::emitBasicBlockStart(const MachineBasicBlock &MBB) { + AsmPrinter::emitBasicBlockStart(MBB); + + // If this is the entry function, we don't need to emit JUMPDEST + // instruction in the first basic block, as it is the entry point + // of the EVM bytecode. + auto IsEntryPoint = [this](const MachineBasicBlock &MBB) { + return !FirstFunctIsHandled && &MBB == &MF->front() && + MF->getFunction().hasFnAttribute("evm-entry-function"); + }; + + // Emit JUMPDEST instruction at the beginning of the basic block, if + // this is not a block that is only reachable by fallthrough. + if (!EVMKeepRegisters && !IsEntryPoint(MBB) && + !AsmPrinter::isBlockOnlyReachableByFallthrough(&MBB)) + emitJumpDest(); +} + +void EVMAsmPrinter::emitInstruction(const MachineInstr *MI) { + EVMMCInstLower MCInstLowering(OutContext, *this, VRegMapping, + MF->getRegInfo()); + + switch (MI->getOpcode()) { + default: + break; + case EVM::PseudoCALL: { + // Generate push instruction with the address of a function. + MCInst Push; + Push.setOpcode(EVM::PUSH4_S); + assert(MI->getOperand(0).isGlobal() && + "The first operand of PseudoCALL should be a GlobalValue."); + + // TODO: #745: Refactor EVMMCInstLower::Lower so we could use lowerOperand + // instead of creating a MCOperand directly. + MCOperand MCOp = MCOperand::createExpr(MCSymbolRefExpr::create( + getSymbol(MI->getOperand(0).getGlobal()), OutContext)); + Push.addOperand(MCOp); + EmitToStreamer(*OutStreamer, Push); + + // Jump to a function. + MCInst Jump; + Jump.setOpcode(EVM::JUMP_S); + EmitToStreamer(*OutStreamer, Jump); + + // In case a function has a return label, emit it, and also + // emit a JUMPDEST instruction. + if (MI->getNumExplicitOperands() > 1) { + assert(MI->getOperand(1).isMCSymbol() && + "The second operand of PseudoCALL should be a MCSymbol."); + OutStreamer->emitLabel(MI->getOperand(1).getMCSymbol()); + emitJumpDest(); + } + return; + } + case EVM::PseudoRET: { + // TODO: #746: Use PseudoInstExpansion and do this expansion in tblgen. + MCInst Jump; + Jump.setOpcode(EVM::JUMP_S); + EmitToStreamer(*OutStreamer, Jump); + return; + } + case EVM::PseudoJUMP: + case EVM::PseudoJUMPI: { + MCInst Push; + Push.setOpcode(EVM::PUSH4_S); + + // TODO: #745: Refactor EVMMCInstLower::Lower so we could use lowerOperand + // instead of creating a MCOperand directly. + MCOperand MCOp = MCOperand::createExpr(MCSymbolRefExpr::create( + MI->getOperand(0).getMBB()->getSymbol(), OutContext)); + Push.addOperand(MCOp); + EmitToStreamer(*OutStreamer, Push); + + MCInst Jump; + Jump.setOpcode(MI->getOpcode() == EVM::PseudoJUMP ? EVM::JUMP_S + : EVM::JUMPI_S); + EmitToStreamer(*OutStreamer, Jump); + return; + } + case EVM::LINKERSYMBOL_S: + emitWideRelocatableSymbol(MI); + return; + case EVM::DATASIZE_S: + case EVM::DATAOFFSET_S: + emitAssemblySymbol(MI); + return; + case EVM::LOADIMMUTABLE_S: + emitLoadImmutableLabel(MI); + return; + } + + MCInst TmpInst; + MCInstLowering.Lower(MI, TmpInst, GlobSymbolToOffsetMap, DataSectionSymbol); + EmitToStreamer(*OutStreamer, TmpInst); +} + +// Lowers LOADIMMUTABLE_S as show below: +// LOADIMMUTABLE_S @immutable_id +// -> +// __load_immutable__immutable_id.N: +// PUSH32 0 +// +// where N = 1 ... 'number of @immutable_id +// references in the current module'. +// +void EVMAsmPrinter::emitLoadImmutableLabel(const MachineInstr *MI) { + assert(MI->getOpcode() == EVM::LOADIMMUTABLE_S); + + const MCSymbol *Symbol = MI->getOperand(0).getMCSymbol(); + StringRef ImmutableId = Symbol->getName(); + std::string LoadImmutableLabel = + EVM::getLoadImmutableSymbol(ImmutableId, ++ImmutablesMap[ImmutableId]); + if (OutContext.lookupSymbol(LoadImmutableLabel)) + report_fatal_error(Twine("MC: duplicating immutable label ") + + LoadImmutableLabel); + + MCSymbol *Sym = OutContext.getOrCreateSymbol(LoadImmutableLabel); + // Emit load immutable label right before PUSH32 instruction. + OutStreamer->emitLabel(Sym); + + MCInst MCI; + MCI.setOpcode(EVM::PUSH32_S); + MCI.addOperand(MCOperand::createImm(0)); + EmitToStreamer(*OutStreamer, MCI); +} + +void EVMAsmPrinter::emitAssemblySymbol(const MachineInstr *MI) { + MCSymbol *LinkerSymbol = MI->getOperand(0).getMCSymbol(); + StringRef LinkerSymbolName = LinkerSymbol->getName(); + unsigned Opc = MI->getOpcode(); + assert(Opc == EVM::DATASIZE_S || Opc == EVM::DATAOFFSET_S); + + std::string NameHash = EVM::getLinkerSymbolHash(LinkerSymbolName); + std::string SymbolNameHash = (Opc == EVM::DATASIZE_S) + ? EVM::getDataSizeSymbol(NameHash) + : EVM::getDataOffsetSymbol(NameHash); + + MCInst MCI; + MCI.setOpcode(EVM::PUSH4_S); + MCOperand MCOp = MCOperand::createExpr(MCSymbolRefExpr::create( + SymbolNameHash, MCSymbolRefExpr::VariantKind::VK_EVM_DATA, OutContext)); + MCI.addOperand(MCOp); + EmitToStreamer(*OutStreamer, MCI); +} + +// Lowers LINKERSYMBOL_S instruction as shown below: +// +// LINKERSYMBOL_S @BaseSymbol +// -> +// PUSH20_S @"__linker_symbol__$KECCAK256(BaseSymbol)$__" +// +// .section ".symbol_name__$KECCAK256(BaseName)$__","S",@progbits +// .ascii "~ \".%%^ [];,<.>? .sol:GreaterHelper" + +void EVMAsmPrinter::emitWideRelocatableSymbol(const MachineInstr *MI) { + constexpr unsigned LinkerSymbolSize = 20; + MCSymbol *BaseSymbol = MI->getOperand(0).getMCSymbol(); + StringRef BaseSymbolName = BaseSymbol->getName(); + std::string BaseSymbolNameHash = EVM::getLinkerSymbolHash(BaseSymbolName); + std::string SymbolName = EVM::getLinkerSymbolName(BaseSymbolNameHash); + + MCInst MCI; + // This represents a library address symbol which is 20-bytes wide. + MCI.setOpcode(EVM::PUSH20_S); + MCOperand MCOp = MCOperand::createExpr(MCSymbolRefExpr::create( + BaseSymbolNameHash, MCSymbolRefExpr::VariantKind::VK_EVM_DATA, + OutContext)); + MCI.addOperand(MCOp); + + if (!WideRelocSymbolsSet.contains(SymbolName)) { + for (unsigned Idx = 0; Idx < LinkerSymbolSize / sizeof(uint32_t); ++Idx) { + std::string SubSymName = EVM::getSymbolIndexedName(SymbolName, Idx); + if (OutContext.lookupSymbol(SubSymName)) + report_fatal_error(Twine("MC: duplicating reference sub-symbol ") + + SubSymName); + } + } + + auto *TS = static_cast(OutStreamer->getTargetStreamer()); + TS->emitWideRelocatableSymbol(MCI, SymbolName, LinkerSymbolSize); + + // The linker symbol and the related section already exist, so just exit. + if (WideRelocSymbolsSet.contains(SymbolName)) + return; + + WideRelocSymbolsSet.insert(SymbolName); + + MCSection *CurrentSection = OutStreamer->getCurrentSectionOnly(); + + // Emit the .symbol_name section that contains the actual symbol + // name. + std::string SymbolSectionName = EVM::getSymbolSectionName(SymbolName); + MCSection *SymbolSection = OutContext.getELFSection( + SymbolSectionName, ELF::SHT_PROGBITS, ELF::SHF_STRINGS); + OutStreamer->switchSection(SymbolSection); + OutStreamer->emitBytes(BaseSymbolName); + OutStreamer->switchSection(CurrentSection); +} + +void EVMAsmPrinter::createDataSectionBuffer(const Module &M) { + SmallVector, 16> Globals; + for (const GlobalVariable &GV : M.globals()) { + if (GV.getAddressSpace() != EVMAS::AS_CODE || !GV.hasInitializer()) + continue; + + const auto *CV = dyn_cast(GV.getInitializer()); + if (!CV) + continue; + + Globals.emplace_back(&GV, CV->getRawDataValues()); + } + // Sort global variables in descending order based on the size of their + // initializers. + stable_sort(Globals, [](const auto &A, const auto &B) { + return A.second.size() > B.second.size(); + }); + + // Construct the data section by concatenating unique initializers, + // eliminating duplicates, and excluding any initializer that is a + // substring of another. + // NOTE: Rather than simply concatenating unique strings, we could attempt + // to compute the Shortest Common Superstring by allowing partial overlaps + // between strings. Although this is an NP-hard problem, we could explore + // an approximate greedy solution. Consider this approach if there are + // real programs that could benefit from the optimization. + DataSectionBuffer.clear(); + raw_string_ostream Stream(DataSectionBuffer); + for (const auto &[_, Init] : Globals) + if (!StringRef(DataSectionBuffer).contains(Init)) + Stream << Init; + + // Compute offsets of each global initializer in the data section. + StringRef DataView(DataSectionBuffer); + for (const auto &[GV, Init] : Globals) { + size_t Offset = DataView.find(Init); + assert(Offset != StringRef::npos && + "Initializer not found in data section"); + GlobSymbolToOffsetMap[getSymbol(GV)] = Offset; + } +} + +void EVMAsmPrinter::emitEndOfAsmFile(Module &) { + // The deploy and runtime code must end with INVALID instruction to + // comply with 'solc'. To ensure this, we append an INVALID + // instruction at the end of the .text section. + OutStreamer->pushSection(); + OutStreamer->switchSection(OutContext.getObjectFileInfo()->getTextSection()); + MCInst MCI; + MCI.setOpcode(EVM::INVALID_S); + + // Construct a local MCSubtargetInfo to use the streamer, as MachineFunction + // is not available here. Since we're at the global level we can use the + // default constructed subtarget. + std::unique_ptr STI(TM.getTarget().createMCSubtargetInfo( + TM.getTargetTriple().str(), TM.getTargetCPU(), + TM.getTargetFeatureString())); + + OutStreamer->emitInstruction(MCI, *STI); + + // Emit constants to the code. + OutStreamer->emitLabel(DataSectionSymbol); + OutStreamer->emitBinaryData(DataSectionBuffer); + + OutStreamer->popSection(); + + GlobSymbolToOffsetMap.clear(); + WideRelocSymbolsSet.clear(); + ImmutablesMap.clear(); + ModuleHasPushDeployAddress = false; + FirstFunctIsHandled = false; +} + +void EVMAsmPrinter::emitJumpDest() { + MCInst JumpDest; + JumpDest.setOpcode(EVM::JUMPDEST_S); + EmitToStreamer(*OutStreamer, JumpDest); +} + +void EVMAsmPrinter::emitStartOfAsmFile(Module &M) { + createDataSectionBuffer(M); + DataSectionSymbol = OutContext.getOrCreateSymbol("code_data_section"); +} + +void EVMAsmPrinter::emitGlobalVariable(const GlobalVariable *GV) { + // Constant arrays are handled above. + if (GV->getAddressSpace() == EVMAS::AS_CODE && GV->hasInitializer()) + if (isa(GV->getInitializer())) + return; + + AsmPrinter::emitGlobalVariable(GV); +} + +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeEVMAsmPrinter() { + const RegisterAsmPrinter X(getTheEVMTarget()); +} diff --git a/llvm/lib/Target/EVM/EVMBackwardPropagationStackification.cpp b/llvm/lib/Target/EVM/EVMBackwardPropagationStackification.cpp new file mode 100644 index 000000000000..f28586d73763 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMBackwardPropagationStackification.cpp @@ -0,0 +1,113 @@ +//===----- EVMBPStackification.cpp - BP stackification ---------*- 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 backward propagation (BP) stackification. +// Original idea was taken from the Ethereum's compiler (solc) stackification +// algorithm. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "EVMMachineFunctionInfo.h" +#include "EVMStackSolver.h" +#include "EVMStackifyCodeEmitter.h" +#include "EVMSubtarget.h" +#include "llvm/CodeGen/LiveIntervals.h" +#include "llvm/CodeGen/LiveStacks.h" +#include "llvm/CodeGen/MachineBlockFrequencyInfo.h" +#include "llvm/CodeGen/MachineLoopInfo.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/InitializePasses.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +#define DEBUG_TYPE "evm-backward-propagation-stackification" + +namespace { +class EVMBPStackification final : public MachineFunctionPass { +public: + static char ID; // Pass identification, replacement for typeid + + EVMBPStackification() : MachineFunctionPass(ID) {} + +private: + StringRef getPassName() const override { + return "EVM backward propagation stackification"; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + AU.addRequired(); + AU.addPreserved(); + AU.addRequired(); + AU.addPreserved(); + AU.addRequired(); + AU.addPreserved(); + AU.addRequired(); + AU.addPreserved(); + MachineFunctionPass::getAnalysisUsage(AU); + } + + bool runOnMachineFunction(MachineFunction &MF) override; + + MachineFunctionProperties getRequiredProperties() const override { + return MachineFunctionProperties().set( + MachineFunctionProperties::Property::TracksLiveness); + } +}; +} // end anonymous namespace + +char EVMBPStackification::ID = 0; + +INITIALIZE_PASS_BEGIN(EVMBPStackification, DEBUG_TYPE, + "Backward propagation stackification", false, false) +INITIALIZE_PASS_DEPENDENCY(LiveIntervalsWrapperPass) +INITIALIZE_PASS_DEPENDENCY(MachineLoopInfoWrapperPass) +INITIALIZE_PASS_DEPENDENCY(SlotIndexesWrapperPass) +INITIALIZE_PASS_DEPENDENCY(VirtRegMap) +INITIALIZE_PASS_DEPENDENCY(LiveStacks) +INITIALIZE_PASS_DEPENDENCY(MachineBlockFrequencyInfoWrapperPass) +INITIALIZE_PASS_END(EVMBPStackification, DEBUG_TYPE, + "Backward propagation stackification", false, false) + +FunctionPass *llvm::createEVMBPStackification() { + return new EVMBPStackification(); +} + +bool EVMBPStackification::runOnMachineFunction(MachineFunction &MF) { + LLVM_DEBUG({ + dbgs() << "********** Backward propagation stackification **********\n" + << "********** Function: " << MF.getName() << '\n'; + }); + + MachineRegisterInfo &MRI = MF.getRegInfo(); + auto &LIS = getAnalysis().getLIS(); + MachineLoopInfo *MLI = &getAnalysis().getLI(); + auto &VRM = getAnalysis(); + auto &LSS = getAnalysis(); + auto &MBFI = getAnalysis().getMBFI(); + + // We don't preserve SSA form. + MRI.leaveSSA(); + + assert(MRI.tracksLiveness() && "Stackification expects liveness"); + EVMStackModel StackModel(MF, LIS, + MF.getSubtarget().stackDepthLimit()); + EVMStackSolver(MF, StackModel, MLI, VRM, MBFI, LIS).run(); + EVMStackifyCodeEmitter(StackModel, MF, VRM, LSS, LIS).run(); + + auto *MFI = MF.getInfo(); + MFI->setIsStackified(); + + // In a stackified code register liveness has no meaning. + MRI.invalidateLiveness(); + return true; +} diff --git a/llvm/lib/Target/EVM/EVMCalculateModuleSize.cpp b/llvm/lib/Target/EVM/EVMCalculateModuleSize.cpp new file mode 100644 index 000000000000..b0ee0db5603e --- /dev/null +++ b/llvm/lib/Target/EVM/EVMCalculateModuleSize.cpp @@ -0,0 +1,153 @@ +//===----- EVMCalculateModuleSize.cpp - Calculate module size --*- 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 +// +//===----------------------------------------------------------------------===// +// +// A utility set for determining the overall size of a module. +// +//===----------------------------------------------------------------------===// + +#include "EVMCalculateModuleSize.h" +#include "EVMMachineFunctionInfo.h" +#include "EVMSubtarget.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/IR/Module.h" + +using namespace llvm; + +// This is the copied from AsmPrinter::isBlockOnlyReachableByFallthrough. +static bool isBlockOnlyReachableByFallthrough(const MachineBasicBlock *MBB) { + // If this is a landing pad, it isn't a fall through. If it has no preds, + // then nothing falls through to it. + if (MBB->isEHPad() || MBB->pred_empty()) + return false; + + // If there isn't exactly one predecessor, it can't be a fall through. + if (MBB->pred_size() > 1) + return false; + + // The predecessor has to be immediately before this block. + MachineBasicBlock *Pred = *MBB->pred_begin(); + if (!Pred->isLayoutSuccessor(MBB)) + return false; + + // If the block is completely empty, then it definitely does fall through. + if (Pred->empty()) + return true; + + // Check the terminators in the previous blocks + for (const auto &MI : Pred->terminators()) { + // If it is not a simple branch, we are in a table somewhere. + if (!MI.isBranch() || MI.isIndirectBranch()) + return false; + + // If we are the operands of one of the branches, this is not a fall + // through. Note that targets with delay slots will usually bundle + // terminators with the delay slot instruction. + for (ConstMIBundleOperands OP(MI); OP.isValid(); ++OP) { + if (OP->isJTI()) + return false; + if (OP->isMBB() && OP->getMBB() == MBB) + return false; + } + } + + return true; +} + +// Return the size of an instruction in bytes. For some pseudo instructions, +// don't use the size from the instruction description, since during code +// generation, some instructions will be relaxed to smaller instructions. +static unsigned getInstSize(const MachineInstr &MI, + const TargetInstrInfo *TII) { + // Skip debug instructions. + if (MI.isDebugInstr()) + return 0; + + unsigned Size = 0; + switch (MI.getOpcode()) { + default: + Size = MI.getDesc().getSize(); + break; + case EVM::PseudoCALL: + // In case that function call has a return label, we will emit JUMPDEST, + // so take it into account. + if (MI.getNumExplicitOperands() > 1) + Size = TII->get(EVM::JUMPDEST_S).getSize(); + LLVM_FALLTHROUGH; + case EVM::PseudoJUMPI: + case EVM::PseudoJUMP: + // We emit PUSH4_S here. The linker usually relaxes it to PUSH2_S, + // since a 16-bit immediate covers the 24,576-byte EVM runtime code cap + // (EIP-170). If a wider immediate were ever required, the contract + // already exceeds the cap, so the push width is moot. + Size += TII->get(EVM::PUSH2_S).getSize() + TII->get(EVM::JUMP_S).getSize(); + break; + case EVM::PUSH_LABEL: + // We emit PUSH4_S here. The linker usually relaxes it to PUSH2_S, + // since a 16-bit immediate covers the 24,576-byte EVM runtime code cap + // (EIP-170). If a wider immediate were ever required, the contract + // already exceeds the cap, so the push width is moot. + Size = TII->get(EVM::PUSH2_S).getSize(); + break; + } + return Size; +} + +uint64_t llvm::EVM::calculateFunctionCodeSize(const MachineFunction &MF) { + uint64_t Size = 0; + const auto *TII = MF.getSubtarget().getInstrInfo(); + + // If the function has a PUSHDEPLOYADDRESS, it starts with a PUSH20. + if (const auto *MFI = MF.getInfo(); + MFI->getHasPushDeployAddress()) + Size += TII->get(EVM::PUSH20).getSize(); + + for (const MachineBasicBlock &MBB : MF) { + // If the block is not only reachable by fallthrough, it starts with + // a JUMPDEST instruction. + if (!isBlockOnlyReachableByFallthrough(&MBB)) + Size += TII->get(EVM::JUMPDEST_S).getSize(); + + Size += std::accumulate(MBB.begin(), MBB.end(), 0, + [&TII](unsigned Sum, const MachineInstr &MI) { + return Sum + getInstSize(MI, TII); + }); + } + return Size; +} + +static uint64_t calculateReadOnlyDataSize(const Module &M) { + uint64_t Size = 0; + for (const GlobalVariable &GV : M.globals()) { + if (GV.getAddressSpace() != EVMAS::AS_CODE || !GV.hasInitializer()) + continue; + + if (const auto *CV = dyn_cast(GV.getInitializer())) + Size += CV->getRawDataValues().size(); + } + return Size; +} + +uint64_t llvm::EVM::calculateModuleCodeSize(Module &M, + const MachineModuleInfo &MMI) { + uint64_t TotalSize = 0; + for (Function &F : M) { + MachineFunction *MF = MMI.getMachineFunction(F); + if (!MF) + continue; + TotalSize += llvm::EVM::calculateFunctionCodeSize(*MF); + } + + // Take into account the read-only data that we append to the .text section. + TotalSize += calculateReadOnlyDataSize(M); + // Take into account INVALID instruction at the end of the .text section. + TotalSize++; + return TotalSize; +} diff --git a/llvm/lib/Target/EVM/EVMCalculateModuleSize.h b/llvm/lib/Target/EVM/EVMCalculateModuleSize.h new file mode 100644 index 000000000000..9acdf82684bb --- /dev/null +++ b/llvm/lib/Target/EVM/EVMCalculateModuleSize.h @@ -0,0 +1,29 @@ +//===----- EVMCalculateModuleSize.h - Calculate module size ----*- 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 +// +//===----------------------------------------------------------------------===// +// +// A utility set for determining the overall size of a module. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVMCALCULATEMODULESIZE_H +#define LLVM_LIB_TARGET_EVM_EVMCALCULATEMODULESIZE_H + +#include + +namespace llvm { +class MachineFunction; +class MachineModuleInfo; +class Module; + +namespace EVM { +uint64_t calculateFunctionCodeSize(const MachineFunction &MF); +uint64_t calculateModuleCodeSize(Module &M, const MachineModuleInfo &MMI); +} // namespace EVM +} // namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_EVMCALCULATEMODULESIZE_H diff --git a/llvm/lib/Target/EVM/EVMCodegenPrepare.cpp b/llvm/lib/Target/EVM/EVMCodegenPrepare.cpp new file mode 100644 index 000000000000..4923d58e231c --- /dev/null +++ b/llvm/lib/Target/EVM/EVMCodegenPrepare.cpp @@ -0,0 +1,121 @@ +//===------ EVMCodegenPrepare.cpp - EVM CodeGen Prepare ---------*- 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 pass replaces general memory transfer intrinsics with +// EVM specific ones, which are custom lowered on ISel. This is required to +// overcome limitations of SelectionDAG::getMemcpy()/getMemmove() that breaks +// an assertion when a memory length is an immediate valued whose bit size is +// more than 64 bits. +// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/IntrinsicsEVM.h" +#include "llvm/Pass.h" + +#include "EVM.h" + +using namespace llvm; + +#define DEBUG_TYPE "evm-codegen-prepare" + +namespace llvm { +FunctionPass *createEVMCodegenPrepare(); + +} // namespace llvm + +namespace { +struct EVMCodegenPrepare : public FunctionPass { +public: + static char ID; + EVMCodegenPrepare() : FunctionPass(ID) { + initializeEVMCodegenPreparePass(*PassRegistry::getPassRegistry()); + } + bool runOnFunction(Function &F) override; + + StringRef getPassName() const override { + return "Final transformations before code generation"; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + FunctionPass::getAnalysisUsage(AU); + } + + void processMemTransfer(MemTransferInst *M); +}; +} // namespace + +char EVMCodegenPrepare::ID = 0; + +INITIALIZE_PASS(EVMCodegenPrepare, "evm-codegen-prepare", + "Final transformations before code generation", false, false) + +void EVMCodegenPrepare::processMemTransfer(MemTransferInst *M) { + // See if the source could be modified by this memmove potentially. + LLVM_DEBUG(dbgs() << "EVM codegenprepare: Replace:" << *M + << " with the target instinsic\n"); + unsigned SrcAS = M->getSourceAddressSpace(); + assert(M->getDestAddressSpace() == EVMAS::AS_HEAP); + + // If the length type is not i256, zext it. + Value *Len = M->getLength(); + Type *LenTy = Len->getType(); + Type *Int256Ty = Type::getInt256Ty(M->getContext()); + assert(LenTy->getIntegerBitWidth() <= Int256Ty->getIntegerBitWidth()); + if (LenTy != Int256Ty) { + if (LenTy->getIntegerBitWidth() < Int256Ty->getIntegerBitWidth()) { + IRBuilder<> Builder(M); + // We cannot use here M->setLength(), as it checks that new type of + // 'Length' is the same, so we use a base functionality of Intrinsics. + // It may look a bit hacky, but should be OK. + M->setArgOperand(2, Builder.CreateZExt(Len, Int256Ty)); + } + } + + Intrinsic::ID IntrID = Intrinsic::not_intrinsic; + switch (SrcAS) { + default: + llvm_unreachable("Unexpected source address space of memcpy/memset"); + break; + case EVMAS::AS_HEAP: + IntrID = Intrinsic::evm_memmoveas1as1; + break; + case EVMAS::AS_CALL_DATA: + IntrID = Intrinsic::evm_memcpyas1as2; + break; + case EVMAS::AS_RETURN_DATA: + IntrID = Intrinsic::evm_memcpyas1as3; + break; + case EVMAS::AS_CODE: + IntrID = Intrinsic::evm_memcpyas1as4; + break; + } + M->setCalledFunction(Intrinsic::getDeclaration(M->getModule(), IntrID)); +} + +bool EVMCodegenPrepare::runOnFunction(Function &F) { + bool Changed = false; + for (auto &BB : F) { + for (auto &I : BB) { + if (auto *M = dyn_cast(&I)) { + processMemTransfer(M); + Changed = true; + } + } + } + + return Changed; +} + +FunctionPass *llvm::createEVMCodegenPreparePass() { + return new EVMCodegenPrepare(); +} diff --git a/llvm/lib/Target/EVM/EVMConstantUnfolding.cpp b/llvm/lib/Target/EVM/EVMConstantUnfolding.cpp new file mode 100644 index 000000000000..786b1a426bfe --- /dev/null +++ b/llvm/lib/Target/EVM/EVMConstantUnfolding.cpp @@ -0,0 +1,735 @@ +//===----- EVMConstantUnfolding.cpp - Constant unfolding -------*- 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 implement large constants unfolding. +// +// Large constants are broken into sequences of operations +// to reduce bytecode size. For example, +// +// PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 +// (33 bytes) +// +// can be replaced with +// +// PUSH4 0xFFFFFFFF +// PUSH1 0xE0 +// SHL +// (8 bytes) +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "EVMCalculateModuleSize.h" +#include "EVMInstrInfo.h" +#include "EVMSubtarget.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "TargetInfo/EVMTargetInfo.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/CodeGen/MachineDominators.h" +#include "llvm/CodeGen/MachineLoopInfo.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/PassInstrumentation.h" +#include "llvm/InitializePasses.h" +#include "llvm/Support/Debug.h" +#include + +using namespace llvm; + +#define DEBUG_TYPE "evm-constant-unfolding" +#define PASS_NAME "EVM constant unfolding" + +STATISTIC(StatNumOfUnfoldings, "Number of unfolded constants"); +STATISTIC(StatCodeSizeReduction, "Total size reduction across all functions"); + +// The initial values of the following options were determined experimentally +// to allow constant unfolding in non-OptForSize mode without noticeably +// impacting performance. +static cl::opt + SizeReductionThreshold("evm-const-unfolding-size-reduction-threshold", + cl::Hidden, cl::init(2.5), + cl::desc("Minimum original-to-unfolded size ratio" + "required for constant unfolding when" + "optimizing for speed")); + +static cl::opt InstrNumLimitUnfoldInto( + "evm-const-unfolding-inst-num-limit", cl::Hidden, cl::init(4), + cl::desc("Maximum number of instructions an original" + "instruction can be unfolded into")); + +static cl::opt + LoopDepthLimit("evm-const-loop-depth-limit", cl::Hidden, cl::init(2), + cl::desc("The maximum loop depth at which constant" + "unfolding is still considered beneficial")); + +static cl::opt + CodeSizeLimit("evm-bytecode-sizelimit", cl::Hidden, cl::init(24576), + cl::desc("EVM contract bytecode size limit")); + +static cl::opt MetadataSize("evm-metadata-size", cl::Hidden, + cl::init(0), + cl::desc("EVM metadata size")); + +namespace { +using InstrsPerLoopDepthTy = SmallVector>; + +// Estimates the execution cost of EVM-style stack operations. +// Tracks instruction count, gas cost, and unfolded bytecode size. +// It abstracts gas accounting for pushes and simple arithmetic/logical +// operations. +class StackCostModel { +public: + StackCostModel() = default; + + void accountInstr(unsigned Opc, const TargetInstrInfo *TII) { + ++InstrCount; + const MCInstrDesc &Desc = TII->get(Opc); + ByteSize += Desc.getSize(); + Gas += EVMInstrInfo::getGasCost(Desc); + } + + unsigned getInstrCount() const { return InstrCount; } + unsigned getByteSize() const { return ByteSize; } + unsigned getGas() const { return Gas; } + +private: + unsigned InstrCount{0}; + unsigned ByteSize{0}; + unsigned Gas{0}; +}; + +// Builds and applies a sequence of machine instructions required to +// unfold a constant. Instruction generation is deferred using lambdas, +// allowing the TransformationCandidate object to be reused for repeated +// constants. As new instructions are added, the StackCostModel is used +// to track the accumulated cost. +class TransformationCandidate { +public: + TransformationCandidate(LLVMContext &Context, const TargetInstrInfo *TII) + : Context(Context), TII(TII) {} + + void addShl() { + constexpr unsigned Opc = EVM::SHL_S; + CostModel.accountInstr(Opc, TII); + BuildItems.push_back([this](MachineInstr &MI) { insertInstr(MI, Opc); }); + } + + void addShr() { + constexpr unsigned Opc = EVM::SHR_S; + CostModel.accountInstr(Opc, TII); + BuildItems.push_back([this](MachineInstr &MI) { insertInstr(MI, Opc); }); + } + + void addNot() { + constexpr unsigned Opc = EVM::NOT_S; + CostModel.accountInstr(Opc, TII); + BuildItems.push_back([this](MachineInstr &MI) { insertInstr(MI, Opc); }); + } + + void addImm(const APInt &Val) { + unsigned Opc = EVM::getStackOpcode(EVM::getPUSHOpcode(Val)); + CostModel.accountInstr(Opc, TII); + BuildItems.push_back([this, Opc, Val = Val](MachineInstr &MI) { + insertImmInstr(MI, Opc, Val); + }); + } + + // Applies queued build instruction steps to replace a given instruction. + void apply(MachineInstr &MI) const { + for (const auto &func : BuildItems) + func(MI); + } + + const StackCostModel &getCost() const { return CostModel; } + +private: + using BuildFunction = std::function; + + LLVMContext &Context; + const TargetInstrInfo *TII{}; + + StackCostModel CostModel; + SmallVector BuildItems; + + void insertInstr(MachineInstr &MI, unsigned Opc) { + BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), TII->get(Opc)); + } + + void insertImmInstr(MachineInstr &MI, unsigned Opc, const APInt &Val) { + auto NewMI = BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), TII->get(Opc)); + if (Opc != EVM::PUSH0_S) + NewMI.addCImm(ConstantInt::get(Context, Val)); + } +}; + +// Discovers, applies, and caches optimal constant unfolding +// transformations. +class ConstantUnfolder { +public: + explicit ConstantUnfolder(LLVMContext *Context) : Context(Context) {} + + unsigned getCodeSizeReduction() const { return OverallCodeReductionSize; } + + bool tryToUnfoldConstant(MachineInstr &MI, bool OptForSize, + const TargetInstrInfo *TII); + +private: + LLVMContext *Context{}; + + // The 'second' field can be set to 0 or 1, indicating whether to + // optimize for performance or size. + using TransformationKey = std::pair; + DenseMap> + TransformationCache; + + unsigned OverallCodeReductionSize{0}; + + const TransformationCandidate * + findOptimalTransformation(const APInt &Imm, bool OptForSize, + const TargetInstrInfo *TII); + + void reduceCodeSizeOn(unsigned Size) { + OverallCodeReductionSize += Size; + ++StatNumOfUnfoldings; + } +}; + +class EVMConstantUnfolding final : public ModulePass { +public: + static char ID; + + EVMConstantUnfolding() : ModulePass(ID) { + initializeEVMConstantUnfoldingPass(*PassRegistry::getPassRegistry()); + } + +private: + StringRef getPassName() const override { return PASS_NAME; } + + bool runOnModule(Module &M) override; + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + AU.addPreserved(); + AU.setPreservesAll(); + ModulePass::getAnalysisUsage(AU); + } +}; +} // end anonymous namespace + +char EVMConstantUnfolding::ID = 0; + +INITIALIZE_PASS_BEGIN(EVMConstantUnfolding, DEBUG_TYPE, PASS_NAME, false, false) +INITIALIZE_PASS_DEPENDENCY(MachineModuleInfoWrapperPass) +INITIALIZE_PASS_END(EVMConstantUnfolding, DEBUG_TYPE, PASS_NAME, false, false) + +ModulePass *llvm::createEVMConstantUnfolding() { + return new EVMConstantUnfolding(); +} + +static bool isBetterCandidate(const TransformationCandidate &A, + const TransformationCandidate &B, + bool OptForSize) { + static constexpr unsigned Weight = 2; + const StackCostModel &CostA = A.getCost(); + const StackCostModel &CostB = B.getCost(); + unsigned ScoreA = 0; + unsigned ScoreB = 0; + + if (OptForSize) { + ScoreA = (CostA.getByteSize() * Weight) + CostA.getGas(); + ScoreB = (CostB.getByteSize() * Weight) + CostB.getGas(); + } else { + ScoreA = CostA.getByteSize() + (Weight * CostA.getGas()); + ScoreB = CostB.getByteSize() + (Weight * CostB.getGas()); + } + if (ScoreA != ScoreB) + return ScoreA < ScoreB; + + return CostA.getByteSize() < CostB.getByteSize(); +} + +const TransformationCandidate * +ConstantUnfolder::findOptimalTransformation(const APInt &Imm, bool OptForSize, + const TargetInstrInfo *TII) { + if (auto It = TransformationCache.find({Imm, OptForSize}); + It != TransformationCache.end()) { + LLVM_DEBUG( + { dbgs() << " Retrieving transformation from the cache\n"; }); + + return It->second.get(); + } + + // Decompose a given immediate operand of the form: + // + // 0x0000001...100000000 + // + // into: + // - trailing and leading zero bits + // - 'Val' part, that starts and ends with '1' + // - abs('Val') + // + unsigned Ones = Imm.popcount(); + unsigned TrailZ = Imm.countTrailingZeros(); + unsigned LeadZ = Imm.countLeadingZeros(); + APInt Val = Imm.extractBits(Imm.getBitWidth() - TrailZ - LeadZ, TrailZ); + unsigned ValLen = Val.getActiveBits(); + bool IsMask = ((Ones + LeadZ + TrailZ) == Imm.getBitWidth()); + assert(ValLen == (Imm.getBitWidth() - TrailZ - LeadZ)); + assert(Val.isNegative()); + + SmallVector, 8> Transformations; + + // 1. A transformation that represents an immediate value as: + // + // (~(AbsVal - 1) << shift_l) >> shift_r + // + // For example, + // + // 0x00000000FFFFFFFFFFFFFF000000000000000000000000000000000000000000 + // + // is represented as: + // + // (~0 << 200) >> 32 + // + // Cost: + // PUSH AbsVal // 1 + AbsValLen + // NOT // 1 + // PUSH1 shiftl // 2 + // SHR // 1 + // -------------------- // Optional + // PUSH1 shiftr // 2 + // SHL // 1 + // + { + auto Tr = std::make_unique(*Context, TII); + assert(!Val.abs().isZero()); + Tr->addImm(Val.abs() - 1); + Tr->addNot(); + Tr->addImm(APInt(256, 256 - ValLen)); + Tr->addShl(); + if (LeadZ) { + Tr->addImm(APInt(256, LeadZ)); + Tr->addShr(); + } + Transformations.emplace_back(std::move(Tr)); + } + + // 2. A transformation that represents an immediate value in the + // form of a right-shifted mask. + // + // For example, + // + // 0x0000000000000000000000000000000000000000000000000000FFFFFFFFFFFF + // + // is represented as: + // + // (~0) >> 192 + // + // Cost: + // PUSH0 // 1 + // NOT // 1 + // PUSH1 shift // 2 + // SHR // 1 + // + if (IsMask && !TrailZ) { + assert(ValLen != 256); + auto Tr = std::make_unique(*Context, TII); + Tr->addImm(APInt::getZero(256)); + Tr->addNot(); + Tr->addImm(APInt(256, 256 - ValLen)); + Tr->addShr(); + Transformations.emplace_back(std::move(Tr)); + } + + // 3. A transformation that expresses an immediate value as a smaller + // bit-width value shifted to the left + // + // For example, + // + // 0xFFFFFF0000000000000000000000000000000000000000000000000000000000 + // + // is represented as: + // + // 0xFFFFFF << 192 + // + // Cost: + // PUSH Val // 1 + ValLen + // PUSH1 shift // 2 + // SHL // 1 + // + if (TrailZ) { + auto Tr = std::make_unique(*Context, TII); + Tr->addImm(Val); + Tr->addImm(APInt(256, TrailZ)); + Tr->addShl(); + Transformations.emplace_back(std::move(Tr)); + } + + if (!LeadZ) { + // 4. A transformation that represents an immediate value in the + // form of bit-reversing. + // + // For example, + // + // 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE + // + // is represented as: + // + // ~0x01 + // + // Cost: + // PUSH // 1 InvImm + // NOT // 1 + // + { + auto Tr = std::make_unique(*Context, TII); + Tr->addImm(~Imm); + Tr->addNot(); + Transformations.emplace_back(std::move(Tr)); + } + + // 5. A transformation that represents an immediate value by reversing its + // bits and representing the reversed portion using a left shift + // operation. + // + // For example, + // + // 0xFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + // + // is represented as: + // + // ~(0x01 << 155) + // + // Cost: + // PUSH // 1 InvImmVal + // PUSH1 shift // 2 + // SHL // 1 + // NOT // 1 + // + { + auto Tr = std::make_unique(*Context, TII); + APInt ImmNot = ~Imm; + unsigned Tz = ImmNot.countTrailingZeros(); + unsigned Lz = ImmNot.countLeadingZeros(); + APInt ImmNotVal = ImmNot.extractBits(ImmNot.getBitWidth() - Tz - Lz, Tz); + Tr->addImm(ImmNotVal); + Tr->addImm(APInt(256, Tz)); + Tr->addShl(); + Tr->addNot(); + Transformations.emplace_back(std::move(Tr)); + } + } + + // 6. No transformation, leave immediate as is. + { + auto Tr = std::make_unique(*Context, TII); + Tr->addImm(Imm); + Transformations.emplace_back(std::move(Tr)); + } + + // Find optimal transformation. + auto *OptIt = std::min_element(Transformations.begin(), Transformations.end(), + [OptForSize](const auto &A, const auto &B) { + return isBetterCandidate(*A, *B, OptForSize); + }); + +#ifndef NDEBUG + LLVM_DEBUG({ dbgs() << " Available transformations:\n"; }); + for (const auto &Tr : Transformations) { + const StackCostModel &Cost = Tr->getCost(); + LLVM_DEBUG({ + dbgs() << " [size: " << Cost.getByteSize() + << ", instr count: " << Cost.getInstrCount() + << ", gas: " << Cost.getGas() << "]\n"; + }); + } +#endif // NDEBUG + + const TransformationCandidate *Tr = OptIt->get(); + [[maybe_unused]] auto Res = + TransformationCache.try_emplace({Imm, OptForSize}, std::move(*OptIt)); + assert(Res.second); + + return Tr; +} + +static bool isProfitableToTranform(const APInt &Imm, const StackCostModel &Cost, + bool OptForSize) { + if (OptForSize) + return true; + + unsigned OrigSize = (alignTo(Imm.getActiveBits(), 8) / 8) + 1; + // When optimizing for speed, only unfold constants if it reduces the size + // by a factor of at least the 'SizeReductionThreshold' and keeps the + // instruction count to 'InstrNumLimitUnfoldInto' or fewer. + if ((static_cast(OrigSize) / static_cast(Cost.getByteSize()) < + SizeReductionThreshold) || + Cost.getInstrCount() > InstrNumLimitUnfoldInto) + return false; + + return true; +} + +bool ConstantUnfolder::tryToUnfoldConstant(MachineInstr &MI, bool OptForSize, + const TargetInstrInfo *TII) { + const APInt Imm = MI.getOperand(0).getCImm()->getValue().zext(256); + unsigned OrigSize = (alignTo(Imm.getActiveBits(), 8) / 8) + 1; + assert(Imm.getActiveBits() > 4 * 8); + + // Check for the -1 value early + if (Imm.isAllOnes()) { + auto Tr = std::make_unique(*Context, TII); + Tr->addImm(APInt::getZero(256)); + Tr->addNot(); + Tr->apply(MI); + MI.eraseFromParent(); + assert(OrigSize > Tr->getCost().getByteSize()); + reduceCodeSizeOn(OrigSize - Tr->getCost().getByteSize()); + + LLVM_DEBUG({ dbgs() << " Transforming -1 to ~0\n"; }); + return true; + } + + const TransformationCandidate *OptTransformation = + findOptimalTransformation(Imm, OptForSize, TII); + + const StackCostModel &OptCost = OptTransformation->getCost(); + LLVM_DEBUG({ + dbgs() << " Optimal transformation:\n" + << " [size: " << OptCost.getByteSize() + << ", instr count: " << OptCost.getInstrCount() + << ", gas: " << OptCost.getGas() << "]\n"; + }); + + if (OptCost.getInstrCount() == 1) { + LLVM_DEBUG({ dbgs() << " Skipping identity transformation\n"; }); + return false; + } + + if (isProfitableToTranform(Imm, OptCost, OptForSize)) { + OptTransformation->apply(MI); + MI.eraseFromParent(); + assert(OrigSize > OptCost.getByteSize()); + reduceCodeSizeOn(OrigSize - OptCost.getByteSize()); + return true; + } + + LLVM_DEBUG({ + dbgs() << " Transformation is omitted as its effect does not meet the" + << " required reduction threshold\n"; + }); + + return false; +} + +static bool shouldSkip(const MachineInstr &MI) { + if (!EVMInstrInfo::isPush(&MI)) + return true; + + // It’s not practical to check small constants, since the default + // instructions are cheapest in those cases. The limit is based on the + // fact that the most compact transformation for representing a + // constant requires at least 5 bytes. + switch (MI.getOpcode()) { + case EVM::PUSH0_S: + case EVM::PUSH1_S: + case EVM::PUSH2_S: + case EVM::PUSH3_S: + case EVM::PUSH4_S: { + return true; + } + default: + return false; + } +} + +// This helper class organizes a MF’s instructions by their loop nesting depth, +// as reported by MachineLoopInfo. It builds a SmallVector of buckets, where +// each bucket contains the MIs for a specific loop depth (0 = out of loop, +// 1 - top level loop, etc.). +class LoopDepthInstrCache { +public: + LoopDepthInstrCache(MachineFunction &MF, const MachineLoopInfo &MLI) { + unsigned MaxLoopDepth = 0; + for (auto *ML : MLI.getLoopsInPreorder()) + MaxLoopDepth = std::max(MaxLoopDepth, ML->getLoopDepth()); + + InstrsPerLoopDepth.resize(MaxLoopDepth + 1); + for (MachineBasicBlock &MBB : MF) { + for (MachineInstr &MI : MBB) { + if (shouldSkip(MI)) + continue; + + unsigned Depth = MLI.getLoopDepth(&MBB); + assert(Depth < InstrsPerLoopDepth.size()); + InstrsPerLoopDepth[Depth].push_back(&MI); + } + } + + LLVM_DEBUG({ + dbgs() << "Instructions in function: " << MF.getName() << '\n'; + for (unsigned Depth = 0; Depth < InstrsPerLoopDepth.size(); ++Depth) { + dbgs() << " loop depth: " << Depth << '\n'; + for (const MachineInstr *MI : InstrsPerLoopDepth[Depth]) + dbgs() << " " << *MI << '\n'; + } + }); + } + + LoopDepthInstrCache(const LoopDepthInstrCache &) = delete; + LoopDepthInstrCache &operator=(const LoopDepthInstrCache &) = delete; + LoopDepthInstrCache(LoopDepthInstrCache &&) noexcept = default; + LoopDepthInstrCache &operator=(LoopDepthInstrCache &&) noexcept = default; + ~LoopDepthInstrCache() = default; + + const InstrsPerLoopDepthTy &getInstructionsPerLoopDepth() const { + return InstrsPerLoopDepth; + } + + SmallVector getAllInstructions() const { + SmallVector Instrs; + for (const SmallVector &Bucket : InstrsPerLoopDepth) + Instrs.append(Bucket.begin(), Bucket.end()); + + return Instrs; + } + + DenseSet &getVisited() { return Visited; } + +private: + InstrsPerLoopDepthTy InstrsPerLoopDepth; + DenseSet Visited; +}; + +static bool processInstructions(ConstantUnfolder &Unfolder, + const SmallVector &Instrs, + DenseSet &Visited, + bool OptForSize, const TargetInstrInfo *TII) { + bool Changed = false; + for (MachineInstr *MI : Instrs) { + if (Visited.count(MI)) + continue; + + LLVM_DEBUG({ dbgs() << " Checking " << *MI; }); + + if (Unfolder.tryToUnfoldConstant(*MI, OptForSize, TII)) { + Visited.insert(MI); + Changed = true; + } + } + + return Changed; +} + +static bool runImpl(Module &M, MachineModuleInfo &MMI) { + bool Changed = false; + ConstantUnfolder Unfolder(&M.getContext()); + + // Metadata size is included into the bytecode size. + const unsigned ModuleCodeSize = + EVM::calculateModuleCodeSize(M, MMI) + MetadataSize; + + // Collect PUSH instructions to process. + DenseMap> + InstrCacheMap; + + for (Function &F : M) { + MachineFunction *MF = MMI.getMachineFunction(F); + if (!MF) + continue; + + // Compute MachineLoopInfo on the fly, as it's not available on the + // Module pass level. + auto OwnedMDT = std::make_unique(); + OwnedMDT->getBase().recalculate(*MF); + MachineDominatorTree *MDT = OwnedMDT.get(); + + auto OwnedMLI = std::make_unique(); + OwnedMLI->analyze(MDT->getBase()); + const MachineLoopInfo *MLI = OwnedMLI.get(); + + [[maybe_unused]] auto It = InstrCacheMap.try_emplace( + MF, std::make_unique(*MF, *MLI)); + assert(It.second); + } + + LLVM_DEBUG({ + dbgs() << "*** Running constant unfolding in the default mode ***\n"; + dbgs() << "*** Initial module size: " << ModuleCodeSize << " ***\n"; + }); + + // First, process all PUSH instructions in the default mode, selecting + // unfolding heuristics based on whether the OptSize flag is set for + // the MachineFunction. + for (auto &[MF, Cache] : InstrCacheMap) { + LLVM_DEBUG({ dbgs() << " Checking function: " << MF->getName() << '\n'; }); + + bool OptForSize = MF->getFunction().hasOptSize(); + const EVMInstrInfo *TII = MF->getSubtarget().getInstrInfo(); + Changed |= processInstructions(Unfolder, Cache->getAllInstructions(), + Cache->getVisited(), OptForSize, TII); + } + + unsigned CodeSizeReduction = Unfolder.getCodeSizeReduction(); + if (ModuleCodeSize < CodeSizeLimit + CodeSizeReduction) { + StatCodeSizeReduction = CodeSizeReduction; + return Changed; + } + + LLVM_DEBUG({ + dbgs() << "*** Current module size is " + << ModuleCodeSize - CodeSizeReduction + << ", which still exceeds the limit, falling back to " + "size-minimization mode ***\n"; + }); + + // First, process all PUSH instructions in the default mode, selecting + // unfolding heuristics based on whether the OptSize flag is set for + // the MachineFunction. + for (unsigned LoopDepth = 0; LoopDepth <= LoopDepthLimit; ++LoopDepth) { + LLVM_DEBUG({ + dbgs() << "*** Running constant unfolding in " + "size-minimization mode at loop depth " + << LoopDepth << " ***\n"; + }); + + for (auto &[MF, Cache] : InstrCacheMap) { + LLVM_DEBUG( + { dbgs() << " Checking function: " << MF->getName() << '\n'; }); + + const InstrsPerLoopDepthTy &InstrsPerLoopDepth = + Cache->getInstructionsPerLoopDepth(); + if (LoopDepth >= InstrsPerLoopDepth.size()) + continue; + + const EVMInstrInfo *TII = MF->getSubtarget().getInstrInfo(); + Changed |= + processInstructions(Unfolder, InstrsPerLoopDepth[LoopDepth], + Cache->getVisited(), /*OptForSize=*/true, TII); + + CodeSizeReduction = Unfolder.getCodeSizeReduction(); + if (ModuleCodeSize < CodeSizeLimit + CodeSizeReduction) { + StatCodeSizeReduction = CodeSizeReduction; + return Changed; + } + } + + LLVM_DEBUG({ + dbgs() << "*** Current module size is " + << ModuleCodeSize - CodeSizeReduction << " ***\n"; + }); + } + + return Changed; +} + +bool EVMConstantUnfolding::runOnModule(Module &M) { + LLVM_DEBUG({ dbgs() << "********** " << PASS_NAME << " **********\n"; }); + + return runImpl(M, getAnalysis().getMMI()); +} diff --git a/llvm/lib/Target/EVM/EVMFinalizeStackFrames.cpp b/llvm/lib/Target/EVM/EVMFinalizeStackFrames.cpp new file mode 100644 index 000000000000..0ece97c31870 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMFinalizeStackFrames.cpp @@ -0,0 +1,191 @@ +//===----- EVMFinalizeStackFrames.cpp - Finalize stack frames --*- 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 pass calculates stack size for each function and replaces frame indices +// with their offsets. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "TargetInfo/EVMTargetInfo.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/Module.h" +#include "llvm/InitializePasses.h" + +using namespace llvm; + +#define DEBUG_TYPE "evm-finalize-stack-frames" +#define PASS_NAME "EVM finalize stack frames" + +static cl::opt + StackRegionSize("evm-stack-region-size", cl::Hidden, cl::init(0), + cl::desc("Allocated stack region size")); + +static cl::opt + StackRegionOffset("evm-stack-region-offset", cl::Hidden, + cl::init(std::numeric_limits::max()), + cl::desc("Offset where the stack region starts")); + +namespace { +class EVMFinalizeStackFrames : public ModulePass { +public: + static char ID; + + EVMFinalizeStackFrames() : ModulePass(ID) { + initializeEVMFinalizeStackFramesPass(*PassRegistry::getPassRegistry()); + } + + bool runOnModule(Module &M) override; + + StringRef getPassName() const override { return PASS_NAME; } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + AU.addPreserved(); + AU.setPreservesAll(); + ModulePass::getAnalysisUsage(AU); + } + +private: + /// Calculate the stack allocation offsets for all stack objects. + uint64_t calculateFrameObjectOffsets(MachineFunction &MF) const; + + /// Replace frame indices with their corresponding offsets. + void replaceFrameIndices(MachineFunction &MF, + uint64_t StackRegionStart) const; +}; +} // end anonymous namespace + +char EVMFinalizeStackFrames::ID = 0; + +INITIALIZE_PASS_BEGIN(EVMFinalizeStackFrames, DEBUG_TYPE, PASS_NAME, false, + false) +INITIALIZE_PASS_DEPENDENCY(MachineModuleInfoWrapperPass) +INITIALIZE_PASS_END(EVMFinalizeStackFrames, DEBUG_TYPE, PASS_NAME, false, false) + +ModulePass *llvm::createEVMFinalizeStackFrames() { + return new EVMFinalizeStackFrames(); +} + +uint64_t +EVMFinalizeStackFrames::calculateFrameObjectOffsets(MachineFunction &MF) const { + // Bail out if there are no stack objects. + auto &MFI = MF.getFrameInfo(); + if (!MFI.hasStackObjects()) + return 0; + + // Set the stack offsets for each object. + uint64_t StackSize = 0; + for (int I = 0, E = MFI.getObjectIndexEnd(); I != E; ++I) { + if (MFI.isDeadObjectIndex(I)) + continue; + + MFI.setObjectOffset(I, StackSize); + StackSize += MFI.getObjectSize(I); + } + + assert(StackSize % 32 == 0 && "Stack size must be a multiple of 32 bytes"); + return StackSize; +} + +void EVMFinalizeStackFrames::replaceFrameIndices( + MachineFunction &MF, uint64_t StackRegionStart) const { + auto &MFI = MF.getFrameInfo(); + assert(MFI.hasStackObjects() && + "Cannot replace frame indices without stack objects"); + + const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo(); + for (MachineBasicBlock &MBB : MF) { + for (MachineInstr &MI : make_early_inc_range(MBB)) { + if (MI.getOpcode() != EVM::PUSH_FRAME) + continue; + + assert(MI.getNumOperands() == 1 && "PUSH_FRAME must have one operand"); + MachineOperand &FIOp = MI.getOperand(0); + assert(FIOp.isFI() && "Expected a frame index operand"); + + // Replace the frame index with the corresponding stack offset. + APInt Offset(256, + StackRegionStart + MFI.getObjectOffset(FIOp.getIndex())); + unsigned PushOpc = EVM::getPUSHOpcode(Offset); + auto NewMI = BuildMI(MBB, MI, MI.getDebugLoc(), + TII->get(EVM::getStackOpcode(PushOpc))); + if (PushOpc != EVM::PUSH0) + NewMI.addCImm(ConstantInt::get(MF.getFunction().getContext(), Offset)); + + MI.eraseFromParent(); + } + } +} + +bool EVMFinalizeStackFrames::runOnModule(Module &M) { + LLVM_DEBUG({ dbgs() << "********** Finalize stack frames **********\n"; }); + + // Check if options for stack region size and offset are set correctly. + if (StackRegionSize.getNumOccurrences()) { + if (!StackRegionOffset.getNumOccurrences()) + report_fatal_error("Stack region offset must be set when stack region " + "size is set. Use --evm-stack-region-offset to set " + "the offset."); + + if (StackRegionOffset % 32 != 0) + report_fatal_error("Stack region offset must be a multiple of 32 bytes."); + } + + uint64_t TotalStackSize = 0; + MachineModuleInfo &MMI = getAnalysis().getMMI(); + SmallVector, 8> ToReplaceFI; + + // Calculate the stack size for each function. + for (Function &F : M) { + MachineFunction *MF = MMI.getMachineFunction(F); + if (!MF) + continue; + + uint64_t StackSize = calculateFrameObjectOffsets(*MF); + if (StackSize == 0) + continue; + + uint64_t StackRegionStart = StackRegionOffset + TotalStackSize; + ToReplaceFI.emplace_back(MF, StackRegionStart); + TotalStackSize += StackSize; + + LLVM_DEBUG({ + dbgs() << "Stack size for function " << MF->getName() + << " is: " << StackSize + << " and starting offset is: " << StackRegionStart << "\n"; + }); + } + LLVM_DEBUG({ dbgs() << "Total stack size: " << TotalStackSize << "\n"; }); + + // Check if it is valid to replace frame indices. + if (TotalStackSize > 0 && TotalStackSize > StackRegionSize) { + report_evm_stack_error( + "Total stack size: " + Twine(TotalStackSize) + + " is larger than the allocated stack region size: " + + Twine(StackRegionSize), + TotalStackSize); + } + if (StackRegionSize > TotalStackSize) + errs() << "warning: allocated stack region size: " + + Twine(StackRegionSize) + + " is larger than the total stack size: " + + Twine(TotalStackSize) + "\n"; + + // Replace frame indices with their offsets. + for (auto &[MF, StackRegionStart] : ToReplaceFI) + replaceFrameIndices(*MF, StackRegionStart); + + return TotalStackSize > 0; +} diff --git a/llvm/lib/Target/EVM/EVMFrameLowering.cpp b/llvm/lib/Target/EVM/EVMFrameLowering.cpp new file mode 100644 index 000000000000..95fda58e365a --- /dev/null +++ b/llvm/lib/Target/EVM/EVMFrameLowering.cpp @@ -0,0 +1,23 @@ +//===-------- EVMFrameLowering.cpp - EVM Frame Information ----------------===// +// +// 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 the EVM implementation of TargetFrameLowering class. +// +//===----------------------------------------------------------------------===// + +#include "EVMFrameLowering.h" + +using namespace llvm; + +bool EVMFrameLowering::hasFP(const MachineFunction &MF) const { return false; } + +void EVMFrameLowering::emitPrologue(MachineFunction &MF, + MachineBasicBlock &MBB) const {} + +void EVMFrameLowering::emitEpilogue(MachineFunction &MF, + MachineBasicBlock &MBB) const {} diff --git a/llvm/lib/Target/EVM/EVMFrameLowering.h b/llvm/lib/Target/EVM/EVMFrameLowering.h new file mode 100644 index 000000000000..2be3491fd3de --- /dev/null +++ b/llvm/lib/Target/EVM/EVMFrameLowering.h @@ -0,0 +1,35 @@ +//==----- EVMFrameLowering.h - Define frame lowering for EVM --*- 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_LIB_TARGET_EVM_EVMFRAMELOWERING_H +#define LLVM_LIB_TARGET_EVM_EVMFRAMELOWERING_H + +#include "llvm/CodeGen/TargetFrameLowering.h" + +namespace llvm { +class EVMFrameLowering final : public TargetFrameLowering { +protected: +public: + explicit EVMFrameLowering() + : TargetFrameLowering(TargetFrameLowering::StackGrowsUp, + /*StackAl=*/Align(32), + /*LAO=*/0, + /*TransAl=*/Align(32)) {} + + /// emitProlog/emitEpilog - These methods insert prolog and epilog code into + /// the function. + void emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const override; + + void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const override; + + bool hasFP(const MachineFunction &MF) const override; +}; + +} // namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_EVMFRAMELOWERING_H diff --git a/llvm/lib/Target/EVM/EVMISD.def b/llvm/lib/Target/EVM/EVMISD.def new file mode 100644 index 000000000000..b2e3af72cfe2 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMISD.def @@ -0,0 +1,23 @@ +//----------------- EVMISD.def - EVM ISD ---------------------------*- 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 describes the various EVM ISD node types. +// +//===----------------------------------------------------------------------===// + +// NOTE: NO INCLUDE GUARD DESIRED! + +HANDLE_NODETYPE(FCALL) +HANDLE_NODETYPE(RET) +HANDLE_NODETYPE(ARGUMENT) +HANDLE_NODETYPE(SIGNEXTEND) +HANDLE_NODETYPE(TARGET_ADDR_WRAPPER) +HANDLE_NODETYPE(MEMCPY_CALL_DATA) +HANDLE_NODETYPE(MEMCPY_CODE) +HANDLE_NODETYPE(MEMCPY_HEAP) +HANDLE_NODETYPE(MEMCPY_RETURN_DATA) diff --git a/llvm/lib/Target/EVM/EVMISelDAGToDAG.cpp b/llvm/lib/Target/EVM/EVMISelDAGToDAG.cpp new file mode 100644 index 000000000000..ff138174ff96 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMISelDAGToDAG.cpp @@ -0,0 +1,102 @@ +//===-------- EVMISelDAGToDAG.cpp - A dag to dag inst selector for EVM ----===// +// +// 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 instruction selector for the EVM target. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "EVMTargetMachine.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm/CodeGen/SelectionDAGISel.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +using namespace llvm; + +#define DEBUG_TYPE "evm-isel" +#define PASS_NAME "EVM DAG->DAG Pattern Instruction Selection" + +// EVMDAGToDAGISel - EVM specific code to select EVM machine +// instructions for SelectionDAG operations. + +namespace { +class EVMDAGToDAGISel final : public SelectionDAGISel { +public: + EVMDAGToDAGISel(EVMTargetMachine &TM, CodeGenOptLevel OptLevel) + : SelectionDAGISel(TM, OptLevel) {} + +private: + + // Include the pieces autogenerated from the target description. +#include "EVMGenDAGISel.inc" + + // Main method to transform nodes into machine nodes. + void Select(SDNode *N) override; +}; + +class EVMDAGToDAGISelLegacy : public SelectionDAGISelLegacy { +public: + static char ID; + explicit EVMDAGToDAGISelLegacy(EVMTargetMachine &TM, CodeGenOptLevel OptLevel) + : SelectionDAGISelLegacy( + ID, std::make_unique(TM, OptLevel)) {} +}; +} // end anonymous namespace + +char EVMDAGToDAGISelLegacy::ID; + +void EVMDAGToDAGISel::Select(SDNode *Node) { + const SDLoc DL(Node); + + // If we have a custom node, we already have selected! + if (Node->isMachineOpcode()) { + LLVM_DEBUG(errs() << "== "; Node->dump(CurDAG); errs() << "\n"); + Node->setNodeId(-1); + return; + } + + switch (Node->getOpcode()) { + case EVMISD::FCALL: { + // FCALL has both variable operands and variable results, but ISel only + // supports one or the other. Split calls into two nodes glued together, one + // for the operands and one for the results. These two nodes will be + // recombined in a custom inserter hook into a single MachineInstr. + SmallVector Ops; + for (size_t I = 1; I < Node->getNumOperands(); ++I) { + SDValue Op = Node->getOperand(I); + if (I == 1 && Op->getOpcode() == EVMISD::TARGET_ADDR_WRAPPER) + Op = Op->getOperand(0); + Ops.push_back(Op); + } + + // Add the chain last + Ops.push_back(Node->getOperand(0)); + MachineSDNode *CallParams = + CurDAG->getMachineNode(EVM::CALL_PARAMS, DL, MVT::Glue, Ops); + + SDValue Link(CallParams, 0); + MachineSDNode *CallResults = + CurDAG->getMachineNode(EVM::CALL_RESULTS, DL, Node->getVTList(), Link); + ReplaceNode(Node, CallResults); + return; + } + + default: + break; + } + + // Select the default instruction + SelectCode(Node); +} + +// createEVMISelDag - This pass converts a legalized DAG into a +// EVM-specific DAG, ready for instruction scheduling. +FunctionPass *llvm::createEVMISelDag(EVMTargetMachine &TM, + CodeGenOptLevel OptLevel) { + return new EVMDAGToDAGISelLegacy(TM, OptLevel); +} diff --git a/llvm/lib/Target/EVM/EVMISelLowering.cpp b/llvm/lib/Target/EVM/EVMISelLowering.cpp new file mode 100644 index 000000000000..db460f8c4025 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMISelLowering.cpp @@ -0,0 +1,957 @@ +//===-------- EVMISelLowering.cpp - EVM DAG Lowering 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 EVMTargetLowering class. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" + +#include "EVMISelLowering.h" +#include "EVMMachineFunctionInfo.h" +#include "EVMTargetMachine.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm/IR/DiagnosticInfo.h" +#include "llvm/IR/IntrinsicsEVM.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCSymbol.h" + +using namespace llvm; + +#define DEBUG_TYPE "evm-lower" + +EVMTargetLowering::EVMTargetLowering(const TargetMachine &TM, + const EVMSubtarget &STI) + : TargetLowering(TM), Subtarget(&STI) { + + setSchedulingPreference(Sched::RegPressure); + + // Booleans always contain 0 or 1. + setBooleanContents(ZeroOrOneBooleanContent); + + // Set up the register classes. + addRegisterClass(MVT::i256, &EVM::GPRRegClass); + + // Compute derived properties from the register classes + computeRegisterProperties(STI.getRegisterInfo()); + + // Provide all sorts of operation actions + setStackPointerRegisterToSaveRestore(EVM::SP); + + // By default, expand all i256bit operations + for (unsigned Opc = 0; Opc < ISD::BUILTIN_OP_END; ++Opc) + setOperationAction(Opc, MVT::i256, Expand); + + // Legal operations + setOperationAction({ISD::ADD, ISD::SUB, ISD::MUL, ISD::AND, ISD::OR, ISD::XOR, + ISD::SHL, ISD::SRL, ISD::SRA, ISD::SDIV, ISD::UDIV, + ISD::UREM, ISD::SREM, ISD::SETCC, ISD::SELECT, + ISD::FrameIndex}, + MVT::i256, Legal); + + for (auto CC : {ISD::SETULT, ISD::SETUGT, ISD::SETLT, ISD::SETGT, ISD::SETGE, + ISD::SETUGE, ISD::SETLE, ISD::SETULE, ISD::SETEQ, ISD::SETNE}) + setCondCodeAction(CC, MVT::i256, Legal); + + // Don't use constant pools. + // TODO: Probably this needs to be relaxed in the future. + setOperationAction(ISD::Constant, MVT::i256, Legal); + + // Sign-extension of a boolean value requires expansion, as we cannot use + // EVM::SIGNEXTEND instruction here. + setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i1, Expand); + + for (const MVT VT : + {MVT::i1, MVT::i8, MVT::i16, MVT::i32, MVT::i64, MVT::i128}) { + setOperationAction(ISD::MERGE_VALUES, VT, Promote); + setTruncStoreAction(MVT::i256, VT, Custom); + } + + setOperationAction(ISD::UMUL_LOHI, MVT::i256, Custom); + setOperationAction(ISD::BSWAP, MVT::i256, Custom); + setOperationAction(ISD::CTPOP, MVT::i256, Custom); + + // Custom lowering of extended loads. + for (const MVT VT : MVT::integer_valuetypes()) { + setLoadExtAction(ISD::SEXTLOAD, MVT::i256, VT, Custom); + setLoadExtAction(ISD::ZEXTLOAD, MVT::i256, VT, Custom); + setLoadExtAction(ISD::EXTLOAD, MVT::i256, VT, Custom); + } + + // Custom lowering operations. + setOperationAction({ISD::GlobalAddress, ISD::LOAD, ISD::STORE}, MVT::i256, + Custom); + + setOperationAction(ISD::INTRINSIC_VOID, MVT::Other, Custom); + setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::Other, Custom); + + // Custom DAGCombine patterns. + setTargetDAGCombine(ISD::SELECT); + + setJumpIsExpensive(true); + setMaximumJumpTableSize(0); +} + +const char *EVMTargetLowering::getTargetNodeName(unsigned Opcode) const { + switch (static_cast(Opcode)) { + case EVMISD::FIRST_NUMBER: + break; +#define HANDLE_NODETYPE(NODE) \ + case EVMISD::NODE: \ + return "EVMISD::" #NODE; +#include "EVMISD.def" +#undef HANDLE_NODETYPE + } + return nullptr; +} + +bool EVMTargetLowering::isLegalAddressingMode(const DataLayout &DL, + const AddrMode &AM, Type *Ty, + unsigned AS, + Instruction *I) const { + // We don't support global and scaled addresses. + if (AM.BaseGV || AM.Scale) + return false; + + // For CALLDATALOAD, benchmark numbers showed it is not profitable to set + // that we support r + imm. + if (AS == EVMAS::AS_CALL_DATA) + return !(AM.HasBaseReg && AM.BaseOffs); + + // Allow r + imm addressing mode for other address spaces. This can help + // to reduce register pressure by sinking add with immediate to its uses, + // minimizing the need for extra registers and reducing spills and reloads. + return true; +} + +//===----------------------------------------------------------------------===// +// EVM Lowering private implementation. +//===----------------------------------------------------------------------===// + +static void fail(const SDLoc &DL, SelectionDAG &DAG, const char *msg) { + MachineFunction &MF = DAG.getMachineFunction(); + DAG.getContext()->diagnose( + DiagnosticInfoUnsupported(MF.getFunction(), msg, DL.getDebugLoc())); +} + +SDValue EVMTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const { + const SDLoc DL(Op); + switch (Op.getOpcode()) { + default: + llvm_unreachable("Unimplemented operation lowering"); + case ISD::GlobalAddress: + return LowerGlobalAddress(Op, DAG); + case ISD::INTRINSIC_WO_CHAIN: + return lowerINTRINSIC_WO_CHAIN(Op, DAG); + case ISD::LOAD: + return LowerLOAD(Op, DAG); + case ISD::STORE: + return LowerSTORE(Op, DAG); + case ISD::UMUL_LOHI: + return LowerUMUL_LOHI(Op, DAG); + case ISD::INTRINSIC_VOID: + return LowerINTRINSIC_VOID(Op, DAG); + case ISD::CTPOP: + return LowerCTPOP(Op, DAG); + case ISD::BSWAP: + return LowerBSWAP(Op, DAG); + } +} + +SDValue EVMTargetLowering::LowerBSWAP(SDValue BSWAP, SelectionDAG &DAG) const { + SDNode *N = BSWAP.getNode(); + SDLoc dl(N); + EVT VT = N->getValueType(0); + SDValue Op = N->getOperand(0); + EVT SHVT = getShiftAmountTy(VT, DAG.getDataLayout()); + + assert(VT == MVT::i256 && "Unexpected type for bswap"); + + std::array Tmp; + + for (int i = 32; i >= 17; i--) + Tmp[i] = DAG.getNode(ISD::SHL, dl, VT, Op, + DAG.getConstant(((i - 17) * 16) + 8, dl, SHVT)); + + for (int i = 16; i >= 1; i--) + Tmp[i] = DAG.getNode(ISD::SRL, dl, VT, Op, + DAG.getConstant(((16 - i) * 16) + 8, dl, SHVT)); + + APInt FFMask = APInt(256, 255); + + // Mask off unwanted bytes + for (int i = 2; i < 32; i++) + Tmp[i] = DAG.getNode(ISD::AND, dl, VT, Tmp[i], + DAG.getConstant(FFMask << ((i - 1) * 8), dl, VT)); + + // OR everything together + for (int i = 2; i <= 32; i += 2) + Tmp[i] = DAG.getNode(ISD::OR, dl, VT, Tmp[i], Tmp[i - 1]); + + for (int i = 4; i <= 32; i += 4) + Tmp[i] = DAG.getNode(ISD::OR, dl, VT, Tmp[i], Tmp[i - 2]); + + for (int i = 8; i <= 32; i += 8) + Tmp[i] = DAG.getNode(ISD::OR, dl, VT, Tmp[i], Tmp[i - 4]); + + Tmp[32] = DAG.getNode(ISD::OR, dl, VT, Tmp[32], Tmp[24]); + Tmp[16] = DAG.getNode(ISD::OR, dl, VT, Tmp[16], Tmp[8]); + + return DAG.getNode(ISD::OR, dl, VT, Tmp[32], Tmp[16]); +} + +// This function only counts the number of bits in the lower 128 bits +// It is a slightly modified version of TargetLowering::expandCTPOP +static SDValue countPOP128(SDValue Op, SelectionDAG &DAG) { + SDLoc dl(Op); + EVT VT = Op->getValueType(0); + + // This is the "best" algorithm from + // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel + SDValue Mask55 = DAG.getConstant( + APInt::getSplat(VT.getScalarSizeInBits(), APInt(8, 0x55)), dl, VT); + SDValue Mask33 = DAG.getConstant( + APInt::getSplat(VT.getScalarSizeInBits(), APInt(8, 0x33)), dl, VT); + SDValue Mask0F = DAG.getConstant( + APInt::getSplat(VT.getScalarSizeInBits(), APInt(8, 0x0F)), dl, VT); + + // v = v - ((v >> 1) & 0x55555555...) + Op = DAG.getNode(ISD::SUB, dl, VT, Op, + DAG.getNode(ISD::AND, dl, VT, + DAG.getNode(ISD::SRL, dl, VT, Op, + DAG.getConstant(1, dl, MVT::i256)), + Mask55)); + // v = (v & 0x33333333...) + ((v >> 2) & 0x33333333...) + Op = DAG.getNode(ISD::ADD, dl, VT, DAG.getNode(ISD::AND, dl, VT, Op, Mask33), + DAG.getNode(ISD::AND, dl, VT, + DAG.getNode(ISD::SRL, dl, VT, Op, + DAG.getConstant(2, dl, MVT::i256)), + Mask33)); + // v = (v + (v >> 4)) & 0x0F0F0F0F... + Op = DAG.getNode(ISD::AND, dl, VT, + DAG.getNode(ISD::ADD, dl, VT, Op, + DAG.getNode(ISD::SRL, dl, VT, Op, + DAG.getConstant(4, dl, MVT::i256))), + Mask0F); + + // v = (v * 0x01010101...) >> (Len - 8) + SDValue Mask01 = DAG.getConstant( + APInt::getSplat(VT.getScalarSizeInBits(), APInt(8, 0x01)), dl, VT); + return DAG.getNode(ISD::SRL, dl, VT, + DAG.getNode(ISD::MUL, dl, VT, Op, Mask01), + DAG.getConstant(120, dl, MVT::i256)); +} + +SDValue EVMTargetLowering::LowerCTPOP(SDValue CTPOP, SelectionDAG &DAG) const { + // TODO: Consider moving this implementation to + // TargetLowering::expandCTPOP() to support i256 types. + SDNode *Node = CTPOP.getNode(); + SDLoc dl(Node); + EVT VT = Node->getValueType(0); + SDValue Op = Node->getOperand(0); + + // Split the Op into two parts and count separately + SDValue hiPart = + DAG.getNode(ISD::SRL, dl, VT, Op, DAG.getConstant(128, dl, MVT::i256)); + SDValue loPart = + DAG.getNode(ISD::AND, dl, VT, Op, + DAG.getConstant(APInt::getLowBitsSet(256, 128), dl, VT)); + auto loSum = DAG.getNode(ISD::AND, dl, VT, countPOP128(loPart, DAG), + DAG.getConstant(255, dl, VT)); + auto hiSum = DAG.getNode(ISD::AND, dl, VT, countPOP128(hiPart, DAG), + DAG.getConstant(255, dl, VT)); + return DAG.getNode(ISD::ADD, dl, VT, loSum, hiSum); +} + +SDValue EVMTargetLowering::LowerGlobalAddress(SDValue Op, + SelectionDAG &DAG) const { + const SDLoc DL(Op); + const auto *GA = cast(Op); + const EVT VT = Op.getValueType(); + assert(GA->getTargetFlags() == 0 && + "Unexpected target flags on generic GlobalAddressSDNode"); + + return DAG.getNode( + EVMISD::TARGET_ADDR_WRAPPER, DL, VT, + DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, GA->getOffset())); +} + +SDValue EVMTargetLowering::lowerINTRINSIC_WO_CHAIN(SDValue Op, + SelectionDAG &DAG) const { + unsigned IntrID = cast(Op.getOperand(0))->getZExtValue(); + switch (IntrID) { + default: + return SDValue(); + case Intrinsic::evm_datasize: + case Intrinsic::evm_dataoffset: + case Intrinsic::evm_loadimmutable: + case Intrinsic::evm_linkersymbol: { + const SDLoc DL(Op); + EVT Ty = Op.getValueType(); + MachineFunction &MF = DAG.getMachineFunction(); + const MDNode *Metadata = cast(Op.getOperand(1))->getMD(); + StringRef ContractID = cast(Metadata->getOperand(0))->getString(); + MCSymbol *Sym = MF.getContext().getOrCreateSymbol(ContractID); + DenseMap OpcMap = { + {Intrinsic::evm_datasize, EVM::DATASIZE}, + {Intrinsic::evm_dataoffset, EVM::DATAOFFSET}, + {Intrinsic::evm_loadimmutable, EVM::LOADIMMUTABLE}, + {Intrinsic::evm_linkersymbol, EVM::LINKERSYMBOL}}; + return SDValue(DAG.getMachineNode(OpcMap.at(IntrID), DL, Ty, + DAG.getMCSymbol(Sym, MVT::i256)), + 0); + } break; + } +} + +SDValue EVMTargetLowering::LowerLOAD(SDValue Op, SelectionDAG &DAG) const { + const SDLoc DL(Op); + auto *Load = cast(Op); + + const SDValue BasePtr = Load->getBasePtr(); + const SDValue Chain = Load->getChain(); + const MachinePointerInfo &PInfo = Load->getPointerInfo(); + + const EVT MemVT = Load->getMemoryVT(); + const unsigned MemVTSize = MemVT.getSizeInBits(); + if (MemVT == MVT::i256) + return {}; + + auto ExtType = Load->getExtensionType(); + + assert(Op->getValueType(0) == MVT::i256 && "Unexpected load type"); + assert(ExtType != ISD::NON_EXTLOAD && "Expected extended LOAD"); + assert(MemVT.isScalarInteger() && "Expected scalar load"); + assert(MemVTSize < 256 && "Expected < 256-bits sized loads"); + + LLVM_DEBUG(errs() << "Special handling of extended LOAD node:\n"; + Op.dump(&DAG)); + + // As the EVM architecture has only 256-bits load, additional handling + // is required to load smaller types. + // In the EVM architecture, values located on stack are right-aligned. Values + // located in memory are left-aligned. + + // A small load is implemented as follows: + // 1. L = load 256 bits starting from the pointer + // 2. Shift the value to the right + // V = V >> (256 - MemVTSize) + // 3. Sign-expand the value for SEXTLOAD + + const SDValue LoadValue = + DAG.getLoad(MVT::i256, DL, Chain, BasePtr, PInfo, Load->getAlign()); + + SDValue Res = DAG.getNode(ISD::SRL, DL, MVT::i256, LoadValue, + DAG.getConstant(256 - MemVTSize, DL, MVT::i256)); + + if (ExtType == ISD::SEXTLOAD) + Res = DAG.getNode(ISD::SIGN_EXTEND_INREG, DL, MVT::i256, Res, + DAG.getValueType(MemVT)); + + return DAG.getMergeValues({Res, LoadValue.getValue(1)}, DL); +} + +SDValue EVMTargetLowering::LowerSTORE(SDValue Op, SelectionDAG &DAG) const { + const SDLoc DL(Op); + auto *Store = cast(Op); + + const SDValue BasePtr = Store->getBasePtr(); + SDValue Chain = Store->getChain(); + const MachinePointerInfo &PInfo = Store->getPointerInfo(); + + const EVT MemVT = Store->getMemoryVT(); + const unsigned MemVTSize = MemVT.getSizeInBits(); + assert(MemVT.isScalarInteger() && "Expected a scalar store"); + if (MemVT == MVT::i256 || MemVT == MVT::i8) + return {}; + + assert(MemVTSize < 256 && "Expected < 256-bits sized stores"); + + LLVM_DEBUG(errs() << "Special handling of STORE node:\n"; Op.dump(&DAG)); + + // As the EVM architecture has only 256-bits stores, additional handling + // is required to store smaller types. + // In the EVM architecture, values located on stack are right-aligned. Values + // located in memory are left-aligned. + // The i8 store is handled a special way, as EVM has MSTORE8 instruction + // for this case. + + // A small store is implemented as follows: + // 1. L = load 256 bits starting from the pointer + // 2. Clear the MSB memory bits that will be overwritten + // L = L << MemVTSize + // L = L >> MemVTSize + // 3. Zero-expand the value being stored + // 4. Shift the value to the left + // V = V << (256 - MemVTSize) + // 5. S = or L, V + // 6. store i256 S + + // Load 256 bits starting from the pointer. + SDValue OrigValue = DAG.getExtLoad( + ISD::NON_EXTLOAD, DL, MVT::i256, Chain, BasePtr, PInfo, MVT::i256, + Store->getAlign(), MachineMemOperand::MOLoad, Store->getAAInfo()); + Chain = OrigValue.getValue(1); + + // Clear LSB bits of the memory word that will be overwritten. + OrigValue = DAG.getNode(ISD::SRL, DL, MVT::i256, OrigValue, + DAG.getConstant(MemVTSize, DL, MVT::i256)); + OrigValue = DAG.getNode(ISD::SHL, DL, MVT::i256, OrigValue, + DAG.getConstant(MemVTSize, DL, MVT::i256)); + + const SDValue ZextValue = + DAG.getZeroExtendInReg(Store->getValue(), DL, MVT::i256); + + const SDValue StoreValue = + DAG.getNode(ISD::SHL, DL, MVT::i256, ZextValue, + DAG.getConstant(256 - MemVTSize, DL, MVT::i256)); + + const SDValue OR = DAG.getNode(ISD::OR, DL, MVT::i256, StoreValue, OrigValue); + + return DAG.getStore(Chain, DL, OR, BasePtr, PInfo); +} + +void EVMTargetLowering::ReplaceNodeResults(SDNode *N, + SmallVectorImpl &Results, + SelectionDAG &DAG) const { + LowerOperationWrapper(N, Results, DAG); +} + +// TODO: CRP-1567. Consider removing UMUL_LOHI primitive. +SDValue EVMTargetLowering::LowerUMUL_LOHI(SDValue Op, SelectionDAG &DAG) const { + EVT VT = Op.getValueType(); + SDLoc DL(Op); + std::array Ops; + + // We'll expand the multiplication by brute force because we have no other + // options. This is a trivially-generalized version of the code from + // Hacker's Delight (itself derived from Knuth's Algorithm M from section + // 4.3.1). + // This is similar to what we have in the ExpandIntRes_MUL() from + // LegalizeIntegerTypes.cpp. + + SDValue LHS = Op.getOperand(0); + SDValue RHS = Op.getOperand(1); + + unsigned Bits = VT.getSizeInBits(); + unsigned HalfBits = Bits >> 1; + SDValue Mask = DAG.getConstant(APInt::getLowBitsSet(Bits, HalfBits), DL, VT); + SDValue LL = DAG.getNode(ISD::AND, DL, VT, LHS, Mask); + SDValue RL = DAG.getNode(ISD::AND, DL, VT, RHS, Mask); + + SDValue T = DAG.getNode(ISD::MUL, DL, VT, LL, RL); + SDValue TL = DAG.getNode(ISD::AND, DL, VT, T, Mask); + + SDValue Shift = DAG.getShiftAmountConstant(HalfBits, VT, DL); + SDValue TH = DAG.getNode(ISD::SRL, DL, VT, T, Shift); + SDValue LH = DAG.getNode(ISD::SRL, DL, VT, LHS, Shift); + SDValue RH = DAG.getNode(ISD::SRL, DL, VT, RHS, Shift); + + SDValue U = + DAG.getNode(ISD::ADD, DL, VT, DAG.getNode(ISD::MUL, DL, VT, LH, RL), TH); + SDValue UL = DAG.getNode(ISD::AND, DL, VT, U, Mask); + SDValue UH = DAG.getNode(ISD::SRL, DL, VT, U, Shift); + + SDValue V = + DAG.getNode(ISD::ADD, DL, VT, DAG.getNode(ISD::MUL, DL, VT, LL, RH), UL); + SDValue VH = DAG.getNode(ISD::SRL, DL, VT, V, Shift); + + Ops[1] = DAG.getNode(ISD::ADD, DL, VT, DAG.getNode(ISD::MUL, DL, VT, LH, RH), + DAG.getNode(ISD::ADD, DL, VT, UH, VH)); + + Ops[0] = DAG.getNode(ISD::ADD, DL, VT, TL, + DAG.getNode(ISD::SHL, DL, VT, V, Shift)); + + return DAG.getMergeValues(Ops, DL); +} + +SDValue EVMTargetLowering::LowerINTRINSIC_VOID(SDValue Op, + SelectionDAG &DAG) const { + unsigned IntNo = + cast( + Op.getOperand(Op.getOperand(0).getValueType() == MVT::Other)) + ->getZExtValue(); + + SDLoc DL(Op); + unsigned MemOpISD = 0; + switch (IntNo) { + default: + return {}; + case Intrinsic::evm_memmoveas1as1: + MemOpISD = EVMISD::MEMCPY_HEAP; + break; + case Intrinsic::evm_memcpyas1as2: + MemOpISD = EVMISD::MEMCPY_CALL_DATA; + break; + case Intrinsic::evm_memcpyas1as3: + MemOpISD = EVMISD::MEMCPY_RETURN_DATA; + break; + case Intrinsic::evm_memcpyas1as4: + MemOpISD = EVMISD::MEMCPY_CODE; + break; + } + + SDValue SrcOp = Op.getOperand(3); + // Support for copying bytes from a data section residing in code memory. + if (MemOpISD == EVMISD::MEMCPY_CODE) + if (const auto *GA = dyn_cast(SrcOp)) + SrcOp = DAG.getMCSymbol(DAG.getTarget().getSymbol(GA->getGlobal()), + MVT::i256); + + return DAG.getNode(MemOpISD, DL, MVT::Other, Op.getOperand(0), + Op.getOperand(2), SrcOp, Op.getOperand(4)); +} + +//===----------------------------------------------------------------------===// +// Calling Convention implementation. +//===----------------------------------------------------------------------===// + +// Test whether the given calling convention is supported. +static bool callingConvSupported(CallingConv::ID CallConv) { + // TODO: EVM currently doesn't distinguish between different calling + // conventions. + return CallConv == CallingConv::C || CallConv == CallingConv::Fast || + CallConv == CallingConv::Cold; +} + +SDValue EVMTargetLowering::LowerFormalArguments( + SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, + const SmallVectorImpl &Ins, const SDLoc &DL, + SelectionDAG &DAG, SmallVectorImpl &InVals) const { + if (!callingConvSupported(CallConv)) + fail(DL, DAG, "EVM doesn't support non-C calling conventions"); + if (IsVarArg) + fail(DL, DAG, "VarArg is not supported yet"); + + MachineFunction &MF = DAG.getMachineFunction(); + auto *MFI = MF.getInfo(); + + // Set up the incoming ARGUMENTS value, which serves to represent the liveness + // of the incoming values before they're represented by virtual registers. + MF.getRegInfo().addLiveIn(EVM::ARGUMENTS); + + for (const ISD::InputArg &In : Ins) { + if (In.Flags.isInAlloca()) + fail(DL, DAG, "EVM hasn't implemented inalloca arguments"); + if (In.Flags.isNest()) + fail(DL, DAG, "EVM hasn't implemented nest arguments"); + if (In.Flags.isInConsecutiveRegs()) + fail(DL, DAG, "EVM hasn't implemented cons regs arguments"); + if (In.Flags.isInConsecutiveRegsLast()) + fail(DL, DAG, "EVM hasn't implemented cons regs last arguments"); + if (In.Flags.isByVal()) + fail(DL, DAG, "EVM hasn't implemented by val arguments"); + + // As EVM has no physical registers, we use ARGUMENT instruction to emulate + // live-in registers. + InVals.push_back(In.Used ? DAG.getNode(EVMISD::ARGUMENT, DL, In.VT, + DAG.getTargetConstant(InVals.size(), + DL, MVT::i32)) + : DAG.getUNDEF(In.VT)); + // Record the number of arguments. + MFI->addParam(); + } + + return Chain; +} + +SDValue EVMTargetLowering::LowerCall(CallLoweringInfo &CLI, + SmallVectorImpl &InVals) const { + SelectionDAG &DAG = CLI.DAG; + const SDLoc DL = CLI.DL; + const SDValue Chain = CLI.Chain; + SDValue Callee = CLI.Callee; + const MachineFunction &MF = DAG.getMachineFunction(); + auto Layout = MF.getDataLayout(); + + const CallingConv::ID CallConv = CLI.CallConv; + if (!callingConvSupported(CallConv)) + fail(DL, DAG, + "EVM doesn't support language-specific or target-specific " + "calling conventions yet"); + if (CLI.IsPatchPoint) + fail(DL, DAG, "EVM doesn't support patch point yet"); + + // TODO: add support of tail call optimization + CLI.IsTailCall = false; + + // Ignore the fact EVM doesn't support varargs to able to compile + // more target-independent LIT tests. Anyway this assert is still + // present in LowerFormalArguments. + // if (CLI.IsVarArg) + // fail(DL, DAG, "EVM hasn't implemented variable arguments"); + + const SmallVectorImpl &Ins = CLI.Ins; + const SmallVectorImpl &Outs = CLI.Outs; + SmallVectorImpl &OutVals = CLI.OutVals; + + for (const auto &Out : Outs) { + if (Out.Flags.isNest()) + fail(DL, DAG, "EVM hasn't implemented nest arguments"); + if (Out.Flags.isInAlloca()) + fail(DL, DAG, "EVM hasn't implemented inalloca arguments"); + if (Out.Flags.isInConsecutiveRegs()) + fail(DL, DAG, "EVM hasn't implemented cons regs arguments"); + if (Out.Flags.isInConsecutiveRegsLast()) + fail(DL, DAG, "EVM hasn't implemented cons regs last arguments"); + if (Out.Flags.isByVal()) + fail(DL, DAG, "EVM hasn't implemented byval arguments"); + } + + if (Callee->getOpcode() == ISD::GlobalAddress) { + auto *GA = cast(Callee); + Callee = DAG.getTargetGlobalAddress(GA->getGlobal(), DL, + getPointerTy(DAG.getDataLayout()), + GA->getOffset()); + Callee = DAG.getNode(EVMISD::TARGET_ADDR_WRAPPER, DL, + getPointerTy(DAG.getDataLayout()), Callee); + } else if (Callee->getOpcode() == ISD::ExternalSymbol) { + auto *ES = cast(Callee); + Callee = + DAG.getTargetExternalSymbol(ES->getSymbol(), Callee.getValueType()); + Callee = DAG.getNode(EVMISD::TARGET_ADDR_WRAPPER, DL, + getPointerTy(DAG.getDataLayout()), Callee); + } + + // Compute the operands for the CALLn node. + SmallVector Ops; + Ops.push_back(Chain); + Ops.push_back(Callee); + + // Add all fixed arguments. + Ops.append(OutVals.begin(), OutVals.end()); + + SmallVector InTys; + for (const auto &In : Ins) { + assert(!In.Flags.isByVal() && "byval is not valid for return values"); + assert(!In.Flags.isNest() && "nest is not valid for return values"); + if (In.Flags.isInAlloca()) + fail(DL, DAG, "EVM hasn't implemented inalloca return values"); + if (In.Flags.isInConsecutiveRegs()) + fail(DL, DAG, "EVM hasn't implemented cons regs return values"); + if (In.Flags.isInConsecutiveRegsLast()) + fail(DL, DAG, "EVM hasn't implemented cons regs last return values"); + // Ignore In.getNonZeroOrigAlign() because all our arguments are passed in + // registers. + InTys.push_back(In.VT); + } + + InTys.push_back(MVT::Other); + const SDVTList InTyList = DAG.getVTList(InTys); + const SDValue Res = DAG.getNode(EVMISD::FCALL, DL, InTyList, Ops); + + for (size_t I = 0; I < Ins.size(); ++I) + InVals.push_back(Res.getValue(I)); + + // Return the chain + return Res.getValue(Ins.size()); +} + +SDValue +EVMTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv, + bool /*IsVarArg*/, + const SmallVectorImpl &Outs, + const SmallVectorImpl &OutVals, + const SDLoc &DL, SelectionDAG &DAG) const { + if (!callingConvSupported(CallConv)) + fail(DL, DAG, "EVM doesn't support non-C calling conventions"); + + SmallVector RetOps(1, Chain); + RetOps.append(OutVals.begin(), OutVals.end()); + Chain = DAG.getNode(EVMISD::RET, DL, MVT::Other, RetOps); + + // Record the number and types of the return values. + for (const ISD::OutputArg &Out : Outs) { + assert(!Out.Flags.isByVal() && "byval is not valid for return values"); + assert(!Out.Flags.isNest() && "nest is not valid for return values"); + assert(Out.IsFixed && "non-fixed return value is not valid"); + if (Out.Flags.isInAlloca()) + fail(DL, DAG, "EVM hasn't implemented inalloca results"); + if (Out.Flags.isInConsecutiveRegs()) + fail(DL, DAG, "EVM hasn't implemented cons regs results"); + if (Out.Flags.isInConsecutiveRegsLast()) + fail(DL, DAG, "EVM hasn't implemented cons regs last results"); + } + + return Chain; +} + +bool EVMTargetLowering::CanLowerReturn( + CallingConv::ID /*CallConv*/, MachineFunction & /*MF*/, bool /*IsVarArg*/, + const SmallVectorImpl &Outs, + LLVMContext & /*Context*/) const { + return true; +} + +static MachineBasicBlock *LowerCallResults(MachineInstr &CallResults, + const DebugLoc &DL, + MachineBasicBlock *BB, + const TargetInstrInfo &TII) { + MachineInstr &CallParams = *CallResults.getPrevNode(); + assert(CallParams.getOpcode() == EVM::CALL_PARAMS); + assert(CallResults.getOpcode() == EVM::CALL_RESULTS); + + MachineFunction &MF = *BB->getParent(); + const MCInstrDesc &MCID = TII.get(EVM::FCALL); + MachineInstrBuilder MIB(MF, MF.CreateMachineInstr(MCID, DL)); + + for (auto Def : CallResults.defs()) + MIB.add(Def); + + for (auto Use : CallParams.explicit_uses()) + MIB.add(Use); + + BB->insert(CallResults.getIterator(), MIB); + CallParams.eraseFromParent(); + CallResults.eraseFromParent(); + + return BB; +} + +MachineBasicBlock * +EVMTargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI, + MachineBasicBlock *BB) const { + const TargetInstrInfo &TII = *Subtarget->getInstrInfo(); + const DebugLoc &DL = MI.getDebugLoc(); + + switch (MI.getOpcode()) { + default: + llvm_unreachable("Unexpected instr type to insert"); + case EVM::SELECT: + return emitSelect(MI, BB); + case EVM::CALL_RESULTS: + return LowerCallResults(MI, DL, BB, TII); + } +} + +MachineBasicBlock *EVMTargetLowering::emitSelect(MachineInstr &MI, + MachineBasicBlock *BB) const { + const TargetInstrInfo *TII = BB->getParent()->getSubtarget().getInstrInfo(); + const DebugLoc &DL = MI.getDebugLoc(); + // To "insert" a SELECT instruction, we actually have to insert the + // diamond control-flow pattern. The incoming instruction knows the + // destination vreg to set, the condition code register to branch on and the + // true/false values to select between. + const BasicBlock *LLVM_BB = BB->getBasicBlock(); + const MachineFunction::iterator It = ++BB->getIterator(); + + // ThisMBB: + // ... + // TrueVal = ... + // setcc $cond, $2, $1 + // JUMPI SinkMBB, $cond + // fallthrough --> FHMBB + MachineBasicBlock *ThisMBB = BB; + MachineFunction *F = BB->getParent(); + MachineBasicBlock *FHMBB = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *SinkMBB = F->CreateMachineBasicBlock(LLVM_BB); + F->insert(It, FHMBB); + F->insert(It, SinkMBB); + + // Transfer the remainder of BB and its successor edges to SinkMBB. + SinkMBB->splice(SinkMBB->begin(), BB, + std::next(MachineBasicBlock::iterator(MI)), BB->end()); + SinkMBB->transferSuccessorsAndUpdatePHIs(BB); + + // Next, add the true and fallthrough blocks as its successors. + BB->addSuccessor(FHMBB); + BB->addSuccessor(SinkMBB); + + BuildMI(BB, DL, TII->get(EVM::JUMPI)) + .addMBB(SinkMBB) + .addReg(MI.getOperand(1).getReg()); + + // FHMBB: + // %FalseValue = ... + // # fallthrough to SinkMBB + BB = FHMBB; + + // Update machine-CFG edges + BB->addSuccessor(SinkMBB); + + // SinkMBB: + // %Result = phi [ %TrueValue, ThisMBB ], [ %FalseValue, FHMBB ] + // ... + BB = SinkMBB; + + BuildMI(*BB, BB->begin(), DL, TII->get(EVM::PHI), MI.getOperand(0).getReg()) + .addReg(MI.getOperand(2).getReg()) + .addMBB(ThisMBB) + .addReg(MI.getOperand(3).getReg()) + .addMBB(FHMBB); + + MI.eraseFromParent(); // The pseudo instruction is gone now. + return BB; +} + +// Fold +// (select C, (op Y, X), Y) -> (op Y, (select C, X, NEUTRAL)) +// and +// (select C, Y, (op Y, X)) -> (op Y, (select not C, X, NEUTRAL)) +// when 0 is the neutral element for op. +// +// That lets a later combine turn (select C, X, 0) into (C * X). +// +// Examples: +// (select C, (sub Y, X), Y) -> (sub Y, (select C, X, 0)) +// (select C, (or Y, X), Y) -> (or Y, (select C, X, 0)) +// (select C, (or X, Y), Y) -> (or Y, (select C, X, 0)) ; commutativity +// handled. +static SDValue tryFoldSelectIntoOp(SDNode *N, SelectionDAG &DAG, SDValue TrueV, + SDValue FalseV, bool Swapped) { + + if (!TrueV.hasOneUse() || isa(FalseV)) + return SDValue(); + + bool Commutative = true; + switch (TrueV.getOpcode()) { + default: + return SDValue(); + case ISD::SHL: + case ISD::SRA: + case ISD::SRL: + case ISD::SUB: + Commutative = false; + break; + case ISD::ADD: + case ISD::OR: + case ISD::XOR: + break; + } + + if (FalseV != TrueV.getOperand(0) && + (!Commutative || FalseV != TrueV.getOperand(1))) + return SDValue(); + + EVT VT = N->getValueType(0); + SDLoc DL(N); + SDValue OtherOp = + FalseV == TrueV.getOperand(0) ? TrueV.getOperand(1) : TrueV.getOperand(0); + EVT OtherOpVT = OtherOp.getValueType(); + SDValue IdentityOperand = DAG.getConstant(0, DL, OtherOpVT); + + if (Swapped) + std::swap(OtherOp, IdentityOperand); + SDValue NewSel = + DAG.getSelect(DL, OtherOpVT, N->getOperand(0), OtherOp, IdentityOperand); + return DAG.getNode(TrueV.getOpcode(), DL, VT, FalseV, NewSel); +} + +SDValue EVMTargetLowering::combineSELECT(SDNode *N, + DAGCombinerInfo &DCI) const { + // Perform combines only after DAG legalisation. + if (!DCI.isAfterLegalizeDAG()) + return SDValue(); + + SelectionDAG &DAG = DCI.DAG; + SDValue CondV = N->getOperand(0); + SDValue TrueV = N->getOperand(1); + SDValue FalseV = N->getOperand(2); + SDLoc DL(N); + MVT VT = N->getSimpleValueType(0); + + const auto freeze = [&DAG](const SDValue V) { return DAG.getFreeze(V); }; + const auto iszero = [&DAG, DL, VT](const SDValue V) { + return SDValue(DAG.getMachineNode(EVM::ISZERO, DL, VT, V), 0); + }; + + // X* means freeze(X) + // fold (Cond ? X : 0) -> (X* * Cond) + if (isNullConstant(FalseV)) + return DAG.getNode(ISD::MUL, DL, VT, freeze(TrueV), CondV); + + // fold (Cond ? 0 : Y) -> (~Cond * Y*) + if (isNullConstant(TrueV)) + return DAG.getNode(ISD::MUL, DL, VT, iszero(CondV), freeze(FalseV)); + + // fold (Cond ? X : 1) -> ((X* * Cond*) + ~Cond*) + if (isOneConstant(FalseV)) + return DAG.getNode( + ISD::ADD, DL, VT, iszero(freeze(CondV)), + DAG.getNode(ISD::MUL, DL, VT, freeze(TrueV), freeze(CondV))); + + // fold (Cond ? 1 : Y) -> (Cond* + ~Cond* * Y*) + if (isOneConstant(TrueV)) + return DAG.getNode( + ISD::ADD, DL, VT, freeze(CondV), + DAG.getNode(ISD::MUL, DL, VT, freeze(FalseV), iszero(freeze(CondV)))); + + // fold (Cond ? X : -1) -> (Cond - 1) | X*) + if (isAllOnesConstant(FalseV)) { + const SDValue Const1 = DAG.getConstant(1, DL, VT); + return DAG.getNode(ISD::OR, DL, VT, + DAG.getNode(ISD::SUB, DL, VT, CondV, Const1), + freeze(TrueV)); + } + + // fold (Cond ? -1 : Y) -> (-Cond | Y*) + if (isAllOnesConstant(TrueV)) { + SDValue Neg = DAG.getNegative(CondV, DL, VT); + return DAG.getNode(ISD::OR, DL, VT, Neg, DAG.getFreeze(FalseV)); + } + + // fold (Cond ? C1 : C2) -> (C2 + (C1 - C2) * Cond) if (C1 > C2) + // fold (Cond ? C1 : C2) -> (C2 - (C2 - C1) * Cond) if (C1 < C2) + // + // We distinguish between C1 > C2 and C1 < C2 to avoid generating + // negative constants, which are expensive in EVM. + if (isa(TrueV) && isa(FalseV)) { + APInt C1Val = cast(TrueV)->getAPIntValue(); + APInt C2Val = cast(FalseV)->getAPIntValue(); + + // This should never happen, but just in case. + if (C1Val == C2Val) + return TrueV; + + unsigned Opc = 0; + SDValue Scale; + if (C1Val.sgt(C2Val)) { + Opc = ISD::ADD; + Scale = DAG.getConstant(C1Val - C2Val, DL, VT); + } else { + // Use sub to avoid generating negative constants. + Opc = ISD::SUB; + Scale = DAG.getConstant(C2Val - C1Val, DL, VT); + } + + SDValue Mul = DAG.getNode(ISD::MUL, DL, VT, CondV, Scale); + return DAG.getNode(Opc, DL, VT, FalseV, Mul); + } + + // fold (Cond ? X : C) -> (C + (X* - C) * Cond) + // fold (Cond ? C : Y) -> (Y* + (C - Y*) * Cond) + if (isa(TrueV) || isa(FalseV)) { + if (!isa(TrueV)) + TrueV = freeze(TrueV); + else if (!isa(FalseV)) + FalseV = freeze(FalseV); + else + llvm_unreachable("Unexpected select operands"); + + SDValue Sub = DAG.getNode(ISD::SUB, DL, VT, TrueV, FalseV); + SDValue Mul = DAG.getNode(ISD::MUL, DL, VT, CondV, Sub); + return DAG.getNode(ISD::ADD, DL, VT, FalseV, Mul); + } + + if (SDValue V = tryFoldSelectIntoOp(N, DAG, TrueV, FalseV, /*Swapped=*/false)) + return V; + // NOLINTNEXTLINE(readability-suspicious-call-argument) + return tryFoldSelectIntoOp(N, DAG, FalseV, TrueV, /*Swapped=*/true); +} + +SDValue EVMTargetLowering::PerformDAGCombine(SDNode *N, + DAGCombinerInfo &DCI) const { + switch (N->getOpcode()) { + default: + break; + case ISD::SELECT: + return combineSELECT(N, DCI); + } + + return SDValue(); +} diff --git a/llvm/lib/Target/EVM/EVMISelLowering.h b/llvm/lib/Target/EVM/EVMISelLowering.h new file mode 100644 index 000000000000..deafb49ef160 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMISelLowering.h @@ -0,0 +1,195 @@ +//----------------- EVMISelLowering.h - EVM DAG Lowering 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 interfaces that EVM uses to lower LLVM +// code into a selection DAG. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVMISELLOWERING_H +#define LLVM_LIB_TARGET_EVM_EVMISELLOWERING_H + +#include "EVM.h" +#include "llvm/CodeGen/TargetLowering.h" + +namespace llvm { + +namespace EVMISD { + +enum NodeType : unsigned { + FIRST_NUMBER = ISD::BUILTIN_OP_END, +#define HANDLE_NODETYPE(NODE) NODE, +#include "EVMISD.def" +#undef HANDLE_NODETYPE +}; + +} // namespace EVMISD + +class EVMSubtarget; + +class EVMTargetLowering final : public TargetLowering { +public: + EVMTargetLowering(const TargetMachine &TM, const EVMSubtarget &STI); + + /// getTargetNodeName - This method returns the name of a target specific + /// DAG node. + const char *getTargetNodeName(unsigned Opcode) const override; + + bool isLegalAddressingMode(const DataLayout &DL, const AddrMode &AM, Type *Ty, + unsigned AS, + Instruction *I = nullptr) const override; + + EVT getSetCCResultType(const DataLayout &DL, LLVMContext &Context, + EVT VT) const override { + return MVT::i256; + } + + /// Return true if it is profitable to move this shift by a constant amount + /// through its operand, adjusting any immediate operands as necessary to + /// preserve semantics. This transformation may not be desirable if it + /// disrupts a particularly auspicious target-specific tree (e.g. bitfield + /// extraction in AArch64). By default, it returns true. + /// For EVM this may result in the creation of a big immediate, + /// which is not profitable. + bool isDesirableToCommuteWithShift(const SDNode *N, + CombineLevel Level) const override { + return false; + } + + /// Return true if integer divide is usually cheaper than a sequence of + /// several shifts, adds, and multiplies for this target. + /// The definition of "cheaper" may depend on whether we're optimizing + /// for speed or for size. + bool isIntDivCheap(EVT VT, AttributeList Attr) const override { return true; } + + /// There are two ways to clear extreme bits (either low or high): + /// Mask: x & (-1 << y) (the instcombine canonical form) + /// Shifts: x >> y << y + /// Return true if the variant with 2 variable shifts is preferred. + /// Return false if there is no preference. + bool shouldFoldMaskToVariableShiftPair(SDValue X) const override { + return true; + } + + /// Return true if it is profitable to fold a pair of shifts into a mask. + /// This is usually true on most targets. But some targets, like Thumb1, + /// have immediate shift instructions, but no immediate "and" instruction; + /// this makes the fold unprofitable. + /// For EVM this may result in the creation of a big immediate, + /// which is not profitable. + bool shouldFoldConstantShiftPairToMask(const SDNode *N, + CombineLevel Level) const override { + return false; + } + + /// Determines the optimal series of memory ops to replace the + /// memset / memcpy. Return true if the number of memory ops is below the + /// threshold (Limit). Note that this is always the case when Limit is ~0. + /// It returns the types of the sequence of memory ops to perform + /// memset / memcpy by reference. + bool + findOptimalMemOpLowering(std::vector &MemOps, unsigned Limit, + const MemOp &Op, unsigned DstAS, unsigned SrcAS, + const AttributeList &FuncAttributes) const override { + // Don't expand memcpy into scalar loads/stores, as it is mapped 1-to-1 to + // the corresponding EVM instruction (MCOPY, CALLDATACOPY, RETURNDATACOPY, + // or CODECOPY). + return false; + } + + /// allowsMisalignedMemoryAccesses - Returns true if the target allows + /// unaligned memory accesses of the specified type. Returns whether it + /// is "fast" by reference in the second argument. + bool allowsMisalignedMemoryAccesses(EVT VT, unsigned AddrSpace, + Align Alignment, + MachineMemOperand::Flags Flags, + unsigned *Fast) const override { + return AddrSpace != EVMAS::AS_STACK; + } + + // MVT::i256 is the only legal type, so DAG combiner should never reduce + // loads. + bool shouldReduceLoadWidth(SDNode *Load, ISD::LoadExtType ExtTy, + EVT NewVT) const override { + return false; + } + + /// Returns true if we should normalize + /// select(N0&N1, X, Y) => select(N0, select(N1, X, Y), Y) and + /// select(N0|N1, X, Y) => select(N0, select(N1, X, Y, Y)) if it is likely + /// that it saves us from materializing N0 and N1 in an integer register. + /// For EVM, selects are expensive, so generating more selects is not + /// beneficial. + bool shouldNormalizeToSelectSequence(LLVMContext &Context, + EVT VT) const override { + return false; + } + + /// Return true if a select of constants (select Cond, C1, C2) should be + /// transformed into simple math ops with the condition value. For example: + /// select Cond, C1, C1-1 --> add (zext Cond), C1-1 + /// For EVM, selects are expensive, so reducing number of selects is + /// beneficial. + bool convertSelectOfConstantsToMath(EVT VT) const override { return true; } + +private: + const EVMSubtarget *Subtarget; + + void ReplaceNodeResults(SDNode *N, SmallVectorImpl &Results, + SelectionDAG &DAG) const override; + + SDValue PerformDAGCombine(SDNode *N, DAGCombinerInfo &DCI) const override; + SDValue combineSELECT(SDNode *N, DAGCombinerInfo &DCI) const; + + SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override; + + SDValue LowerBSWAP(SDValue BSWAP, SelectionDAG &DAG) const; + + SDValue LowerCTPOP(SDValue CTPOP, SelectionDAG &DAG) const; + + SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const; + + SDValue lowerINTRINSIC_WO_CHAIN(SDValue Op, SelectionDAG &DAG) const; + + SDValue LowerLOAD(SDValue Op, SelectionDAG &DAG) const; + + SDValue LowerSTORE(SDValue Op, SelectionDAG &DAG) const; + + SDValue LowerUMUL_LOHI(SDValue Op, SelectionDAG &DAG) const; + + SDValue LowerINTRINSIC_VOID(SDValue Op, SelectionDAG &DAG) const; + + SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv, + bool isVarArg, + const SmallVectorImpl &Ins, + const SDLoc &dl, SelectionDAG &DAG, + SmallVectorImpl &InVals) const override; + + SDValue LowerCall(CallLoweringInfo &CLI, + SmallVectorImpl &InVals) const override; + + bool CanLowerReturn(CallingConv::ID CallConv, MachineFunction &MF, + bool IsVarArg, + const SmallVectorImpl &Outs, + LLVMContext &Context) const override; + + SDValue LowerReturn(SDValue Chain, CallingConv::ID CallConv, bool isVarArg, + const SmallVectorImpl &Outs, + const SmallVectorImpl &OutVals, const SDLoc &dl, + SelectionDAG &DAG) const override; + + MachineBasicBlock * + EmitInstrWithCustomInserter(MachineInstr &MI, + MachineBasicBlock *BB) const override; + + MachineBasicBlock *emitSelect(MachineInstr &MI, MachineBasicBlock *BB) const; +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_EVMISELLOWERING_H diff --git a/llvm/lib/Target/EVM/EVMInstrFormats.td b/llvm/lib/Target/EVM/EVMInstrFormats.td new file mode 100644 index 000000000000..ac0cf9fd67cf --- /dev/null +++ b/llvm/lib/Target/EVM/EVMInstrFormats.td @@ -0,0 +1,88 @@ +//=--------- EVMInstrFormats.td - EVM Instr. Formats -*- tablegen -*---------=// +// +// 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 describes the EVM instruction formats in TableGen format.n +// +//===----------------------------------------------------------------------===// + +class ASList lst> { + list List = lst; +} + +// The 'AS' structure is defined in IntrinsicsEVM.td +// For each address space define a list containing the only address space +// to override the AddressSpaces property of the PatFrag. +def AS_stack : ASList<[AS.STACK]>; +def AS_heap : ASList<[AS.HEAP]>; +def AS_call_data : ASList<[AS.CALL_DATA]>; +def AS_return_data : ASList<[AS.RETURN_DATA]>; +def AS_storage : ASList<[AS.STORAGE]>; +def AS_tstorage : ASList<[AS.TSTORAGE]>; + +// EVM general instruction format. +class EVMInst cost> + : StackRel, RegisterRel, Instruction { + // Instruction encoding. Bitwidth corresponds to the maximum + // size of possible EVM insturction. + // This is 'PUSH32 Imm'. 8 bits for opcode, and 256 bits for + // Imm. + bits<264> Inst; + bits<8> Opc; + bit IsPush = 0; + bit StackBased = stack; + string BaseName = NAME; + bits<16> BaseGasCost = cost; + let Namespace = "EVM"; + let Pattern = []; + let AsmString = asmstr; + let TSFlags{0} = stack; + let TSFlags{1} = IsPush; + let TSFlags{17 -2} = BaseGasCost; +} + +// Normal instructions. Default instantiation of a EVMInst. +class NI pattern, bit stack, string asmstr = "", + bits<8> inst = 0, bits<16> cost = 0> : EVMInst { + dag OutOperandList = oops; + dag InOperandList = iops; + let Pattern = pattern; + let Size = 1; + let Opc = inst; + let Inst{7 -0} = Opc; + let Defs = !if(stack, [], [ARGUMENTS]); +} + +// Generates both register and stack based versions of one actual instruction. +multiclass I pattern_r, string opcstr, + string argstr_r, bits<8> inst, bits<16> cost, dag oops_s = (outs), + dag iops_s = (ins), string argstr_s = ""> { + let isCodeGenOnly = 1 in + def "" : NI; + let BaseName = NAME, Defs = [] in def _S + : NI; +} + +// For pseudo instructions that have no real counterparts. These instructions +// are used in codegen only and print out to assembly if "virtual register" mode +// is on. +let isCodeGenOnly = 1 in +class NRI pattern, string asmstr> + : NI { +} + +class EVMPseudo pattern, bit stack = 0> + : NI { + let isPseudo = 1; + let isCodeGenOnly = 1; +} + +class PushBase pattern, bit stack, + string asmstr = "", bits<8> inst, bits<16> cost> + : NI { + let IsPush = 1; +} diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.cpp b/llvm/lib/Target/EVM/EVMInstrInfo.cpp new file mode 100644 index 000000000000..637883f66b29 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMInstrInfo.cpp @@ -0,0 +1,248 @@ +//===------------------ EVMInstrInfo.cpp - EVM Instruction Information ----===// +// +// 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 the EVM implementation of the TargetInstrInfo class. +// +//===----------------------------------------------------------------------===// + +#include "EVMInstrInfo.h" +#include "EVMMachineFunctionInfo.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +using namespace llvm; + +#define DEBUG_TYPE "evm-instr-info" + +#define GET_INSTRINFO_CTOR_DTOR +#include "EVMGenInstrInfo.inc" + +EVMInstrInfo::EVMInstrInfo() + : EVMGenInstrInfo(EVM::ADJCALLSTACKDOWN, EVM::ADJCALLSTACKUP), RI() {} + +bool EVMInstrInfo::isReallyTriviallyReMaterializable( + const MachineInstr &MI) const { + switch (MI.getOpcode()) { + case EVM::ADDRESS: + case EVM::CALLER: + case EVM::CALLVALUE: + case EVM::CALLDATASIZE: + case EVM::CODESIZE: + case EVM::CONST_I256: + return true; + default: + return false; + } +} + +void EVMInstrInfo::copyPhysReg(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + const DebugLoc &DL, MCRegister DestReg, + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { + // This method is called by post-RA expansion, which expects only + // phys registers to exist. However we expect only virtual here. + assert(Register::isVirtualRegister(DestReg) && + "Unexpected physical register"); + +#ifndef NDEBUG + auto &MRI = MBB.getParent()->getRegInfo(); + assert((MRI.getRegClass(DestReg) == &EVM::GPRRegClass) && + "Unexpected register class"); +#endif // NDEBUG + + BuildMI(MBB, I, DL, get(EVM::COPY_I256), DestReg) + .addReg(SrcReg, KillSrc ? RegState::Kill : 0); +} + +// Branch analysis. +bool EVMInstrInfo::analyzeBranch(MachineBasicBlock &MBB, + MachineBasicBlock *&TBB, + MachineBasicBlock *&FBB, + SmallVectorImpl &Cond, + bool AllowModify) const { + SmallVector BranchInstrs; + BranchType BT = analyzeBranch(MBB, TBB, FBB, Cond, AllowModify, BranchInstrs); + return BT == BT_None; +} + +EVMInstrInfo::BranchType EVMInstrInfo::analyzeBranch( + MachineBasicBlock &MBB, MachineBasicBlock *&TBB, MachineBasicBlock *&FBB, + SmallVectorImpl &Cond, bool AllowModify, + SmallVectorImpl &BranchInstrs) const { + + LLVM_DEBUG(dbgs() << "Analyzing branches of " << printMBBReference(MBB) + << '\n'); + + // Assume this is a fall-through block and update that information later. + TBB = nullptr; + FBB = nullptr; + Cond.clear(); + + const bool IsStackified = + MBB.getParent()->getInfo()->getIsStackified(); + + MachineBasicBlock::reverse_iterator I = MBB.rbegin(), REnd = MBB.rend(); + + // Skip all the debug instructions. + while (I != REnd && I->isDebugInstr()) + ++I; + + if (I == REnd || !isUnpredicatedTerminator(*I)) { + // This block ends with no branches (it just falls through to its succ). + // Leave TBB/FBB null. + return BT_NoBranch; + } + if (!I->isBranch()) { + // Not a branch terminator. + return BT_None; + } + + MachineInstr *LastInst = &*I++; + MachineInstr *SecondLastInst = nullptr; + + // Skip any debug instruction to see if the second last is a branch. + while (I != REnd && I->isDebugInstr()) + ++I; + + if (I != REnd && I->isBranch()) + SecondLastInst = &*I++; + + // Check that there are no unaccounted terminators left. + assert(I == REnd || + std::none_of(I, REnd, [](MachineBasicBlock::reverse_iterator I) { + return I->isTerminator(); + })); + + if (LastInst->isUnconditionalBranch()) { + TBB = LastInst->getOperand(0).getMBB(); + + // Clean things up, if we're allowed to. + if (AllowModify) { + // There should be no instructions after the unconditional branch. + assert(LastInst == MBB.rbegin()); + + // Delete the branch itself, if its target is the fall-through block. + if (MBB.isLayoutSuccessor(TBB)) { + LLVM_DEBUG(dbgs() << "Removing fall-through branch: "; + LastInst->dump()); + LastInst->eraseFromParent(); + TBB = nullptr; // Fall-through case. + } + } + if (TBB) + BranchInstrs.push_back(LastInst); + + if (!SecondLastInst) + return TBB ? BT_Uncond : BT_NoBranch; + } + + MachineInstr *CondBr = SecondLastInst ? SecondLastInst : LastInst; + assert(CondBr->isConditionalBranch()); + BranchInstrs.push_back(CondBr); + + // Set FBB to the destination of the previously encountered unconditional + // branch (if there was any). + FBB = TBB; + TBB = CondBr->getOperand(0).getMBB(); + + // Set from which instruction this condition comes from. It is needed for + // reversing and inserting of branches. + Cond.push_back( + MachineOperand::CreateImm(CondBr->getOpcode() == EVM::JUMPI || + CondBr->getOpcode() == EVM::PseudoJUMPI)); + if (!IsStackified) { + // Put the "use" of the condition into Cond[1]. + const MachineOperand &UseMO = CondBr->getOperand(1); + Cond.push_back(UseMO); + } + + if (!SecondLastInst) + return BT_Cond; + + return BT_CondUncond; +} + +unsigned EVMInstrInfo::removeBranch(MachineBasicBlock &MBB, + int *BytesRemoved) const { + assert(!BytesRemoved && "Code size not handled"); + + LLVM_DEBUG(dbgs() << "Removing branches out of " << printMBBReference(MBB) + << '\n'); + + unsigned Count = 0; + // Only remove branches from the end of the MBB. + for (auto I = MBB.rbegin(); I != MBB.rend() && I->isBranch(); ++Count) { + +#ifndef NDEBUG + if (I->isUnconditionalBranch()) + assert(!Count && "Malformed basic block: unconditional branch is not the " + "last instruction in the block"); +#endif // NDEBUG + + LLVM_DEBUG(dbgs() << "Removing branch: "; I->dump()); + MBB.erase(&MBB.back()); + I = MBB.rbegin(); + } + return Count; +} + +unsigned EVMInstrInfo::insertBranch(MachineBasicBlock &MBB, + MachineBasicBlock *TBB, + MachineBasicBlock *FBB, + ArrayRef Cond, + const DebugLoc &DL, int *BytesAdded) const { + assert(!BytesAdded && "Code size not handled"); + + // The number of instructions inserted. + unsigned InstrCount = 0; + const bool IsStackified = + MBB.getParent()->getInfo()->getIsStackified(); + unsigned UncondOpc = !IsStackified ? EVM::JUMP : EVM::PseudoJUMP; + + // Insert a branch to the "true" destination. + assert(TBB && "A branch must have a destination"); + if (Cond.empty()) { + BuildMI(&MBB, DL, get(UncondOpc)).addMBB(TBB); + } else { + // Destinguish between stackified and non-stackified instructions. + unsigned CondOpc = 0; + if (!IsStackified) + CondOpc = Cond[0].getImm() ? EVM::JUMPI : EVM::JUMP_UNLESS; + else + CondOpc = Cond[0].getImm() ? EVM::PseudoJUMPI : EVM::PseudoJUMP_UNLESS; + + auto NewMI = BuildMI(&MBB, DL, get(CondOpc)).addMBB(TBB); + + // Add a condition operand, if we are not in stackified form. + if (!IsStackified) { + assert( + Cond.size() == 2 && + "Unexpected number of conditional operands in non-stackified code"); + NewMI.add(Cond[1]); + } + } + + ++InstrCount; + + // If there is also a "false" destination, insert another branch. + if (FBB) { + assert(!Cond.empty() && "Unconditional branch can't have two destinations"); + BuildMI(&MBB, DL, get(UncondOpc)).addMBB(FBB); + ++InstrCount; + } + + return InstrCount; +} + +bool EVMInstrInfo::reverseBranchCondition( + SmallVectorImpl &Cond) const { + assert((Cond.size() == 1 || Cond.size() == 2) && + "Unexpected number of conditional operands"); + assert(Cond[0].isImm() && "Unexpected condition type"); + Cond.front() = MachineOperand::CreateImm(!Cond.front().getImm()); + return false; +} diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.h b/llvm/lib/Target/EVM/EVMInstrInfo.h new file mode 100644 index 000000000000..5b0699529bfa --- /dev/null +++ b/llvm/lib/Target/EVM/EVMInstrInfo.h @@ -0,0 +1,110 @@ +//=---------- EVMInstrInfo.h - EVM Instruction Information -*- 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 the EVM implementation of the +// TargetInstrInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVMINSTRINFO_H +#define LLVM_LIB_TARGET_EVM_EVMINSTRINFO_H + +#include "EVMRegisterInfo.h" +#include "llvm/CodeGen/TargetInstrInfo.h" + +#define GET_INSTRINFO_HEADER +#include "EVMGenInstrInfo.inc" + +namespace llvm { + +namespace EVMII { + +enum { + // TSF flag to check if this is a stack instruction. + IsStackPos = 0, + IsStackMask = 0x1, + // TSF flag to check if this is a PUSH instruction. + IsPushPos = 1, + IsPushMask = 0x1, + // Mask/offset representing instruction's base gas cost. + GasCostPos = 2, + GasCostMask = 0xFFFF, +}; + +} // namespace EVMII + +class EVMInstrInfo final : public EVMGenInstrInfo { + const EVMRegisterInfo RI; + +public: + explicit EVMInstrInfo(); + + enum BranchType : uint8_t { + BT_None, // Couldn't analyze branch. + BT_NoBranch, // No branches found. + BT_Uncond, // One unconditional branch. + BT_Cond, // One conditional branch. + BT_CondUncond // A conditional branch followed by an unconditional branch. + }; + + const EVMRegisterInfo &getRegisterInfo() const { return RI; } + + bool isReallyTriviallyReMaterializable(const MachineInstr &MI) const override; + + bool shouldBreakCriticalEdgeToSink(MachineInstr &MI) const override { + return true; + } + + void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, + const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; + + bool analyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TBB, + MachineBasicBlock *&FBB, + SmallVectorImpl &Cond, + bool AllowModify) const override; + + BranchType analyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TBB, + MachineBasicBlock *&FBB, + SmallVectorImpl &Cond, + bool AllowModify, + SmallVectorImpl &BranchInstrs) const; + + unsigned removeBranch(MachineBasicBlock &MBB, + int *BytesRemoved) const override; + + unsigned insertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB, + MachineBasicBlock *FBB, ArrayRef Cond, + const DebugLoc &DL, int *BytesAdded) const override; + + bool + reverseBranchCondition(SmallVectorImpl &Cond) const override; + + /// TSFlags extraction + static uint64_t getTSFlag(const MCInstrDesc &Desc, unsigned Pos, + uint64_t Mask) { + return (Desc.TSFlags >> Pos) & Mask; + } + + static bool isStack(const MachineInstr *MI) { + return getTSFlag(MI->getDesc(), EVMII::IsStackPos, EVMII::IsStackMask); + } + + static bool isPush(const MachineInstr *MI) { + return getTSFlag(MI->getDesc(), EVMII::IsPushPos, EVMII::IsPushMask); + } + + static uint64_t getGasCost(const MCInstrDesc &Desc) { + return getTSFlag(Desc, EVMII::GasCostPos, EVMII::GasCostMask); + } +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_EVMINSTRINFO_H diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.td b/llvm/lib/Target/EVM/EVMInstrInfo.td new file mode 100644 index 000000000000..45727d4b7e2f --- /dev/null +++ b/llvm/lib/Target/EVM/EVMInstrInfo.td @@ -0,0 +1,1212 @@ +//===-------- EVMInstrInfo.td - EVM Instruction defs -------*- tablegen -*-===// +// +// 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 describes the EVM instructions in TableGen format. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// EVM-specific DAG Node Types. +//===----------------------------------------------------------------------===// + +def SDT_EVMCallSeqStart + : SDCallSeqStart<[SDTCisVT<0, i256>, SDTCisVT<1, i256>]>; + +def SDT_EVMCallSeqEnd + : SDCallSeqEnd<[SDTCisVT<0, i256>, SDTCisVT<1, i256>]>; + +def SDT_EVMArgument + : SDTypeProfile<1, 1, [SDTCisVT<1, i256>]>; + +def SDT_EVMRet + : SDTypeProfile<0, -1, []>; + +def SDT_EVMSignextend + : SDTypeProfile<1, 2, [SDTCisSameAs<0, 1>, SDTCisVT<1, i256>]>; + +def SDT_EVMMemcpy + : SDTypeProfile<0, 3, [SDTCisPtrTy<0>, SDTCisPtrTy<1>, SDTCisInt<2>]>; + +//===----------------------------------------------------------------------===// +// EVM-specific DAG Nodes. +//===----------------------------------------------------------------------===// + +def EVMargument + : SDNode<"EVMISD::ARGUMENT", SDT_EVMArgument>; + +def EVMcallseq_start + : SDNode<"ISD::CALLSEQ_START", SDT_EVMCallSeqStart, + [SDNPHasChain, SDNPOutGlue]>; + +def EVMcallseq_end + : SDNode<"ISD::CALLSEQ_END", SDT_EVMCallSeqEnd, + [SDNPHasChain, SDNPOptInGlue, SDNPOutGlue]>; + +def EVMret + : SDNode<"EVMISD::RET", SDT_EVMRet, + [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; + +def EVMSignextend + : SDNode<"EVMISD::SIGNEXTEND", SDT_EVMSignextend>; + +def EVMMemcpy_call_data + : SDNode<"EVMISD::MEMCPY_CALL_DATA", SDT_EVMMemcpy, + [SDNPHasChain, SDNPMayLoad, SDNPMayStore]>; + +def EVMMemcpy_return_data + : SDNode<"EVMISD::MEMCPY_RETURN_DATA", SDT_EVMMemcpy, + [SDNPHasChain, SDNPMayLoad, SDNPMayStore]>; + +def EVMMemcpy_code + : SDNode<"EVMISD::MEMCPY_CODE", SDT_EVMMemcpy, + [SDNPHasChain, SDNPMayLoad, SDNPMayStore]>; + +def EVMMemcpy_heap + : SDNode<"EVMISD::MEMCPY_HEAP", SDT_EVMMemcpy, + [SDNPHasChain, SDNPMayLoad, SDNPMayStore]>; + +//===----------------------------------------------------------------------===// +// EVM Operand Definitions. +//===----------------------------------------------------------------------===// + +let OperandType = "OPERAND_IMMEDIATE" in +def i256imm : Operand; +def jmptarget : Operand; + +def neg_imm : Operand, IntImmLeaf; + +//===----------------------------------------------------------------------===// +// EVM Register to Stack instruction mapping +//===----------------------------------------------------------------------===// + +class StackRel; +def getStackOpcode : InstrMapping { + let FilterClass = "StackRel"; + let RowFields = ["BaseName"]; + let ColFields = ["StackBased"]; + let KeyCol = ["0"]; + let ValueCols = [["1"]]; +} + +//===----------------------------------------------------------------------===// +// EVM Stack to Register instruction mapping +//===----------------------------------------------------------------------===// + +class RegisterRel; +def getRegisterOpcode : InstrMapping { + let FilterClass = "RegisterRel"; + let RowFields = ["BaseName"]; + let ColFields = ["StackBased"]; + let KeyCol = ["1"]; + let ValueCols = [["0"]]; +} + + +//===----------------------------------------------------------------------===// +// EVM Instruction Format Definitions. +//===----------------------------------------------------------------------===// + +include "EVMInstrFormats.td" + + +//===----------------------------------------------------------------------===// +// Custom DAG Selection Operations. +//===----------------------------------------------------------------------===// + +def negate_imm : SDNodeXFormgetAPIntValue(); + return CurDAG->getTargetConstant(-neg, SDLoc(N), MVT::i256); +}]>; + +// Transform that maps the shift amount to BYTE‘s index (31 - Val / 8). +def byte_shift : SDNodeXForm(N)->getZExtValue(); + uint64_t ByteIdx = Val / 8; + return CurDAG->getTargetConstant(31 - ByteIdx, SDLoc(N), MVT::i256); +}]>; + +// Immediate predicate: imm divisible by 8 and within [0, 31 * 8]. +def byte_shift_imm : PatLeaf<(imm), [{ + uint64_t Val = N->getZExtValue(); + return (Val % 8) == 0 && Val <= 31 * 8; +}]>; + +//===----------------------------------------------------------------------===// +// Pattern fragments for memory instructions. +//===----------------------------------------------------------------------===// + +// Load pattern fragments +foreach AS = ["stack", "heap", "call_data", "storage", "tstorage"] in { + let AddressSpaces = !cast("AS_"#AS).List in { + def load_#AS : PatFrag<(ops node:$ptr), (load node:$ptr)> { + let IsLoad = 1; + } + } +} + +// Store patterns for Stack and Memory +foreach AS = ["stack", "heap", "storage", "tstorage"] in { + let AddressSpaces = !cast("AS_"#AS).List in { + def store_#AS : PatFrag<(ops node:$val, node:$ptr), + (store node:$val, node:$ptr)> { + let IsStore = 1; + } + } +} + +let AddressSpaces = AS_heap.List in +def truncstorei8_heap : PatFrag<(ops node:$val, node:$ptr), + (truncstore node:$val, node:$ptr)> { + let IsStore = 1; + let MemoryVT = i8; +} + + +//===----------------------------------------------------------------------===// +// EVM Instructions list. +//===----------------------------------------------------------------------===// + + +//===----------------------------------------------------------------------===// +// Pseudo instructions. +//===----------------------------------------------------------------------===// + +let usesCustomInserter = 1 in +def SELECT + : EVMPseudo<(outs GPR:$dst), (ins GPR:$cond, GPR:$lhs, GPR:$rhs), + [(set GPR:$dst, (select GPR:$cond, GPR:$lhs, GPR:$rhs))]>; + + +//===----------------------------------------------------------------------===// +// Additional instructions. +//===----------------------------------------------------------------------===// + +// Call sequence markers. +let isCodeGenOnly = 1, Defs = [SP], Uses = [SP] in { +def ADJCALLSTACKDOWN + : EVMPseudo<(outs), (ins i256imm:$amt1, i256imm:$amt2), + [(EVMcallseq_start timm:$amt1, timm:$amt2)]>; + +def ADJCALLSTACKUP + : EVMPseudo<(outs), (ins i256imm:$amt1, i256imm:$amt2), + [(EVMcallseq_end timm:$amt1, timm:$amt2)]>; +} + +let hasSideEffects = 1, isNotDuplicable = 1, Defs = [], + Uses = [ARGUMENTS] in def ARGUMENT + : NRI<(outs GPR:$res), + (ins i256imm:$argno), [(set GPR:$res, (EVMargument timm:$argno))], + "ARGUMENT $res, $argno">; + +// This is not real EVM instruction. It should be eliminted while +// stackification. +let isMoveImm = 1, isAsCheapAsAMove = 1, isReMaterializable = 1 in +def CONST_I256 + : NRI<(outs GPR:$res), (ins i256imm:$imm), [(set GPR:$res, imm:$imm)], + "CONST_I256 $res, $imm">; + +// This is not real EVM instruction. It should be eliminted while +// stackification. +let isAsCheapAsAMove = 1 in def COPY_I256 + : NRI<(outs GPR:$res), (ins GPR:$src), [], "COPY_I256 $res, $src">; + +let Uses = [SP], isCall = 1 in { + +// CALL should take both variadic arguments and produce variadic results, but +// this is not possible to model directly. Instead, we select calls to a +// CALL_PARAMS taking variadic arguments linked with a CALL_RESULTS that handles +// producing the call's variadic results. We recombine the two in a custom +// inserter hook after DAG ISel, so passes over MachineInstrs will only ever +// observe CALL nodes with all of the expected variadic uses and defs. +let isPseudo = 1 in +def CALL_PARAMS + : NRI<(outs), (ins jmptarget:$callee, variable_ops), [], + "CALL_PARAMS\t$callee">; + +let variadicOpsAreDefs = 1, usesCustomInserter = 1, isPseudo = 1 in +def CALL_RESULTS + : NRI<(outs), (ins variable_ops), [], "CALL_RESULTS">; + +// Note that instructions with variable_ops have custom printers in +// EVMInstPrinter.cpp. + +let variadicOpsAreDefs = 1 in +def FCALL + : NRI<(outs), (ins jmptarget:$callee, variable_ops), [], + "FCALL\t$callee">; +} // Uses = [SP], isCall = 1 + +let isCall = 1 in def PseudoCALL + : EVMPseudo<(outs), (ins jmptarget:$callee, variable_ops), [], true> { + // PUSH4_S + JUMP_S. + let Size = 6; +} + +//===----------------------------------------------------------------------===// +// EVM arithmetic instructions. +//===----------------------------------------------------------------------===// + +multiclass BinaryInst inst, int cost> + : I<(outs GPR:$dst), (ins GPR:$lhs, GPR:$rhs), + [(set GPR:$dst, (node GPR:$lhs, GPR:$rhs))], + opcode_str, "$dst, $lhs, $rhs", inst, cost>; + +let isCommutable = 1 in { + defm ADD : BinaryInst; + defm MUL : BinaryInst; +} +defm SUB : BinaryInst; +defm DIV : BinaryInst; +defm SDIV : BinaryInst; +defm MOD : BinaryInst; +defm SMOD : BinaryInst; + +let isCommutable = 1 in { + defm ADDMOD : I<(outs GPR:$dst), (ins GPR:$add_op1, GPR:$add_op2, GPR:$denom), + [(set GPR:$dst, (int_evm_addmod GPR:$add_op1, GPR:$add_op2, + GPR:$denom))], + "ADDMOD", "$dst, $add_op1, $add_op2, $denom", 0x08, 8>; + + defm MULMOD : I<(outs GPR:$dst), (ins GPR:$mul_op1, GPR:$mul_op2, GPR:$denom), + [(set GPR:$dst, (int_evm_mulmod GPR:$mul_op1, GPR:$mul_op2, + GPR:$denom))], + "MULMOD", "$dst, $mul_op1, $mul_op2, $denom", 0x09, 8>; +} + +defm EXP + : I<(outs GPR:$dst), (ins GPR:$base, GPR:$exp), + [(set GPR:$dst, (int_evm_exp GPR:$base, GPR:$exp))], + "EXP", "$dst, $base, $exp", 0x0A, 10>; + +defm SIGNEXTEND + : I<(outs GPR:$dst), (ins GPR:$size, GPR:$src), + [(set GPR:$dst, (int_evm_signextend GPR:$size, GPR:$src))], + "SIGNEXTEND", "$dst, $size, $src", 0x0B, 5>; + +// The first operand of SIGNEXTEND is the type size in bytes of +// the value being extendent minus one. +def : Pat<(sext_inreg GPR:$src, i8), (SIGNEXTEND (CONST_I256 0), GPR:$src)>; +def : Pat<(sext_inreg GPR:$src, i16), (SIGNEXTEND (CONST_I256 1), GPR:$src)>; +def : Pat<(sext_inreg GPR:$src, i32), (SIGNEXTEND (CONST_I256 3), GPR:$src)>; +def : Pat<(sext_inreg GPR:$src, i64), (SIGNEXTEND (CONST_I256 7), GPR:$src)>; +def : Pat<(sext_inreg GPR:$src, i128), (SIGNEXTEND (CONST_I256 15), GPR:$src)>; + +def : Pat<(add GPR:$res, neg_imm:$imm), + (SUB GPR:$res, (CONST_I256 (negate_imm imm:$imm)))>; +def : Pat<(sub GPR:$res, neg_imm:$imm), + (ADD GPR:$res, (CONST_I256 (negate_imm imm:$imm)))>; + +def : Pat<(int_evm_div GPR:$op1, GPR:$op2), (DIV GPR:$op1, GPR:$op2)>; +def : Pat<(int_evm_sdiv GPR:$op1, GPR:$op2), (SDIV GPR:$op1, GPR:$op2)>; +def : Pat<(int_evm_mod GPR:$op1, GPR:$op2), (MOD GPR:$op1, GPR:$op2)>; +def : Pat<(int_evm_smod GPR:$op1, GPR:$op2), (SMOD GPR:$op1, GPR:$op2)>; + +//===----------------------------------------------------------------------===// +// EVM comparison instructions. +//===----------------------------------------------------------------------===// + +multiclass ComparisonInst inst, int cost> + : I<(outs GPR:$dst), (ins GPR:$lhs, GPR:$rhs), + [(set GPR:$dst, (setcc GPR:$lhs, GPR:$rhs, cond))], + opcode_str, "$dst, $lhs, $rhs", inst, cost>; + +let isCompare = 1 in { +defm ULT : ComparisonInst; +defm UGT : ComparisonInst; +defm LT : ComparisonInst; +defm GT : ComparisonInst; +let isCommutable = 1 in +defm EQ : ComparisonInst; +defm ISZERO + : I<(outs GPR:$dst), (ins GPR:$src), + [(set GPR:$dst, (setcc GPR:$src, 0, SETEQ))], + "ISZERO", "$dst, $src", 0x15, 3>; +} // isCompare = 1 + +// Patterns for comparison operations that have no +// corresponding machine instructions. +def : Pat<(setcc GPR:$rs0, GPR:$rs1, SETNE), (ISZERO (EQ GPR:$rs0, GPR:$rs1))>; +def : Pat<(setcc GPR:$rs0, GPR:$rs1, SETGE), (ISZERO (LT GPR:$rs0, GPR:$rs1))>; +def : Pat<(setcc GPR:$rs0, GPR:$rs1, SETLE), (ISZERO (GT GPR:$rs0, GPR:$rs1))>; +def : Pat<(setcc GPR:$rs0, GPR:$rs1, SETULE), + (ISZERO (UGT GPR:$rs0, GPR:$rs1))>; +def : Pat<(setcc GPR:$rs0, GPR:$rs1, SETUGE), + (ISZERO (ULT GPR:$rs0, GPR:$rs1))>; + +def : Pat<(setcc GPR:$rs0, 0, SETNE), (ISZERO (ISZERO GPR:$rs0))>; + +//===----------------------------------------------------------------------===// +// EVM bitwise instructions. +//===----------------------------------------------------------------------===// + +let isCommutable = 1 in { + defm AND : BinaryInst; + defm OR : BinaryInst; + defm XOR : BinaryInst; +} +defm NOT + : I<(outs GPR:$dst), (ins GPR:$src), [(set GPR:$dst, (not GPR:$src))], + "NOT", "$dst, $src", 0x19, 3>; + +let mayLoad = 1 in +defm KECCAK256 + : I<(outs GPR:$dst), (ins GPR:$offset, GPR:$size), + [(set GPR:$dst, (int_evm_sha3 GPR:$offset, GPR:$size))], + "KECCAK256", "$dst, $offset, $size", 0x20, 30>; + +defm BYTE + : I<(outs GPR:$dst), (ins GPR:$idx, GPR:$val), + [(set GPR:$dst, (int_evm_byte GPR:$idx, GPR:$val))], + "BYTE", "$dst, $idx, $val", 0x1a, 3>; + +def : Pat<(and (srl i256:$val, byte_shift_imm:$sh), (i256 255)), + (BYTE (CONST_I256 (byte_shift byte_shift_imm:$sh)), $val)>; + +// When the shift amount is 248, the node 'AND $val, 255' gets optimized away +// in the lowered SelectionDAG, preventing the above pattern from matching +// which results to the following instructions: +// +// PUSH1 0xF8 +// SHR +// +// However, using the BYTE instruction instead is often both cheaper in gas +// and uses one less byte: +// +// PUSH0 +// BYTE +// +// So we explicitly match this case with a separate pattern: +def : Pat<(srl i256:$val, (i256 248)), (BYTE (CONST_I256 0), $val)>; + +//===----------------------------------------------------------------------===// +// EVM shift instructions. +//===----------------------------------------------------------------------===// + +multiclass ShiftInst inst, int cost> + : I<(outs GPR:$dst), (ins GPR:$val, GPR:$shift), + [(set GPR:$dst, (node GPR:$shift, GPR:$val))], + opcode_str, "$dst, $shift, $val", inst, cost>; + +defm SHL : ShiftInst; +defm SHR : ShiftInst; +defm SAR : ShiftInst; + +def : Pat<(int_evm_shl GPR:$op1, GPR:$op2), (SHL GPR:$op1, GPR:$op2)>; +def : Pat<(int_evm_shr GPR:$op1, GPR:$op2), (SHR GPR:$op1, GPR:$op2)>; +def : Pat<(int_evm_sar GPR:$op1, GPR:$op2), (SAR GPR:$op1, GPR:$op2)>; + + +//===----------------------------------------------------------------------===// +// EVM control instructions. +//===----------------------------------------------------------------------===// + +let isBranch = 1, isTerminator = 1 in { +// The condition operand is a boolean value which EVM represents as i256. +defm JUMPI + : I<(outs), (ins jmptarget:$dst, GPR:$cond), [(brcond GPR:$cond, bb:$dst)], + "JUMPI", "$dst, $cond", 0x57, 10>; +def JUMP_UNLESS : EVMPseudo<(outs), (ins jmptarget:$dst, GPR:$cond), []>; +def PseudoJUMPI : EVMPseudo<(outs), (ins jmptarget:$dst), [], true> { + // PUSH4_S + JUMPI_S. + let Size = 6; +} +def PseudoJUMP_UNLESS : EVMPseudo<(outs), (ins jmptarget:$dst), [], true>; + +let isBarrier = 1 in { + defm JUMP : I<(outs), (ins jmptarget:$dst), [(br bb:$dst)], "JUMP", "$dst", + 0x56, 8>; + def PseudoJUMP : EVMPseudo<(outs), (ins jmptarget:$dst), [], true> { + // PUSH4_S + JUMP_S. + let Size = 6; + } +} // isBarrier = 1 +} // isBranch = 1, isTerminator = 1 + +def : Pat<(brcond (setcc GPR:$src, 0, SETNE), bb:$dst), + (JUMPI bb:$dst, GPR:$src)>; +def : Pat<(brcond (setcc GPR:$rs0, GPR:$rs1, SETEQ), bb:$dst), + (JUMPI bb:$dst, (EQ GPR:$rs0, GPR:$rs1))>; +def : Pat<(brcond (setcc GPR:$rs0, GPR:$rs1, SETNE), bb:$dst), + (JUMPI bb:$dst, (SUB GPR:$rs1, GPR:$rs0))>; + +def : Pat<(brcond (setcc GPR:$src, 0, SETEQ), bb:$dst), + (JUMP_UNLESS bb:$dst, GPR:$src)>; +def : Pat<(brcond (setcc GPR:$src, -1, SETGT), bb:$dst), + (JUMP_UNLESS bb:$dst, (LT GPR:$src, (CONST_I256 0)))>; +def : Pat<(brcond (setcc GPR:$rs0, GPR:$rs1, SETGE), bb:$dst), + (JUMP_UNLESS bb:$dst, (LT GPR:$rs0, GPR:$rs1))>; +def : Pat<(brcond (setcc GPR:$rs0, GPR:$rs1, SETLE), bb:$dst), + (JUMP_UNLESS bb:$dst, (GT GPR:$rs0, GPR:$rs1))>; +def : Pat<(brcond (setcc GPR:$rs0, GPR:$rs1, SETULE), bb:$dst), + (JUMP_UNLESS bb:$dst, (UGT GPR:$rs0, GPR:$rs1))>; +def : Pat<(brcond (setcc GPR:$rs0, GPR:$rs1, SETUGE), bb:$dst), + (JUMP_UNLESS bb:$dst, (ULT GPR:$rs0, GPR:$rs1))>; + +// This isn't really a control flow instruction, but it should be used to mark +// destination of jump instructions. +defm JUMPDEST : I<(outs), (ins), [], "JUMPDEST", "", 0x5B, 1>; + +let isBarrier = 1, isTerminator = 1, isReturn = 1 in { + def RET : NRI<(outs), (ins variable_ops), [(EVMret)], "RET">; + def PseudoRET : EVMPseudo<(outs), (ins), [], true>; +} + +//===----------------------------------------------------------------------===// +// EVM memory/storage instructions. +//===----------------------------------------------------------------------===// + +let mayLoad = 1 in { + defm MLOAD + : I<(outs GPR:$dst), (ins GPR:$offset), + [(set GPR:$dst, (load_heap GPR:$offset))], + "MLOAD", "$dst, $offset", 0x51, 3>; + + defm SLOAD + : I<(outs GPR:$dst), (ins GPR:$key), + [(set GPR:$dst, (load_storage GPR:$key))], + "SLOAD", "$dst, $key", 0x54, 100>; + + defm TLOAD + : I<(outs GPR:$dst), (ins GPR:$key), + [(set GPR:$dst, (load_tstorage GPR:$key))], + "TLOAD", "$dst, $key", 0x5c, 100>; +} + +let mayStore = 1 in { + defm MSTORE + : I<(outs), (ins GPR:$offset, GPR:$val), + [(store_heap GPR:$val, GPR:$offset)], + "MSTORE", "$offset, $val", 0x52, 3>; + + defm MSTORE8 + : I<(outs), (ins GPR:$offset, GPR:$val), + [(int_evm_mstore8 GPR:$offset, GPR:$val)], + "MSTORE8", "$offset, $val", 0x53, 3>; + + defm SSTORE + : I<(outs), (ins GPR:$key, GPR:$val), [(store_storage GPR:$val, GPR:$key)], + "SSTORE", "$key, $val", 0x55, 100>; + + defm TSTORE + : I<(outs), (ins GPR:$key, GPR:$val), [(store_tstorage GPR:$val, GPR:$key)], + "TSTORE", "$key, $val", 0x5d, 100>; +} + +let mayStore = 1, mayLoad = 1 in +defm MCOPY + : I<(outs), (ins GPR:$dst, GPR:$src, GPR:$size), [], + "MCOPY", "$dst, $src, $size", 0x5E, 3>; + +def : Pat<(EVMMemcpy_heap GPR:$dst, GPR:$src, GPR:$size), + (MCOPY GPR:$dst, GPR:$src, GPR:$size)>; + +let hasSideEffects = 1 in { + defm PC : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_pc))], + "PC", "$dst", 0x58, 2>; + + defm GAS : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_gas))], + "GAS", "$dst", 0x5A, 2>; +} + +defm MSIZE : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_msize))], + "MSIZE", "$dst", 0x59, 2>; + +// The i8 store is handled a speciall way, as EVM has a dedicated instruction +// for this. +def : Pat<(truncstorei8_heap GPR:$val, GPR:$off), (MSTORE8 GPR:$off, GPR:$val)>; + +// Instructions for the stack manipulation. These are not real EVM instructions. +// They are used to model 'alloca' before stackification and should be +// eliminated at that stage. + +def to_tframeindex : SDNodeXFormgetTargetFrameIndex(N->getIndex(), MVT::i256); +}]>; + +def TargetFI: OutPatFrag<(ops node:$fi), (i256 (to_tframeindex $fi))>; + +def add_like: PatFrags<(ops node:$lhs, node:$rhs), + [(add $lhs, $rhs), (or $lhs, $rhs)], [{ + return N->getOpcode() == ISD::ADD || isOrEquivalentToAdd(N); +}]>; + +let mayLoad = 1 in +def STACK_LOAD + : NRI<(outs GPR:$dst), (ins GPR:$fi, i256imm:$off), [], + "STACK_LOAD $dst, $fi, $off">; + +let mayStore = 1 in + def STACK_STORE + : NRI<(outs), (ins GPR:$fi, i256imm:$off, GPR:$val), [], + "STACK_STORE $fi, $off, $val">; + +def : Pat<(i256 frameindex:$fi), (TargetFI $fi)>; +def : Pat<(load_stack frameindex:$fi), (STACK_LOAD (TargetFI $fi), 0)>; +def : Pat<(load_stack (add_like frameindex:$fi, imm:$off)), + (STACK_LOAD (TargetFI $fi), imm:$off)>; +def : Pat<(store_stack GPR:$val, frameindex:$fi), + (STACK_STORE (TargetFI $fi), 0, GPR:$val)>; +def : Pat<(store_stack GPR:$val, (add_like frameindex:$fi, imm:$off)), + (STACK_STORE (TargetFI $fi), imm:$off, GPR:$val)>; + +// The following patterns shouldn't be used for lowering a real code +// generated by FE. We add them only to be able to compile target independent +// LIT tests. +def : Pat<(load_stack GPR:$off), (STACK_LOAD GPR:$off, 0)>; +def : Pat<(store_stack GPR:$val, GPR:$off), (STACK_STORE GPR:$off, 0, GPR:$val)>; +def : Pat<(truncstorei8 GPR:$val, GPR:$off), (STACK_STORE GPR:$off, 0, GPR:$val)>; + + +//===----------------------------------------------------------------------===// +// EVM instructions for retrieval values from context. +//===----------------------------------------------------------------------===// + +let isAsCheapAsAMove = 1, isReMaterializable = 1 in { +defm ADDRESS + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_address))], + "ADDRESS", "$dst", 0x30, 2>; + +defm CALLER + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_caller))], + "CALLER", "$dst", 0x33, 2>; + +defm CALLVALUE + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_callvalue))], + "CALLVALUE", "$dst", 0x34, 2>; + +defm CALLDATASIZE + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_calldatasize))], + "CALLDATASIZE", "$dst", 0x36, 2>; + +defm CODESIZE + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_codesize))], + "CODESIZE", "$dst", 0x38, 2>; +} + +let isAsCheapAsAMove = 1 in { + +defm ORIGIN + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_origin))], + "ORIGIN", "$dst", 0x32, 2>; + +defm TIMESTAMP + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_timestamp))], + "TIMESTAMP", "$dst", 0x42, 2>; + +defm NUMBER + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_number))], + "NUMBER", "$dst", 0x43, 2>; + +defm GASLIMIT + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_gaslimit))], + "GASLIMIT", "$dst", 0x45, 2>; + +defm COINBASE + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_coinbase))], + "COINBASE", "$dst", 0x41, 2>; + +defm BASEFEE + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_basefee))], + "BASEFEE", "$dst", 0x48, 2>; + +defm CHAINID + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_chainid))], + "CHAINID", "$dst", 0x46, 2>; + +defm GASPRICE + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_gasprice))], + "GASPRICE", "$dst", 0x3A, 2>; + +defm DIFFICULTY + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_difficulty))], + "DIFFICULTY", "$dst", 0x44, 2>; + +defm BLOBBASEFEE + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_blobbasefee))], + "BLOBBASEFEE", "$dst", 0x4A, 2>; +} + +defm BALANCE + : I<(outs GPR:$dst), (ins GPR:$addr), + [(set GPR:$dst, (int_evm_balance GPR:$addr))], + "BALANCE", "$dst, $addr", 0x31, 100>; + +let mayLoad = 1 in +defm CALLDATALOAD + : I<(outs GPR:$dst), (ins GPR:$off), + [(set GPR:$dst, (int_evm_calldataload GPR:$off))], + "CALLDATALOAD", "$dst, $off", 0x35, 2>; + +def : Pat<(load_call_data GPR:$off), (CALLDATALOAD GPR:$off)>; + +let mayStore = 1 in +defm CALLDATACOPY + : I<(outs), (ins GPR:$dst_off, GPR:$src_off, GPR:$size), [], + "CALLDATACOPY", "$dst_off, $src_off, $size", 0x37, 3>; + +def : Pat<(EVMMemcpy_call_data GPR:$dst, GPR:$src, GPR:$size), + (CALLDATACOPY GPR:$dst, GPR:$src, GPR:$size)>; + +let mayStore = 1 in +defm CODECOPY + : I<(outs), (ins GPR:$dst_off, GPR:$src_off, GPR:$size), [], + "CODECOPY", "$dst_off, $src_off, $size", 0x39, 3>; + +def : Pat<(EVMMemcpy_code GPR:$dst, GPR:$src, GPR:$size), + (CODECOPY GPR:$dst, GPR:$src, GPR:$size)>; + +defm EXTCODESIZE + : I<(outs GPR:$dst), (ins GPR:$addr), + [(set GPR:$dst, (int_evm_extcodesize GPR:$addr))], + "EXTCODESIZE", "$dst, $addr", 0x3B, 100>; + +let mayStore = 1 in +defm EXTCODECOPY + : I<(outs), (ins GPR:$addr, GPR:$dst_off, GPR:$src_off, GPR:$size), + [(int_evm_extcodecopy GPR:$addr, GPR:$dst_off, GPR:$src_off, GPR:$size)], + "EXTCODECOPY", "$addr, $dst_off, $src_off, $size", 0x3C, 100>; + +defm RETURNDATASIZE + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_returndatasize))], + "RETURNDATASIZE", "$dst", 0x3D, 2>; + +let mayStore = 1 in +defm RETURNDATACOPY + : I<(outs), (ins GPR:$dst_off, GPR:$src_off, GPR:$size), [], + "RETURNDATACOPY", "$dst_off, $src_off, $size", 0x3E, 3>; + +def : Pat<(EVMMemcpy_return_data GPR:$dst, GPR:$src, GPR:$size), + (RETURNDATACOPY GPR:$dst, GPR:$src, GPR:$size)>; + +defm EXTCODEHASH + : I<(outs GPR:$dst), (ins GPR:$addr), + [(set GPR:$dst, (int_evm_extcodehash GPR:$addr))], + "EXTCODEHASH", "$dst, $addr", 0x3F, 100>; + +defm BLOCKHASH + : I<(outs GPR:$dst), (ins GPR:$addr), + [(set GPR:$dst, (int_evm_blockhash GPR:$addr))], + "BLOCKHASH", "$dst, $addr", 0x40, 20>; + +let hasSideEffects = 1 in +defm SELFBALANCE + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_selfbalance))], + "SELFBALANCE", "$dst", 0x47, 5>; + +defm BLOBHASH + : I<(outs GPR:$dst), (ins GPR:$index), + [(set GPR:$dst, (int_evm_blobhash GPR:$index))], + "BLOBHASH", "$dst, $index", 0x49, 3>; + +//===----------------------------------------------------------------------===// +// EVM instructions for logging. +//===----------------------------------------------------------------------===// + +let mayLoad = 1, hasSideEffects = 1 in { + defm LOG0 + : I<(outs), (ins GPR:$offset, GPR:$size), + [(int_evm_log0 GPR:$offset, GPR:$size)], + "LOG0", "$offset, $size", 0xA0, 375>; + + defm LOG1 + : I<(outs), (ins GPR:$offset, GPR:$size, GPR:$t1), + [(int_evm_log1 GPR:$offset, GPR:$size, GPR:$t1)], + "LOG1", "$offset, $size, $t1", 0xA1, 750>; + + defm LOG2 + : I<(outs), (ins GPR:$offset, GPR:$size, GPR:$t1, GPR:$t2), + [(int_evm_log2 GPR:$offset, GPR:$size, GPR:$t1, GPR:$t2)], + "LOG2", "$offset, $size, $t1, $t2", 0xA2, 1125>; + + defm LOG3 + : I<(outs), (ins GPR:$offset, GPR:$size, GPR:$t1, GPR:$t2, GPR:$t3), + [(int_evm_log3 GPR:$offset, GPR:$size, GPR:$t1, GPR:$t2, GPR:$t3)], + "LOG3", "$offset, $size, $t1, $t2, $t3", 0xA3, 1500>; + + defm LOG4 + : I<(outs), (ins GPR:$offset, GPR:$size, GPR:$t1, GPR:$t2, GPR:$t3, GPR:$t4), + [(int_evm_log4 GPR:$offset, GPR:$size, GPR:$t1, GPR:$t2, GPR:$t3, + GPR:$t4)], + "LOG4", "$offset, $size, $t1, $t2, $t3, $t4", 0xA4, 1875>; +} + +//===----------------------------------------------------------------------===// +// EVM system instructions. +//===----------------------------------------------------------------------===// + +let mayLoad = 1, hasSideEffects = 1 in +defm CREATE + : I<(outs GPR:$dst), (ins GPR:$value, GPR:$offset, GPR:$size), + [(set GPR:$dst, (int_evm_create GPR:$value, GPR:$offset, GPR:$size))], + "CREATE", "$dst, $value, $offset, $size", 0xF0, 32000>; + +let isCall = 1, hasSideEffects = 1, mayLoad = 1, mayStore = 1 in { + defm CALL + : I<(outs GPR:$dst), (ins GPR:$gas, GPR:$addr, GPR:$value, GPR:$arg_off, + GPR:$arg_size, GPR:$ret_off, GPR:$ret_size), + [(set GPR:$dst, (int_evm_call GPR:$gas, GPR:$addr, GPR:$value, + GPR:$arg_off, GPR:$arg_size, GPR:$ret_off, GPR:$ret_size))], + "CALL", "$dst, $gas, $addr, $value, $arg_off, $arg_size, $ret_off, $ret_size", + 0xF1, 100>; + + defm CALLCODE + : I<(outs GPR:$dst), (ins GPR:$gas, GPR:$addr, GPR:$value, GPR:$arg_off, + GPR:$arg_size, GPR:$ret_off, GPR:$ret_size), + [(set GPR:$dst, (int_evm_callcode GPR:$gas, GPR:$addr, GPR:$value, + GPR:$arg_off, GPR:$arg_size, GPR:$ret_off, GPR:$ret_size))], + "CALLCODE", "$dst, $gas, $addr, $value, $arg_off, $arg_size, $ret_off, $ret_size", + 0xF2, 100>; + + defm DELEGATECALL + : I<(outs GPR:$dst), (ins GPR:$gas, GPR:$addr, GPR:$arg_off, GPR:$arg_size, + GPR:$ret_off, GPR:$ret_size), + [(set GPR:$dst, (int_evm_delegatecall GPR:$gas, GPR:$addr, GPR:$arg_off, + GPR:$arg_size, GPR:$ret_off, GPR:$ret_size))], + "DELEGATECALL", "$dst, $gas, $addr, $arg_off, $arg_size, $ret_off, $ret_size", + 0xF4, 100>; + + defm STATICCALL + : I<(outs GPR:$dst), (ins GPR:$gas, GPR:$addr, GPR:$arg_off, GPR:$arg_size, + GPR:$ret_off, GPR:$ret_size), + [(set GPR:$dst, (int_evm_staticcall GPR:$gas, GPR:$addr, GPR:$arg_off, + GPR:$arg_size, GPR:$ret_off, GPR:$ret_size))], + "STATICCALL", "$dst, $gas, $addr, $arg_off, $arg_size, $ret_off, $ret_size", + 0xFA, 100>; +} + +let mayLoad = 1, hasSideEffects = 1 in +defm CREATE2 + : I<(outs GPR:$dst), (ins GPR:$value, GPR:$offset, GPR:$size, GPR:$salt), + [(set GPR:$dst, (int_evm_create2 GPR:$value, GPR:$offset, GPR:$size, + GPR:$salt))], + "CREATE2", " $dst, $value, $offset, $size, $salt", 0xF5, 32000>; + + +//===----------------------------------------------------------------------===// +// EVM instructions to return with error. +//===----------------------------------------------------------------------===// + +let isTerminator = 1, isBarrier = 1 in { + defm REVERT + : I<(outs), (ins GPR:$offset, GPR:$size), + [(int_evm_revert GPR:$offset, GPR:$size)], + "REVERT", "$offset, $size", 0xFD, 0>; + + defm RETURN + : I<(outs), (ins GPR:$offset, GPR:$size), + [(int_evm_return GPR:$offset, GPR:$size)], + "RETURN", "$offset, $size", 0xF3, 0>; +} + +let hasSideEffects = 1 in { + defm SELFDESTRUCT + : I<(outs), (ins GPR:$addr), [(int_evm_selfdestruct GPR:$addr)], + "SELFDESTRUCT", "$addr", 0xFF, 5000>; + + let isTerminator = 1, isBarrier = 1 in { + defm STOP : I<(outs), (ins), [(int_evm_stop)], "STOP", "", 0x00, 0>; + defm INVALID : I<(outs), (ins), [(int_evm_invalid)], "INVALID", "", 0xFE, 0>; + } +} + + +//===----------------------------------------------------------------------===// +// EVM instructions for stack manipulation. +//===----------------------------------------------------------------------===// + +defm POP : I<(outs), (ins), [], "POP", "", 0x50, 2>; + +foreach I = {1-16} in { + defm DUP#I : I<(outs), (ins), [], + "DUP"#I, "", !add(I, 0x7F), 3>; +} + +foreach I = {1-16} in { + defm SWAP#I : I<(outs), (ins), [], + "SWAP"#I, "", !add(I, 0x8F), 3>; +} + +def PUSH_LABEL : NI<(outs), (ins jmptarget:$dst), [], true, "", 0, 0> { + let isCodeGenOnly = 1; + // PUSH4_S. + let Size = 5; +} + +def PUSH_FRAME : NI<(outs), (ins i256imm:$imm), [], true, "", 0, 0> { + let isCodeGenOnly = 1; +} + +// Define register PUSH* instructions + +let IsPush = 1 in +defm PUSH0 : I<(outs), (ins), [], "PUSH0", "", 0x5F, 2>; + +foreach I = {1 - 32} in { + def PUSH#I : PushBase<(outs), (ins i256imm:$imm), [], false, "PUSH"#I#" $imm", + !add(I, 0x5F), 3> { + let BaseName = "PUSH"#I; + let Size = !add(I, 1); + let isCodeGenOnly = 1; + } +} + +// Define stack PUSH* instructions +def PUSH1_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH1$imm", + 0x60, 3> { + bits<8> imm; + let Inst{15-8} = Opc; + let Inst{7-0} = imm{7-0}; + let Size = 2; + let BaseName = "PUSH1"; +} + +def PUSH2_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH2$imm", + 0x61, 3> { + bits<16> imm; + let Inst{23-16} = Opc; + let Inst{15-0} = imm{15-0}; + let Size = 3; + let BaseName = "PUSH2"; +} + +def PUSH3_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH3$imm", + 0x62, 3> { + bits<24> imm; + let Inst{31-24} = Opc; + let Inst{23-0} = imm{23-0}; + let Size = 4; + let BaseName = "PUSH3"; +} + +def PUSH4_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH4$imm", + 0x63, 3> { + bits<32> imm; + let Inst{39-32} = Opc; + let Inst{31-0} = imm{31-0}; + let Size = 5; + let BaseName = "PUSH4"; +} + +def PUSH5_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH5$imm", + 0x64, 3> { + bits<40> imm; + let Inst{47-40} = Opc; + let Inst{39-0} = imm{39-0}; + let Size = 6; + let BaseName = "PUSH5"; +} + +def PUSH6_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH6$imm", + 0x65, 3> { + bits<48> imm; + let Inst{55-48} = Opc; + let Inst{47-0} = imm{47-0}; + let Size = 7; + let BaseName = "PUSH6"; +} + +def PUSH7_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH7$imm", + 0x66, 3> { + bits<56> imm; + let Inst{63-56} = Opc; + let Inst{55-0} = imm{55-0}; + let Size = 8; + let BaseName = "PUSH7"; +} + +def PUSH8_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH8$imm", + 0x67, 3> { + bits<64> imm; + let Inst{71-64} = Opc; + let Inst{63-0} = imm{63-0}; + let Size = 9; + let BaseName = "PUSH8"; +} + +def PUSH9_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH9$imm", + 0x68, 3> { + bits<72> imm; + let Inst{79-72} = Opc; + let Inst{71-0} = imm{71-0}; + let Size = 10; + let BaseName = "PUSH9"; + let DecoderMethod = "decodePUSH<9>"; +} + +def PUSH10_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH10$imm", + 0x69, 3> { + bits<80> imm; + let Inst{87-80} = Opc; + let Inst{79-0} = imm{79-0}; + let Size = 11; + let BaseName = "PUSH10"; + let DecoderMethod = "decodePUSH<10>"; +} + +def PUSH11_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH11$imm", + 0x6a, 3> { + bits<88> imm; + let Inst{95-88} = Opc; + let Inst{87-0} = imm{87-0}; + let Size = 12; + let BaseName = "PUSH11"; + let DecoderMethod = "decodePUSH<11>"; +} + +def PUSH12_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH12$imm", + 0x6b, 3> { + bits<96> imm; + let Inst{103-96} = Opc; + let Inst{95-0} = imm{95-0}; + let Size = 13; + let BaseName = "PUSH12"; + let DecoderMethod = "decodePUSH<12>"; +} + +def PUSH13_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH13$imm", + 0x6c, 3> { + bits<104> imm; + let Inst{111-104} = Opc; + let Inst{103-0} = imm{103-0}; + let Size = 14; + let BaseName = "PUSH13"; + let DecoderMethod = "decodePUSH<13>"; +} + +def PUSH14_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH14$imm", + 0x6d, 3> { + bits<112> imm; + let Inst{119-112} = Opc; + let Inst{111-0} = imm{111-0}; + let Size = 15; + let BaseName = "PUSH14"; + let DecoderMethod = "decodePUSH<14>"; +} + +def PUSH15_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH15$imm", + 0x6e, 3> { + bits<120> imm; + let Inst{127-120} = Opc; + let Inst{119-0} = imm{119-0}; + let Size = 16; + let BaseName = "PUSH15"; + let DecoderMethod = "decodePUSH<15>"; +} + +def PUSH16_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH16$imm", + 0x6f, 3> { + bits<128> imm; + let Inst{135-128} = Opc; + let Inst{127-0} = imm{127-0}; + let Size = 17; + let BaseName = "PUSH16"; + let DecoderMethod = "decodePUSH<16>"; +} + +def PUSH17_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH17$imm", + 0x70, 3> { + bits<136> imm; + let Inst{143-136} = Opc; + let Inst{135-0} = imm{135-0}; + let Size = 18; + let BaseName = "PUSH17"; + let DecoderMethod = "decodePUSH<17>"; +} + +def PUSH18_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH18$imm", + 0x71, 3> { + bits<144> imm; + let Inst{151-144} = Opc; + let Inst{143-0} = imm{143-0}; + let Size = 19; + let BaseName = "PUSH18"; + let DecoderMethod = "decodePUSH<18>"; +} + +def PUSH19_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH19$imm", + 0x72, 3> { + bits<152> imm; + let Inst{159-152} = Opc; + let Inst{151-0} = imm{151-0}; + let Size = 20; + let BaseName = "PUSH19"; + let DecoderMethod = "decodePUSH<19>"; +} + +def PUSH20_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH20$imm", + 0x73, 3> { + bits<160> imm; + let Inst{167-160} = Opc; + let Inst{159-0} = imm{159-0}; + let Size = 21; + let BaseName = "PUSH20"; + let DecoderMethod = "decodePUSH<20>"; +} + +def PUSH21_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH21$imm", + 0x74, 3> { + bits<168> imm; + let Inst{175-168} = Opc; + let Inst{167-0} = imm{167-0}; + let Size = 22; + let BaseName = "PUSH21"; + let DecoderMethod = "decodePUSH<21>"; +} + +def PUSH22_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH22$imm", + 0x75, 3> { + bits<176> imm; + let Inst{183-176} = Opc; + let Inst{175-0} = imm{175-0}; + let Size = 23; + let BaseName = "PUSH22"; + let DecoderMethod = "decodePUSH<22>"; +} + +def PUSH23_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH23$imm", + 0x76, 3> { + bits<184> imm; + let Inst{191-184} = Opc; + let Inst{183-0} = imm{183-0}; + let Size = 24; + let BaseName = "PUSH23"; + let DecoderMethod = "decodePUSH<23>"; +} + +def PUSH24_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH24$imm", + 0x77, 3> { + bits<192> imm; + let Inst{199-192} = Opc; + let Inst{191-0} = imm{191-0}; + let Size = 25; + let BaseName = "PUSH24"; + let DecoderMethod = "decodePUSH<24>"; +} + +def PUSH25_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH25$imm", + 0x78, 3> { + bits<200> imm; + let Inst{207-200} = Opc; + let Inst{199-0} = imm{199-0}; + let Size = 26; + let BaseName = "PUSH25"; + let DecoderMethod = "decodePUSH<25>"; +} + +def PUSH26_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH26$imm", + 0x79, 3> { + bits<208> imm; + let Inst{215-208} = Opc; + let Inst{207-0} = imm{207-0}; + let Size = 27; + let BaseName = "PUSH26"; + let DecoderMethod = "decodePUSH<26>"; +} + +def PUSH27_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH27$imm", + 0x7a, 3> { + bits<216> imm; + let Inst{223-216} = Opc; + let Inst{215-0} = imm{215-0}; + let Size = 28; + let BaseName = "PUSH27"; + let DecoderMethod = "decodePUSH<27>"; +} + +def PUSH28_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH28$imm", + 0x7b, 3> { + bits<224> imm; + let Inst{231-224} = Opc; + let Inst{223-0} = imm{223-0}; + let Size = 29; + let BaseName = "PUSH28"; + let DecoderMethod = "decodePUSH<28>"; +} + +def PUSH29_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH29$imm", + 0x7c, 3> { + bits<232> imm; + let Inst{239-232} = Opc; + let Inst{231-0} = imm{231-0}; + let Size = 30; + let BaseName = "PUSH29"; + let DecoderMethod = "decodePUSH<29>"; +} + +def PUSH30_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH30$imm", + 0x7d, 3> { + bits<240> imm; + let Inst{247-240} = Opc; + let Inst{239-0} = imm{239-0}; + let Size = 31; + let BaseName = "PUSH30"; + let DecoderMethod = "decodePUSH<30>"; +} + +def PUSH31_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH31$imm", + 0x7e, 3> { + bits<248> imm; + let Inst{255-248} = Opc; + let Inst{247-0} = imm{247-0}; + let Size = 32; + let BaseName = "PUSH31"; + let DecoderMethod = "decodePUSH<31>"; +} + +def PUSH32_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH32$imm", + 0x7f, 3> { + bits<256> imm; + let Inst{263-256} = Opc; + let Inst{255-0} = imm{255-0}; + let Size = 33; + let BaseName = "PUSH32"; + let DecoderMethod = "decodePUSH<32>"; +} + +// Pseudo instructions for linkage +let isAsCheapAsAMove = 1, isReMaterializable = 1, isCodeGenOnly = 1, hasSideEffects = 0 in { + let BaseName = "DATASIZE" in { + def DATASIZE : NI<(outs GPR:$dst), (ins jmptarget:$reloc), [], false, "", 0, 0>; + def DATASIZE_S : NI<(outs), (ins jmptarget:$reloc), [], true, "", 0, 0> { + // PUSH4_S + let Size = 5; + } + } + + let BaseName = "DATAOFFSET" in { + def DATAOFFSET : NI<(outs GPR:$dst), (ins jmptarget:$reloc), [], false, "", 0, 0>; + def DATAOFFSET_S : NI<(outs), (ins jmptarget:$reloc), [], true, "", 0, 0> { + // PUSH4_S + let Size = 5; + } + } + + let BaseName = "LINKERSYMBOL" in { + def LINKERSYMBOL : NI<(outs GPR:$dst), (ins jmptarget:$sym), [], false, "", 0, 0>; + def LINKERSYMBOL_S : NI<(outs), (ins jmptarget:$sym), [], true, "", 0, 0> { + // PUSH20_S + let Size = 21; + } + } + + let BaseName = "LOADIMMUTABLE" in { + def LOADIMMUTABLE : NI<(outs GPR:$dst), (ins jmptarget:$sym), [], false, "", 0, 0>; + def LOADIMMUTABLE_S : NI<(outs), (ins jmptarget:$sym), [], true, "", 0, 0> { + // PUSH32_S + let Size = 33; + } + } +} + +let isCodeGenOnly = 1, hasSideEffects = 1, isNotDuplicable = 1 in +def PUSHDEPLOYADDRESS + : NRI<(outs GPR:$res), (ins), [(set GPR:$res, (int_evm_pushdeployaddress))], + "PUSHDEPLOYADDRESS $res">; diff --git a/llvm/lib/Target/EVM/EVMLinkRuntime.cpp b/llvm/lib/Target/EVM/EVMLinkRuntime.cpp new file mode 100644 index 000000000000..eab58999400f --- /dev/null +++ b/llvm/lib/Target/EVM/EVMLinkRuntime.cpp @@ -0,0 +1,107 @@ +//===----- EVMLinkRuntime.cpp - inject runtime library into the module ---===// +// +// 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 pas links stdlib (evm-stdlib.ll) and internalize their contents. +// EVM doesn't have a proper linker and all programs consist of a single module. +// The pass links the the necessary modules into the program module. +// It's called at the beginning of optimization pipeline. The pass links +// the context of evm-stdlib.ll and internalize its content, after that +// global DCE is expected to be run to remove all unused functions. +// +//============================================================================// + +#include "llvm/IR/Function.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" +#include "llvm/IRReader/IRReader.h" +#include "llvm/Linker/Linker.h" +#include "llvm/Pass.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Transforms/IPO/Internalize.h" + +#include + +#include "EVM.h" + +#define DEBUG_TYPE "evm-link-runtime" + +using namespace llvm; + +static ExitOnError ExitOnErr; + +namespace { +/// Link std and runtime libraries into the module. +/// At the moment front ends work only with single source programs. +struct EVMLinkRuntime final : public ModulePass { +public: + static char ID; + EVMLinkRuntime() : ModulePass(ID) {} + bool runOnModule(Module &M) override; + + StringRef getPassName() const override { + return "Link runtime library into the module"; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + ModulePass::getAnalysisUsage(AU); + } +}; +} // namespace + +char EVMLinkRuntime::ID = 0; + +INITIALIZE_PASS(EVMLinkRuntime, "evm-link-runtime", + "Link standard and runtime library into the module", false, + false) + +static const char *STDLIB_DATA = +#include "EVMStdLib.inc" + ; + +static bool EVMLinkRuntimeImpl(Module &M, const char *ModuleToLink) { + Linker L(M); + LLVMContext &C = M.getContext(); + unsigned Flags = Linker::Flags::None; + + std::unique_ptr Buffer = + MemoryBuffer::getMemBuffer(ModuleToLink); + SMDiagnostic Err; + std::unique_ptr RTM = parseIR(*Buffer, Err, C); + if (!RTM) { + Err.print("Unable to parse evm-stdlib.ll", errs()); + exit(1); + } + + bool LinkErr = false; + LinkErr = L.linkInModule( + std::move(RTM), Flags, [](Module &M, const StringSet<> &GVS) { + internalizeModule(M, [&GVS](const GlobalValue &GV) { + // Keep original symbols as they are + return !GV.hasName() || (GVS.count(GV.getName()) == 0); + }); + }); + if (LinkErr) { + errs() << "Can't link EVM runtime or stdlib \n"; + exit(1); + } + return true; +} + +bool EVMLinkRuntime::runOnModule(Module &M) { + return EVMLinkRuntimeImpl(M, STDLIB_DATA); +} + +ModulePass *llvm::createEVMLinkRuntimePass() { return new EVMLinkRuntime(); } + +PreservedAnalyses EVMLinkRuntimePass::run(Module &M, + ModuleAnalysisManager &AM) { + if (EVMLinkRuntimeImpl(M, STDLIB_DATA)) + return PreservedAnalyses::none(); + return PreservedAnalyses::all(); +} diff --git a/llvm/lib/Target/EVM/EVMLowerIntrinsics.cpp b/llvm/lib/Target/EVM/EVMLowerIntrinsics.cpp new file mode 100644 index 000000000000..c5cbd3f69f97 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMLowerIntrinsics.cpp @@ -0,0 +1,89 @@ +//===----- EVMLowerIntrinsics.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 "EVM.h" +#include "EVMSubtarget.h" +#include "llvm/Analysis/TargetTransformInfo.h" +#include "llvm/CodeGen/TargetPassConfig.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Module.h" +#include "llvm/Transforms/Utils/LowerMemIntrinsics.h" + +#define DEBUG_TYPE "evm-lower-intrinsics" + +using namespace llvm; + +namespace { + +class EVMLowerIntrinsics : public ModulePass { +public: + static char ID; + + EVMLowerIntrinsics() : ModulePass(ID) {} + + bool runOnModule(Module &M) override; + bool expandMemIntrinsicUses(Function &F); + StringRef getPassName() const override { return "EVM Lower Intrinsics"; } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + } +}; +} // namespace + +char EVMLowerIntrinsics::ID = 0; + +INITIALIZE_PASS(EVMLowerIntrinsics, DEBUG_TYPE, "Lower intrinsics", false, + false) + +bool EVMLowerIntrinsics::expandMemIntrinsicUses(Function &F) { + const Intrinsic::ID ID = F.getIntrinsicID(); + bool Changed = false; + + for (auto I = F.user_begin(), E = F.user_end(); I != E;) { + auto *Inst = cast(*I); + ++I; + + if (ID == Intrinsic::memset) { + auto *Memset = cast(Inst); + expandMemSetAsLoop(Memset); + Changed = true; + Memset->eraseFromParent(); + } + } + + return Changed; +} + +bool EVMLowerIntrinsics::runOnModule(Module &M) { + bool Changed = false; + + for (Function &F : M) { + if (!F.isDeclaration()) + continue; + + switch (F.getIntrinsicID()) { + case Intrinsic::memset: + if (expandMemIntrinsicUses(F)) + Changed = true; + break; + + default: + break; + } + } + + return Changed; +} + +ModulePass *llvm::createEVMLowerIntrinsicsPass() { + return new EVMLowerIntrinsics(); +} diff --git a/llvm/lib/Target/EVM/EVMLowerJumpUnless.cpp b/llvm/lib/Target/EVM/EVMLowerJumpUnless.cpp new file mode 100644 index 000000000000..a553529c7c1f --- /dev/null +++ b/llvm/lib/Target/EVM/EVMLowerJumpUnless.cpp @@ -0,0 +1,88 @@ +//===----- EVMLowerJumpUnless.cpp - Lower jump_unless ----------*- 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 pass lowers jump_unless into iszero and jumpi instructions. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "EVMMachineFunctionInfo.h" +#include "EVMSubtarget.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" + +using namespace llvm; + +#define DEBUG_TYPE "evm-lower-jump-unless" +#define EVM_LOWER_JUMP_UNLESS_NAME "EVM Lower jump_unless" + +namespace { +class EVMLowerJumpUnless final : public MachineFunctionPass { +public: + static char ID; + + EVMLowerJumpUnless() : MachineFunctionPass(ID) { + initializeEVMLowerJumpUnlessPass(*PassRegistry::getPassRegistry()); + } + + StringRef getPassName() const override { return EVM_LOWER_JUMP_UNLESS_NAME; } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesCFG(); + MachineFunctionPass::getAnalysisUsage(AU); + } + + bool runOnMachineFunction(MachineFunction &MF) override; +}; +} // end anonymous namespace + +char EVMLowerJumpUnless::ID = 0; + +INITIALIZE_PASS(EVMLowerJumpUnless, DEBUG_TYPE, EVM_LOWER_JUMP_UNLESS_NAME, + false, false) + +FunctionPass *llvm::createEVMLowerJumpUnless() { + return new EVMLowerJumpUnless(); +} + +// Lower pseudo jump_unless into iszero and jumpi instructions. This pseudo +// instruction can only be present in stackified functions. +static void lowerPseudoJumpUnless(MachineInstr &MI, const EVMInstrInfo *TII, + const bool IsStackified) { + assert(IsStackified && "Found pseudo jump_unless in non-stackified function"); + assert(MI.getNumExplicitOperands() == 1 && + "Unexpected number of operands in pseudo jump_unless"); + BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), TII->get(EVM::ISZERO_S)); + BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), TII->get(EVM::PseudoJUMPI)) + .add(MI.getOperand(0)); +} + +bool EVMLowerJumpUnless::runOnMachineFunction(MachineFunction &MF) { + LLVM_DEBUG({ + dbgs() << "********** Lower jump_unless instructions **********\n" + << "********** Function: " << MF.getName() << '\n'; + }); + + const auto *TII = MF.getSubtarget().getInstrInfo(); + const bool IsStackified = + MF.getInfo()->getIsStackified(); + + bool Changed = false; + for (MachineBasicBlock &MBB : MF) { + for (auto &MI : make_early_inc_range(MBB)) { + if (MI.getOpcode() != EVM::PseudoJUMP_UNLESS) + continue; + + lowerPseudoJumpUnless(MI, TII, IsStackified); + MI.eraseFromParent(); + Changed = true; + } + } + return Changed; +} diff --git a/llvm/lib/Target/EVM/EVMMCInstLower.cpp b/llvm/lib/Target/EVM/EVMMCInstLower.cpp new file mode 100644 index 000000000000..1804d4aa6f65 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMMCInstLower.cpp @@ -0,0 +1,194 @@ +//===----- EVMMCInstLower.cpp - Convert EVM MachineInstr to an MCInst -----===// +// +// 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 code to lower EVM MachineInstrs to their corresponding +// MCInst records. +// +//===----------------------------------------------------------------------===// + +#include "EVMMCInstLower.h" +#include "EVMInstrInfo.h" +#include "MCTargetDesc/EVMMCExpr.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "TargetInfo/EVMTargetInfo.h" +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/IR/Constants.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCInst.h" + +using namespace llvm; + +extern cl::opt EVMKeepRegisters; + +// Stackify instruction that were not stackified before. +static void stackifyInstruction(const MachineInstr *MI, MCInst &OutMI) { + if (MI->isDebugInstr() || MI->isLabel() || MI->isInlineAsm()) + return; + + // Check there are no register operands. + assert(std::all_of(OutMI.begin(), OutMI.end(), + [](const MCOperand &MO) { return !MO.isReg(); })); + + // Set up final opcodes for the following codegen-only instructions. + unsigned Opcode = OutMI.getOpcode(); + if (Opcode == EVM::PUSH_LABEL) + OutMI.setOpcode(EVM::PUSH4_S); + + // Check that all the instructions are in the 'stack' form. + assert(EVM::getRegisterOpcode(OutMI.getOpcode())); +} + +MCSymbol * +EVMMCInstLower::GetGlobalAddressSymbol(const MachineOperand &MO) const { + switch (MO.getTargetFlags()) { + default: + llvm_unreachable("Unknown target flag on GV operand"); + case 0: + break; + } + + return Printer.getSymbol(MO.getGlobal()); +} + +MCSymbol * +EVMMCInstLower::GetExternalSymbolSymbol(const MachineOperand &MO) const { + switch (MO.getTargetFlags()) { + default: + llvm_unreachable("Unknown target flag on GV operand"); + case 0: + break; + } + + return Printer.GetExternalSymbolSymbol(MO.getSymbolName()); +} + +MCOperand EVMMCInstLower::LowerSymbolOperand(const MachineOperand &MO, + MCSymbol *Sym) const { + const MCExpr *Expr = MCSymbolRefExpr::create(Sym, Ctx); + + switch (MO.getTargetFlags()) { + default: + llvm_unreachable("Unknown target flag on GV operand"); + case 0: + break; + } + + if (MO.getOffset()) + Expr = MCBinaryExpr::createAdd( + Expr, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx); + return MCOperand::createExpr(Expr); +} + +void EVMMCInstLower::Lower( + const MachineInstr *MI, MCInst &OutMI, + const DenseMap &GlobSymbolToOffsetMap, + const MCSymbol *DataSectionSymbol) { + OutMI.setOpcode(MI->getOpcode()); + const MCInstrDesc &Desc = MI->getDesc(); + for (unsigned I = 0, E = MI->getNumOperands(); I != E; ++I) { + const MachineOperand &MO = MI->getOperand(I); + MCOperand MCOp; + switch (MO.getType()) { + default: + MI->print(errs()); + llvm_unreachable("Unknown operand type"); + case MachineOperand::MO_Register: + // Ignore all implicit register operands. + if (MO.isImplicit()) + continue; + + MCOp = MCOperand::createReg(EncodeVReg(MO.getReg())); + break; + case MachineOperand::MO_Immediate: + MCOp = MCOperand::createImm(MO.getImm()); + break; + case MachineOperand::MO_CImmediate: { + const APInt &CImmVal = MO.getCImm()->getValue(); + // Check for the max number of significant bits - 64, otherwise + // the assertion in getZExtValue() is failed. + if (CImmVal.getSignificantBits() <= 64 && CImmVal.isNonNegative()) { + MCOp = MCOperand::createImm(MO.getCImm()->getZExtValue()); + } else { + // To avoid a memory leak, initial size of the SmallString should be + // chosen enough for the entire string. Otherwise, its internal memory + // will be reallocated into the generic heap but not into the Ctx + // arena and thus never deallocated. + auto *Str = new (Ctx) SmallString<80>(); + CImmVal.toString(*Str, /*Radix=*/16, /*Signed=*/false, + /*formatAsCLiteral=*/false, /*UpperCase=*/true); + MCOp = MCOperand::createExpr(EVMCImmMCExpr::create(*Str, Ctx)); + } + } break; + case MachineOperand::MO_MCSymbol: { +#ifndef NDEBUG + unsigned Opc = MI->getOpcode(); + // We handle the linkage-related instructions in the EVMAsmPrinter. + assert(Opc != EVM::DATASIZE_S && Opc != EVM::DATAOFFSET_S && + Opc != EVM::LINKERSYMBOL_S && Opc != EVM::LOADIMMUTABLE_S); +#endif // NDEBUG + + if (auto It = GlobSymbolToOffsetMap.find(MO.getMCSymbol()); + It != GlobSymbolToOffsetMap.end()) { + const MCExpr *Expr = MCSymbolRefExpr::create(DataSectionSymbol, Ctx); + if (It->second) + Expr = MCBinaryExpr::createAdd( + Expr, MCConstantExpr::create(It->second, Ctx), Ctx); + + MCOp = MCOperand::createExpr(Expr); + break; + } + MCOp = + MCOperand::createExpr(MCSymbolRefExpr::create(MO.getMCSymbol(), Ctx)); + } break; + case MachineOperand::MO_MachineBasicBlock: + MCOp = MCOperand::createExpr( + MCSymbolRefExpr::create(MO.getMBB()->getSymbol(), Ctx)); + break; + case MachineOperand::MO_GlobalAddress: + MCOp = LowerSymbolOperand(MO, GetGlobalAddressSymbol(MO)); + break; + case MachineOperand::MO_ExternalSymbol: + MCOp = LowerSymbolOperand(MO, GetExternalSymbolSymbol(MO)); + break; + } + OutMI.addOperand(MCOp); + } + if (!EVMKeepRegisters) + stackifyInstruction(MI, OutMI); + else if (Desc.variadicOpsAreDefs()) + OutMI.insert(OutMI.begin(), MCOperand::createImm(MI->getNumExplicitDefs())); +} + +unsigned EVMMCInstLower::EncodeVReg(unsigned Reg) { + if (Register::isVirtualRegister(Reg)) { + const TargetRegisterClass *RC = MRI.getRegClass(Reg); + const VRegRCMap::const_iterator I = VRegMapping.find(RC); + assert(I != VRegMapping.end() && "Bad register class"); + const DenseMap &RegMap = I->second; + + const VRegMap::const_iterator VI = RegMap.find(Reg); + assert(VI != RegMap.end() && "Bad virtual register"); + const unsigned RegNum = VI->second; + unsigned Ret = 0; + if (RC == &EVM::GPRRegClass) + Ret = (1 << 28); + else + report_fatal_error("Unexpected register class"); + + // Insert the vreg number + Ret |= (RegNum & 0x0FFFFFFF); + return Ret; + } + + // Some special-use registers (at least SP) are actually physical registers. + // Encode this as the register class ID of 0 and the real register ID. + return Reg & 0x0FFFFFFF; +} diff --git a/llvm/lib/Target/EVM/EVMMCInstLower.h b/llvm/lib/Target/EVM/EVMMCInstLower.h new file mode 100644 index 000000000000..120ee8844baf --- /dev/null +++ b/llvm/lib/Target/EVM/EVMMCInstLower.h @@ -0,0 +1,62 @@ +//===----- EVMMCInstLower.h - Lower MachineInstr to MCInst ------*- 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_LIB_TARGET_EVM_EVMMCINSTLOWER_H +#define LLVM_LIB_TARGET_EVM_EVMMCINSTLOWER_H + +#include "llvm/ADT/DenseMap.h" + +namespace llvm { +class AsmPrinter; +class GlobalValue; +class MCContext; +class MCInst; +class MCOperand; +class MCSymbol; +class MachineInstr; +class MachineOperand; +class MachineRegisterInfo; +class TargetRegisterClass; + +/// EVMMCInstLower - This class is used to lower an MachineInstr +/// into an MCInst. +class LLVM_LIBRARY_VISIBILITY EVMMCInstLower { + // TODO: Once stackification is implemented this should be removed, + // see the comments in EVMAsmPrinter. + using VRegMap = DenseMap; + using VRegRCMap = DenseMap; + + MCContext &Ctx; + AsmPrinter &Printer; + const VRegRCMap &VRegMapping; + const MachineRegisterInfo &MRI; + +public: + EVMMCInstLower(MCContext &Ctx, AsmPrinter &Printer, + const VRegRCMap &VRegMapping, const MachineRegisterInfo &MRI) + : Ctx(Ctx), Printer(Printer), VRegMapping(VRegMapping), MRI(MRI) {} + + void Lower(const MachineInstr *MI, MCInst &OutMI, + const DenseMap &GlobSymbolToOffsetMap, + const MCSymbol *DataSectionSymbol); + +private: + // Encodes the register class in the upper 4 bits along with the register + // number in 28 lower bits. + // Must be kept in sync with EVMInstPrinter::printRegName. + // TODO: this can be removed once stackification is implemented. + unsigned EncodeVReg(unsigned Reg); + + MCOperand LowerSymbolOperand(const MachineOperand &MO, MCSymbol *Sym) const; + MCSymbol *GetGlobalAddressSymbol(const MachineOperand &MO) const; + MCSymbol *GetExternalSymbolSymbol(const MachineOperand &MO) const; +}; + +} // namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_EVMMCINSTLOWER_H diff --git a/llvm/lib/Target/EVM/EVMMachineFunctionInfo.cpp b/llvm/lib/Target/EVM/EVMMachineFunctionInfo.cpp new file mode 100644 index 000000000000..bae24580f174 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMMachineFunctionInfo.cpp @@ -0,0 +1,42 @@ +//=--------- EVMMachineFunctionInfo.cpp - EVM Machine Function Info ---------=// +// +// 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 EVM-specific per-machine-function information. +// +//===----------------------------------------------------------------------===// + +#include "EVMMachineFunctionInfo.h" +using namespace llvm; + +EVMMachineFunctionInfo::~EVMMachineFunctionInfo() = default; + +MachineFunctionInfo *EVMMachineFunctionInfo::clone( + BumpPtrAllocator &Allocator, MachineFunction &DestMF, + const DenseMap &Src2DstMBB) + const { + return DestMF.cloneInfo(*this); +} + +yaml::EVMMachineFunctionInfo::~EVMMachineFunctionInfo() = default; + +yaml::EVMMachineFunctionInfo::EVMMachineFunctionInfo( + const llvm::EVMMachineFunctionInfo &MFI) + : IsStackified(MFI.getIsStackified()), + NumberOfParameters(MFI.getNumParams()), + HasPushDeployAddress(MFI.getHasPushDeployAddress()) {} + +void yaml::EVMMachineFunctionInfo::mappingImpl(yaml::IO &YamlIO) { + MappingTraits::mapping(YamlIO, *this); +} + +void EVMMachineFunctionInfo::initializeBaseYamlFields( + const yaml::EVMMachineFunctionInfo &YamlMFI) { + IsStackified = YamlMFI.IsStackified; + NumberOfParameters = YamlMFI.NumberOfParameters; + HasPushDeployAddress = YamlMFI.HasPushDeployAddress; +} diff --git a/llvm/lib/Target/EVM/EVMMachineFunctionInfo.h b/llvm/lib/Target/EVM/EVMMachineFunctionInfo.h new file mode 100644 index 000000000000..80a2e5a09a63 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMMachineFunctionInfo.h @@ -0,0 +1,119 @@ +//=------ EVMMachineFunctionInfo.h - EVM machine function info ----*- 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 declares EVM-specific per-machine-function information. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVMMACHINEFUNCTIONINFO_H +#define LLVM_LIB_TARGET_EVM_EVMMACHINEFUNCTIONINFO_H + +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm/CodeGen/MIRYamlMapping.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" + +namespace llvm { + +class EVMMachineFunctionInfo; + +namespace yaml { + +struct EVMMachineFunctionInfo final : public yaml::MachineFunctionInfo { + bool IsStackified = false; + unsigned NumberOfParameters = 0; + bool HasPushDeployAddress = false; + + EVMMachineFunctionInfo() = default; + explicit EVMMachineFunctionInfo(const llvm::EVMMachineFunctionInfo &MFI); + ~EVMMachineFunctionInfo() override; + + void mappingImpl(yaml::IO &YamlIO) override; +}; + +template <> struct MappingTraits { + static void mapping(IO &YamlIO, EVMMachineFunctionInfo &MFI) { + YamlIO.mapOptional("isStackified", MFI.IsStackified, false); + YamlIO.mapOptional("numberOfParameters", MFI.NumberOfParameters, 0); + YamlIO.mapOptional("hasPushDeployAddress", MFI.HasPushDeployAddress, false); + } +}; +} // end namespace yaml + +/// This class is derived from MachineFunctionInfo and contains private +/// EVM-specific information for each MachineFunction. +class EVMMachineFunctionInfo final : public MachineFunctionInfo { + /// A mapping from CodeGen vreg index to a boolean value indicating whether + /// the given register is considered to be "stackified", meaning it has been + /// determined or made to meet the stack requirements: + /// - single use (per path) + /// - single def (per path) + /// - defined and used in LIFO order with other stack registers + BitVector VRegStackified; + + /// Number of parameters. Their type doesn't matter as it always is i256. + unsigned NumberOfParameters = 0; + + /// If the MF's instructions are in 'stack' form. + bool IsStackified = false; + + /// True if MF has PushDeployAddress instruction. + bool HasPushDeployAddress = false; + +public: + explicit EVMMachineFunctionInfo(MachineFunction &MF) {} + EVMMachineFunctionInfo(const Function &F, const TargetSubtargetInfo *STI) {} + ~EVMMachineFunctionInfo() override; + + MachineFunctionInfo * + clone(BumpPtrAllocator &Allocator, MachineFunction &DestMF, + const DenseMap &Src2DstMBB) + const override; + + void initializeBaseYamlFields(const yaml::EVMMachineFunctionInfo &YamlMFI); + + void stackifyVReg(MachineRegisterInfo &MRI, unsigned VReg) { + assert(MRI.getUniqueVRegDef(VReg)); + auto I = Register::virtReg2Index(VReg); + if (I >= VRegStackified.size()) + VRegStackified.resize(I + 1); + VRegStackified.set(I); + } + + void unstackifyVReg(unsigned VReg) { + auto I = Register::virtReg2Index(VReg); + if (I < VRegStackified.size()) + VRegStackified.reset(I); + } + + bool isVRegStackified(unsigned VReg) const { + auto I = Register::virtReg2Index(VReg); + if (I >= VRegStackified.size()) + return false; + return VRegStackified.test(I); + } + + void addParam() { + ++NumberOfParameters; + } + + unsigned getNumParams() const { + return NumberOfParameters; + } + + void setIsStackified(bool Val = true) { IsStackified = Val; } + + bool getIsStackified() const { return IsStackified; } + + void setHasPushDeployAddress(bool Val = true) { HasPushDeployAddress = Val; } + + bool getHasPushDeployAddress() const { return HasPushDeployAddress; } +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_EVMMACHINEFUNCTIONINFO_H diff --git a/llvm/lib/Target/EVM/EVMMarkRecursiveFunctions.cpp b/llvm/lib/Target/EVM/EVMMarkRecursiveFunctions.cpp new file mode 100644 index 000000000000..a8185222a4a0 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMMarkRecursiveFunctions.cpp @@ -0,0 +1,92 @@ +//===-- EVMMarkRecursiveFunctions.cpp - Mark recursive fuctions --*- 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 pass marks all recursive functions in the module with the +// "evm-recursive" attribute by analyzing the call graph. We dont' need to check +// functions to detect unknown calls (as it is implemented in FunctionAttrs.cpp, +// function createSCCNodeSet), because all functions are known at compile time, +// and we don't have any indirect calls. +// This is needed during stackification, since we can't use spills for recursive +// functions, as we are using memory for spills, and not the real stack. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "llvm/ADT/SCCIterator.h" +#include "llvm/Analysis/CallGraph.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" +#include "llvm/InitializePasses.h" + +#define DEBUG_TYPE "evm-mark-recursive-functions" + +using namespace llvm; + +namespace { + +class EVMMarkRecursiveFunctions final : public ModulePass { +public: + static char ID; // Pass ID + EVMMarkRecursiveFunctions() : ModulePass(ID) {} + + StringRef getPassName() const override { + return "EVM mark recursive functions"; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + ModulePass::getAnalysisUsage(AU); + } + + bool runOnModule(Module &M) override; +}; + +} // end anonymous namespace + +static bool runImpl(CallGraph &CG) { + bool Changed = false; + for (auto SCCI = scc_begin(&CG); !SCCI.isAtEnd(); ++SCCI) { + const auto &SCC = *SCCI; + // Only mark indirect recursive functions (size > 1) or self-recursive + // functions (size == 1 with a cycle). Size can't be zero, since we + // always have at least one node in the SCC. + if (SCC.size() == 1 && !SCCI.hasCycle()) + continue; + + for (const auto *Node : SCC) { + Function *F = Node->getFunction(); + if (!F || F->isDeclaration()) + continue; + F->addFnAttr("evm-recursive"); + Changed = true; + } + } + return Changed; +} + +bool EVMMarkRecursiveFunctions::runOnModule(Module &M) { + return runImpl(getAnalysis().getCallGraph()); +} + +char EVMMarkRecursiveFunctions::ID = 0; + +INITIALIZE_PASS_BEGIN(EVMMarkRecursiveFunctions, DEBUG_TYPE, + "Mark all recursive functions", false, false) +INITIALIZE_PASS_DEPENDENCY(CallGraphWrapperPass) +INITIALIZE_PASS_END(EVMMarkRecursiveFunctions, DEBUG_TYPE, + "Mark all recursive functions", false, false) + +ModulePass *llvm::createEVMMarkRecursiveFunctionsPass() { + return new EVMMarkRecursiveFunctions; +} + +PreservedAnalyses +EVMMarkRecursiveFunctionsPass::run(Module &M, ModuleAnalysisManager &AM) { + runImpl(AM.getResult(M)); + return PreservedAnalyses::all(); +} diff --git a/llvm/lib/Target/EVM/EVMOptimizeLiveIntervals.cpp b/llvm/lib/Target/EVM/EVMOptimizeLiveIntervals.cpp new file mode 100644 index 000000000000..90e62461aaea --- /dev/null +++ b/llvm/lib/Target/EVM/EVMOptimizeLiveIntervals.cpp @@ -0,0 +1,111 @@ +//===--- EVMOptimizeLiveIntervals.cpp - LiveInterval processing -*- 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 optimize LiveIntervals for use in a post-RA context. +// +// LiveIntervals normally runs before register allocation when the code is +// only recently lowered out of SSA form, so it's uncommon for registers to +// have multiple defs, and when they do, the defs are usually closely related. +// Later, after coalescing, tail duplication, and other optimizations, it's +// more common to see registers with multiple unrelated defs. This pass +// updates LiveIntervals to distribute the value numbers across separate +// LiveIntervals. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "EVMMachineFunctionInfo.h" +#include "EVMSubtarget.h" +#include "llvm/CodeGen/LiveIntervals.h" +#include "llvm/CodeGen/MachineBlockFrequencyInfo.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +using namespace llvm; + +#define DEBUG_TYPE "evm-optimize-live-intervals" + +namespace { +class EVMOptimizeLiveIntervals final : public MachineFunctionPass { + StringRef getPassName() const override { + return "EVM Optimize Live Intervals"; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesCFG(); + AU.addRequired(); + AU.addPreserved(); + AU.addPreserved(); + AU.addPreserved(); + AU.addPreservedID(LiveVariablesID); + AU.addPreservedID(MachineDominatorsID); + MachineFunctionPass::getAnalysisUsage(AU); + } + + MachineFunctionProperties getRequiredProperties() const override { + return MachineFunctionProperties().set( + MachineFunctionProperties::Property::TracksLiveness); + } + + bool runOnMachineFunction(MachineFunction &MF) override; + +public: + static char ID; // Pass identification, replacement for typeid + EVMOptimizeLiveIntervals() : MachineFunctionPass(ID) {} +}; +} // end anonymous namespace + +char EVMOptimizeLiveIntervals::ID = 0; +INITIALIZE_PASS(EVMOptimizeLiveIntervals, DEBUG_TYPE, + "Optimize LiveIntervals for EVM", false, false) + +FunctionPass *llvm::createEVMOptimizeLiveIntervals() { + return new EVMOptimizeLiveIntervals(); +} + +bool EVMOptimizeLiveIntervals::runOnMachineFunction(MachineFunction &MF) { + LLVM_DEBUG(dbgs() << "********** Optimize LiveIntervals **********\n" + "********** Function: " + << MF.getName() << '\n'); + + MachineRegisterInfo &MRI = MF.getRegInfo(); + auto &LIS = getAnalysis().getLIS(); + + // We don't preserve SSA form. + MRI.leaveSSA(); + + assert(MRI.tracksLiveness() && "OptimizeLiveIntervals expects liveness"); + + // Split multiple-VN LiveIntervals into multiple LiveIntervals. + SmallVector SplitLIs; + for (unsigned I = 0, E = MRI.getNumVirtRegs(); I < E; ++I) { + Register Reg = Register::index2VirtReg(I); + + if (MRI.reg_nodbg_empty(Reg)) + continue; + + LIS.splitSeparateComponents(LIS.getInterval(Reg), SplitLIs); + SplitLIs.clear(); + } + + // In FixIrreducibleControlFlow, we conservatively inserted IMPLICIT_DEF + // instructions to satisfy LiveIntervals' requirement that all uses be + // dominated by defs. Now that LiveIntervals has computed which of these + // defs are actually needed and which are dead, remove the dead ones. + for (MachineInstr &MI : llvm::make_early_inc_range(MF.front())) { + if (MI.isImplicitDef() && MI.getOperand(0).isDead()) { + LiveInterval &LI = LIS.getInterval(MI.getOperand(0).getReg()); + LIS.removeVRegDefAt(LI, LIS.getInstructionIndex(MI).getRegSlot()); + LIS.RemoveMachineInstrFromMaps(MI); + MI.eraseFromParent(); + } + } + + return true; +} diff --git a/llvm/lib/Target/EVM/EVMPeephole.cpp b/llvm/lib/Target/EVM/EVMPeephole.cpp new file mode 100644 index 000000000000..93cc7a4a80d7 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMPeephole.cpp @@ -0,0 +1,100 @@ + +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Pre-emission peephole optimizations. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" + +#define DEBUG_TYPE "evm-peephole" +#define EVM_PEEPHOLE "EVM Peephole" + +using namespace llvm; + +namespace { +/// Perform foldings on stack-form MIR before emission. +class EVMPeephole final : public MachineFunctionPass { +public: + static char ID; + EVMPeephole() : MachineFunctionPass(ID) {} + + StringRef getPassName() const override { return EVM_PEEPHOLE; } + bool runOnMachineFunction(MachineFunction &MF) override; + bool optimizeConditionaJumps(MachineBasicBlock &MBB) const; +}; +} // namespace + +bool EVMPeephole::runOnMachineFunction(MachineFunction &MF) { + bool Changed = false; + for (MachineBasicBlock &MBB : MF) { + Changed |= optimizeConditionaJumps(MBB); + } + return Changed; +} + +static bool isNegatedAndJumpedOn(const MachineBasicBlock &MBB, + MachineBasicBlock::const_iterator I) { + if (I == MBB.end() || I->getOpcode() != EVM::ISZERO_S) + return false; + ++I; + // When a conditional jump’s predicate is a (possibly nested) bitwise `or`, + // both operands are eligible for folding. Currently we only fold the operand + // computed last. + // TODO: #887 Apply folding to all operands. + while (I != MBB.end() && I->getOpcode() == EVM::OR_S) + ++I; + return I != MBB.end() && I->getOpcode() == EVM::PseudoJUMPI; +} + +bool EVMPeephole::optimizeConditionaJumps(MachineBasicBlock &MBB) const { + MachineBasicBlock::iterator I = MBB.begin(); + const TargetInstrInfo *TII = MBB.getParent()->getSubtarget().getInstrInfo(); + + while (I != MBB.end()) { + // Fold ISZERO ISZERO to nothing, only if it's a predicate to JUMPI. + if (I->getOpcode() == EVM::ISZERO_S && + isNegatedAndJumpedOn(MBB, std::next(I))) { + std::next(I)->eraseFromParent(); + I->eraseFromParent(); + return true; + } + + // Fold EQ ISZERO to SUB, only if it's a predicate to JUMPI. + if (I->getOpcode() == EVM::EQ_S && + isNegatedAndJumpedOn(MBB, std::next(I))) { + I->setDesc(TII->get(EVM::SUB_S)); + std::next(I)->eraseFromParent(); + return true; + } + + // Fold SUB ISZERO to EQ, only if it's a predicate to JUMPI. + if (I->getOpcode() == EVM::SUB_S && + isNegatedAndJumpedOn(MBB, std::next(I))) { + I->setDesc(TII->get(EVM::EQ_S)); + std::next(I)->eraseFromParent(); + return true; + } + + ++I; + } + return false; +} + +char EVMPeephole::ID = 0; + +INITIALIZE_PASS(EVMPeephole, DEBUG_TYPE, EVM_PEEPHOLE, false, false) + +FunctionPass *llvm::createEVMPeepholePass() { return new EVMPeephole(); } diff --git a/llvm/lib/Target/EVM/EVMRegisterInfo.cpp b/llvm/lib/Target/EVM/EVMRegisterInfo.cpp new file mode 100644 index 000000000000..e8a9314e9f01 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMRegisterInfo.cpp @@ -0,0 +1,82 @@ +//===------- EVMRegisterInfo.cpp - EVM Register Information ---*- 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 the EVM implementation of the +// TargetRegisterInfo class. +// +//===----------------------------------------------------------------------===// + +#include "EVMFrameLowering.h" +#include "EVMInstrInfo.h" +#include "EVMSubtarget.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +using namespace llvm; + +#define DEBUG_TYPE "evm-reg-info" + +#define GET_REGINFO_TARGET_DESC +#include "EVMGenRegisterInfo.inc" + +EVMRegisterInfo::EVMRegisterInfo() : EVMGenRegisterInfo(0) {} + +const MCPhysReg * +EVMRegisterInfo::getCalleeSavedRegs(const MachineFunction *) const { + static const std::array CalleeSavedRegs = {0}; + return CalleeSavedRegs.data(); +} + +BitVector +EVMRegisterInfo::getReservedRegs(const MachineFunction & /*MF*/) const { + BitVector Reserved(getNumRegs()); + Reserved.set(EVM::SP); + return Reserved; +} + +bool EVMRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, + int SPAdj, unsigned FIOperandNum, + RegScavenger *RS) const { + assert(SPAdj == 0); + MachineInstr &MI = *II; + MachineBasicBlock &MBB = *MI.getParent(); + MachineFunction &MF = *MBB.getParent(); + const int FrameIndex = MI.getOperand(FIOperandNum).getIndex(); + const MachineFrameInfo &MFI = MF.getFrameInfo(); + const int64_t FrameOffset = MFI.getObjectOffset(FrameIndex); + + assert(FrameOffset >= 0 && "FrameOffset < 0"); + assert(FrameOffset < static_cast(MFI.getStackSize()) && + "FrameOffset overflows stack size"); + assert(MFI.getObjectSize(FrameIndex) != 0 && + "We assume that variable-sized objects have already been lowered, " + "and don't use FrameIndex operands."); + + if (FrameOffset > 0) { + MachineOperand &OffsetMO = MI.getOperand(FIOperandNum + 1); + const ConstantInt *ConstVal = OffsetMO.getCImm(); + APInt Offset = ConstVal->getValue(); + Offset += FrameOffset; + assert((Offset.getZExtValue() % 32) == 0); + OffsetMO.setCImm(ConstantInt::get(ConstVal->getContext(), Offset)); + } + + MI.getOperand(FIOperandNum) + .ChangeToRegister(getFrameRegister(MF), + /*isDef=*/false); + return true; +} + +Register EVMRegisterInfo::getFrameRegister(const MachineFunction &MF) const { + return EVM::SP; +} + +const TargetRegisterClass * +EVMRegisterInfo::getPointerRegClass(const MachineFunction &MF, + unsigned Kind) const { + return &EVM::GPRRegClass; +} diff --git a/llvm/lib/Target/EVM/EVMRegisterInfo.h b/llvm/lib/Target/EVM/EVMRegisterInfo.h new file mode 100644 index 000000000000..4c895ca567ef --- /dev/null +++ b/llvm/lib/Target/EVM/EVMRegisterInfo.h @@ -0,0 +1,47 @@ +//===----- EVMRegisterInfo.h - EVM Register Information Impl -*- 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 the EVM implementation of the EVMRegisterInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVMREGISTERINFO_H +#define LLVM_LIB_TARGET_EVM_EVMREGISTERINFO_H + +#define GET_REGINFO_HEADER +#include "EVMGenRegisterInfo.inc" + +namespace llvm { + +class MachineFunction; +class RegScavenger; + +class EVMRegisterInfo final : public EVMGenRegisterInfo { +public: + explicit EVMRegisterInfo(); + + // Code Generation virtual methods. + const MCPhysReg *getCalleeSavedRegs(const MachineFunction *MF) const override; + BitVector getReservedRegs(const MachineFunction &MF) const override; + bool eliminateFrameIndex(MachineBasicBlock::iterator II, int SPAdj, + unsigned FIOperandNum, + RegScavenger *RS = nullptr) const override; + + Register getFrameRegister(const MachineFunction &MF) const override; + + const TargetRegisterClass * + getPointerRegClass(const MachineFunction &MF, + unsigned Kind = 0) const override; + + // This does not apply to wasm. + const uint32_t *getNoPreservedMask() const override { return nullptr; } +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_EVMREGISTERINFO_H diff --git a/llvm/lib/Target/EVM/EVMRegisterInfo.td b/llvm/lib/Target/EVM/EVMRegisterInfo.td new file mode 100644 index 000000000000..af8f6bd704d9 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMRegisterInfo.td @@ -0,0 +1,43 @@ +//===---EVMRegisterInfo.td - Describe the EVM Registers -*- tablegen -*----===// +// +// 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 describes the EVM register classes and some nominal +// physical registers. +// +//===----------------------------------------------------------------------===// + +class EVMReg : Register { + let Namespace = "EVM"; +} + +//===----------------------------------------------------------------------===// +// Registers +//===----------------------------------------------------------------------===// + +// Special registers used as the frame and stack pointer. +def SP : EVMReg<"%SP">; + +// The register allocation framework requires register classes have at least +// one register, so we define one for the integer register class since +// we otherwise don't need a physical register in this class. +def GPR256 : EVMReg<"%reg">; + +// The value stack "register". This is an opaque entity which serves to order +// uses and defs that must remain in LIFO order. +def VALUE_STACK : EVMReg<"STACK">; + +// The incoming arguments "register". This is an opaque entity which serves to +// order the ARGUMENT instructions that are emulating live-in registers and +// must not be scheduled below other instructions. +def ARGUMENTS : EVMReg<"ARGUMENTS">; + +//===----------------------------------------------------------------------===// +// Register classes +//===----------------------------------------------------------------------===// + +def GPR : RegisterClass<"EVM", [i256], 256, (add GPR256, SP)>; diff --git a/llvm/lib/Target/EVM/EVMSHA3ConstFolding.cpp b/llvm/lib/Target/EVM/EVMSHA3ConstFolding.cpp new file mode 100644 index 000000000000..062e527b5d57 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMSHA3ConstFolding.cpp @@ -0,0 +1,48 @@ +//===-- EVMSHA3ConstFolding.cpp - Const fold calls to sha3 ------*- 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 is the EVM SHA3 const folding pass. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "llvm/Analysis/AssumptionCache.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/MemorySSA.h" +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/IntrinsicsEVM.h" +#include "llvm/Transforms/Scalar/SHA3ConstFolding.h" + +using namespace llvm; + +PreservedAnalyses EVMSHA3ConstFoldingPass::run(Function &F, + FunctionAnalysisManager &AM) { + // Don't run this pass if optimizing for size, since result of SHA3 + // calls will be replaced with a 32-byte constant, thus PUSH32 will + // be emitted. This will increase code size. + if (F.hasOptSize()) + return PreservedAnalyses::all(); + + auto &AC = AM.getResult(F); + auto &AA = AM.getResult(F); + const auto &TLI = AM.getResult(F); + auto &DT = AM.getResult(F); + auto &MSSA = AM.getResult(F).getMSSA(); + auto &LI = AM.getResult(F); + auto IsSha3Call = [](const Instruction *I) { + const auto *II = dyn_cast(I); + return II && II->getIntrinsicID() == Intrinsic::evm_sha3; + }; + + return llvm::runSHA3ConstFolding(F, AA, AC, MSSA, DT, TLI, LI, IsSha3Call, + EVMAS::AS_HEAP) + ? PreservedAnalyses::none() + : PreservedAnalyses::all(); +} diff --git a/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp b/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp new file mode 100644 index 000000000000..1a0d180075e4 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp @@ -0,0 +1,829 @@ +//===-- EVMSingleUseExpression.cpp - Register Stackification --------------===// +// +// 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 pass reorders instructions to put register uses and defs in an order +// such that they form single-use expression trees. Registers fitting this form +// are then marked as "stackified", meaning references to them are replaced by +// "push" and "pop" from the stack. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "EVMMachineFunctionInfo.h" +#include "EVMSubtarget.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" // for EVM::ARGUMENT_* +#include "llvm/Analysis/AliasAnalysis.h" +#include "llvm/CodeGen/LiveIntervals.h" +#include "llvm/CodeGen/MachineBlockFrequencyInfo.h" +#include "llvm/CodeGen/MachineDominators.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/IR/GlobalAlias.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include +using namespace llvm; + +#define DEBUG_TYPE "evm-single-use-expressions" + +namespace { +class EVMSingleUseExpression final : public MachineFunctionPass { + StringRef getPassName() const override { + return "EVM Single use expressions"; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesCFG(); + AU.addRequired(); + AU.addRequired(); + AU.addPreserved(); + AU.addPreserved(); + MachineFunctionPass::getAnalysisUsage(AU); + } + + bool runOnMachineFunction(MachineFunction &MF) override; + +public: + static char ID; // Pass identification, replacement for typeid + EVMSingleUseExpression() : MachineFunctionPass(ID) {} +}; +} // end anonymous namespace + +char EVMSingleUseExpression::ID = 0; +INITIALIZE_PASS(EVMSingleUseExpression, DEBUG_TYPE, + "Reorder instructions to use the EVM value stack", false, false) + +FunctionPass *llvm::createEVMSingleUseExpression() { + return new EVMSingleUseExpression(); +} + +// Decorate the given instruction with implicit operands that enforce the +// expression stack ordering constraints for an instruction which is on +// the expression stack. +static void imposeStackOrdering(MachineInstr *MI) { + // Write the opaque VALUE_STACK register. + if (!MI->definesRegister(EVM::VALUE_STACK, /*TRI*/ nullptr)) + MI->addOperand(MachineOperand::CreateReg(EVM::VALUE_STACK, + /*isDef=*/true, + /*isImp=*/true)); + + // Also read the opaque VALUE_STACK register. + if (!MI->readsRegister(EVM::VALUE_STACK, /*TRI*/ nullptr)) + MI->addOperand(MachineOperand::CreateReg(EVM::VALUE_STACK, + /*isDef=*/false, + /*isImp=*/true)); +} + +// Convert an IMPLICIT_DEF instruction into an instruction which defines +// a constant zero value. +static void convertImplicitDefToConstZero(MachineInstr *MI, + MachineRegisterInfo &MRI, + const TargetInstrInfo *TII, + MachineFunction &MF, + LiveIntervals &LIS) { + assert(MI->getOpcode() == TargetOpcode::IMPLICIT_DEF); + + assert(MRI.getRegClass(MI->getOperand(0).getReg()) == &EVM::GPRRegClass); + MI->setDesc(TII->get(EVM::CONST_I256)); + MI->addOperand(MachineOperand::CreateImm(0)); +} + +// Determine whether a call to the callee referenced by +// MI->getOperand(CalleeOpNo) reads memory, writes memory, and/or has side +// effects. +static void queryCallee(const MachineInstr &MI, bool &Read, bool &Write, + bool &Effects, bool &StackPointer) { + // All calls can use the stack pointer. + StackPointer = true; + + const MachineOperand &MO = MI.getOperand(MI.getNumExplicitDefs()); + if (MO.isGlobal()) { + const Constant *GV = MO.getGlobal(); + if (const auto *GA = dyn_cast(GV)) + if (!GA->isInterposable()) + GV = GA->getAliasee(); + + if (const auto *F = dyn_cast(GV)) { + if (!F->doesNotThrow()) + Effects = true; + if (F->doesNotAccessMemory()) + return; + if (F->onlyReadsMemory()) { + Read = true; + return; + } + } + } + + // Assume the worst. + Write = true; + Read = true; + Effects = true; +} + +// Determine whether MI reads memory, writes memory, has side effects, +// and/or uses the stack pointer value. +static void query(const MachineInstr &MI, bool &Read, bool &Write, + bool &Effects, bool &StackPointer) { + assert(!MI.isTerminator()); + + if (MI.isDebugInstr() || MI.isPosition()) + return; + + // Check for loads. + if (MI.mayLoad() && !MI.isDereferenceableInvariantLoad()) + Read = true; + + // Check for stores. + if (MI.mayStore()) { + Write = true; + } else if (MI.hasOrderedMemoryRef()) { + if (!MI.isCall()) { + Write = true; + Effects = true; + } + } + + // Check for side effects. + if (MI.hasUnmodeledSideEffects()) + Effects = true; + + // Analyze calls. + if (MI.isCall()) { + queryCallee(MI, Read, Write, Effects, StackPointer); + } +} + +// Identify the definition for this register at this point. This is a +// generalization of MachineRegisterInfo::getUniqueVRegDef that uses +// LiveIntervals to handle complex cases. +static MachineInstr *getVRegDef(unsigned Reg, const MachineInstr *Insert, + const MachineRegisterInfo &MRI, + const LiveIntervals &LIS) { + // Most registers are in SSA form here so we try a quick MRI query first. + if (MachineInstr *Def = MRI.getUniqueVRegDef(Reg)) + return Def; + + // MRI doesn't know what the Def is. Try asking LIS. + if (const VNInfo *ValNo = LIS.getInterval(Reg).getVNInfoBefore( + LIS.getInstructionIndex(*Insert))) + return LIS.getInstructionFromIndex(ValNo->def); + + return nullptr; +} + +// Test whether Reg, as defined at Def, has exactly one use. This is a +// generalization of MachineRegisterInfo::hasOneUse that uses LiveIntervals +// to handle complex cases. +static bool hasOneUse(unsigned Reg, MachineInstr *Def, MachineRegisterInfo &MRI, + MachineDominatorTree &MDT, LiveIntervals &LIS) { + // Most registers are in SSA form here so we try a quick MRI query first. + if (MRI.hasOneUse(Reg)) + return true; + + bool HasOne = false; + const LiveInterval &LI = LIS.getInterval(Reg); + const VNInfo *DefVNI = + LI.getVNInfoAt(LIS.getInstructionIndex(*Def).getRegSlot()); + assert(DefVNI); + for (auto &I : MRI.use_nodbg_operands(Reg)) { + const auto &Result = LI.Query(LIS.getInstructionIndex(*I.getParent())); + if (Result.valueIn() == DefVNI) { + if (!Result.isKill()) + return false; + if (HasOne) + return false; + HasOne = true; + } + } + return HasOne; +} + +// Test whether Def is safe and profitable to rematerialize. +static bool shouldRematerialize(const MachineInstr &Def, + const MachineRegisterInfo &MRI, + LiveIntervals &LIS, const EVMInstrInfo *TII) { + // FIXME: remateralization of the CALLDATALOAD and ADD instructions + // is just an ad-hoc solution to more aggressively form single use expressions + // to eventually decrease runtime stack height, but this can significantly + // increase a code size. + unsigned Opcode = Def.getOpcode(); + if (Opcode == EVM::CALLDATALOAD && + // Don't rematerialize CALLDATALOAD which redefines the register it uses, + // as the use will no longer contribute to the register pressure. + Def.getOperand(0).getReg() != Def.getOperand(1).getReg()) { + MachineInstr *DefI = getVRegDef(Def.getOperand(1).getReg(), &Def, MRI, LIS); + if (!DefI) + return false; + + return DefI->getOpcode() == EVM::CONST_I256 ? true : false; + } + + if (Opcode == EVM::ADD) { + MachineInstr *DefI1 = + getVRegDef(Def.getOperand(1).getReg(), &Def, MRI, LIS); + MachineInstr *DefI2 = + getVRegDef(Def.getOperand(2).getReg(), &Def, MRI, LIS); + if (!DefI1 || !DefI2) + return false; + + MachineInstr *DefBase = nullptr; + if (DefI1->getOpcode() == EVM::CONST_I256) + DefBase = DefI2; + else if (DefI2->getOpcode() == EVM::CONST_I256) + DefBase = DefI1; + else + return false; + + const Register BaseReg = DefBase->getOperand(0).getReg(); + if (DefBase != MRI.getUniqueVRegDef(BaseReg)) + return false; + + const Register DefReg = Def.getOperand(0).getReg(); + if (&Def != MRI.getUniqueVRegDef(DefReg)) + return false; + + LiveInterval &DefLI = LIS.getInterval(DefReg); + LiveInterval &BaseLI = LIS.getInterval(BaseReg); + + LLVM_DEBUG(dbgs() << "Can rematerialize ADD(base, const): " << Def + << "DefLI: " << DefLI << ", BaseLI: " << BaseLI << '\n'); + + return BaseLI.covers(DefLI); + } + return Def.isAsCheapAsAMove() && TII->isTriviallyReMaterializable(Def); +} + +// Test whether it's safe to move Def to just before Insert. +// TODO: Compute memory dependencies in a way that doesn't require always +// walking the block. +// TODO: Compute memory dependencies in a way that uses AliasAnalysis to be +// more precise. +static bool isSafeToMove(const MachineOperand *Def, const MachineOperand *Use, + const MachineInstr *Insert, + const EVMMachineFunctionInfo &MFI, + const MachineRegisterInfo &MRI) { + const MachineInstr *DefI = Def->getParent(); + const MachineInstr *UseI = Use->getParent(); + assert(DefI->getParent() == Insert->getParent()); + assert(UseI->getParent() == Insert->getParent()); + + // The first def of a multivalue instruction can be stackified by moving, + // since the later defs can always be placed into locals if necessary. Later + // defs can only be stackified if all previous defs are already stackified + // since ExplicitLocals will not know how to place a def in a local if a + // subsequent def is stackified. But only one def can be stackified by moving + // the instruction, so it must be the first one. + // + // TODO: This could be loosened to be the first *live* def, but care would + // have to be taken to ensure the drops of the initial dead defs can be + // placed. This would require checking that no previous defs are used in the + // same instruction as subsequent defs. + if (Def != DefI->defs().begin()) + return false; + + // If any subsequent def is used prior to the current value by the same + // instruction in which the current value is used, we cannot + // stackify. Stackifying in this case would require that def moving below the + // current def in the stack, which cannot be achieved, even with locals. + for (const auto &SubsequentDef : drop_begin(DefI->defs())) { + for (const auto &PriorUse : UseI->uses()) { + if (&PriorUse == Use) + break; + if (PriorUse.isReg() && SubsequentDef.getReg() == PriorUse.getReg()) + return false; + } + } + + // If moving is a semantic nop, it is always allowed + const MachineBasicBlock *MBB = DefI->getParent(); + auto NextI = std::next(MachineBasicBlock::const_iterator(DefI)); + for (auto E = MBB->end(); NextI != E && NextI->isDebugInstr(); ++NextI) + ; + if (NextI == Insert) + return true; + + // Don't move ARGUMENT instructions, as stackification pass relies on this. + if (DefI->getOpcode() == EVM::ARGUMENT || + DefI->getOpcode() == EVM::PUSHDEPLOYADDRESS) + return false; + + // Check for register dependencies. + SmallVector MutableRegisters; + for (const MachineOperand &MO : DefI->operands()) { + if (!MO.isReg() || MO.isUndef()) + continue; + Register Reg = MO.getReg(); + + // If the register is dead here and at Insert, ignore it. + if (MO.isDead() && Insert->definesRegister(Reg, /*TRI*/ nullptr) && + !Insert->readsRegister(Reg, /*TRI*/ nullptr)) + continue; + + if (Register::isPhysicalRegister(Reg)) { + // Ignore ARGUMENTS; it's just used to keep the ARGUMENT_* instructions + // from moving down, and we've already checked for that. + if (Reg == EVM::ARGUMENTS) + continue; + // If the physical register is never modified, ignore it. + if (!MRI.isPhysRegModified(Reg)) + continue; + // Otherwise, it's a physical register with unknown liveness. + return false; + } + + // If one of the operands isn't in SSA form, it has different values at + // different times, and we need to make sure we don't move our use across + // a different def. + if (!MO.isDef() && !MRI.hasOneDef(Reg)) + MutableRegisters.push_back(Reg); + } + + bool Read = false, Write = false, Effects = false, StackPointer = false; + query(*DefI, Read, Write, Effects, StackPointer); + + // If the instruction does not access memory and has no side effects, it has + // no additional dependencies. + bool HasMutableRegisters = !MutableRegisters.empty(); + if (!Read && !Write && !Effects && !StackPointer && !HasMutableRegisters) + return true; + + // Scan through the intervening instructions between DefI and Insert. + MachineBasicBlock::const_iterator D(DefI), I(Insert); + for (--I; I != D; --I) { + bool InterveningRead = false; + bool InterveningWrite = false; + bool InterveningEffects = false; + bool InterveningStackPointer = false; + query(*I, InterveningRead, InterveningWrite, InterveningEffects, + InterveningStackPointer); + if (Effects && InterveningEffects) + return false; + if (Read && InterveningWrite) + return false; + if (Write && (InterveningRead || InterveningWrite)) + return false; + if (StackPointer && InterveningStackPointer) + return false; + + for (unsigned Reg : MutableRegisters) + for (const MachineOperand &MO : I->operands()) + if (MO.isReg() && MO.isDef() && MO.getReg() == Reg) + return false; + + // Check that subsequent defs of a multi-value instruction are not + // defined/used by instructions between 'DefI' and 'Insert'. + // For example, in the code: + // + // bb.0: + // liveins: %0, %4, %6 + // + // %1:gpr, %2:gpr = FCALL @multival, %0:gpr, + // %3:gpr = SIGNEXTEND %4:gpr, %2:gpr + // %5:gpr = AND %1:gpr, %6:gpr + // + // bb.1: + // liveins: %5, %3 + // + // FCALL should not be moved after SIGNEXTEND, as this breaks + // data flow on the register '%2'. + + for (const auto &SubDef : drop_begin(DefI->defs())) + for (const MachineOperand &MO : I->operands()) + if (MO.isReg() && MO.getReg() == SubDef.getReg()) + return false; + } + + return true; +} + +// Shrink LI to its uses, cleaning up LI. +static void shrinkToUses(LiveInterval &LI, LiveIntervals &LIS) { + if (LIS.shrinkToUses(&LI)) { + SmallVector SplitLIs; + LIS.splitSeparateComponents(LI, SplitLIs); + } +} + +/// A single-use def in the same block with no intervening memory or register +/// dependencies; move the def down and nest it with the current instruction. +static MachineInstr *moveForSingleUse(unsigned Reg, MachineOperand &Op, + MachineInstr *Def, MachineBasicBlock &MBB, + MachineInstr *Insert, LiveIntervals &LIS, + EVMMachineFunctionInfo &MFI, + MachineRegisterInfo &MRI) { + LLVM_DEBUG(dbgs() << "Move for single use: "; Def->dump()); + + MBB.splice(Insert, &MBB, Def); + LIS.handleMove(*Def); + + if (MRI.hasOneDef(Reg) && MRI.hasOneUse(Reg)) { + // No one else is using this register for anything so we can just stackify + // it in place. + MFI.stackifyVReg(MRI, Reg); + } else { + // The register may have unrelated uses or defs; create a new register for + // just our one def and use so that we can stackify it. + Register NewReg = MRI.createVirtualRegister(MRI.getRegClass(Reg)); + Def->getOperand(0).setReg(NewReg); + Op.setReg(NewReg); + + // Tell LiveIntervals about the new register. + LIS.createAndComputeVirtRegInterval(NewReg); + + // Tell LiveIntervals about the changes to the old register. + LiveInterval &LI = LIS.getInterval(Reg); + LI.removeSegment(LIS.getInstructionIndex(*Def).getRegSlot(), + LIS.getInstructionIndex(*Op.getParent()).getRegSlot(), + /*RemoveDeadValNo=*/true); + + MFI.stackifyVReg(MRI, NewReg); + LLVM_DEBUG(dbgs() << " - Replaced register: "; Def->dump()); + } + + imposeStackOrdering(Def); + return Def; +} + +/// A trivially cloneable instruction; clone it and nest the new copy with the +/// current instruction. +static MachineInstr *rematerializeCheapDef( + unsigned Reg, MachineOperand &Op, MachineInstr &Def, MachineBasicBlock &MBB, + MachineBasicBlock::instr_iterator Insert, LiveIntervals &LIS, + EVMMachineFunctionInfo &MFI, MachineRegisterInfo &MRI, + const EVMInstrInfo *TII, const EVMRegisterInfo *TRI) { + LLVM_DEBUG(dbgs() << "Rematerializing cheap def: "; Def.dump()); + LLVM_DEBUG(dbgs() << " - for use in "; Op.getParent()->dump()); + + Register NewReg = MRI.createVirtualRegister(MRI.getRegClass(Reg)); + TII->reMaterialize(MBB, Insert, NewReg, 0, Def, *TRI); + Op.setReg(NewReg); + MachineInstr *Clone = &*std::prev(Insert); + LIS.InsertMachineInstrInMaps(*Clone); + LIS.createAndComputeVirtRegInterval(NewReg); + MFI.stackifyVReg(MRI, NewReg); + imposeStackOrdering(Clone); + + LLVM_DEBUG(dbgs() << " - Cloned to "; Clone->dump()); + + // Shrink the interval. + bool IsDead = MRI.use_empty(Reg); + if (!IsDead) { + LiveInterval &LI = LIS.getInterval(Reg); + shrinkToUses(LI, LIS); + IsDead = !LI.liveAt(LIS.getInstructionIndex(Def).getDeadSlot()); + } + + // If that was the last use of the original, delete the original. + // Move or clone corresponding DBG_VALUEs to the 'Insert' location. + if (IsDead) { + LLVM_DEBUG(dbgs() << " - Deleting original\n"); + SlotIndex Idx = LIS.getInstructionIndex(Def).getRegSlot(); + LIS.removePhysRegDefAt(MCRegister::from(EVM::ARGUMENTS), Idx); + LIS.removeInterval(Reg); + LIS.RemoveMachineInstrFromMaps(Def); + Def.eraseFromParent(); + } + + return Clone; +} + +namespace { +/// A stack for walking the tree of instructions being built, visiting the +/// MachineOperands in DFS order. +class TreeWalkerState { + using mop_iterator = MachineInstr::mop_iterator; + using RangeTy = iterator_range; + SmallVector Worklist; + +public: + explicit TreeWalkerState(MachineInstr *Insert) { + const iterator_range &Range = Insert->explicit_uses(); + if (!Range.empty()) + Worklist.push_back(Range); + } + + bool done() const { return Worklist.empty(); } + + MachineOperand &pop() { + RangeTy &Range = Worklist.back(); + MachineOperand &Op = *Range.begin(); + Range = drop_begin(Range); + if (Range.empty()) + Worklist.pop_back(); + assert((Worklist.empty() || !Worklist.back().empty()) && + "Empty ranges shouldn't remain in the worklist"); + return Op; + } + + /// Push Instr's operands onto the stack to be visited. + void pushOperands(MachineInstr *Instr) { + const iterator_range &Range(Instr->explicit_uses()); + if (!Range.empty()) + Worklist.push_back(Range); + } + + /// Some of Instr's operands are on the top of the stack; remove them and + /// re-insert them starting from the beginning (because we've commuted them). + void resetTopOperands(MachineInstr *Instr) { + assert(hasRemainingOperands(Instr) && + "Reseting operands should only be done when the instruction has " + "an operand still on the stack"); + Worklist.back() = Instr->explicit_uses(); + } + + /// Test whether Instr has operands remaining to be visited at the top of + /// the stack. + bool hasRemainingOperands(const MachineInstr *Instr) const { + if (Worklist.empty()) + return false; + const RangeTy &Range = Worklist.back(); + return !Range.empty() && Range.begin()->getParent() == Instr; + } + + /// Test whether the given register is present on the stack, indicating an + /// operand in the tree that we haven't visited yet. Moving a definition of + /// Reg to a point in the tree after that would change its value. + /// + /// This is needed as a consequence of using implicit local.gets for + /// uses and implicit local.sets for defs. + bool isOnStack(unsigned Reg) const { + for (const RangeTy &Range : Worklist) + for (const MachineOperand &MO : Range) + if (MO.isReg() && MO.getReg() == Reg) + return true; + return false; + } +}; + +/// State to keep track of whether commuting is in flight or whether it's been +/// tried for the current instruction and didn't work. +class CommutingState { + /// There are effectively three states: the initial state where we haven't + /// started commuting anything and we don't know anything yet, the tentative + /// state where we've commuted the operands of the current instruction and are + /// revisiting it, and the declined state where we've reverted the operands + /// back to their original order and will no longer commute it further. + bool TentativelyCommuting = false; + bool Declined = false; + + /// During the tentative state, these hold the operand indices of the commuted + /// operands. + unsigned Operand0 = 0, Operand1 = 0; + +public: + /// Stackification for an operand was not successful due to ordering + /// constraints. If possible, and if we haven't already tried it and declined + /// it, commute Insert's operands and prepare to revisit it. + void maybeCommute(MachineInstr *Insert, TreeWalkerState &TreeWalker, + const EVMInstrInfo *TII) { + if (TentativelyCommuting) { + assert(!Declined && + "Don't decline commuting until you've finished trying it"); + // Commuting didn't help. Revert it. + TII->commuteInstruction(*Insert, /*NewMI=*/false, Operand0, Operand1); + TentativelyCommuting = false; + Declined = true; + } else if (!Declined && TreeWalker.hasRemainingOperands(Insert)) { + Operand0 = TargetInstrInfo::CommuteAnyOperandIndex; + Operand1 = TargetInstrInfo::CommuteAnyOperandIndex; + if (TII->findCommutedOpIndices(*Insert, Operand0, Operand1)) { + // Tentatively commute the operands and try again. + TII->commuteInstruction(*Insert, /*NewMI=*/false, Operand0, Operand1); + TreeWalker.resetTopOperands(Insert); + TentativelyCommuting = true; + Declined = false; + } + } + } + + /// Stackification for some operand was successful. Reset to the default + /// state. + void reset() { + TentativelyCommuting = false; + Declined = false; + } +}; +} // end anonymous namespace + +bool EVMSingleUseExpression::runOnMachineFunction(MachineFunction &MF) { + LLVM_DEBUG(dbgs() << "********** Single-use expressions **********\n" + "********** Function: " + << MF.getName() << '\n'); + + bool Changed = false; + MachineRegisterInfo &MRI = MF.getRegInfo(); + EVMMachineFunctionInfo &MFI = *MF.getInfo(); + const auto *TII = MF.getSubtarget().getInstrInfo(); + const auto *TRI = MF.getSubtarget().getRegisterInfo(); + auto &MDT = getAnalysis().getDomTree(); + auto &LIS = getAnalysis().getLIS(); + + // Walk the instructions from the bottom up. Currently we don't look past + // block boundaries, and the blocks aren't ordered so the block visitation + // order isn't significant, but we may want to change this in the future. + for (MachineBasicBlock &MBB : MF) { + // Don't use a range-based for loop, because we modify the list as we're + // iterating over it and the end iterator may change. + for (auto MII = MBB.rbegin(); MII != MBB.rend(); ++MII) { + MachineInstr *Insert = &*MII; + // Don't nest anything inside an inline asm, because we don't have + // constraints for $push inputs. + if (Insert->isInlineAsm()) + continue; + + // Ignore debugging intrinsics. + if (Insert->isDebugValue()) + continue; + + // Iterate through the inputs in reverse order, since we'll be pulling + // operands off the stack in LIFO order. + CommutingState Commuting; + TreeWalkerState TreeWalker(Insert); + while (!TreeWalker.done()) { + MachineOperand &Use = TreeWalker.pop(); + + // We're only interested in explicit virtual register operands. + if (!Use.isReg()) + continue; + + Register Reg = Use.getReg(); + assert(Use.isUse() && "explicit_uses() should only iterate over uses"); + assert(!Use.isImplicit() && + "explicit_uses() should only iterate over explicit operands"); + if (Register::isPhysicalRegister(Reg)) + continue; + + // Identify the definition for this register at this point. + MachineInstr *DefI = getVRegDef(Reg, Insert, MRI, LIS); + if (!DefI) + continue; + + // Don't nest an INLINE_ASM def into anything, because we don't have + // constraints for $pop outputs. + if (DefI->isInlineAsm()) + continue; + + MachineOperand *Def = DefI->findRegisterDefOperand(Reg, TRI); + assert(Def != nullptr); + + // Decide which strategy to take. Prefer to move a single-use value + // over cloning it, and prefer cloning over introducing a tee. + // For moving, we require the def to be in the same block as the use; + // this makes things simpler (LiveIntervals' handleMove function only + // supports intra-block moves) and it's MachineSink's job to catch all + // the sinking opportunities anyway. + bool SameBlock = DefI->getParent() == &MBB; + bool CanMove = SameBlock && isSafeToMove(Def, &Use, Insert, MFI, MRI) && + !TreeWalker.isOnStack(Reg); + if (CanMove && hasOneUse(Reg, DefI, MRI, MDT, LIS)) { + Insert = moveForSingleUse(Reg, Use, DefI, MBB, Insert, LIS, MFI, MRI); + } else if (shouldRematerialize(*DefI, MRI, LIS, TII)) { + if (DefI->getOpcode() == EVM::CALLDATALOAD) { + MachineInstr *OffsetI = + getVRegDef(DefI->getOperand(1).getReg(), DefI, MRI, LIS); + assert(OffsetI); + Insert = rematerializeCheapDef(Reg, Use, *DefI, MBB, + Insert->getIterator(), LIS, MFI, MRI, + TII, TRI); + + MachineOperand &OffsetMO = Insert->getOperand(1); + Insert = rematerializeCheapDef(OffsetMO.getReg(), OffsetMO, + *OffsetI, MBB, Insert->getIterator(), + LIS, MFI, MRI, TII, TRI); + } else if (DefI->getOpcode() == EVM::ADD) { + MachineInstr *DefI1 = + getVRegDef(DefI->getOperand(1).getReg(), DefI, MRI, LIS); + MachineInstr *DefI2 = + getVRegDef(DefI->getOperand(2).getReg(), DefI, MRI, LIS); + assert(DefI1 && DefI2); + + MachineInstr *OffsetI = nullptr; + Register BaseReg; + if (DefI1->getOpcode() == EVM::CONST_I256) { + OffsetI = DefI1; + BaseReg = DefI->getOperand(2).getReg(); + } else { + OffsetI = DefI2; + BaseReg = DefI->getOperand(1).getReg(); + } + Insert = rematerializeCheapDef(Reg, Use, *DefI, MBB, + Insert->getIterator(), LIS, MFI, MRI, + TII, TRI); + + // Recalculate live interval of the base register, since we created + // a new instruction that uses it. + LIS.removeInterval(BaseReg); + LIS.createAndComputeVirtRegInterval(BaseReg); + + MachineOperand &OffsetMO = (DefI1->getOpcode() == EVM::CONST_I256) + ? Insert->getOperand(1) + : Insert->getOperand(2); + Insert = rematerializeCheapDef(OffsetMO.getReg(), OffsetMO, + *OffsetI, MBB, Insert->getIterator(), + LIS, MFI, MRI, TII, TRI); + } else { + Insert = rematerializeCheapDef(Reg, Use, *DefI, MBB, + Insert->getIterator(), LIS, MFI, MRI, + TII, TRI); + } + } else { + // We failed to stackify the operand. If the problem was ordering + // constraints, Commuting may be able to help. + if (!CanMove && SameBlock) + Commuting.maybeCommute(Insert, TreeWalker, TII); + // Proceed to the next operand. + continue; + } + + // Stackifying a multivalue def may unlock in-place stackification of + // subsequent defs. TODO: Handle the case where the consecutive uses are + // not all in the same instruction. + auto *SubsequentDef = Insert->defs().begin(); + auto *SubsequentUse = &Use; + while (SubsequentDef != Insert->defs().end() && + SubsequentUse != Use.getParent()->uses().end()) { + if (!SubsequentDef->isReg() || !SubsequentUse->isReg()) + break; + Register DefReg = SubsequentDef->getReg(); + Register UseReg = SubsequentUse->getReg(); + // TODO: This single-use restriction could be relaxed by using tees + if (DefReg != UseReg || !MRI.hasOneUse(DefReg)) + break; + MFI.stackifyVReg(MRI, DefReg); + ++SubsequentDef; + ++SubsequentUse; + } + + // If the instruction we just stackified is an IMPLICIT_DEF, convert it + // to a constant 0 so that the def is explicit, and the push/pop + // correspondence is maintained. + if (Insert->getOpcode() == TargetOpcode::IMPLICIT_DEF) + convertImplicitDefToConstZero(Insert, MRI, TII, MF, LIS); + + // We stackified an operand. Add the defining instruction's operands to + // the worklist stack now to continue to build an ever deeper tree. + Commuting.reset(); + TreeWalker.pushOperands(Insert); + } + + // If we stackified any operands, skip over the tree to start looking for + // the next instruction we can build a tree on. + if (Insert != &*MII) { + imposeStackOrdering(&*MII); + MII = MachineBasicBlock::iterator(Insert).getReverse(); + Changed = true; + } + } + } + + // If we used VALUE_STACK anywhere, add it to the live-in sets everywhere so + // that it never looks like a use-before-def. + if (Changed) { + MF.getRegInfo().addLiveIn(EVM::VALUE_STACK); + for (MachineBasicBlock &MBB : MF) + MBB.addLiveIn(EVM::VALUE_STACK); + } + +#ifndef NDEBUG + // Verify that pushes and pops are performed in LIFO order. + SmallVector Stack; + for (MachineBasicBlock &MBB : MF) { + for (MachineInstr &MI : MBB) { + if (MI.isDebugInstr()) + continue; + for (MachineOperand &MO : MI.explicit_uses()) { + if (!MO.isReg()) + continue; + Register Reg = MO.getReg(); + if (MFI.isVRegStackified(Reg)) { + LLVM_DEBUG(dbgs() << "Use stackified reg: " << MO << '\n'); + assert(Stack.back() == Reg && + "Register stack pop should be paired with a push"); + Stack.pop_back(); + } + } + for (MachineOperand &MO : reverse(MI.defs())) { + if (!MO.isReg()) + continue; + Register Reg = MO.getReg(); + if (MFI.isVRegStackified(Reg)) { + LLVM_DEBUG(dbgs() << "Stackify reg: " << MO << '\n'); + Stack.push_back(MO.getReg()); + } + } + } + // TODO: Generalize this code to support keeping values on the stack across + // basic block boundaries. + assert(Stack.empty() && + "Register stack pushes and pops should be balanced"); + } +#endif + + return Changed; +} diff --git a/llvm/lib/Target/EVM/EVMSplitCriticalEdges.cpp b/llvm/lib/Target/EVM/EVMSplitCriticalEdges.cpp new file mode 100644 index 000000000000..ee39d4691c17 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMSplitCriticalEdges.cpp @@ -0,0 +1,90 @@ +//===----- EVMSplitCriticalEdges.cpp - Split Critical Edges ----*- 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 performs splitting of CFG critical edges. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/CodeGen/MachineLoopInfo.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/InitializePasses.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +#define DEBUG_TYPE "evm-split-critical-edges" + +namespace { +class EVMSplitCriticalEdges final : public MachineFunctionPass { +public: + static char ID; + + EVMSplitCriticalEdges() : MachineFunctionPass(ID) {} + + StringRef getPassName() const override { return "EVM split critical edges"; } + + bool runOnMachineFunction(MachineFunction &MF) override; + +private: + MachineFunction *MF = nullptr; + + bool splitCriticalEdges(); +}; +} // end anonymous namespace + +char EVMSplitCriticalEdges::ID = 0; + +INITIALIZE_PASS(EVMSplitCriticalEdges, DEBUG_TYPE, "Split critical edges", + false, false) + +FunctionPass *llvm::createEVMSplitCriticalEdges() { + return new EVMSplitCriticalEdges(); +} + +bool EVMSplitCriticalEdges::splitCriticalEdges() { + SetVector> ToSplit; + for (MachineBasicBlock &MBB : *MF) { + if (MBB.pred_size() > 1) { + for (MachineBasicBlock *Pred : MBB.predecessors()) { + if (Pred->succ_size() > 1) + ToSplit.insert(std::make_pair(Pred, &MBB)); + } + } + } + + bool Changed = false; + for (const auto &Pair : ToSplit) { + auto *NewSucc = Pair.first->SplitCriticalEdge(Pair.second, *this); + if (NewSucc != nullptr) { + Pair.first->updateTerminator(NewSucc); + NewSucc->updateTerminator(Pair.second); + LLVM_DEBUG(dbgs() << " *** Splitting critical edge: " + << printMBBReference(*Pair.first) << " -- " + << printMBBReference(*NewSucc) << " -- " + << printMBBReference(*Pair.second) << '\n'); + Changed = true; + } else { + llvm_unreachable("Cannot break critical edge"); + } + } + return Changed; +} + +bool EVMSplitCriticalEdges::runOnMachineFunction(MachineFunction &Mf) { + MF = &Mf; + LLVM_DEBUG({ + dbgs() << "********** Splitting critical edges **********\n" + << "********** Function: " << Mf.getName() << '\n'; + }); + + bool Changed = splitCriticalEdges(); + return Changed; +} diff --git a/llvm/lib/Target/EVM/EVMStackModel.cpp b/llvm/lib/Target/EVM/EVMStackModel.cpp new file mode 100644 index 000000000000..2e72e8b86811 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMStackModel.cpp @@ -0,0 +1,150 @@ +//===----- EVMEVMStackModel.cpp - EVM Stack Model ---------------*- 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 "EVMStackModel.h" +#include "EVMMachineFunctionInfo.h" +#include "EVMSubtarget.h" +#include "llvm/CodeGen/MachineFunction.h" + +using namespace llvm; + +bool llvm::isLinkerPseudoMI(const MachineInstr &MI) { + return MI.getOpcode() == EVM::DATASIZE || MI.getOpcode() == EVM::DATAOFFSET || + MI.getOpcode() == EVM::LOADIMMUTABLE || + MI.getOpcode() == EVM::LINKERSYMBOL; +} +bool llvm::isPushOrDupLikeMI(const MachineInstr &MI) { + return isLinkerPseudoMI(MI) || MI.getOpcode() == EVM::CONST_I256 || + MI.getOpcode() == EVM::COPY_I256; +} +bool llvm::isNoReturnCallMI(const MachineInstr &MI) { + assert(MI.getOpcode() == EVM::FCALL && "Unexpected call instruction"); + const MachineOperand *FuncOp = MI.explicit_uses().begin(); + const auto *F = cast(FuncOp->getGlobal()); + return F->hasFnAttribute(Attribute::NoReturn); +} + +static std::string getInstName(const MachineInstr *MI) { + const MachineFunction *MF = MI->getParent()->getParent(); + const TargetInstrInfo *TII = MF->getSubtarget().getInstrInfo(); + return TII->getName(MI->getOpcode()).str(); +} + +std::string SymbolSlot::toString() const { + return getInstName(MI) + ":" + std::string(Symbol->getName()); +} +std::string CallerReturnSlot::toString() const { + const MachineOperand *FuncOp = Call->explicit_uses().begin(); + const auto *F = cast(FuncOp->getGlobal()); + return "RET[" + std::string(F->getName()) + "]"; +} + +EVMStackModel::EVMStackModel(MachineFunction &MF, const LiveIntervals &LIS, + unsigned StackDepthLimit) + : MF(MF), LIS(LIS), StackDepthLimit(StackDepthLimit) { + for (MachineBasicBlock &MBB : MF) + for (const MachineInstr &MI : instructionsToProcess(&MBB)) + processMI(MI); +} + +Stack EVMStackModel::getFunctionParameters() const { + auto *MFI = MF.getInfo(); + Stack Parameters(MFI->getNumParams(), EVMStackModel::getUnusedSlot()); + for (const MachineInstr &MI : MF.front()) { + if (MI.getOpcode() == EVM::ARGUMENT) { + int64_t ArgIdx = MI.getOperand(1).getImm(); + Parameters[ArgIdx] = getRegisterSlot(MI.getOperand(0).getReg()); + } + } + return Parameters; +} + +StackSlot *EVMStackModel::getStackSlot(const MachineOperand &MO) const { + // If the virtual register defines a constant and this is the only + // definition, emit the literal slot as MI's input. + const LiveInterval *LI = &LIS.getInterval(MO.getReg()); + if (LI->containsOneValue()) { + SlotIndex Idx = LIS.getInstructionIndex(*MO.getParent()); + const VNInfo *VNI = LI->Query(Idx).valueIn(); + assert(VNI && "Use of non-existing value"); + assert(!VNI->isPHIDef()); + const MachineInstr *DefMI = LIS.getInstructionFromIndex(VNI->def); + assert(DefMI && "Dead valno in interval"); + if (DefMI->getOpcode() == EVM::CONST_I256) + return getLiteralSlot(DefMI->getOperand(1).getCImm()->getValue()); + } + return getRegisterSlot(MO.getReg()); +} + +Stack EVMStackModel::getSlotsForInstructionUses(const MachineInstr &MI) const { + Stack In; + for (const auto &MO : reverse(MI.explicit_uses())) { + if (MO.isReg() && MO.getReg() != EVM::SP) + In.push_back(getStackSlot(MO)); + else if (MO.isMCSymbol()) + In.push_back(getSymbolSlot(MO.getMCSymbol(), &MI)); + } + return In; +} + +void EVMStackModel::processMI(const MachineInstr &MI) { + unsigned Opc = MI.getOpcode(); + assert(Opc != EVM::STACK_LOAD && Opc != EVM::STACK_STORE && + "Unexpected stack memory instruction"); + assert(all_of(MI.implicit_operands(), + [](const MachineOperand &MO) { + return MO.getReg() == EVM::VALUE_STACK || + MO.getReg() == EVM::ARGUMENTS || + MO.getReg() == EVM::SP; + }) && + "Unexpected implicit def or use"); + + assert(all_of(MI.explicit_operands(), + [](const MachineOperand &MO) { + return !MO.isReg() || + Register::isVirtualRegister(MO.getReg()); + }) && + "Unexpected explicit def or use"); + + if (Opc == EVM::FCALL) { + Stack Input; + if (!isNoReturnCallMI(MI)) + Input.push_back(getCallerReturnSlot(&MI)); + + append_range(Input, getSlotsForInstructionUses(MI)); + MIInputMap[&MI] = Input; + return; + } + if (Opc == EVM::CONST_I256) { + MIInputMap[&MI] = + Stack(1, getLiteralSlot(MI.getOperand(1).getCImm()->getValue())); + return; + } + if (isLinkerPseudoMI(MI)) { + MCSymbol *Sym = MI.getOperand(1).getMCSymbol(); + MIInputMap[&MI] = Stack(1, getSymbolSlot(Sym, &MI)); + return; + } + + MIInputMap[&MI] = getSlotsForInstructionUses(MI); +} + +Stack EVMStackModel::getReturnArguments(const MachineInstr &MI) const { + assert(MI.getOpcode() == EVM::RET); + Stack Input = getSlotsForInstructionUses(MI); + // We need to reverse input operands to restore original ordering, + // in the instruction. + // Calling convention: return values are passed in stack such that the + // last one specified in the RET instruction is passed on the stack TOP. + std::reverse(Input.begin(), Input.end()); + Input.push_back(getCalleeReturnSlot(&MF)); + return Input; +} diff --git a/llvm/lib/Target/EVM/EVMStackModel.h b/llvm/lib/Target/EVM/EVMStackModel.h new file mode 100644 index 000000000000..42bf5877444e --- /dev/null +++ b/llvm/lib/Target/EVM/EVMStackModel.h @@ -0,0 +1,365 @@ +//===------------- EVMStackModel.h - Stack Model ----------------*- 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 representation used by the backwards propagation +// stackification algorithm. It consists of 'StackSlot' and 'Stack' entities. +// New stack representation is derived from Machine IR as following: +// MachineOperand -> StackSlot +// MI's defs/uses -> Stack (array of StackSlot) +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVMSTACKMODEL_H +#define LLVM_LIB_TARGET_EVM_EVMSTACKMODEL_H + +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/CodeGen/LiveIntervals.h" +#include "llvm/CodeGen/TargetInstrInfo.h" + +namespace llvm { + +class MachineFunction; +class MachineBasicBlock; +class MCSymbol; + +class StackSlot { +public: + enum SlotKind : uint8_t { + SK_Literal, + SK_Register, + SK_Symbol, + SK_CallerReturn, + SK_CalleeReturn, + SK_Unused, + SK_Unknown + }; + +private: + const SlotKind KindID; + +protected: + explicit StackSlot(SlotKind KindID) : KindID(KindID) {} + +public: + virtual ~StackSlot() = default; + + unsigned getSlotKind() const { return KindID; } + + // 'isRematerializable()' returns true, if a slot always has a known value + // at compile time and therefore can safely be removed from the stack at any + // time and then regenerated later. + virtual bool isRematerializable() const = 0; + virtual std::string toString() const = 0; +}; + +/// A slot containing a literal value. +class LiteralSlot final : public StackSlot { + APInt Value; + +public: + explicit LiteralSlot(const APInt &V) : StackSlot(SK_Literal), Value(V) {} + const APInt &getValue() const { return Value; } + + bool isRematerializable() const override { return true; } + std::string toString() const override { + SmallString<64> S; + Value.toStringSigned(S); + return std::string(S.str()); + } + static bool classof(const StackSlot *S) { + return S->getSlotKind() == SK_Literal; + } +}; + +/// A slot containing a register def. +class RegisterSlot final : public StackSlot { + Register Reg; + bool IsSpill = false; + +public: + explicit RegisterSlot(const Register &R) : StackSlot(SK_Register), Reg(R) {} + const Register &getReg() const { return Reg; } + + void setIsSpill(bool Spill = true) { IsSpill = Spill; } + bool isSpill() const { return IsSpill; } + + bool isRematerializable() const override { return false; } + std::string toString() const override { + SmallString<64> S; + raw_svector_ostream OS(S); + if (IsSpill) + OS << "Spill["; + OS << printReg(Reg, nullptr, 0, nullptr); + if (IsSpill) + OS << ']'; + return std::string(S.str()); + } + static bool classof(const StackSlot *S) { + return S->getSlotKind() == SK_Register; + } +}; + +/// A slot containing a MCSymbol. +class SymbolSlot final : public StackSlot { + MCSymbol *Symbol; + const MachineInstr *MI = nullptr; + +public: + SymbolSlot(MCSymbol *S, const MachineInstr *MI) + : StackSlot(SK_Symbol), Symbol(S), MI(MI) {} + const MachineInstr *getMachineInstr() const { return MI; } + MCSymbol *getSymbol() const { return Symbol; } + + bool isRematerializable() const override { return true; } + std::string toString() const override; + + static bool classof(const StackSlot *S) { + return S->getSlotKind() == SK_Symbol; + } +}; + +/// The label pushed as the return address seen from the caller. +class CallerReturnSlot final : public StackSlot { + const MachineInstr *Call = nullptr; + +public: + explicit CallerReturnSlot(const MachineInstr *Call) + : StackSlot(SK_CallerReturn), Call(Call) {} + const MachineInstr *getCall() const { return Call; } + + bool isRematerializable() const override { return true; } + std::string toString() const override; + + static bool classof(const StackSlot *S) { + return S->getSlotKind() == SK_CallerReturn; + } +}; + +/// The label pushed as the return address seen from the callee. +class CalleeReturnSlot final : public StackSlot { + const MachineFunction *MF = nullptr; + +public: + explicit CalleeReturnSlot(const MachineFunction *MF) + : StackSlot(SK_CalleeReturn), MF(MF) {} + const MachineFunction *getMachineFunction() { return MF; } + + bool isRematerializable() const override { return false; } + std::string toString() const override { return "RET"; } + + static bool classof(const StackSlot *S) { + return S->getSlotKind() == SK_CalleeReturn; + } +}; + +/// A slot containing an arbitrary value that is always eventually popped and +/// never used. Used to maintain stack balance on control flow joins. +class UnusedSlot final : public StackSlot { +public: + UnusedSlot() : StackSlot(SK_Unused) {} + + bool isRematerializable() const override { return true; } + std::string toString() const override { return "Unused"; } + + static bool classof(const StackSlot *S) { + return S->getSlotKind() == SK_Unused; + } +}; + +class UnknownSlot final : public StackSlot { + size_t Index = 0; + +public: + explicit UnknownSlot(size_t Index) : StackSlot(SK_Unknown), Index(Index) {} + + size_t getIndex() const { return Index; } + bool isRematerializable() const override { return true; } + std::string toString() const override { + return "UNKNOWN(" + std::to_string(Index) + ")"; + } + + static bool classof(const StackSlot *S) { + return S->getSlotKind() == SK_Unknown; + } +}; + +/// The stack top is the last element of the vector. +class Stack : public SmallVector { +public: + explicit Stack(const StackSlot **Start, const StackSlot **End) + : SmallVector(Start, End) {} + explicit Stack(size_t Size, const StackSlot *Value) + : SmallVector(Size, Value) {} + explicit Stack(SmallVector &&Slots) + : SmallVector(std::move(Slots)) {} + // TODO: should it be explicit? If yes, fix all build errors. + Stack(const SmallVector &Slots) : SmallVector(Slots) {} + Stack() = default; + + std::string toString() const { + std::string Result("[ "); + for (const auto *It : *this) + Result += It->toString() + ' '; + Result += ']'; + return Result; + } +}; + +bool isPushOrDupLikeMI(const MachineInstr &MI); +bool isLinkerPseudoMI(const MachineInstr &MI); +bool isNoReturnCallMI(const MachineInstr &MI); + +/// Returns true if the \p Slot is a spill register slot. +inline bool isSpillReg(const StackSlot *Slot) { + const auto *RegSlot = dyn_cast(Slot); + return RegSlot && RegSlot->isSpill(); +} + +class EVMStackModel { + MachineFunction &MF; + const LiveIntervals &LIS; + unsigned StackDepthLimit; + + // Storage for stack slots. + mutable DenseMap> LiteralStorage; + mutable DenseMap> RegStorage; + mutable DenseMap, + std::unique_ptr> + SymbolStorage; + mutable DenseMap> + CallerReturnStorage; + + // There should be a single CalleeReturnSlot for the MF. + mutable std::unique_ptr TheCalleeReturnSlot; + + using MBBStackMap = DenseMap; + using InstStackMap = DenseMap; + + // Map MBB to its entry and exit stacks. + MBBStackMap MBBEntryStackMap; + // Note: For branches ending with a conditional jump, the exit stack + // retains the jump condition slot, even though the jump consumes it. + MBBStackMap MBBExitStackMap; + + // Map an MI to its entry stack. + InstStackMap InstEntryStackMap; + + // Map an MI to its input slots. + DenseMap MIInputMap; + + // Mutable getters for EVMStackSolver to manage the maps. + MBBStackMap &getMBBEntryMap() { return MBBEntryStackMap; } + MBBStackMap &getMBBExitMap() { return MBBExitStackMap; } + InstStackMap &getInstEntryMap() { return InstEntryStackMap; } + friend class EVMStackSolver; + +public: + EVMStackModel(MachineFunction &MF, const LiveIntervals &LIS, + unsigned StackDepthLimit); + Stack getFunctionParameters() const; + Stack getReturnArguments(const MachineInstr &MI) const; + + const Stack &getMIInput(const MachineInstr &MI) const { + return MIInputMap.at(&MI); + } + Stack getSlotsForInstructionDefs(const MachineInstr *MI) const { + Stack Defs; + for (const auto &MO : MI->defs()) + Defs.push_back(getRegisterSlot(MO.getReg())); + return Defs; + } + + void addSpillRegs(const SmallSet &SpillRegs) { + for (const auto &R : SpillRegs) { + auto *RegSlot = RegStorage.at(R).get(); + assert(!RegSlot->isSpill() && + "Register slot has already been marked as spill"); + RegSlot->setIsSpill(); + } + } + + // Get or create a requested stack slot. + StackSlot *getStackSlot(const MachineOperand &MO) const; + LiteralSlot *getLiteralSlot(const APInt &V) const { + if (LiteralStorage.count(V) == 0) + LiteralStorage[V] = std::make_unique(V); + return LiteralStorage[V].get(); + } + RegisterSlot *getRegisterSlot(const Register &R) const { + if (RegStorage.count(R) == 0) + RegStorage[R] = std::make_unique(R); + return RegStorage[R].get(); + } + SymbolSlot *getSymbolSlot(MCSymbol *S, const MachineInstr *MI) const { + auto Key = std::make_pair(S, MI); + if (SymbolStorage.count(Key) == 0) + SymbolStorage[Key] = std::make_unique(S, MI); + return SymbolStorage[Key].get(); + } + CallerReturnSlot *getCallerReturnSlot(const MachineInstr *Call) const { + if (CallerReturnStorage.count(Call) == 0) + CallerReturnStorage[Call] = std::make_unique(Call); + return CallerReturnStorage[Call].get(); + } + CalleeReturnSlot *getCalleeReturnSlot(const MachineFunction *MF) const { + if (!TheCalleeReturnSlot) + TheCalleeReturnSlot = std::make_unique(MF); + assert(MF == TheCalleeReturnSlot->getMachineFunction()); + return TheCalleeReturnSlot.get(); + } + // Unused is always the same slot. + static UnusedSlot *getUnusedSlot() { + static UnusedSlot TheUnusedSlot; + return &TheUnusedSlot; + } + + const Stack &getMBBEntryStack(const MachineBasicBlock *MBB) const { + return MBBEntryStackMap.at(MBB); + } + const Stack &getMBBExitStack(const MachineBasicBlock *MBB) const { + return MBBExitStackMap.at(MBB); + } + const Stack &getInstEntryStack(const MachineInstr *MI) const { + return InstEntryStackMap.at(MI); + } + + unsigned stackDepthLimit() const { return StackDepthLimit; } + +private: + Stack getSlotsForInstructionUses(const MachineInstr &MI) const; + void processMI(const MachineInstr &MI); + +public: + bool skipMI(const MachineInstr &MI) const { + auto Opc = MI.getOpcode(); + // If the virtual register has the only definition, ignore this instruction, + // as we create literal slots from the immediate value at the register uses. + if (Opc == EVM::CONST_I256 && + LIS.getInterval(MI.getOperand(0).getReg()).containsOneValue()) + return true; + return Opc == EVM::ARGUMENT || Opc == EVM::RET || Opc == EVM::JUMP || + Opc == EVM::JUMPI || Opc == EVM::PUSHDEPLOYADDRESS || + Opc == EVM::JUMP_UNLESS; + } + auto instructionsToProcess(const MachineBasicBlock *MBB) const { + return make_filter_range( + MBB->instrs(), [this](const MachineInstr &MI) { return !skipMI(MI); }); + } + auto reverseInstructionsToProcess(const MachineBasicBlock *MBB) const { + return make_filter_range( + make_range(MBB->rbegin(), MBB->rend()), + [this](const MachineInstr &MI) { return !skipMI(MI); }); + } +}; +} // namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_EVMSTACKMODEL_H diff --git a/llvm/lib/Target/EVM/EVMStackShuffler.cpp b/llvm/lib/Target/EVM/EVMStackShuffler.cpp new file mode 100644 index 000000000000..f2cea3660758 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMStackShuffler.cpp @@ -0,0 +1,283 @@ +//===---- EVMStackShuffler.cpp - Implementation of stack shuffling ---- 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 "EVMStackShuffler.h" +#include + +using namespace llvm; + +bool EVMStackShuffler::copyMissingSlotFromTarget(size_t StartRIdx, + bool CannotFail) { + assert(StartRIdx < Target.size()); + std::deque Worklist{StartRIdx}; + DenseSet Visited; + + while (!Worklist.empty()) { + size_t RIdx = Worklist.front(); + Worklist.pop_front(); + Visited.insert(RIdx); + // If a slot is missing or underrepresented in the current stack, copy it + // from the target. + if (getTargetNumOccurrences(Target[RIdx]) > 0) { + rematerialize(Target[RIdx]); + return true; + } + // Otherwise, the target slot already exists in the current stack, + // indicating that matching slots are present but not in the correct + // positions. Continue searching among the corresponding target slots. + assert(Current.size() <= Target.size()); + for (size_t NextRIdx = 0, Size = Current.size(); NextRIdx < Size; + ++NextRIdx) { + if (Visited.count(NextRIdx)) + continue; + if (match(Current[NextRIdx], Target[NextRIdx])) + continue; + if (match(Current[NextRIdx], Target[RIdx])) + Worklist.push_back(NextRIdx); + } + } + if (CannotFail) + report_fatal_error("Unexpected stack state."); + + return false; +} + +bool EVMStackShuffler::rematerializeUnreachableSlot() { + size_t Limit = StackDepthLimit - 1; + size_t Size = Current.size(); + if (Size < Limit) + return false; + + assert(Size <= Target.size()); + // Check whether any deep slot might still be needed later (i.e. we still + // need to reach it with a DUP or SWAP). + for (size_t RIdx = 0, REndIdx = Size - Limit; RIdx < REndIdx; ++RIdx) { + if (match(Current[RIdx], Target[RIdx])) { + if (getCurrentNumOccurrences(Current[RIdx]) > 0) { + // If this slot occurs again later, we skip this occurrence. + auto *It = std::find_if( + Current.begin() + RIdx + 1, Current.end(), + [&](const StackSlot *S) { return Current[RIdx] == S; }); + if (It != Current.end()) + continue; + + // Duplicate unreachable slot. + for (const auto *TargetSlot : Target) { + if (isa(TargetSlot)) + continue; + if (match(Current[RIdx], TargetSlot)) { + rematerialize(TargetSlot); + return true; + } + } + } + continue; + } + + // This slot needs to be moved. + // If the current top matches the slot, swap it down. + if (match(Current[Size - 1], Target[RIdx])) { + swap(Size - RIdx - 1); + return true; + } + // Otherwise try to copy it on top from target. + if (copyMissingSlotFromTarget(RIdx)) + return true; + // Otherwise swap up with the first slot that matches the target one. + if (swapIfCurrent(RIdx + 1, Size, [this, RIdx](const StackSlot *S) { + return match(S, Target[RIdx]); + })) + return true; + } + return false; +} + +bool EVMStackShuffler::step() { + if (Current.size() <= Target.size() && + all_of(zip(Current, Target), [&](auto Slots) { + return match(std::get<0>(Slots), std::get<1>(Slots)); + })) { + if (Current.size() < Target.size()) { + if (!rematerializeUnreachableSlot()) + // Since all current slots already match their target counterparts, + // a deficit must exist. + copyMissingSlotFromTarget(Current.size(), /* can't fail */ true); + return true; + } + return false; + } + + size_t Top = Current.size() - 1; + const auto *SrcTop = Current[Top]; + const auto *TgtTop = Top < Target.size() ? Target[Top] : nullptr; + + if (getCurrentNumOccurrences(SrcTop) < 0 && + !isa_and_nonnull(TgtTop)) { + pop(); + return true; + } + + assert(!Target.empty()); + // If the top slot is not on its position, swap it down to increase number of + // matching slots. + if (!match(SrcTop, TgtTop) || isa_and_nonnull(TgtTop)) { + for (size_t RIdx = 0, REndIdx = std::min(Current.size(), Target.size()); + RIdx < REndIdx; ++RIdx) { + bool CanSwap = SrcTop != Current[RIdx] && + !match(Current[RIdx], Target[RIdx]) && + match(SrcTop, Target[RIdx]); + if (!CanSwap) + continue; + + if (Current.size() - RIdx - 1 > StackDepthLimit) { + // If there is a reachable slot to be removed, swap it and remove. + for (size_t SwapDepth = StackDepthLimit; SwapDepth > 0; --SwapDepth) { + if (getCurrentNumOccurrences( + Current[Current.size() - 1 - SwapDepth]) < 0) { + swap(SwapDepth); + // Pop the redundant slot to compress the stack, even if the + // target's top slot is unused and therefore match with anything. + pop(); + return true; + } + } + } + swap(Current.size() - RIdx - 1); + return true; + } + } + + // If the top of the current stack was not needed, it was popped; + // if it was needed, it was swapped into the correct position. + assert(Current.size() <= Target.size()); + + // If a non-top slot is to be removed, try coping its corresponding target + // slot so that a swap can occur and the slot can be popped in the next + // iteration. + for (size_t RIdx = 0, REndIdx = Current.size(); RIdx < REndIdx; ++RIdx) { + if (match(Current[RIdx], Target[RIdx])) + continue; + + if (getCurrentNumOccurrences(Current[RIdx]) < 0) { + if (!rematerializeUnreachableSlot()) + // Given that the current stack is no larger than the target, + // any excess implies a corresponding deficit, so copying is guaranteed. + copyMissingSlotFromTarget(RIdx, /* can't fail */ true); + return true; + } + } + + for ([[maybe_unused]] const auto *CurrentSlot : Current) + assert(getCurrentNumOccurrences(CurrentSlot) >= 0); + + // Put the top at the right position. + if (!match(SrcTop, TgtTop)) + if (swapIfCurrent(0, Current.size(), + [TgtTop, this](const StackSlot *SrcSlot) { + return match(SrcSlot, TgtTop); + })) + return true; + + if (Current.size() < Target.size()) { + if (!rematerializeUnreachableSlot()) + copyMissingSlotFromTarget(Current.size(), /* can't fail */ true); + return true; + } + + assert(Current.size() == Target.size()); + for (size_t RIdx = 0, REndIdx = Current.size(); RIdx < REndIdx; ++RIdx) { + assert(getCurrentNumOccurrences(Current[RIdx]) == 0 && + "Current stack should have required number of slot copies."); + assert((isa(Target[RIdx]) || + getTargetNumOccurrences(Target[RIdx]) == 0) && + "Target stack should have required number of copies of used slots."); + } + assert(match(SrcTop, TgtTop) && "The top slot must match at this point."); + + size_t StartRIdx = Current.size() > (StackDepthLimit + 1) + ? Current.size() - (StackDepthLimit + 1) + : 0U; + // If a lower slot that mismatches the target matches the top slot, + // swap it so that it can be processed in the next iteration. + if (swapIfTarget(StartRIdx, Current.size(), + [SrcTop, this](const StackSlot *TgtSlot) { + return match(SrcTop, TgtSlot); + })) + return true; + + // Swap any mismatch reachable slot. + if (swapIfCurrent( + StartRIdx, Current.size(), + [SrcTop](const StackSlot *SrcSlot) { return SrcTop != SrcSlot; })) + return true; + + // Stack has unreachable slots, remove slots that match with unused. + if (isa(TgtTop)) { + pop(); + return true; + } + for (size_t RIdx = 0, REndIdx = Current.size(); RIdx < REndIdx; ++RIdx) { + if (isa(Target[RIdx])) { + swap(Current.size() - RIdx - 1); + pop(); + return true; + } + } + + // Unreachable slots cannot be handled; equate stacks without enforcing the + // depth limit. + if (swapIfTarget(0, Current.size(), [SrcTop, this](const StackSlot *TgtSlot) { + return match(SrcTop, TgtSlot); + })) + return true; + + if (swapIfCurrent(0, Current.size(), [SrcTop](const StackSlot *SrcSlot) { + return SrcTop != SrcSlot; + })) + return true; + + llvm_unreachable("Unexpected state"); +} + +void llvm::calculateStack( + Stack &CurrentStack, Stack const &TargetStack, unsigned StackDepthLimit, + const std::function &Swap, + const std::function &Rematerialize, + const std::function &Pop) { + EVMStackShuffler TheShuffler = + EVMStackShuffler(CurrentStack, TargetStack, StackDepthLimit); + auto getNumOccurrences = [](const StackSlot *Slot, Stack &C, const Stack &T) { + int CUses = -count(C, Slot); + if (T.size() > C.size()) + CUses += std::count(T.begin() + C.size(), T.end(), Slot); + CUses += count_if(zip(C, T), [Slot](auto In) { + auto [CSlot, TSlot] = In; + return isa(TSlot) ? CSlot == Slot : TSlot == Slot; + }); + return CUses; + }; + + TheShuffler.setGetCurrentNumOccurrences(getNumOccurrences); + TheShuffler.setGetTargetNumOccurrences(getNumOccurrences); + + TheShuffler.setSwap([&Swap](size_t Idx, Stack &C) { Swap(Idx); }); + TheShuffler.setPop(Pop); + TheShuffler.setRematerialize(Rematerialize); + + TheShuffler.shuffle(); + + assert(CurrentStack.size() == TargetStack.size()); + for (unsigned I = 0; I < CurrentStack.size(); ++I) { + const StackSlot *&Current = CurrentStack[I]; + const StackSlot *Target = TargetStack[I]; + if (isa(Target)) + Current = EVMStackModel::getUnusedSlot(); + else + assert(Current == Target); + } +} diff --git a/llvm/lib/Target/EVM/EVMStackShuffler.h b/llvm/lib/Target/EVM/EVMStackShuffler.h new file mode 100644 index 000000000000..c5b8c8305416 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMStackShuffler.h @@ -0,0 +1,158 @@ +//===---- EVMStackShuffler.h - Implementation of stack shuffling ---- 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 +// +//===----------------------------------------------------------------------===// +// +// Attempts to find the cheapest transition between two stacks. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVMSTACKSHUFFLER_H +#define LLVM_LIB_TARGET_EVM_EVMSTACKSHUFFLER_H + +#include "EVMStackModel.h" + +namespace llvm { + +/// Attempts to find the cheapest transition between two stacks. +class EVMStackShuffler { + Stack &Current; + const Stack &Target; + unsigned StackDepthLimit; + + using MatchFTy = std::function; + using GetNumOccurrencesFTy = + std::function; + using SwapFTy = std::function; + using PopFTy = std::function; + using RematerializeFTy = std::function; + + MatchFTy MatchF = nullptr; + GetNumOccurrencesFTy GetCurrentNumOccurrencesF = nullptr; + GetNumOccurrencesFTy GetTargetNumOccurrencesF = nullptr; + SwapFTy SwapF = nullptr; + PopFTy PopF = nullptr; + RematerializeFTy RematerializeF = nullptr; + +public: + EVMStackShuffler(Stack &Current, const Stack &Target, + unsigned StackDepthLimit) + : Current(Current), Target(Target), StackDepthLimit(StackDepthLimit) {} + + void setMatch(MatchFTy F) { MatchF = std::move(F); } + void setGetCurrentNumOccurrences(GetNumOccurrencesFTy F) { + GetCurrentNumOccurrencesF = std::move(F); + } + void setGetTargetNumOccurrences(GetNumOccurrencesFTy F) { + GetTargetNumOccurrencesF = std::move(F); + } + void setSwap(SwapFTy F) { SwapF = std::move(F); } + void setPop(PopFTy F) { PopF = std::move(F); } + void setRematerialize(RematerializeFTy F) { RematerializeF = std::move(F); } + +private: + /// Checks if \p SrcSlot can share the same position in a stack with + /// \p TgtSlot. + /// \return true if the slots are equal or if \p TgtSlot is + /// unused allowing any slot to be placed at that position. + bool match(const StackSlot *SrcSlot, const StackSlot *TgtSlot) { + // nullptr is used to pad the smaller stack so it matches the larger one. + // A missing slot does not match any slot. + if (!SrcSlot || !TgtSlot) + return false; + if (isa(TgtSlot)) + return true; + return MatchF ? MatchF(SrcSlot, TgtSlot) : SrcSlot == TgtSlot; + } + int getCurrentNumOccurrences(const StackSlot *SrcSlot) { + if (GetCurrentNumOccurrencesF) + return GetCurrentNumOccurrencesF(SrcSlot, Current, Target); + return 0; + } + int getTargetNumOccurrences(const StackSlot *TgtSlot) { + if (GetTargetNumOccurrencesF) + return GetTargetNumOccurrencesF(TgtSlot, Current, Target); + return 0; + } + bool swapIfCurrent(size_t StartRIdx, size_t EndRIdx, + const std::function &P) { + for (size_t RIdx = StartRIdx; RIdx < EndRIdx; ++RIdx) { + if (match(Current[RIdx], Target[RIdx])) + continue; + if (P(Current[RIdx])) { + swap(Current.size() - RIdx - 1); + return true; + } + } + return false; + } + bool swapIfTarget(size_t StartRIdx, size_t EndRIdx, + const std::function &P) { + for (size_t RIdx = StartRIdx; RIdx < EndRIdx; ++RIdx) { + if (match(Current[RIdx], Target[RIdx])) + continue; + if (P(Target[RIdx])) { + swap(Current.size() - RIdx - 1); + return true; + } + } + return false; + } + void swap(size_t I) { + if (SwapF) + SwapF(I, Current); + std::swap(Current[Current.size() - I - 1], Current.back()); + } + void pop() { + if (PopF) + PopF(); + Current.pop_back(); + } + void rematerialize(const StackSlot *S) { + if (RematerializeF) + RematerializeF(S); + Current.push_back(S); + } + +public: + /// After `shuffle`, the source and target stacks are of equal size and + /// corresponding slots match. + /// TODO: assert + void shuffle() { + // The shuffling algorithm should always terminate in polynomial time, but + // we provide a limit in case it does not terminate due to a bug. + for (unsigned I = 0; I < 1000; ++I) + if (!step()) + return; + report_fatal_error("Could not create stack after 1000 iterations."); + } + +private: + /// Perform one stack manipulation (push, pop, dup, swap). + /// \return false if shuffling is done. + bool step(); + + /// Copies a slot from the target stack into the current stack if it is either + /// missing or present in fewer copies. + /// \param StartRIdx The index from which to start searching. + bool copyMissingSlotFromTarget(size_t StartRIdx, bool CannotFail = false); + + // If dupping an ideal slot causes a slot that will still be required to + // become unreachable, then dup the latter slot first. + // \return true, if it performed a dup. + bool rematerializeUnreachableSlot(); +}; + +/// Transforms \p CurrentStack to \p TargetStack. Modifies `CurrentStack` itself +/// after each step(). +void calculateStack(Stack &CurrentStack, Stack const &TargetStack, + unsigned StackDepthLimit, + const std::function &Swap, + const std::function &Rematerialize, + const std::function &Pop); + +} // end namespace llvm +#endif // LLVM_LIB_TARGET_EVM_EVMSTACKSHUFFLER_H diff --git a/llvm/lib/Target/EVM/EVMStackSolver.cpp b/llvm/lib/Target/EVM/EVMStackSolver.cpp new file mode 100644 index 000000000000..d70e443b007e --- /dev/null +++ b/llvm/lib/Target/EVM/EVMStackSolver.cpp @@ -0,0 +1,1004 @@ +//===--------------------- EVMStackSolver.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 "EVMStackSolver.h" + +#include "EVM.h" +#include "EVMInstrInfo.h" +#include "EVMMachineFunctionInfo.h" +#include "EVMRegisterInfo.h" +#include "EVMStackShuffler.h" +#include "llvm/ADT/DepthFirstIterator.h" +#include "llvm/CodeGen/CalcSpillWeights.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineLoopInfo.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include + +using namespace llvm; + +#define DEBUG_TYPE "evm-stack-solver" + +static cl::opt MaxSpillIterations( + "evm-max-spill-iterations", cl::Hidden, cl::init(100), + cl::desc("Maximum number of iterations to spill stack slots " + "to avoid stack too deep issues.")); + +#ifndef NDEBUG +static cl::list + ForceRegSpills("evm-force-reg-spills", + llvm::cl::desc("Force spilling of the specified registers."), + cl::CommaSeparated, cl::Hidden); +#endif // NDEBUG + +namespace { + +/// \return index of \p Item in \p RangeOrContainer or std::nullopt. +template +std::optional offset(T &&RangeOrContainer, V &&Item) { + auto &&Range = std::forward(RangeOrContainer); + auto It = find(Range, std::forward(Item)); + return (It == adl_end(Range)) + ? std::nullopt + : std::optional(std::distance(adl_begin(Range), It)); +} + +/// \return a range covering the last N elements of \p RangeOrContainer. +template auto take_back(T &&RangeOrContainer, size_t N = 1) { + return make_range(std::prev(adl_end(std::forward(RangeOrContainer)), N), + adl_end(std::forward(RangeOrContainer))); +} + +template +std::optional add(std::optional a, std::optional b) { + if (a && b) + return *a + *b; + return std::nullopt; +} + +#ifndef NDEBUG +std::string getMBBId(const MachineBasicBlock *MBB) { + return std::to_string(MBB->getNumber()) + "." + std::string(MBB->getName()); +} +#endif + +/// Given stack \p AfterInst, compute stack before the instruction excluding +/// instruction input operands. +/// \param CompressStack: remove duplicates and rematerializable slots. +Stack calculateStackBeforeInst(const Stack &InstDefs, const Stack &AfterInst, + bool CompressStack, unsigned StackDepthLimit) { + // Number of slots on stack before the instruction excluding its inputs. + size_t BeforeInstSize = count_if(AfterInst, [&](const StackSlot *S) { + return !is_contained(InstDefs, S) && + !(CompressStack && (S->isRematerializable() || isSpillReg(S))); + }); + + // To use StackTransformer, the computed stack must be transformable to the + // AfterInst stack. Initialize it as follows: + // UnknownSlot{0}, ..., UnknownSlot{BeforeInstSize - 1}, [def<0>], ..., + // [def] + // where UnknownSlot{I} denotes an element to be copied from the AfterInst + // stack to the I-th position in the BeforeInst stack. + SmallVector UnknownSlots; + for (size_t Index = 0; Index < BeforeInstSize; ++Index) + UnknownSlots.emplace_back(Index); + + Stack Tmp; + for (auto &S : UnknownSlots) + Tmp.push_back(&S); + append_range(Tmp, InstDefs); + + if (Tmp.empty()) + return Stack{}; + + EVMStackShuffler Shuffler(Tmp, AfterInst, StackDepthLimit); + + auto canSkipSlot = [&InstDefs, CompressStack](const StackSlot *Slot) { + return count(InstDefs, Slot) || + (CompressStack && (Slot->isRematerializable() || isSpillReg(Slot))); + }; + auto countOccurences = [&canSkipSlot](const StackSlot *Slot, Stack &C, + const Stack &T) { + int Num = -count(C, Slot); + if (canSkipSlot(Slot)) + Num = Num + count(T, Slot); + return Num; + }; + + Shuffler.setMatch( + [&canSkipSlot](const StackSlot *SrcSlot, const StackSlot *TgtSlot) { + return isa(SrcSlot) ? !canSkipSlot(TgtSlot) + : SrcSlot == TgtSlot; + }); + + Shuffler.setGetCurrentNumOccurrences( + [&countOccurences](const StackSlot *Slot, Stack &C, const Stack &T) { + return isa(Slot) ? 0 : countOccurences(Slot, C, T); + }); + + Shuffler.setGetTargetNumOccurrences( + [&countOccurences, &canSkipSlot](const StackSlot *Slot, Stack &C, + const Stack &T) { + return !canSkipSlot(Slot) ? 0 : countOccurences(Slot, C, T); + }); + + Shuffler.setSwap([](size_t I, Stack &C) { + assert(!isa(C[C.size() - I - 1]) || + !isa(C.back())); + }); + + Shuffler.shuffle(); + + // After the stack transformation, for each index I, move the AfterInst slot + // corresponding to UnknownSlot{I} from Tmp to the I-th position of the + // BeforeInst. + assert(Tmp.size() == AfterInst.size()); + SmallVector BeforeInst(BeforeInstSize, nullptr); + for (unsigned Idx = 0; Idx < Tmp.size(); ++Idx) { + if (const auto *Slot = dyn_cast(Tmp[Idx])) + BeforeInst[Slot->getIndex()] = AfterInst[Idx]; + } + assert(all_of(BeforeInst, + [](const StackSlot *Slot) { return Slot != nullptr; })); + + return BeforeInst; +} + +/// From a vector of spillable registers, find the cheapest one to spill based +/// on the weights. +Register getRegToSpill(const SmallSetVector &SpillableRegs, + const LiveIntervals &LIS) { + assert(!SpillableRegs.empty() && "SpillableRegs should not be empty"); + + const auto *BestInterval = &LIS.getInterval(SpillableRegs[0]); + for (auto Reg : drop_begin(SpillableRegs)) { + const auto *LI = &LIS.getInterval(Reg); + + // Take this interval only if it has a non-zero weight and + // either BestInterval has zero weight or this interval has a lower + // weight than the current best. + if (LI->weight() != 0.0F && (BestInterval->weight() == 0.0F || + LI->weight() < BestInterval->weight())) + BestInterval = LI; + } + + LLVM_DEBUG({ + for (Register Reg : SpillableRegs) { + dbgs() << "Spill candidate: "; + LIS.getInterval(Reg).dump(); + } + dbgs() << " Best spill candidate: "; + BestInterval->dump(); + dbgs() << '\n'; + }); + return BestInterval->reg(); +} + +/// EVM-specific implementation of weight normalization. +class EVMVirtRegAuxInfo final : public VirtRegAuxInfo { + float normalize(float UseDefFreq, unsigned Size, unsigned NumInstr) override { + // All intervals have a spill weight that is mostly proportional to the + // number of uses, with uses in loops having a bigger weight. + return NumInstr * VirtRegAuxInfo::normalize(UseDefFreq, Size, 1); + } + +public: + EVMVirtRegAuxInfo(MachineFunction &MF, LiveIntervals &LIS, + const VirtRegMap &VRM, const MachineLoopInfo &Loops, + const MachineBlockFrequencyInfo &MBFI) + : VirtRegAuxInfo(MF, LIS, VRM, Loops, MBFI) {} +}; + +} // end anonymous namespace + +BranchInfoTy llvm::getBranchInfo(const MachineBasicBlock *MBB) { + const auto *EVMTII = static_cast( + MBB->getParent()->getSubtarget().getInstrInfo()); + auto *UnConstMBB = const_cast(MBB); + SmallVector Cond; + SmallVector BranchInstrs; + MachineBasicBlock *TBB = nullptr, *FBB = nullptr; + auto BT = EVMTII->analyzeBranch(*UnConstMBB, TBB, FBB, Cond, + /* AllowModify */ false, BranchInstrs); + if (BT == EVMInstrInfo::BT_Cond && !FBB) + FBB = UnConstMBB->getFallThrough(); + + if (BT == EVMInstrInfo::BT_NoBranch && !TBB && !MBB->succ_empty()) + TBB = UnConstMBB->getFallThrough(); + + assert((BT != EVMInstrInfo::BT_Cond && BT != EVMInstrInfo::BT_CondUncond) || + !Cond.empty() && + "Condition should be defined for a condition branch."); + assert(BranchInstrs.size() <= 2); + return {BT, TBB, FBB, BranchInstrs, + Cond.empty() ? std::nullopt : std::optional(Cond[1])}; +} + +/// Return cost of transformation in gas from \p Source to \p Target or +/// nullopt if unreachable stack elements are detected during the +/// transformation. +/// An element is considered unreachable if the transformation requires a stack +/// manipulation on an element deeper than \p StackDepthLimit, which EVM does +/// not support. +/// \note The copy of \p Source is intentional since it is modified during +/// computation. We retain the original \p Source to reattempt the +/// transformation using a compressed stack. +std::optional +llvm::calculateStackTransformCost(Stack Source, const Stack &Target, + unsigned StackDepthLimit) { + unsigned Gas = 0; + bool HasError = false; + auto Swap = [&Gas, &HasError, StackDepthLimit](unsigned SwapDepth) { + if (SwapDepth > StackDepthLimit) + HasError = true; + Gas += EVMCOST::SWAP; + }; + + auto Rematerialize = [&Gas, &HasError, &Source, + StackDepthLimit](const StackSlot *Slot) { + if (isSpillReg(Slot)) { + // Prefer dup for spills and reloads, since it is cheaper. + std::optional Depth = offset(reverse(Source), Slot); + if (Depth && *Depth < StackDepthLimit) + Gas += EVMCOST::DUP; + else + Gas += EVMCOST::PUSH + EVMCOST::MLOAD; + } else if (Slot->isRematerializable()) { + Gas += EVMCOST::PUSH; + } else { + std::optional Depth = offset(reverse(Source), Slot); + if (Depth && *Depth >= StackDepthLimit) + HasError = true; + Gas += EVMCOST::DUP; + } + }; + auto Pop = [&Gas]() { Gas += EVMCOST::POP; }; + + calculateStack(Source, Target, StackDepthLimit, Swap, Rematerialize, Pop); + return HasError ? std::nullopt : std::optional(Gas); +} + +EVMStackSolver::EVMStackSolver(MachineFunction &MF, EVMStackModel &StackModel, + const MachineLoopInfo *MLI, + const VirtRegMap &VRM, + const MachineBlockFrequencyInfo &MBFI, + LiveIntervals &LIS) + : MF(MF), StackModel(StackModel), MLI(MLI), VRM(VRM), MBFI(MBFI), LIS(LIS) { +} + +void EVMStackSolver::calculateSpillWeights() { + if (IsSpillWeightsCalculated) + return; + + EVMVirtRegAuxInfo EVRAI(MF, LIS, VRM, *MLI, MBFI); + EVRAI.calculateSpillWeightsAndHints(); + IsSpillWeightsCalculated = true; +} + +bool EVMStackSolver::hasUnreachableDef(const Register &Reg) const { + for (const auto &MI : MF.getRegInfo().def_instructions(Reg)) { + int Depth = -1; + if (MI.getOpcode() == EVM::ARGUMENT) { + // For function arguments, first operand represents the index of the + // argument on the stack, so we can use it to determine the depth. + Depth = MI.getOperand(1).getImm(); + } else { + for (auto [DefIdx, Def] : enumerate(reverse(MI.defs()))) { + if (Def.getReg() == Reg) { + Depth = DefIdx; + break; + } + } + } + assert(Depth >= 0 && "Register not found in the instruction defs."); + + // If the def is deeper than the stack depth limit, it is unreachable. + if (static_cast(Depth + 1) > StackModel.stackDepthLimit()) + return true; + } + return false; +} + +EVMStackSolver::UnreachableSlotVec EVMStackSolver::getUnreachableSlots() const { + UnreachableSlotVec UnreachableSlots; + const unsigned StackDepthLimit = StackModel.stackDepthLimit(); + + // Check if the stack transformation is valid when transforming from + // \p From to \p To. If the transformation is not valid, collect the + // unreachable slots. \p From will be changed in place, but that is + // fine since we are not using it after. + auto checkStackTransformation = + [&UnreachableSlots, StackDepthLimit](Stack &From, const Stack &To) { + calculateStack( + From, To, StackDepthLimit, + // Swap. + [&](unsigned I) { + assert(I > 0 && I < From.size()); + if (I > StackDepthLimit) + UnreachableSlots.push_back({From, From.size() - I - 1}); + }, + // Push or dup. + [&](const StackSlot *Slot) { + if (Slot->isRematerializable() || isSpillReg(Slot)) + return; + + auto SlotIt = llvm::find(llvm::reverse(From), Slot); + assert(SlotIt != From.rend() && + "Slot for duplication not found on the stack."); + unsigned Depth = std::distance(From.rbegin(), SlotIt); + if (Depth >= StackDepthLimit) + UnreachableSlots.push_back({From, From.size() - Depth - 1}); + }, + // Pop. + [&]() {}); + }; + + for (const auto &MBB : MF) { + Stack CurrentStack = StackModel.getMBBEntryStack(&MBB); + + // Check the stack transformation for each instruction. + for (const auto &MI : StackModel.instructionsToProcess(&MBB)) { + checkStackTransformation(CurrentStack, StackModel.getInstEntryStack(&MI)); + CurrentStack = getMIExitStack(&MI); + } + + auto [BranchTy, TBB, FBB, BrInsts, Condition] = getBranchInfo(&MBB); + + // Check the transformation for the exit stack. This mimics logic from + // the EVMStackifyCodeEmitter::run() method, as in some cases this is + // not needed. + switch (BranchTy) { + case EVMInstrInfo::BT_Uncond: + case EVMInstrInfo::BT_NoBranch: + if (!MBB.succ_empty()) + // For loop latches, exit stack is not the same as the entry stack + // of the successor block, so we need to check the transformation + // from the current stack to the entry stack of the successor block. + checkStackTransformation(CurrentStack, + StackModel.getMBBEntryStack(TBB)); + break; + case EVMInstrInfo::BT_None: + if (!MBB.isReturnBlock()) + break; + LLVM_FALLTHROUGH; + case EVMInstrInfo::BT_Cond: + case EVMInstrInfo::BT_CondUncond: + checkStackTransformation(CurrentStack, StackModel.getMBBExitStack(&MBB)); + break; + } + } + return UnreachableSlots; +} + +void EVMStackSolver::run() { +#ifndef NDEBUG + SmallSet ForceSpillRegs; + for (auto Reg : ForceRegSpills) + ForceSpillRegs.insert(Register::index2VirtReg(Reg)); + if (!ForceSpillRegs.empty()) + StackModel.addSpillRegs(ForceSpillRegs); +#endif // NDEBUG + + unsigned IterCount = 0; + while (true) { + runPropagation(); + LLVM_DEBUG({ + dbgs() << "************* Stack *************\n"; + dump(dbgs()); + }); + + auto UnreachableSlots = getUnreachableSlots(); + if (UnreachableSlots.empty()) + break; + + if (MF.getFunction().hasFnAttribute("evm-recursive")) { + // For recursive functions we can't use spills to fix the stack too deep + // errors, as we are using memory to spill and not real stack. Report an + // error if this function is recursive and we forced compress stack + // before. + if (ForceCompressStack) + report_fatal_error( + "Stackification failed for '" + MF.getName() + + "' function. It is recursive and has stack too deep errors. " + "Consider refactoring it to use a non-recursive approach."); + + LLVM_DEBUG({ + dbgs() + << "EVMStackSolver: force CompressStack for recursive function.\n"; + }); + + // propagateThroughMBB can't detect stack-too-deep errors for + // transformations from MBB's entry stack to the first instruction entry + // stack. Since a recursive function doesn't support spills and reloads, + // try to compress the stack. + // TODO: This is a little bit agressive, since we can detect MBBs where + // the stack is too deep and compress only them. + ForceCompressStack = true; + continue; + } + + if (++IterCount > MaxSpillIterations) + report_fatal_error("EVMStackSolver: maximum number of spill iterations " + "exceeded."); + + LLVM_DEBUG({ + dbgs() << "Unreachable slots found: " << UnreachableSlots.size() + << ", iteration: " << IterCount << '\n'; + }); + + // We are about to spill registers, so we need to calculate spill + // weights to determine which register to spill. + calculateSpillWeights(); + + SmallSet RegsToSpill; + for (auto &[StackSlots, Idx] : UnreachableSlots) { + LLVM_DEBUG({ + dbgs() << "Unreachable slot: " << StackSlots[Idx]->toString() + << " at index: " << Idx << '\n'; + dbgs() << "Stack with unreachable slot: " << StackSlots.toString() + << '\n'; + }); + + // Collect spillable registers from the stack slots starting from + // the given index. + SmallSetVector SpillableRegs; + for (unsigned I = Idx, E = StackSlots.size(); I < E; ++I) + if (const auto *RegSlot = dyn_cast(StackSlots[I])) + if (!RegSlot->isSpill() && !hasUnreachableDef(RegSlot->getReg())) + SpillableRegs.insert(RegSlot->getReg()); + + if (!SpillableRegs.empty()) + RegsToSpill.insert(getRegToSpill(SpillableRegs, LIS)); + } + + LLVM_DEBUG( + { dbgs() << "Spilling " << RegsToSpill.size() << " registers\n"; }); + if (RegsToSpill.empty()) + report_fatal_error("EVMStackSolver: no spillable registers found for " + "unreachable slots."); + StackModel.addSpillRegs(RegsToSpill); + } +} + +/// Return true if \p Slot is a spillable register and it has no use or +/// definition before \p MI. +static bool spillRegHasNoUseOrDefBefore(const StackSlot *Slot, + const MachineInstr &MI) { + if (!isSpillReg(Slot)) + return false; + + auto Reg = cast(Slot)->getReg(); + return std::all_of(std::next(MachineBasicBlock::const_reverse_iterator(MI)), + MI.getParent()->rend(), [Reg](const MachineInstr &Instr) { + return !Instr.readsRegister(Reg, /*TRI*/ nullptr) && + !Instr.definesRegister(Reg, /*TRI=*/nullptr); + }); +} + +bool EVMStackSolver::shouldRemoveSlot(const StackSlot *Slot, + const MachineInstr &MI) const { + const auto *Literal = dyn_cast(Slot); + if (!Literal) + return Slot->isRematerializable(); + + const auto &Imm = Literal->getValue(); + + // PUSH0 is always profitable to remove, since it is one of the cheapest + // instructions. + if (Imm.isZero()) + return true; + + // Remove smaller literals, as they are not profitable to propagate and + // can increase stack pressure and potentially cause spilling. The minimum + // PUSH size of 4 is got after doing benchmarks with different sizes + // (9, 8, 6, 5, 4, 3). + const unsigned PushByteWidth = alignTo(Imm.getActiveBits(), 8) / 8; + unsigned MinPushByteWidth = MI.getMF()->getFunction().hasOptSize() ? 4 : 8; + if (PushByteWidth < MinPushByteWidth) + return true; + + // If there is no other use of the literal in the MBB, we can remove it + // from the stack to reduce stack pressure, otherwise we keep it to reduce + // code size. + return std::all_of( + std::next(MachineBasicBlock::const_reverse_iterator(MI)), + MI.getParent()->rend(), [Literal, this](const MachineInstr &Instr) { + return StackModel.skipMI(Instr) || + !is_contained(StackModel.getMIInput(Instr), Literal); + }); +} + +Stack EVMStackSolver::propagateThroughMI(const Stack &ExitStack, + const MachineInstr &MI, + bool CompressStack) { + LLVM_DEBUG({ + dbgs() << "\tMI: "; + MI.dump(); + }); + const Stack MIDefs = StackModel.getSlotsForInstructionDefs(&MI); + // Stack before MI except for MI inputs. + Stack BeforeMI = calculateStackBeforeInst(MIDefs, ExitStack, CompressStack, + StackModel.stackDepthLimit()); + +#ifndef NDEBUG + // Ensure MI defs are not present in BeforeMI stack. + for (const auto *StackSlot : BeforeMI) + if (const auto *RegSlot = dyn_cast(StackSlot)) + assert(!MI.definesRegister(RegSlot->getReg(), /*TRI=*/nullptr)); +#endif // NDEBUG + + BeforeMI.append(StackModel.getMIInput(MI)); + // Store computed stack to StackModel. + insertInstEntryStack(&MI, BeforeMI); + + LLVM_DEBUG({ dbgs() << "\tstack before: " << BeforeMI.toString() << ".\n"; }); + + // If this is a commutable instruction, and second operand is spilled and + // has no use or def before MI, we can remove it from the stack. This should + // reduce the stack pressure and in some cases, it should reduce code size. + if (MI.isCommutable() && + spillRegHasNoUseOrDefBefore(BeforeMI[BeforeMI.size() - 2], MI)) { + std::swap(BeforeMI[BeforeMI.size() - 1], BeforeMI[BeforeMI.size() - 2]); + BeforeMI.pop_back(); + } + + // If the top stack slots can be rematerialized just before MI, remove it + // from propagation to reduce pressure. + while (!BeforeMI.empty()) { + const auto *BackSlot = BeforeMI.back(); + // Don't remove register spills if the register is used or defined before + // MI. They are not cheap and ideally we shouldn't do more than one reload + // in the BB. + if (shouldRemoveSlot(BackSlot, MI) || + spillRegHasNoUseOrDefBefore(BackSlot, MI)) { + BeforeMI.pop_back(); + } else if (auto Offset = + offset(drop_begin(reverse(BeforeMI), 1), BackSlot)) { + if (*Offset + 2 < StackModel.stackDepthLimit()) + BeforeMI.pop_back(); + else + break; + } else + break; + } + + return BeforeMI; +} + +Stack EVMStackSolver::getMIExitStack(const MachineInstr *MI) const { + const Stack &MIInput = StackModel.getMIInput(*MI); + Stack MIEntry = StackModel.getInstEntryStack(MI); + assert(MIInput.size() <= MIEntry.size()); + MIEntry.resize(MIEntry.size() - MIInput.size()); + MIEntry.append(StackModel.getSlotsForInstructionDefs(MI)); + return MIEntry; +} + +Stack EVMStackSolver::propagateThroughMBB(const Stack &ExitStack, + const MachineBasicBlock *MBB, + bool CompressStack) { + Stack CurrentStack = ExitStack; + + LLVM_DEBUG({ + dbgs() << "EVMStackSolver: start back propagating stack through " + << getMBBId(MBB) + << " (CompressStack=" << (CompressStack ? "true" : "false") + << "):\n"; + }); + for (const auto &MI : StackModel.reverseInstructionsToProcess(MBB)) { + Stack EntryMI = propagateThroughMI(CurrentStack, MI, CompressStack); + + // Check the exit stack of the MI can be transformed to the entry stack + // of the next MI, or rerun the propagation with compression. + // It also checks that the last MIs exit stack can be tranformed to + // the MBBs exit stack, but it doesn't do that for MBBs entry stack to + // the first MI entry stack transformation to avoid false positives + // (we rewrite the MBBs entry stack just after the function finished). + if (!CompressStack && + !calculateStackTransformCost(getMIExitStack(&MI), CurrentStack, + StackModel.stackDepthLimit())) { + LLVM_DEBUG({ + dbgs() << "\terror: stack-too-deep detected, trying to rerun with " + "CompressStack=true.\n"; + }); + return propagateThroughMBB(ExitStack, MBB, + /*CompressStack*/ true); + } + CurrentStack = EntryMI; + } + + return CurrentStack; +} + +void EVMStackSolver::runPropagation() { + // Reset StackModel's internal state. + StackModel.getMBBEntryMap().clear(); + StackModel.getMBBExitMap().clear(); + StackModel.getInstEntryMap().clear(); + + std::deque Worklist{&MF.front()}; + DenseSet Visited; + + // Collect all the backedges in the MF. + // TODO: CPR-1847. Consider changing CFG before the stackification such that + // every loop has only one backedge. + SmallVector, + 64> + Backedges; + for (const MachineLoop *TopLevelLoop : *MLI) { + // TODO: CPR-1847. Investigate in which order it's better to traverse + // loops. + for (const MachineLoop *L : depth_first(TopLevelLoop)) { + SmallVector Latches; + L->getLoopLatches(Latches); + const MachineBasicBlock *Header = L->getHeader(); + transform(Latches, std::back_inserter(Backedges), + [Header](const MachineBasicBlock *MBB) { + return std::make_pair(MBB, Header); + }); + } + } + + // Propagate stack information until no new information is discovered. + while (!Worklist.empty()) { + // Process blocks in the worklist without following backedges first. + while (!Worklist.empty()) { + const MachineBasicBlock *MBB = *Worklist.begin(); + Worklist.pop_front(); + if (Visited.count(MBB)) + continue; + + // Get the MBB exit stack. + std::optional ExitStack = std::nullopt; + // True if we need to pop the spilled condition from propagating it + // through the MBB. + bool PopSpilledCond = false; + auto [BranchTy, TBB, FBB, BrInsts, Condition] = getBranchInfo(MBB); + + switch (BranchTy) { + case EVMInstrInfo::BT_None: { + ExitStack = MBB->isReturnBlock() + ? StackModel.getReturnArguments(MBB->back()) + : Stack{}; + } break; + case EVMInstrInfo::BT_Uncond: + case EVMInstrInfo::BT_NoBranch: { + const MachineBasicBlock *Target = MBB->getSingleSuccessor(); + // Currently a basic block could end with FCALL and have no successors, + // but FCALL is not a terminator, so we fall into BT_NoBranch. + // TODO: #788 address it + if (!Target) { // No successors. + ExitStack = Stack{}; + break; + } + if (MachineLoop *ML = MLI->getLoopFor(MBB); + ML && ML->isLoopLatch(MBB)) { + // If the loop header's entry stack has been computed, + // use it as the latch's exit stack; otherwise, assume an empty + // exit stack to avoid non-termination. + auto It = StackModel.getMBBEntryMap().find(Target); + ExitStack = + (It == StackModel.getMBBEntryMap().end() ? Stack{} : It->second); + } else { + if (Visited.count(Target)) + ExitStack = StackModel.getMBBEntryStack(Target); + else + Worklist.push_front(Target); + } + } break; + case EVMInstrInfo::BT_Cond: + case EVMInstrInfo::BT_CondUncond: { + bool FBBVisited = Visited.count(FBB); + bool TBBVisited = Visited.count(TBB); + + if (!FBBVisited) + Worklist.push_front(FBB); + if (!TBBVisited) + Worklist.push_front(TBB); + + if (FBBVisited && TBBVisited) { + // The order of arguments in 'combineStack' influences the + // resulting combined stack, which is unexpected. Additionally, + // passing TBB's stack resolves many 'stack-too-deep' errors in CI. + // TODO: #781. Investigate the cause of this behavior. + Stack CombinedStack = combineStack(StackModel.getMBBEntryStack(TBB), + StackModel.getMBBEntryStack(FBB)); + // Retain the jump condition in ExitStack because StackSolver doesn't + // propagate stacks through branch instructions. + const auto *CondSlot = StackModel.getStackSlot(*Condition); + CombinedStack.emplace_back(CondSlot); + ExitStack = std::move(CombinedStack); + + // If the condition is spilled, we should not propagate it through the + // MBB, if there is no use or def of the condition. This should reduce + // the stack pressure if the condition is not used in the MBB. + if (spillRegHasNoUseOrDefBefore(CondSlot, + *BrInsts[BrInsts.size() - 1])) + PopSpilledCond = true; + } + } + } + + if (ExitStack) { + Visited.insert(MBB); + insertMBBExitStack(MBB, *ExitStack); + if (PopSpilledCond) { + assert(ExitStack->back() == + StackModel.getRegisterSlot(Condition->getReg()) && + "Condition slot should be on top of the exit stack."); + ExitStack->pop_back(); + } + + insertMBBEntryStack( + MBB, propagateThroughMBB(*ExitStack, MBB, ForceCompressStack)); + append_range(Worklist, MBB->predecessors()); + } + } + + // Revisit blocks connected by loop backedges. + for (auto [Latch, Header] : Backedges) { + const Stack &HeaderEntryStack = StackModel.getMBBEntryStack(Header); + const Stack &LatchExitStack = StackModel.getMBBExitStack(Latch); + if (all_of(HeaderEntryStack, [LatchExitStack](const StackSlot *Slot) { + return is_contained(LatchExitStack, Slot); + })) + continue; + + // The latch block does not provide all the slots required by the loop + // header. Mark the subgraph between the latch and header for + // reprocessing. + assert(Latch); + Worklist.emplace_back(Latch); + + // Since the loop header's entry may be permuted, revisit its predecessors + // to minimize stack transformation costs. + for (const MachineBasicBlock *Pred : Header->predecessors()) + Visited.erase(Pred); + + // DFS upwards traversal from latch to the header. + for (auto I = idf_begin(Latch), E = idf_end(Latch); I != E;) { + const MachineBasicBlock *MBB = *I; + Visited.erase(MBB); + if (MBB == Header) { + I.skipChildren(); + continue; + } + ++I; + } + } + } + + // For an MBB with multiple successors the exit stack is a union of the entry + // stack slots from all successors. Each successor's entry stack may be + // missing some slots present in the exit stack. Adjust each successor's entry + // stack to mirror the basic block's exit stack. For any slot that is dead in + // a successor, mark it as 'unused'. + for (const MachineBasicBlock &MBB : MF) { + auto [BranchTy, TBB, FBB, BrInsts, Condition] = getBranchInfo(&MBB); + + if (BranchTy != EVMInstrInfo::BT_Cond && + BranchTy != EVMInstrInfo::BT_CondUncond) + continue; + + Stack ExitStack = StackModel.getMBBExitStack(&MBB); + + // The condition must be on top of ExitStack, but a conditional jump + // consumes it. + assert(ExitStack.back() == StackModel.getStackSlot(*Condition)); + ExitStack.pop_back(); + + for (const MachineBasicBlock *Succ : MBB.successors()) { + const Stack &SuccEntryStack = StackModel.getMBBEntryStack(Succ); + Stack NewSuccEntryStack = ExitStack; + for (const StackSlot *&Slot : NewSuccEntryStack) + if (!is_contained(SuccEntryStack, Slot)) + Slot = EVMStackModel::getUnusedSlot(); + +#ifndef NDEBUG + for (const StackSlot *Slot : SuccEntryStack) + assert(Slot->isRematerializable() || isSpillReg(Slot) || + is_contained(NewSuccEntryStack, Slot)); +#endif // NDEBUG + + insertMBBEntryStack(Succ, NewSuccEntryStack); + } + } + + Stack EntryStack; + bool IsNoReturn = MF.getFunction().hasFnAttribute(Attribute::NoReturn); + Stack FunctionParameters = StackModel.getFunctionParameters(); + + // We assume the function containing PUSHDEPLOYADDRESS instruction has the + // following properties: + // - It is unique (verified in AsmPrinter) + // - It appears first in the module layout. TODO: #778 + // - It does not return and has no arguments + if (const auto *MFI = MF.getInfo(); + MFI->getHasPushDeployAddress()) { + MachineBasicBlock::const_iterator I = MF.front().getFirstNonDebugInstr(); + assert(I != MF.front().end() && I->getOpcode() == EVM::PUSHDEPLOYADDRESS && + "MF has no PUSHDEPLOYADDRESS"); + assert(IsNoReturn && FunctionParameters.empty() && + "PUSHDEPLOYADDRESS in a non - void/nullary function"); + EntryStack.push_back(StackModel.getRegisterSlot(I->getOperand(0).getReg())); + } + // The entry MBB's stack contains the function parameters, which cannot be + // inferred; put them to the stack. + if (!IsNoReturn) + EntryStack.push_back(StackModel.getCalleeReturnSlot(&MF)); + + // Calling convention: input arguments are passed in stack such that the + // first one specified in the function declaration is passed on the stack TOP. + append_range(EntryStack, reverse(FunctionParameters)); + insertMBBEntryStack(&MF.front(), EntryStack); +} + +Stack EVMStackSolver::combineStack(const Stack &Stack1, const Stack &Stack2) { + auto [it1, it2] = + std::mismatch(Stack1.begin(), Stack1.end(), Stack2.begin(), Stack2.end()); + + Stack CommonPrefix; + CommonPrefix.append(Stack1.begin(), it1); + + Stack Stack1Tail, Stack2Tail; + Stack1Tail.append(it1, Stack1.end()); + Stack2Tail.append(it2, Stack2.end()); + + if (Stack1Tail.empty()) { + CommonPrefix.append(compressStack(Stack2Tail)); + return CommonPrefix; + } + + if (Stack2Tail.empty()) { + CommonPrefix.append(compressStack(Stack1Tail)); + return CommonPrefix; + } + + Stack Candidate; + for (const auto *Slot : concat(Stack1Tail, Stack2Tail)) + if (!is_contained(Candidate, Slot) && !Slot->isRematerializable() && + !isSpillReg(Slot)) + Candidate.push_back(Slot); + + auto evaluate = [&CommonPrefix, &Stack1, &Stack2, + this](const Stack &Candidate) { + Stack Test = CommonPrefix; + Test.append(Candidate); + return add(calculateStackTransformCost(Test, Stack1, + StackModel.stackDepthLimit()), + calculateStackTransformCost(Test, Stack2, + StackModel.stackDepthLimit())) + .value_or(std::numeric_limits::max()); + }; + + // This is a modified implementation of the Heap algorithm that does + // not restart I at 1 for each swap. + size_t N = Candidate.size(); + Stack BestCandidate = Candidate; + size_t BestCost = evaluate(Candidate); + + // Initialize counters for Heap's algorithm. + SmallVector C(N, 0); + size_t I = 1; + while (I < N) { + if (C[I] < I) { + if (I & 1) + std::swap(Candidate.front(), Candidate[I]); + else + std::swap(Candidate[C[I]], Candidate[I]); + + size_t Cost = evaluate(Candidate); + if (Cost < BestCost) { + BestCost = Cost; + BestCandidate = Candidate; + } + ++C[I]; + // Note: A proper implementation would reset I to 1 here, + // but doing so would generate all N! permutations, + ++I; + } else { + C[I] = 0; + ++I; + } + } + + CommonPrefix.append(BestCandidate); + return CommonPrefix; +} + +Stack EVMStackSolver::compressStack(Stack CurStack) { + auto ShouldRemove = + [&CurStack, this](SmallVector::reverse_iterator I) { + size_t RIdx = std::distance(CurStack.rbegin(), I); + if ((*I)->isRematerializable() || isSpillReg(*I)) + return true; + if (auto DistanceToCopy = + offset(make_range(std::next(I), CurStack.rend()), *I)) + return (RIdx + *DistanceToCopy < StackModel.stackDepthLimit()); + return false; + }; + for (auto I = CurStack.rbegin(); I != CurStack.rend();) { + if (ShouldRemove(I)) { + std::swap(*I, CurStack.back()); + ++I; /* In case I == rbegin(), pop_back() will invalidate it */ + CurStack.pop_back(); + continue; + } + ++I; + } + return CurStack; +} + +#ifndef NDEBUG +void EVMStackSolver::dump(raw_ostream &OS) { + OS << "Function: " << MF.getName() << "("; + for (const StackSlot *ParamSlot : StackModel.getFunctionParameters()) + OS << ParamSlot->toString(); + OS << ");\n"; + OS << "Entry MBB: " << getMBBId(&MF.front()) << ";\n"; + + for (const MachineBasicBlock &MBB : MF) + dumpMBB(OS, &MBB); +} + +void EVMStackSolver::dumpMBB(raw_ostream &OS, const MachineBasicBlock *MBB) { + OS << getMBBId(MBB) << ":\n"; + OS << '\t' << StackModel.getMBBEntryStack(MBB).toString() << '\n'; + for (const auto &MI : StackModel.instructionsToProcess(MBB)) { + const Stack &MIOutput = StackModel.getSlotsForInstructionDefs(&MI); + const Stack &MIInput = StackModel.getMIInput(MI); + if (isPushOrDupLikeMI(MI) && MIInput == MIOutput) + continue; + + OS << '\n'; + Stack MIEntry = StackModel.getInstEntryStack(&MI); + OS << '\t' << MIEntry.toString() << '\n'; + OS << '\t'; + MI.print(OS); + assert(MIInput.size() <= MIEntry.size()); + MIEntry.resize(MIEntry.size() - MIInput.size()); + MIEntry.append(MIOutput); + OS << '\t' << MIEntry.toString() << "\n"; + } + OS << '\n'; + OS << '\t' << StackModel.getMBBExitStack(MBB).toString() << "\n"; + + auto [BranchTy, TBB, FBB, BrInsts, Condition] = getBranchInfo(MBB); + switch (BranchTy) { + case EVMInstrInfo::BT_None: { + if (MBB->isReturnBlock()) { + OS << "Exit type: function return, " << MF.getName() << '\n'; + OS << "Return values: " + << StackModel.getReturnArguments(MBB->back()).toString() << '\n'; + } else { + OS << "Exit type: terminate\n"; + } + } break; + case EVMInstrInfo::BT_Uncond: + case EVMInstrInfo::BT_NoBranch: { + if (TBB) { + OS << "Exit type: unconditional branch\n"; + OS << "Target: " << getMBBId(TBB) << '\n'; + } else { + OS << "Exit type: terminate\n"; + } + } break; + case EVMInstrInfo::BT_Cond: + case EVMInstrInfo::BT_CondUncond: { + OS << "Exit type: conditional branch, "; + OS << "cond: " << StackModel.getStackSlot(*Condition)->toString() << '\n'; + OS << "False branch: " << getMBBId(FBB) << '\n'; + OS << "True branch: " << getMBBId(TBB) << '\n'; + } break; + } + OS << '\n'; +} +#endif // NDEBUG diff --git a/llvm/lib/Target/EVM/EVMStackSolver.h b/llvm/lib/Target/EVM/EVMStackSolver.h new file mode 100644 index 000000000000..fc19e4ee2bcc --- /dev/null +++ b/llvm/lib/Target/EVM/EVMStackSolver.h @@ -0,0 +1,137 @@ +//===--------- EVMStackSolver.h - Calculate stack states --------*- 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_LIB_TARGET_EVM_EVMSTACKSOLVER_H +#define LLVM_LIB_TARGET_EVM_EVMSTACKSOLVER_H + +#include "EVMInstrInfo.h" +#include "EVMStackModel.h" +#include "llvm/ADT/DenseMap.h" + +namespace llvm { + +class MachineLoopInfo; + +/// Compute the gas cost to transform \p Source into \p Target. +/// \note The copy of \p Source is intentional because the function modifies +/// it during computation. +std::optional calculateStackTransformCost(Stack Source, + const Stack &Target, + unsigned StackDepthLimit); + +using BranchInfoTy = + std::tuple, + std::optional>; + +BranchInfoTy getBranchInfo(const MachineBasicBlock *MBB); + +class EVMStackSolver { +public: + EVMStackSolver(MachineFunction &MF, EVMStackModel &StackModel, + const MachineLoopInfo *MLI, const VirtRegMap &VRM, + const MachineBlockFrequencyInfo &MBFI, LiveIntervals &LIS); + void run(); + +private: + /// Given \p MI's \p ExitStack, compute the entry stack so that after + /// executing the instruction the cost to transform to \p ExitStack is + /// minimal. + /// Side effect: the computed entry stack is stored in StackModel. + /// \param CompressStack: remove duplicates and rematerializable slots. + Stack propagateThroughMI(const Stack &ExitStack, const MachineInstr &MI, + bool CompressStack = false); + + /// Given \p ExitStack, compute the stack at the entry of \p MBB. + /// \param CompressStack: remove duplicates and rematerializable slots. + Stack propagateThroughMBB(const Stack &ExitStack, + const MachineBasicBlock *MBB, + bool CompressStack = false); + + // Build the exit stack of the given \p MI. + Stack getMIExitStack(const MachineInstr *MI) const; + + /// Main algorithm walking the graph from entry to exit and propagating stack + /// states back to the entries. Iteratively reruns itself along backward jumps + /// until the state is stabilized. + void runPropagation(); + +#ifndef NDEBUG + void dump(raw_ostream &OS); + void dumpMBB(raw_ostream &OS, const MachineBasicBlock *MBB); +#endif + + /// Compute a stack S that minimizes the number of permutations + /// needed to transform S into \p Stack1 and \p Stack2. + /// TODO: Compute weighted sum based on branch probabilities. + Stack combineStack(const Stack &Stack1, const Stack &Stack2); + + /// Returns a copy of \p Stack with duplicates and rematerializable + /// slots removed. + /// Used when stackification faces slot accessibility issues. + /// Otherwise, copies and rematerializable entities are kept on stack. + Stack compressStack(Stack Stack); + + // Manage StackModel. + void insertMBBEntryStack(const MachineBasicBlock *MBB, const Stack &S) { + StackModel.getMBBEntryMap()[MBB] = S; + } + void insertMBBExitStack(const MachineBasicBlock *MBB, const Stack &S) { + StackModel.getMBBExitMap()[MBB] = S; + } + void insertInstEntryStack(const MachineInstr *MI, const Stack &S) { + StackModel.getInstEntryMap()[MI] = S; + } + void insertMBBEntryStack(const MachineBasicBlock *MBB, Stack &&S) { + StackModel.getMBBEntryMap()[MBB] = std::move(S); + } + void insertMBBExitStack(const MachineBasicBlock *MBB, Stack &&S) { + StackModel.getMBBExitMap()[MBB] = std::move(S); + } + void insertInstEntryStack(const MachineInstr *MI, Stack &&S) { + StackModel.getInstEntryMap()[MI] = std::move(S); + } + + using UnreachableSlotVec = SmallVector>; + /// Get all unreachable slots in case of stack too deep issues. + UnreachableSlotVec getUnreachableSlots() const; + + /// Calculate spill weights. This is needed to determine which register to + /// spill when we have multiple spillable registers. + void calculateSpillWeights(); + + /// Return true if any definition of \p Reg is unreachable. This can happen + /// for function arguments and return values. + bool hasUnreachableDef(const Register &Reg) const; + + /// Return true if \p Slot is a rematerializable slot and it can be removed. + /// In cases of LiteralSlot, it checks if the literal is used before \p MI, + /// and if it is not, it can be removed to reduce stack pressure. In case it + /// is used, we don't remove it to reduce size of the stack, as PUSH + /// instructions are large. + bool shouldRemoveSlot(const StackSlot *Slot, const MachineInstr &MI) const; + + MachineFunction &MF; + EVMStackModel &StackModel; + const MachineLoopInfo *MLI; + const VirtRegMap &VRM; + const MachineBlockFrequencyInfo &MBFI; + LiveIntervals &LIS; + bool IsSpillWeightsCalculated = false; + /// In case of recursive functions, we can't use spills to fix stack too deep + /// issues, as we are using memory to spill and not real stack. If we run into + /// stack too deep issues for recursive functions, we will force compress + /// stack across the whole function to try to fix the issues. + bool ForceCompressStack = false; +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_EVMSTACKSOLVER_H diff --git a/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.cpp b/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.cpp new file mode 100644 index 000000000000..5fdb3e3b11c7 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.cpp @@ -0,0 +1,602 @@ +//===--- EVMStackifyCodeEmitter.h - Create stackified MIR -------*- 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 transforms MIR to the 'stackified' MIR. +// +//===----------------------------------------------------------------------===// + +#include "EVMStackifyCodeEmitter.h" +#include "EVMStackShuffler.h" +#include "EVMStackSolver.h" +#include "TargetInfo/EVMTargetInfo.h" +#include "llvm/CodeGen/LiveStacks.h" +#include "llvm/MC/MCContext.h" + +using namespace llvm; + +#define DEBUG_TYPE "evm-stackify-code-emitter" + +// Return the number of input arguments of the call instruction. +static size_t getCallNumArgs(const MachineInstr *Call) { + assert(Call->getOpcode() == EVM::FCALL && "Unexpected call instruction"); + assert(Call->explicit_uses().begin()->isGlobal() && + "First operand must be a function"); + size_t NumArgs = Call->getNumExplicitOperands() - Call->getNumExplicitDefs(); + // The first operand is a function, so don't count it. + NumArgs = NumArgs - 1; + // If function will return, we need to account for the return label. + return isNoReturnCallMI(*Call) ? NumArgs : NumArgs + 1; +} + +static std::string getUnreachableStackSlotError(const MachineFunction &MF, + const Stack &CurrentStack, + const StackSlot *Slot, + size_t Depth, bool isSwap) { + return (MF.getName() + Twine(": cannot ") + (isSwap ? "swap " : "dup ") + + std::to_string(Depth) + "-th stack item, " + Slot->toString() + + ".\nItem it located too deep in the stack: " + + CurrentStack.toString()) + .str(); +} + +size_t EVMStackifyCodeEmitter::CodeEmitter::stackHeight() const { + return StackHeight; +} + +void EVMStackifyCodeEmitter::CodeEmitter::enterMBB(MachineBasicBlock *MBB, + int Height) { + StackHeight = Height; + CurMBB = MBB; + LLVM_DEBUG(dbgs() << "\n" + << "Set stack height: " << StackHeight << "\n"); + LLVM_DEBUG(dbgs() << "Setting current location to: " << MBB->getNumber() + << "." << MBB->getName() << "\n"); +} + +void EVMStackifyCodeEmitter::CodeEmitter::emitInst(const MachineInstr *MI) { + unsigned Opc = MI->getOpcode(); + assert(Opc != EVM::JUMP && Opc != EVM::JUMPI && Opc != EVM::JUMP_UNLESS && + Opc != EVM::ARGUMENT && Opc != EVM::RET && Opc != EVM::CONST_I256 && + Opc != EVM::COPY_I256 && Opc != EVM::FCALL && + "Unexpected instruction"); + + size_t NumInputs = MI->getNumExplicitOperands() - MI->getNumExplicitDefs(); + assert(StackHeight >= NumInputs && "Not enough operands on the stack"); + StackHeight -= NumInputs; + StackHeight += MI->getNumExplicitDefs(); + + auto NewMI = BuildMI(*CurMBB, CurMBB->end(), MI->getDebugLoc(), + TII->get(EVM::getStackOpcode(Opc))); + verify(NewMI); +} + +void EVMStackifyCodeEmitter::CodeEmitter::emitSWAP(unsigned Depth) { + assert(StackHeight >= (Depth + 1) && + "Not enough operands on the stack for SWAP"); + unsigned Opc = EVM::getSWAPOpcode(Depth); + auto NewMI = BuildMI(*CurMBB, CurMBB->end(), DebugLoc(), + TII->get(EVM::getStackOpcode(Opc))); + verify(NewMI); +} + +void EVMStackifyCodeEmitter::CodeEmitter::emitDUP(unsigned Depth) { + assert(StackHeight >= Depth && "Not enough operands on the stack for DUP"); + StackHeight += 1; + unsigned Opc = EVM::getDUPOpcode(Depth); + auto NewMI = BuildMI(*CurMBB, CurMBB->end(), DebugLoc(), + TII->get(EVM::getStackOpcode(Opc))); + verify(NewMI); +} + +void EVMStackifyCodeEmitter::CodeEmitter::emitPOP() { + assert(StackHeight > 0 && "Expected at least one operand on the stack"); + StackHeight -= 1; + auto NewMI = + BuildMI(*CurMBB, CurMBB->end(), DebugLoc(), TII->get(EVM::POP_S)); + verify(NewMI); +} + +void EVMStackifyCodeEmitter::CodeEmitter::emitConstant(const APInt &Val) { + StackHeight += 1; + unsigned Opc = EVM::getPUSHOpcode(Val); + auto NewMI = BuildMI(*CurMBB, CurMBB->end(), DebugLoc(), + TII->get(EVM::getStackOpcode(Opc))); + if (Opc != EVM::PUSH0) + NewMI.addCImm(ConstantInt::get(MF.getFunction().getContext(), Val)); + verify(NewMI); +} + +void EVMStackifyCodeEmitter::CodeEmitter::emitConstant(uint64_t Val) { + emitConstant(APInt(256, Val)); +} + +void EVMStackifyCodeEmitter::CodeEmitter::emitSymbol(const MachineInstr *MI, + MCSymbol *Symbol) { + assert((isLinkerPseudoMI(*MI) || MI->getOpcode() == EVM::CODECOPY) && + "Unexpected symbol instruction"); + + StackHeight += 1; + unsigned Opc = isLinkerPseudoMI(*MI) ? EVM::getStackOpcode(MI->getOpcode()) + : EVM::PUSH_LABEL; + auto NewMI = BuildMI(*CurMBB, CurMBB->end(), MI->getDebugLoc(), TII->get(Opc)) + .addSym(Symbol); + verify(NewMI); +} + +void EVMStackifyCodeEmitter::CodeEmitter::emitLabelReference( + const MachineInstr *Call) { + assert(Call->getOpcode() == EVM::FCALL && "Unexpected call instruction"); + StackHeight += 1; + auto [It, Inserted] = CallReturnSyms.try_emplace(Call); + if (Inserted) + It->second = MF.getContext().createTempSymbol("FUNC_RET", true); + auto NewMI = + BuildMI(*CurMBB, CurMBB->end(), DebugLoc(), TII->get(EVM::PUSH_LABEL)) + .addSym(It->second); + verify(NewMI); +} + +void EVMStackifyCodeEmitter::CodeEmitter::emitFuncCall(const MachineInstr *MI) { + assert(MI->getOpcode() == EVM::FCALL && "Unexpected call instruction"); + assert(CurMBB == MI->getParent()); + + size_t NumInputs = getCallNumArgs(MI); + assert(StackHeight >= NumInputs && "Not enough operands on the stack"); + StackHeight -= NumInputs; + + // PUSH_LABEL increases the stack height on 1, but we don't increase it + // explicitly here, as the label will be consumed by the following JUMP. + StackHeight += MI->getNumExplicitDefs(); + + // Create pseudo jump to the function, that will be expanded into PUSH and + // JUMP instructions in the AsmPrinter. + auto NewMI = BuildMI(*CurMBB, CurMBB->end(), MI->getDebugLoc(), + TII->get(EVM::PseudoCALL)) + .addGlobalAddress(MI->explicit_uses().begin()->getGlobal()); + + // If this function returns, add a return label so we can emit it together + // with JUMPDEST. This is taken care in the AsmPrinter. + if (!isNoReturnCallMI(*MI)) + NewMI.addSym(CallReturnSyms.at(MI)); + verify(NewMI); +} + +void EVMStackifyCodeEmitter::CodeEmitter::emitRet(const MachineInstr *MI) { + assert(MI->getOpcode() == EVM::RET && "Unexpected ret instruction"); + auto NewMI = BuildMI(*CurMBB, CurMBB->end(), MI->getDebugLoc(), + TII->get(EVM::PseudoRET)); + verify(NewMI); +} + +void EVMStackifyCodeEmitter::CodeEmitter::emitUncondJump( + const MachineInstr *MI, MachineBasicBlock *Target) { + assert(MI->getOpcode() == EVM::JUMP && + "Unexpected unconditional jump instruction"); + auto NewMI = BuildMI(*CurMBB, CurMBB->end(), MI->getDebugLoc(), + TII->get(EVM::PseudoJUMP)) + .addMBB(Target); + verify(NewMI); +} + +void EVMStackifyCodeEmitter::CodeEmitter::emitCondJump( + const MachineInstr *MI, MachineBasicBlock *Target) { + assert(MI->getOpcode() == EVM::JUMPI || + MI->getOpcode() == EVM::JUMP_UNLESS && + "Unexpected conditional jump instruction"); + assert(StackHeight > 0 && "Expected at least one operand on the stack"); + StackHeight -= 1; + auto NewMI = + BuildMI(*CurMBB, CurMBB->end(), MI->getDebugLoc(), + TII->get(MI->getOpcode() == EVM::JUMPI ? EVM::PseudoJUMPI + : EVM::PseudoJUMP_UNLESS)) + .addMBB(Target); + verify(NewMI); +} + +void EVMStackifyCodeEmitter::CodeEmitter::emitReload(Register Reg) { + StackHeight += 1; + auto NewMI = + BuildMI(*CurMBB, CurMBB->end(), DebugLoc(), TII->get(EVM::PUSH_FRAME)) + .addFrameIndex(getStackSlot(Reg)); + verify(NewMI); + NewMI = BuildMI(*CurMBB, CurMBB->end(), DebugLoc(), TII->get(EVM::MLOAD_S)); + NewMI->setAsmPrinterFlag(MachineInstr::ReloadReuse); + verify(NewMI); +} + +void EVMStackifyCodeEmitter::CodeEmitter::emitSpill(Register Reg, + unsigned DupIdx) { + if (DupIdx == 0) { + assert(StackHeight > 0 && "Expected at least one operand on the stack"); + + // Reduce height if we are not going to duplicate the register. + // In this case, register will be used by the MSTORE instruction + // that is used for spilling. + StackHeight -= 1; + } else { + assert(StackHeight >= DupIdx && + "Not enough operands on the stack for DUP while spilling"); + + // Register is used after spill, so we need to duplicate it. + emitDUP(DupIdx); + + // Since we are going to spill the register, stack height doesn't + // change, so we need to reduce it by 1, as emitDUP increases + // the stack height by that amount. + StackHeight -= 1; + } + + auto NewMI = + BuildMI(*CurMBB, CurMBB->end(), DebugLoc(), TII->get(EVM::PUSH_FRAME)) + .addFrameIndex(getStackSlot(Reg)); + verify(NewMI); + NewMI = BuildMI(*CurMBB, CurMBB->end(), DebugLoc(), TII->get(EVM::MSTORE_S)); + NewMI->setAsmPrinterFlag(MachineInstr::ReloadReuse); + verify(NewMI); +} + +int EVMStackifyCodeEmitter::CodeEmitter::getStackSlot(Register Reg) { + int StackSlot = VRM.getStackSlot(Reg); + if (StackSlot != VirtRegMap::NO_STACK_SLOT) + return StackSlot; + + // Generate a new stack slot for the register and add register's live interval + // to the stack slot. This is the same thing what InlineSpiller does, and this + // is needed to StackSlotColoring afterwards to reduce the stack area. + StackSlot = VRM.assignVirt2StackSlot(Reg); + auto &StackInt = + LSS.getOrCreateInterval(StackSlot, MF.getRegInfo().getRegClass(Reg)); + StackInt.getNextValue(SlotIndex(), LSS.getVNInfoAllocator()); + assert(StackInt.getNumValNums() == 1 && "Bad stack interval values"); + StackInt.MergeSegmentsInAsValue(LIS.getInterval(Reg), + StackInt.getValNumInfo(0)); + return StackSlot; +} + +// Verify that a stackified instruction doesn't have registers and dump it. +void EVMStackifyCodeEmitter::CodeEmitter::verify(const MachineInstr *MI) const { + assert(EVMInstrInfo::isStack(MI) && + "Only stackified instructions are allowed"); + assert(all_of(MI->operands(), + [](const MachineOperand &MO) { return !MO.isReg(); }) && + "Registers are not allowed in stackified instructions"); + + LLVM_DEBUG(dbgs() << "Adding: " << *MI << "stack height: " << StackHeight + << "\n"); +} +void EVMStackifyCodeEmitter::CodeEmitter::finalize() { + for (MachineBasicBlock &MBB : MF) + for (MachineInstr &MI : make_early_inc_range(MBB)) + // Remove all the instructions that are not stackified. + // TODO: #749: Fix debug info for stackified instructions and don't + // remove debug instructions. + if (!EVMInstrInfo::isStack(&MI)) + MI.eraseFromParent(); +} + +void EVMStackifyCodeEmitter::adjustStackForInst(const MachineInstr *MI, + size_t NumArgs) { + // Remove arguments from CurrentStack. + CurrentStack.erase(CurrentStack.end() - NumArgs, CurrentStack.end()); + + // Push return values to CurrentStack. + append_range(CurrentStack, StackModel.getSlotsForInstructionDefs(MI)); + assert(Emitter.stackHeight() == CurrentStack.size()); +} + +void EVMStackifyCodeEmitter::emitMI(const MachineInstr &MI) { + assert(Emitter.stackHeight() == CurrentStack.size()); + + if (MI.getOpcode() == EVM::FCALL) { + size_t NumArgs = getCallNumArgs(&MI); + assert(CurrentStack.size() >= NumArgs); + + // Assert that we got the correct return label on stack. + if (!isNoReturnCallMI(MI)) { + [[maybe_unused]] const auto *ReturnLabelSlot = dyn_cast( + CurrentStack[CurrentStack.size() - NumArgs]); + assert(ReturnLabelSlot && ReturnLabelSlot->getCall() == &MI); + } + Emitter.emitFuncCall(&MI); + adjustStackForInst(&MI, NumArgs); + } else if (!isPushOrDupLikeMI(MI)) { + size_t NumArgs = MI.getNumExplicitOperands() - MI.getNumExplicitDefs(); + assert(CurrentStack.size() >= NumArgs); + // TODO: assert that we got a correct stack for the call. + + Emitter.emitInst(&MI); + adjustStackForInst(&MI, NumArgs); + } + + // If the MI doesn't define anything, we are done. + if (!MI.getNumExplicitDefs()) + return; + + // Invalidate occurrences of the assigned variables. + for (auto *&CurrentSlot : CurrentStack) + if (const auto *RegSlot = dyn_cast(CurrentSlot)) + if (MI.definesRegister(RegSlot->getReg(), /*TRI=*/nullptr)) + CurrentSlot = EVMStackModel::getUnusedSlot(); + + // Assign variables to current stack top. + assert(CurrentStack.size() >= MI.getNumExplicitDefs()); + llvm::copy(StackModel.getSlotsForInstructionDefs(&MI), + CurrentStack.end() - MI.getNumExplicitDefs()); +} + +void EVMStackifyCodeEmitter::emitSpills(const MachineBasicBlock &MBB, + MachineBasicBlock::const_iterator Start, + const Stack &Defs) { + // Check if we have any spillable registers. + if (find_if(Defs, [](const StackSlot *Slot) { return isSpillReg(Slot); }) == + Defs.end()) + return; + + // In case of a single definition, we can remove it from the stack + // if it is not used after spill. + if (Defs.size() == 1) { + // Find the first instruction from which we can get entry stack. + while (Start != MBB.end() && StackModel.skipMI(*Start)) + ++Start; + + // Find the next target stack, as we need to check if the register + // is used after spill. + const Stack &NextTargetStack = + Start != MBB.end() && !EVMInstrInfo::isStack(&*Start) + ? StackModel.getInstEntryStack(&*Start) + : StackModel.getMBBExitStack(&MBB); + const auto *RegSlot = cast(Defs[0]); + + // Find if if the register is used after spill. If it is we need to + // emit DUP instruction to keep it on the stack. + bool UsedAfter = is_contained(NextTargetStack, RegSlot); + Emitter.emitSpill(RegSlot->getReg(), UsedAfter); + + // Remove the register from the current stack, if it is not used + // after spill. + if (!UsedAfter) + CurrentStack.pop_back(); + } else { + // TODO: In case definition are not used after spill, we can + // remove them from the current stack, and not emit DUP. This + // is more complex when we have multiple definitions, as we + // need to do stack manipulation to keep the stack in sync + // with the target stack. + for (auto [DefIdx, Def] : enumerate(reverse(Defs))) + if (isSpillReg(Def)) + Emitter.emitSpill(cast(Def)->getReg(), DefIdx + 1); + } + assert(Emitter.stackHeight() == CurrentStack.size()); +} + +// Checks if it's valid to transition from \p SourceStack to \p TargetStack, +// that is \p SourceStack matches each slot in \p TargetStack that is not a +// UnusedSlot exactly. +[[maybe_unused]] static bool match(const Stack &Source, const Stack &Target) { + return Source.size() == Target.size() && + all_of(zip_equal(Source, Target), [](const auto &Pair) { + const auto [Src, Tgt] = Pair; + return isa(Tgt) || (Src == Tgt); + }); +} + +void EVMStackifyCodeEmitter::emitStackPermutations(const Stack &TargetStack) { + assert(Emitter.stackHeight() == CurrentStack.size()); + const unsigned StackDepthLimit = StackModel.stackDepthLimit(); + + calculateStack( + CurrentStack, TargetStack, StackDepthLimit, + // Swap. + [&](unsigned I) { + assert(CurrentStack.size() == Emitter.stackHeight()); + assert(I > 0 && I < CurrentStack.size()); + if (I <= StackDepthLimit) { + Emitter.emitSWAP(I); + return; + } + const StackSlot *Slot = CurrentStack[CurrentStack.size() - I - 1]; + std::string ErrMsg = getUnreachableStackSlotError( + MF, CurrentStack, Slot, I + 1, /* isSwap */ true); + report_fatal_error(ErrMsg.c_str()); + }, + // Push or dup. + [&](const StackSlot *Slot) { + assert(CurrentStack.size() == Emitter.stackHeight()); + + // Prefer to emit PUSH0 instead of DUP, as it is cheaper. + if (isa(Slot) && + cast(Slot)->getValue().isZero()) { + Emitter.emitConstant(0); + return; + } + + // Dup the slot, if already on stack and reachable. + auto SlotIt = llvm::find(llvm::reverse(CurrentStack), Slot); + if (SlotIt != CurrentStack.rend()) { + unsigned Depth = std::distance(CurrentStack.rbegin(), SlotIt); + if (Depth < StackDepthLimit) { + Emitter.emitDUP(static_cast(Depth + 1)); + return; + } + if (!Slot->isRematerializable() && !isSpillReg(Slot)) { + std::string ErrMsg = getUnreachableStackSlotError( + MF, CurrentStack, Slot, Depth + 1, /* isSwap */ false); + report_fatal_error(ErrMsg.c_str()); + } + } + + // Rematerialize the slot. + assert(Slot->isRematerializable() || isSpillReg(Slot)); + if (const auto *L = dyn_cast(Slot)) { + Emitter.emitConstant(L->getValue()); + } else if (const auto *S = dyn_cast(Slot)) { + Emitter.emitSymbol(S->getMachineInstr(), S->getSymbol()); + } else if (const auto *CallRet = dyn_cast(Slot)) { + Emitter.emitLabelReference(CallRet->getCall()); + } else if (const auto *Spill = dyn_cast(Slot)) { + Emitter.emitReload(Spill->getReg()); + } else { + assert(isa(Slot)); + // Note: this will always be popped, so we can push anything. + Emitter.emitConstant(0); + } + }, + // Pop. + [&]() { Emitter.emitPOP(); }); + + assert(Emitter.stackHeight() == CurrentStack.size()); +} + +// Emit the stack required for enterting the MI. +void EVMStackifyCodeEmitter::emitMIEntryStack(const MachineInstr &MI) { + // Check if we can choose cheaper stack shuffling if the MI is commutable. + const Stack &TargetStack = StackModel.getInstEntryStack(&MI); + bool SwapCommutable = false; + if (MI.isCommutable()) { + assert(TargetStack.size() > 1); + + size_t DefaultCost = + calculateStackTransformCost(CurrentStack, TargetStack, + StackModel.stackDepthLimit()) + .value_or(std::numeric_limits::max()); + + // Swap the commutable stack items and measure the stack shuffling cost. + // Commutable operands always take top two stack slots. + Stack CommutedTargetStack = TargetStack; + std::swap(CommutedTargetStack[CommutedTargetStack.size() - 1], + CommutedTargetStack[CommutedTargetStack.size() - 2]); + size_t CommutedCost = + calculateStackTransformCost(CurrentStack, CommutedTargetStack, + StackModel.stackDepthLimit()) + .value_or(std::numeric_limits::max()); + + // Choose the cheapest transformation. + SwapCommutable = CommutedCost < DefaultCost; + emitStackPermutations(SwapCommutable ? CommutedTargetStack : TargetStack); + } else { + emitStackPermutations(TargetStack); + } + +#ifndef NDEBUG + // Assert that we have the inputs of the MI on stack top. + const Stack &SavedInput = StackModel.getMIInput(MI); + assert(CurrentStack.size() == Emitter.stackHeight()); + assert(CurrentStack.size() >= SavedInput.size()); + Stack Input(CurrentStack.end() - SavedInput.size(), CurrentStack.end()); + + // Adjust the Input if needed. + if (SwapCommutable) + std::swap(Input[Input.size() - 1], Input[Input.size() - 2]); + + assert(match(Input, SavedInput)); +#endif // NDEBUG +} + +void EVMStackifyCodeEmitter::run() { + assert(CurrentStack.empty() && Emitter.stackHeight() == 0); + + SmallPtrSet Visited; + SmallVector WorkList{&MF.front()}; + while (!WorkList.empty()) { + auto *MBB = WorkList.pop_back_val(); + if (!Visited.insert(MBB).second) + continue; + + CurrentStack = StackModel.getMBBEntryStack(MBB); + Emitter.enterMBB(MBB, CurrentStack.size()); + + // Get branch information before we start to change the BB. + auto [BranchTy, TBB, FBB, BrInsts, Condition] = getBranchInfo(MBB); + bool HasReturn = MBB->isReturnBlock(); + const MachineInstr *ReturnMI = HasReturn ? &MBB->back() : nullptr; + + // Emit the spills for the arguments in the entry block, if needed. + if (MBB == &MF.front()) + emitSpills(*MBB, MBB->begin(), StackModel.getMBBEntryStack(MBB)); + + for (const auto &MI : StackModel.instructionsToProcess(MBB)) { + // We are done if the MI is in the stack form. + if (EVMInstrInfo::isStack(&MI)) + break; + + emitMIEntryStack(MI); + + [[maybe_unused]] size_t BaseHeight = + CurrentStack.size() - StackModel.getMIInput(MI).size(); + + emitMI(MI); + +#ifndef NDEBUG + // Assert that the MI produced its proclaimed output. + size_t NumDefs = MI.getNumExplicitDefs(); + size_t StackSize = CurrentStack.size(); + assert(StackSize == Emitter.stackHeight()); + assert(StackSize == BaseHeight + NumDefs); + assert(StackSize >= NumDefs); + // Check that the top NumDefs slots are the MI defs. + for (size_t I = StackSize - NumDefs; I < StackSize; ++I) + assert(MI.definesRegister(cast(CurrentStack[I])->getReg(), + /*TRI=*/nullptr)); +#endif // NDEBUG + + // Emit spills for the instruction definitions, if needed. + emitSpills(*MBB, std::next(MI.getIterator()), + StackModel.getSlotsForInstructionDefs(&MI)); + } + + // Exit the block. + if (BranchTy == EVMInstrInfo::BT_None) { + if (HasReturn) { + assert(!MF.getFunction().hasFnAttribute(Attribute::NoReturn)); + assert(StackModel.getReturnArguments(*ReturnMI) == + StackModel.getMBBExitStack(MBB)); + // Create the function return stack and jump. + emitStackPermutations(StackModel.getMBBExitStack(MBB)); + Emitter.emitRet(ReturnMI); + } + } else if (BranchTy == EVMInstrInfo::BT_Uncond || + BranchTy == EVMInstrInfo::BT_NoBranch) { + if (!MBB->succ_empty()) { + // Create the stack expected at the jump target. + emitStackPermutations(StackModel.getMBBEntryStack(TBB)); + assert(match(CurrentStack, StackModel.getMBBEntryStack(TBB))); + + if (!BrInsts.empty()) + Emitter.emitUncondJump(BrInsts[0], TBB); + + WorkList.push_back(TBB); + } + } else { + assert(BranchTy == EVMInstrInfo::BT_Cond || + BranchTy == EVMInstrInfo::BT_CondUncond); + // Create the shared entry stack of the jump targets, which is + // stored as exit stack of the current MBB. + emitStackPermutations(StackModel.getMBBExitStack(MBB)); + assert(!CurrentStack.empty() && + CurrentStack.back() == StackModel.getStackSlot(*Condition)); + + // Emit the conditional jump to the non-zero label and update the + // stored stack. + assert(!BrInsts.empty()); + Emitter.emitCondJump(BrInsts[BrInsts.size() - 1], TBB); + CurrentStack.pop_back(); + + // Assert that we have a valid stack for both jump targets. + assert(match(CurrentStack, StackModel.getMBBEntryStack(TBB))); + assert(match(CurrentStack, StackModel.getMBBEntryStack(FBB))); + + // Generate unconditional jump if needed. + if (BrInsts.size() == 2) + Emitter.emitUncondJump(BrInsts[0], FBB); + + WorkList.push_back(TBB); + WorkList.push_back(FBB); + } + } + Emitter.finalize(); +} diff --git a/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.h b/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.h new file mode 100644 index 000000000000..532e0e5b99e1 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.h @@ -0,0 +1,99 @@ +//===--- EVMStackifyCodeEmitter.h - Create stackified MIR ------*- 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 transforms MIR to the 'stackified' MIR. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVMSTACKIFYCODEEMITTER_H +#define LLVM_LIB_TARGET_EVM_EVMSTACKIFYCODEEMITTER_H + +#include "EVMStackModel.h" +#include "EVMSubtarget.h" + +namespace llvm { + +class MachineInstr; +class MCSymbol; +class LiveStacks; + +class EVMStackifyCodeEmitter { +public: + EVMStackifyCodeEmitter(const EVMStackModel &StackModel, MachineFunction &MF, + VirtRegMap &VRM, LiveStacks &LSS, LiveIntervals &LIS) + : Emitter(MF, VRM, LSS, LIS), StackModel(StackModel), MF(MF) {} + + /// Stackify instructions, starting from the first MF's MBB. + void run(); + +private: + class CodeEmitter { + public: + explicit CodeEmitter(MachineFunction &MF, VirtRegMap &VRM, LiveStacks &LSS, + const LiveIntervals &LIS) + : MF(MF), VRM(VRM), LSS(LSS), LIS(LIS), + TII(MF.getSubtarget().getInstrInfo()) {} + size_t stackHeight() const; + void enterMBB(MachineBasicBlock *MBB, int Height); + void emitInst(const MachineInstr *MI); + void emitSWAP(unsigned Depth); + void emitDUP(unsigned Depth); + void emitPOP(); + void emitConstant(const APInt &Val); + void emitConstant(uint64_t Val); + void emitSymbol(const MachineInstr *MI, MCSymbol *Symbol); + void emitFuncCall(const MachineInstr *MI); + void emitRet(const MachineInstr *MI); + void emitCondJump(const MachineInstr *MI, MachineBasicBlock *Target); + void emitUncondJump(const MachineInstr *MI, MachineBasicBlock *Target); + void emitLabelReference(const MachineInstr *Call); + void emitReload(Register Reg); + void emitSpill(Register Reg, unsigned DupIdx); + /// Remove all the instructions that are not in stack form. + void finalize(); + + private: + MachineFunction &MF; + VirtRegMap &VRM; + LiveStacks &LSS; + const LiveIntervals &LIS; + const EVMInstrInfo *TII; + size_t StackHeight = 0; + MachineBasicBlock *CurMBB = nullptr; + DenseMap CallReturnSyms; + + void verify(const MachineInstr *MI) const; + int getStackSlot(Register Reg); + }; + + CodeEmitter Emitter; + const EVMStackModel &StackModel; + MachineFunction &MF; + Stack CurrentStack; + + /// Emit stack operations to turn CurrentStack into \p TargetStack. + void emitStackPermutations(const Stack &TargetStack); + + /// Creates the MI's entry stack from the 'CurrentStack' taking into + /// account commutative property of the instruction. + void emitMIEntryStack(const MachineInstr &MI); + + /// Remove the arguments from the stack and push the return values. + void adjustStackForInst(const MachineInstr *MI, size_t NumArgs); + + /// Generate code for the instruction. + void emitMI(const MachineInstr &MI); + + /// Emit spill instructions for the \p Defs, if needed. + void emitSpills(const MachineBasicBlock &MBB, + MachineBasicBlock::const_iterator Start, const Stack &Defs); +}; + +} // namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_EVMSTACKIFYCODEEMITTER_H diff --git a/llvm/lib/Target/EVM/EVMSubtarget.cpp b/llvm/lib/Target/EVM/EVMSubtarget.cpp new file mode 100644 index 000000000000..40f3917c6813 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMSubtarget.cpp @@ -0,0 +1,27 @@ +//===-------- EVMSubtarget.cpp - EVM Subtarget Information ---*- 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 EVM specific subclass of TargetSubtargetInfo. +// +//===----------------------------------------------------------------------===// + +#include "EVMSubtarget.h" + +using namespace llvm; + +#define DEBUG_TYPE "evm-subtarget" + +#define GET_SUBTARGETINFO_TARGET_DESC +#define GET_SUBTARGETINFO_CTOR +#include "EVMGenSubtargetInfo.inc" + +EVMSubtarget::EVMSubtarget(const Triple &TT, const std::string &CPU, + const std::string &FS, const TargetMachine &TM) + : EVMGenSubtargetInfo(TT, CPU, /*TuneCPU*/ CPU, FS), TLInfo(TM, *this) {} + +bool EVMSubtarget::useAA() const { return true; } diff --git a/llvm/lib/Target/EVM/EVMSubtarget.h b/llvm/lib/Target/EVM/EVMSubtarget.h new file mode 100644 index 000000000000..4b0a33799e96 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMSubtarget.h @@ -0,0 +1,63 @@ +//===------ EVMSubtarget.h - Define Subtarget for the EVM ----*- 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 declares the EVM specific subclass of TargetSubtargetInfo. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVMSUBTARGET_H +#define LLVM_LIB_TARGET_EVM_EVMSUBTARGET_H + +#include "EVMFrameLowering.h" +#include "EVMISelLowering.h" +#include "EVMInstrInfo.h" +#include "EVMRegisterInfo.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" + +#define GET_SUBTARGETINFO_ENUM +#define GET_SUBTARGETINFO_HEADER +#include "EVMGenSubtargetInfo.inc" + +namespace llvm { +class StringRef; + +class EVMSubtarget final : public EVMGenSubtargetInfo { +private: + EVMFrameLowering FrameLowering; + EVMInstrInfo InstrInfo; + EVMTargetLowering TLInfo; + +public: + // This constructor initializes the data members to match that + // of the specified triple. + EVMSubtarget(const Triple &TT, const std::string &CPU, const std::string &FS, + const TargetMachine &TM); + + /// ParseSubtargetFeatures - Parses features string setting specified + /// subtarget options. Definition of function is auto generated by tblgen. + void ParseSubtargetFeatures(StringRef CPU, StringRef TuneCPU, StringRef FS); + + const TargetFrameLowering *getFrameLowering() const override { + return &FrameLowering; + } + const EVMInstrInfo *getInstrInfo() const override { return &InstrInfo; } + + const EVMRegisterInfo *getRegisterInfo() const override { + return &InstrInfo.getRegisterInfo(); + } + const EVMTargetLowering *getTargetLowering() const override { + return &TLInfo; + } + + bool useAA() const override; + + unsigned stackDepthLimit() const { return 16; } +}; +} // namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_EVMSUBTARGET_H diff --git a/llvm/lib/Target/EVM/EVMTargetMachine.cpp b/llvm/lib/Target/EVM/EVMTargetMachine.cpp new file mode 100644 index 000000000000..16a512d1dcd4 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMTargetMachine.cpp @@ -0,0 +1,293 @@ +//===------ EVMTargetMachine.cpp - Define TargetMachine for EVM -*- 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 +// +//===----------------------------------------------------------------------===// +// +// Top-level implementation for the EVM target. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" + +#include "EVMAliasAnalysis.h" +#include "EVMMachineFunctionInfo.h" +#include "EVMTargetMachine.h" +#include "EVMTargetObjectFile.h" +#include "EVMTargetTransformInfo.h" +#include "TargetInfo/EVMTargetInfo.h" +#include "llvm/CodeGen/MIRParser/MIParser.h" +#include "llvm/CodeGen/MIRYamlMapping.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/CodeGen/TargetPassConfig.h" +#include "llvm/InitializePasses.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/Transforms/IPO/GlobalDCE.h" +#include "llvm/Transforms/Scalar/MergeIdenticalBB.h" +#include "llvm/Transforms/Utils.h" + +using namespace llvm; + +// Disable stackification pass and keep a register form +// form of instructions. Is only used for debug purposes +// when assembly printing. +cl::opt + EVMKeepRegisters("evm-keep-registers", cl::Hidden, + cl::desc("EVM: output stack registers in" + " instruction output for test purposes only."), + cl::init(false)); + +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeEVMTarget() { + // Register the target. + const RegisterTargetMachine X(getTheEVMTarget()); + auto &PR = *PassRegistry::getPassRegistry(); + initializeEVMCodegenPreparePass(PR); + initializeEVMAllocaHoistingPass(PR); + initializeEVMLinkRuntimePass(PR); + initializeEVMLowerIntrinsicsPass(PR); + initializeEVMOptimizeLiveIntervalsPass(PR); + initializeEVMPeepholePass(PR); + initializeEVMSingleUseExpressionPass(PR); + initializeEVMSplitCriticalEdgesPass(PR); + initializeEVMBPStackificationPass(PR); + initializeEVMAAWrapperPassPass(PR); + initializeEVMExternalAAWrapperPass(PR); + initializeEVMAlwaysInlinePass(PR); + initializeEVMLowerJumpUnlessPass(PR); + initializeEVMFinalizeStackFramesPass(PR); + initializeEVMMarkRecursiveFunctionsPass(PR); + initializeEVMConstantUnfoldingPass(PR); +} + +static std::string computeDataLayout() { + return "E-p:256:256-i256:256:256-S256-a:256:256"; +} + +static Reloc::Model getEffectiveRelocModel(std::optional RM) { + if (!RM) + return Reloc::Static; + return *RM; +} + +EVMTargetMachine::EVMTargetMachine(const Target &T, const Triple &TT, + StringRef CPU, StringRef FS, + const TargetOptions &Options, + std::optional RM, + std::optional CM, + CodeGenOptLevel OL, bool JIT) + : LLVMTargetMachine(T, computeDataLayout(), TT, CPU, FS, Options, + getEffectiveRelocModel(RM), + getEffectiveCodeModel(CM, CodeModel::Small), OL), + TLOF(std::make_unique()), + Subtarget(TT, std::string(CPU), std::string(FS), *this) { + initAsmInfo(); +} + +TargetTransformInfo +EVMTargetMachine::getTargetTransformInfo(const Function &F) const { + return TargetTransformInfo(EVMTTIImpl(this, F)); +} + +MachineFunctionInfo *EVMTargetMachine::createMachineFunctionInfo( + BumpPtrAllocator &Allocator, const Function &F, + const TargetSubtargetInfo *STI) const { + return EVMMachineFunctionInfo::create(Allocator, F, + STI); +} + +yaml::MachineFunctionInfo *EVMTargetMachine::createDefaultFuncInfoYAML() const { + return new yaml::EVMMachineFunctionInfo(); +} + +yaml::MachineFunctionInfo * +EVMTargetMachine::convertFuncInfoToYAML(const MachineFunction &MF) const { + const auto *MFI = MF.getInfo(); + return new yaml::EVMMachineFunctionInfo(*MFI); +} + +bool EVMTargetMachine::parseMachineFunctionInfo( + const yaml::MachineFunctionInfo &MFI, PerFunctionMIParsingState &PFS, + SMDiagnostic &Error, SMRange &SourceRange) const { + const auto &YamlMFI = static_cast(MFI); + PFS.MF.getInfo()->initializeBaseYamlFields(YamlMFI); + return false; +} + +void EVMTargetMachine::registerEarlyDefaultAliasAnalyses(AAManager &AAM) { + AAM.registerFunctionAnalysis(); +} + +void EVMTargetMachine::registerPassBuilderCallbacks(PassBuilder &PB) { + PB.registerPipelineStartEPCallback( + [](ModulePassManager &PM, OptimizationLevel Level) { + if (Level != OptimizationLevel::O0) + PM.addPass(EVMAlwaysInlinePass()); + PM.addPass(EVMLinkRuntimePass()); + PM.addPass(GlobalDCEPass()); + PM.addPass(createModuleToFunctionPassAdaptor(EVMAllocaHoistingPass())); + }); + PB.registerScalarOptimizerLateEPCallback( + [](FunctionPassManager &PM, OptimizationLevel Level) { + if (Level.getSizeLevel() || Level.getSpeedupLevel() > 1) + PM.addPass(MergeIdenticalBBPass()); + if (Level.isOptimizingForSpeed()) + PM.addPass(EVMSHA3ConstFoldingPass()); + }); + + PB.registerAnalysisRegistrationCallback([](FunctionAnalysisManager &FAM) { + FAM.registerPass([] { return EVMAA(); }); + }); + + PB.registerPipelineParsingCallback( + [](StringRef PassName, ModulePassManager &PM, + ArrayRef) { + if (PassName == "evm-mark-recursive-functions") { + PM.addPass(EVMMarkRecursiveFunctionsPass()); + return true; + } + if (PassName == "evm-always-inline") { + PM.addPass(EVMAlwaysInlinePass()); + return true; + } + return false; + }); + + PB.registerPipelineParsingCallback( + [](StringRef PassName, FunctionPassManager &PM, + ArrayRef) { + if (PassName == "evm-sha3-constant-folding") { + PM.addPass(EVMSHA3ConstFoldingPass()); + return true; + } + return false; + }); + + PB.registerParseAACallback([](StringRef AAName, AAManager &AAM) { + if (AAName == "evm-aa") { + AAM.registerFunctionAnalysis(); + return true; + } + return false; + }); +} + +namespace { +/// EVM Code Generator Pass Configuration Options. +class EVMPassConfig final : public TargetPassConfig { +public: + EVMPassConfig(EVMTargetMachine &TM, PassManagerBase &PM) + : TargetPassConfig(TM, PM) { + disablePass(&EarlyTailDuplicateID); + } + + EVMTargetMachine &getEVMTargetMachine() const { + return getTM(); + } + + // EVM target - is a virtual stack machine that requires stackification + // of the instructions instead of allocating registers for their operands. + FunctionPass *createTargetRegisterAllocator(bool) override { return nullptr; } + + // No reg alloc + bool addRegAssignAndRewriteFast() override { return false; } + + // No reg alloc + bool addRegAssignAndRewriteOptimized() override { return false; } + + bool addPreISel() override; + void addCodeGenPrepare() override; + void addIRPasses() override; + bool addGCPasses() override { return false; } + bool addInstSelector() override; + void addPostRegAlloc() override; + void addPreEmitPass() override; + void addPreEmitPass2() override; +}; +} // namespace + +void EVMPassConfig::addIRPasses() { + addPass(createEVMLowerIntrinsicsPass()); + if (TM->getOptLevel() != CodeGenOptLevel::None) { + addPass(createEVMAAWrapperPass()); + addPass(createEVMExternalAAWrapperPass()); + } + TargetPassConfig::addIRPasses(); +} + +bool EVMPassConfig::addPreISel() { + TargetPassConfig::addPreISel(); + addPass(createEVMMarkRecursiveFunctionsPass()); + return false; +} + +void EVMPassConfig::addCodeGenPrepare() { + addPass(createEVMCodegenPreparePass()); + TargetPassConfig::addCodeGenPrepare(); +} + +bool EVMPassConfig::addInstSelector() { + (void)TargetPassConfig::addInstSelector(); + // Install an instruction selector. + addPass(createEVMISelDag(getEVMTargetMachine(), getOptLevel())); + // Run the argument-move pass immediately after the ScheduleDAG scheduler + // so that we can fix up the ARGUMENT instructions before anything else + // sees them in the wrong place. + addPass(createEVMArgumentMove()); + return false; +} + +void EVMPassConfig::addPostRegAlloc() { + // TODO: The following CodeGen passes don't currently support code containing + // virtual registers. Consider removing their restrictions and re-enabling + // them. These functions all require the NoVRegs property. + disablePass(&MachineLateInstrsCleanupID); + disablePass(&MachineCopyPropagationID); + disablePass(&PostRAMachineSinkingID); + disablePass(&PostRASchedulerID); + disablePass(&FuncletLayoutID); + disablePass(&StackMapLivenessID); + disablePass(&LiveDebugValuesID); + disablePass(&PatchableFunctionID); + disablePass(&ShrinkWrapID); + disablePass(&TailDuplicateID); + + // TODO: This pass is disabled in WebAssembly, as it hurts code size because + // it can generate irreducible control flow. Check if this also true for EVM? + disablePass(&MachineBlockPlacementID); + + TargetPassConfig::addPostRegAlloc(); +} + +void EVMPassConfig::addPreEmitPass() { + TargetPassConfig::addPreEmitPass(); + + // FIXME: enable all the passes below, but the Stackify with EVMKeepRegisters. + if (!EVMKeepRegisters) { + addPass(createEVMSplitCriticalEdges()); + addPass(createEVMOptimizeLiveIntervals()); + addPass(createEVMSingleUseExpression()); + addPass(createEVMBPStackification()); + addPass(&StackSlotColoringID); + addPass(createEVMFinalizeStackFrames()); + + // Optimize branch instructions after stackification. This is done again + // here, since EVMSplitCriticalEdges may introduce new BBs that could + // contain only branches after stackification. + if (getOptLevel() != CodeGenOptLevel::None) + addPass(&BranchFolderPassID); + } +} + +void EVMPassConfig::addPreEmitPass2() { + addPass(createEVMLowerJumpUnless()); + addPass(createEVMConstantUnfolding()); + if (getOptLevel() != CodeGenOptLevel::None) + addPass(createEVMPeepholePass()); +} + +TargetPassConfig *EVMTargetMachine::createPassConfig(PassManagerBase &PM) { + return new EVMPassConfig(*this, PM); +} diff --git a/llvm/lib/Target/EVM/EVMTargetMachine.h b/llvm/lib/Target/EVM/EVMTargetMachine.h new file mode 100644 index 000000000000..f605f563f046 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMTargetMachine.h @@ -0,0 +1,70 @@ +//===---- EVMTargetMachine.h - Define TargetMachine for EVM -*- 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 declares the EVM specific subclass of TargetMachine. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVMTARGETMACHINE_H +#define LLVM_LIB_TARGET_EVM_EVMTARGETMACHINE_H + +#include "EVMSubtarget.h" +#include "llvm/Target/TargetMachine.h" + +namespace llvm { + +/// EVMTargetMachine +/// +class EVMTargetMachine final : public LLVMTargetMachine { + std::unique_ptr TLOF; + EVMSubtarget Subtarget; + +public: + EVMTargetMachine(const Target &T, const Triple &TT, StringRef CPU, + StringRef FS, const TargetOptions &Options, + std::optional RM, + std::optional CM, CodeGenOptLevel OL, + bool JIT); + + TargetPassConfig *createPassConfig(PassManagerBase &PM) override; + + const EVMSubtarget *getSubtargetImpl(const Function &F) const override { + return &Subtarget; + } + + TargetTransformInfo getTargetTransformInfo(const Function &F) const override; + + MachineFunctionInfo * + createMachineFunctionInfo(BumpPtrAllocator &Allocator, const Function &F, + const TargetSubtargetInfo *STI) const override; + + yaml::MachineFunctionInfo *createDefaultFuncInfoYAML() const override; + yaml::MachineFunctionInfo * + convertFuncInfoToYAML(const MachineFunction &MF) const override; + bool parseMachineFunctionInfo(const yaml::MachineFunctionInfo &, + PerFunctionMIParsingState &PFS, + SMDiagnostic &Error, + SMRange &SourceRange) const override; + + TargetLoweringObjectFile *getObjFileLowering() const override { + return TLOF.get(); + } + + // For EVM target this should return false to avoid assertion fails in some + // post-RA passes that require NoVreg property. + // For example, PrologEpilogInserter. + bool usesPhysRegsForValues() const override { return false; } + + void registerPassBuilderCallbacks(PassBuilder &PB) override; + + void registerEarlyDefaultAliasAnalyses(AAManager &AAM) override; +}; // EVMTargetMachine. + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_EVMTARGETMACHINE_H diff --git a/llvm/lib/Target/EVM/EVMTargetObjectFile.h b/llvm/lib/Target/EVM/EVMTargetObjectFile.h new file mode 100644 index 000000000000..1afa2bb7fb41 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMTargetObjectFile.h @@ -0,0 +1,32 @@ +//===---------- EVMTargetObjectFile.h - EVM Object Info -*- 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 declares the EVM-specific subclass of +// TargetLoweringObjectFile. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVMTARGETOBJECTFILE_H +#define LLVM_LIB_TARGET_EVM_EVMTARGETOBJECTFILE_H + +#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" + +namespace llvm { + +class EVMELFTargetObjectFile final : public TargetLoweringObjectFileELF { +public: + EVMELFTargetObjectFile() = default; + + // Code sections need to be aligned on 1, otherwise linker will add padding + // between .text sections of the object files being linked. + unsigned getTextSectionAlignment() const override { return 1; } +}; + +} // end namespace llvm + +#endif diff --git a/llvm/lib/Target/EVM/EVMTargetTransformInfo.cpp b/llvm/lib/Target/EVM/EVMTargetTransformInfo.cpp new file mode 100644 index 000000000000..ad7c259757f8 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMTargetTransformInfo.cpp @@ -0,0 +1,83 @@ +//===----------- EVMTargetTransformInfo.cpp - EVM-specific TTI -*- 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 EVM-specific TargetTransformInfo +// implementation. +// +//===----------------------------------------------------------------------===// + +#include "EVMTargetTransformInfo.h" +#include "llvm/IR/IntrinsicsEVM.h" +#include "llvm/Transforms/InstCombine/InstCombiner.h" +using namespace llvm; +using namespace llvm::PatternMatch; + +#define DEBUG_TYPE "evmtti" + +static std::optional instCombineSignExtend(InstCombiner &IC, + IntrinsicInst &II) { + // Fold signextend(b, signextend(b, x)) -> signextend(b, x) + Value *B = nullptr, *X = nullptr; + if (match(&II, m_Intrinsic( + m_Value(B), m_Intrinsic( + m_Deferred(B), m_Value(X))))) + return IC.replaceInstUsesWith(II, II.getArgOperand(1)); + + return std::nullopt; +} + +std::optional +EVMTTIImpl::instCombineIntrinsic(InstCombiner &IC, IntrinsicInst &II) const { + if (II.getIntrinsicID() == Intrinsic::evm_signextend) + return instCombineSignExtend(IC, II); + + return std::nullopt; +} + +unsigned EVMTTIImpl::getAssumedAddrSpace(const Value *V) const { + const auto *LD = dyn_cast(V); + if (!LD) + return 0; + + return LD->getPointerAddressSpace(); +} + +InstructionCost EVMTTIImpl::getVectorInstrCost(unsigned Opcode, Type *Val, + TTI::TargetCostKind CostKind, + unsigned Index, Value *, + Value *) { + InstructionCost Cost = BasicTTIImplBase::getVectorInstrCost( + Opcode, Val, CostKind, Index, nullptr, nullptr); + return Cost + 25 * TargetTransformInfo::TCC_Expensive; +} + +void EVMTTIImpl::getUnrollingPreferences(Loop *L, ScalarEvolution &SE, + TTI::UnrollingPreferences &UP, + OptimizationRemarkEmitter *ORE) { + BaseT::getUnrollingPreferences(L, SE, UP, ORE); + + // Only allow unrolling small loops. + UP.Threshold = 4; + UP.MaxIterationsCountToAnalyze = 4; + + // Disable runtime, partial unrolling and unrolling using + // trip count upper bound. + UP.Partial = UP.Runtime = UP.UpperBound = false; + UP.PartialThreshold = 0; + + // Avoid unrolling when optimizing for size. + UP.OptSizeThreshold = 0; + UP.PartialOptSizeThreshold = 0; +} + +bool EVMTTIImpl::isLSRCostLess(const TargetTransformInfo::LSRCost &C1, + const TargetTransformInfo::LSRCost &C2) const { + return C1.NumRegs < C2.NumRegs; +} + +bool EVMTTIImpl::isNumRegsMajorCostOfLSR() const { return true; } diff --git a/llvm/lib/Target/EVM/EVMTargetTransformInfo.h b/llvm/lib/Target/EVM/EVMTargetTransformInfo.h new file mode 100644 index 000000000000..82caa748ed69 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMTargetTransformInfo.h @@ -0,0 +1,121 @@ +//===---------- EVMTargetTransformInfo.h - EVM-specific TTI -*- 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 a TargetTransformInfo::Concept conforming object specific +// to the EVM target machine. +// +// It uses the target's detailed information to provide more precise answers to +// certain TTI queries, while letting the target independent and default TTI +// implementations handle the rest. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVMTARGETTRANSFORMINFO_H +#define LLVM_LIB_TARGET_EVM_EVMTARGETTRANSFORMINFO_H + +#include "EVMTargetMachine.h" +#include "llvm/CodeGen/BasicTTIImpl.h" +#include "llvm/IR/Module.h" + +namespace llvm { + +class EVMTTIImpl final : public BasicTTIImplBase { + using BaseT = BasicTTIImplBase; + using TTI = TargetTransformInfo; + friend BaseT; + + const EVMSubtarget *ST; + const EVMTargetLowering *TLI; + + const EVMSubtarget *getST() const { return ST; } + const EVMTargetLowering *getTLI() const { return TLI; } + +public: + enum EVMRegisterClass { Vector /* Unsupported */, GPR }; + + EVMTTIImpl(const EVMTargetMachine *TM, const Function &F) + : BaseT(TM, F.getParent()->getDataLayout()), ST(TM->getSubtargetImpl(F)), + TLI(ST->getTargetLowering()) {} + + std::optional instCombineIntrinsic(InstCombiner &IC, + IntrinsicInst &II) const; + + bool allowsMisalignedMemoryAccesses(LLVMContext &Context, unsigned BitWidth, + unsigned AddressSpace = 0, + Align Alignment = Align(1), + unsigned *Fast = nullptr) const { + return true; + } + + unsigned getAssumedAddrSpace(const Value *V) const; + + unsigned getNumberOfRegisters(unsigned ClassID) const { + return ClassID == Vector ? 0 : 1; + } + + TypeSize getRegisterBitWidth(TargetTransformInfo::RegisterKind RK) const { + return TypeSize::getFixed(256); + } + + unsigned getRegisterClassForType(bool IsVector, Type *Ty = nullptr) const { + if (IsVector) + return Vector; + return GPR; + } + + const char *getRegisterClassName(unsigned ClassID) const { + if (ClassID == GPR) { + return "EVM::GPR"; + } + llvm_unreachable("unknown register class"); + } + + InstructionCost getVectorInstrCost(unsigned Opcode, Type *Val, + TTI::TargetCostKind CostKind, + unsigned Index = -1, Value *Op0 = nullptr, + Value *Op1 = nullptr); + InstructionCost getVectorInstrCost(const Instruction &I, Type *Val, + TTI::TargetCostKind CostKind, + unsigned Index = -1) { + return getVectorInstrCost(I.getOpcode(), Val, CostKind, Index); + } + + Type * + getMemcpyLoopLoweringType(LLVMContext &Context, Value *Length, + unsigned SrcAddrSpace, unsigned DestAddrSpace, + unsigned SrcAlign, unsigned DestAlign, + std::optional AtomicElementSize) const { + return IntegerType::get(Context, 256); + } + + void getMemcpyLoopResidualLoweringType( + SmallVectorImpl &OpsOut, LLVMContext &Context, + unsigned RemainingBytes, unsigned SrcAddrSpace, unsigned DestAddrSpace, + unsigned SrcAlign, unsigned DestAlign, + std::optional AtomicCpySize) const { + assert(RemainingBytes < 32); + OpsOut.push_back(Type::getIntNTy(Context, RemainingBytes * 8)); + } + + void getUnrollingPreferences(Loop *L, ScalarEvolution &SE, + TTI::UnrollingPreferences &UP, + OptimizationRemarkEmitter *ORE); + + /// Return true if LSR cost of C1 is lower than C1. + bool isLSRCostLess(const TargetTransformInfo::LSRCost &C1, + const TargetTransformInfo::LSRCost &C2) const; + + /// Return true if LSR major cost is number of registers. Targets which + /// implement their own isLSRCostLess and unset number of registers as major + /// cost should return false, otherwise return true. + bool isNumRegsMajorCostOfLSR() const; +}; + +} // end namespace llvm + +#endif diff --git a/llvm/lib/Target/EVM/MCTargetDesc/CMakeLists.txt b/llvm/lib/Target/EVM/MCTargetDesc/CMakeLists.txt new file mode 100644 index 000000000000..8a3356f4e7e2 --- /dev/null +++ b/llvm/lib/Target/EVM/MCTargetDesc/CMakeLists.txt @@ -0,0 +1,18 @@ +add_llvm_component_library(LLVMEVMDesc + EVMAsmBackend.cpp + EVMELFObjectWriter.cpp + EVMInstPrinter.cpp + EVMMCAsmInfo.cpp + EVMMCCodeEmitter.cpp + EVMMCExpr.cpp + EVMMCTargetDesc.cpp + EVMTargetStreamer.cpp + + LINK_COMPONENTS + MC + EVMInfo + Support + + ADD_TO_COMPONENT + EVM + ) diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMAsmBackend.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMAsmBackend.cpp new file mode 100644 index 000000000000..a28bcaf4ee29 --- /dev/null +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMAsmBackend.cpp @@ -0,0 +1,248 @@ +//===-------- EVMAsmBackend.cpp - EVM Assembler Backend -----*- 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 EVMAsmBackend class. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/EVMFixupKinds.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "TargetInfo/EVMTargetInfo.h" +#include "llvm/ADT/APInt.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCELFObjectWriter.h" +#include "llvm/MC/MCFixupKindInfo.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/MC/MCValue.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" + +#include + +using namespace llvm; + +#define DEBUG_TYPE "evm-asm-backend" + +namespace { +class EVMAsmBackend final : public MCAsmBackend { + uint8_t OSABI; + std::unique_ptr MCII; + +public: + EVMAsmBackend(const Target &T, const MCSubtargetInfo &STI, uint8_t OSABI) + : MCAsmBackend(llvm::endianness::big), OSABI(OSABI), MCII(T.createMCInstrInfo()) {} + EVMAsmBackend(const EVMAsmBackend &) = delete; + EVMAsmBackend(EVMAsmBackend &&) = delete; + EVMAsmBackend &operator=(const EVMAsmBackend &) = delete; + EVMAsmBackend &operator=(EVMAsmBackend &&) = delete; + ~EVMAsmBackend() override = default; + + void applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, + const MCValue &Target, MutableArrayRef Data, + uint64_t Value, bool IsResolved, + const MCSubtargetInfo *STI) const override; + + std::unique_ptr + createObjectTargetWriter() const override { + return createEVMELFObjectWriter(OSABI); + } + + unsigned getNumFixupKinds() const override { + return EVM::NumTargetFixupKinds; + } + + const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override; + + bool evaluateTargetFixup(const MCAssembler &Asm, const MCFixup &Fixup, + const MCFragment *DF, const MCValue &Target, + const MCSubtargetInfo *STI, + uint64_t &Value, bool &WasForced) override; + + bool fixupNeedsRelaxation(const MCFixup &Fixup, + uint64_t Value) const override { + llvm_unreachable("Handled by fixupNeedsRelaxationAdvanced"); + } + + bool fixupNeedsRelaxationAdvanced(const MCAssembler &Asm, + const MCFixup &Fixup, bool Resolved, + uint64_t Value, + const MCRelaxableFragment *DF, + const bool WasForced) const override; + + void relaxInstruction(MCInst &Inst, + const MCSubtargetInfo &STI) const override; + + bool mayNeedRelaxation(const MCInst &Inst, + const MCSubtargetInfo &STI) const override; + + bool writeNopData(raw_ostream &OS, uint64_t Count, + const MCSubtargetInfo *STI) const override; +}; +} // end anonymous namespace + +bool EVMAsmBackend::writeNopData(raw_ostream &OS, uint64_t Count, + const MCSubtargetInfo *STI) const { + for (uint64_t I = 0; I < Count; ++I) + OS << char(EVM::INVALID); + + return true; +} + +const MCFixupKindInfo &EVMAsmBackend::getFixupKindInfo(MCFixupKind Kind) const { + const static std::array Infos = { + {// This table *must* be in the order that the fixup_* kinds are defined + // in EVMFixupKinds.h. + // + // Name Offset (bits) Size (bits) Flags + {"fixup_SecRel_i32", 0, 8 * 4, MCFixupKindInfo::FKF_IsTarget}, + {"fixup_SecRel_i24", 0, 8 * 3, MCFixupKindInfo::FKF_IsTarget}, + {"fixup_SecRel_i16", 0, 8 * 2, MCFixupKindInfo::FKF_IsTarget}, + {"fixup_SecRel_i8", 0, 8 * 1, MCFixupKindInfo::FKF_IsTarget}, + {"fixup_Data_i32", 0, 8 * 4, MCFixupKindInfo::FKF_IsTarget}}}; + + if (Kind < FirstTargetFixupKind) + llvm_unreachable("Unexpected fixup kind"); + + assert(static_cast(Kind - FirstTargetFixupKind) < + getNumFixupKinds() && + "Invalid kind!"); + return Infos[Kind - FirstTargetFixupKind]; +} + +bool EVMAsmBackend::evaluateTargetFixup(const MCAssembler &Asm, + const MCFixup &Fixup, + const MCFragment *DF, + const MCValue &Target, + const MCSubtargetInfo *STI, + uint64_t &Value, bool &WasForced) { + assert(unsigned(Fixup.getTargetKind() - FirstTargetFixupKind) < + getNumFixupKinds() && + "Invalid kind!"); + + // The following fixups should be emited as relocations, + // as they can only be resolved at link time. + unsigned FixUpKind = Fixup.getTargetKind(); + if (FixUpKind == EVM::fixup_Data_i32) + return false; + + Value = Target.getConstant(); + if (Value > std::numeric_limits::max()) + report_fatal_error("Fixup value exceeds the displacement 2^32"); + + if (const MCSymbolRefExpr *A = Target.getSymA()) { + const MCSymbol &Sym = A->getSymbol(); + // If 'Sym' is undefined, 'getSymbolOffset' will + // raise 'unable to evaluate offset to undefined symbol' error. + Value += Asm.getSymbolOffset(Sym); + return true; + } + llvm_unreachable("Unexpect target MCValue"); +} + +void EVMAsmBackend::applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, + const MCValue &Target, + MutableArrayRef Data, uint64_t Value, + bool IsResolved, + const MCSubtargetInfo *STI) const { + if (!IsResolved) + return; + + // Doesn't change encoding. + if (Value == 0) + return; + + const MCFixupKindInfo &Info = getFixupKindInfo(Fixup.getKind()); + unsigned NumBytes = alignTo(Info.TargetSize, 8) / 8; + unsigned Offset = Fixup.getOffset(); + assert(Offset + NumBytes <= Data.size() && "Invalid fixup offset!"); + + LLVM_DEBUG(dbgs() << "applyFixup: value: " << Value + << ", nbytes: " << NumBytes << ", sym: "); + LLVM_DEBUG(Target.getSymA()->print(dbgs(), nullptr)); + LLVM_DEBUG(dbgs() << '\n'); + + // For each byte of the fragment that the fixup touches, mask in the + // bits from the fixup value. + for (unsigned I = 0; I != NumBytes; ++I) + Data[Offset + I] |= uint8_t((Value >> ((NumBytes - I - 1) * 8)) & 0xff); +} + +void EVMAsmBackend::relaxInstruction(MCInst &Inst, + const MCSubtargetInfo &STI) const { + // On each iteration of the relaxation process we try to decrease on one the + // byte width of the value to be pushed. + switch (Inst.getOpcode()) { + default: + llvm_unreachable("Unexpected instruction for relaxation"); + case EVM::PUSH4_S: + Inst.setOpcode(EVM::PUSH3_S); + break; + case EVM::PUSH3_S: + Inst.setOpcode(EVM::PUSH2_S); + break; + case EVM::PUSH2_S: + Inst.setOpcode(EVM::PUSH1_S); + break; + } +} + +bool EVMAsmBackend::fixupNeedsRelaxationAdvanced(const MCAssembler &Asm, + const MCFixup &Fixup, + bool Resolved, uint64_t Value, + const MCRelaxableFragment *DF, + const bool WasForced) const { + unsigned FixUpKind = Fixup.getTargetKind(); + // The following fixups shouls always be emited as relocations, + // as they can only be resolved at linking time. + if (FixUpKind == EVM::fixup_Data_i32) + return false; + + assert(Resolved); + unsigned Opcode = EVM::getPUSHOpcode(APInt(256, Value)); + // The first byte of an instruction is an opcode, so + // subtract it from the total size to get size of an immediate. + unsigned OffsetByteWidth = MCII->get(Opcode).getSize() - 1; + + LLVM_DEBUG(dbgs() << "fixupNeedsRelaxationAdvanced : value: " << Value + << ", OffsetByteWidth: " << OffsetByteWidth << ", sym: "); + LLVM_DEBUG(Fixup.getValue()->print(dbgs(), nullptr)); + LLVM_DEBUG(dbgs() << '\n'); + + switch (FixUpKind) { + default: + llvm_unreachable("Unexpected target fixup kind"); + case EVM::fixup_SecRel_i32: + return OffsetByteWidth < 4; + case EVM::fixup_SecRel_i24: + return OffsetByteWidth < 3; + case EVM::fixup_SecRel_i16: + return OffsetByteWidth < 2; + } +} + +bool EVMAsmBackend::mayNeedRelaxation(const MCInst &Inst, + const MCSubtargetInfo &STI) const { + switch (Inst.getOpcode()) { + default: + return false; + case EVM::PUSH4_S: + case EVM::PUSH3_S: + case EVM::PUSH2_S: + return Inst.getOperand(0).isExpr(); + } +} + +MCAsmBackend *llvm::createEVMMCAsmBackend(const Target &T, + const MCSubtargetInfo &STI, + const MCRegisterInfo &MRI, + const MCTargetOptions &Options) { + return new EVMAsmBackend(T, STI, ELF::ELFOSABI_STANDALONE); +} diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMELFObjectWriter.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMELFObjectWriter.cpp new file mode 100644 index 000000000000..de3d796b2214 --- /dev/null +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMELFObjectWriter.cpp @@ -0,0 +1,64 @@ +//===-------- EVMELFObjectWriter.cpp - EVM ELF Writer ---------------------===// +// +// 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 handles EVM-specific object emission, converting LLVM's +// internal fixups into the appropriate relocations. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/EVMFixupKinds.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm/MC/MCELFObjectWriter.h" + +using namespace llvm; + +namespace { +class EVMELFObjectWriter final : public MCELFObjectTargetWriter { +public: + explicit EVMELFObjectWriter(uint8_t OSABI) + : MCELFObjectTargetWriter(false, OSABI, ELF::EM_EVM, + /*HasRelocationAddend*/ true) {} + + EVMELFObjectWriter(const EVMELFObjectWriter &) = delete; + EVMELFObjectWriter(EVMELFObjectWriter &&) = delete; + EVMELFObjectWriter &operator=(EVMELFObjectWriter &&) = delete; + EVMELFObjectWriter &operator=(const EVMELFObjectWriter &) = delete; + ~EVMELFObjectWriter() override = default; + +protected: + bool needsRelocateWithSymbol(const MCValue &Val, const MCSymbol &Sym, + unsigned Type) const override; + + unsigned getRelocType(MCContext &Ctx, const MCValue &Target, + const MCFixup &Fixup, bool IsPCRel) const override; +}; +} // end of anonymous namespace + +bool EVMELFObjectWriter::needsRelocateWithSymbol(const MCValue &Val, + const MCSymbol &Sym, + unsigned Type) const { + // We support only relocations with a symbol, not section. + return true; +} + +unsigned EVMELFObjectWriter::getRelocType(MCContext &Ctx, const MCValue &Target, + const MCFixup &Fixup, + bool IsPCRel) const { + // Translate fixup kind to ELF relocation type. + switch (Fixup.getTargetKind()) { + default: + llvm_unreachable("Unexpected EVM fixup"); + case EVM::fixup_Data_i32: + return ELF::R_EVM_DATA; + } +} + +std::unique_ptr +llvm::createEVMELFObjectWriter(uint8_t OSABI) { + return std::make_unique(OSABI); +} diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMFixupKinds.h b/llvm/lib/Target/EVM/MCTargetDesc/EVMFixupKinds.h new file mode 100644 index 000000000000..ed5ea473e603 --- /dev/null +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMFixupKinds.h @@ -0,0 +1,30 @@ +//===------ EVMFixupKinds.h - EVM Specific Fixup Entries --------*- 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_LIB_TARGET_EVM_MCTARGETDESC_EVMFIXUPKINDS_H +#define LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMFIXUPKINDS_H + +#include "llvm/MC/MCFixup.h" + +namespace llvm { +namespace EVM { +enum Fixups { + fixup_SecRel_i32 = FirstTargetFixupKind, // 32-bit unsigned + fixup_SecRel_i24, // 24-bit unsigned + fixup_SecRel_i16, // 16-bit unsigned + fixup_SecRel_i8, // 8-bit unsigned + fixup_Data_i32, // 32-bit unsigned + + // Marker + LastTargetFixupKind, + NumTargetFixupKinds = LastTargetFixupKind - FirstTargetFixupKind +}; +} // end namespace EVM +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMFIXUPKINDS_H diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMInstPrinter.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMInstPrinter.cpp new file mode 100644 index 000000000000..5f4bc3b3a147 --- /dev/null +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMInstPrinter.cpp @@ -0,0 +1,98 @@ +//===----- EVMInstPrinter.cpp - Convert EVM MCInst to assembly syntax -----===// +// +// 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 class prints an EVM MCInst to a .s file. +// +//===----------------------------------------------------------------------===// + +#include "EVMInstPrinter.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/Support/FormattedStream.h" +using namespace llvm; + +#define DEBUG_TYPE "asm-printer" + +// Include the auto-generated portion of the assembly writer. +#include "EVMGenAsmWriter.inc" + +void EVMInstPrinter::printRegName(raw_ostream &OS, MCRegister RegNo) const { + // Decode the virtual register. This must be kept in sync with + // EVMMCInstLower::encodeVirtualRegister. + const unsigned RCId = (RegNo >> 28); + switch (RCId) { + default: + report_fatal_error("Bad virtual register encoding"); + case 0: + // This is actually a physical register, so defer to the autogenerated + // register printer. + OS << EVMInstPrinter::getRegisterName(RegNo); + return; + case 1: + OS << "$"; + break; + } + + const unsigned VReg = RegNo & 0x0FFFFFFF; + OS << VReg; +} + +void EVMInstPrinter::printInst(const MCInst *MI, uint64_t Address, + StringRef Annot, const MCSubtargetInfo &STI, + raw_ostream &O) { + // Print the instruction manually instead of using the TableGen-generated + // printInstruction() to allow custom formatting of the mnemonic. + O << "\t"; + std::pair MnemonicInfo = getMnemonic(MI); + // Add padding to the mnemonic so that it is 16 characters long. + if (MI->getNumOperands() > 0) + O << left_justify(MnemonicInfo.first, /*Width=*/16); + else + O << MnemonicInfo.first; + for (unsigned I = 0; I < MI->getNumOperands(); ++I) + printOperand(MI, I, O); + + // Print any additional variadic operands. + const MCInstrDesc &Desc = MII.get(MI->getOpcode()); + if (Desc.isVariadic()) { + if ((Desc.getNumOperands() == 0 && MI->getNumOperands() > 0) || + Desc.variadicOpsAreDefs()) + O << " "; + unsigned Start = Desc.getNumOperands(); + unsigned NumVariadicDefs = 0; + if (Desc.variadicOpsAreDefs()) { + // The number of variadic defs is encoded in an immediate by MCInstLower + NumVariadicDefs = MI->getOperand(0).getImm(); + Start = 1; + } + bool NeedsComma = Desc.getNumOperands() > 0 && !Desc.variadicOpsAreDefs(); + for (auto I = Start, E = MI->getNumOperands(); I < E; ++I) { + if (NeedsComma) + O << ", "; + printOperand(MI, I, O, I - Start < NumVariadicDefs); + NeedsComma = true; + } + } + + // Print any added annotation. + printAnnotation(O, Annot); +} + +void EVMInstPrinter::printOperand(const MCInst *MI, unsigned OpNo, + raw_ostream &O, bool IsVariadicDef) { + const MCOperand &Op = MI->getOperand(OpNo); + if (Op.isReg()) { + printRegName(O, Op.getReg()); + } else if (Op.isImm()) { + O << format_hex(Op.getImm(), 0, /*Upper=*/true); + } else { + Op.getExpr()->print(O, &MAI); + } +} diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMInstPrinter.h b/llvm/lib/Target/EVM/MCTargetDesc/EVMInstPrinter.h new file mode 100644 index 000000000000..a7f70ed5b5dc --- /dev/null +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMInstPrinter.h @@ -0,0 +1,40 @@ +//=------ EVMInstPrinter.h - Convert EVM MCInst to assembly syntax -*- 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 class prints a EVM MCInst to a .s file. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMINSTPRINTER_H +#define LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMINSTPRINTER_H + +#include "llvm/MC/MCInstPrinter.h" + +namespace llvm { +class EVMInstPrinter final : public MCInstPrinter { +public: + EVMInstPrinter(const MCAsmInfo &MAI, const MCInstrInfo &MII, + const MCRegisterInfo &MRI) + : MCInstPrinter(MAI, MII, MRI) {} + + void printRegName(raw_ostream &OS, MCRegister RegNo) const override; + void printInst(const MCInst *MI, uint64_t Address, StringRef Annot, + const MCSubtargetInfo &STI, raw_ostream &O) override; + + // Used by tblegen code. + void printOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O, + bool IsVariadicDef = false); + + // Autogenerated by tblgen. + std::pair getMnemonic(const MCInst *MI) override; + void printInstruction(const MCInst *MI, uint64_t Address, raw_ostream &O); + static const char *getRegisterName(MCRegister RegNo); +}; +} // namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMINSTPRINTER_H diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCAsmInfo.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCAsmInfo.cpp new file mode 100644 index 000000000000..47c4c83b9d8b --- /dev/null +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCAsmInfo.cpp @@ -0,0 +1,33 @@ +//===-------- EVMMCAsmInfo.cpp - EVM asm properties ------*- 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 the declarations of the EVMMCAsmInfo properties. +// +//===----------------------------------------------------------------------===// + +#include "EVMMCAsmInfo.h" +#include "llvm/TargetParser/Triple.h" + +using namespace llvm; + +EVMMCAsmInfo::EVMMCAsmInfo(const Triple &TT) { + IsLittleEndian = false; + HasFunctionAlignment = false; + HasDotTypeDotSizeDirective = false; + HasFourStringsDotFile = false; + PrivateGlobalPrefix = "."; + PrivateLabelPrefix = "."; + AlignmentIsInBytes = true; + PrependSymbolRefWithAt = true; + CommentString = ";"; + SupportsDebugInformation = true; +} + +bool EVMMCAsmInfo::shouldOmitSectionDirective(StringRef Name) const { + return !Name.starts_with(".symbol_name"); +} diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCAsmInfo.h b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCAsmInfo.h new file mode 100644 index 000000000000..29248f1ca35e --- /dev/null +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCAsmInfo.h @@ -0,0 +1,35 @@ +//===------ EVMMCAsmInfo.h - EVM asm properties --------------*- 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 the declaration of the EVMMCAsmInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMMCASMINFO_H +#define LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMMCASMINFO_H + +#include "llvm/MC/MCAsmInfoELF.h" + +namespace llvm { +class Triple; + +// Inherit from MCAsmInfo instead of MCAsmInfoELF to enable overriding +// getNonexecutableStackSection(), as the base class functionality is minimal +// in this context. +class EVMMCAsmInfo final : public MCAsmInfo { +public: + explicit EVMMCAsmInfo(const Triple &TT); + bool shouldOmitSectionDirective(StringRef) const override; + MCSection *getNonexecutableStackSection(MCContext &Ctx) const override { + return nullptr; + } +}; + +} // namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMMCASMINFO_H diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp new file mode 100644 index 000000000000..4aa567aa6861 --- /dev/null +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp @@ -0,0 +1,146 @@ +//===-- EVMMCCodeEmitter.cpp - Convert EVM code to machine code -*- 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 EVMMCCodeEmitter class. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/EVMFixupKinds.h" +#include "MCTargetDesc/EVMMCExpr.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCFixup.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/EndianStream.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +#define DEBUG_TYPE "mccodeemitter" + +namespace { +class EVMMCCodeEmitter final : public MCCodeEmitter { + const MCInstrInfo &MCII; + + // Implementation generated by tablegen. + void getBinaryCodeForInstr(const MCInst &MI, SmallVectorImpl &Fixups, + APInt &Inst, APInt &Scratch, + const MCSubtargetInfo &STI) const; + + // Return binary encoding of operand. + unsigned getMachineOpValue(const MCInst &MI, const MCOperand &MO, APInt &Op, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + void encodeInstruction(const MCInst &MI, SmallVectorImpl &CB, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const override; + +public: + EVMMCCodeEmitter(MCContext &Ctx, MCInstrInfo const &MCII) : MCII(MCII) {} +}; + +EVM::Fixups getFixupForOpc(unsigned Opcode, MCSymbolRefExpr::VariantKind Kind) { + if (Kind == MCSymbolRefExpr::VariantKind::VK_EVM_DATA) + return EVM::fixup_Data_i32; + + switch (Opcode) { + default: + llvm_unreachable("Unexpected MI for the SymbolRef MO"); + case EVM::PUSH4_S: + return EVM::fixup_SecRel_i32; + case EVM::PUSH3_S: + return EVM::fixup_SecRel_i24; + case EVM::PUSH2_S: + return EVM::fixup_SecRel_i16; + case EVM::PUSH1_S: + return EVM::fixup_SecRel_i8; + } +} + +} // end anonymous namespace + +void EVMMCCodeEmitter::encodeInstruction(const MCInst &MI, + SmallVectorImpl &CB, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + APInt Inst, Scratch; + getBinaryCodeForInstr(MI, Fixups, Inst, Scratch, STI); + + constexpr unsigned ByteSize = sizeof(std::byte) * 8; + unsigned InstSize = MCII.get(MI.getOpcode()).getSize(); + +#ifndef NDEBUG + const unsigned ActBitWidth = + MI.getOpcode() != EVM::STOP_S ? Inst.getActiveBits() : 1; + assert(ActBitWidth > 0 && ActBitWidth <= InstSize * ByteSize); +#endif // NDEBUG + + unsigned BitNum = InstSize * ByteSize; + while (BitNum > 0) { + std::byte ByteVal{0}; + for (unsigned I = 0; I < ByteSize; ++I, --BitNum) + ByteVal |= std::byte(Inst[BitNum - 1]) << (ByteSize - I - 1); + + support::endian::write(CB, ByteVal, llvm::endianness::big); + } +} + +unsigned EVMMCCodeEmitter::getMachineOpValue(const MCInst &MI, + const MCOperand &MO, APInt &Op, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + if (MO.isImm()) { + Op = MO.getImm(); + return 0; + } + + if (!MO.isExpr()) + llvm_unreachable("Unable to encode MCOperand"); + + MCExpr::ExprKind Kind = MO.getExpr()->getKind(); + if (Kind == MCExpr::ExprKind::Target) { + const auto *CImmExp = cast(MO.getExpr()); + Op = APInt(Op.getBitWidth(), CImmExp->getString(), /*radix=*/16); + return 0; + } + + // We expect the relocatable immediate operand to be in the + // form: @symbol + imm. + const MCSymbolRefExpr *RefExpr = nullptr; + if (Kind == MCExpr::ExprKind::Binary) { + const auto *BE = cast(MO.getExpr()); + RefExpr = dyn_cast( + isa(BE->getLHS()) ? BE->getLHS() : BE->getRHS()); + } else if (Kind == MCExpr::ExprKind::SymbolRef) { + RefExpr = cast(MO.getExpr()); + } + + if (!RefExpr) + llvm_unreachable("Unexpected MCOperand type"); + + EVM::Fixups Fixup = getFixupForOpc(MI.getOpcode(), RefExpr->getKind()); + // The byte index of start of the relocation is always 1, as + // we need to skip the instruction opcode which is always one byte. + Fixups.push_back( + MCFixup::create(1, MO.getExpr(), MCFixupKind(Fixup), MI.getLoc())); + + return 0; +} + +MCCodeEmitter *llvm::createEVMMCCodeEmitter(const MCInstrInfo &MCII, + MCContext &Ctx) { + return new EVMMCCodeEmitter(Ctx, MCII); +} + +#include "EVMGenMCCodeEmitter.inc" diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCExpr.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCExpr.cpp new file mode 100644 index 000000000000..f2530def9dc1 --- /dev/null +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCExpr.cpp @@ -0,0 +1,27 @@ +//===----- EVMMCExpr.cpp - EVM specific MC expression classes -*- 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 +// +//===----------------------------------------------------------------------===// +// +// Define EVM-specific MC classes. +// +//===----------------------------------------------------------------------===// + +#include "EVMMCExpr.h" +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCContext.h" +using namespace llvm; + +#define DEBUG_TYPE "evm-mcexpr" + +const EVMCImmMCExpr *EVMCImmMCExpr::create(const StringRef &Data, + MCContext &Ctx) { + return new (Ctx) EVMCImmMCExpr(Data); +} + +void EVMCImmMCExpr::printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const { + OS << "0x" << Data; +} diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCExpr.h b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCExpr.h new file mode 100644 index 000000000000..4d99c9508462 --- /dev/null +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCExpr.h @@ -0,0 +1,48 @@ +//===-------- EVMMCExpr.h - EVM specific MC expression classes --*- 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 +// +//===----------------------------------------------------------------------===// +// +// Define EVM specific MC expression classes +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMMCEXPR_H +#define LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMMCEXPR_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/MC/MCExpr.h" + +namespace llvm { + +class EVMCImmMCExpr final : public MCTargetExpr { +private: + StringRef Data; + + explicit EVMCImmMCExpr(const StringRef &Data) : Data(Data) {} + +public: + static const EVMCImmMCExpr *create(const StringRef &Data, MCContext &Ctx); + + StringRef getString() const { return Data; } + + void printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const override; + + bool evaluateAsRelocatableImpl(MCValue &Res, const MCAssembler *Asm, + const MCFixup *Fixup) const override { + return false; + } + + void visitUsedExpr(MCStreamer &Streamer) const override{}; + + MCFragment *findAssociatedFragment() const override { return nullptr; } + + void fixELFSymbolsInTLSFixups(MCAssembler &Asm) const override {} +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMMCEXPR_H diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.cpp new file mode 100644 index 000000000000..0700022c5d30 --- /dev/null +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.cpp @@ -0,0 +1,217 @@ +//===----- EVMMCTargetDesc.cpp - EVM Target Descriptions -*- 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 EVM specific target descriptions. +// +//===----------------------------------------------------------------------===// +// +#include "EVMMCTargetDesc.h" +#include "EVMInstPrinter.h" +#include "EVMMCAsmInfo.h" +#include "EVMTargetStreamer.h" +#include "TargetInfo/EVMTargetInfo.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Support/KECCAK.h" +#include "llvm/Support/Regex.h" + +using namespace llvm; + +#define GET_INSTRINFO_MC_DESC +#include "EVMGenInstrInfo.inc" + +#define GET_SUBTARGETINFO_MC_DESC +#include "EVMGenSubtargetInfo.inc" + +#define GET_REGINFO_MC_DESC +#include "EVMGenRegisterInfo.inc" + +static MCInstrInfo *createEVMMCInstrInfo() { + auto *X = new MCInstrInfo(); + InitEVMMCInstrInfo(X); + return X; +} + +static MCRegisterInfo *createEVMMCRegisterInfo(const Triple &TT) { + auto *X = new MCRegisterInfo(); + InitEVMMCRegisterInfo(X, 0); + return X; +} + +static MCInstPrinter *createEVMMCInstPrinter(const Triple &T, + unsigned SyntaxVariant, + const MCAsmInfo &MAI, + const MCInstrInfo &MII, + const MCRegisterInfo &MRI) { + if (SyntaxVariant == 0) + return new EVMInstPrinter(MAI, MII, MRI); + return nullptr; +} + +static MCAsmInfo *createEVMMCAsmInfo(const MCRegisterInfo & /*MRI*/, + const Triple &TT, + const MCTargetOptions & /*Options*/) { + return new EVMMCAsmInfo(TT); +} + +static MCSubtargetInfo *createEVMMCSubtargetInfo(const Triple &TT, + StringRef CPU, StringRef FS) { + return createEVMMCSubtargetInfoImpl(TT, CPU, /*TuneCPU*/ CPU, FS); +} + +static MCTargetStreamer * +createEVMObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo & /*STI*/) { + return new EVMTargetObjStreamer(S); +} + +static MCTargetStreamer * +createEVMAsmTargetStreamer(MCStreamer &S, formatted_raw_ostream & /*OS*/, + MCInstPrinter * /*InstPrint*/) { + return new EVMTargetAsmStreamer(S); +} + +static MCTargetStreamer *createEVMNullTargetStreamer(MCStreamer &S) { + return new EVMTargetStreamer(S); +} + +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeEVMTargetMC() { + Target &T = getTheEVMTarget(); + + // Register the MC asm info. + const RegisterMCAsmInfoFn X(T, createEVMMCAsmInfo); + + // Register the MC instruction info. + TargetRegistry::RegisterMCInstrInfo(T, createEVMMCInstrInfo); + + // Register the MC register info. + TargetRegistry::RegisterMCRegInfo(T, createEVMMCRegisterInfo); + + // Register the MC subtarget info. + TargetRegistry::RegisterMCSubtargetInfo(T, createEVMMCSubtargetInfo); + + // Register the MCInstPrinter. + TargetRegistry::RegisterMCInstPrinter(T, createEVMMCInstPrinter); + + // Register the MC code emitter. + TargetRegistry::RegisterMCCodeEmitter(T, createEVMMCCodeEmitter); + + // Register the ASM Backend. + TargetRegistry::RegisterMCAsmBackend(T, createEVMMCAsmBackend); + + // Register the object target streamer. + TargetRegistry::RegisterObjectTargetStreamer(T, + createEVMObjectTargetStreamer); + + // Register the asm target streamer. + TargetRegistry::RegisterAsmTargetStreamer(T, createEVMAsmTargetStreamer); + + // Register the null target streamer. + TargetRegistry::RegisterNullTargetStreamer(T, createEVMNullTargetStreamer); +} + +// Returs a string of the following format: +// '__$KECCAK256(SymName)$__' +std::string EVM::getLinkerSymbolHash(StringRef SymName) { + std::array Hash = KECCAK::KECCAK_256(SymName); + SmallString<72> HexHash; + toHex(Hash, /*LowerCase*/ true, HexHash); + return (Twine("__$") + HexHash + "$__").str(); +} + +// Returns concatenation of the \p Name with the \p SubIdx. +std::string EVM::getSymbolIndexedName(StringRef Name, unsigned SubIdx) { + return (Twine(Name) + std::to_string(SubIdx)).str(); +} + +// Returns concatenation of '.symbol_name' with the \p Name. +std::string EVM::getSymbolSectionName(StringRef Name) { + return (Twine(".symbol_name") + Name).str(); +} + + +// Returns true if the \p Name starts with '.symbol_name'. +bool EVM::isSymbolSectionName(StringRef Name) { + return Name.find(".symbol_name") == 0; +} + +// Strips index from the \p Name. +std::string EVM::getNonIndexedSymbolName(StringRef Name) { + Regex suffixRegex(R"(.*[0-4]$)"); + if (!suffixRegex.match(Name)) + report_fatal_error("Unexpected indexed symbol name"); + + return Name.drop_back().str(); +} + +std::string EVM::getLinkerSymbolName(StringRef Name) { + return (Twine("__linker_symbol") + Name).str(); +} + +bool EVM::isLinkerSymbolName(StringRef Name) { + return Name.find("__linker_symbol") == 0; +} + +std::string EVM::getDataSizeSymbol(StringRef Name) { + return (Twine("__datasize") + Name).str(); +} + +bool EVM::isDataSizeSymbolName(StringRef SymbolName) { + return SymbolName.find("__datasize") == 0; +} + +std::string EVM::extractDataSizeName(StringRef SymbolName) { + if (!SymbolName.consume_front("__datasize")) + report_fatal_error("Unexpected datasize symbol format"); + + return SymbolName.str(); +} + +std::string EVM::getDataOffsetSymbol(StringRef Name) { + return (Twine("__dataoffset") + Name).str(); +} + +bool EVM::isDataOffsetSymbolName(StringRef Name) { + return Name.find("__dataoffset") == 0; +} + +std::string EVM::extractDataOffseteName(StringRef SymbolName) { + if (!SymbolName.consume_front("__dataoffset")) + report_fatal_error("Unexpected dataoffset symbol format"); + + return SymbolName.str(); +} + +std::string EVM::getLoadImmutableSymbol(StringRef Name, unsigned Idx) { + return (Twine("__load_immutable__") + Name + "." + std::to_string(Idx)).str(); +} + +bool EVM::isLoadImmutableSymbolName(StringRef Name) { + return Name.find("__load_immutable__") == 0; +} + +// extract immutable ID from the load immutable symbol name. +// '__load_immutable__ID.N' -> 'ID'. +std::string EVM::getImmutableId(StringRef Name) { + SmallVector Matches; + Regex suffixRegex(R"(\.[0-9]+$)"); + if (!suffixRegex.match(Name, &Matches)) + report_fatal_error("No immutable symbol index"); + assert(Matches.size() == 1); + + // Strip suffix + Name.consume_back(Matches[0]); + + // Strip prefix + if (!Name.consume_front("__load_immutable__")) + report_fatal_error("Unexpected load immutable symbol format"); + + return Name.str(); +} diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.h b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.h new file mode 100644 index 000000000000..cbdb9bb29333 --- /dev/null +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.h @@ -0,0 +1,70 @@ +//===----- EVMMCTargetDesc.h - EVM Target Descriptions ----------*- 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 EVM specific target descriptions. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMMCTARGETDESC_H +#define LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMMCTARGETDESC_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/DataTypes.h" +#include + +namespace llvm { +class Target; +class MCAsmBackend; +class MCCodeEmitter; +class MCInstrInfo; +class MCSubtargetInfo; +class MCRegisterInfo; +class MCContext; +class MCTargetOptions; +class MCObjectTargetWriter; + +/// Creates a machine code emitter for EVM. +MCCodeEmitter *createEVMMCCodeEmitter(const MCInstrInfo &MCII, MCContext &Ctx); + +MCAsmBackend *createEVMMCAsmBackend(const Target &T, const MCSubtargetInfo &STI, + const MCRegisterInfo &MRI, + const MCTargetOptions &Options); + +std::unique_ptr createEVMELFObjectWriter(uint8_t OSABI); + +namespace EVM { +std::string getLinkerSymbolHash(StringRef SymName); +std::string getSymbolIndexedName(StringRef Name, unsigned SubIdx); +std::string getNonIndexedSymbolName(StringRef Name); +bool isSymbolSectionName(StringRef Name); +std::string getSymbolSectionName(StringRef Name); +bool isLinkerSymbolName(StringRef Name); +std::string getLinkerSymbolName(StringRef Name); +bool isDataSizeSymbolName(StringRef SymbolName); +std::string getDataSizeSymbol(StringRef Name); +std::string extractDataSizeName(StringRef SymbolName); +bool isDataOffsetSymbolName(StringRef Name); +std::string getDataOffsetSymbol(StringRef Name); +std::string extractDataOffseteName(StringRef SymbolName); +bool isLoadImmutableSymbolName(StringRef Name); +std::string getLoadImmutableSymbol(StringRef Name, unsigned Idx); +std::string getImmutableId(StringRef Name); +} // namespace EVM +} // namespace llvm + +// Defines symbolic names for EVM registers. +// This defines a mapping from register name to register number. +#define GET_REGINFO_ENUM +#include "EVMGenRegisterInfo.inc" + +// Defines symbolic names for the EVM instructions. +#define GET_INSTRINFO_ENUM +#define GET_INSTRINFO_MC_HELPER_DECLS +#include "EVMGenInstrInfo.inc" + +#endif // LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMMCTARGETDESC_H diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.cpp new file mode 100644 index 000000000000..760bb15bf99e --- /dev/null +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.cpp @@ -0,0 +1,92 @@ +//===------- EVMTargetStreamer.cpp - EVMTargetStreamer class --*- 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 EVMTargetStreamer class. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/EVMTargetStreamer.h" +#include "EVMFixupKinds.h" +#include "EVMMCTargetDesc.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCELFStreamer.h" +#include "llvm/MC/MCFragment.h" +#include "llvm/MC/MCSymbolELF.h" +#include "llvm/Support/Casting.h" + +using namespace llvm; + +// EVMTargetStreamer implementations + +EVMTargetStreamer::EVMTargetStreamer(MCStreamer &S) : MCTargetStreamer(S) {} + +void EVMTargetStreamer::emitLabel(MCSymbol *Symbol) { + // This is mostly a workaround for the current linking scheme. + // Mark all the symbols as local to their translation units. + auto *ELFSymbol = cast(Symbol); + ELFSymbol->setBinding(ELF::STB_LOCAL); +} + +EVMTargetAsmStreamer::EVMTargetAsmStreamer(MCStreamer &S) + : EVMTargetStreamer(S) {} + +void EVMTargetAsmStreamer::emitWideRelocatableSymbol(const MCInst &PushInst, + StringRef SymbolName, + unsigned SymbolSize) { + MCContext &Ctx = Streamer.getContext(); + const MCSubtargetInfo *STI = Ctx.getSubtargetInfo(); + Streamer.emitInstruction(PushInst, *STI); +} + +EVMTargetObjStreamer::EVMTargetObjStreamer(MCStreamer &S) + : EVMTargetStreamer(S) {} + +// Emits a PUSH instruction with relocatable symbol of the size up to 32 bytes. +// The symbol value is represented as an array of 4-byte relocatable +// sub-symbols. +void EVMTargetObjStreamer::emitWideRelocatableSymbol(const MCInst &PushInst, + StringRef SymbolName, + unsigned SymbolSize) { + if (SymbolSize > 32) + report_fatal_error("MC: relocatable symbol size exceeds 32 bytes"); + + // The code below is based on the MCObjectStreamer::emitInstToFragment() + // implementation. + MCContext &Ctx = Streamer.getContext(); + const MCSubtargetInfo *STI = Ctx.getSubtargetInfo(); + auto &S = static_cast(Streamer); + + auto *DF = Ctx.allocFragment(); + S.insert(DF); + SmallString<128> Code; + S.getAssembler().getEmitter().encodeInstruction(PushInst, Code, + DF->getFixups(), *STI); + // Remove a fixup corresponding to the initial symbol operand. + DF->getFixups().clear(); + DF->getContents().append(Code.begin(), Code.end()); + + // Emit 4-byte fixups to cover a wide symbol value. + assert(DF->getContents().size() == SymbolSize + 1 /* opcode byte */); + assert(!((DF->getContents().size() - 1) % 4)); + for (unsigned Idx = 0; Idx < SymbolSize / sizeof(uint32_t); ++Idx) { + std::string SubSymName = EVM::getSymbolIndexedName(SymbolName, Idx); + auto *Sym = cast(Ctx.getOrCreateSymbol(SubSymName)); + Sym->setOther(ELF::STO_EVM_REFERENCE_SYMBOL); + const MCExpr *Expr = MCSymbolRefExpr::create(Sym, Ctx); + S.visitUsedExpr(*Expr); + + assert(SymbolSize > Idx * 4); + // The byte index of start of the relocation is always 1, as + // we need to skip the instruction opcode which is always one byte. + constexpr auto FK = static_cast(EVM::fixup_Data_i32); + DF->getFixups().push_back(MCFixup::create((Idx * 4) + 1, Expr, FK)); + } +} diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.h b/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.h new file mode 100644 index 000000000000..b6eca5053a63 --- /dev/null +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.h @@ -0,0 +1,62 @@ +//===--------- EVMTargetStreamer.h - EVMTargetStreamer class --*- 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 declares the EVMTargetStreamer class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMTARGETSTREAMER_H +#define LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMTARGETSTREAMER_H +#include "llvm/MC/MCStreamer.h" + +namespace llvm { + +// EVM streamer interface to support EVM assembly directives +class EVMTargetStreamer : public MCTargetStreamer { +public: + explicit EVMTargetStreamer(MCStreamer &S); + EVMTargetStreamer(const EVMTargetStreamer &) = delete; + EVMTargetStreamer(EVMTargetStreamer &&) = delete; + EVMTargetStreamer &operator=(const EVMTargetStreamer &) = delete; + EVMTargetStreamer &operator=(EVMTargetStreamer &&) = delete; + + void emitLabel(MCSymbol *Symbol) override; + + virtual void emitWideRelocatableSymbol(const MCInst &PushInst, + StringRef SymbolName, + unsigned SymbolSize) {}; +}; + +/// This part is for ASCII assembly output +class EVMTargetAsmStreamer final : public EVMTargetStreamer { +public: + explicit EVMTargetAsmStreamer(MCStreamer &S); + EVMTargetAsmStreamer(const EVMTargetAsmStreamer &) = delete; + EVMTargetAsmStreamer(EVMTargetAsmStreamer &&) = delete; + EVMTargetAsmStreamer &operator=(const EVMTargetAsmStreamer &) = delete; + EVMTargetAsmStreamer &operator=(EVMTargetAsmStreamer &&) = delete; + + void emitWideRelocatableSymbol(const MCInst &PushInst, StringRef SymbolName, + unsigned SymbolSize) override; +}; + +// This part is for EVM object output +class EVMTargetObjStreamer final : public EVMTargetStreamer { +public: + explicit EVMTargetObjStreamer(MCStreamer &S); + EVMTargetObjStreamer(const EVMTargetObjStreamer &) = delete; + EVMTargetObjStreamer(EVMTargetObjStreamer &&) = delete; + EVMTargetObjStreamer &operator=(const EVMTargetObjStreamer &) = delete; + EVMTargetObjStreamer &operator=(EVMTargetObjStreamer &&) = delete; + + void emitWideRelocatableSymbol(const MCInst &PushInst, StringRef SymbolName, + unsigned SymbolSize) override; +}; +} // namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMTARGETSTREAMER_H diff --git a/llvm/lib/Target/EVM/TargetInfo/CMakeLists.txt b/llvm/lib/Target/EVM/TargetInfo/CMakeLists.txt new file mode 100644 index 000000000000..fc178574a43c --- /dev/null +++ b/llvm/lib/Target/EVM/TargetInfo/CMakeLists.txt @@ -0,0 +1,12 @@ +add_llvm_component_library(LLVMEVMInfo + EVMTargetInfo.cpp + + LINK_COMPONENTS + MC + Support + + ADD_TO_COMPONENT + EVM + ) + +add_dependencies(LLVMEVMInfo EVMCommonTableGen) diff --git a/llvm/lib/Target/EVM/TargetInfo/EVMTargetInfo.cpp b/llvm/lib/Target/EVM/TargetInfo/EVMTargetInfo.cpp new file mode 100644 index 000000000000..cb4ae74f2672 --- /dev/null +++ b/llvm/lib/Target/EVM/TargetInfo/EVMTargetInfo.cpp @@ -0,0 +1,202 @@ +//===------ EVMTargetInfo.cpp - EVM Target 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 registers the EVM target. +// +//===----------------------------------------------------------------------===// + +#include "TargetInfo/EVMTargetInfo.h" +#include "llvm/ADT/APInt.h" +#include "llvm/MC/TargetRegistry.h" + +using namespace llvm; + +Target &llvm::getTheEVMTarget() { + static Target TheEVMTarget; + return TheEVMTarget; +} + +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeEVMTargetInfo() { + const RegisterTarget X( + getTheEVMTarget(), "evm", + "Ethereum Virtual Machine [experimental] (256-bit big-endian)", "EVM"); +} + +#define GET_INSTRMAP_INFO +#define GET_INSTRINFO_ENUM +#define GET_INSTRINFO_MC_HELPER_DECLS +#include "EVMGenInstrInfo.inc" + +unsigned llvm::EVM::getRegisterOpcode(unsigned Opcode) { + assert(Opcode <= std::numeric_limits::max()); + auto Opc = static_cast(Opcode); + int Res = EVM::getRegisterOpcode(Opc); + assert(Res >= 0); + return static_cast(Res); +} + +unsigned llvm::EVM::getStackOpcode(unsigned Opcode) { + assert(Opcode <= std::numeric_limits::max()); + auto Opc = static_cast(Opcode); + int Res = EVM::getStackOpcode(Opc); + assert(Res >= 0); + return static_cast(Res); +} + +unsigned llvm::EVM::getPUSHOpcode(const APInt &Imm) { + if (Imm.isZero()) + return EVM::PUSH0; + + const unsigned ByteWidth = alignTo(Imm.getActiveBits(), 8) / 8; + switch (ByteWidth) { + case 1: + return EVM::PUSH1; + case 2: + return EVM::PUSH2; + case 3: + return EVM::PUSH3; + case 4: + return EVM::PUSH4; + case 5: + return EVM::PUSH5; + case 6: + return EVM::PUSH6; + case 7: + return EVM::PUSH7; + case 8: + return EVM::PUSH8; + case 9: + return EVM::PUSH9; + case 10: + return EVM::PUSH10; + case 11: + return EVM::PUSH11; + case 12: + return EVM::PUSH12; + case 13: + return EVM::PUSH13; + case 14: + return EVM::PUSH14; + case 15: + return EVM::PUSH15; + case 16: + return EVM::PUSH16; + case 17: + return EVM::PUSH17; + case 18: + return EVM::PUSH18; + case 19: + return EVM::PUSH19; + case 20: + return EVM::PUSH20; + case 21: + return EVM::PUSH21; + case 22: + return EVM::PUSH22; + case 23: + return EVM::PUSH23; + case 24: + return EVM::PUSH24; + case 25: + return EVM::PUSH25; + case 26: + return EVM::PUSH26; + case 27: + return EVM::PUSH27; + case 28: + return EVM::PUSH28; + case 29: + return EVM::PUSH29; + case 30: + return EVM::PUSH30; + case 31: + return EVM::PUSH31; + case 32: + return EVM::PUSH32; + default: + llvm_unreachable("Unexpected stack depth"); + } +} + +unsigned llvm::EVM::getDUPOpcode(unsigned Depth) { + switch (Depth) { + case 1: + return EVM::DUP1; + case 2: + return EVM::DUP2; + case 3: + return EVM::DUP3; + case 4: + return EVM::DUP4; + case 5: + return EVM::DUP5; + case 6: + return EVM::DUP6; + case 7: + return EVM::DUP7; + case 8: + return EVM::DUP8; + case 9: + return EVM::DUP9; + case 10: + return EVM::DUP10; + case 11: + return EVM::DUP11; + case 12: + return EVM::DUP12; + case 13: + return EVM::DUP13; + case 14: + return EVM::DUP14; + case 15: + return EVM::DUP15; + case 16: + return EVM::DUP16; + default: + llvm_unreachable("Unexpected stack depth"); + } +} + +unsigned llvm::EVM::getSWAPOpcode(unsigned Depth) { + switch (Depth) { + case 1: + return EVM::SWAP1; + case 2: + return EVM::SWAP2; + case 3: + return EVM::SWAP3; + case 4: + return EVM::SWAP4; + case 5: + return EVM::SWAP5; + case 6: + return EVM::SWAP6; + case 7: + return EVM::SWAP7; + case 8: + return EVM::SWAP8; + case 9: + return EVM::SWAP9; + case 10: + return EVM::SWAP10; + case 11: + return EVM::SWAP11; + case 12: + return EVM::SWAP12; + case 13: + return EVM::SWAP13; + case 14: + return EVM::SWAP14; + case 15: + return EVM::SWAP15; + case 16: + return EVM::SWAP16; + default: + llvm_unreachable("Unexpected stack depth"); + } +} diff --git a/llvm/lib/Target/EVM/TargetInfo/EVMTargetInfo.h b/llvm/lib/Target/EVM/TargetInfo/EVMTargetInfo.h new file mode 100644 index 000000000000..e0c6998bbb5a --- /dev/null +++ b/llvm/lib/Target/EVM/TargetInfo/EVMTargetInfo.h @@ -0,0 +1,35 @@ +//===-------- EVMTargetInfo.h - EVM Target 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 registers the EVM target. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_TARGETINFO_EVMTARGETINFO_H +#define LLVM_LIB_TARGET_EVM_TARGETINFO_EVMTARGETINFO_H + +namespace llvm { + +class Target; +class APInt; + +Target &getTheEVMTarget(); + +namespace EVM { + +unsigned getStackOpcode(unsigned Opcode); +unsigned getRegisterOpcode(unsigned Opcode); +unsigned getPUSHOpcode(const APInt &Imm); +unsigned getDUPOpcode(unsigned Depth); +unsigned getSWAPOpcode(unsigned Depth); + +} // namespace EVM + +} // namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_TARGETINFO_EVMTARGETINFO_H diff --git a/llvm/lib/Target/EVM/evm-stdlib.ll b/llvm/lib/Target/EVM/evm-stdlib.ll new file mode 100644 index 000000000000..6f7eca7d3aa5 --- /dev/null +++ b/llvm/lib/Target/EVM/evm-stdlib.ll @@ -0,0 +1,84 @@ +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @__addmod(i256 %arg1, i256 %arg2, i256 %modulo) #0 { + %res = call i256 @llvm.evm.addmod(i256 %arg1, i256 %arg2, i256 %modulo) + ret i256 %res +} + +define i256 @__mulmod(i256 %arg1, i256 %arg2, i256 %modulo) #0 { + %res = call i256 @llvm.evm.mulmod(i256 %arg1, i256 %arg2, i256 %modulo) + ret i256 %res +} + +define i256 @__signextend(i256 %bytesize, i256 %val) #0 { + %res = call i256 @llvm.evm.signextend(i256 %bytesize, i256 %val) + ret i256 %res +} + +define i256 @__exp(i256 %base, i256 %exp) #0 { + %res = call i256 @llvm.evm.exp(i256 %base, i256 %exp) + ret i256 %res +} + +define i256 @__byte(i256 %index, i256 %val) #0 { + %res = call i256 @llvm.evm.byte(i256 %index, i256 %val) + ret i256 %res +} + +define i256 @__sdiv(i256 %a, i256 %b) #0 { + %res = call i256 @llvm.evm.sdiv(i256 %a, i256 %b) + ret i256 %res +} + +define i256 @__div(i256 %a, i256 %b) #0 { + %res = call i256 @llvm.evm.div(i256 %a, i256 %b) + ret i256 %res +} + +define i256 @__smod(i256 %val, i256 %mod) #0 { + %res = call i256 @llvm.evm.smod(i256 %val, i256 %mod) + ret i256 %res +} + +define i256 @__mod(i256 %val, i256 %mod) #0 { + %res = call i256 @llvm.evm.mod(i256 %val, i256 %mod) + ret i256 %res +} + +define i256 @__shl(i256 %shift, i256 %val) #0 { + %res = call i256 @llvm.evm.shl(i256 %shift, i256 %val) + ret i256 %res +} + +define i256 @__shr(i256 %shift, i256 %val) #0 { + %res = call i256 @llvm.evm.shr(i256 %shift, i256 %val) + ret i256 %res +} + +define i256 @__sar(i256 %shift, i256 %val) #0 { + %res = call i256 @llvm.evm.sar(i256 %shift, i256 %val) + ret i256 %res +} + +define i256 @__sha3(ptr addrspace(1) %offset, i256 %len, i1 %unused) #1 { + %res = call i256 @llvm.evm.sha3(ptr addrspace(1) %offset, i256 %len) + ret i256 %res +} + +declare i256 @llvm.evm.addmod(i256, i256, i256) +declare i256 @llvm.evm.mulmod(i256, i256, i256) +declare i256 @llvm.evm.signextend(i256, i256) +declare i256 @llvm.evm.exp(i256, i256) +declare i256 @llvm.evm.byte(i256, i256) +declare i256 @llvm.evm.sdiv(i256, i256) +declare i256 @llvm.evm.div(i256, i256) +declare i256 @llvm.evm.mod(i256, i256) +declare i256 @llvm.evm.smod(i256, i256) +declare i256 @llvm.evm.shl(i256, i256) +declare i256 @llvm.evm.shr(i256, i256) +declare i256 @llvm.evm.sar(i256, i256) +declare i256 @llvm.evm.sha3(ptr addrspace(1), i256) + +attributes #0 = { alwaysinline mustprogress nofree norecurse nosync nounwind readnone willreturn } +attributes #1 = { alwaysinline argmemonly readonly nofree null_pointer_is_valid } diff --git a/llvm/lib/Target/Hexagon/HexagonConstPropagation.cpp b/llvm/lib/Target/Hexagon/HexagonConstPropagation.cpp index dae316ccb5e9..f68444c0b8d4 100644 --- a/llvm/lib/Target/Hexagon/HexagonConstPropagation.cpp +++ b/llvm/lib/Target/Hexagon/HexagonConstPropagation.cpp @@ -2503,7 +2503,8 @@ APInt HexagonConstEvaluator::getCmpImm(unsigned Opc, unsigned OpX, } uint64_t Val = MO.getImm(); - return APInt(32, Val, Signed); + // TODO: Is implicitTrunc correct here? + return APInt(32, Val, Signed, /*implicitTrunc=*/true); } void HexagonConstEvaluator::replaceWithNop(MachineInstr &MI) { diff --git a/llvm/lib/Target/Hexagon/HexagonGenExtract.cpp b/llvm/lib/Target/Hexagon/HexagonGenExtract.cpp index 3274f9162b54..2ed5150ec64f 100644 --- a/llvm/lib/Target/Hexagon/HexagonGenExtract.cpp +++ b/llvm/lib/Target/Hexagon/HexagonGenExtract.cpp @@ -171,7 +171,7 @@ bool HexagonGenExtract::convert(Instruction *In) { // this value. if (!LogicalSR && (SR > SL)) return false; - APInt A = APInt(BW, ~0ULL).lshr(SR).shl(SL); + APInt A = APInt(BW, ~0ULL, true).lshr(SR).shl(SL); CM = ConstantInt::get(Ctx, A); } diff --git a/llvm/lib/Target/Hexagon/HexagonInstrInfo.cpp b/llvm/lib/Target/Hexagon/HexagonInstrInfo.cpp index 3f6de365fe39..7c77bf2b31b8 100644 --- a/llvm/lib/Target/Hexagon/HexagonInstrInfo.cpp +++ b/llvm/lib/Target/Hexagon/HexagonInstrInfo.cpp @@ -857,7 +857,9 @@ static void getLiveOutRegsAt(LivePhysRegs &Regs, const MachineInstr &MI) { void HexagonInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, + bool RenamableSrc) const { const HexagonRegisterInfo &HRI = *Subtarget.getRegisterInfo(); unsigned KillFlag = getKillRegState(KillSrc); diff --git a/llvm/lib/Target/Hexagon/HexagonInstrInfo.h b/llvm/lib/Target/Hexagon/HexagonInstrInfo.h index 4efc62fd717c..854c3694ceba 100644 --- a/llvm/lib/Target/Hexagon/HexagonInstrInfo.h +++ b/llvm/lib/Target/Hexagon/HexagonInstrInfo.h @@ -174,7 +174,8 @@ class HexagonInstrInfo : public HexagonGenInstrInfo { /// large registers. See for example the ARM target. void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; /// Store the specified register of the given register class to the specified /// stack frame index. The store instruction is to be added to the given diff --git a/llvm/lib/Target/Lanai/LanaiInstrInfo.cpp b/llvm/lib/Target/Lanai/LanaiInstrInfo.cpp index b8a37435f5a6..f7ff63bb5784 100644 --- a/llvm/lib/Target/Lanai/LanaiInstrInfo.cpp +++ b/llvm/lib/Target/Lanai/LanaiInstrInfo.cpp @@ -35,8 +35,8 @@ void LanaiInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator Position, const DebugLoc &DL, MCRegister DestinationRegister, - MCRegister SourceRegister, - bool KillSource) const { + MCRegister SourceRegister, bool KillSource, + bool RenamableDest, bool RenamableSrc) const { if (!Lanai::GPRRegClass.contains(DestinationRegister, SourceRegister)) { llvm_unreachable("Impossible reg-to-reg copy"); } diff --git a/llvm/lib/Target/Lanai/LanaiInstrInfo.h b/llvm/lib/Target/Lanai/LanaiInstrInfo.h index 8ad2b9237c92..2630464f0a76 100644 --- a/llvm/lib/Target/Lanai/LanaiInstrInfo.h +++ b/llvm/lib/Target/Lanai/LanaiInstrInfo.h @@ -49,7 +49,9 @@ class LanaiInstrInfo : public LanaiGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator Position, const DebugLoc &DL, MCRegister DestinationRegister, - MCRegister SourceRegister, bool KillSource) const override; + MCRegister SourceRegister, bool KillSource, + bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator Position, diff --git a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.cpp b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.cpp index a85b054a85d7..9079b72f70e1 100644 --- a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.cpp +++ b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.cpp @@ -39,7 +39,9 @@ MCInst LoongArchInstrInfo::getNop() const { void LoongArchInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, const DebugLoc &DL, MCRegister DstReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, + bool RenamableSrc) const { if (LoongArch::GPRRegClass.contains(DstReg, SrcReg)) { BuildMI(MBB, MBBI, DL, get(LoongArch::OR), DstReg) .addReg(SrcReg, getKillRegState(KillSrc)) diff --git a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.h b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.h index eb19051e380c..1183fdd56d7d 100644 --- a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.h +++ b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.h @@ -31,7 +31,8 @@ class LoongArchInstrInfo : public LoongArchGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, const DebugLoc &DL, MCRegister DstReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, Register SrcReg, diff --git a/llvm/lib/Target/M68k/M68kInstrInfo.cpp b/llvm/lib/Target/M68k/M68kInstrInfo.cpp index 338db45782c9..23c5c76a4747 100644 --- a/llvm/lib/Target/M68k/M68kInstrInfo.cpp +++ b/llvm/lib/Target/M68k/M68kInstrInfo.cpp @@ -663,7 +663,8 @@ bool M68kInstrInfo::isPCRelRegisterOperandLegal( void M68kInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DstReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { unsigned Opc = 0; // First deal with the normal symmetric copies. diff --git a/llvm/lib/Target/M68k/M68kInstrInfo.h b/llvm/lib/Target/M68k/M68kInstrInfo.h index d1e1e1cd9998..5d81956d89fd 100644 --- a/llvm/lib/Target/M68k/M68kInstrInfo.h +++ b/llvm/lib/Target/M68k/M68kInstrInfo.h @@ -271,7 +271,8 @@ class M68kInstrInfo : public M68kGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; bool getStackSlotRange(const TargetRegisterClass *RC, unsigned SubIdx, unsigned &Size, unsigned &Offset, diff --git a/llvm/lib/Target/MSP430/MSP430InstrInfo.cpp b/llvm/lib/Target/MSP430/MSP430InstrInfo.cpp index 740571651664..ae1228ceaa4e 100644 --- a/llvm/lib/Target/MSP430/MSP430InstrInfo.cpp +++ b/llvm/lib/Target/MSP430/MSP430InstrInfo.cpp @@ -90,7 +90,8 @@ void MSP430InstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB, void MSP430InstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { unsigned Opc; if (MSP430::GR16RegClass.contains(DestReg, SrcReg)) Opc = MSP430::MOV16rr; diff --git a/llvm/lib/Target/MSP430/MSP430InstrInfo.h b/llvm/lib/Target/MSP430/MSP430InstrInfo.h index b8d015a21cd1..113a22318bec 100644 --- a/llvm/lib/Target/MSP430/MSP430InstrInfo.h +++ b/llvm/lib/Target/MSP430/MSP430InstrInfo.h @@ -37,7 +37,8 @@ class MSP430InstrInfo : public MSP430GenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, Register SrcReg, diff --git a/llvm/lib/Target/Mips/Mips16InstrInfo.cpp b/llvm/lib/Target/Mips/Mips16InstrInfo.cpp index 30ac96936de2..1bc1ed7ab93e 100644 --- a/llvm/lib/Target/Mips/Mips16InstrInfo.cpp +++ b/llvm/lib/Target/Mips/Mips16InstrInfo.cpp @@ -69,7 +69,8 @@ Register Mips16InstrInfo::isStoreToStackSlot(const MachineInstr &MI, void Mips16InstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { unsigned Opc = 0; if (Mips::CPU16RegsRegClass.contains(DestReg) && diff --git a/llvm/lib/Target/Mips/Mips16InstrInfo.h b/llvm/lib/Target/Mips/Mips16InstrInfo.h index e8567ee3b9ce..8e73c8079b0f 100644 --- a/llvm/lib/Target/Mips/Mips16InstrInfo.h +++ b/llvm/lib/Target/Mips/Mips16InstrInfo.h @@ -50,7 +50,8 @@ class Mips16InstrInfo : public MipsInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStack(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, diff --git a/llvm/lib/Target/Mips/MipsSEInstrInfo.cpp b/llvm/lib/Target/Mips/MipsSEInstrInfo.cpp index b99ddfab2a47..87e9ef1c2642 100644 --- a/llvm/lib/Target/Mips/MipsSEInstrInfo.cpp +++ b/llvm/lib/Target/Mips/MipsSEInstrInfo.cpp @@ -83,7 +83,8 @@ Register MipsSEInstrInfo::isStoreToStackSlot(const MachineInstr &MI, void MipsSEInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { unsigned Opc = 0, ZeroReg = 0; bool isMicroMips = Subtarget.inMicroMipsMode(); diff --git a/llvm/lib/Target/Mips/MipsSEInstrInfo.h b/llvm/lib/Target/Mips/MipsSEInstrInfo.h index a8855e26ad10..36bddba10410 100644 --- a/llvm/lib/Target/Mips/MipsSEInstrInfo.h +++ b/llvm/lib/Target/Mips/MipsSEInstrInfo.h @@ -44,7 +44,8 @@ class MipsSEInstrInfo : public MipsInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStack(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, diff --git a/llvm/lib/Target/NVPTX/NVPTXAliasAnalysis.h b/llvm/lib/Target/NVPTX/NVPTXAliasAnalysis.h index 2d204979eb6c..eec0c537448d 100644 --- a/llvm/lib/Target/NVPTX/NVPTXAliasAnalysis.h +++ b/llvm/lib/Target/NVPTX/NVPTXAliasAnalysis.h @@ -79,16 +79,23 @@ class NVPTXAAWrapperPass : public ImmutablePass { // Wrapper around ExternalAAWrapperPass so that the default // constructor gets the callback. +// Note that NVPTXAA will run before BasicAA for compile time considerations. class NVPTXExternalAAWrapper : public ExternalAAWrapperPass { public: static char ID; + bool runEarly() override { return true; } + NVPTXExternalAAWrapper() : ExternalAAWrapperPass([](Pass &P, Function &, AAResults &AAR) { if (auto *WrapperPass = P.getAnalysisIfAvailable()) AAR.addAAResult(WrapperPass->getResult()); }) {} + + StringRef getPassName() const override { + return "NVPTX Address space based Alias Analysis Wrapper"; + } }; ImmutablePass *createNVPTXAAWrapperPass(); diff --git a/llvm/lib/Target/NVPTX/NVPTXInstrInfo.cpp b/llvm/lib/Target/NVPTX/NVPTXInstrInfo.cpp index 673858f92e7c..bec40874c894 100644 --- a/llvm/lib/Target/NVPTX/NVPTXInstrInfo.cpp +++ b/llvm/lib/Target/NVPTX/NVPTXInstrInfo.cpp @@ -32,7 +32,8 @@ NVPTXInstrInfo::NVPTXInstrInfo() : RegInfo() {} void NVPTXInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { const MachineRegisterInfo &MRI = MBB.getParent()->getRegInfo(); const TargetRegisterClass *DestRC = MRI.getRegClass(DestReg); const TargetRegisterClass *SrcRC = MRI.getRegClass(SrcReg); diff --git a/llvm/lib/Target/NVPTX/NVPTXInstrInfo.h b/llvm/lib/Target/NVPTX/NVPTXInstrInfo.h index d6cbeae6984c..f674a00bc351 100644 --- a/llvm/lib/Target/NVPTX/NVPTXInstrInfo.h +++ b/llvm/lib/Target/NVPTX/NVPTXInstrInfo.h @@ -53,7 +53,8 @@ class NVPTXInstrInfo : public NVPTXGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; // Branch analysis. bool analyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TBB, diff --git a/llvm/lib/Target/NVPTX/NVPTXTargetMachine.cpp b/llvm/lib/Target/NVPTX/NVPTXTargetMachine.cpp index 097e29527eed..dd3e173694a7 100644 --- a/llvm/lib/Target/NVPTX/NVPTXTargetMachine.cpp +++ b/llvm/lib/Target/NVPTX/NVPTXTargetMachine.cpp @@ -221,7 +221,7 @@ MachineFunctionInfo *NVPTXTargetMachine::createMachineFunctionInfo( F, STI); } -void NVPTXTargetMachine::registerDefaultAliasAnalyses(AAManager &AAM) { +void NVPTXTargetMachine::registerEarlyDefaultAliasAnalyses(AAManager &AAM) { AAM.registerFunctionAnalysis(); } @@ -317,10 +317,7 @@ void NVPTXPassConfig::addIRPasses() { disablePass(&ShrinkWrapID); addPass(createNVPTXAAWrapperPass()); - addPass(createExternalAAWrapperPass([](Pass &P, Function &, AAResults &AAR) { - if (auto *WrapperPass = P.getAnalysisIfAvailable()) - AAR.addAAResult(WrapperPass->getResult()); - })); + addPass(createNVPTXExternalAAWrapperPass()); // NVVMReflectPass is added in addEarlyAsPossiblePasses, so hopefully running // it here does nothing. But since we need it for correctness when lowering diff --git a/llvm/lib/Target/NVPTX/NVPTXTargetMachine.h b/llvm/lib/Target/NVPTX/NVPTXTargetMachine.h index 2b88da67a50f..bb65a14a8d79 100644 --- a/llvm/lib/Target/NVPTX/NVPTXTargetMachine.h +++ b/llvm/lib/Target/NVPTX/NVPTXTargetMachine.h @@ -64,7 +64,7 @@ class NVPTXTargetMachine : public LLVMTargetMachine { createMachineFunctionInfo(BumpPtrAllocator &Allocator, const Function &F, const TargetSubtargetInfo *STI) const override; - void registerDefaultAliasAnalyses(AAManager &AAM) override; + void registerEarlyDefaultAliasAnalyses(AAManager &AAM) override; void registerPassBuilderCallbacks(PassBuilder &PB) override; diff --git a/llvm/lib/Target/PowerPC/PPCInstrInfo.cpp b/llvm/lib/Target/PowerPC/PPCInstrInfo.cpp index 81f16eb1a905..48833e8f8806 100644 --- a/llvm/lib/Target/PowerPC/PPCInstrInfo.cpp +++ b/llvm/lib/Target/PowerPC/PPCInstrInfo.cpp @@ -1678,7 +1678,8 @@ static unsigned getCRBitValue(unsigned CRBit) { void PPCInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { // We can end up with self copies and similar things as a result of VSX copy // legalization. Promote them here. const TargetRegisterInfo *TRI = &getRegisterInfo(); diff --git a/llvm/lib/Target/PowerPC/PPCInstrInfo.h b/llvm/lib/Target/PowerPC/PPCInstrInfo.h index 1e2687f92c61..40996f6fbb75 100644 --- a/llvm/lib/Target/PowerPC/PPCInstrInfo.h +++ b/llvm/lib/Target/PowerPC/PPCInstrInfo.h @@ -454,7 +454,8 @@ class PPCInstrInfo : public PPCGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, Register SrcReg, diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp index 823fb428472e..752e58493e9a 100644 --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -3452,7 +3452,8 @@ static std::optional isSimpleVIDSequence(SDValue Op, if (!Elt) continue; uint64_t ExpectedVal = - (int64_t)(Idx * (uint64_t)*SeqStepNum) / *SeqStepDenom; + (APInt(EltSizeInBits, Idx, /*isSigned=*/false, /*implicitTrunc=*/true) * + *SeqStepNum) / *SeqStepDenom; int64_t Addend = SignExtend64(*Elt - ExpectedVal, EltSizeInBits); if (!SeqAddend) SeqAddend = Addend; diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp index 6c0cbeadebf4..5dcda041f00e 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp @@ -440,12 +440,14 @@ void RISCVInstrInfo::copyPhysRegVector( void RISCVInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, const DebugLoc &DL, MCRegister DstReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { const TargetRegisterInfo *TRI = STI.getRegisterInfo(); if (RISCV::GPRRegClass.contains(DstReg, SrcReg)) { BuildMI(MBB, MBBI, DL, get(RISCV::ADDI), DstReg) - .addReg(SrcReg, getKillRegState(KillSrc)) + .addReg(SrcReg, + getKillRegState(KillSrc) | getRenamableRegState(RenamableSrc)) .addImm(0); return; } diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.h b/llvm/lib/Target/RISCV/RISCVInstrInfo.h index 025cc36d19eb..59c47063c8dc 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.h +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.h @@ -84,7 +84,8 @@ class RISCVInstrInfo : public RISCVGenInstrInfo { const TargetRegisterClass *RegClass) const; void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, const DebugLoc &DL, MCRegister DstReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, Register SrcReg, diff --git a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.cpp index 12cf7613a45c..d0a134cfbdf0 100644 --- a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.cpp @@ -241,7 +241,8 @@ unsigned SPIRVInstrInfo::insertBranch( void SPIRVInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { // Actually we don't need this COPY instruction. However if we do nothing with // it, post RA pseudo instrs expansion just removes it and we get the code // with undef registers. Therefore, we need to replace all uses of dst with diff --git a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.h b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.h index 95f387491357..67d2d979cb5a 100644 --- a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.h +++ b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.h @@ -51,7 +51,8 @@ class SPIRVInstrInfo : public SPIRVGenInstrInfo { int *BytesAdded = nullptr) const override; void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; bool expandPostRAPseudo(MachineInstr &MI) const override; }; diff --git a/llvm/lib/Target/Sparc/SparcInstrInfo.cpp b/llvm/lib/Target/Sparc/SparcInstrInfo.cpp index 2727a9f2efbb..0bb2540a97d7 100644 --- a/llvm/lib/Target/Sparc/SparcInstrInfo.cpp +++ b/llvm/lib/Target/Sparc/SparcInstrInfo.cpp @@ -438,7 +438,8 @@ bool SparcInstrInfo::isBranchOffsetInRange(unsigned BranchOpc, void SparcInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { unsigned numSubRegs = 0; unsigned movOpc = 0; const unsigned *subRegIdx = nullptr; diff --git a/llvm/lib/Target/Sparc/SparcInstrInfo.h b/llvm/lib/Target/Sparc/SparcInstrInfo.h index a7bb34c6c8e7..fc04542c819d 100644 --- a/llvm/lib/Target/Sparc/SparcInstrInfo.h +++ b/llvm/lib/Target/Sparc/SparcInstrInfo.h @@ -87,7 +87,8 @@ class SparcInstrInfo : public SparcGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, Register SrcReg, diff --git a/llvm/lib/Target/SystemZ/SystemZInstrInfo.cpp b/llvm/lib/Target/SystemZ/SystemZInstrInfo.cpp index 16bbfd44ef8a..f0f083464f54 100644 --- a/llvm/lib/Target/SystemZ/SystemZInstrInfo.cpp +++ b/llvm/lib/Target/SystemZ/SystemZInstrInfo.cpp @@ -858,7 +858,9 @@ bool SystemZInstrInfo::PredicateInstruction( void SystemZInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, + bool RenamableSrc) const { // Split 128-bit GPR moves into two 64-bit moves. Add implicit uses of the // super register in case one of the subregs is undefined. // This handles ADDR128 too. diff --git a/llvm/lib/Target/SystemZ/SystemZInstrInfo.h b/llvm/lib/Target/SystemZ/SystemZInstrInfo.h index 61338b081615..cc8a4ccd234c 100644 --- a/llvm/lib/Target/SystemZ/SystemZInstrInfo.h +++ b/llvm/lib/Target/SystemZ/SystemZInstrInfo.h @@ -276,7 +276,8 @@ class SystemZInstrInfo : public SystemZGenInstrInfo { ArrayRef Pred) const override; void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, Register SrcReg, bool isKill, int FrameIndex, diff --git a/llvm/lib/Target/VE/VEInstrInfo.cpp b/llvm/lib/Target/VE/VEInstrInfo.cpp index c001dc4d92b9..fccbed3bdec8 100644 --- a/llvm/lib/Target/VE/VEInstrInfo.cpp +++ b/llvm/lib/Target/VE/VEInstrInfo.cpp @@ -359,7 +359,8 @@ static void copyPhysSubRegs(MachineBasicBlock &MBB, void VEInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const { + bool KillSrc, bool RenamableDest, + bool RenamableSrc) const { if (IsAliasOfSX(SrcReg) && IsAliasOfSX(DestReg)) { BuildMI(MBB, I, DL, get(VE::ORri), DestReg) diff --git a/llvm/lib/Target/VE/VEInstrInfo.h b/llvm/lib/Target/VE/VEInstrInfo.h index 4fcc479a13d5..3a9718f2f260 100644 --- a/llvm/lib/Target/VE/VEInstrInfo.h +++ b/llvm/lib/Target/VE/VEInstrInfo.h @@ -81,7 +81,8 @@ class VEInstrInfo : public VEGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; /// Stack Spill & Reload { Register isLoadFromStackSlot(const MachineInstr &MI, diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp index 32a4accd040e..75011ab3c872 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp @@ -57,7 +57,9 @@ bool WebAssemblyInstrInfo::isReallyTriviallyReMaterializable( void WebAssemblyInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, + bool RenamableSrc) const { // This method is called by post-RA expansion, which expects only pregs to // exist. However we need to handle both here. auto &MRI = MBB.getParent()->getRegInfo(); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.h b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.h index c1e1a790c60e..8cb692f9bc0c 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.h @@ -47,7 +47,8 @@ class WebAssemblyInstrInfo final : public WebAssemblyGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; MachineInstr *commuteInstructionImpl(MachineInstr &MI, bool NewMI, unsigned OpIdx1, unsigned OpIdx2) const override; diff --git a/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp b/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp index 74804e5c9783..d0a54ab8993c 100644 --- a/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp +++ b/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp @@ -314,7 +314,8 @@ namespace { Disp = CurDAG->getTargetBlockAddress(AM.BlockAddr, MVT::i32, AM.Disp, AM.SymbolFlags); else - Disp = CurDAG->getTargetConstant(AM.Disp, DL, MVT::i32); + Disp = + CurDAG->getSignedConstant(AM.Disp, DL, MVT::i32, /*isTarget=*/true); if (AM.Segment.getNode()) Segment = AM.Segment; @@ -2130,7 +2131,7 @@ static bool foldMaskedShiftToScaledMask(SelectionDAG &DAG, SDValue N, X = NewX; } - SDValue NewMask = DAG.getConstant(Mask >> ShiftAmt, DL, VT); + SDValue NewMask = DAG.getSignedConstant(Mask >> ShiftAmt, DL, VT); SDValue NewAnd = DAG.getNode(ISD::AND, DL, VT, X, NewMask); SDValue NewShift = DAG.getNode(ISD::SHL, DL, VT, NewAnd, Shift.getOperand(1)); @@ -3733,7 +3734,8 @@ bool X86DAGToDAGISel::foldLoadStoreIntoMemOperand(SDNode *Node) { } if (MemVT != MVT::i64 || isInt<32>(OperandV)) { - Operand = CurDAG->getTargetConstant(OperandV, SDLoc(Node), MemVT); + Operand = CurDAG->getSignedConstant(OperandV, SDLoc(Node), MemVT, + /*isTarget=*/true); NewOpc = SelectImmOpcode(Opc); } } @@ -4507,7 +4509,7 @@ bool X86DAGToDAGISel::tryShrinkShlLogicImm(SDNode *N) { X = NewX; } - SDValue NewCst = CurDAG->getConstant(ShiftedVal, dl, NVT); + SDValue NewCst = CurDAG->getSignedConstant(ShiftedVal, dl, NVT); insertDAGNode(*CurDAG, SDValue(N, 0), NewCst); SDValue NewBinOp = CurDAG->getNode(Opcode, dl, NVT, X, NewCst); insertDAGNode(*CurDAG, SDValue(N, 0), NewBinOp); diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp index 4e3a181f9a3a..975f629c58af 100644 --- a/llvm/lib/Target/X86/X86ISelLowering.cpp +++ b/llvm/lib/Target/X86/X86ISelLowering.cpp @@ -10762,9 +10762,9 @@ static SDValue lowerShuffleAsBlend(const SDLoc &DL, MVT VT, SDValue V1, for (int i = 0, Size = Mask.size(); i < Size; ++i) for (int j = 0; j < Scale; ++j) VSELECTMask.push_back( - Mask[i] < 0 ? DAG.getUNDEF(MVT::i8) - : DAG.getConstant(Mask[i] < Size ? -1 : 0, DL, - MVT::i8)); + Mask[i] < 0 + ? DAG.getUNDEF(MVT::i8) + : DAG.getSignedConstant(Mask[i] < Size ? -1 : 0, DL, MVT::i8)); V1 = DAG.getBitcast(BlendVT, V1); V2 = DAG.getBitcast(BlendVT, V2); @@ -18649,7 +18649,7 @@ SDValue X86TargetLowering::LowerGlobalOrExternal(SDValue Op, SelectionDAG &DAG, // addition for it. if (Offset != 0) Result = DAG.getNode(ISD::ADD, dl, PtrVT, Result, - DAG.getConstant(Offset, dl, PtrVT)); + DAG.getSignedConstant(Offset, dl, PtrVT)); return Result; } @@ -23593,7 +23593,7 @@ static SDValue LowerVSETCC(SDValue Op, const X86Subtarget &Subtarget, if (!FlipSigns && !Invert && ISD::isBuildVectorAllOnes(Op1.getNode())) { Op0 = DAG.getBitcast(MVT::v4i32, Op0); - Op1 = DAG.getConstant(-1, dl, MVT::v4i32); + Op1 = DAG.getAllOnesConstant(dl, MVT::v4i32); SDValue GT = DAG.getNode(X86ISD::PCMPGT, dl, MVT::v4i32, Op0, Op1); static const int MaskHi[] = { 1, 1, 3, 3 }; @@ -24385,7 +24385,7 @@ static SDValue LowerSIGN_EXTEND_Mask(SDValue Op, const SDLoc &dl, (Subtarget.hasBWI() && WideEltVT.getSizeInBits() <= 16)) { V = DAG.getNode(Op.getOpcode(), dl, WideVT, In); } else { - SDValue NegOne = DAG.getConstant(-1, dl, WideVT); + SDValue NegOne = DAG.getAllOnesConstant(dl, WideVT); SDValue Zero = DAG.getConstant(0, dl, WideVT); V = DAG.getSelect(dl, WideVT, In, NegOne, Zero); } @@ -24969,9 +24969,9 @@ X86TargetLowering::LowerDYNAMIC_STACKALLOC(SDValue Op, Result = DAG.getNode(ISD::SUB, dl, VT, SP, Size); // Value } if (Alignment && *Alignment > StackAlign) - Result = - DAG.getNode(ISD::AND, dl, VT, Result, - DAG.getConstant(~(Alignment->value() - 1ULL), dl, VT)); + Result = DAG.getNode( + ISD::AND, dl, VT, Result, + DAG.getSignedConstant(~(Alignment->value() - 1ULL), dl, VT)); Chain = DAG.getCopyToReg(Chain, dl, SPReg, Result); // Output chain } else if (SplitStack) { MachineRegisterInfo &MRI = MF.getRegInfo(); @@ -25003,8 +25003,9 @@ X86TargetLowering::LowerDYNAMIC_STACKALLOC(SDValue Op, Chain = SP.getValue(1); if (Alignment) { - SP = DAG.getNode(ISD::AND, dl, VT, SP.getValue(0), - DAG.getConstant(~(Alignment->value() - 1ULL), dl, VT)); + SP = DAG.getNode( + ISD::AND, dl, VT, SP.getValue(0), + DAG.getSignedConstant(~(Alignment->value() - 1ULL), dl, VT)); Chain = DAG.getCopyToReg(Chain, dl, SPReg, SP); } @@ -26088,7 +26089,7 @@ SDValue X86TargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op, Op.getOperand(3)); } else { SDValue GenCF = DAG.getNode(X86ISD::ADD, dl, CFVTs, Op.getOperand(1), - DAG.getConstant(-1, dl, MVT::i8)); + DAG.getAllOnesConstant(dl, MVT::i8)); Res = DAG.getNode(IntrData->Opc0, dl, VTs, Op.getOperand(2), Op.getOperand(3), GenCF.getValue(1)); } @@ -29369,7 +29370,7 @@ static SDValue LowerShiftByScalarVariable(SDValue Op, SelectionDAG &DAG, // Create the mask using vXi16 shifts. For shift-rights we need to move // the upper byte down before splatting the vXi8 mask. - SDValue BitMask = DAG.getConstant(-1, dl, ExtVT); + SDValue BitMask = DAG.getAllOnesConstant(dl, ExtVT); BitMask = getTargetVShiftNode(LogicalX86Op, dl, ExtVT, BitMask, BaseShAmt, BaseShAmtIdx, Subtarget, DAG); if (Opcode != ISD::SHL) @@ -50519,7 +50520,7 @@ static SDValue combineAddOrSubToADCOrSBB(bool IsSub, const SDLoc &DL, EVT VT, // X - SETAE --> adc X, -1 return DAG.getNode(IsSub ? X86ISD::ADC : X86ISD::SBB, DL, DAG.getVTList(VT, MVT::i32), X, - DAG.getConstant(-1, DL, VT), EFLAGS); + DAG.getAllOnesConstant(DL, VT), EFLAGS); } if (CC == X86::COND_BE) { @@ -50540,7 +50541,7 @@ static SDValue combineAddOrSubToADCOrSBB(bool IsSub, const SDLoc &DL, EVT VT, SDValue NewEFLAGS = NewSub.getValue(EFLAGS.getResNo()); return DAG.getNode(IsSub ? X86ISD::ADC : X86ISD::SBB, DL, DAG.getVTList(VT, MVT::i32), X, - DAG.getConstant(-1, DL, VT), NewEFLAGS); + DAG.getAllOnesConstant(DL, VT), NewEFLAGS); } } @@ -50599,7 +50600,7 @@ static SDValue combineAddOrSubToADCOrSBB(bool IsSub, const SDLoc &DL, EVT VT, // X + (Z != 0) --> add X, (zext(setne Z, 0)) --> sbb X, -1, (cmp Z, 1) if (CC == X86::COND_NE) return DAG.getNode(IsSub ? X86ISD::ADC : X86ISD::SBB, DL, VTs, X, - DAG.getConstant(-1ULL, DL, VT), Cmp1.getValue(1)); + DAG.getAllOnesConstant(DL, VT), Cmp1.getValue(1)); // X - (Z == 0) --> sub X, (zext(sete Z, 0)) --> sbb X, 0, (cmp Z, 1) // X + (Z == 0) --> add X, (zext(sete Z, 0)) --> adc X, 0, (cmp Z, 1) @@ -50883,8 +50884,9 @@ static SDValue foldXorTruncShiftIntoCmp(SDNode *N, SelectionDAG &DAG) { const TargetLowering &TLI = DAG.getTargetLoweringInfo(); EVT SetCCResultType = TLI.getSetCCResultType(DAG.getDataLayout(), *DAG.getContext(), ResultType); - SDValue Cond = DAG.getSetCC(DL, SetCCResultType, ShiftOp, - DAG.getConstant(-1, DL, ShiftOpTy), ISD::SETGT); + SDValue Cond = + DAG.getSetCC(DL, SetCCResultType, ShiftOp, + DAG.getAllOnesConstant(DL, ShiftOpTy), ISD::SETGT); if (SetCCResultType != ResultType) Cond = DAG.getNode(ISD::ZERO_EXTEND, DL, ResultType, Cond); return Cond; @@ -52179,8 +52181,8 @@ static SDValue combineFMulcFCMulc(SDNode *N, SelectionDAG &DAG, if (XOR->getOpcode() == ISD::XOR && XOR.hasOneUse()) { KnownBits XORRHS = DAG.computeKnownBits(XOR.getOperand(1)); if (XORRHS.isConstant()) { - APInt ConjugationInt32 = APInt(32, 0x80000000, true); - APInt ConjugationInt64 = APInt(64, 0x8000000080000000ULL, true); + APInt ConjugationInt32 = APInt(32, 0x80000000); + APInt ConjugationInt64 = APInt(64, 0x8000000080000000ULL); if ((XORRHS.getBitWidth() == 32 && XORRHS.getConstant() == ConjugationInt32) || (XORRHS.getBitWidth() == 64 && @@ -52219,7 +52221,7 @@ static SDValue combineFaddCFmul(SDNode *N, SelectionDAG &DAG, Flags.hasNoSignedZeros(); }; auto IsVectorAllNegativeZero = [&DAG](SDValue Op) { - APInt AI = APInt(32, 0x80008000, true); + APInt AI = APInt(32, 0x80008000); KnownBits Bits = DAG.computeKnownBits(Op); return Bits.getBitWidth() == 32 && Bits.isConstant() && Bits.getConstant() == AI; diff --git a/llvm/lib/Target/X86/X86InstrInfo.cpp b/llvm/lib/Target/X86/X86InstrInfo.cpp index fab7c167e385..d7aad744083b 100644 --- a/llvm/lib/Target/X86/X86InstrInfo.cpp +++ b/llvm/lib/Target/X86/X86InstrInfo.cpp @@ -4238,7 +4238,8 @@ static unsigned CopyToFromAsymmetricReg(unsigned DestReg, unsigned SrcReg, void X86InstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { // First deal with the normal symmetric copies. bool HasAVX = Subtarget.hasAVX(); bool HasVLX = Subtarget.hasVLX(); diff --git a/llvm/lib/Target/X86/X86InstrInfo.h b/llvm/lib/Target/X86/X86InstrInfo.h index eaa3dd089394..0573229d4ccf 100644 --- a/llvm/lib/Target/X86/X86InstrInfo.h +++ b/llvm/lib/Target/X86/X86InstrInfo.h @@ -418,7 +418,8 @@ class X86InstrInfo final : public X86GenInstrInfo { Register FalseReg) const override; void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, Register SrcReg, bool isKill, int FrameIndex, diff --git a/llvm/lib/Target/XCore/XCoreInstrInfo.cpp b/llvm/lib/Target/XCore/XCoreInstrInfo.cpp index ae2e0fec3f89..90a195e928a5 100644 --- a/llvm/lib/Target/XCore/XCoreInstrInfo.cpp +++ b/llvm/lib/Target/XCore/XCoreInstrInfo.cpp @@ -331,7 +331,8 @@ XCoreInstrInfo::removeBranch(MachineBasicBlock &MBB, int *BytesRemoved) const { void XCoreInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { bool GRDest = XCore::GRRegsRegClass.contains(DestReg); bool GRSrc = XCore::GRRegsRegClass.contains(SrcReg); diff --git a/llvm/lib/Target/XCore/XCoreInstrInfo.h b/llvm/lib/Target/XCore/XCoreInstrInfo.h index 1dafb6ea7d21..7f330539dd76 100644 --- a/llvm/lib/Target/XCore/XCoreInstrInfo.h +++ b/llvm/lib/Target/XCore/XCoreInstrInfo.h @@ -64,7 +64,8 @@ class XCoreInstrInfo : public XCoreGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, Register SrcReg, diff --git a/llvm/lib/TargetParser/Triple.cpp b/llvm/lib/TargetParser/Triple.cpp index 21d6c74b5956..d5af903167be 100644 --- a/llvm/lib/TargetParser/Triple.cpp +++ b/llvm/lib/TargetParser/Triple.cpp @@ -86,6 +86,9 @@ StringRef Triple::getArchTypeName(ArchType Kind) { case x86_64: return "x86_64"; case xcore: return "xcore"; case xtensa: return "xtensa"; +// EVM local begin + case evm: return "evm"; +// EVM local end } llvm_unreachable("Invalid ArchType!"); @@ -233,6 +236,9 @@ StringRef Triple::getArchTypePrefix(ArchType Kind) { case dxil: return "dx"; case xtensa: return "xtensa"; + // EVM local begin + case evm: return "evm"; + // EVM local end } } @@ -461,6 +467,9 @@ Triple::ArchType Triple::getArchTypeForLLVMName(StringRef Name) { .Case("loongarch64", loongarch64) .Case("dxil", dxil) .Case("xtensa", xtensa) + // EVM local begin + .Case("evm", evm) + // EVM local end .Default(UnknownArch); } @@ -609,6 +618,9 @@ static Triple::ArchType parseArch(StringRef ArchName) { "dxilv1.4", "dxilv1.5", "dxilv1.6", "dxilv1.7", "dxilv1.8", Triple::dxil) .Case("xtensa", Triple::xtensa) + // EVM local begin + .Case("evm", Triple::evm) + // EVM local end .Default(Triple::UnknownArch); // Some architectures require special parsing logic just to compute the @@ -945,6 +957,9 @@ static Triple::ObjectFormatType getDefaultFormat(const Triple &T) { case Triple::ve: case Triple::xcore: case Triple::xtensa: + // EVM local begin + case Triple::evm: + // EVM local end return Triple::ELF; case Triple::ppc64: @@ -1663,6 +1678,11 @@ unsigned Triple::getArchPointerBitWidth(llvm::Triple::ArchType Arch) { case llvm::Triple::wasm64: case llvm::Triple::x86_64: return 64; + + // EVM local begin + case llvm::Triple::evm: + return 256; + // EVM local end } llvm_unreachable("Invalid architecture value"); } @@ -1690,6 +1710,9 @@ Triple Triple::get32BitArchVariant() const { case Triple::msp430: case Triple::systemz: case Triple::ve: + // EVM local begin + case Triple::evm: + // EVM local end T.setArch(UnknownArch); break; @@ -1780,6 +1803,9 @@ Triple Triple::get64BitArchVariant() const { case Triple::tcele: case Triple::xcore: case Triple::xtensa: + // EVM local begin + case Triple::evm: + // EVM local end T.setArch(UnknownArch); break; @@ -1883,6 +1909,9 @@ Triple Triple::getBigEndianArchVariant() const { case Triple::ve: case Triple::csky: case Triple::xtensa: + // EVM local begin + case Triple::evm: + // EVM local end // ARM is intentionally unsupported here, changing the architecture would // drop any arch suffixes. diff --git a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp index 99ec50aa4775..bac713021a99 100644 --- a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp +++ b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp @@ -100,7 +100,8 @@ using OffsetAndArgPart = std::pair; static Value *createByteGEP(IRBuilderBase &IRB, const DataLayout &DL, Value *Ptr, Type *ResElemTy, int64_t Offset) { if (Offset != 0) { - APInt APOffset(DL.getIndexTypeSizeInBits(Ptr->getType()), Offset); + APInt APOffset(DL.getIndexTypeSizeInBits(Ptr->getType()), Offset, + /*isSigned=*/true); Ptr = IRB.CreatePtrAdd(Ptr, IRB.getInt(APOffset)); } return Ptr; diff --git a/llvm/lib/Transforms/InstCombine/CMakeLists.txt b/llvm/lib/Transforms/InstCombine/CMakeLists.txt index 6ab5e6da21cf..ae0ae4131b18 100644 --- a/llvm/lib/Transforms/InstCombine/CMakeLists.txt +++ b/llvm/lib/Transforms/InstCombine/CMakeLists.txt @@ -26,5 +26,6 @@ add_llvm_component_library(LLVMInstCombine Analysis Core Support + TargetParser # EVM local TransformUtils ) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp index 0a55f4762fdf..977fb778cdfc 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp @@ -1338,14 +1338,10 @@ Instruction *InstCombinerImpl:: // low bits to skip = shift bitwidth - high bits to extract // The shift amount itself may be extended, and we need to look past zero-ext // when matching NBits, that will matter for matching later. - Constant *C; Value *NBits; - if (!match( - LowBitsToSkip, - m_ZExtOrSelf(m_Sub(m_Constant(C), m_ZExtOrSelf(m_Value(NBits))))) || - !match(C, m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_EQ, - APInt(C->getType()->getScalarSizeInBits(), - X->getType()->getScalarSizeInBits())))) + if (!match(LowBitsToSkip, + m_ZExtOrSelf(m_Sub(m_SpecificInt(XTy->getScalarSizeInBits()), + m_ZExtOrSelf(m_Value(NBits)))))) return nullptr; // Sign-extending value can be zero-extended if we `sub`tract it, diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp index 3223fccbcf49..4a297c689877 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -64,6 +64,9 @@ #include "llvm/Support/KnownBits.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" +// EVM local begin +#include "llvm/TargetParser/Triple.h" +// EVM local end #include "llvm/Transforms/InstCombine/InstCombiner.h" #include "llvm/Transforms/Utils/AssumeBundleBuilder.h" #include "llvm/Transforms/Utils/Local.h" @@ -156,6 +159,14 @@ Instruction *InstCombinerImpl::SimplifyAnyMemTransfer(AnyMemTransferInst *MI) { uint64_t Size = MemOpLength->getLimitedValue(); assert(Size && "0-sized memory transferring should be removed already."); + // EVM local begin + // For EVM we do not need to expand memcpy at all, as the possible memcpy + // operations are 1 to 1 mapped to corresponding instructions. + Triple TT(MI->getFunction()->getParent()->getTargetTriple()); + if (TT.isEVM()) + return nullptr; + // EVM local end + if (Size > 8 || (Size&(Size-1))) return nullptr; // If not 1/2/4/8 bytes, exit. @@ -260,13 +271,11 @@ Instruction *InstCombinerImpl::SimplifyAnyMemSet(AnyMemSetInst *MI) { // memset(s,c,n) -> store s, c (for n=1,2,4,8) if (Len <= 8 && isPowerOf2_32((uint32_t)Len)) { - Type *ITy = IntegerType::get(MI->getContext(), Len*8); // n=1 -> i8. - Value *Dest = MI->getDest(); // Extract the fill value and store. - const uint64_t Fill = FillC->getZExtValue()*0x0101010101010101ULL; - Constant *FillVal = ConstantInt::get(ITy, Fill); + Constant *FillVal = ConstantInt::get( + MI->getContext(), APInt::getSplat(Len * 8, FillC->getValue())); StoreInst *S = Builder.CreateStore(FillVal, Dest, MI->isVolatile()); S->copyMetadata(*MI, LLVMContext::MD_DIAssignID); auto replaceOpForAssignmentMarkers = [FillC, FillVal](auto *DbgAssign) { diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index abadf54a9676..669f8b6f5f77 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -27,6 +27,9 @@ #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/PatternMatch.h" #include "llvm/Support/KnownBits.h" +// EVM local begin +#include "llvm/TargetParser/Triple.h" +// EVM local end #include "llvm/Transforms/InstCombine/InstCombiner.h" #include @@ -312,7 +315,7 @@ Instruction *InstCombinerImpl::foldCmpLoadFromIndexedGlobal( DL.getTypeAllocSize(Init->getType()->getArrayElementType()); auto MaskIdx = [&](Value *Idx) { if (!GEP->isInBounds() && llvm::countr_zero(ElementSize) != 0) { - Value *Mask = ConstantInt::get(Idx->getType(), -1); + Value *Mask = Constant::getAllOnesValue(Idx->getType()); Mask = Builder.CreateLShr(Mask, llvm::countr_zero(ElementSize)); Idx = Builder.CreateAnd(Idx, Mask); } @@ -3035,12 +3038,12 @@ Instruction *InstCombinerImpl::foldICmpAddConstant(ICmpInst &Cmp, unsigned BW = C.getBitWidth(); std::bitset<4> Table; auto ComputeTable = [&](bool Op0Val, bool Op1Val) { - int Res = 0; + APInt Res(BW, 0); if (Op0Val) - Res += isa(Ext0) ? 1 : -1; + Res += APInt(BW, isa(Ext0) ? 1 : -1, /*isSigned=*/true); if (Op1Val) - Res += isa(Ext1) ? 1 : -1; - return ICmpInst::compare(APInt(BW, Res, true), C, Pred); + Res += APInt(BW, isa(Ext1) ? 1 : -1, /*isSigned=*/true); + return ICmpInst::compare(Res, C, Pred); }; Table[0] = ComputeTable(false, false); @@ -5354,9 +5357,24 @@ Instruction *InstCombinerImpl::foldICmpBinOp(ICmpInst &I, return new ICmpInst(ICmpInst::getSignedPredicate(Pred), Constant::getNullValue(Op0->getType()), Op0); } - + // EVM local begin + // Disable generation of llvm.?mul.with.overflow intrinsic for EVM, as + // its default lowering in ISel triggers an assertion: + // + // LC != RTLIB::UNKNOWN_LIBCALL && "Cannot expand this operation!" + // + // when expanding ISD::?MULO. While we could expand MULO using MUL_LOHI, + // we would first need to custom lower the MUL_LOHI operation, which + // requires a dozen instructions. + // TODO: #794: Investigate whether it is reasonable to enable the generation. + Triple TT(I.getFunction()->getParent()->getTargetTriple()); + if (!TT.isEVM()) { + // EVM local end if (Value *V = foldMultiplicationOverflowCheck(I)) return replaceInstUsesWith(I, V); + // EVM local begin + } + // EVM local end if (Instruction *R = foldICmpAndXX(I, Q, *this)) return R; @@ -7583,6 +7601,18 @@ Instruction *InstCombinerImpl::visitICmpInst(ICmpInst &I) { !ACXI->isWeak()) return ExtractValueInst::Create(ACXI, 1); + + // EVM local begin + { + Value *X = nullptr; + // -X > ~X --> X != 0 + if (Pred == ICmpInst::ICMP_UGT && match(Op0, m_Neg(m_Value(X))) && + match(Op1, m_Not(m_Specific(X)))) + return new ICmpInst(ICmpInst::ICMP_NE, X, + ConstantInt::getNullValue(Op0->getType())); + } + // EVM local end + if (Instruction *Res = foldICmpWithHighBitMask(I, Builder)) return Res; diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp index aaf4ece3249a..da31846a723b 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp @@ -681,11 +681,11 @@ static Value *foldSelectICmpLshrAshr(const ICmpInst *IC, Value *TrueVal, Value *X, *Y; unsigned Bitwidth = CmpRHS->getType()->getScalarSizeInBits(); if ((Pred != ICmpInst::ICMP_SGT || - !match(CmpRHS, - m_SpecificInt_ICMP(ICmpInst::ICMP_SGE, APInt(Bitwidth, -1)))) && + !match(CmpRHS, m_SpecificInt_ICMP(ICmpInst::ICMP_SGE, + APInt::getAllOnes(Bitwidth)))) && (Pred != ICmpInst::ICMP_SLT || - !match(CmpRHS, - m_SpecificInt_ICMP(ICmpInst::ICMP_SGE, APInt(Bitwidth, 0))))) + !match(CmpRHS, m_SpecificInt_ICMP(ICmpInst::ICMP_SGE, + APInt::getZero(Bitwidth))))) return nullptr; // Canonicalize so that ashr is in FalseVal. diff --git a/llvm/lib/Transforms/Scalar/CMakeLists.txt b/llvm/lib/Transforms/Scalar/CMakeLists.txt index ba09ebf8b04c..83ff552f114e 100644 --- a/llvm/lib/Transforms/Scalar/CMakeLists.txt +++ b/llvm/lib/Transforms/Scalar/CMakeLists.txt @@ -56,6 +56,9 @@ add_llvm_component_library(LLVMScalarOpts MakeGuardsExplicit.cpp MemCpyOptimizer.cpp MergeICmps.cpp + # EVM local begin + MergeIdenticalBB.cpp + # EVM local end MergedLoadStoreMotion.cpp NaryReassociate.cpp NewGVN.cpp @@ -70,6 +73,9 @@ add_llvm_component_library(LLVMScalarOpts Scalarizer.cpp ScalarizeMaskedMemIntrin.cpp SeparateConstOffsetFromGEP.cpp + # EVM local begin + SHA3ConstFolding.cpp + # EVM local end SimpleLoopUnswitch.cpp SimplifyCFGPass.cpp Sink.cpp @@ -96,5 +102,6 @@ add_llvm_component_library(LLVMScalarOpts Core InstCombine Support + TargetParser # EVM local TransformUtils ) diff --git a/llvm/lib/Transforms/Scalar/ConstantHoisting.cpp b/llvm/lib/Transforms/Scalar/ConstantHoisting.cpp index 4a6dedc93d30..61c6193bfd9f 100644 --- a/llvm/lib/Transforms/Scalar/ConstantHoisting.cpp +++ b/llvm/lib/Transforms/Scalar/ConstantHoisting.cpp @@ -533,25 +533,6 @@ void ConstantHoistingPass::collectConstantCandidates(Function &Fn) { } } -// This helper function is necessary to deal with values that have different -// bit widths (APInt Operator- does not like that). If the value cannot be -// represented in uint64 we return an "empty" APInt. This is then interpreted -// as the value is not in range. -static std::optional calculateOffsetDiff(const APInt &V1, - const APInt &V2) { - std::optional Res; - unsigned BW = V1.getBitWidth() > V2.getBitWidth() ? - V1.getBitWidth() : V2.getBitWidth(); - uint64_t LimVal1 = V1.getLimitedValue(); - uint64_t LimVal2 = V2.getLimitedValue(); - - if (LimVal1 == ~0ULL || LimVal2 == ~0ULL) - return Res; - - uint64_t Diff = LimVal1 - LimVal2; - return APInt(BW, Diff, true); -} - // From a list of constants, one needs to picked as the base and the other // constants will be transformed into an offset from that base constant. The // question is which we can pick best? For example, consider these constants @@ -608,16 +589,13 @@ ConstantHoistingPass::maximizeConstantsInRange(ConstCandVecType::iterator S, LLVM_DEBUG(dbgs() << "Cost: " << Cost << "\n"); for (auto C2 = S; C2 != E; ++C2) { - std::optional Diff = calculateOffsetDiff( - C2->ConstInt->getValue(), ConstCand->ConstInt->getValue()); - if (Diff) { - const InstructionCost ImmCosts = - TTI->getIntImmCodeSizeCost(Opcode, OpndIdx, *Diff, Ty); - Cost -= ImmCosts; - LLVM_DEBUG(dbgs() << "Offset " << *Diff << " " - << "has penalty: " << ImmCosts << "\n" - << "Adjusted cost: " << Cost << "\n"); - } + APInt Diff = C2->ConstInt->getValue() - ConstCand->ConstInt->getValue(); + const InstructionCost ImmCosts = + TTI->getIntImmCodeSizeCost(Opcode, OpndIdx, Diff, Ty); + Cost -= ImmCosts; + LLVM_DEBUG(dbgs() << "Offset " << Diff << " " + << "has penalty: " << ImmCosts << "\n" + << "Adjusted cost: " << Cost << "\n"); } } LLVM_DEBUG(dbgs() << "Cumulative cost: " << Cost << "\n"); diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp index d1c80aa67124..5bb4dc8c9e23 100644 --- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp +++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp @@ -875,7 +875,7 @@ void ConstraintInfo::transferToOtherSystem( addFact(CmpInst::ICMP_ULT, A, B, NumIn, NumOut, DFSInStack); break; case CmpInst::ICMP_SGT: { - if (doesHold(CmpInst::ICMP_SGE, B, ConstantInt::get(B->getType(), -1))) + if (doesHold(CmpInst::ICMP_SGE, B, Constant::getAllOnesValue(B->getType()))) addFact(CmpInst::ICMP_UGE, A, ConstantInt::get(B->getType(), 0), NumIn, NumOut, DFSInStack); if (IsKnownNonNegative(B)) diff --git a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp index 95de8eceb6be..4f8030b0dc79 100644 --- a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp +++ b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp @@ -832,9 +832,7 @@ static bool expandUDivOrURem(BinaryOperator *Instr, const ConstantRange &XCR, // Even if we don't know X's range, the divisor may be so large, X can't ever // be 2x larger than that. I.e. if divisor is always negative. - if (!XCR.icmp(ICmpInst::ICMP_ULT, - YCR.umul_sat(APInt(YCR.getBitWidth(), 2))) && - !YCR.isAllNegative()) + if (!XCR.icmp(ICmpInst::ICMP_ULT, YCR.uadd_sat(YCR)) && !YCR.isAllNegative()) return false; IRBuilder<> B(Instr); diff --git a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp index 931606c6f8fe..7cccce072187 100644 --- a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp +++ b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp @@ -63,6 +63,9 @@ #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" +// EVM local begin +#include "llvm/IR/IntrinsicsEVM.h" +// EVM local begin #include "llvm/IR/Module.h" #include "llvm/IR/PassManager.h" #include "llvm/IR/PatternMatch.h" @@ -73,6 +76,9 @@ #include "llvm/Support/DebugCounter.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" +// EVM local begin +#include "llvm/TargetParser/Triple.h" +// EVM local end #include "llvm/Transforms/Utils/AssumeBundleBuilder.h" #include "llvm/Transforms/Utils/BuildLibCalls.h" #include "llvm/Transforms/Utils/Local.h" @@ -1801,6 +1807,29 @@ struct DSEState { return false; } + /// Verify whether there is a path from the block containing the store to a + /// return block, i.e., a block that does not end with 'unreachable'. + bool isOnPathToFunctionReturn(const MemoryDef *MaybeDeadDef) { + const BasicBlock *BB = MaybeDeadDef->getBlock(); + // Handle the trivial case where the store resides in a block that ends + // with 'unreachable'. + if (isa(BB->getTerminator())) + return false; + + // Handle the general case by performing a depth-first traversal on the + // inverse control flow graph (CFG), starting from a return block and + // continuing until the block containing the store is reached. + for (const BasicBlock *R : PDT.roots()) { + if (isa(R->getTerminator())) + continue; + + for (auto I = idf_begin(R), E = idf_end(R); I != E; ++I) + if (*I == BB) + return true; + } + return false; + } + /// Eliminate writes to objects that are not visible in the caller and are not /// accessed before returning from the function. bool eliminateDeadWritesAtEndOfFunction() { @@ -1828,8 +1857,23 @@ struct DSEState { // underlying objects is very uncommon. If it turns out to be important, // we can use getUnderlyingObjects here instead. const Value *UO = getUnderlyingObject(DefLoc->Ptr); - if (!isInvisibleToCallerAfterRet(UO)) - continue; + if (!isInvisibleToCallerAfterRet(UO)) { + // EVM local begin + // For EVM verify that all paths originating from the basic block + // containing the store eventually lead to blocks that terminate with + // an 'unreachable' instruction. If, in addition, the store is not + // read before the function returns (as determined by + // isWriteAtEndOfFunction), then the store can be safely eliminated. + // TODO: Evaluate this transformation across all targets + // (i.e., without limiting it to EVM) to assess whether it could be + // upstreamed. At first glance, this appears to be EVM-specific, + // since in the general case it is not guaranteed that memory side + // effects preceding an unreachable can be safely ignored. + Triple TT(DefI->getFunction()->getParent()->getTargetTriple()); + if (!TT.isEVM() || isOnPathToFunctionReturn(Def)) + continue; + // EVM local end + } if (isWriteAtEndOfFunction(Def, *DefLoc)) { // See through pointer-to-pointer bitcasts diff --git a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp index 5e2131b0b180..ed7e76e9c10e 100644 --- a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp +++ b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp @@ -357,19 +357,19 @@ bool IndVarSimplify::handleFloatingPointIV(Loop *L, PHINode *PN) { // Insert new integer induction variable. PHINode *NewPHI = PHINode::Create(Int32Ty, 2, PN->getName() + ".int", PN->getIterator()); - NewPHI->addIncoming(ConstantInt::get(Int32Ty, InitValue), + NewPHI->addIncoming(ConstantInt::getSigned(Int32Ty, InitValue), PN->getIncomingBlock(IncomingEdge)); NewPHI->setDebugLoc(PN->getDebugLoc()); - Instruction *NewAdd = - BinaryOperator::CreateAdd(NewPHI, ConstantInt::get(Int32Ty, IncValue), - Incr->getName() + ".int", Incr->getIterator()); + Instruction *NewAdd = BinaryOperator::CreateAdd( + NewPHI, ConstantInt::getSigned(Int32Ty, IncValue), + Incr->getName() + ".int", Incr->getIterator()); NewAdd->setDebugLoc(Incr->getDebugLoc()); NewPHI->addIncoming(NewAdd, PN->getIncomingBlock(BackEdge)); - ICmpInst *NewCompare = - new ICmpInst(TheBr->getIterator(), NewPred, NewAdd, - ConstantInt::get(Int32Ty, ExitValue), Compare->getName()); + ICmpInst *NewCompare = new ICmpInst( + TheBr->getIterator(), NewPred, NewAdd, + ConstantInt::getSigned(Int32Ty, ExitValue), Compare->getName()); NewCompare->setDebugLoc(Compare->getDebugLoc()); // In the following deletions, PN may become dead and may be deleted. diff --git a/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp b/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp index 0ee1afa76a82..8ead63ab2e99 100644 --- a/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp +++ b/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp @@ -567,8 +567,13 @@ bool LoopIdiomRecognize::runOnLoopBlock( // Look for a single store or sets of stores with a common base, which can be // optimized into a memset (memset_pattern). The latter most commonly happens // with structs and handunrolled loops. + // EVM local begin + // TODO: Support memset +#if 0 for (auto &SL : StoreRefsForMemset) MadeChange |= processLoopStores(SL.second, BECount, ForMemset::Yes); +#endif + // EVM local end for (auto &SL : StoreRefsForMemsetPattern) MadeChange |= processLoopStores(SL.second, BECount, ForMemset::No); @@ -579,8 +584,13 @@ bool LoopIdiomRecognize::runOnLoopBlock( MadeChange |= processLoopMemIntrinsic( BB, &LoopIdiomRecognize::processLoopMemCpy, BECount); + // EVM local begin + // TODO: Support memset +#if 0 MadeChange |= processLoopMemIntrinsic( BB, &LoopIdiomRecognize::processLoopMemSet, BECount); +#endif + // EVM local end return MadeChange; } diff --git a/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp b/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp index cee34f0a6da1..f4e495c9eed9 100644 --- a/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp +++ b/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp @@ -836,6 +836,11 @@ bool MemCpyOptPass::processStore(StoreInst *SI, BasicBlock::iterator &BBI) { } bool MemCpyOptPass::processMemSet(MemSetInst *MSI, BasicBlock::iterator &BBI) { + // EVM local begin + if (isa(MSI->getLength()) && + cast(MSI->getLength())->getValue().getSignificantBits() > 64) + return false; + // EVM local end // See if there is another memset or store neighboring this memset which // allows us to widen out the memset to do a single larger store. if (isa(MSI->getLength()) && !MSI->isVolatile()) @@ -1125,6 +1130,12 @@ bool MemCpyOptPass::performCallSlotOptzn(Instruction *cpyLoad, bool MemCpyOptPass::processMemCpyMemCpyDependence(MemCpyInst *M, MemCpyInst *MDep, BatchAAResults &BAA) { + // EVM local begin + if (isa(M->getLength()) && + cast(M->getLength())->getValue().getSignificantBits() > 64) + return false; + // EVM local end + // If dep instruction is reading from our current input, then it is a noop // transfer and substituting the input won't change this instruction. Just // ignore the input and let someone else zap MDep. This handles cases like: @@ -1723,6 +1734,11 @@ static bool isZeroSize(Value *Size) { /// circumstances). This allows later passes to remove the first memcpy /// altogether. bool MemCpyOptPass::processMemCpy(MemCpyInst *M, BasicBlock::iterator &BBI) { + // EVM local begin + if (isa(M->getLength()) && + cast(M->getLength())->getValue().getSignificantBits() > 64) + return false; + // EVM local end // We can only optimize non-volatile memcpy's. if (M->isVolatile()) return false; @@ -1857,6 +1873,11 @@ bool MemCpyOptPass::processMemCpy(MemCpyInst *M, BasicBlock::iterator &BBI) { /// Transforms memmove calls to memcpy calls when the src/dst are guaranteed /// not to alias. bool MemCpyOptPass::processMemMove(MemMoveInst *M) { + // EVM local begin + if (isa(M->getLength()) && + cast(M->getLength())->getValue().getSignificantBits() > 64) + return false; + // EVM local end // See if the source could be modified by this memmove potentially. if (isModSet(AA->getModRefInfo(M, MemoryLocation::getForSource(M)))) return false; @@ -1904,6 +1925,10 @@ bool MemCpyOptPass::processByValArgument(CallBase &CB, unsigned ArgNo) { // The length of the memcpy must be larger or equal to the size of the byval. auto *C1 = dyn_cast(MDep->getLength()); + // EVM local begin + if (C1 && C1->getValue().getSignificantBits() > 64) + return false; + // EVM local end if (!C1 || !TypeSize::isKnownGE( TypeSize::getFixed(C1->getValue().getZExtValue()), ByValSize)) return false; diff --git a/llvm/lib/Transforms/Scalar/MergeIdenticalBB.cpp b/llvm/lib/Transforms/Scalar/MergeIdenticalBB.cpp new file mode 100644 index 000000000000..3bc7c38efbb5 --- /dev/null +++ b/llvm/lib/Transforms/Scalar/MergeIdenticalBB.cpp @@ -0,0 +1,242 @@ +//===------------- MergeIdenticalBB.cpp - Identical BB merging pass +//-----------===// +// +// 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 merge of similar basic blocks. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Scalar/MergeIdenticalBB.h" + +#include "llvm/ADT/MapVector.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/Analysis/AssumptionCache.h" +#include "llvm/Analysis/CFG.h" +#include "llvm/Analysis/DomTreeUpdater.h" +#include "llvm/Analysis/GlobalsModRef.h" +#include "llvm/Analysis/TargetTransformInfo.h" +#include "llvm/IR/Attributes.h" +#include "llvm/IR/CFG.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/ValueHandle.h" +#include "llvm/InitializePasses.h" +#include "llvm/Pass.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/Local.h" +#include +using namespace llvm; + +#define DEBUG_TYPE "mergebb" + +/// Maximum number of candidate blocks with same hash to consider for merging. +static const unsigned MaxBlockMergeCandidates = 32; + +STATISTIC(NumBlocksMerged, "Number of blocks merged"); + +/// Check whether replacing BB with ReplacementBB would result in a CallBr +/// instruction with a duplicate destination in one of the predecessors. +// FIXME: See note in CodeGenPrepare.cpp. +bool wouldDuplicateCallBrDest(const BasicBlock &BB, + const BasicBlock &ReplacementBB) { + for (const BasicBlock *Pred : predecessors(&BB)) { + if (auto *CBI = dyn_cast(Pred->getTerminator())) + for (const BasicBlock *Succ : successors(CBI)) + if (&ReplacementBB == Succ) + return true; + } + return false; +} + +class HashAccumulator64 { + uint64_t Hash; + +public: + HashAccumulator64() { Hash = 0x6acaa36bef8325c5ULL; } + void add(uint64_t V) { Hash = hashing::detail::hash_16_bytes(Hash, V); } + uint64_t getHash() { return Hash; } +}; + +static uint64_t hashBlock(const BasicBlock &BB) { + HashAccumulator64 Acc; + for (const Instruction &I : BB) + Acc.add(I.getOpcode()); + for (const BasicBlock *Succ : successors(&BB)) + Acc.add((uintptr_t)Succ); + return Acc.getHash(); +} + +static bool canMergeBlocks(const BasicBlock &BB1, const BasicBlock &BB2) { + // Quickly bail out if successors don't match. + if (!std::equal( + succ_begin(&BB1), succ_end(&BB1), succ_begin(&BB2), + [](const BasicBlock *S1, const BasicBlock *S2) { return S1 == S2; })) + return false; + + // Map from instructions in one block to instructions in the other. + SmallDenseMap Map; + auto ValuesEqual = [&Map](const Value *V1, const Value *V2) { + if (V1 == V2) + return true; + + if (const auto *I1 = dyn_cast(V1)) + if (const auto *I2 = dyn_cast(V2)) + if (const Instruction *Mapped = Map.lookup(I1)) + if (Mapped == I2) + return true; + + return false; + }; + + auto InstructionsEqual = [&](const Instruction &I1, const Instruction &I2) { + if (!I1.isSameOperationAs(&I2)) + return false; + + if (const auto *Call = dyn_cast(&I1)) + if (Call->cannotMerge()) + return false; + + if (!std::equal(I1.op_begin(), I1.op_end(), I2.op_begin(), I2.op_end(), + ValuesEqual)) + return false; + + if (const PHINode *PHI1 = dyn_cast(&I2)) { + const PHINode *PHI2 = cast(&I2); + return std::equal(PHI1->block_begin(), PHI1->block_end(), + PHI2->block_begin(), PHI2->block_end()); + } + + if (!I1.use_empty()) + Map.insert({&I1, &I2}); + return true; + }; + auto It1 = BB1.instructionsWithoutDebug(); + auto It2 = BB2.instructionsWithoutDebug(); + if (!std::equal(It1.begin(), It1.end(), It2.begin(), It2.end(), + InstructionsEqual)) + return false; + + // Make sure phi values in successor blocks match. + for (const BasicBlock *Succ : successors(&BB1)) { + for (const PHINode &Phi : Succ->phis()) { + const Value *Incoming1 = Phi.getIncomingValueForBlock(&BB1); + const Value *Incoming2 = Phi.getIncomingValueForBlock(&BB2); + if (!ValuesEqual(Incoming1, Incoming2)) + return false; + } + } + + if (wouldDuplicateCallBrDest(BB1, BB2)) + return false; + + return true; +} + +static bool tryMergeTwoBlocks(BasicBlock &BB1, BasicBlock &BB2) { + if (!canMergeBlocks(BB1, BB2)) + return false; + + // We will keep BB1 and drop BB2. Merge metadata and attributes. + for (auto Insts : llvm::zip(BB1, BB2)) { + Instruction &I1 = std::get<0>(Insts); + Instruction &I2 = std::get<1>(Insts); + + I1.andIRFlags(&I2); + combineMetadataForCSE(&I1, &I2, true); + if (!isa(&I1)) + I1.applyMergedLocation(I1.getDebugLoc(), I2.getDebugLoc()); + } + + // Store predecessors, because they will be modified in this loop. + SmallVector Preds(predecessors(&BB2)); + for (BasicBlock *Pred : Preds) + Pred->getTerminator()->replaceSuccessorWith(&BB2, &BB1); + + for (BasicBlock *Succ : successors(&BB2)) + Succ->removePredecessor(&BB2); + + BB2.eraseFromParent(); + return true; +} + +static bool mergeIdenticalBlocks(Function &F) { + bool Changed = false; + SmallDenseMap> SameHashBlocks; + + for (BasicBlock &BB : make_early_inc_range(F)) { + // The entry block cannot be merged. + if (&BB == &F.getEntryBlock()) + continue; + + // Identify potential merging candidates based on a basic block hash. + bool Merged = false; + auto &Blocks = SameHashBlocks.try_emplace(hashBlock(BB)).first->second; + for (BasicBlock *Block : Blocks) { + if (tryMergeTwoBlocks(*Block, BB)) { + Merged = true; + ++NumBlocksMerged; + break; + } + } + + Changed |= Merged; + if (!Merged && Blocks.size() < MaxBlockMergeCandidates) + Blocks.push_back(&BB); + } + + // TODO: Merge iteratively. + return Changed; +} + +static bool mergeIdenticalBBImpl(Function &F, const TargetTransformInfo &TTI, + DominatorTree *DT) { + DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Eager); + + bool EverChanged = removeUnreachableBlocks(F, DT ? &DTU : nullptr); + EverChanged |= mergeIdenticalBlocks(F); + + return EverChanged; +} + +static bool mergeIdenticalBB(Function &F, const TargetTransformInfo &TTI, + DominatorTree *DT) { + assert((!RequireAndPreserveDomTree || + (DT && DT->verify(DominatorTree::VerificationLevel::Full))) && + "Original domtree is invalid?"); + + bool Changed = mergeIdenticalBBImpl(F, TTI, DT); + + assert((!RequireAndPreserveDomTree || + (DT && DT->verify(DominatorTree::VerificationLevel::Full))) && + "Failed to maintain validity of domtree!"); + + return Changed; +} + +PreservedAnalyses MergeIdenticalBBPass::run(Function &F, + FunctionAnalysisManager &AM) { + auto &TTI = AM.getResult(F); + DominatorTree *DT = nullptr; + if (RequireAndPreserveDomTree) + DT = &AM.getResult(F); + if (!mergeIdenticalBB(F, TTI, DT)) + return PreservedAnalyses::all(); + PreservedAnalyses PA; + if (RequireAndPreserveDomTree) + PA.preserve(); + return PA; +} + diff --git a/llvm/lib/Transforms/Scalar/NewGVN.cpp b/llvm/lib/Transforms/Scalar/NewGVN.cpp index fc0b31c43396..82b2c50b4583 100644 --- a/llvm/lib/Transforms/Scalar/NewGVN.cpp +++ b/llvm/lib/Transforms/Scalar/NewGVN.cpp @@ -4098,7 +4098,9 @@ bool NewGVN::eliminateInstructions(Function &F) { U->set(DominatingLeader); // This is now a use of the dominating leader, which means if the // dominating leader was dead, it's now live! - auto &LeaderUseCount = UseCounts[DominatingLeader]; + // EVM local begin + auto LeaderUseCount = UseCounts[DominatingLeader]; + // EVM local end // It's about to be alive again. if (LeaderUseCount == 0 && isa(DominatingLeader)) ProbablyDead.erase(cast(DominatingLeader)); @@ -4112,7 +4114,9 @@ bool NewGVN::eliminateInstructions(Function &F) { ProbablyDead.insert(II); } } - ++LeaderUseCount; + // EVM local begin + ++UseCounts[DominatingLeader]; + // EVM local end AnythingReplaced = true; } } diff --git a/llvm/lib/Transforms/Scalar/SHA3ConstFolding.cpp b/llvm/lib/Transforms/Scalar/SHA3ConstFolding.cpp new file mode 100644 index 000000000000..1275610ad735 --- /dev/null +++ b/llvm/lib/Transforms/Scalar/SHA3ConstFolding.cpp @@ -0,0 +1,742 @@ +//===-- SHA3ConstFolding.cpp - Const fold calls to sha3 ---------*- 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 +// +//===----------------------------------------------------------------------===// +// +// The code below tries to replace sha3 (that calucates keccak256 hash) calls +// with the calculated hash values. +// +// It uses the following general approach: given a sha3 call (MemoryUse), +// walk upwards to find store instructions (clobbers) with +// constant values that fully define data (in memory) for which we compute hash. +// Only sha3 calls with the constant 'size' argument are checked. +// +// For example: +// +// store i256 1, ptr addrspace(1) null +// store i256 2, ptr addrspace(1) inttoptr (i256 32 to ptr addrspace(1)) +// %hash = tail call i256 @sha3(ptr addrspace(1) null, i256 64, i1 true) +// ret i256 %hash +// +// is transformed into: +// +// store i256 1, ptr addrspace(1) null +// store i256 2, ptr addrspace(1) inttoptr (i256 32 to ptr addrspace(1)) +// ret i256 -536754096339594166047489462334966539640... +// +// A bit more concretely: +// +// For all sha3 calls: +// 1. Collect potentially dominating clobbering MemoryDefs by walking upwards. +// Check that clobbering values are constants, otherwise bail out. +// +// 2. Check that +// 1. Each clobber is withing the sha3 memory location: +// |--clobber--| +// |------MemUse-------| +// 2. Clobbers are not intersected with each other: +// |--cl1--| +// |cl2| +// |--cl3--| +// |------MemUse-------| +// 3.Collect clobber values +// +// 3. Create a memory array from the collected values and calculate +// the Keccak256 hash. +// +// 4. Run simplification for each instruction in the function, as sha3 folding +// can provide new opportunities for this. +// +// 5. If the simplification has changed the function, run one more iteration +// of the whole process starting from p.1. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Scalar/SHA3ConstFolding.h" +#include "llvm/ADT/PostOrderIterator.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Analysis/AssumptionCache.h" +#include "llvm/Analysis/GlobalsModRef.h" +#include "llvm/Analysis/InstructionSimplify.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/MemoryBuiltins.h" +#include "llvm/Analysis/MemoryLocation.h" +#include "llvm/Analysis/MemorySSA.h" +#include "llvm/Analysis/MemorySSAUpdater.h" +#include "llvm/Analysis/MustExecute.h" +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/Analysis/ValueTracking.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/Support/DebugCounter.h" +#include "llvm/Support/EndianStream.h" +#include "llvm/Support/KECCAK.h" +#include "llvm/Transforms/Utils/AssumeBundleBuilder.h" +#include "llvm/Transforms/Utils/Local.h" +#include + +using namespace llvm; + +#define DEBUG_TYPE "sha3-constant-folding" + +STATISTIC(NumSHA3Folded, "Number of sha3 calls folded"); + +DEBUG_COUNTER(SHA3Counter, "sha3-constant-folding", + "Controls which instructions are removed"); + +namespace { +/// This structure holds an information about a single memory clobber. +/// While walking upwards starting at a sha3 call (which is a MemUse), +/// we create a MemClobber instance for each dominating memory clobber +/// (in current implementation - a store instruction) and put it in a +/// list which then gets sorted (by the 'Start' value). As result, we get +/// representation of a continuous memory region the sha3 computes a hash for. +struct MemClobber { + // Byte offset relatively to the beginning of the sha3 memory location. + uint64_t Start; + + // Size in bytes of the clobber. + uint64_t Size; + + // Value to be stored in the memory. + APInt Clobber; +}; + +/// This class holds an information required for folding sha3 calls +/// in the function F. +/// +/// The workflow with the class is as follows: +/// 1. Create an FoldingState instance. +/// The constructor collects the MemorySSA uses corresponding to +/// sha3 calls in the function F. +/// 2. Iterate through the collected memory uses calling the runFolding(), +/// which, on success, returns a computed keccak256 +/// hash value. +/// Replace sha3 values with the calculated hash values, +/// removing the calls and invoking removeFromMSSA() to keep up to date +/// the MemorySSA representation. +/// 3. Run simplifyInstructions() to simplify/clean up the F. +/// +class FoldingState { + /// Types of the relative placement of the two memory locations. + enum class OverlapType { + // Loc1 is completely within the Loc2 + // |-Loc1-| + // |-----Loc2----| + OL_Complete, + + // Loc1 is partially with the Loc2 + // and they both refer to the same underlying object + // |-Loc1-| + // |-----Loc2----| + OL_MaybePartial, + + // Memory locations don't intersect + // |--Loc1--| + // |---Loc2--| + OL_None, + + // Nothing can be determined + OL_Unknown + }; + + /// Describes how the memory clobber is placed with respect + /// to the memory use (sha3 call). + struct OverlapResult { + OverlapType Type; + + // Clobber offset relative to the beginning of the memory use. + // Unless the type is 'OL_Complete' actual offset value doesn't matter. + uint64_t ClobberOffset; + }; + + // Whether the function contains any irreducible control flow, useful for + // being accurately able to detect loops. + const bool ContainsIrreducibleLoops; + + // The sha3 calls to be analyzed. + SmallVector SHA3MemUses; + + Function &F; + AliasAnalysis &AA; + AssumptionCache &AC; + MemorySSA &MSSA; + std::unique_ptr MSSAUpdater; + const TargetLibraryInfo &TLI; + const DataLayout &DL; + const SimplifyQuery SQ; + const LoopInfo &LI; + unsigned HeapAS; + +public: + FoldingState(const FoldingState &) = delete; + FoldingState &operator=(const FoldingState &) = delete; + FoldingState(FoldingState &&) = delete; + FoldingState &&operator=(FoldingState &&) = delete; + ~FoldingState() = default; + + FoldingState(Function &F, AliasAnalysis &AA, AssumptionCache &AC, + MemorySSA &MSSA, DominatorTree &DT, const TargetLibraryInfo &TLI, + const LoopInfo &LI, + const std::function &IsSha3Call, + unsigned HeapAS); + + /// Collect all the potential clobbering memory accesses for the + /// given sha3 call (\p Call). + SmallVector collectSHA3Clobbers(const CallInst *Call); + + /// For the given sha3 call (\p Call), walk through the collected MemorySSA + /// definitions (\p MemDefs) and try to build a continuous memory segment with + /// the data for which we compute the keccak256 hash. On success return + /// the computed hash value. + Value *runFolding(const CallInst *Call, + SmallVectorImpl &MemDefs) const; + + /// Try to simplify instructions in the function F. The candidates for + /// simplification may appear after replacing sha3 calls with the + /// calculated hash values. + bool simplifyInstructions(); + + /// Exclude the instruction from MSSA if it's going to be removed from a BB. + void removeFromMSSA(Instruction *Inst) { + if (VerifyMemorySSA) + MSSA.verifyMemorySSA(); + + MSSAUpdater->removeMemoryAccess(Inst, true); + } + + /// Return collected MemorySSa uses corresponding to sha3 calls. + const SmallVectorImpl &getSHA3MemUses() const { + return SHA3MemUses; + } + +private: + /// Return true if a dependency between \p Clobber and \p MemUse is + /// guaranteed to be loop invariant for the loops that they are in. + bool isGuaranteedLoopIndependent(const Instruction *Clobber, + const Instruction *MemUse, + const MemoryLocation &ClobberLoc) const; + + /// Return true if \p Ptr is guaranteed to be loop invariant for any possible + /// loop. In particular, this guarantees that it only references a single + /// MemoryLocation during execution of the containing function. + bool isGuaranteedLoopInvariant(const Value *Ptr) const; + + /// Check how the two memory locations (\p MemUseLoc and \p ClobberLoc) + /// are located with respect to each other. + OverlapResult isOverlap(const Instruction *MemUse, const Instruction *Clobber, + const MemoryLocation &MemUseLoc, + const MemoryLocation &ClobberLoc) const; + + /// Try to cast the constant pointer value \p Ptr to the integer offset. + /// Consider moving it to a common code. TODO: CPR-1380. + std::optional tryToCastPtrToInt(const Value *Ptr) const; + + /// Ensure the sorted array of clobbers forms a continuous memory region. + /// On success return the size the memory region. + std::optional + checkMemoryClobbers(const SmallVector &MemClobbers) const; + + /// Compute keccak256 hash for the memory segment, formed by the sorted list + /// of memory clobbers passed in \p MemClobbers. + std::array + computeKeccak256Hash(const SmallVectorImpl &MemClobbers) const; + + /// Check if we can ignore non store clobber instruction that doesn't actually + /// clobber heap memory. For example, a memcpy to AS other than heap. + /// Probably we should check more cases here. TODO: CPR-1370. + bool shouldSkipClobber(const Instruction *MemInst) const { + if (!MemInst->mayWriteToMemory()) + return true; + + if (const auto *Intr = dyn_cast(MemInst)) + return Intr->getDestAddressSpace() != HeapAS; + + return false; + } + + /// Return MemoryLocation corresponding to the pointer argument of + /// sha3 call. + MemoryLocation getLocForSHA3Call(const CallInst *I) const { + return MemoryLocation::getForArgument(I, 0, TLI); + } +}; // end FoldingState struct + +} // end anonymous namespace + +static uint64_t getPointerSize(const Value *V, const DataLayout &DL, + const TargetLibraryInfo &TLI, + const Function *F) { + uint64_t Size = 0; + ObjectSizeOpts Opts; + Opts.NullIsUnknownSize = NullPointerIsDefined(F); + + if (getObjectSize(V, Size, DL, &TLI, Opts)) + return Size; + return MemoryLocation::UnknownSize; +} + +FoldingState::FoldingState( + Function &F, AliasAnalysis &AA, AssumptionCache &AC, MemorySSA &MSSA, + DominatorTree &DT, const TargetLibraryInfo &TLI, const LoopInfo &LI, + const std::function &IsSha3Call, unsigned HeapAS) + : ContainsIrreducibleLoops(mayContainIrreducibleControl(F, &LI)), F(F), + AA(AA), AC(AC), MSSA(MSSA), + MSSAUpdater(std::make_unique(&MSSA)), TLI(TLI), + DL(F.getParent()->getDataLayout()), SQ(DL, &TLI, &DT, &AC), LI(LI), + HeapAS(HeapAS) { + MSSA.ensureOptimizedUses(); + + const ReversePostOrderTraversal RPOT(&*F.begin()); + for (BasicBlock *BB : RPOT) { + for (Instruction &I : *BB) { + if (!IsSha3Call(&I)) + continue; + + const auto *Call = cast(&I); + auto *MA = MSSA.getMemoryAccess(Call); + auto *MU = dyn_cast_or_null(MA); + // sha3 is passed a pointer in the first argument + // and the memory size in the second one. It's expected that the + // memory size to be a constant expression. In this case + // the memory location should be precise. + if (MU && getLocForSHA3Call(Call).Size.isPrecise()) + SHA3MemUses.push_back(MU); + } + } +} + +SmallVector +FoldingState::collectSHA3Clobbers(const CallInst *Call) { + SmallVector Clobbers; + const MemoryLocation Loc = getLocForSHA3Call(Call); + MemorySSAWalker *Walker = MSSA.getWalker(); + + // For the given sha3 call (which is MemoryUse in terms of MemorySSA) we + // need to collect all memory clobbering accesses. Usually these are just + // 'store' instructions. Other cases are not handled by the current + // implementation. For this we employ MemorySSA representation that maps + // memory clobbering Instructions to three access types: + // - live on entry (nothing to do, sha3 is not clobbered), + // - MemoryDef, + // - MemoryPhi + // + // We start with a nearest to the sha3 call dominating clobbering access. + // Then we do the same for the just found clobber and so on until we find the + // 'live on entry'. + // For simplicity, in case of a MemoryPhi we also stop the search. This + // constraint can be relaxed. TODO: CPR-1370. + + MemoryAccess *MA = + Walker->getClobberingMemoryAccess(MSSA.getMemoryAccess(Call)); + for (; !MSSA.isLiveOnEntryDef(MA) && !isa(MA);) { + if (auto *Def = dyn_cast(MA)) { + Clobbers.push_back(Def); + MA = Walker->getClobberingMemoryAccess(Def->getDefiningAccess(), Loc); + } + } + + return Clobbers; +} + +bool FoldingState::simplifyInstructions() { + bool Changed = false; + for (auto &Inst : make_early_inc_range(instructions(F))) { + if (Value *V = simplifyInstruction(&Inst, SQ)) { + LLVM_DEBUG(dbgs() << " SHA3ConstFolding simplify: " << Inst + << " to: " << *V << '\n'); + if (!DebugCounter::shouldExecute(SHA3Counter)) { + LLVM_DEBUG(dbgs() << "Skipping due to debug counter\n"); + continue; + } + + if (!Inst.use_empty()) { + Inst.replaceAllUsesWith(V); + Changed = true; + } + if (isInstructionTriviallyDead(&Inst, &TLI)) { + removeFromMSSA(&Inst); + salvageKnowledge(&Inst, &AC); + Inst.eraseFromParent(); + Changed = true; + } + } + } + return Changed; +} + +bool FoldingState::isGuaranteedLoopIndependent( + const Instruction *Clobber, const Instruction *MemUse, + const MemoryLocation &ClobberLoc) const { + // If the dependency is within the same block or loop level (being careful + // of irreducible loops), we know that AA will return a valid result for the + // memory dependency. + if (MemUse->getParent() == Clobber->getParent()) + return true; + + // Check if both Clobber and MemUse are known to be in the same loop level. + const Loop *CurrentLI = LI.getLoopFor(MemUse->getParent()); + if (!ContainsIrreducibleLoops && CurrentLI && + CurrentLI == LI.getLoopFor(Clobber->getParent())) + return true; + + // Otherwise check the memory location is invariant to any loops. + return isGuaranteedLoopInvariant(ClobberLoc.Ptr); +} + +bool FoldingState::isGuaranteedLoopInvariant(const Value *Ptr) const { + Ptr = Ptr->stripPointerCasts(); + if (const auto *GEP = dyn_cast(Ptr)) + if (GEP->hasAllConstantIndices()) + Ptr = GEP->getPointerOperand()->stripPointerCasts(); + + if (const auto *I = dyn_cast(Ptr)) + return I->getParent()->isEntryBlock(); + + return true; +} + +static std::optional getNullOrInt(const APInt &APVal) { + if (APVal.getActiveBits() <= 64) + return APVal.getZExtValue(); + + return std::nullopt; +} + +std::optional +FoldingState::tryToCastPtrToInt(const Value *Ptr) const { + if (isa(Ptr)) + return UINT64_C(0); + + if (const auto *CE = dyn_cast(Ptr)) { + if (CE->getOpcode() == Instruction::IntToPtr) { + if (auto *CI = dyn_cast(CE->getOperand(0))) { + // Give up in case of a huge offsset, as this shouldn't happen + // in a real life. + return getNullOrInt(CI->getValue()); + } + } + } + + if (const auto *IntToPtr = dyn_cast(Ptr)) { + if (auto *CI = dyn_cast(IntToPtr->getOperand(0))) { + return getNullOrInt(CI->getValue()); + } + } + + return std::nullopt; +} + +FoldingState::OverlapResult +FoldingState::isOverlap(const Instruction *MemUse, const Instruction *Clobber, + const MemoryLocation &MemUseLoc, + const MemoryLocation &ClobberLoc) const { + // AliasAnalysis does not always account for loops. Limit overlap checks + // to dependencies for which we can guarantee they are independent of any + // loops they are in. + if (!isGuaranteedLoopIndependent(Clobber, MemUse, ClobberLoc)) + return {OverlapType::OL_Unknown, 0}; + + const Value *ClobberPtr = ClobberLoc.Ptr->stripPointerCasts(); + const Value *MemUsePtr = MemUseLoc.Ptr->stripPointerCasts(); + const Value *ClobberUndObj = getUnderlyingObject(ClobberPtr); + const Value *MemUseUndObj = getUnderlyingObject(MemUsePtr); + const uint64_t MemUseSize = MemUseLoc.Size.getValue(); + const uint64_t ClobberSize = ClobberLoc.Size.getValue(); + + // If both the Clobber and MemUse pointers are constant we expect two cases: + // - pointer is just a nullptr + // - pointer is a cast of an integer constant + + if (isa(ClobberPtr) && isa(MemUsePtr)) { + if (!tryToCastPtrToInt(ClobberPtr) || !tryToCastPtrToInt(MemUsePtr)) + return {OverlapType::OL_Unknown, 0}; + + const uint64_t ClobberPtrInt = *tryToCastPtrToInt(ClobberPtr); + const uint64_t MemUsePtrInt = *tryToCastPtrToInt(MemUsePtr); + if (MemUsePtrInt <= ClobberPtrInt && + (ClobberPtrInt + ClobberSize) <= (MemUsePtrInt + MemUseSize)) { + return {OverlapType::OL_Complete, ClobberPtrInt - MemUsePtrInt}; + } + } + + // Check whether the Clobber overwrites the MemUse object. + if (ClobberUndObj == MemUseUndObj) { + const uint64_t MemUseUndObjSize = getPointerSize(MemUseUndObj, DL, TLI, &F); + if (MemUseUndObjSize != MemoryLocation::UnknownSize && + MemUseUndObjSize == MemUseSize && MemUseSize == ClobberSize) + return {OverlapType::OL_Complete, 0}; + } + + // Query the alias information + const AliasResult AAR = AA.alias(MemUseLoc, ClobberLoc); + + // If the start pointers are the same, we just have to compare sizes to see + // if the MemUse was larger than the Clobber. + if (AAR == AliasResult::MustAlias) { + // Make sure that the MemUseSize size is >= the ClobberSize size. + if (MemUseSize >= ClobberSize) + return {OverlapType::OL_Complete, 0}; + } + + // If we hit a partial alias we may have a full overwrite + if (AAR == AliasResult::PartialAlias && AAR.hasOffset()) { + const int32_t Offset = AAR.getOffset(); + if (Offset >= 0 && + static_cast(Offset) + ClobberSize <= MemUseSize) { + return {OverlapType::OL_Complete, static_cast(Offset)}; + } + } + + // If we can't resolve the same pointers to the same object, then we can't + // analyze them at all. + if (MemUseUndObj != ClobberUndObj) { + if (AAR == AliasResult::NoAlias) + return {OverlapType::OL_None, 0}; + return {OverlapType::OL_Unknown, 0}; + } + + // Okay, we have stores to two completely different pointers. Try to + // decompose the pointer into a "base + constant_offset" form. If the base + // pointers are equal, then we can reason about the MemUse and the Clobber. + int64_t ClobberOffset = 0, MemUseOffset = 0; + const Value *ClobberBasePtr = + GetPointerBaseWithConstantOffset(ClobberPtr, ClobberOffset, DL); + const Value *MemUseBasePtr = + GetPointerBaseWithConstantOffset(MemUsePtr, MemUseOffset, DL); + + // If the base pointers still differ, we have two completely different + // stores. + if (ClobberBasePtr != MemUseBasePtr) + return {OverlapType::OL_Unknown, 0}; + + // The MemUse completely overlaps the clobber if both the start and the end + // of the Clobber are inside the MemUse: + // | |--Clobber-| | + // |------MemUse------| + // We have to be careful here as *Off is signed while *.Size is unsigned. + + // Check if the Clobber access starts not before the MemUse one. + if (ClobberOffset >= MemUseOffset) { + // If the Clobber access ends not after the MemUse access then the + // clobber one is completely overlapped by the MemUse one. + if (static_cast(ClobberOffset - MemUseOffset) + ClobberSize <= + MemUseSize) + return {OverlapType::OL_Complete, + static_cast(ClobberOffset - MemUseOffset)}; + + // If start of the Clobber access is before end of the MemUse access + // then accesses overlap. + if (static_cast(ClobberOffset - MemUseOffset) < MemUseSize) + return {OverlapType::OL_MaybePartial, 0}; + } + // If start of the MemUse access is before end of the Clobber access then + // accesses overlap. + else if (static_cast(MemUseOffset - ClobberOffset) < ClobberSize) { + return {OverlapType::OL_MaybePartial, 0}; + } + + // Can reach here only if accesses are known not to overlap. + return {OverlapType::OL_None, 0}; +} + +std::optional FoldingState::checkMemoryClobbers( + const SmallVector &MemClobbers) const { + auto Begin = MemClobbers.begin(), End = MemClobbers.end(); + assert(Begin != End); + + uint64_t TotalSize = Begin->Size; + for (const auto *It = std::next(Begin); It != End; ++It) { + TotalSize += It->Size; + const auto *ItPrev = std::prev(It); + if ((ItPrev->Start + ItPrev->Size) != It->Start) { + LLVM_DEBUG(dbgs() << "\tclobbers do alias, or there is a gap: [" + << ItPrev->Start << ", " << ItPrev->Start + ItPrev->Size + << "] -> [" << It->Start << ", " << It->Start + It->Size + << "]" << '\n'); + return std::nullopt; + } + } + + return TotalSize; +} + +std::array FoldingState::computeKeccak256Hash( + const SmallVectorImpl &MemClobbers) const { + SmallVector MemBuf; + raw_svector_ostream OS(MemBuf); + // Put all the clobber values to the buffer in the BE order. + for (const auto &MemClobber : MemClobbers) { + // Words of the APInt are in the LE order, so we need to + // iterate them starting from the end. + const auto *ValRawData = MemClobber.Clobber.getRawData(); + for (unsigned I = 0, E = MemClobber.Clobber.getNumWords(); I != E; ++I) + support::endian::write(OS, ValRawData[E - I - 1], + llvm::endianness::big); + } + LLVM_DEBUG(dbgs() << "\tinput sha3 data: " << toHex(OS.str()) << '\n'); + + return KECCAK::KECCAK_256(OS.str()); +} + +Value *FoldingState::runFolding(const CallInst *Call, + SmallVectorImpl &MemDefs) const { + uint64_t TotalSizeOfClobbers = 0; + SmallVector MemClobbers; + auto UseMemLoc = getLocForSHA3Call(Call); + const uint64_t UseMemSize = UseMemLoc.Size.getValue(); + + for (MemoryDef *MemDef : MemDefs) { + const auto *MemInstr = MemDef->getMemoryInst(); + if (shouldSkipClobber(MemInstr)) + continue; + + const auto *SI = dyn_cast(MemInstr); + if (!SI) { + LLVM_DEBUG(dbgs() << "\tunknown clobber: " << *MemInstr << '\n'); + return nullptr; + } + + LLVM_DEBUG(dbgs() << "\tfound clobber: " << *SI << '\n'); + + const MemoryLocation ClobberLoc = MemoryLocation::get(SI); + if (!ClobberLoc.Size.isPrecise()) + return nullptr; + + auto OverlapRes = isOverlap(Call, SI, UseMemLoc, ClobberLoc); + + // This clobber doesn't write to the memory sha3 is reading from. + // Just skip it and come to the next clobber. + if (OverlapRes.Type == OverlapType::OL_None) { + LLVM_DEBUG(dbgs() << "\t\twrites out of sha3 memory, offset: " + << OverlapRes.ClobberOffset << '\n'); + continue; + } + + // We give up in these cases, as it's difficult or impossible + // to determine the full memory data for the sha3. + // If required, we could try to support some cases. TODO: CPR-1370. + if (OverlapRes.Type == OverlapType::OL_MaybePartial || + OverlapRes.Type == OverlapType::OL_Unknown) { + LLVM_DEBUG(dbgs() << "\t\tpartially or unknow overlap" << '\n'); + return nullptr; + } + + // Handle OL_Complete. Try to add new clobber to the memory clobbers. + + // We cannot perform constant folding, if the stored value is not + // a constant expression. + const auto *ClobberVal = dyn_cast(SI->getValueOperand()); + if (!ClobberVal) { + LLVM_DEBUG(dbgs() << "\t\tstored value isn't constant" << '\n'); + return nullptr; + } + + const uint64_t ClobberSize = ClobberLoc.Size.getValue(); + // If we have already seen a clobber with the same start position + // and the size, we can ignore the current clobber, as it's killed + // by existing one. In fact this is similar to what DSE pass does. + if (std::any_of(MemClobbers.begin(), MemClobbers.end(), + [OverlapRes, ClobberSize](const auto &C) { + return C.Start == OverlapRes.ClobberOffset && + C.Size == ClobberSize; + })) { + LLVM_DEBUG(dbgs() << "\t\tstored value is killed by the" + "consequent clobber" + << '\n'); + continue; + } + + TotalSizeOfClobbers += ClobberSize; + assert(ClobberSize * 8 == ClobberVal->getBitWidth()); + + MemClobbers.push_back( + {OverlapRes.ClobberOffset, ClobberSize, ClobberVal->getValue()}); + + // If we have collected clobbers that fully cover the sha3 memory + // location, sort them in the ascending order of their starting addresses + // and perform some checks. + if (TotalSizeOfClobbers < UseMemSize) + continue; + + // Sort memory clobbers in the ascending order of their starting + // positions. + std::sort( + MemClobbers.begin(), MemClobbers.end(), + [](const auto &Lhs, const auto &Rhs) { return Lhs.Start < Rhs.Start; }); + + // Ensure the sorted array of clobbers forms a continuous memory region. + auto TotalSize = checkMemoryClobbers(MemClobbers); + if (!TotalSize) + return nullptr; + + assert(TotalSize == UseMemSize); + auto Hash = computeKeccak256Hash(MemClobbers); + + LLVM_DEBUG(dbgs() << "\tkeccak256 hash: " << toHex(Hash) << '\n'); + assert(Call->getType()->getIntegerBitWidth() == 256); + + Value *HashVal = + ConstantInt::get(Call->getType(), APInt(256, toHex(Hash), 16)); + return HashVal; + } + + LLVM_DEBUG(dbgs() << "\tcouldn't find enough clobbers that would fully" + "cover sha3 memory" + << '\n'); + return nullptr; +} + +bool llvm::runSHA3ConstFolding( + Function &F, AliasAnalysis &AA, AssumptionCache &AC, MemorySSA &MSSA, + DominatorTree &DT, const TargetLibraryInfo &TLI, const LoopInfo &LI, + const std::function &IsSha3Call, + unsigned HeapAS) { + LLVM_DEBUG(dbgs() << "********** SHA3 constant folding **********\n" + << "********** Function: " << F.getName() << '\n'); + + FoldingState State(F, AA, AC, MSSA, DT, TLI, LI, IsSha3Call, HeapAS); + SmallSet RemovedSHA3; + + bool Changed = false, ChangedOnIter = false; + do { + LLVM_DEBUG(dbgs() << "Running new iteration of sha3 constant folding...\n"); + + for (MemoryUse *SHA3MemUse : State.getSHA3MemUses()) { + if (RemovedSHA3.count(SHA3MemUse)) + continue; + + auto *SHA3Call = dyn_cast(SHA3MemUse->getMemoryInst()); + assert(SHA3Call != nullptr); + + LLVM_DEBUG(dbgs() << "Analyzing: " << *SHA3Call << '\n'); + SmallVector Clobbers = + State.collectSHA3Clobbers(SHA3Call); + + if (Value *HashVal = State.runFolding(SHA3Call, Clobbers)) { + if (!DebugCounter::shouldExecute(SHA3Counter)) { + LLVM_DEBUG(dbgs() << "Skipping due to debug counter\n"); + return Changed; + } + + SHA3Call->replaceAllUsesWith(HashVal); + State.removeFromMSSA(SHA3Call); + SHA3Call->eraseFromParent(); + RemovedSHA3.insert(SHA3MemUse); + Changed = ChangedOnIter = true; + NumSHA3Folded++; + + LLVM_DEBUG(dbgs() << "\treplacing with the value: " << *HashVal + << '\n'); + } + } + // If we simplified some instructions after folding sha3 calls, + // run the folding again, as there may be new opportunities for this. + } while (ChangedOnIter && State.simplifyInstructions()); + + return Changed; +} diff --git a/llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp b/llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp index 73e3ff296cf1..111508cfac47 100644 --- a/llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp +++ b/llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp @@ -904,7 +904,9 @@ void SeparateConstOffsetFromGEP::lowerToSingleIndexGEPs( // Create a GEP with the constant offset index. if (AccumulativeByteOffset != 0) { - Value *Offset = ConstantInt::get(PtrIndexTy, AccumulativeByteOffset); + // EVM local begin + Value *Offset = ConstantInt::get(PtrIndexTy, AccumulativeByteOffset, true); + // EVM local end ResultPtr = Builder.CreatePtrAdd(ResultPtr, Offset, "uglygep"); } else isSwapCandidate = false; @@ -961,8 +963,10 @@ SeparateConstOffsetFromGEP::lowerToArithmetics(GetElementPtrInst *Variadic, // Create an ADD for the constant offset index. if (AccumulativeByteOffset != 0) { + // EVM local begin ResultPtr = Builder.CreateAdd( - ResultPtr, ConstantInt::get(IntPtrTy, AccumulativeByteOffset)); + ResultPtr, ConstantInt::get(IntPtrTy, AccumulativeByteOffset, true)); + // EVM local end } ResultPtr = Builder.CreateIntToPtr(ResultPtr, Variadic->getType()); diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp index f68cbf62b982..3d447c713410 100644 --- a/llvm/lib/Transforms/Utils/Local.cpp +++ b/llvm/lib/Transforms/Utils/Local.cpp @@ -56,6 +56,9 @@ #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Intrinsics.h" +// EVM local begin +#include "llvm/IR/IntrinsicsEVM.h" +// EVM local end #include "llvm/IR/IntrinsicsWebAssembly.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/MDBuilder.h" @@ -436,6 +439,17 @@ bool llvm::wouldInstructionBeTriviallyDead(const Instruction *I, return true; } + // EVM local begin + if (auto *II = dyn_cast(I)) + if (II->getIntrinsicID() == Intrinsic::evm_msize || + II->getIntrinsicID() == Intrinsic::evm_pc || + II->getIntrinsicID() == Intrinsic::evm_gas || + II->getIntrinsicID() == Intrinsic::evm_balance || + II->getIntrinsicID() == Intrinsic::evm_returndatasize || + II->getIntrinsicID() == Intrinsic::evm_selfbalance) + return true; + // EVM local end + if (auto *CB = dyn_cast(I)) if (isRemovableAlloc(CB, TLI)) return true; diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index f23e28888931..9f34a7d84885 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -71,6 +71,9 @@ #include "llvm/Support/KnownBits.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" +// EVM local begin +#include "llvm/TargetParser/Triple.h" +// EVM local end #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/Local.h" #include "llvm/Transforms/Utils/ValueMapper.h" @@ -7026,7 +7029,7 @@ static bool ReduceSwitchRange(SwitchInst *SI, IRBuilder<> &Builder, for (auto Case : SI->cases()) { auto *Orig = Case.getCaseValue(); - auto Sub = Orig->getValue() - APInt(Ty->getBitWidth(), Base); + auto Sub = Orig->getValue() - APInt(Ty->getBitWidth(), Base, true); Case.setValue(cast(ConstantInt::get(Ty, Sub.lshr(Shift)))); } return true; @@ -7149,9 +7152,13 @@ bool SimplifyCFGOpt::simplifySwitch(SwitchInst *SI, IRBuilder<> &Builder) { // switch expression itself can still be restricted as a result of inlining or // CVP. Therefore, only apply this transformation during late stages of the // optimisation pipeline. - if (Options.ConvertSwitchToLookupTable && - SwitchToLookupTable(SI, Builder, DTU, DL, TTI)) - return requestResimplify(); + // EVM local begin + // TODO: CPR-940 Support const arrays. + if (!Triple(BB->getModule()->getTargetTriple()).isEVM()) + if (Options.ConvertSwitchToLookupTable && + SwitchToLookupTable(SI, Builder, DTU, DL, TTI)) + return requestResimplify(); + // EVM local end if (simplifySwitchOfPowersOfTwo(SI, Builder, DL, TTI)) return requestResimplify(); diff --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp index 89c8c5bf0895..d8319af363a8 100644 --- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp @@ -127,6 +127,21 @@ static bool callHasFP128Argument(const CallInst *CI) { }); } +// EVM local begin +// On EVM target, we allow sizes up to 256bits because we allow i256 +// datatype, but it is not always the case that LLVM infra can accept i256 +// values. So here when we are trying to annotate memory location info according +// for intrinsics, we simply just bail out if the size is larger than 64bits +// because the size attributes are assuming an i64 type size information. +static bool isInt64Constant(Value *Size) { + if (ConstantInt *LenC = dyn_cast(Size)) { + if (LenC->getValue().getActiveBits() > 64) + return false; + } + return true; +} +// EVM local end + // Convert the entire string Str representing an integer in Base, up to // the terminating nul if present, to a constant according to the rules // of strtoul[l] or, when AsSigned is set, of strtol[l]. On success @@ -1622,6 +1637,10 @@ Value *LibCallSimplifier::optimizeBCmp(CallInst *CI, IRBuilderBase &B) { Value *LibCallSimplifier::optimizeMemCpy(CallInst *CI, IRBuilderBase &B) { Value *Size = CI->getArgOperand(2); + // EVM local begin + if (!isInt64Constant(Size)) + return nullptr; + // EVM local end annotateNonNullAndDereferenceable(CI, {0, 1}, Size, DL); if (isa(CI)) return nullptr; @@ -1688,6 +1707,10 @@ Value *LibCallSimplifier::optimizeMemPCpy(CallInst *CI, IRBuilderBase &B) { Value *LibCallSimplifier::optimizeMemMove(CallInst *CI, IRBuilderBase &B) { Value *Size = CI->getArgOperand(2); + // EVM local begin + if (!isInt64Constant(Size)) + return nullptr; + // EVM local end annotateNonNullAndDereferenceable(CI, {0, 1}, Size, DL); if (isa(CI)) return nullptr; @@ -1701,6 +1724,10 @@ Value *LibCallSimplifier::optimizeMemMove(CallInst *CI, IRBuilderBase &B) { Value *LibCallSimplifier::optimizeMemSet(CallInst *CI, IRBuilderBase &B) { Value *Size = CI->getArgOperand(2); + // EVM local begin + if (!isInt64Constant(Size)) + return nullptr; + // EVM local end annotateNonNullAndDereferenceable(CI, 0, Size, DL); if (isa(CI)) return nullptr; diff --git a/llvm/test/Analysis/GlobalsModRef/memset-escape.ll b/llvm/test/Analysis/GlobalsModRef/memset-escape.ll index a84987c3bc69..5a899e263ead 100644 --- a/llvm/test/Analysis/GlobalsModRef/memset-escape.ll +++ b/llvm/test/Analysis/GlobalsModRef/memset-escape.ll @@ -1,4 +1,5 @@ ; RUN: opt < %s -O1 -S | FileCheck %s +; XFAIL: target=evm{{.*}} target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-apple-macosx10.10.0" diff --git a/llvm/test/CMakeLists.txt b/llvm/test/CMakeLists.txt index 6b7f2b58e603..9b64092e547d 100644 --- a/llvm/test/CMakeLists.txt +++ b/llvm/test/CMakeLists.txt @@ -245,6 +245,54 @@ add_lit_testsuite(check-llvm "Running the LLVM regression tests" ) set_target_properties(check-llvm PROPERTIES FOLDER "LLVM/Tests") +add_lit_testsuite(verify-llvm-codegen-opt "Running CodeGen LLVM regression tests with verification options set" + ${CMAKE_CURRENT_BINARY_DIR} + EXCLUDE_FROM_CHECK_ALL + PARAMS "opt=${LLVM_BINARY_DIR}/bin/opt \ +--cgp-verify-bfi-updates \ +--earlycse-debug-hash \ +--loop-distribute-verify \ +--machine-combiner-verify-pattern-order \ +--phicse-debug-hash \ +--scev-verify-ir \ +--tail-dup-verify \ +--unroll-verify-domtree \ +--verify-assumption-cache \ +--verify-analysis-invalidation \ +--verify-cfiinstrs \ +--verify-coalescing \ +--verify-dom-info \ +--verify-each \ +--verify-loop-info \ +--verify-loop-lcssa \ +--verify-machine-dom-info \ +--verify-machineinstrs \ +--verify-memoryssa \ +--verify-misched \ +--verify-regalloc \ +--verify-scev-strict" + ARGS "--filter CodeGen" + DEPENDS ${LLVM_TEST_DEPENDS} + ) + +add_lit_testsuite(verify-llvm-codegen-llc "Running EVM LLVM regression tests with verification options set" + ${CMAKE_CURRENT_BINARY_DIR} + EXCLUDE_FROM_CHECK_ALL + PARAMS "llc=${LLVM_BINARY_DIR}/bin/llc \ +--cgp-verify-bfi-updates \ +--compile-twice \ +--earlycse-debug-hash \ +--machine-combiner-verify-pattern-order \ +--phicse-debug-hash \ +--reassociate-geps-verify-no-dead-code \ +--safepoint-ir-verifier-print-only \ +--scev-verify-ir \ +--tail-dup-verify \ +--unroll-verify-domtree \ +--verify-regalloc" + DEPENDS ${LLVM_TEST_DEPENDS} + ) + add_lit_testsuites(LLVM ${CMAKE_CURRENT_SOURCE_DIR} ${exclude_from_check_all} DEPENDS ${LLVM_TEST_DEPENDS} @@ -255,3 +303,8 @@ add_lit_testsuites(LLVM ${CMAKE_CURRENT_SOURCE_DIR} add_custom_target(check) add_dependencies(check check-all) set_target_properties(check PROPERTIES FOLDER "LLVM/Tests") + +# Setup an alias for 'verify-llvm'. +add_custom_target(verify-llvm DEPENDS check verify-llvm-codegen-opt verify-llvm-codegen-llc) +add_dependencies(check verify-llvm-codegen-opt verify-llvm-codegen-llc) +set_target_properties(verify-llvm PROPERTIES FOLDER "LLVM/Tests") diff --git a/llvm/test/CodeGen/EVM/O0-pipeline.ll b/llvm/test/CodeGen/EVM/O0-pipeline.ll new file mode 100644 index 000000000000..02d77d8eeabf --- /dev/null +++ b/llvm/test/CodeGen/EVM/O0-pipeline.ll @@ -0,0 +1,86 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc -O0 -debug-pass=Structure < %s -o /dev/null 2>&1 | FileCheck %s +target triple = "evm" + +; REQUIRES: asserts + +; CHECK-LABEL: Pass Arguments: +; CHECK-NEXT: Target Library Information +; CHECK-NEXT: Target Pass Configuration +; CHECK-NEXT: Machine Module Information +; CHECK-NEXT: Target Transform Information +; CHECK-NEXT: Create Garbage Collector Module Metadata +; CHECK-NEXT: Assumption Cache Tracker +; CHECK-NEXT: Profile summary info +; CHECK-NEXT: Machine Branch Probability Analysis +; CHECK-NEXT: ModulePass Manager +; CHECK-NEXT: Pre-ISel Intrinsic Lowering +; CHECK-NEXT: EVM Lower Intrinsics +; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: Module Verifier +; CHECK-NEXT: Lower Garbage Collection Instructions +; CHECK-NEXT: Shadow Stack GC Lowering +; CHECK-NEXT: Lower constant intrinsics +; CHECK-NEXT: Remove unreachable blocks from the CFG +; CHECK-NEXT: Expand vector predication intrinsics +; CHECK-NEXT: Instrument function entry/exit with calls to e.g. mcount() (post inlining) +; CHECK-NEXT: Scalarize Masked Memory Intrinsics +; CHECK-NEXT: Expand reduction intrinsics +; CHECK-NEXT: Final transformations before code generation +; CHECK-NEXT: Lower invoke and unwind, for unwindless code generators +; CHECK-NEXT: Remove unreachable blocks from the CFG +; CHECK-NEXT: CallGraph Construction +; CHECK-NEXT: EVM mark recursive functions +; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: Prepare callbr +; CHECK-NEXT: Safe Stack instrumentation pass +; CHECK-NEXT: Insert stack protectors +; CHECK-NEXT: Module Verifier +; CHECK-NEXT: Assignment Tracking Analysis +; CHECK-NEXT: Unnamed pass: implement Pass::getPassName() +; CHECK-NEXT: EVM Argument Move +; CHECK-NEXT: Finalize ISel and expand pseudo-instructions +; CHECK-NEXT: Local Stack Slot Allocation +; CHECK-NEXT: Eliminate PHI nodes for register allocation +; CHECK-NEXT: Two-Address instruction pass +; CHECK-NEXT: Remove Redundant DEBUG_VALUE analysis +; CHECK-NEXT: Fixup Statepoint Caller Saved +; CHECK-NEXT: Lazy Machine Block Frequency Analysis +; CHECK-NEXT: Machine Optimization Remark Emitter +; CHECK-NEXT: Prologue/Epilogue Insertion & Frame Finalization +; CHECK-NEXT: Post-RA pseudo instruction expansion pass +; CHECK-NEXT: Insert fentry calls +; CHECK-NEXT: Insert XRay ops +; CHECK-NEXT: EVM split critical edges +; CHECK-NEXT: MachineDominator Tree Construction +; CHECK-NEXT: Slot index numbering +; CHECK-NEXT: Live Interval Analysis +; CHECK-NEXT: EVM Optimize Live Intervals +; CHECK-NEXT: EVM Single use expressions +; CHECK-NEXT: Slot index numbering +; CHECK-NEXT: Live Interval Analysis +; CHECK-NEXT: Machine Natural Loop Construction +; CHECK-NEXT: Virtual Register Map +; CHECK-NEXT: Live Stack Slot Analysis +; CHECK-NEXT: Machine Block Frequency Analysis +; CHECK-NEXT: EVM backward propagation stackification +; CHECK-NEXT: Stack Slot Coloring +; CHECK-NEXT: EVM finalize stack frames +; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: Machine Sanitizer Binary Metadata +; CHECK-NEXT: Lazy Machine Block Frequency Analysis +; CHECK-NEXT: Machine Optimization Remark Emitter +; CHECK-NEXT: Stack Frame Layout Analysis +; CHECK-NEXT: EVM Lower jump_unless +; CHECK-NEXT: EVM constant unfolding +; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: Lazy Machine Block Frequency Analysis +; CHECK-NEXT: Machine Optimization Remark Emitter +; CHECK-NEXT: EVM Assembly +; CHECK-NEXT: Free MachineFunction + +define void @f() { + ret void +} +;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: +; CHECK: {{.*}} diff --git a/llvm/test/CodeGen/EVM/O3-pipeline.ll b/llvm/test/CodeGen/EVM/O3-pipeline.ll new file mode 100644 index 000000000000..5185960da01a --- /dev/null +++ b/llvm/test/CodeGen/EVM/O3-pipeline.ll @@ -0,0 +1,160 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc -O3 -debug-pass=Structure < %s -o /dev/null 2>&1 | FileCheck %s +target triple = "evm" + +; REQUIRES: asserts + +; CHECK-LABEL: Pass Arguments: +; CHECK-NEXT: Target Library Information +; CHECK-NEXT: Target Pass Configuration +; CHECK-NEXT: Machine Module Information +; CHECK-NEXT: Target Transform Information +; CHECK-NEXT: EVM Address space based Alias Analysis +; CHECK-NEXT: EVM Address space based Alias Analysis Wrapper +; CHECK-NEXT: Type-Based Alias Analysis +; CHECK-NEXT: Scoped NoAlias Alias Analysis +; CHECK-NEXT: Assumption Cache Tracker +; CHECK-NEXT: Profile summary info +; CHECK-NEXT: Create Garbage Collector Module Metadata +; CHECK-NEXT: Machine Branch Probability Analysis +; CHECK-NEXT: ModulePass Manager +; CHECK-NEXT: Pre-ISel Intrinsic Lowering +; CHECK-NEXT: EVM Lower Intrinsics +; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: Module Verifier +; CHECK-NEXT: Dominator Tree Construction +; CHECK-NEXT: Basic Alias Analysis (stateless AA impl) +; CHECK-NEXT: Natural Loop Information +; CHECK-NEXT: Canonicalize natural loops +; CHECK-NEXT: Scalar Evolution Analysis +; CHECK-NEXT: Loop Pass Manager +; CHECK-NEXT: Canonicalize Freeze Instructions in Loops +; CHECK-NEXT: Induction Variable Users +; CHECK-NEXT: Loop Strength Reduction +; CHECK-NEXT: Basic Alias Analysis (stateless AA impl) +; CHECK-NEXT: Function Alias Analysis Results +; CHECK-NEXT: Merge contiguous icmps into a memcmp +; CHECK-NEXT: Natural Loop Information +; CHECK-NEXT: Lazy Branch Probability Analysis +; CHECK-NEXT: Lazy Block Frequency Analysis +; CHECK-NEXT: Expand memcmp() to load/stores +; CHECK-NEXT: Lower Garbage Collection Instructions +; CHECK-NEXT: Shadow Stack GC Lowering +; CHECK-NEXT: Lower constant intrinsics +; CHECK-NEXT: Remove unreachable blocks from the CFG +; CHECK-NEXT: Natural Loop Information +; CHECK-NEXT: Post-Dominator Tree Construction +; CHECK-NEXT: Branch Probability Analysis +; CHECK-NEXT: Block Frequency Analysis +; CHECK-NEXT: Constant Hoisting +; CHECK-NEXT: Replace intrinsics with calls to vector library +; CHECK-NEXT: Partially inline calls to library functions +; CHECK-NEXT: Expand vector predication intrinsics +; CHECK-NEXT: Instrument function entry/exit with calls to e.g. mcount() (post inlining) +; CHECK-NEXT: Scalarize Masked Memory Intrinsics +; CHECK-NEXT: Expand reduction intrinsics +; CHECK-NEXT: Natural Loop Information +; CHECK-NEXT: TLS Variable Hoist +; CHECK-NEXT: Final transformations before code generation +; CHECK-NEXT: Dominator Tree Construction +; CHECK-NEXT: Natural Loop Information +; CHECK-NEXT: CodeGen Prepare +; CHECK-NEXT: Lower invoke and unwind, for unwindless code generators +; CHECK-NEXT: Remove unreachable blocks from the CFG +; CHECK-NEXT: CallGraph Construction +; CHECK-NEXT: EVM mark recursive functions +; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: Prepare callbr +; CHECK-NEXT: Safe Stack instrumentation pass +; CHECK-NEXT: Insert stack protectors +; CHECK-NEXT: Module Verifier +; CHECK-NEXT: Dominator Tree Construction +; CHECK-NEXT: Basic Alias Analysis (stateless AA impl) +; CHECK-NEXT: Function Alias Analysis Results +; CHECK-NEXT: Natural Loop Information +; CHECK-NEXT: Post-Dominator Tree Construction +; CHECK-NEXT: Branch Probability Analysis +; CHECK-NEXT: Assignment Tracking Analysis +; CHECK-NEXT: Lazy Branch Probability Analysis +; CHECK-NEXT: Lazy Block Frequency Analysis +; CHECK-NEXT: Unnamed pass: implement Pass::getPassName() +; CHECK-NEXT: EVM Argument Move +; CHECK-NEXT: Finalize ISel and expand pseudo-instructions +; CHECK-NEXT: Optimize machine instruction PHIs +; CHECK-NEXT: Slot index numbering +; CHECK-NEXT: Merge disjoint stack slots +; CHECK-NEXT: Local Stack Slot Allocation +; CHECK-NEXT: Remove dead machine instructions +; CHECK-NEXT: MachineDominator Tree Construction +; CHECK-NEXT: Machine Natural Loop Construction +; CHECK-NEXT: Machine Block Frequency Analysis +; CHECK-NEXT: Early Machine Loop Invariant Code Motion +; CHECK-NEXT: MachineDominator Tree Construction +; CHECK-NEXT: Machine Block Frequency Analysis +; CHECK-NEXT: Machine Common Subexpression Elimination +; CHECK-NEXT: MachinePostDominator Tree Construction +; CHECK-NEXT: Machine Cycle Info Analysis +; CHECK-NEXT: Machine code sinking +; CHECK-NEXT: Peephole Optimizations +; CHECK-NEXT: Remove dead machine instructions +; CHECK-NEXT: Detect Dead Lanes +; CHECK-NEXT: Init Undef Pass +; CHECK-NEXT: Process Implicit Definitions +; CHECK-NEXT: Remove unreachable machine basic blocks +; CHECK-NEXT: Live Variable Analysis +; CHECK-NEXT: Eliminate PHI nodes for register allocation +; CHECK-NEXT: Two-Address instruction pass +; CHECK-NEXT: MachineDominator Tree Construction +; CHECK-NEXT: Slot index numbering +; CHECK-NEXT: Live Interval Analysis +; CHECK-NEXT: Register Coalescer +; CHECK-NEXT: Rename Disconnected Subregister Components +; CHECK-NEXT: Machine Instruction Scheduler +; CHECK-NEXT: Remove Redundant DEBUG_VALUE analysis +; CHECK-NEXT: Fixup Statepoint Caller Saved +; CHECK-NEXT: Lazy Machine Block Frequency Analysis +; CHECK-NEXT: Machine Optimization Remark Emitter +; CHECK-NEXT: Prologue/Epilogue Insertion & Frame Finalization +; CHECK-NEXT: Machine Block Frequency Analysis +; CHECK-NEXT: Control Flow Optimizer +; CHECK-NEXT: Post-RA pseudo instruction expansion pass +; CHECK-NEXT: Insert fentry calls +; CHECK-NEXT: Insert XRay ops +; CHECK-NEXT: EVM split critical edges +; CHECK-NEXT: MachineDominator Tree Construction +; CHECK-NEXT: Slot index numbering +; CHECK-NEXT: Live Interval Analysis +; CHECK-NEXT: EVM Optimize Live Intervals +; CHECK-NEXT: EVM Single use expressions +; CHECK-NEXT: Slot index numbering +; CHECK-NEXT: Live Interval Analysis +; CHECK-NEXT: Machine Natural Loop Construction +; CHECK-NEXT: Virtual Register Map +; CHECK-NEXT: Live Stack Slot Analysis +; CHECK-NEXT: Machine Block Frequency Analysis +; CHECK-NEXT: EVM backward propagation stackification +; CHECK-NEXT: Stack Slot Coloring +; CHECK-NEXT: EVM finalize stack frames +; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: MachineDominator Tree Construction +; CHECK-NEXT: Machine Natural Loop Construction +; CHECK-NEXT: Machine Block Frequency Analysis +; CHECK-NEXT: Control Flow Optimizer +; CHECK-NEXT: Machine Sanitizer Binary Metadata +; CHECK-NEXT: Lazy Machine Block Frequency Analysis +; CHECK-NEXT: Machine Optimization Remark Emitter +; CHECK-NEXT: Stack Frame Layout Analysis +; CHECK-NEXT: EVM Lower jump_unless +; CHECK-NEXT: EVM constant unfolding +; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: EVM Peephole +; CHECK-NEXT: Lazy Machine Block Frequency Analysis +; CHECK-NEXT: Machine Optimization Remark Emitter +; CHECK-NEXT: EVM Assembly +; CHECK-NEXT: Free MachineFunction + +define void @f() { + ret void +} +;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: +; CHECK: {{.*}} diff --git a/llvm/test/CodeGen/EVM/aa-dse.ll b/llvm/test/CodeGen/EVM/aa-dse.ll new file mode 100644 index 000000000000..c2b639fd88b4 --- /dev/null +++ b/llvm/test/CodeGen/EVM/aa-dse.ll @@ -0,0 +1,18 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2 +; RUN: opt -passes=dse -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define void @test() { +; CHECK-LABEL: define void @test() { +; CHECK-NEXT: [[PTR2:%.*]] = inttoptr i256 32 to ptr addrspace(1) +; CHECK-NEXT: store i256 1, ptr addrspace(1) [[PTR2]], align 64 +; CHECK-NEXT: ret void +; + %ptr1 = inttoptr i256 32 to ptr addrspace(1) + %ptr2 = inttoptr i256 32 to ptr addrspace(1) + store i256 2, ptr addrspace(1) %ptr1, align 64 + store i256 1, ptr addrspace(1) %ptr2, align 64 + ret void +} diff --git a/llvm/test/CodeGen/EVM/aa-eval.ll b/llvm/test/CodeGen/EVM/aa-eval.ll new file mode 100644 index 000000000000..32916b01cbe2 --- /dev/null +++ b/llvm/test/CodeGen/EVM/aa-eval.ll @@ -0,0 +1,473 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: opt -passes=aa-eval -aa-pipeline=evm-aa,basic-aa -print-all-alias-modref-info -disable-output < %s 2>&1 | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare void @llvm.evm.extcodecopy(i256, ptr addrspace(1), ptr addrspace(4), i256) +declare i256 @llvm.evm.create(i256, ptr addrspace(1), i256) +declare i256 @llvm.evm.call(i256, i256, i256, ptr addrspace(1), i256, ptr addrspace(1), i256) +declare i256 @llvm.evm.callcode(i256, i256, i256, ptr addrspace(1), i256, ptr addrspace(1), i256) +declare i256 @llvm.evm.delegatecall(i256, i256, ptr addrspace(1), i256, ptr addrspace(1), i256) +declare i256 @llvm.evm.create2(i256, ptr addrspace(1), i256, i256) +declare i256 @llvm.evm.staticcall(i256, i256, ptr addrspace(1), i256, ptr addrspace(1), i256) +declare void @llvm.evm.return(ptr addrspace(1), i256) +declare void @llvm.evm.revert(ptr addrspace(1), i256) + +; CHECK-LABEL: Function: test_offset_i8_noalias +; CHECK: NoAlias: i8 addrspace(1)* %inttoptr1, i8 addrspace(1)* %inttoptr2 +define void @test_offset_i8_noalias(ptr addrspace(1) %addr) { + %ptrtoint = ptrtoint ptr addrspace(1) %addr to i8 + %add1 = add i8 %ptrtoint, 32 + %inttoptr1 = inttoptr i8 %add1 to ptr addrspace(1) + store i8 3, ptr addrspace(1) %inttoptr1, align 1 + %add2 = add i8 %ptrtoint, 33 + %inttoptr2 = inttoptr i8 %add2 to ptr addrspace(1) + store i8 58, ptr addrspace(1) %inttoptr2, align 1 + ret void +} + +; CHECK-LABEL: Function: test_offset_i512_noalias +; CHECK: NoAlias: i512 addrspace(1)* %inttoptr1, i512 addrspace(1)* %inttoptr2 +define void @test_offset_i512_noalias(ptr addrspace(1) %addr) { + %ptrtoint = ptrtoint ptr addrspace(1) %addr to i512 + %add1 = add i512 %ptrtoint, 32 + %inttoptr1 = inttoptr i512 %add1 to ptr addrspace(1) + store i512 3, ptr addrspace(1) %inttoptr1, align 1 + %add2 = add i512 %ptrtoint, 96 + %inttoptr2 = inttoptr i512 %add2 to ptr addrspace(1) + store i512 58, ptr addrspace(1) %inttoptr2, align 1 + ret void +} + +; CHECK-LABEL: Function: test_offset_mustalias +; CHECK: MustAlias: i256 addrspace(1)* %inttoptr1, i256 addrspace(1)* %inttoptr2 +define void @test_offset_mustalias(ptr addrspace(1) %addr) { + %ptrtoint = ptrtoint ptr addrspace(1) %addr to i256 + %inttoptr1 = inttoptr i256 %ptrtoint to ptr addrspace(1) + store i256 3, ptr addrspace(1) %inttoptr1, align 1 + %inttoptr2 = inttoptr i256 %ptrtoint to ptr addrspace(1) + store i256 58, ptr addrspace(1) %inttoptr2, align 1 + ret void +} + +; CHECK-LABEL: Function: test_offset_noalias1 +; CHECK: NoAlias: i256 addrspace(1)* %inttoptr1, i256 addrspace(1)* %inttoptr2 +define void @test_offset_noalias1(ptr addrspace(1) %addr) { + %ptrtoint = ptrtoint ptr addrspace(1) %addr to i256 + %add1 = add i256 %ptrtoint, 32 + %inttoptr1 = inttoptr i256 %add1 to ptr addrspace(1) + store i256 3, ptr addrspace(1) %inttoptr1, align 1 + %add2 = add i256 %ptrtoint, 64 + %inttoptr2 = inttoptr i256 %add2 to ptr addrspace(1) + store i256 58, ptr addrspace(1) %inttoptr2, align 1 + ret void +} + +; CHECK-LABEL: Function: test_offset_noalias2 +; CHECK: NoAlias: i256 addrspace(1)* %inttoptr1, i256 addrspace(1)* %inttoptr2 +define void @test_offset_noalias2(ptr addrspace(1) %addr) { + %ptrtoint = ptrtoint ptr addrspace(1) %addr to i256 + %add1 = add i256 %ptrtoint, -32 + %inttoptr1 = inttoptr i256 %add1 to ptr addrspace(1) + store i256 3, ptr addrspace(1) %inttoptr1, align 1 + %add2 = add i256 %ptrtoint, -64 + %inttoptr2 = inttoptr i256 %add2 to ptr addrspace(1) + store i256 58, ptr addrspace(1) %inttoptr2, align 1 + ret void +} + +; CHECK-LABEL: Function: test_offset_noalias3 +; CHECK: NoAlias: i256 addrspace(1)* %inttoptr1, i256 addrspace(1)* %inttoptr2 +define void @test_offset_noalias3(ptr addrspace(1) %addr) { + %ptrtoint = ptrtoint ptr addrspace(1) %addr to i256 + %add1 = sub i256 %ptrtoint, 32 + %inttoptr1 = inttoptr i256 %add1 to ptr addrspace(1) + store i256 3, ptr addrspace(1) %inttoptr1, align 1 + %add2 = sub i256 %ptrtoint, 64 + %inttoptr2 = inttoptr i256 %add2 to ptr addrspace(1) + store i256 58, ptr addrspace(1) %inttoptr2, align 1 + ret void +} + +; CHECK-LABEL: Function: test_offset_partialalias1 +; CHECK: PartialAlias: i256 addrspace(1)* %inttoptr1, i256 addrspace(1)* %inttoptr2 +define void @test_offset_partialalias1(ptr addrspace(1) %addr) { + %ptrtoint = ptrtoint ptr addrspace(1) %addr to i256 + %add1 = add i256 %ptrtoint, 32 + %inttoptr1 = inttoptr i256 %add1 to ptr addrspace(1) + store i256 3, ptr addrspace(1) %inttoptr1, align 1 + %add2 = add i256 %ptrtoint, 48 + %inttoptr2 = inttoptr i256 %add2 to ptr addrspace(1) + store i256 58, ptr addrspace(1) %inttoptr2, align 1 + ret void +} + +; CHECK-LABEL: Function: test_offset_partialalias2 +; CHECK: PartialAlias: i256 addrspace(1)* %inttoptr1, i256 addrspace(1)* %inttoptr2 +define void @test_offset_partialalias2(ptr addrspace(1) %addr) { + %ptrtoint = ptrtoint ptr addrspace(1) %addr to i256 + %add1 = add i256 %ptrtoint, -32 + %inttoptr1 = inttoptr i256 %add1 to ptr addrspace(1) + store i256 3, ptr addrspace(1) %inttoptr1, align 1 + %add2 = add i256 %ptrtoint, -48 + %inttoptr2 = inttoptr i256 %add2 to ptr addrspace(1) + store i256 58, ptr addrspace(1) %inttoptr2, align 1 + ret void +} + +; CHECK-LABEL: Function: test_offset_partialalias3 +; CHECK: PartialAlias: i256 addrspace(1)* %inttoptr1, i256 addrspace(1)* %inttoptr2 +define void @test_offset_partialalias3(ptr addrspace(1) %addr) { + %ptrtoint = ptrtoint ptr addrspace(1) %addr to i256 + %add1 = sub i256 %ptrtoint, 32 + %inttoptr1 = inttoptr i256 %add1 to ptr addrspace(1) + store i256 3, ptr addrspace(1) %inttoptr1, align 1 + %add2 = sub i256 %ptrtoint, 48 + %inttoptr2 = inttoptr i256 %add2 to ptr addrspace(1) + store i256 58, ptr addrspace(1) %inttoptr2, align 1 + ret void +} + +; CHECK-LABEL: Function: test_as1_i8_noalias +; CHECK: NoAlias: i8 addrspace(1)* %ptr1, i8 addrspace(1)* %ptr2 +define void @test_as1_i8_noalias() { + %ptr1 = inttoptr i8 32 to ptr addrspace(1) + %ptr2 = inttoptr i8 64 to ptr addrspace(1) + store i8 2, ptr addrspace(1) %ptr1, align 64 + store i8 1, ptr addrspace(1) %ptr2, align 64 + ret void +} + +; CHECK-LABEL: Function: test_unknown_as +; CHECK: MayAlias: i256 addrspace(1)* %ptr1, i256 addrspace(10)* %ptr2 +define void @test_unknown_as() { + %ptr1 = inttoptr i256 32 to ptr addrspace(1) + %ptr2 = inttoptr i256 64 to ptr addrspace(10) + store i256 2, ptr addrspace(1) %ptr1, align 64 + store i256 1, ptr addrspace(10) %ptr2, align 64 + ret void +} + +; CHECK-LABEL: Function: test_mayalias +; CHECK: MayAlias: i256* %0, i256* %1 +define void @test_mayalias(ptr %0, ptr %1) { + store i256 2, ptr %0, align 64 + store i256 1, ptr %1, align 64 + ret void +} + +; CHECK-LABEL: Function: test_noalias_fallback +; CHECK: NoAlias: i256* %0, i256* %1 +define void @test_noalias_fallback(ptr noalias %0, ptr noalias %1) { + store i256 2, ptr %0, align 64 + store i256 1, ptr %1, align 64 + ret void +} + +; CHECK-LABEL: Function: test_noalias +; CHECK: NoAlias: i256* %ptr0, i256 addrspace(1)* %ptr1 +; CHECK: NoAlias: i256* %ptr0, i256 addrspace(2)* %ptr2 +; CHECK: NoAlias: i256 addrspace(1)* %ptr1, i256 addrspace(2)* %ptr2 +; CHECK: NoAlias: i256* %ptr0, i256 addrspace(4)* %ptr4 +; CHECK: NoAlias: i256 addrspace(1)* %ptr1, i256 addrspace(4)* %ptr4 +; CHECK: NoAlias: i256 addrspace(2)* %ptr2, i256 addrspace(4)* %ptr4 +; CHECK: NoAlias: i256* %ptr0, i256 addrspace(5)* %ptr5 +; CHECK: NoAlias: i256 addrspace(1)* %ptr1, i256 addrspace(5)* %ptr5 +; CHECK: NoAlias: i256 addrspace(2)* %ptr2, i256 addrspace(5)* %ptr5 +; CHECK: NoAlias: i256 addrspace(4)* %ptr4, i256 addrspace(5)* %ptr5 +; CHECK: NoAlias: i256* %ptr0, i256 addrspace(6)* %ptr6 +; CHECK: NoAlias: i256 addrspace(1)* %ptr1, i256 addrspace(6)* %ptr6 +; CHECK: NoAlias: i256 addrspace(2)* %ptr2, i256 addrspace(6)* %ptr6 +; CHECK: NoAlias: i256 addrspace(4)* %ptr4, i256 addrspace(6)* %ptr6 +; CHECK: NoAlias: i256 addrspace(5)* %ptr5, i256 addrspace(6)* %ptr6 +define void @test_noalias() { + %ptr0 = inttoptr i256 32 to ptr + %ptr1 = inttoptr i256 32 to ptr addrspace(1) + %ptr2 = inttoptr i256 32 to ptr addrspace(2) + %ptr4 = inttoptr i256 32 to ptr addrspace(4) + %ptr5 = inttoptr i256 32 to ptr addrspace(5) + %ptr6 = inttoptr i256 32 to ptr addrspace(6) + store i256 1, ptr %ptr0, align 64 + store i256 1, ptr addrspace(1) %ptr1, align 64 + store i256 1, ptr addrspace(2) %ptr2, align 64 + store i256 1, ptr addrspace(4) %ptr4, align 64 + store i256 1, ptr addrspace(5) %ptr5, align 64 + store i256 1, ptr addrspace(6) %ptr6, align 64 + ret void +} + +; CHECK-LABEL: Function: test_as1_noalias +; CHECK: NoAlias: i256 addrspace(1)* %ptr1, i256 addrspace(1)* %ptr2 +define void @test_as1_noalias() { + %ptr1 = inttoptr i256 32 to ptr addrspace(1) + %ptr2 = inttoptr i256 64 to ptr addrspace(1) + store i256 2, ptr addrspace(1) %ptr1, align 64 + store i256 1, ptr addrspace(1) %ptr2, align 64 + ret void +} + +; CHECK-LABEL: Function: test_as1_mustalias +; CHECK: MustAlias: i256 addrspace(1)* %ptr1, i256 addrspace(1)* %ptr2 +define void @test_as1_mustalias() { + %ptr1 = inttoptr i256 32 to ptr addrspace(1) + %ptr2 = inttoptr i256 32 to ptr addrspace(1) + store i256 2, ptr addrspace(1) %ptr1, align 64 + store i256 1, ptr addrspace(1) %ptr2, align 64 + ret void +} + +; CHECK-LABEL: Function: test_as1_partialalias +; CHECK: PartialAlias: i256 addrspace(1)* %ptr1, i256 addrspace(1)* %ptr2 +define void @test_as1_partialalias() { + %ptr1 = inttoptr i256 32 to ptr addrspace(1) + %ptr2 = inttoptr i256 48 to ptr addrspace(1) + store i256 2, ptr addrspace(1) %ptr1, align 64 + store i256 1, ptr addrspace(1) %ptr2, align 64 + ret void +} + +; CHECK-LABEL: Function: test_as5_noalias +; CHECK: NoAlias: i256 addrspace(5)* %ptr1, i256 addrspace(5)* %ptr2 +define void @test_as5_noalias() { + %ptr1 = inttoptr i256 0 to ptr addrspace(5) + %ptr2 = inttoptr i256 1 to ptr addrspace(5) + store i256 2, ptr addrspace(5) %ptr1, align 64 + store i256 1, ptr addrspace(5) %ptr2, align 64 + ret void +} + +; CHECK-LABEL: Function: test_as5_mustalias +; CHECK: MustAlias: i256 addrspace(5)* %ptr1, i256 addrspace(5)* %ptr2 +define void @test_as5_mustalias() { + %ptr1 = inttoptr i256 0 to ptr addrspace(5) + %ptr2 = inttoptr i256 0 to ptr addrspace(5) + store i256 2, ptr addrspace(5) %ptr1, align 64 + store i256 1, ptr addrspace(5) %ptr2, align 64 + ret void +} + +; CHECK-LABEL: Function: test_as6_noalias +; CHECK: NoAlias: i256 addrspace(6)* %ptr1, i256 addrspace(6)* %ptr2 +define void @test_as6_noalias() { + %ptr1 = inttoptr i256 0 to ptr addrspace(6) + %ptr2 = inttoptr i256 1 to ptr addrspace(6) + store i256 2, ptr addrspace(6) %ptr1, align 64 + store i256 1, ptr addrspace(6) %ptr2, align 64 + ret void +} + +; CHECK-LABEL: Function: test_as6_mustalias +; CHECK: MustAlias: i256 addrspace(6)* %ptr1, i256 addrspace(6)* %ptr2 +define void @test_as6_mustalias() { + %ptr1 = inttoptr i256 0 to ptr addrspace(6) + %ptr2 = inttoptr i256 0 to ptr addrspace(6) + store i256 2, ptr addrspace(6) %ptr1, align 64 + store i256 1, ptr addrspace(6) %ptr2, align 64 + ret void +} + +; CHECK-LABEL: Function: test_as6_as5_alias_create +; CHECK: Both ModRef: Ptr: i256* %ptr1 <-> %ret = call i256 @llvm.evm.create +; CHECK: Both ModRef: Ptr: i256* %ptr2 <-> %ret = call i256 @llvm.evm.create +define i256 @test_as6_as5_alias_create(ptr addrspace(5) %ptr1) { + store i256 1, ptr addrspace(5) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(6) + store i256 2, ptr addrspace(6) %ptr2, align 32 + %ret = call i256 @llvm.evm.create(i256 0, ptr addrspace(1) null, i256 1) + ret i256 %ret +} + +; CHECK-LABEL: Function: test_as1_alias_create +; CHECK: Just Ref: Ptr: i256* %ptr1 <-> %ret = call i256 @llvm.evm.create +; CHECK: Just Ref: Ptr: i256* %ptr2 <-> %ret = call i256 @llvm.evm.create +; CHECK: NoModRef: Ptr: i256* %ptr3 <-> %ret = call i256 @llvm.evm.create +define i256 @test_as1_alias_create(ptr addrspace(1) %ptr1) { + store i256 1, ptr addrspace(1) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(1) + store i256 2, ptr addrspace(1) %ptr2, align 32 + %ptr3 = inttoptr i256 64 to ptr addrspace(1) + store i256 3, ptr addrspace(1) %ptr3, align 32 + %ret = call i256 @llvm.evm.create(i256 0, ptr addrspace(1) null, i256 33) + ret i256 %ret +} + +; CHECK-LABEL: Function: test_as6_as5_alias_create +; CHECK: Both ModRef: Ptr: i256* %ptr1 <-> %ret = call i256 @llvm.evm.create2 +; CHECK: Both ModRef: Ptr: i256* %ptr2 <-> %ret = call i256 @llvm.evm.create2 +define i256 @test_as6_as5_alias_create2(ptr addrspace(5) %ptr1) { + store i256 1, ptr addrspace(5) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(6) + store i256 2, ptr addrspace(6) %ptr2, align 32 + %ret = call i256 @llvm.evm.create2(i256 0, ptr addrspace(1) null, i256 1, i256 0) + ret i256 %ret +} + +; CHECK-LABEL: Function: test_as1_alias_create2 +; CHECK: Just Ref: Ptr: i256* %ptr1 <-> %ret = call i256 @llvm.evm.create2 +; CHECK: Just Ref: Ptr: i256* %ptr2 <-> %ret = call i256 @llvm.evm.create2 +; CHECK: NoModRef: Ptr: i256* %ptr3 <-> %ret = call i256 @llvm.evm.create2 +define i256 @test_as1_alias_create2(ptr addrspace(1) %ptr1) { + store i256 1, ptr addrspace(1) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(1) + store i256 2, ptr addrspace(1) %ptr2, align 32 + %ptr3 = inttoptr i256 64 to ptr addrspace(1) + store i256 3, ptr addrspace(1) %ptr3, align 32 + %ret = call i256 @llvm.evm.create2(i256 0, ptr addrspace(1) null, i256 33, i256 0) + ret i256 %ret +} + +; CHECK-LABEL: Function: test_as6_as5_alias_call +; CHECK: Both ModRef: Ptr: i256* %ptr1 <-> %ret = call i256 @llvm.evm.call +; CHECK: Both ModRef: Ptr: i256* %ptr2 <-> %ret = call i256 @llvm.evm.call +define i256 @test_as6_as5_alias_call(ptr addrspace(5) %ptr1) { + store i256 1, ptr addrspace(5) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(6) + store i256 2, ptr addrspace(6) %ptr2, align 32 + %ret = call i256 @llvm.evm.call(i256 1, i256 1, i256 1, ptr addrspace(1) null, i256 1, ptr addrspace(1) null, i256 1) + ret i256 %ret +} + +; CHECK-LABEL: Function: test_as1_alias_call +; CHECK: Both ModRef: Ptr: i256* %ptr1 <-> %ret = call i256 @llvm.evm.call +; CHECK: Just Ref: Ptr: i256* %ptr2 <-> %ret = call i256 @llvm.evm.call +; CHECK: NoModRef: Ptr: i256* %ptr3 <-> %ret = call i256 @llvm.evm.call +define i256 @test_as1_alias_call(ptr addrspace(1) %ptr1) { + store i256 1, ptr addrspace(1) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(1) + store i256 2, ptr addrspace(1) %ptr2, align 32 + %ptr3 = inttoptr i256 64 to ptr addrspace(1) + store i256 3, ptr addrspace(1) %ptr3, align 32 + %ret = call i256 @llvm.evm.call(i256 1, i256 1, i256 1, ptr addrspace(1) null, i256 33, ptr addrspace(1) null, i256 1) + ret i256 %ret +} + +; CHECK-LABEL: Function: test_as6_as5_alias_staticcall +; CHECK: Just Ref: Ptr: i256* %ptr1 <-> %ret = call i256 @llvm.evm.staticcall +; CHECK: Just Ref: Ptr: i256* %ptr2 <-> %ret = call i256 @llvm.evm.staticcall +define i256 @test_as6_as5_alias_staticcall(ptr addrspace(5) %ptr1) { + store i256 1, ptr addrspace(5) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(6) + store i256 2, ptr addrspace(6) %ptr2, align 32 + %ret = call i256 @llvm.evm.staticcall(i256 1, i256 1, ptr addrspace(1) null, i256 1, ptr addrspace(1) null, i256 1) + ret i256 %ret +} + +; CHECK-LABEL: Function: test_as1_alias_staticcall +; CHECK: Both ModRef: Ptr: i256* %ptr1 <-> %ret = call i256 @llvm.evm.staticcall +; CHECK: Just Ref: Ptr: i256* %ptr2 <-> %ret = call i256 @llvm.evm.staticcall +; CHECK: NoModRef: Ptr: i256* %ptr3 <-> %ret = call i256 @llvm.evm.staticcall +define i256 @test_as1_alias_staticcall(ptr addrspace(1) %ptr1) { + store i256 1, ptr addrspace(1) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(1) + store i256 2, ptr addrspace(1) %ptr2, align 32 + %ptr3 = inttoptr i256 64 to ptr addrspace(1) + store i256 3, ptr addrspace(1) %ptr3, align 32 + %ret = call i256 @llvm.evm.staticcall(i256 1, i256 1, ptr addrspace(1) null, i256 33, ptr addrspace(1) null, i256 1) + ret i256 %ret +} + +; CHECK-LABEL: Function: test_as6_as5_callcode +; CHECK: Both ModRef: Ptr: i256* %ptr1 <-> %ret = call i256 @llvm.evm.callcode +; CHECK: Both ModRef: Ptr: i256* %ptr2 <-> %ret = call i256 @llvm.evm.callcode +define i256 @test_as6_as5_callcode(ptr addrspace(5) %ptr1) { + store i256 1, ptr addrspace(5) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(6) + store i256 2, ptr addrspace(6) %ptr2, align 32 + %ret = call i256 @llvm.evm.callcode(i256 1, i256 1, i256 1, ptr addrspace(1) null, i256 1, ptr addrspace(1) null, i256 1) + ret i256 %ret +} + +; CHECK-LABEL: Function: test_as1_alias_callcode +; CHECK: Both ModRef: Ptr: i256* %ptr1 <-> %ret = call i256 @llvm.evm.callcode +; CHECK: Just Ref: Ptr: i256* %ptr2 <-> %ret = call i256 @llvm.evm.callcode +; CHECK: NoModRef: Ptr: i256* %ptr3 <-> %ret = call i256 @llvm.evm.callcode +define i256 @test_as1_alias_callcode(ptr addrspace(1) %ptr1) { + store i256 1, ptr addrspace(1) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(1) + store i256 2, ptr addrspace(1) %ptr2, align 32 + %ptr3 = inttoptr i256 64 to ptr addrspace(1) + store i256 3, ptr addrspace(1) %ptr3, align 32 + %ret = call i256 @llvm.evm.callcode(i256 1, i256 1, i256 1, ptr addrspace(1) null, i256 33, ptr addrspace(1) null, i256 1) + ret i256 %ret +} + +; CHECK-LABEL: Function: test_as6_as5_alias_delegatecall +; CHECK: Both ModRef: Ptr: i256* %ptr1 <-> %ret = call i256 @llvm.evm.delegatecall +; CHECK: Both ModRef: Ptr: i256* %ptr2 <-> %ret = call i256 @llvm.evm.delegatecall +define i256 @test_as6_as5_alias_delegatecall(ptr addrspace(5) %ptr1) { + store i256 1, ptr addrspace(5) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(6) + store i256 2, ptr addrspace(6) %ptr2, align 32 + %ret = call i256 @llvm.evm.delegatecall(i256 1, i256 1, ptr addrspace(1) null, i256 1, ptr addrspace(1) null, i256 1) + ret i256 %ret +} + +; CHECK-LABEL: Function: test_as1_alias_delegatecall +; CHECK: Both ModRef: Ptr: i256* %ptr1 <-> %ret = call i256 @llvm.evm.delegatecall +; CHECK: Just Ref: Ptr: i256* %ptr2 <-> %ret = call i256 @llvm.evm.delegatecall +; CHECK: NoModRef: Ptr: i256* %ptr3 <-> %ret = call i256 @llvm.evm.delegatecall +define i256 @test_as1_alias_delegatecall(ptr addrspace(1) %ptr1) { + store i256 1, ptr addrspace(1) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(1) + store i256 2, ptr addrspace(1) %ptr2, align 32 + %ptr3 = inttoptr i256 64 to ptr addrspace(1) + store i256 3, ptr addrspace(1) %ptr3, align 32 + %ret = call i256 @llvm.evm.delegatecall(i256 1, i256 1, ptr addrspace(1) null, i256 33, ptr addrspace(1) null, i256 1) + ret i256 %ret +} + +; CHECK-LABEL: Function: test_as6_as5_alias_revert +; CHECK: NoModRef: Ptr: i256* %ptr1 <-> call void @llvm.evm.revert +; CHECK: NoModRef: Ptr: i256* %ptr2 <-> call void @llvm.evm.revert +define void @test_as6_as5_alias_revert(ptr addrspace(5) %ptr1) noreturn { + store i256 1, ptr addrspace(5) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(6) + store i256 2, ptr addrspace(6) %ptr2, align 32 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 0) + unreachable +} + +; CHECK-LABEL: Function: test_as1_alias_revert +; CHECK: Just Ref: Ptr: i256* %ptr1 <-> call void @llvm.evm.revert +; CHECK: NoModRef: Ptr: i256* %ptr2 <-> call void @llvm.evm.revert +define void @test_as1_alias_revert(ptr addrspace(1) %ptr1) noreturn { + store i256 1, ptr addrspace(1) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(1) + store i256 2, ptr addrspace(1) %ptr2, align 32 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 32) + unreachable +} + +; CHECK-LABEL: Function: test_as6_as5_alias_return +; CHECK: Just Ref: Ptr: i256* %ptr1 <-> call void @llvm.evm.return +; CHECK: Just Ref: Ptr: i256* %ptr2 <-> call void @llvm.evm.return +define void @test_as6_as5_alias_return(ptr addrspace(5) %ptr1) noreturn { + store i256 1, ptr addrspace(5) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(6) + store i256 2, ptr addrspace(6) %ptr2, align 32 + call void @llvm.evm.return(ptr addrspace(1) null, i256 0) + unreachable +} + +; CHECK-LABEL: Function: test_as1_alias_return +; CHECK: Just Ref: Ptr: i256* %ptr1 <-> call void @llvm.evm.return +; CHECK: NoModRef: Ptr: i256* %ptr2 <-> call void @llvm.evm.return +define void @test_as1_alias_return(ptr addrspace(1) %ptr1) noreturn { + store i256 1, ptr addrspace(1) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(1) + store i256 2, ptr addrspace(1) %ptr2, align 32 + call void @llvm.evm.return(ptr addrspace(1) null, i256 32) + unreachable +} + +; CHECK-LABEL: Function: test_as1_alias_extcodecopy +; CHECK: Just Mod: Ptr: i256* null <-> call void @llvm.evm.extcodecopy +; CHECK: NoModRef: Ptr: i256* %ptr <-> call void @llvm.evm.extcodecopy +define void @test_as1_alias_extcodecopy() { + store i256 1, ptr addrspace(1) null, align 32 + %ptr = inttoptr i256 32 to ptr addrspace(1) + store i256 2, ptr addrspace(1) %ptr, align 32 + call void @llvm.evm.extcodecopy(i256 0, ptr addrspace(1) null, ptr addrspace(4) null, i256 32) + ret void +} diff --git a/llvm/test/CodeGen/EVM/aa-memory-opt.ll b/llvm/test/CodeGen/EVM/aa-memory-opt.ll new file mode 100644 index 000000000000..d4b598ec2b91 --- /dev/null +++ b/llvm/test/CodeGen/EVM/aa-memory-opt.ll @@ -0,0 +1,451 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2 +; RUN: opt -O3 -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @test_offset(ptr addrspace(1) %addr) { +; CHECK-LABEL: define noundef i256 @test_offset +; CHECK-SAME: (ptr addrspace(1) [[ADDR:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: [[PTRTOINT:%.*]] = ptrtoint ptr addrspace(1) [[ADDR]] to i256 +; CHECK-NEXT: [[ADD1:%.*]] = add i256 [[PTRTOINT]], 32 +; CHECK-NEXT: [[INTTOPTR1:%.*]] = inttoptr i256 [[ADD1]] to ptr addrspace(1) +; CHECK-NEXT: store i256 3, ptr addrspace(1) [[INTTOPTR1]], align 1 +; CHECK-NEXT: [[ADD2:%.*]] = add i256 [[PTRTOINT]], 64 +; CHECK-NEXT: [[INTTOPTR2:%.*]] = inttoptr i256 [[ADD2]] to ptr addrspace(1) +; CHECK-NEXT: store i256 58, ptr addrspace(1) [[INTTOPTR2]], align 1 +; CHECK-NEXT: ret i256 3 +; + %ptrtoint = ptrtoint ptr addrspace(1) %addr to i256 + %add1 = add i256 %ptrtoint, 32 + %inttoptr1 = inttoptr i256 %add1 to ptr addrspace(1) + store i256 3, ptr addrspace(1) %inttoptr1, align 1 + %add2 = add i256 %ptrtoint, 64 + %inttoptr2 = inttoptr i256 %add2 to ptr addrspace(1) + store i256 58, ptr addrspace(1) %inttoptr2, align 1 + %load = load i256, ptr addrspace(1) %inttoptr1, align 1 + ret i256 %load +} + +define i256 @test_memcpy() { +; CHECK-LABEL: define noundef i256 @test_memcpy +; CHECK-SAME: () local_unnamed_addr #[[ATTR1:[0-9]+]] { +; CHECK-NEXT: store i256 2, ptr addrspace(1) inttoptr (i256 2 to ptr addrspace(1)), align 64 +; CHECK-NEXT: tail call void @llvm.memcpy.p1.p1.i256(ptr addrspace(1) noundef nonnull align 32 dereferenceable(53) inttoptr (i256 96 to ptr addrspace(1)), ptr addrspace(1) noundef nonnull align 256 dereferenceable(53) inttoptr (i256 256 to ptr addrspace(1)), i256 53, i1 false) +; CHECK-NEXT: ret i256 2 +; + store i256 2, ptr addrspace(1) inttoptr (i256 2 to ptr addrspace(1)), align 64 + tail call void @llvm.memcpy.p1.p1.i256(ptr addrspace(1) inttoptr (i256 96 to ptr addrspace(1)), ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 53, i1 false) + %ret = load i256, ptr addrspace(1) inttoptr (i256 2 to ptr addrspace(1)), align 64 + ret i256 %ret +} + +define i256 @test_different_locsizes() { +; CHECK-LABEL: define noundef i256 @test_different_locsizes +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: store i256 2, ptr addrspace(1) inttoptr (i256 2 to ptr addrspace(1)), align 64 +; CHECK-NEXT: store i8 1, ptr addrspace(1) inttoptr (i8 1 to ptr addrspace(1)), align 64 +; CHECK-NEXT: ret i256 2 +; + store i256 2, ptr addrspace(1) inttoptr (i256 2 to ptr addrspace(1)), align 64 + store i8 1, ptr addrspace(1) inttoptr (i8 1 to ptr addrspace(1)), align 64 + %ret = load i256, ptr addrspace(1) inttoptr (i256 2 to ptr addrspace(1)), align 64 + ret i256 %ret +} + +; TODO: #904: It is safe to remove load in this test, but it is not done since benchmark +; numbers showed that it is not profitable to do that for heap address space. +define i256 @test_gas_as1() { +; CHECK-LABEL: define i256 @test_gas_as1 +; CHECK-SAME: () local_unnamed_addr #[[ATTR2:[0-9]+]] { +; CHECK-NEXT: store i256 2, ptr addrspace(1) inttoptr (i256 2 to ptr addrspace(1)), align 64 +; CHECK-NEXT: [[GAS:%.*]] = tail call i256 @llvm.evm.gas() +; CHECK-NEXT: [[LOAD:%.*]] = load i256, ptr addrspace(1) inttoptr (i256 2 to ptr addrspace(1)), align 64 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[LOAD]], [[GAS]] +; CHECK-NEXT: ret i256 [[RET]] +; + store i256 2, ptr addrspace(1) inttoptr (i256 2 to ptr addrspace(1)), align 64 + %gas = call i256 @llvm.evm.gas() + %load = load i256, ptr addrspace(1) inttoptr (i256 2 to ptr addrspace(1)), align 64 + %ret = add i256 %load, %gas + ret i256 %ret +} + +define i256 @test_gas_as5() { +; CHECK-LABEL: define i256 @test_gas_as5 +; CHECK-SAME: () local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 +; CHECK-NEXT: [[GAS:%.*]] = tail call i256 @llvm.evm.gas() +; CHECK-NEXT: [[RET:%.*]] = add i256 [[GAS]], 2 +; CHECK-NEXT: ret i256 [[RET]] +; + store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 + %gas = call i256 @llvm.evm.gas() + %load = load i256, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 + %ret = add i256 %load, %gas + ret i256 %ret +} + +define i256 @test_gas_as6() { +; CHECK-LABEL: define i256 @test_gas_as6 +; CHECK-SAME: () local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: store i256 2, ptr addrspace(6) inttoptr (i256 2 to ptr addrspace(6)), align 64 +; CHECK-NEXT: [[GAS:%.*]] = tail call i256 @llvm.evm.gas() +; CHECK-NEXT: [[RET:%.*]] = add i256 [[GAS]], 2 +; CHECK-NEXT: ret i256 [[RET]] +; + store i256 2, ptr addrspace(6) inttoptr (i256 2 to ptr addrspace(6)), align 64 + %gas = call i256 @llvm.evm.gas() + %load = load i256, ptr addrspace(6) inttoptr (i256 2 to ptr addrspace(6)), align 64 + %ret = add i256 %load, %gas + ret i256 %ret +} + +define i256 @test_log0() { +; CHECK-LABEL: define noundef i256 @test_log0 +; CHECK-SAME: () local_unnamed_addr #[[ATTR3:[0-9]+]] { +; CHECK-NEXT: store i256 2, ptr addrspace(5) null, align 4294967296 +; CHECK-NEXT: store i256 7, ptr addrspace(1) null, align 4294967296 +; CHECK-NEXT: tail call void @llvm.evm.log0(ptr addrspace(1) null, i256 32) +; CHECK-NEXT: ret i256 9 +; + store i256 2, ptr addrspace(5) null, align 32 + store i256 7, ptr addrspace(1) null, align 32 + call void @llvm.evm.log0(ptr addrspace(1) null, i256 32) + %tmp = load i256, ptr addrspace(5) null, align 32 + %tmp2 = load i256, ptr addrspace(1) null, align 32 + %ret = add i256 %tmp, %tmp2 + ret i256 %ret +} + +define i256 @test_log1() { +; CHECK-LABEL: define noundef i256 @test_log1 +; CHECK-SAME: () local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: store i256 2, ptr addrspace(5) null, align 4294967296 +; CHECK-NEXT: store i256 7, ptr addrspace(1) null, align 4294967296 +; CHECK-NEXT: tail call void @llvm.evm.log1(ptr addrspace(1) null, i256 32, i256 0) +; CHECK-NEXT: ret i256 9 +; + store i256 2, ptr addrspace(5) null, align 32 + store i256 7, ptr addrspace(1) null, align 32 + call void @llvm.evm.log1(ptr addrspace(1) null, i256 32, i256 0) + %tmp = load i256, ptr addrspace(5) null, align 32 + %tmp2 = load i256, ptr addrspace(1) null, align 32 + %ret = add i256 %tmp, %tmp2 + ret i256 %ret +} + +define i256 @test_log2() { +; CHECK-LABEL: define noundef i256 @test_log2 +; CHECK-SAME: () local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: store i256 2, ptr addrspace(5) null, align 4294967296 +; CHECK-NEXT: store i256 7, ptr addrspace(1) null, align 4294967296 +; CHECK-NEXT: tail call void @llvm.evm.log2(ptr addrspace(1) null, i256 32, i256 0, i256 0) +; CHECK-NEXT: ret i256 9 +; + store i256 2, ptr addrspace(5) null, align 32 + store i256 7, ptr addrspace(1) null, align 32 + call void @llvm.evm.log2(ptr addrspace(1) null, i256 32, i256 0, i256 0) + %tmp = load i256, ptr addrspace(5) null, align 32 + %tmp2 = load i256, ptr addrspace(1) null, align 32 + %ret = add i256 %tmp, %tmp2 + ret i256 %ret +} + +define i256 @test_log3() { +; CHECK-LABEL: define noundef i256 @test_log3 +; CHECK-SAME: () local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: store i256 2, ptr addrspace(5) null, align 4294967296 +; CHECK-NEXT: store i256 7, ptr addrspace(1) null, align 4294967296 +; CHECK-NEXT: tail call void @llvm.evm.log3(ptr addrspace(1) null, i256 32, i256 0, i256 0, i256 0) +; CHECK-NEXT: ret i256 9 +; + store i256 2, ptr addrspace(5) null, align 32 + store i256 7, ptr addrspace(1) null, align 32 + call void @llvm.evm.log3(ptr addrspace(1) null, i256 32, i256 0, i256 0, i256 0) + %tmp = load i256, ptr addrspace(5) null, align 32 + %tmp2 = load i256, ptr addrspace(1) null, align 32 + %ret = add i256 %tmp, %tmp2 + ret i256 %ret +} + +define i256 @test_log4() { +; CHECK-LABEL: define noundef i256 @test_log4 +; CHECK-SAME: () local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: store i256 2, ptr addrspace(5) null, align 4294967296 +; CHECK-NEXT: store i256 7, ptr addrspace(1) null, align 4294967296 +; CHECK-NEXT: tail call void @llvm.evm.log4(ptr addrspace(1) null, i256 32, i256 0, i256 0, i256 0, i256 0) +; CHECK-NEXT: ret i256 9 +; + store i256 2, ptr addrspace(5) null, align 32 + store i256 7, ptr addrspace(1) null, align 32 + call void @llvm.evm.log4(ptr addrspace(1) null, i256 32, i256 0, i256 0, i256 0, i256 0) + %tmp = load i256, ptr addrspace(5) null, align 32 + %tmp2 = load i256, ptr addrspace(1) null, align 32 + %ret = add i256 %tmp, %tmp2 + ret i256 %ret +} + +define i256 @test_as() { +; CHECK-LABEL: define noundef i256 @test_as +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 +; CHECK-NEXT: store i256 1, ptr addrspace(1) inttoptr (i256 1 to ptr addrspace(1)), align 64 +; CHECK-NEXT: ret i256 2 +; + store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 + store i256 1, ptr addrspace(1) inttoptr (i256 1 to ptr addrspace(1)), align 64 + %ret = load i256, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 + ret i256 %ret +} + +define i256 @test_as1_overlap() { +; CHECK-LABEL: define i256 @test_as1_overlap +; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { +; CHECK-NEXT: store i256 2, ptr addrspace(1) null, align 4294967296 +; CHECK-NEXT: store i256 1, ptr addrspace(1) inttoptr (i256 31 to ptr addrspace(1)), align 64 +; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(1) null, align 4294967296 +; CHECK-NEXT: ret i256 [[RET]] +; + store i256 2, ptr addrspace(1) null, align 64 + store i256 1, ptr addrspace(1) inttoptr (i256 31 to ptr addrspace(1)), align 64 + %ret = load i256, ptr addrspace(1) null, align 64 + ret i256 %ret +} + +define i256 @test_as1_null() { +; CHECK-LABEL: define noundef i256 @test_as1_null +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: store i256 2, ptr addrspace(1) null, align 4294967296 +; CHECK-NEXT: store i256 1, ptr addrspace(1) inttoptr (i256 32 to ptr addrspace(1)), align 64 +; CHECK-NEXT: ret i256 2 +; + store i256 2, ptr addrspace(1) null, align 64 + store i256 1, ptr addrspace(1) inttoptr (i256 32 to ptr addrspace(1)), align 64 + %ret = load i256, ptr addrspace(1) null, align 64 + ret i256 %ret +} + +define i256 @test_as1_small() { +; CHECK-LABEL: define noundef i256 @test_as1_small +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: store i256 2, ptr addrspace(1) inttoptr (i256 33 to ptr addrspace(1)), align 64 +; CHECK-NEXT: store i256 1, ptr addrspace(1) inttoptr (i256 1 to ptr addrspace(1)), align 64 +; CHECK-NEXT: ret i256 2 +; + store i256 2, ptr addrspace(1) inttoptr (i256 33 to ptr addrspace(1)), align 64 + store i256 1, ptr addrspace(1) inttoptr (i256 1 to ptr addrspace(1)), align 64 + %ret = load i256, ptr addrspace(1) inttoptr (i256 33 to ptr addrspace(1)), align 64 + ret i256 %ret +} + +define i256 @test_as1_large() { +; CHECK-LABEL: define noundef i256 @test_as1_large +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: store i256 2, ptr addrspace(1) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533664 to ptr addrspace(1)), align 64 +; CHECK-NEXT: store i256 1, ptr addrspace(1) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533632 to ptr addrspace(1)), align 4294967296 +; CHECK-NEXT: ret i256 2 +; + store i256 2, ptr addrspace(1) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533664 to ptr addrspace(1)), align 64 + store i256 1, ptr addrspace(1) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533632 to ptr addrspace(1)), align 64 + %ret = load i256, ptr addrspace(1) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533664 to ptr addrspace(1)), align 64 + ret i256 %ret +} + +define i256 @test_as5_null() { +; CHECK-LABEL: define noundef i256 @test_as5_null +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: store i256 2, ptr addrspace(5) null, align 4294967296 +; CHECK-NEXT: store i256 1, ptr addrspace(5) inttoptr (i256 1 to ptr addrspace(5)), align 64 +; CHECK-NEXT: ret i256 2 +; + store i256 2, ptr addrspace(5) null, align 64 + store i256 1, ptr addrspace(5) inttoptr (i256 1 to ptr addrspace(5)), align 64 + %ret = load i256, ptr addrspace(5) null, align 64 + ret i256 %ret +} + +define i256 @test_as5_small() { +; CHECK-LABEL: define noundef i256 @test_as5_small +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 +; CHECK-NEXT: store i256 1, ptr addrspace(5) inttoptr (i256 1 to ptr addrspace(5)), align 64 +; CHECK-NEXT: ret i256 2 +; + store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 + store i256 1, ptr addrspace(5) inttoptr (i256 1 to ptr addrspace(5)), align 64 + %ret = load i256, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 + ret i256 %ret +} + +define i256 @test_as5_large() { +; CHECK-LABEL: define noundef i256 @test_as5_large +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: store i256 2, ptr addrspace(5) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533632 to ptr addrspace(5)), align 4294967296 +; CHECK-NEXT: store i256 1, ptr addrspace(5) inttoptr (i256 1 to ptr addrspace(5)), align 64 +; CHECK-NEXT: ret i256 2 +; + store i256 2, ptr addrspace(5) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533632 to ptr addrspace(5)), align 64 + store i256 1, ptr addrspace(5) inttoptr (i256 1 to ptr addrspace(5)), align 64 + %ret = load i256, ptr addrspace(5) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533632 to ptr addrspace(5)), align 64 + ret i256 %ret +} + +define i256 @test_as6_small() { +; CHECK-LABEL: define noundef i256 @test_as6_small +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: store i256 2, ptr addrspace(6) inttoptr (i256 2 to ptr addrspace(6)), align 64 +; CHECK-NEXT: store i256 1, ptr addrspace(6) inttoptr (i256 1 to ptr addrspace(6)), align 64 +; CHECK-NEXT: ret i256 2 +; + store i256 2, ptr addrspace(6) inttoptr (i256 2 to ptr addrspace(6)), align 64 + store i256 1, ptr addrspace(6) inttoptr (i256 1 to ptr addrspace(6)), align 64 + %ret = load i256, ptr addrspace(6) inttoptr (i256 2 to ptr addrspace(6)), align 64 + ret i256 %ret +} + +define i256 @test_as6_large() { +; CHECK-LABEL: define noundef i256 @test_as6_large +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: store i256 2, ptr addrspace(6) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533632 to ptr addrspace(6)), align 4294967296 +; CHECK-NEXT: store i256 1, ptr addrspace(6) inttoptr (i256 1 to ptr addrspace(6)), align 64 +; CHECK-NEXT: ret i256 2 +; + store i256 2, ptr addrspace(6) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533632 to ptr addrspace(6)), align 64 + store i256 1, ptr addrspace(6) inttoptr (i256 1 to ptr addrspace(6)), align 64 + %ret = load i256, ptr addrspace(6) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533632 to ptr addrspace(6)), align 64 + ret i256 %ret +} + +define i256 @test_opt_staticcall(ptr addrspace(5) %ptr1, ptr addrspace(6) %ptr2) { +; CHECK-LABEL: define noundef i256 @test_opt_staticcall +; CHECK-SAME: (ptr addrspace(5) nocapture writeonly [[PTR1:%.*]], ptr addrspace(6) nocapture writeonly [[PTR2:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: store i256 1, ptr addrspace(5) [[PTR1]], align 32 +; CHECK-NEXT: store i256 2, ptr addrspace(6) [[PTR2]], align 32 +; CHECK-NEXT: [[TMP1:%.*]] = tail call i256 @llvm.evm.staticcall(i256 1, i256 1, ptr addrspace(1) null, i256 1, ptr addrspace(1) null, i256 1) +; CHECK-NEXT: ret i256 3 +; + store i256 1, ptr addrspace(5) %ptr1, align 32 + store i256 2, ptr addrspace(6) %ptr2, align 32 + call i256 @llvm.evm.staticcall(i256 1, i256 1, ptr addrspace(1) null, i256 1, ptr addrspace(1) null, i256 1) + %ret1 = load i256, ptr addrspace(5) %ptr1 + %ret2 = load i256, ptr addrspace(6) %ptr2 + %ret = add i256 %ret1, %ret2 + ret i256 %ret +} + +; Verify that in the following tests all load operations are preserved. + +define i256 @test_noopt_create(ptr addrspace(5) %ptr1, ptr addrspace(6) %ptr2) { +; CHECK-LABEL: define i256 @test_noopt_create +; CHECK-SAME: (ptr addrspace(5) nocapture [[PTR1:%.*]], ptr addrspace(6) nocapture [[PTR2:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: store i256 1, ptr addrspace(5) [[PTR1]], align 32 +; CHECK-NEXT: store i256 2, ptr addrspace(6) [[PTR2]], align 32 +; CHECK-NEXT: [[TMP1:%.*]] = tail call i256 @llvm.evm.create(i256 0, ptr addrspace(1) null, i256 1) +; CHECK-NEXT: [[RET1:%.*]] = load i256, ptr addrspace(5) [[PTR1]], align 32 +; CHECK-NEXT: [[RET2:%.*]] = load i256, ptr addrspace(6) [[PTR2]], align 32 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[RET2]], [[RET1]] +; CHECK-NEXT: ret i256 [[RET]] +; + store i256 1, ptr addrspace(5) %ptr1, align 32 + store i256 2, ptr addrspace(6) %ptr2, align 32 + call i256 @llvm.evm.create(i256 0, ptr addrspace(1) null, i256 1) + %ret1 = load i256, ptr addrspace(5) %ptr1 + %ret2 = load i256, ptr addrspace(6) %ptr2 + %ret = add i256 %ret1, %ret2 + ret i256 %ret +} + +define i256 @test_noopt_create2(ptr addrspace(5) %ptr1, ptr addrspace(6) %ptr2) { +; CHECK-LABEL: define i256 @test_noopt_create2 +; CHECK-SAME: (ptr addrspace(5) nocapture [[PTR1:%.*]], ptr addrspace(6) nocapture [[PTR2:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: store i256 1, ptr addrspace(5) [[PTR1]], align 32 +; CHECK-NEXT: store i256 2, ptr addrspace(6) [[PTR2]], align 32 +; CHECK-NEXT: [[TMP1:%.*]] = tail call i256 @llvm.evm.create2(i256 0, ptr addrspace(1) null, i256 1, i256 0) +; CHECK-NEXT: [[RET1:%.*]] = load i256, ptr addrspace(5) [[PTR1]], align 32 +; CHECK-NEXT: [[RET2:%.*]] = load i256, ptr addrspace(6) [[PTR2]], align 32 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[RET2]], [[RET1]] +; CHECK-NEXT: ret i256 [[RET]] +; + store i256 1, ptr addrspace(5) %ptr1, align 32 + store i256 2, ptr addrspace(6) %ptr2, align 32 + call i256 @llvm.evm.create2(i256 0, ptr addrspace(1) null, i256 1, i256 0) + %ret1 = load i256, ptr addrspace(5) %ptr1 + %ret2 = load i256, ptr addrspace(6) %ptr2 + %ret = add i256 %ret1, %ret2 + ret i256 %ret +} + +define i256 @test_noopt_call(ptr addrspace(5) %ptr1, ptr addrspace(6) %ptr2) { +; CHECK-LABEL: define i256 @test_noopt_call +; CHECK-SAME: (ptr addrspace(5) nocapture [[PTR1:%.*]], ptr addrspace(6) nocapture [[PTR2:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: store i256 1, ptr addrspace(5) [[PTR1]], align 32 +; CHECK-NEXT: store i256 2, ptr addrspace(6) [[PTR2]], align 32 +; CHECK-NEXT: [[TMP1:%.*]] = tail call i256 @llvm.evm.call(i256 1, i256 1, i256 1, ptr addrspace(1) null, i256 1, ptr addrspace(1) null, i256 1) +; CHECK-NEXT: [[RET1:%.*]] = load i256, ptr addrspace(5) [[PTR1]], align 32 +; CHECK-NEXT: [[RET2:%.*]] = load i256, ptr addrspace(6) [[PTR2]], align 32 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[RET2]], [[RET1]] +; CHECK-NEXT: ret i256 [[RET]] +; + store i256 1, ptr addrspace(5) %ptr1, align 32 + store i256 2, ptr addrspace(6) %ptr2, align 32 + call i256 @llvm.evm.call(i256 1, i256 1, i256 1, ptr addrspace(1) null, i256 1, ptr addrspace(1) null, i256 1) + %ret1 = load i256, ptr addrspace(5) %ptr1 + %ret2 = load i256, ptr addrspace(6) %ptr2 + %ret = add i256 %ret1, %ret2 + ret i256 %ret +} + +define i256 @test_noopt_callcode(ptr addrspace(5) %ptr1, ptr addrspace(6) %ptr2) { +; CHECK-LABEL: define i256 @test_noopt_callcode +; CHECK-SAME: (ptr addrspace(5) nocapture [[PTR1:%.*]], ptr addrspace(6) nocapture [[PTR2:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: store i256 1, ptr addrspace(5) [[PTR1]], align 32 +; CHECK-NEXT: store i256 2, ptr addrspace(6) [[PTR2]], align 32 +; CHECK-NEXT: [[TMP1:%.*]] = tail call i256 @llvm.evm.callcode(i256 1, i256 1, i256 1, ptr addrspace(1) null, i256 1, ptr addrspace(1) null, i256 1) +; CHECK-NEXT: [[RET1:%.*]] = load i256, ptr addrspace(5) [[PTR1]], align 32 +; CHECK-NEXT: [[RET2:%.*]] = load i256, ptr addrspace(6) [[PTR2]], align 32 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[RET2]], [[RET1]] +; CHECK-NEXT: ret i256 [[RET]] +; + store i256 1, ptr addrspace(5) %ptr1, align 32 + store i256 2, ptr addrspace(6) %ptr2, align 32 + call i256 @llvm.evm.callcode(i256 1, i256 1, i256 1, ptr addrspace(1) null, i256 1, ptr addrspace(1) null, i256 1) + %ret1 = load i256, ptr addrspace(5) %ptr1 + %ret2 = load i256, ptr addrspace(6) %ptr2 + %ret = add i256 %ret1, %ret2 + ret i256 %ret +} + +define i256 @test_noopt_delegatecall(ptr addrspace(5) %ptr1, ptr addrspace(6) %ptr2) { +; CHECK-LABEL: define i256 @test_noopt_delegatecall +; CHECK-SAME: (ptr addrspace(5) nocapture [[PTR1:%.*]], ptr addrspace(6) nocapture [[PTR2:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: store i256 1, ptr addrspace(5) [[PTR1]], align 32 +; CHECK-NEXT: store i256 2, ptr addrspace(6) [[PTR2]], align 32 +; CHECK-NEXT: [[TMP1:%.*]] = tail call i256 @llvm.evm.delegatecall(i256 1, i256 1, ptr addrspace(1) null, i256 1, ptr addrspace(1) null, i256 1) +; CHECK-NEXT: [[RET1:%.*]] = load i256, ptr addrspace(5) [[PTR1]], align 32 +; CHECK-NEXT: [[RET2:%.*]] = load i256, ptr addrspace(6) [[PTR2]], align 32 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[RET2]], [[RET1]] +; CHECK-NEXT: ret i256 [[RET]] +; + store i256 1, ptr addrspace(5) %ptr1, align 32 + store i256 2, ptr addrspace(6) %ptr2, align 32 + call i256 @llvm.evm.delegatecall(i256 1, i256 1, ptr addrspace(1) null, i256 1, ptr addrspace(1) null, i256 1) + %ret1 = load i256, ptr addrspace(5) %ptr1 + %ret2 = load i256, ptr addrspace(6) %ptr2 + %ret = add i256 %ret1, %ret2 + ret i256 %ret +} + +declare i256 @llvm.evm.create(i256, ptr addrspace(1), i256) +declare i256 @llvm.evm.call(i256, i256, i256, ptr addrspace(1), i256, ptr addrspace(1), i256) +declare i256 @llvm.evm.callcode(i256, i256, i256, ptr addrspace(1), i256, ptr addrspace(1), i256) +declare i256 @llvm.evm.delegatecall(i256, i256, ptr addrspace(1), i256, ptr addrspace(1), i256) +declare i256 @llvm.evm.create2(i256, ptr addrspace(1), i256, i256) +declare i256 @llvm.evm.staticcall(i256, i256, ptr addrspace(1), i256, ptr addrspace(1), i256) +declare void @llvm.memcpy.p1.p1.i256(ptr addrspace(1), ptr addrspace(1), i256, i1 immarg) +declare i256 @llvm.evm.gas() +declare void @llvm.evm.log0(ptr addrspace(1), i256) +declare void @llvm.evm.log1(ptr addrspace(1), i256, i256) +declare void @llvm.evm.log2(ptr addrspace(1), i256, i256, i256) +declare void @llvm.evm.log3(ptr addrspace(1), i256, i256, i256, i256) +declare void @llvm.evm.log4(ptr addrspace(1), i256, i256, i256, i256, i256) diff --git a/llvm/test/CodeGen/EVM/add.ll b/llvm/test/CodeGen/EVM/add.ll new file mode 100644 index 000000000000..12f7aed61c23 --- /dev/null +++ b/llvm/test/CodeGen/EVM/add.ll @@ -0,0 +1,43 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @addrrr(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: addrrr: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = add i256 %rs1, %rs2 + ret i256 %res +} + +define i256 @addrri(i256 %rs1) nounwind { +; CHECK-LABEL: addrri: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH9 0x10000000000000000 +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = add i256 %rs1, 18446744073709551616 ; 65-bits + ret i256 %res +} + +define i256 @subrri(i256 %rs1) nounwind { +; CHECK-LABEL: subrri: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = sub i256 %rs1, -1 + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/aext.ll b/llvm/test/CodeGen/EVM/aext.ll new file mode 100644 index 000000000000..6231c1580511 --- /dev/null +++ b/llvm/test/CodeGen/EVM/aext.ll @@ -0,0 +1,18 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i8 @aexti8(i8 %rs1) nounwind { +; CHECK-LABEL: aexti8: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = add i8 %rs1, 1 + ret i8 %res +} diff --git a/llvm/test/CodeGen/EVM/always-inline.ll b/llvm/test/CodeGen/EVM/always-inline.ll new file mode 100644 index 000000000000..126da7aa14b8 --- /dev/null +++ b/llvm/test/CodeGen/EVM/always-inline.ll @@ -0,0 +1,45 @@ +; RUN: opt -passes=evm-always-inline -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S32-a:256:256" +target triple = "evm" + +; CHECK: Function Attrs: alwaysinline +; CHECK-LABEL: @inline +define void @inline() { + ret void +} + +; CHECK-NOT: Function Attrs: alwaysinline +; CHECK-LABEL: @noinline +define void @noinline() { + ret void +} + +; CHECK-NOT: Function Attrs: alwaysinline +; CHECK-LABEL: @test +define void @test() { + call void @inline() + call void @noinline() + call void @noinline() + ret void +} + +; CHECK-NOT: Function Attrs: alwaysinline +; CHECK-LABEL: @callattr +define void @callattr() { + ret void +} + +; CHECK-NOT: Function Attrs: alwaysinline +; CHECK-LABEL: @test_noinline_callattr +define void @test_noinline_callattr() { + call void @callattr() noinline + ret void +} + +; CHECK-NOT: Function Attrs: alwaysinline +; CHECK-LABEL: @test_noinline_recursion +define void @test_noinline_recursion() { + call void @test_noinline_recursion() + ret void +} diff --git a/llvm/test/CodeGen/EVM/bitmanipulation-intrinsics.ll b/llvm/test/CodeGen/EVM/bitmanipulation-intrinsics.ll new file mode 100644 index 000000000000..e5156c87e18e --- /dev/null +++ b/llvm/test/CodeGen/EVM/bitmanipulation-intrinsics.ll @@ -0,0 +1,770 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare i256 @llvm.bitreverse.i256(i256) +declare i256 @llvm.bswap.i256(i256) +declare i256 @llvm.ctpop.i256(i256) +declare i256 @llvm.ctlz.i256(i256, i1) +declare i256 @llvm.cttz.i256(i256, i1) + +define i256 @bitreversetest(i256 %v) { +; CHECK-LABEL: bitreversetest: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: BYTE +; CHECK-NEXT: PUSH2 0xFF00 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0xE8 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH3 0xFF0000 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0xD8 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH4 0xFF000000 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 0xC8 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH5 0xFF00000000 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0xB8 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH6 0xFF0000000000 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 0xA8 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH7 0xFF000000000000 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 0x98 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH8 0xFF00000000000000 +; CHECK-NEXT: DUP5 +; CHECK-NEXT: PUSH1 0x88 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH9 0xFF0000000000000000 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0x78 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH10 0xFF000000000000000000 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 0x68 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH11 0xFF00000000000000000000 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 0x58 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH12 0xFF0000000000000000000000 +; CHECK-NEXT: DUP5 +; CHECK-NEXT: PUSH1 0x48 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH13 0xFF000000000000000000000000 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 0x38 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH14 0xFF00000000000000000000000000 +; CHECK-NEXT: DUP5 +; CHECK-NEXT: PUSH1 0x28 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH15 0xFF0000000000000000000000000000 +; CHECK-NEXT: DUP5 +; CHECK-NEXT: PUSH1 0x18 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0x78 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP6 +; CHECK-NEXT: PUSH1 0x8 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0x80 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH1 0x8 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0x88 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0x18 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0x90 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0x28 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0x98 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 0x38 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xA0 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0x48 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xA8 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 0x58 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xB0 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 0x68 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xB8 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP5 +; CHECK-NEXT: PUSH1 0x78 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xC0 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH1 0x88 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xC8 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0x98 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xD0 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0xA8 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xD8 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 0xB8 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xE0 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH1 0xC8 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xE8 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0xD8 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xF0 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH1 0xE8 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 0xF8 +; CHECK-NEXT: SHL +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH32 0xF0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F +; CHECK-NEXT: DUP1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 0x4 +; CHECK-NEXT: SHL +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: PUSH1 0x4 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH32 0x3333333333333333333333333333333333333333333333333333333333333333 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 0x2 +; CHECK-NEXT: SHL +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: PUSH1 0x2 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH32 0x5555555555555555555555555555555555555555555555555555555555555555 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: SHL +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.bitreverse.i256(i256 %v) + ret i256 %res +} + +define i256 @bswaptest(i256 %v) { +; CHECK-LABEL: bswaptest: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: BYTE +; CHECK-NEXT: PUSH2 0xFF00 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0xE8 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH3 0xFF0000 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0xD8 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH4 0xFF000000 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 0xC8 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH5 0xFF00000000 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0xB8 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH6 0xFF0000000000 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 0xA8 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH7 0xFF000000000000 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 0x98 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH8 0xFF00000000000000 +; CHECK-NEXT: DUP5 +; CHECK-NEXT: PUSH1 0x88 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH9 0xFF0000000000000000 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0x78 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH10 0xFF000000000000000000 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 0x68 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH11 0xFF00000000000000000000 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 0x58 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH12 0xFF0000000000000000000000 +; CHECK-NEXT: DUP5 +; CHECK-NEXT: PUSH1 0x48 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH13 0xFF000000000000000000000000 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 0x38 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH14 0xFF00000000000000000000000000 +; CHECK-NEXT: DUP5 +; CHECK-NEXT: PUSH1 0x28 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH15 0xFF0000000000000000000000000000 +; CHECK-NEXT: DUP5 +; CHECK-NEXT: PUSH1 0x18 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0x78 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP6 +; CHECK-NEXT: PUSH1 0x8 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0x80 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH1 0x8 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0x88 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0x18 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0x90 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0x28 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0x98 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 0x38 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xA0 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0x48 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xA8 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 0x58 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xB0 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 0x68 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xB8 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP5 +; CHECK-NEXT: PUSH1 0x78 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xC0 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH1 0x88 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xC8 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0x98 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xD0 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0xA8 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xD8 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 0xB8 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xE0 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH1 0xC8 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xE8 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0xD8 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xF0 +; CHECK-NEXT: SHL +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH1 0xE8 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 0xF8 +; CHECK-NEXT: SHL +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.bswap.i256(i256 %v) + ret i256 %res +} + +define i256 @ctpoptest(i256 %v) { +; CHECK-LABEL: ctpoptest: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH32 0x101010101010101010101010101010101010101010101010101010101010101 +; CHECK-NEXT: PUSH16 0xF0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F +; CHECK-NEXT: DUP2 +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH32 0x3333333333333333333333333333333333333333333333333333333333333333 +; CHECK-NEXT: DUP6 +; CHECK-NEXT: PUSH16 0x55555555555555555555555555555555 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP8 +; CHECK-NEXT: PUSH1 0x81 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH1 0x80 +; CHECK-NEXT: SHR +; CHECK-NEXT: SUB +; CHECK-NEXT: DUP3 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0x2 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: AND +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP7 +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH16 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +; CHECK-NEXT: AND +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0x2 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: AND +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP5 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH1 0x4 +; CHECK-NEXT: SHR +; CHECK-NEXT: ADD +; CHECK-NEXT: AND +; CHECK-NEXT: MUL +; CHECK-NEXT: PUSH1 0x10 +; CHECK-NEXT: BYTE +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH1 0x4 +; CHECK-NEXT: SHR +; CHECK-NEXT: ADD +; CHECK-NEXT: AND +; CHECK-NEXT: MUL +; CHECK-NEXT: PUSH1 0x10 +; CHECK-NEXT: BYTE +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.ctpop.i256(i256 %v) + ret i256 %res +} + +define i256 @ctlztest(i256 %v) { +; CHECK-LABEL: ctlztest: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH32 0x101010101010101010101010101010101010101010101010101010101010101 +; CHECK-NEXT: PUSH16 0xF0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F +; CHECK-NEXT: DUP2 +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH32 0x3333333333333333333333333333333333333333333333333333333333333333 +; CHECK-NEXT: DUP6 +; CHECK-NEXT: PUSH16 0x55555555555555555555555555555555 +; CHECK-NEXT: SWAP7 +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: SHR +; CHECK-NEXT: OR +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH1 0x2 +; CHECK-NEXT: SHR +; CHECK-NEXT: OR +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH1 0x4 +; CHECK-NEXT: SHR +; CHECK-NEXT: OR +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH1 0x8 +; CHECK-NEXT: SHR +; CHECK-NEXT: OR +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH1 0x10 +; CHECK-NEXT: SHR +; CHECK-NEXT: OR +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH1 0x20 +; CHECK-NEXT: SHR +; CHECK-NEXT: OR +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH1 0x40 +; CHECK-NEXT: SHR +; CHECK-NEXT: OR +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH1 0x80 +; CHECK-NEXT: SHR +; CHECK-NEXT: OR +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH16 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +; CHECK-NEXT: DUP8 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0x81 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0x80 +; CHECK-NEXT: SHR +; CHECK-NEXT: SUB +; CHECK-NEXT: DUP4 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0x2 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: AND +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP8 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: AND +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0x2 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: AND +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP5 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH1 0x4 +; CHECK-NEXT: SHR +; CHECK-NEXT: ADD +; CHECK-NEXT: AND +; CHECK-NEXT: MUL +; CHECK-NEXT: PUSH1 0x10 +; CHECK-NEXT: BYTE +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH1 0x4 +; CHECK-NEXT: SHR +; CHECK-NEXT: ADD +; CHECK-NEXT: AND +; CHECK-NEXT: MUL +; CHECK-NEXT: PUSH1 0x10 +; CHECK-NEXT: BYTE +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.ctlz.i256(i256 %v, i1 false) + ret i256 %res +} + +define i256 @cttztest(i256 %v) { +; CHECK-LABEL: cttztest: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH32 0x101010101010101010101010101010101010101010101010101010101010101 +; CHECK-NEXT: PUSH16 0xF0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F +; CHECK-NEXT: DUP2 +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH32 0x3333333333333333333333333333333333333333333333333333333333333333 +; CHECK-NEXT: DUP6 +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: PUSH16 0x55555555555555555555555555555555 +; CHECK-NEXT: SWAP8 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: NOT +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH16 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +; CHECK-NEXT: DUP8 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0x81 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0x80 +; CHECK-NEXT: SHR +; CHECK-NEXT: SUB +; CHECK-NEXT: DUP4 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0x2 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: AND +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP8 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: AND +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0x2 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: AND +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP5 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH1 0x4 +; CHECK-NEXT: SHR +; CHECK-NEXT: ADD +; CHECK-NEXT: AND +; CHECK-NEXT: MUL +; CHECK-NEXT: PUSH1 0x10 +; CHECK-NEXT: BYTE +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH1 0x4 +; CHECK-NEXT: SHR +; CHECK-NEXT: ADD +; CHECK-NEXT: AND +; CHECK-NEXT: MUL +; CHECK-NEXT: PUSH1 0x10 +; CHECK-NEXT: BYTE +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.cttz.i256(i256 %v, i1 false) + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/bps-compress-stack-bug.ll b/llvm/test/CodeGen/EVM/bps-compress-stack-bug.ll new file mode 100644 index 000000000000..36194c8312df --- /dev/null +++ b/llvm/test/CodeGen/EVM/bps-compress-stack-bug.ll @@ -0,0 +1,165 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s + +; calldata_struct_array_reencode test reduced with bugpoint. +; before the fix the test caused stack too deep failure because of an incorrect +; depth check in EVMStackSolver::compressStack. +source_filename = "era-solidity/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_array_reencode.sol:C.runtime" +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +; Function Attrs: nounwind willreturn memory(none) +declare i256 @llvm.evm.calldatasize() #0 + +; Function Attrs: nounwind willreturn memory(none) +declare i256 @llvm.evm.callvalue() #0 + +; Function Attrs: noreturn nounwind +declare void @llvm.evm.return(ptr addrspace(1), i256) #1 + +; Function Attrs: noreturn nounwind +declare void @llvm.evm.revert(ptr addrspace(1), i256) #1 + +; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) +declare void @llvm.memmove.p1.p1.i256(ptr addrspace(1) nocapture writeonly, ptr addrspace(1) nocapture readonly, i256, i1 immarg) #2 + +; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) +declare void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(2) noalias nocapture readonly, i256, i1 immarg) #2 + +; Function Attrs: null_pointer_is_valid +define void @__entry() local_unnamed_addr #3 { +entry: + switch i32 poison, label %if_join [ + i32 14668030, label %switch_case_branch_1_block + i32 594809185, label %switch_case_branch_2_block + i32 655224780, label %switch_case_branch_3_block + i32 1501966044, label %switch_case_branch_4_block + i32 1931402874, label %switch_case_branch_5_block + i32 -1789535483, label %switch_case_branch_6_block + i32 -1503295334, label %switch_case_branch_7_block + ] + +if_join: ; preds = %switch_case_branch_6_block, %calldata_access_struct_D_calldata.exit, %for_body335, %for_body296, %entry + unreachable + +switch_case_branch_1_block: ; preds = %entry + unreachable + +switch_case_branch_2_block: ; preds = %entry + unreachable + +switch_case_branch_3_block: ; preds = %entry + %calldata_load_result264 = load i256, ptr addrspace(2) inttoptr (i256 4 to ptr addrspace(2)), align 4 + %addition_result273 = add nuw nsw i256 %calldata_load_result264, 4 + %calldataload_pointer.i853 = inttoptr i256 %addition_result273 to ptr addrspace(2) + %calldata_load_result.i854 = load i256, ptr addrspace(2) %calldataload_pointer.i853, align 1 + %addition_result16.i856 = add nuw nsw i256 %calldata_load_result264, 36 + br i1 poison, label %for_join298, label %for_body296.lr.ph + +for_body296.lr.ph: ; preds = %switch_case_branch_3_block + %reass.sub.i878 = add i256 0, -31 + br label %for_body296 + +for_body296: ; preds = %for_join337, %for_body296.lr.ph + %pos_3.0952 = phi i256 [ 224, %for_body296.lr.ph ], [ %addition_result362, %for_join337 ] + %tail_4.0951 = phi i256 [ poison, %for_body296.lr.ph ], [ %tail_5.0.lcssa, %for_join337 ] + %srcPtr_3.0950 = phi i256 [ %addition_result16.i856, %for_body296.lr.ph ], [ 0, %for_join337 ] + %i_3.0949 = phi i256 [ 0, %for_body296.lr.ph ], [ %addition_result364, %for_join337 ] + %memory_store_pointer309 = inttoptr i256 %pos_3.0952 to ptr addrspace(1) + %calldataload_pointer.i858 = inttoptr i256 %srcPtr_3.0950 to ptr addrspace(2) + %calldata_load_result.i859 = load i256, ptr addrspace(2) %calldataload_pointer.i858, align 1 + %comparison_result.not.i861 = icmp slt i256 %calldata_load_result.i859, poison + br i1 %comparison_result.not.i861, label %if_join.i863, label %if_join + +if_join.i863: ; preds = %for_body296 + %addition_result8.i = add i256 %calldata_load_result.i859, %addition_result16.i856 + %calldataload_pointer10.i = inttoptr i256 %addition_result8.i to ptr addrspace(2) + %calldata_load_result11.i = load i256, ptr addrspace(2) %calldataload_pointer10.i, align 1 + %addition_result13.i = add i256 %addition_result8.i, 32 + br i1 poison, label %for_join337, label %for_body335.lr.ph + +for_body335.lr.ph: ; preds = %if_join.i863 + %addition_result.i871 = add i256 0, -63 + br label %for_body335 + +for_join298: ; preds = %for_join337, %switch_case_branch_3_block + %tail_4.0.lcssa = phi i256 [ poison, %switch_case_branch_3_block ], [ %tail_5.0.lcssa, %for_join337 ] + %addition_result369 = add i256 %tail_4.0.lcssa, -160 + store i256 %addition_result369, ptr addrspace(1) inttoptr (i256 128 to ptr addrspace(1)), align 128 + unreachable + +for_body335: ; preds = %if_join.i.i879, %for_body335.lr.ph + %pos_4.0.in946 = phi i256 [ %tail_4.0951, %for_body335.lr.ph ], [ %pos_4.0, %if_join.i.i879 ] + %tail_5.0945 = phi i256 [ poison, %for_body335.lr.ph ], [ %addition_result19.i, %if_join.i.i879 ] + %srcPtr_4.0944 = phi i256 [ %addition_result13.i, %for_body335.lr.ph ], [ %addition_result353, %if_join.i.i879 ] + %i_4.0943 = phi i256 [ 0, %for_body335.lr.ph ], [ %addition_result357, %if_join.i.i879 ] + %pos_4.0 = add i256 %pos_4.0.in946, 32 + %reass.sub = sub i256 %tail_5.0945, %tail_4.0951 + %addition_result346 = add i256 %reass.sub, -32 + %memory_store_pointer348 = inttoptr i256 %pos_4.0 to ptr addrspace(1) + store i256 %addition_result346, ptr addrspace(1) %memory_store_pointer348, align 1 + %calldataload_pointer.i867 = inttoptr i256 %srcPtr_4.0944 to ptr addrspace(2) + %calldata_load_result.i868 = load i256, ptr addrspace(2) %calldataload_pointer.i867, align 1 + %comparison_result.not.i872 = icmp slt i256 %calldata_load_result.i868, %addition_result.i871 + br i1 %comparison_result.not.i872, label %calldata_access_struct_D_calldata.exit, label %if_join + +calldata_access_struct_D_calldata.exit: ; preds = %for_body335 + %addition_result8.i875 = add i256 %calldata_load_result.i868, %addition_result13.i + %calldataload_pointer.i.i876 = inttoptr i256 %addition_result8.i875 to ptr addrspace(2) + %calldata_load_result.i.i877 = load i256, ptr addrspace(2) %calldataload_pointer.i.i876, align 1 + %addition_result.i.i = sub i256 %reass.sub.i878, %addition_result8.i875 + %comparison_result.not.i.i = icmp slt i256 %calldata_load_result.i.i877, %addition_result.i.i + br i1 %comparison_result.not.i.i, label %if_join.i.i879, label %if_join + +if_join.i.i879: ; preds = %calldata_access_struct_D_calldata.exit + %addition_result8.i.i = add i256 %calldata_load_result.i.i877, %addition_result8.i875 + %calldataload_pointer10.i.i = inttoptr i256 %addition_result8.i.i to ptr addrspace(2) + %calldata_load_result11.i.i = load i256, ptr addrspace(2) %calldataload_pointer10.i.i, align 1 + %shift_left_non_overflow_result.i.i880 = shl nuw nsw i256 %calldata_load_result11.i.i, 5 + %addition_result15.i = add i256 %tail_5.0945, 64 + %addition_result19.i = add i256 %shift_left_non_overflow_result.i.i880, %addition_result15.i + %addition_result353 = add i256 %srcPtr_4.0944, 32 + %addition_result357 = add nuw nsw i256 %i_4.0943, 1 + %comparison_result340 = icmp ult i256 %addition_result357, %calldata_load_result11.i + br i1 %comparison_result340, label %for_body335, label %for_join337 + +for_join337: ; preds = %if_join.i.i879, %if_join.i863 + %tail_5.0.lcssa = phi i256 [ poison, %if_join.i863 ], [ %addition_result19.i, %if_join.i.i879 ] + %addition_result362 = add i256 %pos_3.0952, 32 + %addition_result364 = add nuw nsw i256 %i_3.0949, 1 + %comparison_result301 = icmp ult i256 %addition_result364, %calldata_load_result.i854 + br i1 %comparison_result301, label %for_body296, label %for_join298 + +switch_case_branch_4_block: ; preds = %entry + unreachable + +switch_case_branch_5_block: ; preds = %entry + unreachable + +switch_case_branch_6_block: ; preds = %entry + br label %if_join + +switch_case_branch_7_block: ; preds = %entry + unreachable +} + +; Function Attrs: null_pointer_is_valid +declare dso_local fastcc void @finalize_allocation() unnamed_addr #3 + +; Function Attrs: null_pointer_is_valid +declare dso_local fastcc void @abi_decode_array_struct_S_dyn() unnamed_addr #3 + +; Function Attrs: memory(readwrite, inaccessiblemem: none) +declare dso_local fastcc void @abi_encode_bytes() unnamed_addr #4 + +; Function Attrs: null_pointer_is_valid +declare dso_local fastcc void @abi_encode_struct_D_calldata() unnamed_addr #3 + +; Function Attrs: null_pointer_is_valid +declare dso_local fastcc void @abi_encode_array_struct_D_calldata_calldata() unnamed_addr #3 + +attributes #0 = { nounwind willreturn memory(none) } +attributes #1 = { noreturn nounwind } +attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } +attributes #3 = { null_pointer_is_valid } +attributes #4 = { memory(readwrite, inaccessiblemem: none) } diff --git a/llvm/test/CodeGen/EVM/bps-imm-propagation-optsize.mir b/llvm/test/CodeGen/EVM/bps-imm-propagation-optsize.mir new file mode 100644 index 000000000000..daf8a6d4c10f --- /dev/null +++ b/llvm/test/CodeGen/EVM/bps-imm-propagation-optsize.mir @@ -0,0 +1,107 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5 +# RUN: llc -x mir -run-pass=evm-backward-propagation-stackification < %s | FileCheck %s + +--- | + source_filename = "test.ll" + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm" + + define i256 @foo(i256 %arg1, i256 %arg2) #0 { + %srem = srem i256 1000000000000000000000000000000000000000000000000000000000000000000000000001, %arg1 + %sdiv = sdiv i256 1000000000000000000000000000000000000000000000000000000000000000000000000001, %arg2 + %add = add nsw i256 %sdiv, %srem + ret i256 %add + } + + attributes #0 = { minsize } + +... +--- +name: foo +alignment: 1 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gpr, preferred-register: '' } + - { id: 1, class: gpr, preferred-register: '' } + - { id: 2, class: gpr, preferred-register: '' } + - { id: 3, class: gpr, preferred-register: '' } + - { id: 4, class: gpr, preferred-register: '' } + - { id: 5, class: gpr, preferred-register: '' } + - { id: 6, class: gpr, preferred-register: '' } +liveins: + - { reg: '$arguments', virtual-reg: '' } + - { reg: '$value_stack', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + isStackified: false + numberOfParameters: 2 + hasPushDeployAddress: false +body: | + bb.0 (%ir-block.0): + liveins: $arguments, $value_stack + + ; CHECK-LABEL: name: foo + ; CHECK: liveins: $arguments, $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: PUSH32_S i256 1000000000000000000000000000000000000000000000000000000000000000000000000001 + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: DUP3_S + ; CHECK-NEXT: SMOD_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: SDIV_S + ; CHECK-NEXT: ADD_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: PseudoRET + %0:gpr = ARGUMENT 0, implicit $arguments + %3:gpr = ARGUMENT 1, implicit $arguments + %1:gpr = CONST_I256 i256 1000000000000000000000000000000000000000000000000000000000000000000000000001, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %2:gpr = SMOD %1, %0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %6:gpr = CONST_I256 i256 1000000000000000000000000000000000000000000000000000000000000000000000000001, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %4:gpr = SDIV %6, %3, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %5:gpr = nsw ADD %4, %2, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + RET %5, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + +... diff --git a/llvm/test/CodeGen/EVM/bps-inifinite-loop-bug.ll b/llvm/test/CodeGen/EVM/bps-inifinite-loop-bug.ll new file mode 100644 index 000000000000..3af71adca821 --- /dev/null +++ b/llvm/test/CodeGen/EVM/bps-inifinite-loop-bug.ll @@ -0,0 +1,38 @@ +; RUN: llc -O3 < %s + +; This test case is reduced with llvm-reduce. +; Before the fix, we had an infinite loop in EVMStackSolver::runPropagation, +; because EarlyTailDuplicate merged header and latch blocks. This caused +; MachineLoopInfo not to detect the loop correctly, thus running into an +; infinite loop. + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +; Function Attrs: nounwind willreturn memory(none) +declare i256 @llvm.evm.signextend(i256, i256) #0 + +define void @test(i1 %comparison_result6, i256 %mulmod1446) { +entry: + br i1 %comparison_result6, label %"block_rt_2/0", label %shift_right_non_overflow + +"block_rt_2/0": ; preds = %entry + unreachable + +"block_rt_66/0": ; preds = %division_join1531, %shift_right_non_overflow + br i1 %division_is_divider_zero1455, label %division_join1453, label %division_join1531 + +shift_right_non_overflow: ; preds = %entry + %division_is_divider_zero1455 = icmp eq i256 %mulmod1446, 0 + br label %"block_rt_66/0" + +division_join1453: ; preds = %"block_rt_66/0" + %signextend1463 = tail call i256 @llvm.evm.signextend(i256 0, i256 0) + br label %division_join1531 + +division_join1531: ; preds = %division_join1453, %"block_rt_66/0" + store i256 0, ptr addrspace(1) null, align 4294967296 + br label %"block_rt_66/0" +} + +attributes #0 = { nounwind willreturn memory(none) } diff --git a/llvm/test/CodeGen/EVM/bps-recursive-func-compress-stack.mir b/llvm/test/CodeGen/EVM/bps-recursive-func-compress-stack.mir new file mode 100644 index 000000000000..78468a60ea28 --- /dev/null +++ b/llvm/test/CodeGen/EVM/bps-recursive-func-compress-stack.mir @@ -0,0 +1,232 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5 +# RUN: llc -x mir -run-pass=evm-backward-propagation-stackification < %s | FileCheck %s + +# This test case is reduced with llvm-reduce. +# Before the change, the test caused stack too deep failure during +# stackification. Since we can't use spills for recursive functions, +# only option we have is to agressively compress the stack across the +# whole function, to resolve stack too deep issues. + +--- | + source_filename = "reduced.ll" + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm-unknown-unknown" + + define fastcc void @_testRemoveAndInsertBack_rt_707(i256 %0, i256 %1, i256 %storage_load_value12845, ptr addrspace(1) %2, i256 %storage_load_value18549, i256 %shift_right_non_overflow_result6002, i256 %and_result11667, ptr addrspace(5) %storage_load_position_pointer14560, ptr addrspace(5) %storage_load_position_pointer19042, i256 %storage_load_value19083, ptr addrspace(5) %storage_load_position_pointer14550, ptr addrspace(5) %storage_load_position_pointer18694, ptr addrspace(1) %3, i256 %memory_load_result22114.pre) #0 { + entry: + store i256 0, ptr addrspace(1) %2, align 16 + store i256 0, ptr addrspace(5) null, align 1 + store i256 %1, ptr addrspace(5) %storage_load_position_pointer14560, align 1 + store i256 %0, ptr addrspace(5) null, align 1 + store i256 0, ptr addrspace(5) %storage_load_position_pointer19042, align 1 + store i256 0, ptr addrspace(5) %storage_load_position_pointer14550, align 1 + store i256 0, ptr addrspace(5) %storage_load_position_pointer18694, align 1 + store i256 0, ptr addrspace(5) %storage_load_position_pointer14560, align 1 + store i256 0, ptr addrspace(1) %3, align 32 + store i256 %storage_load_value12845, ptr addrspace(5) null, align 4294967296 + call fastcc void @_testRemoveAndInsertBack_rt_707(i256 0, i256 0, i256 0, ptr addrspace(1) null, i256 0, i256 0, i256 0, ptr addrspace(5) null, ptr addrspace(5) null, i256 0, ptr addrspace(5) null, ptr addrspace(5) null, ptr addrspace(1) null, i256 0) + ret void + } + + attributes #0 = { "evm-recursive" } + +... +--- +name: _testRemoveAndInsertBack_rt_707 +alignment: 1 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gpr, preferred-register: '' } + - { id: 1, class: gpr, preferred-register: '' } + - { id: 2, class: gpr, preferred-register: '' } + - { id: 3, class: gpr, preferred-register: '' } + - { id: 4, class: gpr, preferred-register: '' } + - { id: 5, class: gpr, preferred-register: '' } + - { id: 6, class: gpr, preferred-register: '' } + - { id: 7, class: gpr, preferred-register: '' } + - { id: 8, class: gpr, preferred-register: '' } + - { id: 9, class: gpr, preferred-register: '' } + - { id: 10, class: gpr, preferred-register: '' } + - { id: 11, class: gpr, preferred-register: '' } + - { id: 12, class: gpr, preferred-register: '' } + - { id: 13, class: gpr, preferred-register: '' } + - { id: 14, class: gpr, preferred-register: '' } + - { id: 15, class: gpr, preferred-register: '' } + - { id: 16, class: gpr, preferred-register: '' } + - { id: 17, class: gpr, preferred-register: '' } + - { id: 18, class: gpr, preferred-register: '' } + - { id: 19, class: gpr, preferred-register: '' } + - { id: 20, class: gpr, preferred-register: '' } + - { id: 21, class: gpr, preferred-register: '' } + - { id: 22, class: gpr, preferred-register: '' } + - { id: 23, class: gpr, preferred-register: '' } + - { id: 24, class: gpr, preferred-register: '' } + - { id: 25, class: gpr, preferred-register: '' } + - { id: 26, class: gpr, preferred-register: '' } + - { id: 27, class: gpr, preferred-register: '' } + - { id: 28, class: gpr, preferred-register: '' } + - { id: 29, class: gpr, preferred-register: '' } + - { id: 30, class: gpr, preferred-register: '' } + - { id: 31, class: gpr, preferred-register: '' } + - { id: 32, class: gpr, preferred-register: '' } +liveins: + - { reg: '$arguments', virtual-reg: '' } + - { reg: '$value_stack', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: true + stackProtector: '' + functionContext: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + isStackified: false + numberOfParameters: 14 + hasPushDeployAddress: false +body: | + bb.0.entry: + liveins: $arguments, $value_stack + + ; CHECK-LABEL: name: _testRemoveAndInsertBack_rt_707 + ; CHECK: liveins: $arguments, $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: SWAP6_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP11_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP7_S + ; CHECK-NEXT: SWAP5_S + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: SWAP8_S + ; CHECK-NEXT: SWAP6_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SSTORE_S + ; CHECK-NEXT: DUP7_S + ; CHECK-NEXT: SSTORE_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SSTORE_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: SSTORE_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: SSTORE_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: SSTORE_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: SSTORE_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SSTORE_S + ; CHECK-NEXT: PUSH_LABEL + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PseudoCALL @_testRemoveAndInsertBack_rt_707, + ; CHECK-NEXT: PseudoRET + %3:gpr = ARGUMENT 0, implicit $arguments + %1:gpr = ARGUMENT 1, implicit $arguments + %9:gpr = ARGUMENT 2, implicit $arguments + %7:gpr = ARGUMENT 3, implicit $arguments + %2:gpr = ARGUMENT 7, implicit $arguments + %4:gpr = ARGUMENT 8, implicit $arguments + %5:gpr = ARGUMENT 10, implicit $arguments + %6:gpr = ARGUMENT 11, implicit $arguments + %8:gpr = ARGUMENT 12, implicit $arguments + %0:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %32:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + SSTORE %32, %0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into `ptr addrspace(5) null`, align 1, addrspace 5) + SSTORE %2, %1, implicit-def dead $arguments :: (store (s256) into %ir.storage_load_position_pointer14560, align 1, addrspace 5) + %31:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + SSTORE %31, %3, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into `ptr addrspace(5) null`, align 1, addrspace 5) + %30:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + SSTORE %4, %30, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into %ir.storage_load_position_pointer19042, align 1, addrspace 5) + %29:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + SSTORE %5, %29, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into %ir.storage_load_position_pointer14550, align 1, addrspace 5) + %28:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + SSTORE %6, %28, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into %ir.storage_load_position_pointer18694, align 1, addrspace 5) + %27:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + MSTORE %7, %27, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into %ir.2, align 16, addrspace 1) + %26:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + SSTORE %2, %26, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into %ir.storage_load_position_pointer14560, align 1, addrspace 5) + %25:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + MSTORE %8, %25, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into %ir.3, addrspace 1) + %24:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + SSTORE %24, %9, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into `ptr addrspace(5) null`, align 4294967296, addrspace 5) + %23:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %22:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %21:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %20:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %19:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %18:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %17:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %16:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %15:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %14:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %13:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %12:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %11:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %10:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + FCALL @_testRemoveAndInsertBack_rt_707, %10, %11, %12, %13, %14, %15, %16, %17, %18, %19, %20, %21, %22, %23, implicit-def dead $arguments, implicit $sp, implicit-def $value_stack, implicit $value_stack + RET implicit-def dead $arguments + +... diff --git a/llvm/test/CodeGen/EVM/bps-recursive-func-fail.mir b/llvm/test/CodeGen/EVM/bps-recursive-func-fail.mir new file mode 100644 index 000000000000..d7f68954498b --- /dev/null +++ b/llvm/test/CodeGen/EVM/bps-recursive-func-fail.mir @@ -0,0 +1,284 @@ +# RUN: not --crash llc -x mir -run-pass=evm-backward-propagation-stackification < %s 2>&1 | FileCheck %s + +# This is the same test as bps-spills-2.mir, just with an "evm-recursive" attribute. +# Since for recursive functions we can't use spills and reloads, purpose of this is +# to test that we issue an error if there are unreachable slots in a recursive +# function and compression didn't help. + +# CHECK: LLVM ERROR: Stackification failed for 'main' function. It is recursive and has stack too deep errors. Consider refactoring it to use a non-recursive approach. + +--- | + source_filename = "reduced.ll" + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm-unknown-unknown" + + declare void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(2) noalias nocapture readonly, i256, i1 immarg) #0 + + define fastcc void @main(i256 %calldata_load_result2781, i256 %addition_result2798, i256 %shift_left_non_overflow_result3390, i256 %calldata_load_result1899, i256 %calldata_load_result3245, i1 %comparison_result3399.not, i256 %stack_var_012.36954, i256 %calldata_load_result2605, i1 %comparison_result4030.not) "evm-recursive" { + entry: + br label %conditional_rt_187_join_block + + "block_rt_2/0": ; preds = %conditional_rt_187_join_block, %conditional_rt_181_join_block, %"block_rt_165/1" + unreachable + + "block_rt_160/3": ; preds = %"block_rt_165/1" + %calldatacopy_destination_pointer3688 = inttoptr i256 %stack_var_021.0.in6947 to ptr addrspace(1) + %calldatacopy_source_pointer3689 = inttoptr i256 %addition_result3756 to ptr addrspace(2) + tail call void @llvm.evm.memcpyas1as2(ptr addrspace(1) %calldatacopy_destination_pointer3688, ptr addrspace(2) %calldatacopy_source_pointer3689, i256 %shift_left_non_overflow_result3390, i1 false) + br i1 %comparison_result3909.not, label %conditional_rt_181_join_block, label %"block_rt_181/0" + + "block_rt_165/1": ; preds = %conditional_rt_181_join_block + %addition_result3756 = or i256 %calldata_load_result1899, %addition_result4054 + br i1 %comparison_result3399.not, label %"block_rt_2/0", label %"block_rt_160/3" + + "block_rt_181/0": ; preds = %"block_rt_160/3" + %addition_result4064 = or i256 %stack_var_011.36953, 1 + %comparison_result4003.not = icmp ult i256 %stack_var_013.36955, %calldata_load_result2605 + br i1 %comparison_result4003.not, label %conditional_rt_187_join_block, label %"block_rt_187/0.loopexit" + + "block_rt_187/0.loopexit": ; preds = %"block_rt_181/0" + store i256 %stack_var_021.06950, ptr addrspace(1) null, align 64 + unreachable + + "block_rt_188/0": ; preds = %conditional_rt_187_join_block + %addition_result4054 = or i256 %stack_var_012.36954, 1 + %addition_result1909 = or i256 %calldata_load_result3245, 1 + br label %conditional_rt_181_join_block + + conditional_rt_181_join_block: ; preds = %"block_rt_188/0", %"block_rt_160/3" + %stack_var_021.06950 = phi i256 [ 0, %"block_rt_188/0" ], [ %addition_result2798, %"block_rt_160/3" ] + %comparison_result3909.not = phi i1 [ true, %"block_rt_188/0" ], [ false, %"block_rt_160/3" ] + %stack_var_021.0.in6947 = phi i256 [ 0, %"block_rt_188/0" ], [ %addition_result2798, %"block_rt_160/3" ] + store i256 %calldata_load_result2781, ptr addrspace(1) null, align 1 + %comparison_result1913.not = icmp slt i256 0, %addition_result1909 + br i1 %comparison_result1913.not, label %"block_rt_165/1", label %"block_rt_2/0" + + conditional_rt_187_join_block: ; preds = %"block_rt_181/0", %entry + %stack_var_013.36955 = phi i256 [ 0, %entry ], [ 1, %"block_rt_181/0" ] + %stack_var_011.36953 = phi i256 [ 0, %entry ], [ %addition_result4064, %"block_rt_181/0" ] + br i1 %comparison_result4030.not, label %"block_rt_188/0", label %"block_rt_2/0" + } + + declare void @llvm.evm.memcpyas1as2(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(2) noalias nocapture readonly, i256, i1 immarg) #0 + + attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } + +... +--- +name: main +alignment: 1 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gpr, preferred-register: '' } + - { id: 1, class: gpr, preferred-register: '' } + - { id: 2, class: gpr, preferred-register: '' } + - { id: 3, class: gpr, preferred-register: '' } + - { id: 4, class: gpr, preferred-register: '' } + - { id: 5, class: gpr, preferred-register: '' } + - { id: 6, class: gpr, preferred-register: '' } + - { id: 7, class: gpr, preferred-register: '' } + - { id: 8, class: gpr, preferred-register: '' } + - { id: 9, class: gpr, preferred-register: '' } + - { id: 10, class: gpr, preferred-register: '' } + - { id: 11, class: gpr, preferred-register: '' } + - { id: 12, class: gpr, preferred-register: '' } + - { id: 13, class: gpr, preferred-register: '' } + - { id: 14, class: gpr, preferred-register: '' } + - { id: 15, class: gpr, preferred-register: '' } + - { id: 16, class: gpr, preferred-register: '' } + - { id: 17, class: gpr, preferred-register: '' } + - { id: 18, class: gpr, preferred-register: '' } + - { id: 19, class: gpr, preferred-register: '' } + - { id: 20, class: gpr, preferred-register: '' } + - { id: 21, class: gpr, preferred-register: '' } + - { id: 22, class: gpr, preferred-register: '' } + - { id: 23, class: gpr, preferred-register: '' } + - { id: 24, class: gpr, preferred-register: '' } + - { id: 25, class: gpr, preferred-register: '' } + - { id: 26, class: gpr, preferred-register: '' } + - { id: 27, class: gpr, preferred-register: '' } + - { id: 28, class: gpr, preferred-register: '' } + - { id: 29, class: gpr, preferred-register: '' } + - { id: 30, class: gpr, preferred-register: '' } + - { id: 31, class: gpr, preferred-register: '' } + - { id: 32, class: gpr, preferred-register: '' } + - { id: 33, class: gpr, preferred-register: '' } + - { id: 34, class: gpr, preferred-register: '' } + - { id: 35, class: gpr, preferred-register: '' } + - { id: 36, class: gpr, preferred-register: '' } + - { id: 37, class: gpr, preferred-register: '' } + - { id: 38, class: gpr, preferred-register: '' } + - { id: 39, class: gpr, preferred-register: '' } + - { id: 40, class: gpr, preferred-register: '' } + - { id: 41, class: gpr, preferred-register: '' } + - { id: 42, class: gpr, preferred-register: '' } + - { id: 43, class: gpr, preferred-register: '' } + - { id: 44, class: gpr, preferred-register: '' } + - { id: 45, class: gpr, preferred-register: '' } +liveins: + - { reg: '$arguments', virtual-reg: '' } + - { reg: '$value_stack', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + isStackified: false + numberOfParameters: 9 + hasPushDeployAddress: false +body: | + bb.0.entry: + successors: %bb.1(0x80000000) + liveins: $arguments, $value_stack + + %9:gpr = ARGUMENT 0, implicit $arguments + %10:gpr = ARGUMENT 1, implicit $arguments + %11:gpr = ARGUMENT 2, implicit $arguments + %12:gpr = ARGUMENT 3, implicit $arguments + %13:gpr = ARGUMENT 4, implicit $arguments + %14:gpr = ARGUMENT 5, implicit $arguments + %15:gpr = ARGUMENT 6, implicit $arguments + %16:gpr = ARGUMENT 7, implicit $arguments + %17:gpr = ARGUMENT 8, implicit $arguments + %39:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %20:gpr = AND %39, %17, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %38:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %26:gpr = AND %38, %14, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %7:gpr = CONST_I256 i256 0, implicit-def dead $arguments + %8:gpr = CONST_I256 i256 0, implicit-def dead $arguments + + bb.1.conditional_rt_187_join_block: + successors: %bb.7(0x80000000), %bb.10(0x00000000) + liveins: $value_stack + + JUMPI %bb.7, %20, implicit-def $arguments + + bb.10: + successors: %bb.2(0x80000000) + liveins: $value_stack + + bb.2: + successors: + liveins: $value_stack + + bb.3: + successors: %bb.12(0x00000000), %bb.4(0x80000000) + liveins: $value_stack + + JUMP_UNLESS %bb.4, %26, implicit-def $arguments + + bb.12: + successors: %bb.2(0x80000000) + liveins: $value_stack + + JUMP %bb.2, implicit-def $arguments + + bb.4: + successors: %bb.13(0x7c000000), %bb.5(0x04000000) + liveins: $value_stack + + CALLDATACOPY %6, %0, %11, implicit-def dead $arguments + %40:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %29:gpr = AND %5, %40, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %5:gpr = CONST_I256 i256 0, implicit-def dead $arguments + %6:gpr = COPY_I256 %10, implicit-def $arguments + JUMP_UNLESS %bb.5, %29, implicit-def $arguments + + bb.13: + successors: %bb.8(0x80000000) + liveins: $value_stack + + JUMP %bb.8, implicit-def $arguments + + bb.5: + successors: %bb.9(0x80000000), %bb.6(0x00000000) + liveins: $value_stack + + %31:gpr = ULT %7, %16, implicit-def dead $arguments + %7:gpr = CONST_I256 i256 1, implicit-def dead $arguments + %41:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %8:gpr = OR %8, %41, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP_UNLESS %bb.6, %31, implicit-def $arguments + + bb.9: + successors: %bb.1(0x80000000) + liveins: $value_stack + + JUMP %bb.1, implicit-def $arguments + + bb.6: + successors: + liveins: $value_stack + + %32:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + MSTORE %32, %4, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into `ptr addrspace(1) null`, align 64, addrspace 1) + + bb.7: + successors: %bb.8(0x80000000) + liveins: $value_stack + + %5:gpr = CONST_I256 i256 1, implicit-def dead $arguments + %6:gpr = CONST_I256 i256 0, implicit-def dead $arguments + %44:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %43:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %3:gpr = OR %13, %43, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %24:gpr = GT %3, %44, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %42:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %2:gpr = OR %15, %42, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %0:gpr = OR %12, %2, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + + bb.8.conditional_rt_181_join_block: + successors: %bb.3(0x80000000), %bb.11(0x00000000) + liveins: $value_stack + + %4:gpr = COPY_I256 %6, implicit-def $arguments + %45:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + MSTORE %45, %9, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into `ptr addrspace(1) null`, align 1, addrspace 1) + JUMPI %bb.3, %24, implicit-def $arguments + + bb.11: + successors: %bb.2(0x80000000) + liveins: $value_stack + + JUMP %bb.2, implicit-def $arguments + +... diff --git a/llvm/test/CodeGen/EVM/bps-spill-propagation-1.mir b/llvm/test/CodeGen/EVM/bps-spill-propagation-1.mir new file mode 100644 index 000000000000..066bccfb1815 --- /dev/null +++ b/llvm/test/CodeGen/EVM/bps-spill-propagation-1.mir @@ -0,0 +1,177 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5 +# REQUIRES: asserts +# RUN: llc -x mir -run-pass=evm-backward-propagation-stackification -evm-force-reg-spills=2 < %s | FileCheck %s + +# In bb.0 we have def and use of %2:gpr and two uses in bb.2. +# When spilling %2:gpr, test that in bb.0 we are not doing reload +# immediately after spill, and we are doing only one reload in bb.2, +# even there are two uses of %2:gpr in bb.2. + +--- | + source_filename = "test_rename.ll" + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm" + + define i256 @test(i256 %arg) { + bb: + %icmp = icmp sgt i256 %arg, 0 + br i1 %icmp, label %bb1, label %bb4 + + bb1: ; preds = %bb + %xor = xor i256 %arg, 10 + %or = or i256 %arg, 1234 + %add = add nuw nsw i256 %or, 5 + %add2 = add nuw nsw i256 %add, %xor + br label %bb4 + + bb4: ; preds = %bb, %bb1 + %phi = phi i256 [ %add2, %bb1 ], [ 10, %bb ] + ret i256 %phi + } + +... +--- +name: test +alignment: 1 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gpr, preferred-register: '' } + - { id: 1, class: gpr, preferred-register: '' } + - { id: 2, class: gpr, preferred-register: '' } + - { id: 3, class: gpr, preferred-register: '' } + - { id: 4, class: gpr, preferred-register: '' } + - { id: 5, class: gpr, preferred-register: '' } + - { id: 6, class: gpr, preferred-register: '' } + - { id: 7, class: gpr, preferred-register: '' } + - { id: 8, class: gpr, preferred-register: '' } + - { id: 9, class: gpr, preferred-register: '' } + - { id: 10, class: gpr, preferred-register: '' } + - { id: 11, class: gpr, preferred-register: '' } + - { id: 12, class: gpr, preferred-register: '' } +liveins: + - { reg: '$arguments', virtual-reg: '' } + - { reg: '$value_stack', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + isStackified: false + numberOfParameters: 1 + hasPushDeployAddress: false +body: | + ; CHECK-LABEL: name: test + ; CHECK: bb.0.bb: + ; CHECK-NEXT: successors: %bb.2(0x50000000), %bb.1(0x30000000) + ; CHECK-NEXT: liveins: $arguments, $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: LT_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.2 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.1: + ; CHECK-NEXT: successors: %bb.3(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH1_S i256 10 + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: PseudoJUMP %bb.3 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.2.bb1: + ; CHECK-NEXT: successors: %bb.3(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH1_S i256 5 + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MLOAD_S + ; CHECK-NEXT: PUSH2_S i256 1234 + ; CHECK-NEXT: PUSH1_S i256 10 + ; CHECK-NEXT: DUP3_S + ; CHECK-NEXT: XOR_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: OR_S + ; CHECK-NEXT: ADD_S + ; CHECK-NEXT: ADD_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.3.bb4: + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PseudoRET + bb.0.bb: + successors: %bb.2(0x50000000), %bb.1(0x30000000) + liveins: $arguments, $value_stack + + %2:gpr = ARGUMENT 0, implicit $arguments + %4:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %5:gpr = LT %2, %4, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP_UNLESS %bb.2, %5, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + + bb.1: + successors: %bb.3(0x80000000) + liveins: $value_stack + + %12:gpr = CONST_I256 i256 10, implicit-def dead $arguments + JUMP %bb.3, implicit-def $arguments + + bb.2.bb1: + successors: %bb.3(0x80000000) + liveins: $value_stack + + %11:gpr = CONST_I256 i256 5, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %6:gpr = CONST_I256 i256 10, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %7:gpr = XOR %2, %6, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %8:gpr = CONST_I256 i256 1234, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %9:gpr = OR %2, %8, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %10:gpr = nuw ADD %9, %7, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %12:gpr = nuw ADD %10, %11, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + + bb.3.bb4: + liveins: $value_stack + + RET %12, implicit-def dead $arguments + +... diff --git a/llvm/test/CodeGen/EVM/bps-spill-propagation-2.mir b/llvm/test/CodeGen/EVM/bps-spill-propagation-2.mir new file mode 100644 index 000000000000..700bd9166223 --- /dev/null +++ b/llvm/test/CodeGen/EVM/bps-spill-propagation-2.mir @@ -0,0 +1,164 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5 +# REQUIRES: asserts +# RUN: llc -x mir -run-pass=evm-backward-propagation-stackification -evm-force-reg-spills=9 < %s | FileCheck %s + +# Test that when spilling %9:gpr, we are not doing DUP1 before and +# POP after spill, since %9:gpr is not used anymore in these MBBs. + +--- | + source_filename = "test_rename.ll" + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm" + + define dso_local i256 @test(i256 noundef %arg) local_unnamed_addr { + bb: + %icmp = icmp sgt i256 %arg, 0 + %add = add nuw nsw i256 %arg, 10 + br i1 %icmp, label %bb2, label %bb1 + + bb1: ; preds = %bb + br label %bb2 + + bb2: ; preds = %bb1, %bb + %phi = phi i256 [ 10, %bb1 ], [ %add, %bb ] + %ret = add nuw nsw i256 %phi, 5 + ret i256 %ret + } + +... +--- +name: test +alignment: 1 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gpr, preferred-register: '' } + - { id: 1, class: gpr, preferred-register: '' } + - { id: 2, class: gpr, preferred-register: '' } + - { id: 3, class: gpr, preferred-register: '' } + - { id: 4, class: gpr, preferred-register: '' } + - { id: 5, class: gpr, preferred-register: '' } + - { id: 6, class: gpr, preferred-register: '' } + - { id: 7, class: gpr, preferred-register: '' } + - { id: 8, class: gpr, preferred-register: '' } + - { id: 9, class: gpr, preferred-register: '' } + - { id: 10, class: gpr, preferred-register: '' } +liveins: + - { reg: '$arguments', virtual-reg: '' } + - { reg: '$value_stack', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + isStackified: false + numberOfParameters: 1 + hasPushDeployAddress: false +body: | + ; CHECK-LABEL: name: test + ; CHECK: bb.0.bb: + ; CHECK-NEXT: successors: %bb.2(0x50000000), %bb.1(0x30000000) + ; CHECK-NEXT: liveins: $arguments, $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH1_S i256 10 + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: DUP2_S + ; CHECK-NEXT: GT_S + ; CHECK-NEXT: PseudoJUMPI %bb.2 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.1: + ; CHECK-NEXT: successors: %bb.3(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.3 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.2: + ; CHECK-NEXT: successors: %bb.3(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH1_S i256 10 + ; CHECK-NEXT: ADD_S + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.3.bb2: + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH1_S i256 5 + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MLOAD_S + ; CHECK-NEXT: ADD_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: PseudoRET + bb.0.bb: + successors: %bb.1(0x50000000), %bb.3(0x30000000) + liveins: $arguments, $value_stack + + %3:gpr = ARGUMENT 0, implicit $arguments + %9:gpr = CONST_I256 i256 10, implicit-def dead $arguments + %4:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %5:gpr = GT %3, %4, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMPI %bb.1, %5, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + + bb.3: + successors: %bb.2(0x80000000) + liveins: $value_stack + + JUMP %bb.2, implicit-def $arguments + + bb.1: + successors: %bb.2(0x80000000) + liveins: $value_stack + + %10:gpr = CONST_I256 i256 10, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %9:gpr = nuw nsw ADD %3, %10, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + + bb.2.bb2: + liveins: $value_stack + + %7:gpr = CONST_I256 i256 5, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %8:gpr = nuw nsw ADD %9, %7, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + RET %8, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + +... diff --git a/llvm/test/CodeGen/EVM/bps-spill-propagation-3.mir b/llvm/test/CodeGen/EVM/bps-spill-propagation-3.mir new file mode 100644 index 000000000000..d2fa395aa532 --- /dev/null +++ b/llvm/test/CodeGen/EVM/bps-spill-propagation-3.mir @@ -0,0 +1,262 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5 +# REQUIRES: asserts +# RUN: llc -x mir -run-pass=evm-backward-propagation-stackification -evm-force-reg-spills=0 < %s | FileCheck %s + +# Test how we are propagating conditional register reloads. + +--- | + source_filename = "test.ll" + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm" + + define i256 @test(i256 %arg1, i256 %arg2, i256 %arg3) { + entry: + %cmp1 = icmp sgt i256 %arg1, 0 + %cmp2 = icmp sgt i256 %arg3, 0 + %zext = zext i1 %cmp2 to i256 + %sub = sub nuw nsw i256 %arg2, %zext + br i1 %cmp1, label %bb1, label %bb2 + + bb1: ; preds = %entry + %cmp3 = icmp sgt i256 %arg2, 0 + br i1 %cmp3, label %bb2, label %bb3 + + bb2: ; preds = %bb1, %entry + br i1 %cmp2, label %bb3, label %bb4 + + bb3: ; preds = %bb2, %bb1 + %add1 = add nuw nsw i256 %arg1, 10 + br label %bb4 + + bb4: ; preds = %bb3, %bb2 + %phi1 = phi i256 [ %add1, %bb3 ], [ %sub, %bb2 ] + ret i256 %phi1 + } + +... +--- +name: test +alignment: 1 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gpr, preferred-register: '' } + - { id: 1, class: gpr, preferred-register: '' } + - { id: 2, class: gpr, preferred-register: '' } + - { id: 3, class: gpr, preferred-register: '' } + - { id: 4, class: gpr, preferred-register: '' } + - { id: 5, class: gpr, preferred-register: '' } + - { id: 6, class: gpr, preferred-register: '' } + - { id: 7, class: gpr, preferred-register: '' } + - { id: 8, class: gpr, preferred-register: '' } + - { id: 9, class: gpr, preferred-register: '' } + - { id: 10, class: gpr, preferred-register: '' } + - { id: 11, class: gpr, preferred-register: '' } + - { id: 12, class: gpr, preferred-register: '' } + - { id: 13, class: gpr, preferred-register: '' } + - { id: 14, class: gpr, preferred-register: '' } + - { id: 15, class: gpr, preferred-register: '' } +liveins: + - { reg: '$arguments', virtual-reg: '' } + - { reg: '$value_stack', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + isStackified: false + numberOfParameters: 3 + hasPushDeployAddress: false +body: | + ; CHECK-LABEL: name: test + ; CHECK: bb.0.entry: + ; CHECK-NEXT: successors: %bb.2(0x50000000), %bb.1(0x30000000) + ; CHECK-NEXT: liveins: $arguments, $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: GT_S + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: DUP4_S + ; CHECK-NEXT: SUB_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP3_S + ; CHECK-NEXT: LT_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.2 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.1: + ; CHECK-NEXT: successors: %bb.5(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.5 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.2.bb1: + ; CHECK-NEXT: successors: %bb.4(0x50000000), %bb.3(0x30000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: LT_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.4 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.3: + ; CHECK-NEXT: successors: %bb.8(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH1_S i256 10 + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.8 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.4: + ; CHECK-NEXT: successors: %bb.5(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.5.bb2: + ; CHECK-NEXT: successors: %bb.7(0x50000000), %bb.6(0x30000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MLOAD_S + ; CHECK-NEXT: PseudoJUMPI %bb.7 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.6: + ; CHECK-NEXT: successors: %bb.9(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.9 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.7: + ; CHECK-NEXT: successors: %bb.8(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH1_S i256 10 + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.8.bb3: + ; CHECK-NEXT: successors: %bb.9(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: ADD_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.9.bb4: + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PseudoRET + bb.0.entry: + successors: %bb.1(0x50000000), %bb.5(0x30000000) + liveins: $arguments, $value_stack + + %4:gpr = ARGUMENT 0, implicit $arguments + %5:gpr = ARGUMENT 1, implicit $arguments + %7:gpr = ARGUMENT 2, implicit $arguments + %6:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %0:gpr = GT %7, %6, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %13:gpr = nuw nsw SUB %5, %0, implicit-def dead $arguments + %14:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %9:gpr = LT %4, %14, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP_UNLESS %bb.1, %9, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + + bb.5: + successors: %bb.2(0x80000000) + liveins: $value_stack + + JUMP %bb.2, implicit-def $arguments + + bb.1.bb1: + successors: %bb.6(0x50000000), %bb.7(0x30000000) + liveins: $value_stack + + %15:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %11:gpr = LT %5, %15, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP_UNLESS %bb.6, %11, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + + bb.7: + successors: %bb.3(0x80000000) + liveins: $value_stack + + JUMP %bb.3, implicit-def $arguments + + bb.6: + successors: %bb.2(0x80000000) + liveins: $value_stack + + bb.2.bb2: + successors: %bb.8(0x50000000), %bb.9(0x30000000) + liveins: $value_stack + + JUMPI %bb.8, %0, implicit-def $arguments + + bb.9: + successors: %bb.4(0x80000000) + liveins: $value_stack + + JUMP %bb.4, implicit-def $arguments + + bb.8: + successors: %bb.3(0x80000000) + liveins: $value_stack + + bb.3.bb3: + successors: %bb.4(0x80000000) + liveins: $value_stack + + %12:gpr = CONST_I256 i256 10, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %13:gpr = nuw nsw ADD %4, %12, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + + bb.4.bb4: + liveins: $value_stack + + RET %13, implicit-def dead $arguments + +... diff --git a/llvm/test/CodeGen/EVM/bps-spill-propagation-4.mir b/llvm/test/CodeGen/EVM/bps-spill-propagation-4.mir new file mode 100644 index 000000000000..f6aa12037fbd --- /dev/null +++ b/llvm/test/CodeGen/EVM/bps-spill-propagation-4.mir @@ -0,0 +1,254 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5 +# REQUIRES: asserts +# RUN: llc -x mir -run-pass=evm-backward-propagation-stackification -evm-force-reg-spills=5 < %s | FileCheck %s + +# Test how we are propagating second operand of commutable instruction that is spilled. + +--- | + source_filename = "test.ll" + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm" + + define i256 @test(i256 %arg1, i256 %arg2, i256 %arg3, i256 %arg4) { + entry: + %cmp1 = icmp sgt i256 %arg1, 0 + br i1 %cmp1, label %bb1, label %bb2 + + bb1: ; preds = %entry + %cmp3 = icmp sgt i256 %arg2, 0 + br i1 %cmp3, label %bb2, label %bb3 + + bb2: ; preds = %bb1, %entry + %cmp2 = icmp sgt i256 %arg3, 0 + br i1 %cmp2, label %bb3, label %bb4 + + bb3: ; preds = %bb2, %bb1 + %add1 = add nuw nsw i256 %arg1, %arg4 + br label %bb4 + + bb4: ; preds = %bb3, %bb2 + %phi1 = phi i256 [ %add1, %bb3 ], [ 10, %bb2 ] + ret i256 %phi1 + } + +... +--- +name: test +alignment: 1 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gpr, preferred-register: '' } + - { id: 1, class: gpr, preferred-register: '' } + - { id: 2, class: gpr, preferred-register: '' } + - { id: 3, class: gpr, preferred-register: '' } + - { id: 4, class: gpr, preferred-register: '' } + - { id: 5, class: gpr, preferred-register: '' } + - { id: 6, class: gpr, preferred-register: '' } + - { id: 7, class: gpr, preferred-register: '' } + - { id: 8, class: gpr, preferred-register: '' } + - { id: 9, class: gpr, preferred-register: '' } + - { id: 10, class: gpr, preferred-register: '' } + - { id: 11, class: gpr, preferred-register: '' } + - { id: 12, class: gpr, preferred-register: '' } + - { id: 13, class: gpr, preferred-register: '' } + - { id: 14, class: gpr, preferred-register: '' } + - { id: 15, class: gpr, preferred-register: '' } + - { id: 16, class: gpr, preferred-register: '' } +liveins: + - { reg: '$arguments', virtual-reg: '' } + - { reg: '$value_stack', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + isStackified: false + numberOfParameters: 4 + hasPushDeployAddress: false +body: | + ; CHECK-LABEL: name: test + ; CHECK: bb.0.entry: + ; CHECK-NEXT: successors: %bb.2(0x50000000), %bb.1(0x30000000) + ; CHECK-NEXT: liveins: $arguments, $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP4_S + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP4_S + ; CHECK-NEXT: LT_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.2 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.1: + ; CHECK-NEXT: successors: %bb.5(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.5 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.2.bb1: + ; CHECK-NEXT: successors: %bb.4(0x50000000), %bb.3(0x30000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: LT_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.4 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.3: + ; CHECK-NEXT: successors: %bb.8(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.8 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.4: + ; CHECK-NEXT: successors: %bb.5(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.5.bb2: + ; CHECK-NEXT: successors: %bb.7(0x50000000), %bb.6(0x30000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: PUSH1_S i256 10 + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: LT_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.7 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.6: + ; CHECK-NEXT: successors: %bb.9(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.9 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.7: + ; CHECK-NEXT: successors: %bb.8(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.8.bb3: + ; CHECK-NEXT: successors: %bb.9(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MLOAD_S + ; CHECK-NEXT: ADD_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.9.bb4: + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PseudoRET + bb.0.entry: + successors: %bb.1(0x50000000), %bb.5(0x30000000) + liveins: $arguments, $value_stack + + %2:gpr = ARGUMENT 0, implicit $arguments + %3:gpr = ARGUMENT 1, implicit $arguments + %4:gpr = ARGUMENT 2, implicit $arguments + %5:gpr = ARGUMENT 3, implicit $arguments + %14:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %7:gpr = LT %2, %14, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP_UNLESS %bb.1, %7, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + + bb.5: + successors: %bb.2(0x80000000) + liveins: $value_stack + + JUMP %bb.2, implicit-def $arguments + + bb.1.bb1: + successors: %bb.6(0x50000000), %bb.7(0x30000000) + liveins: $value_stack + + %15:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %9:gpr = LT %3, %15, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP_UNLESS %bb.6, %9, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + + bb.7: + successors: %bb.3(0x80000000) + liveins: $value_stack + + JUMP %bb.3, implicit-def $arguments + + bb.6: + successors: %bb.2(0x80000000) + liveins: $value_stack + + bb.2.bb2: + successors: %bb.8(0x50000000), %bb.9(0x30000000) + liveins: $value_stack + + %13:gpr = CONST_I256 i256 10, implicit-def dead $arguments + %16:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %12:gpr = LT %4, %16, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP_UNLESS %bb.8, %12, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + + bb.9: + successors: %bb.4(0x80000000) + liveins: $value_stack + + JUMP %bb.4, implicit-def $arguments + + bb.8: + successors: %bb.3(0x80000000) + liveins: $value_stack + + bb.3.bb3: + successors: %bb.4(0x80000000) + liveins: $value_stack + + %13:gpr = nuw nsw ADD %2, %5, implicit-def dead $arguments + + bb.4.bb4: + liveins: $value_stack + + RET %13, implicit-def dead $arguments + +... diff --git a/llvm/test/CodeGen/EVM/bps-spill-unreachable-arg.mir b/llvm/test/CodeGen/EVM/bps-spill-unreachable-arg.mir new file mode 100644 index 000000000000..2523d2bea559 --- /dev/null +++ b/llvm/test/CodeGen/EVM/bps-spill-unreachable-arg.mir @@ -0,0 +1,499 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5 +# RUN: llc -x mir -run-pass=evm-backward-propagation-stackification < %s | FileCheck %s + +# Test that regs (%12:gpr, %13:gpr, %15:gpr, %16:gpr) that have unreachable defs +# are not taken into account when spilling, and we chose other reachable register +# to spill, so we compile successfully. If we chose one of them, we would hit an +# assert, since we would exceed the depth of DUP instruction. + +--- | + source_filename = "test.ll" + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm-unknown-unknown" + + declare void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(2) noalias nocapture readonly, i256, i1 immarg) #0 + + define fastcc void @main(i256 %unused, i256 %unused1, i256 %unused2, i256 %unused3, i256 %unused4, i256 %unused5, i256 %unused6, i256 %unused7, i256 %unused8, i256 %unused9, i256 %unused10, i256 %unused11, i256 %unused12, i256 %calldata_load_result2781, i256 %addition_result2798, i256 %shift_left_non_overflow_result3390, i256 %calldata_load_result1899, i256 %calldata_load_result3245, i1 %comparison_result3399.not, i256 %stack_var_012.36954, i256 %calldata_load_result2605, i1 %comparison_result4030.not) { + entry: + br label %conditional_rt_187_join_block + + "block_rt_2/0": ; preds = %conditional_rt_187_join_block, %conditional_rt_181_join_block, %"block_rt_165/1" + unreachable + + "block_rt_160/3": ; preds = %"block_rt_165/1" + %calldatacopy_destination_pointer3688 = inttoptr i256 %stack_var_021.0.in6947 to ptr addrspace(1) + %calldatacopy_source_pointer3689 = inttoptr i256 %addition_result3756 to ptr addrspace(2) + tail call void @llvm.evm.memcpyas1as2(ptr addrspace(1) %calldatacopy_destination_pointer3688, ptr addrspace(2) %calldatacopy_source_pointer3689, i256 %shift_left_non_overflow_result3390, i1 false) + br i1 %comparison_result3909.not, label %conditional_rt_181_join_block, label %"block_rt_181/0" + + "block_rt_165/1": ; preds = %conditional_rt_181_join_block + %addition_result3756 = or i256 %calldata_load_result1899, %addition_result4054 + br i1 %comparison_result3399.not, label %"block_rt_2/0", label %"block_rt_160/3" + + "block_rt_181/0": ; preds = %"block_rt_160/3" + %addition_result4064 = or i256 %stack_var_011.36953, 1 + %comparison_result4003.not = icmp ult i256 %stack_var_013.36955, %calldata_load_result2605 + br i1 %comparison_result4003.not, label %conditional_rt_187_join_block, label %"block_rt_187/0.loopexit" + + "block_rt_187/0.loopexit": ; preds = %"block_rt_181/0" + store i256 %stack_var_021.06950, ptr addrspace(1) null, align 64 + unreachable + + "block_rt_188/0": ; preds = %conditional_rt_187_join_block + %addition_result4054 = or i256 %stack_var_012.36954, 1 + %addition_result1909 = or i256 %calldata_load_result3245, 1 + br label %conditional_rt_181_join_block + + conditional_rt_181_join_block: ; preds = %"block_rt_188/0", %"block_rt_160/3" + %stack_var_021.06950 = phi i256 [ 0, %"block_rt_188/0" ], [ %addition_result2798, %"block_rt_160/3" ] + %comparison_result3909.not = phi i1 [ true, %"block_rt_188/0" ], [ false, %"block_rt_160/3" ] + %stack_var_021.0.in6947 = phi i256 [ 0, %"block_rt_188/0" ], [ %addition_result2798, %"block_rt_160/3" ] + store i256 %calldata_load_result2781, ptr addrspace(1) null, align 1 + %comparison_result1913.not = icmp slt i256 0, %addition_result1909 + br i1 %comparison_result1913.not, label %"block_rt_165/1", label %"block_rt_2/0" + + conditional_rt_187_join_block: ; preds = %"block_rt_181/0", %entry + %stack_var_013.36955 = phi i256 [ 0, %entry ], [ 1, %"block_rt_181/0" ] + %stack_var_011.36953 = phi i256 [ 0, %entry ], [ %addition_result4064, %"block_rt_181/0" ] + br i1 %comparison_result4030.not, label %"block_rt_188/0", label %"block_rt_2/0" + } + + declare void @llvm.evm.memcpyas1as2(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(2) noalias nocapture readonly, i256, i1 immarg) #0 + + attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } + +... +--- +name: main +alignment: 1 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gpr, preferred-register: '' } + - { id: 1, class: gpr, preferred-register: '' } + - { id: 2, class: gpr, preferred-register: '' } + - { id: 3, class: gpr, preferred-register: '' } + - { id: 4, class: gpr, preferred-register: '' } + - { id: 5, class: gpr, preferred-register: '' } + - { id: 6, class: gpr, preferred-register: '' } + - { id: 7, class: gpr, preferred-register: '' } + - { id: 8, class: gpr, preferred-register: '' } + - { id: 9, class: gpr, preferred-register: '' } + - { id: 10, class: gpr, preferred-register: '' } + - { id: 11, class: gpr, preferred-register: '' } + - { id: 12, class: gpr, preferred-register: '' } + - { id: 13, class: gpr, preferred-register: '' } + - { id: 14, class: gpr, preferred-register: '' } + - { id: 15, class: gpr, preferred-register: '' } + - { id: 16, class: gpr, preferred-register: '' } + - { id: 17, class: gpr, preferred-register: '' } + - { id: 18, class: gpr, preferred-register: '' } + - { id: 19, class: gpr, preferred-register: '' } + - { id: 20, class: gpr, preferred-register: '' } + - { id: 21, class: gpr, preferred-register: '' } + - { id: 22, class: gpr, preferred-register: '' } + - { id: 23, class: gpr, preferred-register: '' } + - { id: 24, class: gpr, preferred-register: '' } + - { id: 25, class: gpr, preferred-register: '' } + - { id: 26, class: gpr, preferred-register: '' } + - { id: 27, class: gpr, preferred-register: '' } + - { id: 28, class: gpr, preferred-register: '' } + - { id: 29, class: gpr, preferred-register: '' } + - { id: 30, class: gpr, preferred-register: '' } + - { id: 31, class: gpr, preferred-register: '' } + - { id: 32, class: gpr, preferred-register: '' } + - { id: 33, class: gpr, preferred-register: '' } + - { id: 34, class: gpr, preferred-register: '' } + - { id: 35, class: gpr, preferred-register: '' } + - { id: 36, class: gpr, preferred-register: '' } + - { id: 37, class: gpr, preferred-register: '' } + - { id: 38, class: gpr, preferred-register: '' } + - { id: 39, class: gpr, preferred-register: '' } + - { id: 40, class: gpr, preferred-register: '' } + - { id: 41, class: gpr, preferred-register: '' } + - { id: 42, class: gpr, preferred-register: '' } + - { id: 43, class: gpr, preferred-register: '' } + - { id: 44, class: gpr, preferred-register: '' } + - { id: 45, class: gpr, preferred-register: '' } +liveins: + - { reg: '$arguments', virtual-reg: '' } + - { reg: '$value_stack', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + isStackified: false + numberOfParameters: 22 + hasPushDeployAddress: false +body: | + ; CHECK-LABEL: name: main + ; CHECK: bb.0.entry: + ; CHECK-NEXT: successors: %bb.1(0x80000000) + ; CHECK-NEXT: liveins: $arguments, $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP9_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: SWAP6_S + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: SWAP7_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: AND_S + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: AND_S + ; CHECK-NEXT: SWAP5_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP7_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP6_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.1.conditional_rt_187_join_block: + ; CHECK-NEXT: successors: %bb.11(0x80000000), %bb.2(0x00000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MLOAD_S + ; CHECK-NEXT: PseudoJUMPI %bb.11 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.2: + ; CHECK-NEXT: successors: %bb.3(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.3: + ; CHECK-NEXT: successors: + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.4: + ; CHECK-NEXT: successors: %bb.5(0x00000000), %bb.6(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP13_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.6 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.5: + ; CHECK-NEXT: successors: %bb.3(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.3 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.6: + ; CHECK-NEXT: successors: %bb.7(0x7c000000), %bb.8(0x04000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP7_S + ; CHECK-NEXT: DUP5_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: CALLDATACOPY_S + ; CHECK-NEXT: AND_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: DUP10_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.8 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.7: + ; CHECK-NEXT: successors: %bb.12(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP14_S + ; CHECK-NEXT: SWAP15_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.12 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.8: + ; CHECK-NEXT: successors: %bb.9(0x80000000), %bb.10(0x00000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: DUP7_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: ULT_S + ; CHECK-NEXT: SWAP8_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: SWAP9_S + ; CHECK-NEXT: OR_S + ; CHECK-NEXT: SWAP9_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.10 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.9: + ; CHECK-NEXT: successors: %bb.1(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: SWAP8_S + ; CHECK-NEXT: SWAP7_S + ; CHECK-NEXT: SWAP9_S + ; CHECK-NEXT: SWAP10_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.10: + ; CHECK-NEXT: successors: + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.11: + ; CHECK-NEXT: successors: %bb.12(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP10_S + ; CHECK-NEXT: SWAP9_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP9_S + ; CHECK-NEXT: OR_S + ; CHECK-NEXT: GT_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP6_S + ; CHECK-NEXT: OR_S + ; CHECK-NEXT: DUP8_S + ; CHECK-NEXT: OR_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.12.conditional_rt_181_join_block: + ; CHECK-NEXT: successors: %bb.4(0x80000000), %bb.13(0x00000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP3_S + ; CHECK-NEXT: DUP3_S + ; CHECK-NEXT: SWAP16_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: PseudoJUMPI %bb.4 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.13: + ; CHECK-NEXT: successors: %bb.3(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.3 + bb.0.entry: + successors: %bb.1(0x80000000) + liveins: $arguments, $value_stack + + %9:gpr = ARGUMENT 13, implicit $arguments + %10:gpr = ARGUMENT 14, implicit $arguments + %11:gpr = ARGUMENT 15, implicit $arguments + %12:gpr = ARGUMENT 16, implicit $arguments + %13:gpr = ARGUMENT 17, implicit $arguments + %14:gpr = ARGUMENT 18, implicit $arguments + %15:gpr = ARGUMENT 19, implicit $arguments + %16:gpr = ARGUMENT 20, implicit $arguments + %17:gpr = ARGUMENT 21, implicit $arguments + %39:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %20:gpr = AND %39, %17, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %38:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %26:gpr = AND %38, %14, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %7:gpr = CONST_I256 i256 0, implicit-def dead $arguments + %8:gpr = CONST_I256 i256 0, implicit-def dead $arguments + + bb.1.conditional_rt_187_join_block: + successors: %bb.7(0x80000000), %bb.10(0x00000000) + liveins: $value_stack + + JUMPI %bb.7, %20, implicit-def $arguments + + bb.10: + successors: %bb.2(0x80000000) + liveins: $value_stack + + bb.2: + successors: + liveins: $value_stack + + bb.3: + successors: %bb.12(0x00000000), %bb.4(0x80000000) + liveins: $value_stack + + JUMP_UNLESS %bb.4, %26, implicit-def $arguments + + bb.12: + successors: %bb.2(0x80000000) + liveins: $value_stack + + JUMP %bb.2, implicit-def $arguments + + bb.4: + successors: %bb.13(0x7c000000), %bb.5(0x04000000) + liveins: $value_stack + + CALLDATACOPY %6, %0, %11, implicit-def dead $arguments + %40:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %29:gpr = AND %5, %40, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %5:gpr = CONST_I256 i256 0, implicit-def dead $arguments + %6:gpr = COPY_I256 %10, implicit-def $arguments + JUMP_UNLESS %bb.5, %29, implicit-def $arguments + + bb.13: + successors: %bb.8(0x80000000) + liveins: $value_stack + + JUMP %bb.8, implicit-def $arguments + + bb.5: + successors: %bb.9(0x80000000), %bb.6(0x00000000) + liveins: $value_stack + + %31:gpr = ULT %7, %16, implicit-def dead $arguments + %7:gpr = CONST_I256 i256 1, implicit-def dead $arguments + %41:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %8:gpr = OR %8, %41, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP_UNLESS %bb.6, %31, implicit-def $arguments + + bb.9: + successors: %bb.1(0x80000000) + liveins: $value_stack + + JUMP %bb.1, implicit-def $arguments + + bb.6: + successors: + liveins: $value_stack + + %32:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + MSTORE %32, %4, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into `ptr addrspace(1) null`, align 64, addrspace 1) + + bb.7: + successors: %bb.8(0x80000000) + liveins: $value_stack + + %5:gpr = CONST_I256 i256 1, implicit-def dead $arguments + %6:gpr = CONST_I256 i256 0, implicit-def dead $arguments + %44:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %43:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %3:gpr = OR %13, %43, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %24:gpr = GT %3, %44, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %42:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %2:gpr = OR %15, %42, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %0:gpr = OR %12, %2, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + + bb.8.conditional_rt_181_join_block: + successors: %bb.3(0x80000000), %bb.11(0x00000000) + liveins: $value_stack + + %4:gpr = COPY_I256 %6, implicit-def $arguments + %45:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + MSTORE %45, %9, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into `ptr addrspace(1) null`, align 1, addrspace 1) + JUMPI %bb.3, %24, implicit-def $arguments + + bb.11: + successors: %bb.2(0x80000000) + liveins: $value_stack + + JUMP %bb.2, implicit-def $arguments + +... diff --git a/llvm/test/CodeGen/EVM/bps-spill-unreachable-ret.mir b/llvm/test/CodeGen/EVM/bps-spill-unreachable-ret.mir new file mode 100644 index 000000000000..c504d024f6ed --- /dev/null +++ b/llvm/test/CodeGen/EVM/bps-spill-unreachable-ret.mir @@ -0,0 +1,519 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5 +# RUN: llc -x mir -run-pass=evm-backward-propagation-stackification < %s | FileCheck %s + +# Test that regs (%1:gpr, %2:gpr, %4:gpr, %5:gpr) that have unreachable defs +# are not taken into account when spilling, and we chose other reachable register +# to spill, so we compile successfully. If we chose one of them, we would hit an +# assert, since we would exceed the depth of DUP instruction. + +--- | + source_filename = "test.ll" + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm-unknown-unknown" + + declare void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(2) noalias nocapture readonly, i256, i1 immarg) #0 + + declare { i1, i256, i256, i1, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256 } @foo() + + define fastcc void @main() { + entry: + %ret = call { i1, i256, i256, i1, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256 } @foo() + %comparison_result4030.not = extractvalue { i1, i256, i256, i1, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256 } %ret, 0 + %calldata_load_result2605 = extractvalue { i1, i256, i256, i1, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256 } %ret, 1 + %stack_var_012.36954 = extractvalue { i1, i256, i256, i1, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256 } %ret, 2 + %comparison_result3399.not = extractvalue { i1, i256, i256, i1, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256 } %ret, 3 + %calldata_load_result3245 = extractvalue { i1, i256, i256, i1, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256 } %ret, 4 + %calldata_load_result1899 = extractvalue { i1, i256, i256, i1, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256 } %ret, 5 + %shift_left_non_overflow_result3390 = extractvalue { i1, i256, i256, i1, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256 } %ret, 6 + %addition_result2798 = extractvalue { i1, i256, i256, i1, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256 } %ret, 7 + %calldata_load_result2781 = extractvalue { i1, i256, i256, i1, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256 } %ret, 8 + br label %conditional_rt_187_join_block + + "block_rt_2/0": ; preds = %conditional_rt_187_join_block, %conditional_rt_181_join_block, %"block_rt_165/1" + unreachable + + "block_rt_160/3": ; preds = %"block_rt_165/1" + %calldatacopy_destination_pointer3688 = inttoptr i256 %stack_var_021.0.in6947 to ptr addrspace(1) + %calldatacopy_source_pointer3689 = inttoptr i256 %addition_result3756 to ptr addrspace(2) + tail call void @llvm.evm.memcpyas1as2(ptr addrspace(1) %calldatacopy_destination_pointer3688, ptr addrspace(2) %calldatacopy_source_pointer3689, i256 %shift_left_non_overflow_result3390, i1 false) + br i1 %comparison_result3909.not, label %conditional_rt_181_join_block, label %"block_rt_181/0" + + "block_rt_165/1": ; preds = %conditional_rt_181_join_block + %addition_result3756 = or i256 %calldata_load_result1899, %addition_result4054 + br i1 %comparison_result3399.not, label %"block_rt_2/0", label %"block_rt_160/3" + + "block_rt_181/0": ; preds = %"block_rt_160/3" + %addition_result4064 = or i256 %stack_var_011.36953, 1 + %comparison_result4003.not = icmp ult i256 %stack_var_013.36955, %calldata_load_result2605 + br i1 %comparison_result4003.not, label %conditional_rt_187_join_block, label %"block_rt_187/0.loopexit" + + "block_rt_187/0.loopexit": ; preds = %"block_rt_181/0" + store i256 %stack_var_021.06950, ptr addrspace(1) null, align 64 + unreachable + + "block_rt_188/0": ; preds = %conditional_rt_187_join_block + %addition_result4054 = or i256 %stack_var_012.36954, 1 + %addition_result1909 = or i256 %calldata_load_result3245, 1 + br label %conditional_rt_181_join_block + + conditional_rt_181_join_block: ; preds = %"block_rt_188/0", %"block_rt_160/3" + %stack_var_021.06950 = phi i256 [ 0, %"block_rt_188/0" ], [ %addition_result2798, %"block_rt_160/3" ] + %comparison_result3909.not = phi i1 [ true, %"block_rt_188/0" ], [ false, %"block_rt_160/3" ] + %stack_var_021.0.in6947 = phi i256 [ 0, %"block_rt_188/0" ], [ %addition_result2798, %"block_rt_160/3" ] + store i256 %calldata_load_result2781, ptr addrspace(1) null, align 1 + %comparison_result1913.not = icmp slt i256 0, %addition_result1909 + br i1 %comparison_result1913.not, label %"block_rt_165/1", label %"block_rt_2/0" + + conditional_rt_187_join_block: ; preds = %"block_rt_181/0", %entry + %stack_var_013.36955 = phi i256 [ 0, %entry ], [ 1, %"block_rt_181/0" ] + %stack_var_011.36953 = phi i256 [ 0, %entry ], [ %addition_result4064, %"block_rt_181/0" ] + br i1 %comparison_result4030.not, label %"block_rt_188/0", label %"block_rt_2/0" + } + + declare void @llvm.evm.memcpyas1as2(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(2) noalias nocapture readonly, i256, i1 immarg) #0 + + attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } + +... +--- +name: main +alignment: 1 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gpr, preferred-register: '' } + - { id: 1, class: gpr, preferred-register: '' } + - { id: 2, class: gpr, preferred-register: '' } + - { id: 3, class: gpr, preferred-register: '' } + - { id: 4, class: gpr, preferred-register: '' } + - { id: 5, class: gpr, preferred-register: '' } + - { id: 6, class: gpr, preferred-register: '' } + - { id: 7, class: gpr, preferred-register: '' } + - { id: 8, class: gpr, preferred-register: '' } + - { id: 9, class: gpr, preferred-register: '' } + - { id: 10, class: gpr, preferred-register: '' } + - { id: 11, class: gpr, preferred-register: '' } + - { id: 12, class: gpr, preferred-register: '' } + - { id: 13, class: gpr, preferred-register: '' } + - { id: 14, class: gpr, preferred-register: '' } + - { id: 15, class: gpr, preferred-register: '' } + - { id: 16, class: gpr, preferred-register: '' } + - { id: 17, class: gpr, preferred-register: '' } + - { id: 18, class: gpr, preferred-register: '' } + - { id: 19, class: gpr, preferred-register: '' } + - { id: 20, class: gpr, preferred-register: '' } + - { id: 21, class: gpr, preferred-register: '' } + - { id: 22, class: gpr, preferred-register: '' } + - { id: 23, class: gpr, preferred-register: '' } + - { id: 24, class: gpr, preferred-register: '' } + - { id: 25, class: gpr, preferred-register: '' } + - { id: 26, class: gpr, preferred-register: '' } + - { id: 27, class: gpr, preferred-register: '' } + - { id: 28, class: gpr, preferred-register: '' } + - { id: 29, class: gpr, preferred-register: '' } + - { id: 30, class: gpr, preferred-register: '' } + - { id: 31, class: gpr, preferred-register: '' } + - { id: 32, class: gpr, preferred-register: '' } + - { id: 33, class: gpr, preferred-register: '' } + - { id: 34, class: gpr, preferred-register: '' } + - { id: 35, class: gpr, preferred-register: '' } + - { id: 36, class: gpr, preferred-register: '' } + - { id: 37, class: gpr, preferred-register: '' } + - { id: 38, class: gpr, preferred-register: '' } + - { id: 39, class: gpr, preferred-register: '' } + - { id: 40, class: gpr, preferred-register: '' } + - { id: 41, class: gpr, preferred-register: '' } + - { id: 42, class: gpr, preferred-register: '' } + - { id: 43, class: gpr, preferred-register: '' } + - { id: 44, class: gpr, preferred-register: '' } + - { id: 45, class: gpr, preferred-register: '' } + - { id: 46, class: gpr, preferred-register: '' } + - { id: 47, class: gpr, preferred-register: '' } + - { id: 48, class: gpr, preferred-register: '' } + - { id: 49, class: gpr, preferred-register: '' } + - { id: 50, class: gpr, preferred-register: '' } + - { id: 51, class: gpr, preferred-register: '' } + - { id: 52, class: gpr, preferred-register: '' } + - { id: 53, class: gpr, preferred-register: '' } + - { id: 54, class: gpr, preferred-register: '' } + - { id: 55, class: gpr, preferred-register: '' } + - { id: 56, class: gpr, preferred-register: '' } + - { id: 57, class: gpr, preferred-register: '' } + - { id: 58, class: gpr, preferred-register: '' } +liveins: + - { reg: '$arguments', virtual-reg: '' } + - { reg: '$value_stack', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: true + stackProtector: '' + functionContext: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + isStackified: false + numberOfParameters: 0 + hasPushDeployAddress: false +body: | + ; CHECK-LABEL: name: main + ; CHECK: bb.0.entry: + ; CHECK-NEXT: successors: %bb.1(0x80000000) + ; CHECK-NEXT: liveins: $arguments, $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: PUSH_LABEL + ; CHECK-NEXT: PseudoCALL @foo, + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP9_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: SWAP7_S + ; CHECK-NEXT: SWAP5_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: SWAP8_S + ; CHECK-NEXT: AND_S + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: AND_S + ; CHECK-NEXT: SWAP5_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP7_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP6_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.1.conditional_rt_187_join_block: + ; CHECK-NEXT: successors: %bb.11(0x80000000), %bb.2(0x00000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MLOAD_S + ; CHECK-NEXT: PseudoJUMPI %bb.11 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.2: + ; CHECK-NEXT: successors: %bb.3(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.3: + ; CHECK-NEXT: successors: + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.4: + ; CHECK-NEXT: successors: %bb.5(0x00000000), %bb.6(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP13_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.6 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.5: + ; CHECK-NEXT: successors: %bb.3(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.3 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.6: + ; CHECK-NEXT: successors: %bb.7(0x7c000000), %bb.8(0x04000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP7_S + ; CHECK-NEXT: DUP5_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: CALLDATACOPY_S + ; CHECK-NEXT: AND_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: DUP10_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.8 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.7: + ; CHECK-NEXT: successors: %bb.12(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP14_S + ; CHECK-NEXT: SWAP15_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.12 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.8: + ; CHECK-NEXT: successors: %bb.9(0x80000000), %bb.10(0x00000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: DUP7_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: ULT_S + ; CHECK-NEXT: SWAP8_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: SWAP9_S + ; CHECK-NEXT: OR_S + ; CHECK-NEXT: SWAP9_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.10 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.9: + ; CHECK-NEXT: successors: %bb.1(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: SWAP8_S + ; CHECK-NEXT: SWAP7_S + ; CHECK-NEXT: SWAP9_S + ; CHECK-NEXT: SWAP10_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.10: + ; CHECK-NEXT: successors: + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.11: + ; CHECK-NEXT: successors: %bb.12(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP10_S + ; CHECK-NEXT: SWAP9_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP9_S + ; CHECK-NEXT: OR_S + ; CHECK-NEXT: GT_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP6_S + ; CHECK-NEXT: OR_S + ; CHECK-NEXT: DUP8_S + ; CHECK-NEXT: OR_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.12.conditional_rt_181_join_block: + ; CHECK-NEXT: successors: %bb.4(0x80000000), %bb.13(0x00000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP3_S + ; CHECK-NEXT: DUP3_S + ; CHECK-NEXT: SWAP16_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: PseudoJUMPI %bb.4 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.13: + ; CHECK-NEXT: successors: %bb.3(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.3 + bb.0.entry: + successors: %bb.1(0x80000000) + liveins: $arguments, $value_stack + + %52:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %0:gpr, %1:gpr, %2:gpr, %3:gpr, %4:gpr, %5:gpr, %6:gpr, %7:gpr, %8:gpr, dead %19:gpr, dead %20:gpr, dead %21:gpr, dead %22:gpr, dead %23:gpr, dead %24:gpr, dead %25:gpr, dead %26:gpr, dead %27:gpr, dead %28:gpr, dead %29:gpr, dead %30:gpr, dead %31:gpr = FCALL @foo, implicit-def dead $arguments, implicit $sp, implicit-def $value_stack, implicit $value_stack + %33:gpr = AND %0, %52, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %51:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %39:gpr = AND %51, %3, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %16:gpr = CONST_I256 i256 0, implicit-def dead $arguments + %17:gpr = CONST_I256 i256 0, implicit-def dead $arguments + + bb.1.conditional_rt_187_join_block: + successors: %bb.7(0x80000000), %bb.10(0x00000000) + liveins: $value_stack + + JUMPI %bb.7, %33, implicit-def $arguments + + bb.10: + successors: %bb.2(0x80000000) + liveins: $value_stack + + bb.2: + successors: + liveins: $value_stack + + bb.3: + successors: %bb.12(0x00000000), %bb.4(0x80000000) + liveins: $value_stack + + JUMP_UNLESS %bb.4, %39, implicit-def $arguments + + bb.12: + successors: %bb.2(0x80000000) + liveins: $value_stack + + JUMP %bb.2, implicit-def $arguments + + bb.4: + successors: %bb.13(0x7c000000), %bb.5(0x04000000) + liveins: $value_stack + + CALLDATACOPY %15, %9, %6, implicit-def dead $arguments + %53:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %42:gpr = AND %14, %53, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %14:gpr = CONST_I256 i256 0, implicit-def dead $arguments + %15:gpr = COPY_I256 %7, implicit-def $arguments + JUMP_UNLESS %bb.5, %42, implicit-def $arguments + + bb.13: + successors: %bb.8(0x80000000) + liveins: $value_stack + + JUMP %bb.8, implicit-def $arguments + + bb.5: + successors: %bb.9(0x80000000), %bb.6(0x00000000) + liveins: $value_stack + + %44:gpr = ULT %16, %1, implicit-def dead $arguments + %16:gpr = CONST_I256 i256 1, implicit-def dead $arguments + %54:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %17:gpr = OR %17, %54, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP_UNLESS %bb.6, %44, implicit-def $arguments + + bb.9: + successors: %bb.1(0x80000000) + liveins: $value_stack + + JUMP %bb.1, implicit-def $arguments + + bb.6: + successors: + liveins: $value_stack + + %45:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + MSTORE %45, %13, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into `ptr addrspace(1) null`, align 64, addrspace 1) + + bb.7: + successors: %bb.8(0x80000000) + liveins: $value_stack + + %14:gpr = CONST_I256 i256 1, implicit-def dead $arguments + %15:gpr = CONST_I256 i256 0, implicit-def dead $arguments + %57:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %56:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %12:gpr = OR %4, %56, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %37:gpr = GT %12, %57, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %55:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %11:gpr = OR %2, %55, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %9:gpr = OR %5, %11, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + + bb.8.conditional_rt_181_join_block: + successors: %bb.3(0x80000000), %bb.11(0x00000000) + liveins: $value_stack + + %13:gpr = COPY_I256 %15, implicit-def $arguments + %58:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + MSTORE %58, %8, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into `ptr addrspace(1) null`, align 1, addrspace 1) + JUMPI %bb.3, %37, implicit-def $arguments + + bb.11: + successors: %bb.2(0x80000000) + liveins: $value_stack + + JUMP %bb.2, implicit-def $arguments + +... diff --git a/llvm/test/CodeGen/EVM/bps-spills-1.mir b/llvm/test/CodeGen/EVM/bps-spills-1.mir new file mode 100644 index 000000000000..ae2843cb14fa --- /dev/null +++ b/llvm/test/CodeGen/EVM/bps-spills-1.mir @@ -0,0 +1,1072 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5 +# RUN: llc -x mir -run-pass=evm-backward-propagation-stackification -run-pass=evm-finalize-stack-frames -evm-stack-region-offset=128 -evm-stack-region-size=128 < %s | FileCheck %s + +--- | + source_filename = "reduced.ll" + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm-unknown-unknown" + + define fastcc void @main(i255 %trunc22495, i1 %comparison_result7009.not, i8 %trunc, i8 %trunc8) { + entry: + br label %conditional_rt_20_join_block + + "block_rt_46/7": ; preds = %conditional_rt_52_join_block3336, %conditional_rt_45_join_block2670 + %addition_result2803 = or i256 %stack_var_009.022000, 1 + %and_result2660 = and i256 %stack_var_009.022000, 0 + br label %conditional_rt_45_join_block2670 + + "block_rt_59/7.lr.ph.split": ; preds = %conditional_rt_52_join_block3336 + %0 = zext i255 %trunc22495 to i256 + switch i256 %0, label %remainder_join12885.preheader [ + i256 4, label %conditional_rt_52_join_block3336 + i256 6, label %"block_rt_64/7.us21835" + i256 0, label %"block_rt_123/7" + ] + + remainder_join12885.preheader: ; preds = %"block_rt_59/7.lr.ph.split" + br label %remainder_join12885 + + "block_rt_64/7.us21835": ; preds = %"block_rt_59/7.lr.ph.split" + br label %conditional_rt_52_join_block3336 + + "block_rt_58/7": ; preds = %"block_rt_123/8.split", %remainder_join12885 + %and_result2179 = and i256 %addition_result218222482, 1 + %addition_result2182 = or i256 %addition_result218222482, 0 + br label %remainder_join12885 + + "block_rt_73/7.preheader": ; preds = %"block_rt_73/7.preheader.preheader", %"block_rt_123/8.split" + %and_result58612176722479 = phi i256 [ 0, %"block_rt_123/8.split" ], [ 1, %"block_rt_73/7.preheader.preheader" ] + %1 = zext i8 0 to i256 + switch i256 %1, label %conditional_rt_74_join_block6475.preheader [ + i256 1, label %"block_rt_123/7.loopexit1" + i256 0, label %"block_rt_123/8.split" + ] + + conditional_rt_74_join_block6475.preheader: ; preds = %"block_rt_73/7.preheader" + br label %conditional_rt_74_join_block6475 + + "block_rt_80/7.split.split.split": ; preds = %"block_rt_81/7" + %comparison_result6468 = icmp ugt i256 %stack_var_013.221721, 0 + br i1 %comparison_result6468, label %"block_rt_123/8.split", label %conditional_rt_74_join_block6475 + + "block_rt_81/7": ; preds = %conditional_rt_89_join_block7784 + br i1 %comparison_result7009.not, label %"block_rt_80/7.split.split.split", label %remainder_join12901 + + "block_rt_97/7": ; preds = %conditional_rt_89_join_block7784 + ret void + + "block_rt_123/7.loopexit": ; preds = %conditional_rt_89_join_block7784 + br label %"block_rt_123/7" + + "block_rt_123/7.loopexit1": ; preds = %"block_rt_73/7.preheader" + br label %"block_rt_123/7" + + "block_rt_123/7.loopexit3": ; preds = %conditional_rt_45_join_block2670 + br label %"block_rt_123/7" + + "block_rt_123/7": ; preds = %"block_rt_59/7.lr.ph.split", %"block_rt_123/7.loopexit3", %"block_rt_123/7.loopexit1", %"block_rt_123/7.loopexit" + %stack_var_007.0 = phi i256 [ 1, %"block_rt_123/7.loopexit" ], [ 50, %"block_rt_123/7.loopexit1" ], [ 0, %"block_rt_123/7.loopexit3" ], [ 50, %"block_rt_59/7.lr.ph.split" ] + %and_result1807 = and i256 %stack_var_007.0, 1 + %stack_var_004.122011.be = or i256 %stack_var_004.1220116, 1 + br label %conditional_rt_20_join_block + + "block_rt_123/7.thread": ; preds = %conditional_rt_20_join_block + unreachable + + "block_rt_123/8.split": ; preds = %"block_rt_80/7.split.split.split", %"block_rt_73/7.preheader" + %comparison_result6061 = icmp ugt i256 %and_result58612176722479, 0 + br i1 %comparison_result6061, label %"block_rt_58/7", label %"block_rt_73/7.preheader" + + conditional_rt_20_join_block: ; preds = %"block_rt_123/7", %entry + %stack_var_004.1220116 = phi i256 [ %stack_var_004.122011.be, %"block_rt_123/7" ], [ 0, %entry ] + %stack_var_003.122010 = phi i256 [ %and_result1807, %"block_rt_123/7" ], [ 0, %entry ] + %comparison_result1783 = icmp eq i256 %stack_var_003.122010, 0 + br i1 %comparison_result1783, label %"block_rt_123/7.thread", label %conditional_rt_22_join_block + + conditional_rt_22_join_block: ; preds = %conditional_rt_20_join_block + br label %conditional_rt_45_join_block2670 + + conditional_rt_45_join_block2670: ; preds = %conditional_rt_22_join_block, %"block_rt_46/7" + %stack_var_009.022000 = phi i256 [ %addition_result2803, %"block_rt_46/7" ], [ 0, %conditional_rt_22_join_block ] + %2 = zext i8 %trunc8 to i256 + switch i256 %2, label %conditional_rt_52_join_block3336.preheader [ + i256 1, label %"block_rt_46/7" + i256 0, label %"block_rt_123/7.loopexit3" + ] + + conditional_rt_52_join_block3336.preheader: ; preds = %conditional_rt_45_join_block2670 + br label %conditional_rt_52_join_block3336 + + conditional_rt_52_join_block3336: ; preds = %"block_rt_64/7.us21835", %remainder_join12885, %"block_rt_59/7.lr.ph.split", %conditional_rt_52_join_block3336.preheader + br i1 %comparison_result7009.not, label %"block_rt_46/7", label %"block_rt_59/7.lr.ph.split" + + conditional_rt_74_join_block6475: ; preds = %conditional_rt_74_join_block6475.preheader, %"block_rt_80/7.split.split.split" + %stack_var_013.221721 = phi i256 [ 0, %"block_rt_80/7.split.split.split" ], [ 1, %conditional_rt_74_join_block6475.preheader ] + br label %remainder_join12901 + + conditional_rt_89_join_block7784: ; preds = %remainder_join12901, %conditional_rt_89_join_block7784 + %3 = zext i8 %trunc to i256 + switch i256 %3, label %"block_rt_97/7" [ + i256 0, label %conditional_rt_89_join_block7784 + i256 32, label %"block_rt_81/7" + i256 8, label %"block_rt_123/7.loopexit" + ] + + remainder_join12885: ; preds = %remainder_join12885.preheader, %"block_rt_58/7" + %addition_result218222482 = phi i256 [ %and_result2179, %"block_rt_58/7" ], [ 0, %remainder_join12885.preheader ] + %4 = zext i8 0 to i256 + switch i256 %4, label %"block_rt_73/7.preheader.preheader" [ + i256 0, label %"block_rt_58/7" + i256 1, label %conditional_rt_52_join_block3336 + ] + + "block_rt_73/7.preheader.preheader": ; preds = %remainder_join12885 + br label %"block_rt_73/7.preheader" + + remainder_join12901: ; preds = %conditional_rt_74_join_block6475, %"block_rt_81/7" + br label %conditional_rt_89_join_block7784 + } + +... +--- +name: main +alignment: 1 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gpr, preferred-register: '' } + - { id: 1, class: gpr, preferred-register: '' } + - { id: 2, class: gpr, preferred-register: '' } + - { id: 3, class: gpr, preferred-register: '' } + - { id: 4, class: gpr, preferred-register: '' } + - { id: 5, class: gpr, preferred-register: '' } + - { id: 6, class: gpr, preferred-register: '' } + - { id: 7, class: gpr, preferred-register: '' } + - { id: 8, class: gpr, preferred-register: '' } + - { id: 9, class: gpr, preferred-register: '' } + - { id: 10, class: gpr, preferred-register: '' } + - { id: 11, class: gpr, preferred-register: '' } + - { id: 12, class: gpr, preferred-register: '' } + - { id: 13, class: gpr, preferred-register: '' } + - { id: 14, class: gpr, preferred-register: '' } + - { id: 15, class: gpr, preferred-register: '' } + - { id: 16, class: gpr, preferred-register: '' } + - { id: 17, class: gpr, preferred-register: '' } + - { id: 18, class: gpr, preferred-register: '' } + - { id: 19, class: gpr, preferred-register: '' } + - { id: 20, class: gpr, preferred-register: '' } + - { id: 21, class: gpr, preferred-register: '' } + - { id: 22, class: gpr, preferred-register: '' } + - { id: 23, class: gpr, preferred-register: '' } + - { id: 24, class: gpr, preferred-register: '' } + - { id: 25, class: gpr, preferred-register: '' } + - { id: 26, class: gpr, preferred-register: '' } + - { id: 27, class: gpr, preferred-register: '' } + - { id: 28, class: gpr, preferred-register: '' } + - { id: 29, class: gpr, preferred-register: '' } + - { id: 30, class: gpr, preferred-register: '' } + - { id: 31, class: gpr, preferred-register: '' } + - { id: 32, class: gpr, preferred-register: '' } + - { id: 33, class: gpr, preferred-register: '' } + - { id: 34, class: gpr, preferred-register: '' } + - { id: 35, class: gpr, preferred-register: '' } + - { id: 36, class: gpr, preferred-register: '' } + - { id: 37, class: gpr, preferred-register: '' } + - { id: 38, class: gpr, preferred-register: '' } + - { id: 39, class: gpr, preferred-register: '' } + - { id: 40, class: gpr, preferred-register: '' } + - { id: 41, class: gpr, preferred-register: '' } + - { id: 42, class: gpr, preferred-register: '' } + - { id: 43, class: gpr, preferred-register: '' } + - { id: 44, class: gpr, preferred-register: '' } + - { id: 45, class: gpr, preferred-register: '' } + - { id: 46, class: gpr, preferred-register: '' } + - { id: 47, class: gpr, preferred-register: '' } + - { id: 48, class: gpr, preferred-register: '' } + - { id: 49, class: gpr, preferred-register: '' } + - { id: 50, class: gpr, preferred-register: '' } + - { id: 51, class: gpr, preferred-register: '' } + - { id: 52, class: gpr, preferred-register: '' } + - { id: 53, class: gpr, preferred-register: '' } + - { id: 54, class: gpr, preferred-register: '' } + - { id: 55, class: gpr, preferred-register: '' } + - { id: 56, class: gpr, preferred-register: '' } + - { id: 57, class: gpr, preferred-register: '' } + - { id: 58, class: gpr, preferred-register: '' } + - { id: 59, class: gpr, preferred-register: '' } + - { id: 60, class: gpr, preferred-register: '' } + - { id: 61, class: gpr, preferred-register: '' } + - { id: 62, class: gpr, preferred-register: '' } + - { id: 63, class: gpr, preferred-register: '' } + - { id: 64, class: gpr, preferred-register: '' } + - { id: 65, class: gpr, preferred-register: '' } + - { id: 66, class: gpr, preferred-register: '' } + - { id: 67, class: gpr, preferred-register: '' } + - { id: 68, class: gpr, preferred-register: '' } + - { id: 69, class: gpr, preferred-register: '' } + - { id: 70, class: gpr, preferred-register: '' } + - { id: 71, class: gpr, preferred-register: '' } + - { id: 72, class: gpr, preferred-register: '' } + - { id: 73, class: gpr, preferred-register: '' } + - { id: 74, class: gpr, preferred-register: '' } + - { id: 75, class: gpr, preferred-register: '' } +liveins: + - { reg: '$arguments', virtual-reg: '' } + - { reg: '$value_stack', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + isStackified: false + numberOfParameters: 4 + hasPushDeployAddress: false +body: | + ; CHECK-LABEL: name: main + ; CHECK: bb.0.entry: + ; CHECK-NEXT: successors: %bb.1(0x80000000) + ; CHECK-NEXT: liveins: $arguments, $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: PUSH1_S i256 255 + ; CHECK-NEXT: AND_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP5_S + ; CHECK-NEXT: EQ_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: PUSH32_S i256 57896044618658097711785492504343953926634992332820282019728792003956564819967 + ; CHECK-NEXT: AND_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: PUSH1_S i256 4 + ; CHECK-NEXT: DUP4_S + ; CHECK-NEXT: EQ_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: PUSH1_S i256 6 + ; CHECK-NEXT: DUP5_S + ; CHECK-NEXT: EQ_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: PUSH1_S i256 255 + ; CHECK-NEXT: AND_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: PUSH1_S i256 8 + ; CHECK-NEXT: DUP5_S + ; CHECK-NEXT: EQ_S + ; CHECK-NEXT: SWAP8_S + ; CHECK-NEXT: PUSH1_S i256 32 + ; CHECK-NEXT: DUP6_S + ; CHECK-NEXT: EQ_S + ; CHECK-NEXT: PUSH1_S i256 128 + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP8_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.1.conditional_rt_20_join_block: + ; CHECK-NEXT: successors: %bb.43(0x00000000), %bb.2(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.43 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.2.conditional_rt_22_join_block: + ; CHECK-NEXT: successors: %bb.3(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP3_S + ; CHECK-NEXT: DUP6_S + ; CHECK-NEXT: DUP3_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.3.conditional_rt_45_join_block2670: + ; CHECK-NEXT: successors: %bb.4(0x3efbefbf), %bb.5(0x41041041) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.5 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.4: + ; CHECK-NEXT: successors: %bb.44(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.44 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.5.conditional_rt_45_join_block2670: + ; CHECK-NEXT: successors: %bb.41(0x04000000), %bb.6(0x7c000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.41 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.6: + ; CHECK-NEXT: successors: %bb.7(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP6_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: DUP9_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.7.conditional_rt_52_join_block3336: + ; CHECK-NEXT: successors: %bb.8(0x04000000), %bb.9(0x7c000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP6_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP7_S + ; CHECK-NEXT: AND_S + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: DUP5_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.9 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.8: + ; CHECK-NEXT: successors: %bb.44(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.44 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.9: + ; CHECK-NEXT: successors: %bb.10(0x2a3677d4), %bb.11(0x55c9882c) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP2_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.11 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.10: + ; CHECK-NEXT: successors: %bb.7(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.7 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.11: + ; CHECK-NEXT: successors: %bb.12(0x3efbefbf), %bb.13(0x41041041) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.13 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.12: + ; CHECK-NEXT: successors: %bb.7(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: DUP7_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.7 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.13: + ; CHECK-NEXT: successors: %bb.14(0x04000001), %bb.15(0x7bffffff) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: DUP13_S + ; CHECK-NEXT: PUSH1_S i256 50 + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: PseudoJUMPI %bb.15 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.14: + ; CHECK-NEXT: successors: %bb.42(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP10_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: DUP2_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: PseudoJUMP %bb.42 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.15.remainder_join12885.preheader: + ; CHECK-NEXT: successors: %bb.16(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: DUP7_S + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP5_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.16.remainder_join12885: + ; CHECK-NEXT: successors: %bb.17(0x3efbefbf), %bb.18(0x41041041) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.18 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.17: + ; CHECK-NEXT: successors: %bb.39(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: SWAP5_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: DUP5_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: PseudoJUMP %bb.39 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.18.remainder_join12885: + ; CHECK-NEXT: successors: %bb.19(0x04000000), %bb.20(0x7c000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.20 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.19: + ; CHECK-NEXT: successors: %bb.7(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: DUP7_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: DUP10_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.7 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.20: + ; CHECK-NEXT: successors: %bb.21(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: SWAP6_S + ; CHECK-NEXT: SWAP7_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP5_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.21: + ; CHECK-NEXT: successors: %bb.22(0x3efbefbf), %bb.23(0x41041041) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.23 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.22: + ; CHECK-NEXT: successors: %bb.36(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: PseudoJUMP %bb.36 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.23: + ; CHECK-NEXT: successors: %bb.24(0x04000000), %bb.25(0x7c000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.25 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.24: + ; CHECK-NEXT: successors: %bb.42(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP12_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: DUP3_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.42 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.25.conditional_rt_74_join_block6475.preheader: + ; CHECK-NEXT: successors: %bb.26(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP12_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: DUP2_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.26.conditional_rt_74_join_block6475: + ; CHECK-NEXT: successors: %bb.27(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.27.conditional_rt_89_join_block7784: + ; CHECK-NEXT: successors: %bb.28(0x74b4b4b5), %bb.29(0x0b4b4b4b) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PseudoJUMPI %bb.29 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.28: + ; CHECK-NEXT: successors: %bb.27(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: PseudoJUMP %bb.27 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.29.conditional_rt_89_join_block7784: + ; CHECK-NEXT: successors: %bb.40(0x2aaaaaae), %bb.30(0x55555552) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMPI %bb.40 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.30.conditional_rt_89_join_block7784: + ; CHECK-NEXT: successors: %bb.31(0x40000008), %bb.45(0x3ffffff8) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: SWAP13_S + ; CHECK-NEXT: SWAP15_S + ; CHECK-NEXT: SWAP14_S + ; CHECK-NEXT: SWAP13_S + ; CHECK-NEXT: PUSH1_S i256 128 + ; CHECK-NEXT: MLOAD_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.45 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.31: + ; CHECK-NEXT: successors: %bb.33(0x04000000), %bb.32(0x7c000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: DUP15_S + ; CHECK-NEXT: SWAP16_S + ; CHECK-NEXT: SWAP14_S + ; CHECK-NEXT: SWAP15_S + ; CHECK-NEXT: DUP13_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: DUP6_S + ; CHECK-NEXT: PseudoJUMPI %bb.33 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.32: + ; CHECK-NEXT: successors: %bb.27(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: PseudoJUMP %bb.27 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.33: + ; CHECK-NEXT: successors: %bb.34(0x04000000), %bb.35(0x7c000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: EQ_S + ; CHECK-NEXT: PseudoJUMPI %bb.35 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.34: + ; CHECK-NEXT: successors: %bb.36(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP5_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.36 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.35: + ; CHECK-NEXT: successors: %bb.26(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: PseudoJUMP %bb.26 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.36: + ; CHECK-NEXT: successors: %bb.37(0x04000000), %bb.38(0x7c000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: EQ_S + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP5_S + ; CHECK-NEXT: PseudoJUMPI %bb.38 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.37: + ; CHECK-NEXT: successors: %bb.39(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: SWAP5_S + ; CHECK-NEXT: PseudoJUMP %bb.39 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.38: + ; CHECK-NEXT: successors: %bb.21(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: PseudoJUMP %bb.21 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.39: + ; CHECK-NEXT: successors: %bb.16(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: AND_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: DUP15_S + ; CHECK-NEXT: DUP9_S + ; CHECK-NEXT: SWAP6_S + ; CHECK-NEXT: PseudoJUMP %bb.16 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.40: + ; CHECK-NEXT: successors: %bb.42(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: SWAP11_S + ; CHECK-NEXT: PseudoJUMP %bb.42 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.41: + ; CHECK-NEXT: successors: %bb.42(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP11_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.42: + ; CHECK-NEXT: successors: %bb.1(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: OR_S + ; CHECK-NEXT: SWAP10_S + ; CHECK-NEXT: AND_S + ; CHECK-NEXT: PseudoJUMP %bb.1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.43: + ; CHECK-NEXT: successors: + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.44: + ; CHECK-NEXT: successors: %bb.3(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: OR_S + ; CHECK-NEXT: DUP4_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: DUP3_S + ; CHECK-NEXT: DUP8_S + ; CHECK-NEXT: PseudoJUMP %bb.3 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.45: + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoRET + bb.0.entry: + successors: %bb.1(0x80000000) + liveins: $arguments, $value_stack + + %11:gpr = ARGUMENT 0, implicit $arguments + %12:gpr = ARGUMENT 1, implicit $arguments + %13:gpr = ARGUMENT 2, implicit $arguments + %14:gpr = ARGUMENT 3, implicit $arguments + %18:gpr = CONST_I256 i256 255, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %17:gpr = AND %18, %14, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %65:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %20:gpr = EQ %17, %65, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %26:gpr = CONST_I256 i256 57896044618658097711785492504343953926634992332820282019728792003956564819967, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %25:gpr = AND %26, %11, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %27:gpr = CONST_I256 i256 4, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %28:gpr = EQ %25, %27, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %29:gpr = CONST_I256 i256 6, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %30:gpr = EQ %25, %29, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %64:gpr = CONST_I256 i256 255, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %41:gpr = AND %64, %13, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %43:gpr = CONST_I256 i256 8, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %44:gpr = EQ %41, %43, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %45:gpr = CONST_I256 i256 32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %46:gpr = EQ %41, %45, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %6:gpr = CONST_I256 i256 0, implicit-def dead $arguments + %60:gpr = CONST_I256 i256 0, implicit-def dead $arguments + + bb.1.conditional_rt_20_join_block: + successors: %bb.27(0x00000000), %bb.2(0x80000000) + liveins: $value_stack + + JUMP_UNLESS %bb.27, %60, implicit-def dead $arguments + + bb.2.conditional_rt_22_join_block: + successors: %bb.3(0x80000000) + liveins: $value_stack + + %8:gpr = CONST_I256 i256 0, implicit-def dead $arguments + + bb.3.conditional_rt_45_join_block2670: + successors: %bb.44(0x3efbefbf), %bb.4(0x41041041) + liveins: $value_stack + + JUMP_UNLESS %bb.4, %20, implicit-def $arguments + + bb.44: + successors: %bb.28(0x80000000) + liveins: $value_stack + + JUMP %bb.28, implicit-def $arguments + + bb.4.conditional_rt_45_join_block2670: + successors: %bb.25(0x04000000), %bb.33(0x7c000000) + liveins: $value_stack + + JUMP_UNLESS %bb.25, %17, implicit-def dead $arguments + + bb.33: + successors: %bb.5(0x80000000) + liveins: $value_stack + + bb.5.conditional_rt_52_join_block3336: + successors: %bb.45(0x04000000), %bb.6(0x7c000000) + liveins: $value_stack + + %66:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %23:gpr = AND %12, %66, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP_UNLESS %bb.6, %23, implicit-def $arguments + + bb.45: + successors: %bb.28(0x80000000) + liveins: $value_stack + + JUMP %bb.28, implicit-def $arguments + + bb.6: + successors: %bb.30(0x2a3677d4), %bb.7(0x55c9882c) + liveins: $value_stack + + JUMP_UNLESS %bb.7, %28, implicit-def $arguments + + bb.30: + successors: %bb.5(0x80000000) + liveins: $value_stack + + JUMP %bb.5, implicit-def $arguments + + bb.7: + successors: %bb.32(0x3efbefbf), %bb.8(0x41041041) + liveins: $value_stack + + JUMP_UNLESS %bb.8, %30, implicit-def $arguments + + bb.32: + successors: %bb.5(0x80000000) + liveins: $value_stack + + JUMP %bb.5, implicit-def $arguments + + bb.8: + successors: %bb.42(0x04000001), %bb.9(0x7bffffff) + liveins: $value_stack + + %24:gpr = CONST_I256 i256 50, implicit-def dead $arguments + JUMPI %bb.9, %25, implicit-def $arguments + + bb.42: + successors: %bb.26(0x80000000) + liveins: $value_stack + + JUMP %bb.26, implicit-def $arguments + + bb.9.remainder_join12885.preheader: + successors: %bb.10(0x80000000) + liveins: $value_stack + + %10:gpr = CONST_I256 i256 0, implicit-def dead $arguments + + bb.10.remainder_join12885: + successors: %bb.40(0x3efbefbf), %bb.11(0x41041041) + liveins: $value_stack + + %67:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP_UNLESS %bb.11, %67, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + + bb.40: + successors: %bb.23(0x80000000) + liveins: $value_stack + + JUMP %bb.23, implicit-def $arguments + + bb.11.remainder_join12885: + successors: %bb.31(0x04000000), %bb.12(0x7c000000) + liveins: $value_stack + + %68:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP_UNLESS %bb.12, %68, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + + bb.31: + successors: %bb.5(0x80000000) + liveins: $value_stack + + JUMP %bb.5, implicit-def $arguments + + bb.12: + successors: %bb.13(0x80000000) + liveins: $value_stack + + %2:gpr = CONST_I256 i256 1, implicit-def dead $arguments + + bb.13: + successors: %bb.38(0x3efbefbf), %bb.14(0x41041041) + liveins: $value_stack + + %69:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP_UNLESS %bb.14, %69, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + + bb.38: + successors: %bb.22(0x80000000) + liveins: $value_stack + + JUMP %bb.22, implicit-def $arguments + + bb.14: + successors: %bb.43(0x04000000), %bb.15(0x7c000000) + liveins: $value_stack + + %70:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP_UNLESS %bb.15, %70, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + + bb.43: + successors: %bb.26(0x80000000) + liveins: $value_stack + + JUMP %bb.26, implicit-def $arguments + + bb.15.conditional_rt_74_join_block6475.preheader: + successors: %bb.16(0x80000000) + liveins: $value_stack + + %62:gpr = CONST_I256 i256 1, implicit-def dead $arguments + + bb.16.conditional_rt_74_join_block6475: + successors: %bb.17(0x80000000) + liveins: $value_stack + + %9:gpr = COPY_I256 %62, implicit-def $arguments + + bb.17.conditional_rt_89_join_block7784: + successors: %bb.36(0x74b4b4b5), %bb.18(0x0b4b4b4b) + liveins: $value_stack + + JUMPI %bb.18, %41, implicit-def $arguments + + bb.36: + successors: %bb.17(0x80000000) + liveins: $value_stack + + JUMP %bb.17, implicit-def $arguments + + bb.18.conditional_rt_89_join_block7784: + successors: %bb.24(0x2aaaaaae), %bb.19(0x55555552) + liveins: $value_stack + + JUMPI %bb.24, %44, implicit-def dead $arguments + + bb.19.conditional_rt_89_join_block7784: + successors: %bb.20(0x40000008), %bb.29(0x3ffffff8) + liveins: $value_stack + + JUMP_UNLESS %bb.29, %46, implicit-def $arguments + + bb.20: + successors: %bb.21(0x04000000), %bb.37(0x7c000000) + liveins: $value_stack + + JUMPI %bb.21, %23, implicit-def $arguments + + bb.37: + successors: %bb.17(0x80000000) + liveins: $value_stack + + JUMP %bb.17, implicit-def $arguments + + bb.21: + successors: %bb.39(0x04000000), %bb.35(0x7c000000) + liveins: $value_stack + + %62:gpr = CONST_I256 i256 0, implicit-def dead $arguments + %71:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %52:gpr = EQ %9, %71, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMPI %bb.35, %52, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + + bb.39: + successors: %bb.22(0x80000000) + liveins: $value_stack + + JUMP %bb.22, implicit-def $arguments + + bb.35: + successors: %bb.16(0x80000000) + liveins: $value_stack + + JUMP %bb.16, implicit-def $arguments + + bb.22: + successors: %bb.41(0x04000000), %bb.34(0x7c000000) + liveins: $value_stack + + %53:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %54:gpr = EQ %2, %53, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %2:gpr = CONST_I256 i256 0, implicit-def dead $arguments + JUMPI %bb.34, %54, implicit-def $arguments + + bb.41: + successors: %bb.23(0x80000000) + liveins: $value_stack + + JUMP %bb.23, implicit-def $arguments + + bb.34: + successors: %bb.13(0x80000000) + liveins: $value_stack + + JUMP %bb.13, implicit-def $arguments + + bb.23: + successors: %bb.10(0x80000000) + liveins: $value_stack + + %72:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %10:gpr = AND %10, %72, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP %bb.10, implicit-def $arguments + + bb.24: + successors: %bb.26(0x80000000) + liveins: $value_stack + + %24:gpr = CONST_I256 i256 1, implicit-def dead $arguments + JUMP %bb.26, implicit-def $arguments + + bb.25: + successors: %bb.26(0x80000000) + liveins: $value_stack + + %24:gpr = CONST_I256 i256 0, implicit-def dead $arguments + + bb.26: + successors: %bb.1(0x80000000) + liveins: $value_stack + + %74:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %6:gpr = OR %6, %74, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %73:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %60:gpr = AND %24, %73, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP %bb.1, implicit-def $arguments + + bb.27: + successors: + liveins: $value_stack + + bb.28: + successors: %bb.3(0x80000000) + liveins: $value_stack + + %75:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %8:gpr = OR %8, %75, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP %bb.3, implicit-def $arguments + + bb.29: + liveins: $value_stack + + RET implicit-def dead $arguments + +... diff --git a/llvm/test/CodeGen/EVM/bps-spills-2.mir b/llvm/test/CodeGen/EVM/bps-spills-2.mir new file mode 100644 index 000000000000..2b80f72a8f8a --- /dev/null +++ b/llvm/test/CodeGen/EVM/bps-spills-2.mir @@ -0,0 +1,486 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5 +# RUN: llc -x mir -run-pass=evm-backward-propagation-stackification -run-pass=evm-finalize-stack-frames -evm-stack-region-offset=128 -evm-stack-region-size=128 < %s | FileCheck %s + +--- | + source_filename = "reduced.ll" + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm-unknown-unknown" + + declare void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(2) noalias nocapture readonly, i256, i1 immarg) #0 + + define fastcc void @main(i256 %calldata_load_result2781, i256 %addition_result2798, i256 %shift_left_non_overflow_result3390, i256 %calldata_load_result1899, i256 %calldata_load_result3245, i1 %comparison_result3399.not, i256 %stack_var_012.36954, i256 %calldata_load_result2605, i1 %comparison_result4030.not) { + entry: + br label %conditional_rt_187_join_block + + "block_rt_2/0": ; preds = %conditional_rt_187_join_block, %conditional_rt_181_join_block, %"block_rt_165/1" + unreachable + + "block_rt_160/3": ; preds = %"block_rt_165/1" + %calldatacopy_destination_pointer3688 = inttoptr i256 %stack_var_021.0.in6947 to ptr addrspace(1) + %calldatacopy_source_pointer3689 = inttoptr i256 %addition_result3756 to ptr addrspace(2) + tail call void @llvm.evm.memcpyas1as2(ptr addrspace(1) %calldatacopy_destination_pointer3688, ptr addrspace(2) %calldatacopy_source_pointer3689, i256 %shift_left_non_overflow_result3390, i1 false) + br i1 %comparison_result3909.not, label %conditional_rt_181_join_block, label %"block_rt_181/0" + + "block_rt_165/1": ; preds = %conditional_rt_181_join_block + %addition_result3756 = or i256 %calldata_load_result1899, %addition_result4054 + br i1 %comparison_result3399.not, label %"block_rt_2/0", label %"block_rt_160/3" + + "block_rt_181/0": ; preds = %"block_rt_160/3" + %addition_result4064 = or i256 %stack_var_011.36953, 1 + %comparison_result4003.not = icmp ult i256 %stack_var_013.36955, %calldata_load_result2605 + br i1 %comparison_result4003.not, label %conditional_rt_187_join_block, label %"block_rt_187/0.loopexit" + + "block_rt_187/0.loopexit": ; preds = %"block_rt_181/0" + store i256 %stack_var_021.06950, ptr addrspace(1) null, align 64 + unreachable + + "block_rt_188/0": ; preds = %conditional_rt_187_join_block + %addition_result4054 = or i256 %stack_var_012.36954, 1 + %addition_result1909 = or i256 %calldata_load_result3245, 1 + br label %conditional_rt_181_join_block + + conditional_rt_181_join_block: ; preds = %"block_rt_188/0", %"block_rt_160/3" + %stack_var_021.06950 = phi i256 [ 0, %"block_rt_188/0" ], [ %addition_result2798, %"block_rt_160/3" ] + %comparison_result3909.not = phi i1 [ true, %"block_rt_188/0" ], [ false, %"block_rt_160/3" ] + %stack_var_021.0.in6947 = phi i256 [ 0, %"block_rt_188/0" ], [ %addition_result2798, %"block_rt_160/3" ] + store i256 %calldata_load_result2781, ptr addrspace(1) null, align 1 + %comparison_result1913.not = icmp slt i256 0, %addition_result1909 + br i1 %comparison_result1913.not, label %"block_rt_165/1", label %"block_rt_2/0" + + conditional_rt_187_join_block: ; preds = %"block_rt_181/0", %entry + %stack_var_013.36955 = phi i256 [ 0, %entry ], [ 1, %"block_rt_181/0" ] + %stack_var_011.36953 = phi i256 [ 0, %entry ], [ %addition_result4064, %"block_rt_181/0" ] + br i1 %comparison_result4030.not, label %"block_rt_188/0", label %"block_rt_2/0" + } + + declare void @llvm.evm.memcpyas1as2(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(2) noalias nocapture readonly, i256, i1 immarg) #0 + + attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } + +... +--- +name: main +alignment: 1 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gpr, preferred-register: '' } + - { id: 1, class: gpr, preferred-register: '' } + - { id: 2, class: gpr, preferred-register: '' } + - { id: 3, class: gpr, preferred-register: '' } + - { id: 4, class: gpr, preferred-register: '' } + - { id: 5, class: gpr, preferred-register: '' } + - { id: 6, class: gpr, preferred-register: '' } + - { id: 7, class: gpr, preferred-register: '' } + - { id: 8, class: gpr, preferred-register: '' } + - { id: 9, class: gpr, preferred-register: '' } + - { id: 10, class: gpr, preferred-register: '' } + - { id: 11, class: gpr, preferred-register: '' } + - { id: 12, class: gpr, preferred-register: '' } + - { id: 13, class: gpr, preferred-register: '' } + - { id: 14, class: gpr, preferred-register: '' } + - { id: 15, class: gpr, preferred-register: '' } + - { id: 16, class: gpr, preferred-register: '' } + - { id: 17, class: gpr, preferred-register: '' } + - { id: 18, class: gpr, preferred-register: '' } + - { id: 19, class: gpr, preferred-register: '' } + - { id: 20, class: gpr, preferred-register: '' } + - { id: 21, class: gpr, preferred-register: '' } + - { id: 22, class: gpr, preferred-register: '' } + - { id: 23, class: gpr, preferred-register: '' } + - { id: 24, class: gpr, preferred-register: '' } + - { id: 25, class: gpr, preferred-register: '' } + - { id: 26, class: gpr, preferred-register: '' } + - { id: 27, class: gpr, preferred-register: '' } + - { id: 28, class: gpr, preferred-register: '' } + - { id: 29, class: gpr, preferred-register: '' } + - { id: 30, class: gpr, preferred-register: '' } + - { id: 31, class: gpr, preferred-register: '' } + - { id: 32, class: gpr, preferred-register: '' } + - { id: 33, class: gpr, preferred-register: '' } + - { id: 34, class: gpr, preferred-register: '' } + - { id: 35, class: gpr, preferred-register: '' } + - { id: 36, class: gpr, preferred-register: '' } + - { id: 37, class: gpr, preferred-register: '' } + - { id: 38, class: gpr, preferred-register: '' } + - { id: 39, class: gpr, preferred-register: '' } + - { id: 40, class: gpr, preferred-register: '' } + - { id: 41, class: gpr, preferred-register: '' } + - { id: 42, class: gpr, preferred-register: '' } + - { id: 43, class: gpr, preferred-register: '' } + - { id: 44, class: gpr, preferred-register: '' } + - { id: 45, class: gpr, preferred-register: '' } +liveins: + - { reg: '$arguments', virtual-reg: '' } + - { reg: '$value_stack', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + isStackified: false + numberOfParameters: 9 + hasPushDeployAddress: false +body: | + ; CHECK-LABEL: name: main + ; CHECK: bb.0.entry: + ; CHECK-NEXT: successors: %bb.1(0x80000000) + ; CHECK-NEXT: liveins: $arguments, $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP4_S + ; CHECK-NEXT: PUSH1_S i256 128 + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: SWAP9_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP5_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: SWAP6_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: AND_S + ; CHECK-NEXT: SWAP5_S + ; CHECK-NEXT: DUP6_S + ; CHECK-NEXT: SWAP5_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: AND_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP8_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP7_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.1.conditional_rt_187_join_block: + ; CHECK-NEXT: successors: %bb.11(0x80000000), %bb.2(0x00000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PseudoJUMPI %bb.11 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.2: + ; CHECK-NEXT: successors: %bb.3(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.3: + ; CHECK-NEXT: successors: + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.4: + ; CHECK-NEXT: successors: %bb.5(0x00000000), %bb.6(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP8_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.6 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.5: + ; CHECK-NEXT: successors: %bb.3(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.3 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.6: + ; CHECK-NEXT: successors: %bb.7(0x7c000000), %bb.8(0x04000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP7_S + ; CHECK-NEXT: DUP5_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: CALLDATACOPY_S + ; CHECK-NEXT: AND_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: DUP10_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.8 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.7: + ; CHECK-NEXT: successors: %bb.12(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP14_S + ; CHECK-NEXT: SWAP15_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.12 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.8: + ; CHECK-NEXT: successors: %bb.9(0x80000000), %bb.10(0x00000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: DUP7_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: ULT_S + ; CHECK-NEXT: SWAP8_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: SWAP9_S + ; CHECK-NEXT: OR_S + ; CHECK-NEXT: SWAP9_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.10 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.9: + ; CHECK-NEXT: successors: %bb.1(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: SWAP9_S + ; CHECK-NEXT: SWAP8_S + ; CHECK-NEXT: SWAP10_S + ; CHECK-NEXT: SWAP11_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.10: + ; CHECK-NEXT: successors: + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.11: + ; CHECK-NEXT: successors: %bb.12(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP10_S + ; CHECK-NEXT: SWAP9_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP9_S + ; CHECK-NEXT: OR_S + ; CHECK-NEXT: GT_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP6_S + ; CHECK-NEXT: OR_S + ; CHECK-NEXT: PUSH1_S i256 128 + ; CHECK-NEXT: MLOAD_S + ; CHECK-NEXT: OR_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.12.conditional_rt_181_join_block: + ; CHECK-NEXT: successors: %bb.4(0x80000000), %bb.13(0x00000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP3_S + ; CHECK-NEXT: DUP3_S + ; CHECK-NEXT: SWAP16_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: PseudoJUMPI %bb.4 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.13: + ; CHECK-NEXT: successors: %bb.3(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.3 + bb.0.entry: + successors: %bb.1(0x80000000) + liveins: $arguments, $value_stack + + %9:gpr = ARGUMENT 0, implicit $arguments + %10:gpr = ARGUMENT 1, implicit $arguments + %11:gpr = ARGUMENT 2, implicit $arguments + %12:gpr = ARGUMENT 3, implicit $arguments + %13:gpr = ARGUMENT 4, implicit $arguments + %14:gpr = ARGUMENT 5, implicit $arguments + %15:gpr = ARGUMENT 6, implicit $arguments + %16:gpr = ARGUMENT 7, implicit $arguments + %17:gpr = ARGUMENT 8, implicit $arguments + %39:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %20:gpr = AND %39, %17, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %38:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %26:gpr = AND %38, %14, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %7:gpr = CONST_I256 i256 0, implicit-def dead $arguments + %8:gpr = CONST_I256 i256 0, implicit-def dead $arguments + + bb.1.conditional_rt_187_join_block: + successors: %bb.7(0x80000000), %bb.10(0x00000000) + liveins: $value_stack + + JUMPI %bb.7, %20, implicit-def $arguments + + bb.10: + successors: %bb.2(0x80000000) + liveins: $value_stack + + bb.2: + successors: + liveins: $value_stack + + bb.3: + successors: %bb.12(0x00000000), %bb.4(0x80000000) + liveins: $value_stack + + JUMP_UNLESS %bb.4, %26, implicit-def $arguments + + bb.12: + successors: %bb.2(0x80000000) + liveins: $value_stack + + JUMP %bb.2, implicit-def $arguments + + bb.4: + successors: %bb.13(0x7c000000), %bb.5(0x04000000) + liveins: $value_stack + + CALLDATACOPY %6, %0, %11, implicit-def dead $arguments + %40:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %29:gpr = AND %5, %40, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %5:gpr = CONST_I256 i256 0, implicit-def dead $arguments + %6:gpr = COPY_I256 %10, implicit-def $arguments + JUMP_UNLESS %bb.5, %29, implicit-def $arguments + + bb.13: + successors: %bb.8(0x80000000) + liveins: $value_stack + + JUMP %bb.8, implicit-def $arguments + + bb.5: + successors: %bb.9(0x80000000), %bb.6(0x00000000) + liveins: $value_stack + + %31:gpr = ULT %7, %16, implicit-def dead $arguments + %7:gpr = CONST_I256 i256 1, implicit-def dead $arguments + %41:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %8:gpr = OR %8, %41, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP_UNLESS %bb.6, %31, implicit-def $arguments + + bb.9: + successors: %bb.1(0x80000000) + liveins: $value_stack + + JUMP %bb.1, implicit-def $arguments + + bb.6: + successors: + liveins: $value_stack + + %32:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + MSTORE %32, %4, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into `ptr addrspace(1) null`, align 64, addrspace 1) + + bb.7: + successors: %bb.8(0x80000000) + liveins: $value_stack + + %5:gpr = CONST_I256 i256 1, implicit-def dead $arguments + %6:gpr = CONST_I256 i256 0, implicit-def dead $arguments + %44:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %43:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %3:gpr = OR %13, %43, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %24:gpr = GT %3, %44, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %42:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %2:gpr = OR %15, %42, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %0:gpr = OR %12, %2, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + + bb.8.conditional_rt_181_join_block: + successors: %bb.3(0x80000000), %bb.11(0x00000000) + liveins: $value_stack + + %4:gpr = COPY_I256 %6, implicit-def $arguments + %45:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + MSTORE %45, %9, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into `ptr addrspace(1) null`, align 1, addrspace 1) + JUMPI %bb.3, %24, implicit-def $arguments + + bb.11: + successors: %bb.2(0x80000000) + liveins: $value_stack + + JUMP %bb.2, implicit-def $arguments + +... diff --git a/llvm/test/CodeGen/EVM/bps-stack-solver-bug.ll b/llvm/test/CodeGen/EVM/bps-stack-solver-bug.ll new file mode 100644 index 000000000000..5e6ed84bea78 --- /dev/null +++ b/llvm/test/CodeGen/EVM/bps-stack-solver-bug.ll @@ -0,0 +1,179 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s + +; calldata_struct_array_reencode test reduced with bugpoint. +; before the fix the test caused stack too deep failure because of unreachable +; slots mishandling in EVMStackSolver. +source_filename = "era-solidity/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_array_reencode.sol:C.runtime" +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +; Function Attrs: nounwind willreturn memory(none) +declare i256 @llvm.evm.calldatasize() #0 + +; Function Attrs: nounwind willreturn memory(none) +declare i256 @llvm.evm.callvalue() #0 + +; Function Attrs: noreturn nounwind +declare void @llvm.evm.return(ptr addrspace(1), i256) #1 + +; Function Attrs: noreturn nounwind +declare void @llvm.evm.revert(ptr addrspace(1), i256) #1 + +; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) +declare void @llvm.memmove.p1.p1.i256(ptr addrspace(1) nocapture writeonly, ptr addrspace(1) nocapture readonly, i256, i1 immarg) #2 + +; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) +declare void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(2) noalias nocapture readonly, i256, i1 immarg) #2 + +; Function Attrs: null_pointer_is_valid +define dso_local fastcc void @main() unnamed_addr #3 { +entry: + br i1 poison, label %"block_rt_7/0", label %"block_rt_2/0" + +"block_rt_2/0": ; preds = %conditional_rt_181_join_block, %"block_rt_165/1", %entry + unreachable + +"block_rt_7/0": ; preds = %entry + %calldata_load_result2781 = load i256, ptr addrspace(2) inttoptr (i256 4 to ptr addrspace(2)), align 4 + %calldata_load_result2605 = load i256, ptr addrspace(2) poison, align 1 + %addition_result2659 = add nuw nsw i256 %calldata_load_result2781, 36 + %subtraction_result1906 = add i256 0, -31 + br label %conditional_rt_187_join_block + +"block_rt_165/1": ; preds = %conditional_rt_181_join_block + %addition_result3756 = add i256 0, %addition_result4054 + %addition_result3239 = add i256 0, %addition_result3756 + %addition_result3251 = add i256 %addition_result3239, 32 + %comparison_result3399.not = icmp sgt i256 %addition_result3251, poison + br i1 %comparison_result3399.not, label %"block_rt_2/0", label %shift_left_join3672 + +"block_rt_181/0": ; preds = %shift_left_join3672 + %addition_result4058 = add i256 %stack_var_012.36954, 32 + %addition_result4064 = add i256 %stack_var_011.36953, 32 + %addition_result4044 = add nuw i256 %stack_var_013.36955, 1 + %comparison_result4003.not = icmp ult i256 %addition_result4044, %calldata_load_result2605 + br i1 %comparison_result4003.not, label %conditional_rt_187_join_block, label %"block_rt_187/0.loopexit" + +"block_rt_187/0.loopexit": ; preds = %"block_rt_181/0" + unreachable + +shift_left_join3672: ; preds = %"block_rt_165/1" + %addition_result3684 = add i256 %stack_var_021.0.in6947, 128 + %calldatacopy_destination_pointer3688 = inttoptr i256 %addition_result3684 to ptr addrspace(1) + tail call void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) align 1 %calldatacopy_destination_pointer3688, ptr addrspace(2) align 1 poison, i256 poison, i1 false) + %addition_result3694 = add i256 poison, %stack_var_021.06950 + %addition_result3972 = add i256 %stack_var_022.06948, 32 + %addition_result3978 = add i256 %stack_var_017.26946, 32 + %stack_var_021.0 = add i256 %addition_result3694, 64 + br i1 %comparison_result3909.not, label %conditional_rt_181_join_block, label %"block_rt_181/0" + +conditional_rt_181_join_block: ; preds = %conditional_rt_187_join_block, %shift_left_join3672 + %stack_var_021.06950 = phi i256 [ poison, %conditional_rt_187_join_block ], [ %stack_var_021.0, %shift_left_join3672 ] + %comparison_result3909.not = phi i1 [ true, %conditional_rt_187_join_block ], [ false, %shift_left_join3672 ] + %stack_var_022.06948 = phi i256 [ %addition_result4054, %conditional_rt_187_join_block ], [ %addition_result3972, %shift_left_join3672 ] + %stack_var_021.0.in6947 = phi i256 [ %stack_var_010.26952, %conditional_rt_187_join_block ], [ %addition_result3694, %shift_left_join3672 ] + %stack_var_017.26946 = phi i256 [ %stack_var_010.26952, %conditional_rt_187_join_block ], [ %addition_result3978, %shift_left_join3672 ] + %subtraction_result3918 = sub i256 %stack_var_021.06950, %stack_var_010.26952 + %memory_store_pointer3922 = inttoptr i256 %stack_var_017.26946 to ptr addrspace(1) + store i256 %subtraction_result3918, ptr addrspace(1) %memory_store_pointer3922, align 1 + %calldataload_pointer1898 = inttoptr i256 %stack_var_022.06948 to ptr addrspace(2) + %comparison_result1913.not = icmp slt i256 0, %addition_result1909 + br i1 %comparison_result1913.not, label %"block_rt_165/1", label %"block_rt_2/0" + +conditional_rt_187_join_block: ; preds = %"block_rt_181/0", %"block_rt_7/0" + %stack_var_013.36955 = phi i256 [ 0, %"block_rt_7/0" ], [ %addition_result4044, %"block_rt_181/0" ] + %stack_var_012.36954 = phi i256 [ %addition_result2659, %"block_rt_7/0" ], [ %addition_result4058, %"block_rt_181/0" ] + %stack_var_011.36953 = phi i256 [ 224, %"block_rt_7/0" ], [ %addition_result4064, %"block_rt_181/0" ] + %stack_var_010.26952 = phi i256 [ poison, %"block_rt_7/0" ], [ %stack_var_021.0, %"block_rt_181/0" ] + %memory_store_pointer4021 = inttoptr i256 %stack_var_011.36953 to ptr addrspace(1) + %calldataload_pointer4024 = inttoptr i256 %stack_var_012.36954 to ptr addrspace(2) + %calldata_load_result4025 = load i256, ptr addrspace(2) %calldataload_pointer4024, align 1 + %addition_result4054 = add i256 %calldata_load_result4025, %addition_result2659 + %addition_result1909 = sub i256 %subtraction_result1906, %addition_result4054 + br label %conditional_rt_181_join_block +} + +; Function Attrs: null_pointer_is_valid +define dso_local fastcc void @main2() unnamed_addr #3 { +entry: + switch i32 poison, label %"block_rt_2/0" [ + i32 1931402874, label %"block_rt_7/0" + ] + +"block_rt_2/0": ; preds = %conditional_rt_181_join_block, %"block_rt_165/1", %entry + unreachable + +"block_rt_7/0": ; preds = %entry + %calldata_load_result2781 = load i256, ptr addrspace(2) inttoptr (i256 4 to ptr addrspace(2)), align 4 + %addition_result2798 = add nuw nsw i256 %calldata_load_result2781, 4 + %calldataload_pointer2604 = inttoptr i256 %addition_result2798 to ptr addrspace(2) + %calldata_load_result2605 = load i256, ptr addrspace(2) %calldataload_pointer2604, align 1 + %addition_result2659 = add nuw nsw i256 %calldata_load_result2781, 36 + %subtraction_result1906 = add i256 0, -31 + br label %conditional_rt_187_join_block + +"block_rt_165/1": ; preds = %conditional_rt_181_join_block + %addition_result3756 = add i256 %calldata_load_result1899, %addition_result4054 + %addition_result3239 = add i256 0, %addition_result3756 + %calldataload_pointer3244 = inttoptr i256 %addition_result3239 to ptr addrspace(2) + %calldata_load_result3245 = load i256, ptr addrspace(2) %calldataload_pointer3244, align 1 + %addition_result3251 = add i256 %addition_result3239, 32 + %shift_left_non_overflow_result3390 = shl nuw nsw i256 %calldata_load_result3245, 5 + %comparison_result3399.not = icmp sgt i256 %addition_result3251, poison + br i1 %comparison_result3399.not, label %"block_rt_2/0", label %shift_left_join3672 + +"block_rt_181/0": ; preds = %shift_left_join3672 + %addition_result4064 = add i256 %stack_var_011.36953, 32 + %addition_result4044 = add nuw i256 %stack_var_013.36955, 1 + %comparison_result4003.not = icmp ult i256 %addition_result4044, %calldata_load_result2605 + br i1 %comparison_result4003.not, label %conditional_rt_187_join_block, label %"block_rt_187/0.loopexit" + +"block_rt_187/0.loopexit": ; preds = %"block_rt_181/0" + %addition_result439 = add i256 %stack_var_021.0, 32 + %memory_store_pointer442 = inttoptr i256 %addition_result439 to ptr addrspace(1) + store i256 0, ptr addrspace(1) %memory_store_pointer442, align 1 + unreachable + +shift_left_join3672: ; preds = %"block_rt_165/1" + %addition_result3535 = add i256 %stack_var_021.0.in6947, 96 + %memory_store_pointer3538 = inttoptr i256 %addition_result3535 to ptr addrspace(1) + store i256 %calldata_load_result3245, ptr addrspace(1) %memory_store_pointer3538, align 1 + %addition_result3694 = add i256 %shift_left_non_overflow_result3390, %stack_var_021.06950 + %addition_result3972 = add i256 %stack_var_022.06948, 32 + %addition_result3978 = add i256 %stack_var_017.26946, 32 + %stack_var_021.0 = add i256 %addition_result3694, 64 + br i1 %comparison_result3909.not, label %conditional_rt_181_join_block, label %"block_rt_181/0" + +conditional_rt_181_join_block: ; preds = %conditional_rt_187_join_block, %shift_left_join3672 + %stack_var_021.06950 = phi i256 [ poison, %conditional_rt_187_join_block ], [ %stack_var_021.0, %shift_left_join3672 ] + %comparison_result3909.not = phi i1 [ true, %conditional_rt_187_join_block ], [ false, %shift_left_join3672 ] + %stack_var_022.06948 = phi i256 [ %addition_result4054, %conditional_rt_187_join_block ], [ %addition_result3972, %shift_left_join3672 ] + %stack_var_021.0.in6947 = phi i256 [ %stack_var_010.26952, %conditional_rt_187_join_block ], [ %addition_result3694, %shift_left_join3672 ] + %stack_var_017.26946 = phi i256 [ %stack_var_010.26952, %conditional_rt_187_join_block ], [ %addition_result3978, %shift_left_join3672 ] + %subtraction_result3918 = sub i256 %stack_var_021.06950, %stack_var_010.26952 + %memory_store_pointer3922 = inttoptr i256 %stack_var_017.26946 to ptr addrspace(1) + store i256 %subtraction_result3918, ptr addrspace(1) %memory_store_pointer3922, align 1 + %calldataload_pointer1898 = inttoptr i256 %stack_var_022.06948 to ptr addrspace(2) + %calldata_load_result1899 = load i256, ptr addrspace(2) %calldataload_pointer1898, align 1 + %comparison_result1913.not = icmp slt i256 %calldata_load_result1899, %addition_result1909 + br i1 %comparison_result1913.not, label %"block_rt_165/1", label %"block_rt_2/0" + +conditional_rt_187_join_block: ; preds = %"block_rt_181/0", %"block_rt_7/0" + %stack_var_013.36955 = phi i256 [ 0, %"block_rt_7/0" ], [ %addition_result4044, %"block_rt_181/0" ] + %stack_var_011.36953 = phi i256 [ 224, %"block_rt_7/0" ], [ %addition_result4064, %"block_rt_181/0" ] + %stack_var_010.26952 = phi i256 [ poison, %"block_rt_7/0" ], [ %stack_var_021.0, %"block_rt_181/0" ] + %memory_store_pointer4021 = inttoptr i256 %stack_var_011.36953 to ptr addrspace(1) + %calldata_load_result4025 = load i256, ptr addrspace(2) poison, align 1 + %addition_result4054 = add i256 %calldata_load_result4025, %addition_result2659 + %addition_result1909 = sub i256 %subtraction_result1906, %addition_result4054 + br label %conditional_rt_181_join_block +} + +; Function Attrs: null_pointer_is_valid +declare void @__entry() local_unnamed_addr #3 + +attributes #0 = { nounwind willreturn memory(none) } +attributes #1 = { noreturn nounwind } +attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } +attributes #3 = { null_pointer_is_valid } diff --git a/llvm/test/CodeGen/EVM/br.ll b/llvm/test/CodeGen/EVM/br.ll new file mode 100644 index 000000000000..49170eb40263 --- /dev/null +++ b/llvm/test/CodeGen/EVM/br.ll @@ -0,0 +1,98 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @diamond(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: diamond: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: SUB +; CHECK-NEXT: PUSH4 @.BB0_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true_bb +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: MUL +; CHECK-NEXT: PUSH4 @.BB0_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB0_2: ; %false_bb +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ADD +; CHECK-NEXT: .BB0_3: ; %end_bb +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %cmp = icmp eq i256 %rs1, %rs2 + br i1 %cmp, label %true_bb, label %false_bb + +true_bb: + %mul = mul i256 %rs1, %rs1 + br label %end_bb + +false_bb: + %add = add i256 %rs1, %rs2 + br label %end_bb + +end_bb: + %res = phi i256 [%mul, %true_bb], [%add, %false_bb] + ret i256 %res +} + + +define i256 @loop(i256 %p1) nounwind { +; CHECK-LABEL: loop: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: .BB1_1: ; %loop.cond +; CHECK-NEXT: ; =>This Inner Loop Header: Depth=1 +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: EQ +; CHECK-NEXT: PUSH4 @.BB1_3 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.2: ; %loop.body +; CHECK-NEXT: ; in Loop: Header=BB1_1 Depth=1 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP4 +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.BB1_1 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB1_3: ; %loop.exit +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: POP +; CHECK-NEXT: JUMP +entry: + br label %loop.cond + +loop.cond: + %i = phi i256 [0, %entry], [%i.next, %loop.body] + %res = phi i256 [0, %entry], [%res.next, %loop.body] + %cond = icmp ne i256 %i, %p1 + br i1 %cond, label %loop.body, label %loop.exit + +loop.body: + %i.next = add i256 %i, 1 + %res.next = add i256 %res, %i + br label %loop.cond + +loop.exit: + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll b/llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll new file mode 100644 index 000000000000..1d8d470c3ac7 --- /dev/null +++ b/llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll @@ -0,0 +1,56 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc -O3 < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @test(i256 %arg) { +; CHECK-LABEL: test: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: DUP2 +; CHECK-NEXT: SLT +; CHECK-NEXT: PUSH4 @.BB0_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: PUSH1 0x14 +; CHECK-NEXT: PUSH4 @.BB0_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB0_2: ; %bb1 +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: PUSH1 0xA +; CHECK-NEXT: .BB0_3: ; %bb2 +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SLT +; CHECK-NEXT: PUSH4 @.BB0_5 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.4: ; %bb3 +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH1 0x5 +; CHECK-NEXT: .BB0_5: ; %bb4 +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP +entry: + %cmp1 = icmp slt i256 %arg, 0 + br i1 %cmp1, label %bb1, label %bb2 + +bb1: + br label %bb2 + +bb2: + %phi1 = phi i256 [ 20, %entry ], [ 10, %bb1 ] + %cmp2 = icmp sgt i256 %arg, 0 + br i1 %cmp2, label %bb3, label %bb4 + +bb3: + br label %bb4 + +bb4: + %phi2 = phi i256 [ %phi1, %bb2 ], [ 5, %bb3 ] + ret i256 %phi2 +} diff --git a/llvm/test/CodeGen/EVM/brcond.ll b/llvm/test/CodeGen/EVM/brcond.ll new file mode 100644 index 000000000000..305942f28712 --- /dev/null +++ b/llvm/test/CodeGen/EVM/brcond.ll @@ -0,0 +1,453 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +; Two-value comparisons + +define void @br_eq(i256 %a, i256 %b) { +; CHECK-LABEL: br_eq: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: SUB +; CHECK-NEXT: PUSH4 @.BB0_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB0_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp eq i256 %a, %b + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_ne(i256 %a, i256 %b) { +; CHECK-LABEL: br_ne: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: EQ +; CHECK-NEXT: PUSH4 @.BB1_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB1_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp ne i256 %a, %b + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_ugt(i256 %a, i256 %b) { +; CHECK-LABEL: br_ugt: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: GT +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB2_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB2_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp ugt i256 %a, %b + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_uge(i256 %a, i256 %b) { +; CHECK-LABEL: br_uge: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: LT +; CHECK-NEXT: PUSH4 @.BB3_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB3_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp uge i256 %a, %b + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_ult(i256 %a, i256 %b) { +; CHECK-LABEL: br_ult: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: LT +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB4_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB4_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp ult i256 %a, %b + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_ule(i256 %a, i256 %b) { +; CHECK-LABEL: br_ule: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: GT +; CHECK-NEXT: PUSH4 @.BB5_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB5_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp ule i256 %a, %b + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_sgt(i256 %a, i256 %b) { +; CHECK-LABEL: br_sgt: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: SGT +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB6_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB6_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp sgt i256 %a, %b + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_sge(i256 %a, i256 %b) { +; CHECK-LABEL: br_sge: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: SLT +; CHECK-NEXT: PUSH4 @.BB7_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB7_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp sge i256 %a, %b + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_slt(i256 %a, i256 %b) { +; CHECK-LABEL: br_slt: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: SLT +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB8_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB8_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp slt i256 %a, %b + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_sle(i256 %a, i256 %b) { +; CHECK-LABEL: br_sle: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: SGT +; CHECK-NEXT: PUSH4 @.BB9_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB9_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp sle i256 %a, %b + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +; One-value comparisons with zero + +define void @br_eq_0(i256 %a) { +; CHECK-LABEL: br_eq_0: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH4 @.BB10_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB10_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp eq i256 %a, 0 + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_ne_0(i256 %a) { +; CHECK-LABEL: br_ne_0: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: POP +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB11_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB11_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp ne i256 %a, 0 + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_ugt_0(i256 %a) { +; CHECK-LABEL: br_ugt_0: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: POP +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB12_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB12_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp ugt i256 %a, 0 + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_uge_0(i256 %a) { +; CHECK-LABEL: br_uge_0: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: PUSH4 @.BB13_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB13_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp uge i256 %a, 0 + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_ult_0(i256 %a) { +; CHECK-LABEL: br_ult_0: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: PUSH4 @.BB14_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB14_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp ult i256 %a, 0 + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_ule_0(i256 %a) { +; CHECK-LABEL: br_ule_0: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH4 @.BB15_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB15_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp ule i256 %a, 0 + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_sgt_0(i256 %a) { +; CHECK-LABEL: br_sgt_0: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: SLT +; CHECK-NEXT: PUSH4 @.BB16_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB16_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp sgt i256 %a, 0 + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_sge_0(i256 %a) { +; CHECK-LABEL: br_sge_0: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: SLT +; CHECK-NEXT: PUSH4 @.BB17_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB17_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp sge i256 %a, 0 + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_slt_0(i256 %a) { +; CHECK-LABEL: br_slt_0: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: SLT +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB18_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB18_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp slt i256 %a, 0 + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_sle_0(i256 %a) { +; CHECK-LABEL: br_sle_0: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: SGT +; CHECK-NEXT: PUSH4 @.BB19_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB19_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp sle i256 %a, 0 + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_or(i256 %a, i256 %b, i256 %c) { +; CHECK-LABEL: br_or: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: POP +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: EQ +; CHECK-NEXT: ISZERO +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SUB +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH4 @.BB20_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %false +; CHECK-NEXT: .BB20_2: ; %true +; CHECK-NEXT: JUMPDEST + %cond1 = icmp ne i256 %a, %b + %cond2 = icmp ne i256 %a, %c + %cond = or i1 %cond1, %cond2 + br i1 %cond, label %true, label %false +false: + unreachable +true: + unreachable +} diff --git a/llvm/test/CodeGen/EVM/call.ll b/llvm/test/CodeGen/EVM/call.ll new file mode 100644 index 000000000000..b3d3b16256c9 --- /dev/null +++ b/llvm/test/CodeGen/EVM/call.ll @@ -0,0 +1,62 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @foo(i256) noinline { +; CHECK-LABEL: foo: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + ret i256 0 +} + +define void @foo2(i256) noinline { +; CHECK-LABEL: foo2: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: JUMP + ret void +} + +define i256 @call(i256 %a, i256 %b) nounwind { +; CHECK-LABEL: call: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH4 @.FUNC_RET0 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: ADD +; CHECK-NEXT: PUSH4 @foo +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %sum = add i256 %a, %b + %res = call i256 @foo(i256 %sum) + ret i256 %res +} + +define void @call2(i256 %a, i256 %b) nounwind { +; CHECK-LABEL: call2: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH4 @.FUNC_RET1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: ADD +; CHECK-NEXT: PUSH4 @foo2 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET1: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + + %sum = add i256 %a, %b + call void @foo2(i256 %sum) + ret void +} diff --git a/llvm/test/CodeGen/EVM/cgp-sink-addrmode.ll b/llvm/test/CodeGen/EVM/cgp-sink-addrmode.ll new file mode 100644 index 000000000000..31017d31a722 --- /dev/null +++ b/llvm/test/CodeGen/EVM/cgp-sink-addrmode.ll @@ -0,0 +1,157 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -S -passes='require,function(codegenprepare)' < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +@glob = external addrspace(1) global i256 + +define i256 @test_as1(i256 %arg) { +; CHECK-LABEL: define i256 @test_as1( +; CHECK-SAME: i256 [[ARG:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i256 [[ARG]], 10 +; CHECK-NEXT: br i1 [[CMP]], label %[[THEN:.*]], label %[[EXIT:.*]] +; CHECK: [[THEN]]: +; CHECK-NEXT: [[SUNKADDR:%.*]] = inttoptr i256 [[ARG]] to ptr addrspace(1) +; CHECK-NEXT: [[INTTOPTR:%.*]] = getelementptr i8, ptr addrspace(1) [[SUNKADDR]], i256 4 +; CHECK-NEXT: [[LOAD:%.*]] = load i256, ptr addrspace(1) [[INTTOPTR]], align 1 +; CHECK-NEXT: br label %[[EXIT]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: [[PHI:%.*]] = phi i256 [ [[LOAD]], %[[THEN]] ], [ 0, %[[ENTRY]] ] +; CHECK-NEXT: ret i256 [[PHI]] +; +entry: + %add = add i256 %arg, 4 + %cmp = icmp ugt i256 %arg, 10 + br i1 %cmp, label %then, label %exit + +then: + %inttoptr = inttoptr i256 %add to ptr addrspace(1) + %load = load i256, ptr addrspace(1) %inttoptr, align 1 + br label %exit + +exit: + %phi = phi i256 [ %load, %then ], [ 0, %entry ] + ret i256 %phi +} + +define i256 @test_as5(i256 %arg) { +; CHECK-LABEL: define i256 @test_as5( +; CHECK-SAME: i256 [[ARG:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i256 [[ARG]], 10 +; CHECK-NEXT: br i1 [[CMP]], label %[[THEN:.*]], label %[[EXIT:.*]] +; CHECK: [[THEN]]: +; CHECK-NEXT: [[SUNKADDR:%.*]] = inttoptr i256 [[ARG]] to ptr addrspace(5) +; CHECK-NEXT: [[INTTOPTR:%.*]] = getelementptr i8, ptr addrspace(5) [[SUNKADDR]], i256 4 +; CHECK-NEXT: [[LOAD:%.*]] = load i256, ptr addrspace(5) [[INTTOPTR]], align 1 +; CHECK-NEXT: br label %[[EXIT]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: [[PHI:%.*]] = phi i256 [ [[LOAD]], %[[THEN]] ], [ 0, %[[ENTRY]] ] +; CHECK-NEXT: ret i256 [[PHI]] +; +entry: + %add = add i256 %arg, 4 + %cmp = icmp ugt i256 %arg, 10 + br i1 %cmp, label %then, label %exit + +then: + %inttoptr = inttoptr i256 %add to ptr addrspace(5) + %load = load i256, ptr addrspace(5) %inttoptr, align 1 + br label %exit + +exit: + %phi = phi i256 [ %load, %then ], [ 0, %entry ] + ret i256 %phi +} + +define i256 @test_no_as2(i256 %arg) { +; CHECK-LABEL: define i256 @test_no_as2( +; CHECK-SAME: i256 [[ARG:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: [[ADD:%.*]] = add i256 [[ARG]], 4 +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i256 [[ARG]], 10 +; CHECK-NEXT: br i1 [[CMP]], label %[[THEN:.*]], label %[[EXIT:.*]] +; CHECK: [[THEN]]: +; CHECK-NEXT: [[INTTOPTR:%.*]] = inttoptr i256 [[ADD]] to ptr addrspace(2) +; CHECK-NEXT: [[LOAD:%.*]] = load i256, ptr addrspace(2) [[INTTOPTR]], align 1 +; CHECK-NEXT: br label %[[EXIT]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: [[PHI:%.*]] = phi i256 [ [[LOAD]], %[[THEN]] ], [ 0, %[[ENTRY]] ] +; CHECK-NEXT: ret i256 [[PHI]] +; +entry: + %add = add i256 %arg, 4 + %cmp = icmp ugt i256 %arg, 10 + br i1 %cmp, label %then, label %exit + +then: + %inttoptr = inttoptr i256 %add to ptr addrspace(2) + %load = load i256, ptr addrspace(2) %inttoptr, align 1 + br label %exit + +exit: + %phi = phi i256 [ %load, %then ], [ 0, %entry ] + ret i256 %phi +} + +define i256 @test_no_scale(i256 %arg) { +; CHECK-LABEL: define i256 @test_no_scale( +; CHECK-SAME: i256 [[ARG:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: [[MUL:%.*]] = mul i256 [[ARG]], 4 +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i256 [[ARG]], 10 +; CHECK-NEXT: br i1 [[CMP]], label %[[THEN:.*]], label %[[EXIT:.*]] +; CHECK: [[THEN]]: +; CHECK-NEXT: [[INTTOPTR:%.*]] = inttoptr i256 [[MUL]] to ptr addrspace(1) +; CHECK-NEXT: [[LOAD:%.*]] = load i256, ptr addrspace(1) [[INTTOPTR]], align 1 +; CHECK-NEXT: br label %[[EXIT]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: [[PHI:%.*]] = phi i256 [ [[LOAD]], %[[THEN]] ], [ 0, %[[ENTRY]] ] +; CHECK-NEXT: ret i256 [[PHI]] +; +entry: + %mul = mul i256 %arg, 4 + %cmp = icmp ugt i256 %arg, 10 + br i1 %cmp, label %then, label %exit + +then: + %inttoptr = inttoptr i256 %mul to ptr addrspace(1) + %load = load i256, ptr addrspace(1) %inttoptr, align 1 + br label %exit + +exit: + %phi = phi i256 [ %load, %then ], [ 0, %entry ] + ret i256 %phi +} + +define i256 @test_no_glob(i256 %arg) { +; CHECK-LABEL: define i256 @test_no_glob( +; CHECK-SAME: i256 [[ARG:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: [[LOAD1:%.*]] = load i256, ptr addrspace(1) @glob, align 64 +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i256 [[ARG]], 10 +; CHECK-NEXT: br i1 [[CMP]], label %[[THEN:.*]], label %[[EXIT:.*]] +; CHECK: [[THEN]]: +; CHECK-NEXT: [[INTTOPTR:%.*]] = inttoptr i256 [[LOAD1]] to ptr addrspace(1) +; CHECK-NEXT: [[LOAD2:%.*]] = load i256, ptr addrspace(1) [[INTTOPTR]], align 1 +; CHECK-NEXT: br label %[[EXIT]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: [[PHI:%.*]] = phi i256 [ [[LOAD2]], %[[THEN]] ], [ 0, %[[ENTRY]] ] +; CHECK-NEXT: ret i256 [[PHI]] +; +entry: + %load1 = load i256, ptr addrspace(1) @glob, align 64 + %cmp = icmp ugt i256 %arg, 10 + br i1 %cmp, label %then, label %exit + +then: + %inttoptr = inttoptr i256 %load1 to ptr addrspace(1) + %load2 = load i256, ptr addrspace(1) %inttoptr, align 1 + br label %exit + +exit: + %phi = phi i256 [ %load2, %then ], [ 0, %entry ] + ret i256 %phi +} diff --git a/llvm/test/CodeGen/EVM/constant-folding-earlycse-addmod.ll b/llvm/test/CodeGen/EVM/constant-folding-earlycse-addmod.ll new file mode 100644 index 000000000000..f62917139629 --- /dev/null +++ b/llvm/test/CodeGen/EVM/constant-folding-earlycse-addmod.ll @@ -0,0 +1,53 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -passes=early-cse -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare i256 @llvm.evm.addmod(i256, i256, i256) + +define i256 @test_addmod1() { +; CHECK-LABEL: define i256 @test_addmod1() { +; CHECK-NEXT: ret i256 1 +; + + ; Params are treated as unsigned values + %res = call i256 @llvm.evm.addmod(i256 -1, i256 2, i256 2) + ret i256 %res +} + +define i256 @test_addmod2() { +; CHECK-LABEL: define i256 @test_addmod2() { +; CHECK-NEXT: ret i256 4 +; + + %res = call i256 @llvm.evm.addmod(i256 48, i256 1, i256 5) + ret i256 %res +} + +define i256 @test_addmod3() { +; CHECK-LABEL: define i256 @test_addmod3() { +; CHECK-NEXT: ret i256 poison +; + + %res = call i256 @llvm.evm.addmod(i256 48, i256 undef, i256 5) + ret i256 %res +} + +define i256 @test_addmod4() { +; CHECK-LABEL: define i256 @test_addmod4() { +; CHECK-NEXT: ret i256 poison +; + + %res = call i256 @llvm.evm.addmod(i256 undef, i256 48, i256 5) + ret i256 %res +} + +define i256 @test_addmod5() { +; CHECK-LABEL: define i256 @test_addmod5() { +; CHECK-NEXT: ret i256 poison +; + + %res = call i256 @llvm.evm.addmod(i256 48, i256 1, i256 undef) + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/constant-folding-earlycse-byte.ll b/llvm/test/CodeGen/EVM/constant-folding-earlycse-byte.ll new file mode 100644 index 000000000000..3b79d6d241af --- /dev/null +++ b/llvm/test/CodeGen/EVM/constant-folding-earlycse-byte.ll @@ -0,0 +1,349 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -passes=early-cse -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare i256 @llvm.evm.byte(i256, i256) + +define i256 @test_byte1() { +; CHECK-LABEL: define i256 @test_byte1() { +; CHECK-NEXT: ret i256 poison +; + + %res = call i256 @llvm.evm.byte(i256 undef, i256 1) + ret i256 %res +} + +define i256 @test_byte2() { +; CHECK-LABEL: define i256 @test_byte2() { +; CHECK-NEXT: ret i256 poison +; + + %res = call i256 @llvm.evm.byte(i256 1, i256 undef) + ret i256 %res +} + +define i256 @test_byte3() { +; CHECK-LABEL: define i256 @test_byte3() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.byte(i256 0, i256 0) + ret i256 %res +} + +define i256 @test_byte4() { +; CHECK-LABEL: define i256 @test_byte4() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.byte(i256 0, i256 1) + ret i256 %res +} + +define i256 @test_byte5() { +; CHECK-LABEL: define i256 @test_byte5() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.byte(i256 0, i256 62786337629547936342664354281295019512044052096983040078175507080572122364) + ret i256 %res +} + +define i256 @test_byte6() { +; CHECK-LABEL: define i256 @test_byte6() { +; CHECK-NEXT: ret i256 83 +; + + %res = call i256 @llvm.evm.byte(i256 0, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) + ret i256 %res +} + +define i256 @test_byte7() { +; CHECK-LABEL: define i256 @test_byte7() { +; CHECK-NEXT: ret i256 255 +; + + %res = call i256 @llvm.evm.byte(i256 0, i256 -213508454229078891452382036238048110874681386347114622284045643289719458749) + ret i256 %res +} + +define i256 @test_byte8() { +; CHECK-LABEL: define i256 @test_byte8() { +; CHECK-NEXT: ret i256 255 +; + + %res = call i256 @llvm.evm.byte(i256 0, i256 -1) + ret i256 %res +} + +define i256 @test_byte9() { +; CHECK-LABEL: define i256 @test_byte9() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.byte(i256 1, i256 0) + ret i256 %res +} + +define i256 @test_byte10() { +; CHECK-LABEL: define i256 @test_byte10() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.byte(i256 1, i256 1) + ret i256 %res +} + +define i256 @test_byte11() { +; CHECK-LABEL: define i256 @test_byte11() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.byte(i256 1, i256 15831896390776628077873594548411842773272337831711882241313510853617203623164) + ret i256 %res +} + +define i256 @test_byte12() { +; CHECK-LABEL: define i256 @test_byte12() { +; CHECK-NEXT: ret i256 72 +; + + %res = call i256 @llvm.evm.byte(i256 1, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) + ret i256 %res +} + +define i256 @test_byte13() { +; CHECK-LABEL: define i256 @test_byte13() { +; CHECK-NEXT: ret i256 255 +; + + %res = call i256 @llvm.evm.byte(i256 1, i256 -54279028636447639376701285558971354695195688630741054740805195402843884604349) + ret i256 %res +} + +define i256 @test_byte14() { +; CHECK-LABEL: define i256 @test_byte14() { +; CHECK-NEXT: ret i256 255 +; + + %res = call i256 @llvm.evm.byte(i256 1, i256 -1) + ret i256 %res +} + +define i256 @test_byte15() { +; CHECK-LABEL: define i256 @test_byte15() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.byte(i256 17, i256 0) + ret i256 %res +} + +define i256 @test_byte16() { +; CHECK-LABEL: define i256 @test_byte16() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.byte(i256 17, i256 1) + ret i256 %res +} + +define i256 @test_byte17() { +; CHECK-LABEL: define i256 @test_byte17() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.byte(i256 17, i256 16073302433164271703722074696011524995083260326051756269332189763149975074044) + ret i256 %res +} + +define i256 @test_byte18() { +; CHECK-LABEL: define i256 @test_byte18() { +; CHECK-NEXT: ret i256 205 +; + + %res = call i256 @llvm.evm.byte(i256 17, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) + ret i256 %res +} + +define i256 @test_byte19() { +; CHECK-LABEL: define i256 @test_byte19() { +; CHECK-NEXT: ret i256 255 +; + + %res = call i256 @llvm.evm.byte(i256 17, i256 -54658164282644196211809801276940316383917872613704572060086920199132892875709) + ret i256 %res +} + +define i256 @test_byte20() { +; CHECK-LABEL: define i256 @test_byte20() { +; CHECK-NEXT: ret i256 255 +; + + %res = call i256 @llvm.evm.byte(i256 17, i256 -1) + ret i256 %res +} + +define i256 @test_byte21() { +; CHECK-LABEL: define i256 @test_byte21() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.byte(i256 31, i256 0) + ret i256 %res +} + +define i256 @test_byte22() { +; CHECK-LABEL: define i256 @test_byte22() { +; CHECK-NEXT: ret i256 1 +; + + %res = call i256 @llvm.evm.byte(i256 31, i256 1) + ret i256 %res +} + +define i256 @test_byte23() { +; CHECK-LABEL: define i256 @test_byte23() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.byte(i256 31, i256 16073302433164271703722074696011524995083277336827658260012929812626463325184) + ret i256 %res +} + +define i256 @test_byte24() { +; CHECK-LABEL: define i256 @test_byte24() { +; CHECK-NEXT: ret i256 67 +; + + %res = call i256 @llvm.evm.byte(i256 31, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) + ret i256 %res +} + +define i256 @test_byte25() { +; CHECK-LABEL: define i256 @test_byte25() { +; CHECK-NEXT: ret i256 255 +; + + %res = call i256 @llvm.evm.byte(i256 31, i256 -54658164282644196211809801276940316383918434904861343304715684682168181439489) + ret i256 %res +} + +define i256 @test_byte26() { +; CHECK-LABEL: define i256 @test_byte26() { +; CHECK-NEXT: ret i256 255 +; + + %res = call i256 @llvm.evm.byte(i256 31, i256 -1) + ret i256 %res +} + +define i256 @test_byte27() { +; CHECK-LABEL: define i256 @test_byte27() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.byte(i256 32, i256 0) + ret i256 %res +} + +define i256 @test_byte28() { +; CHECK-LABEL: define i256 @test_byte28() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.byte(i256 32, i256 1) + ret i256 %res +} + +define i256 @test_byte29() { +; CHECK-LABEL: define i256 @test_byte29() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.byte(i256 32, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) + ret i256 %res +} + +define i256 @test_byte30() { +; CHECK-LABEL: define i256 @test_byte30() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.byte(i256 32, i256 -1) + ret i256 %res +} + +define i256 @test_byte31() { +; CHECK-LABEL: define i256 @test_byte31() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.byte(i256 35242523534534534233424343343443, i256 0) + ret i256 %res +} + +define i256 @test_byte32() { +; CHECK-LABEL: define i256 @test_byte32() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.byte(i256 35242523534534534233424343343443, i256 1) + ret i256 %res +} + +define i256 @test_byte33() { +; CHECK-LABEL: define i256 @test_byte33() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.byte(i256 35242523534534534233424343343443, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) + ret i256 %res +} + +define i256 @test_byte34() { +; CHECK-LABEL: define i256 @test_byte34() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.byte(i256 35242523534534534233424343343443, i256 -1) + ret i256 %res +} + +define i256 @test_byte35() { +; CHECK-LABEL: define i256 @test_byte35() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.byte(i256 -1, i256 0) + ret i256 %res +} + +define i256 @test_byte36() { +; CHECK-LABEL: define i256 @test_byte36() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.byte(i256 -1, i256 1) + ret i256 %res +} + +define i256 @test_byte37() { +; CHECK-LABEL: define i256 @test_byte37() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.byte(i256 -1, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) + ret i256 %res +} + +define i256 @test_byte38() { +; CHECK-LABEL: define i256 @test_byte38() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.byte(i256 -1, i256 -1) + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/constant-folding-earlycse-exp.ll b/llvm/test/CodeGen/EVM/constant-folding-earlycse-exp.ll new file mode 100644 index 000000000000..e73d09038cb9 --- /dev/null +++ b/llvm/test/CodeGen/EVM/constant-folding-earlycse-exp.ll @@ -0,0 +1,233 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -passes=early-cse -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare i256 @llvm.evm.exp(i256, i256) + +define i256 @test_exponent1() { +; CHECK-LABEL: define i256 @test_exponent1() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.exp(i256 0, i256 10) + ret i256 %res +} + +define i256 @test_exponent2() { +; CHECK-LABEL: define i256 @test_exponent2() { +; CHECK-NEXT: ret i256 poison +; + + %res = call i256 @llvm.evm.exp(i256 2, i256 undef) + ret i256 %res +} + +define i256 @test_exponent3() { +; CHECK-LABEL: define i256 @test_exponent3() { +; CHECK-NEXT: ret i256 -57896044618658097711785492504343953926634992332820282019728792003956564819968 +; + + %res = call i256 @llvm.evm.exp(i256 2, i256 255) + ret i256 %res +} + +define i256 @test_exponent4() { +; CHECK-LABEL: define i256 @test_exponent4() { +; CHECK-NEXT: ret i256 -26400738010602378953627016196889292963087978848325315750873680393886838386559 +; + + %res = call i256 @llvm.evm.exp(i256 307, i256 32) + ret i256 %res +} + +define i256 @test_exponent5() { +; CHECK-LABEL: define i256 @test_exponent5() { +; CHECK-NEXT: ret i256 poison +; + + %res = call i256 @llvm.evm.exp(i256 undef, i256 2) + ret i256 %res +} + +define i256 @test_exponent6() { +; CHECK-LABEL: define i256 @test_exponent6() { +; CHECK-NEXT: ret i256 1 +; + + %res = call i256 @llvm.evm.exp(i256 0, i256 0) + ret i256 %res +} + +define i256 @test_exponent7() { +; CHECK-LABEL: define i256 @test_exponent7() { +; CHECK-NEXT: ret i256 1 +; + + %res = call i256 @llvm.evm.exp(i256 1, i256 0) + ret i256 %res +} + +define i256 @test_exponent7.1() { +; CHECK-LABEL: define i256 @test_exponent7.1() { +; CHECK-NEXT: ret i256 1 +; + + %res = call i256 @llvm.evm.exp(i256 1, i256 1) + ret i256 %res +} + +define i256 @test_exponent8() { +; CHECK-LABEL: define i256 @test_exponent8() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.exp(i256 0, i256 433478394034343) + ret i256 %res +} + +define i256 @test_exponent9() { +; CHECK-LABEL: define i256 @test_exponent9() { +; CHECK-NEXT: ret i256 1 +; + + %res = call i256 @llvm.evm.exp(i256 121563127839120, i256 0) + ret i256 %res +} + +define i256 @test_exponent10() { +; CHECK-LABEL: define i256 @test_exponent10() { +; CHECK-NEXT: ret i256 1 +; + + %res = call i256 @llvm.evm.exp(i256 1, i256 433478394034343) + ret i256 %res +} + +define i256 @test_exponent11() { +; CHECK-LABEL: define i256 @test_exponent11() { +; CHECK-NEXT: ret i256 121563127839120 +; + + %res = call i256 @llvm.evm.exp(i256 121563127839120, i256 1) + ret i256 %res +} + +define i256 @test_exponent12() { +; CHECK-LABEL: define i256 @test_exponent12() { +; CHECK-NEXT: ret i256 569381465857367090636427305760163241950353347303833610101782245331441 +; + + %res = call i256 @llvm.evm.exp(i256 21, i256 52) + ret i256 %res +} + +define i256 @test_exponent13() { +; CHECK-LABEL: define i256 @test_exponent13() { +; CHECK-NEXT: ret i256 -680564733841876926926749214863536422911 +; + + ; 0x00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 2 -> + ; 0xfffffffffffffffffffffffffffffffe00000000000000000000000000000001 + %res = call i256 @llvm.evm.exp(i256 340282366920938463463374607431768211455, i256 2) + ret i256 %res +} + +define i256 @test_exponent14() { +; CHECK-LABEL: define i256 @test_exponent14() { +; CHECK-NEXT: ret i256 0 +; + + ; 0x0000000000000000000000000000000000000000000000000000000000010000 ^ 16 -> 0 + %res = call i256 @llvm.evm.exp(i256 65536, i256 16) + ret i256 %res +} + +define i256 @test_exponent15() { +; CHECK-LABEL: define i256 @test_exponent15() { +; CHECK-NEXT: ret i256 46756260758475007021788099943083655901358133181480408838873172916982662561791 +; + + ; 0x2fff1ffffffffff5ffffff0fffffffff2ffffffafffafffcffff1ff234ffffff ^ + ; 0xef231ffffffffff4f12fff34ffffffff2fffffbbfffafffdffff22f538ffffff -> + ; 46756260758475007021788099943083655901358133181480408838873172916982662561791 + %res = call i256 @llvm.evm.exp(i256 21709470740815105492860156599188632070735699051917406219058709325770546741247, i256 108164831314551007501389766781044785045182831393737867957367412557260901056511) + ret i256 %res +} + +define i256 @test_exponent16() { +; CHECK-LABEL: define i256 @test_exponent16() { +; CHECK-NEXT: ret i256 0 +; + + ; 0 ^ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0 + %res = call i256 @llvm.evm.exp(i256 0, i256 115792089237316195423570985008687907853269984665640564039457584007913129639935) + ret i256 %res +} + +define i256 @test_exponent17() { +; CHECK-LABEL: define i256 @test_exponent17() { +; CHECK-NEXT: ret i256 1 +; + + ; 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ^ 0 - > 1 + %res = call i256 @llvm.evm.exp(i256 115792089237316195423570985008687907853269984665640564039457584007913129639935, i256 0) + ret i256 %res +} + +define i256 @test_exponent18() { +; CHECK-LABEL: define i256 @test_exponent18() { +; CHECK-NEXT: ret i256 1 +; + + ; 1 ^ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 1 + %res = call i256 @llvm.evm.exp(i256 1, i256 115792089237316195423570985008687907853269984665640564039457584007913129639935) + ret i256 %res +} + +define i256 @test_exponent19() { +; CHECK-LABEL: define i256 @test_exponent19() { +; CHECK-NEXT: ret i256 -1 +; + + ; 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ^ 1 - > + ; 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + %res = call i256 @llvm.evm.exp(i256 115792089237316195423570985008687907853269984665640564039457584007913129639935, i256 1) + ret i256 %res +} + +define i256 @test_exponent20() { +; CHECK-LABEL: define i256 @test_exponent20() { +; CHECK-NEXT: ret i256 0 +; + + ; 7437834752357434334343423343443375834785783474 ^ + ; 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - > 0 + %res = call i256 @llvm.evm.exp(i256 7437834752357434334343423343443375834785783474, i256 115792089237316195423570985008687907853269984665640564039457584007913129639935) + ret i256 %res +} + +define i256 @test_exponent21() { +; CHECK-LABEL: define i256 @test_exponent21() { +; CHECK-NEXT: ret i256 -1 +; + + ; 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ^ + ; 23784273472384723848213821342323233223 - > + ; 115792089237316195423570985008687907853269984665640564039457584007913129639935 + %res = call i256 @llvm.evm.exp(i256 115792089237316195423570985008687907853269984665640564039457584007913129639935, i256 23784273472384723848213821342323233223) + ret i256 %res +} + +define i256 @test_exponent22() { +; CHECK-LABEL: define i256 @test_exponent22() { +; CHECK-NEXT: ret i256 -1 +; + + ; 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ^ + ; 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> + ; 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + %res = call i256 @llvm.evm.exp(i256 115792089237316195423570985008687907853269984665640564039457584007913129639935, i256 115792089237316195423570985008687907853269984665640564039457584007913129639935) + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/constant-folding-earlycse-mulmod.ll b/llvm/test/CodeGen/EVM/constant-folding-earlycse-mulmod.ll new file mode 100644 index 000000000000..02acd728b2e9 --- /dev/null +++ b/llvm/test/CodeGen/EVM/constant-folding-earlycse-mulmod.ll @@ -0,0 +1,53 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -passes=early-cse -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare i256 @llvm.evm.mulmod(i256, i256, i256) + +define i256 @test_mulmod1() { +; CHECK-LABEL: define i256 @test_mulmod1() { +; CHECK-NEXT: ret i256 1 +; + + ; Params are treated as unsigned values + %res = call i256 @llvm.evm.mulmod(i256 -2, i256 -2, i256 3) + ret i256 %res +} + +define i256 @test_mulmod2() { +; CHECK-LABEL: define i256 @test_mulmod2() { +; CHECK-NEXT: ret i256 2 +; + + %res = call i256 @llvm.evm.mulmod(i256 3, i256 17, i256 7) + ret i256 %res +} + +define i256 @test_mulmod3() { +; CHECK-LABEL: define i256 @test_mulmod3() { +; CHECK-NEXT: ret i256 poison +; + + %res = call i256 @llvm.evm.mulmod(i256 undef, i256 17, i256 7) + ret i256 %res +} + +define i256 @test_mulmod4() { +; CHECK-LABEL: define i256 @test_mulmod4() { +; CHECK-NEXT: ret i256 poison +; + + %res = call i256 @llvm.evm.mulmod(i256 17, i256 undef, i256 7) + ret i256 %res +} + +define i256 @test_mulmod5() { +; CHECK-LABEL: define i256 @test_mulmod5() { +; CHECK-NEXT: ret i256 poison +; + + %res = call i256 @llvm.evm.mulmod(i256 3, i256 17, i256 undef) + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/constant-folding-earlycse-signextend.ll b/llvm/test/CodeGen/EVM/constant-folding-earlycse-signextend.ll new file mode 100644 index 000000000000..2e4bff24a548 --- /dev/null +++ b/llvm/test/CodeGen/EVM/constant-folding-earlycse-signextend.ll @@ -0,0 +1,322 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -passes=early-cse -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare i256 @llvm.evm.signextend(i256, i256) + +define i256 @test_signextend1() { +; CHECK-LABEL: define i256 @test_signextend1() { +; CHECK-NEXT: ret i256 -1 +; + + %res = call i256 @llvm.evm.signextend(i256 0, i256 255) + ret i256 %res +} + +define i256 @test_signextend2() { +; CHECK-LABEL: define i256 @test_signextend2() { +; CHECK-NEXT: ret i256 32767 +; + + %res = call i256 @llvm.evm.signextend(i256 1, i256 32767) + ret i256 %res +} + +define i256 @test_signextend3() { +; CHECK-LABEL: define i256 @test_signextend3() { +; CHECK-NEXT: ret i256 poison +; + + %res = call i256 @llvm.evm.signextend(i256 undef, i256 32767) + ret i256 %res +} + +define i256 @test_signextend4() { +; CHECK-LABEL: define i256 @test_signextend4() { +; CHECK-NEXT: ret i256 poison +; + + %res = call i256 @llvm.evm.signextend(i256 1, i256 undef) + ret i256 %res +} + +define i256 @test_signextend5() { +; CHECK-LABEL: define i256 @test_signextend5() { +; CHECK-NEXT: ret i256 poison +; + + %res = call i256 @llvm.evm.signextend(i256 32, i256 undef) + ret i256 %res +} + +define i256 @test_signextend6() { +; CHECK-LABEL: define i256 @test_signextend6() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.signextend(i256 0, i256 0) + ret i256 %res +} + +define i256 @test_signextend7() { +; CHECK-LABEL: define i256 @test_signextend7() { +; CHECK-NEXT: ret i256 1 +; + + %res = call i256 @llvm.evm.signextend(i256 0, i256 1) + ret i256 %res +} + +define i256 @test_signextend8() { +; CHECK-LABEL: define i256 @test_signextend8() { +; CHECK-NEXT: ret i256 67 +; + + %res = call i256 @llvm.evm.signextend(i256 0, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) + ret i256 %res +} + +define i256 @test_signextend9() { +; CHECK-LABEL: define i256 @test_signextend9() { +; CHECK-NEXT: ret i256 -1 +; + + %res = call i256 @llvm.evm.signextend(i256 0, i256 -1) + ret i256 %res +} + +define i256 @test_signextend10() { +; CHECK-LABEL: define i256 @test_signextend10() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.signextend(i256 1, i256 0) + ret i256 %res +} + +define i256 @test_signextend11() { +; CHECK-LABEL: define i256 @test_signextend11() { +; CHECK-NEXT: ret i256 1 +; + + %res = call i256 @llvm.evm.signextend(i256 1, i256 1) + ret i256 %res +} + +define i256 @test_signextend12() { +; CHECK-LABEL: define i256 @test_signextend12() { +; CHECK-NEXT: ret i256 -31677 +; + + %res = call i256 @llvm.evm.signextend(i256 1, i256 37670211480306196047687443673641227745170897112008692523754794019498533094467) + ret i256 %res +} + +define i256 @test_signextend13() { +; CHECK-LABEL: define i256 @test_signextend13() { +; CHECK-NEXT: ret i256 -1 +; + + %res = call i256 @llvm.evm.signextend(i256 1, i256 -1) + ret i256 %res +} + +define i256 @test_signextend14() { +; CHECK-LABEL: define i256 @test_signextend14() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.signextend(i256 17, i256 0) + ret i256 %res +} + +define i256 @test_signextend15() { +; CHECK-LABEL: define i256 @test_signextend15() { +; CHECK-NEXT: ret i256 1 +; + + %res = call i256 @llvm.evm.signextend(i256 4, i256 1) + ret i256 %res +} + +define i256 @test_signextend16() { +; CHECK-LABEL: define i256 @test_signextend16() { +; CHECK-NEXT: ret i256 9111590707254702215554908033119796504552448929051039916 +; + + %res = call i256 @llvm.evm.signextend(i256 22, i256 9111590707254702215554908033119796504552448929051039916) + ret i256 %res +} + +define i256 @test_signextend17() { +; CHECK-LABEL: define i256 @test_signextend17() { +; CHECK-NEXT: ret i256 2936025693725350223284924577414370008619180 +; + + %res = call i256 @llvm.evm.signextend(i256 17, i256 25236770892255973364820642850062731514599596) + ret i256 %res +} + +define i256 @test_signextend18() { +; CHECK-LABEL: define i256 @test_signextend18() { +; CHECK-NEXT: ret i256 -7745860242270075226386909265533604515253681414968584020 +; + + %res = call i256 @llvm.evm.signextend(i256 22, i256 16774068411584146507346643168871342422646144539969050796) + ret i256 %res +} + +define i256 @test_signextend19() { +; CHECK-LABEL: define i256 @test_signextend19() { +; CHECK-NEXT: ret i256 -5426753755723633454790969774828765556123476 +; + + %res = call i256 @llvm.evm.signextend(i256 17, i256 93509753590725542020426220953279705230436762145237986916280948908) + ret i256 %res +} + +define i256 @test_signextend20() { +; CHECK-LABEL: define i256 @test_signextend20() { +; CHECK-NEXT: ret i256 -1 +; + + %res = call i256 @llvm.evm.signextend(i256 25, i256 -1) + ret i256 %res +} + +define i256 @test_signextend21() { +; CHECK-LABEL: define i256 @test_signextend21() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.signextend(i256 255, i256 0) + ret i256 %res +} + +define i256 @test_signextend22() { +; CHECK-LABEL: define i256 @test_signextend22() { +; CHECK-NEXT: ret i256 1 +; + + %res = call i256 @llvm.evm.signextend(i256 255, i256 1) + ret i256 %res +} + +define i256 @test_signextend23() { +; CHECK-LABEL: define i256 @test_signextend23() { +; CHECK-NEXT: ret i256 -49173855447680950519990795082874703144781591387221730505838393986436314155965 +; + + %res = call i256 @llvm.evm.signextend(i256 255, i256 66618233789635244903580189925813204708488393278418833533619190021476815483971) + ret i256 %res +} + +define i256 @test_signextend24() { +; CHECK-LABEL: define i256 @test_signextend24() { +; CHECK-NEXT: ret i256 -1 +; + + %res = call i256 @llvm.evm.signextend(i256 255, i256 -1) + ret i256 %res +} + +define i256 @test_signextend25() { +; CHECK-LABEL: define i256 @test_signextend25() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.signextend(i256 256, i256 0) + ret i256 %res +} + +define i256 @test_signextend26() { +; CHECK-LABEL: define i256 @test_signextend26() { +; CHECK-NEXT: ret i256 37670211480306196047687443673641227745170897112008692523754794019498533073987 +; + + %res = call i256 @llvm.evm.signextend(i256 256, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) + ret i256 %res +} + +define i256 @test_signextend27() { +; CHECK-LABEL: define i256 @test_signextend27() { +; CHECK-NEXT: ret i256 -1 +; + + %res = call i256 @llvm.evm.signextend(i256 256, i256 -1) + ret i256 %res +} + +define i256 @test_signextend28() { +; CHECK-LABEL: define i256 @test_signextend28() { +; CHECK-NEXT: ret i256 1 +; + + %res = call i256 @llvm.evm.signextend(i256 35242523534534534233424343343443, i256 1) + ret i256 %res +} + +define i256 @test_signextend29() { +; CHECK-LABEL: define i256 @test_signextend29() { +; CHECK-NEXT: ret i256 37670211480306196047687443673641227745170897112008692523754794019498533073987 +; + + %res = call i256 @llvm.evm.signextend(i256 35242523534534534233424343343443, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) + ret i256 %res +} + +define i256 @test_signextend30() { +; CHECK-LABEL: define i256 @test_signextend30() { +; CHECK-NEXT: ret i256 -1 +; + + %res = call i256 @llvm.evm.signextend(i256 35242523534534534233424343343443, i256 -1) + ret i256 %res +} + +define i256 @test_signextend31() { +; CHECK-LABEL: define i256 @test_signextend31() { +; CHECK-NEXT: ret i256 0 +; + + %res = call i256 @llvm.evm.signextend(i256 -1, i256 0) + ret i256 %res +} + +define i256 @test_signextend32() { +; CHECK-LABEL: define i256 @test_signextend32() { +; CHECK-NEXT: ret i256 1 +; + + %res = call i256 @llvm.evm.signextend(i256 -1, i256 1) + ret i256 %res +} + +define i256 @test_signextend33() { +; CHECK-LABEL: define i256 @test_signextend33() { +; CHECK-NEXT: ret i256 37670211480306196047687443673641227745170897112008692523754794019498533073987 +; + + %res = call i256 @llvm.evm.signextend(i256 -1, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) + ret i256 %res +} + +define i256 @test_signextend34() { +; CHECK-LABEL: define i256 @test_signextend34() { +; CHECK-NEXT: ret i256 -1 +; + + %res = call i256 @llvm.evm.signextend(i256 -1, i256 -1) + ret i256 %res +} + +define i256 @test_signextend35() { +; CHECK-LABEL: define i256 @test_signextend35() { +; CHECK-NEXT: ret i256 -1 +; + + %res = call i256 @llvm.evm.signextend(i256 0, i256 33023) + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/constant-unfolding-Oz-fallback.ll b/llvm/test/CodeGen/EVM/constant-unfolding-Oz-fallback.ll new file mode 100644 index 000000000000..ff010cf64e52 --- /dev/null +++ b/llvm/test/CodeGen/EVM/constant-unfolding-Oz-fallback.ll @@ -0,0 +1,78 @@ +; RUN: llc -O3 --debug-only=evm-constant-unfolding -evm-metadata-size=16 -evm-bytecode-sizelimit=96 < %s 2>&1 | FileCheck %s +; REQUIRES: asserts +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +; CHECK: *** Running constant unfolding in the default mode *** +; CHECK-NEXT: *** Initial module size: 152 *** +; CHECK: Checking PUSH32_S i256 35408467139433450592217433187231851964531694900788300625387963629091585785856 +; CHECK: Checking PUSH27_S i256 52656145834278593348959013841835216134069776894924259991723442175 +; CHECK: Skipping identity transformation +; CHECK: Checking PUSH27_S i256 52656145834278593348959013841835216132831836855638879716824317951 +; CHECK: Skipping identity transformation + +; CHECK: *** Current module size is 127, which still exceeds the limit, falling back to size-minimization mode *** +; CHECK-NEXT: *** Running constant unfolding in size-minimization mode at loop depth 0 *** + +; CHECK: *** Current module size is 127 *** +; CHECK-NEXT: *** Running constant unfolding in size-minimization mode at loop depth 1 *** +; CHECK: Checking PUSH27_S i256 52656145834278593348959013841835216134069776894924259991723442175 + +; CHECK: *** Current module size is 119 *** +; CHECK-NEXT: *** Running constant unfolding in size-minimization mode at loop depth 2 *** +; CHECK: Checking PUSH27_S i256 52656145834278593348959013841835216132831836855638879716824317951 + +; CHECK: *** Current module size is 111 *** + +define i256 @test(i256 %p) { + +; CHECK-LABEL: .BB0_2: +; CHECK: PUSH12 0x520000000000000000000000 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 0x29 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH1 0x29 +; CHECK-NEXT: SHR + +; CHECK-LABEL: .BB0_4: +; CHECK: PUSH4 0x4E487B71 +; CHECK-NEXT: PUSH1 0xE0 +; CHECK-NEXT: SHL + +; CHECK-LABEL: .BB0_6: +; CHECK: PUSH12 0x560000000000000000000000 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 0x29 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH1 0x29 +; CHECK-NEXT: SHR + +entry: + %p.cmp = icmp eq i256 %p, 35 + br i1 %p.cmp, label %exit, label %outer.cond + +outer.cond: + %i = phi i256 [0, %entry], [%i.next, %outer.inc] + %i.cmp = icmp slt i256 %i, 52656145834278593348959013841835216134069776894924259991723442176 + br i1 %i.cmp, label %inner.cond, label %exit + +inner.cond: + %j = phi i256 [0, %outer.cond], [%j.next, %inner.inc] + %j.cmp = icmp slt i256 %j, 52656145834278593348959013841835216132831836855638879716824317952 + br i1 %j.cmp, label %inner.body, label %outer.inc + +inner.body: + br label %inner.inc + +inner.inc: + %j.next = add i256 %j, 1 + br label %inner.cond + +outer.inc: + %i.next = add i256 %i, 1 + br label %outer.cond + +exit: + ret i256 35408467139433450592217433187231851964531694900788300625387963629091585785856 + +} diff --git a/llvm/test/CodeGen/EVM/constant-unfolding-Oz.ll b/llvm/test/CodeGen/EVM/constant-unfolding-Oz.ll new file mode 100644 index 000000000000..b2d9d3468a7b --- /dev/null +++ b/llvm/test/CodeGen/EVM/constant-unfolding-Oz.ll @@ -0,0 +1,295 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc -O3 < %s | FileCheck %s +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare void @llvm.evm.return(ptr addrspace(1), i256) noreturn + +; This file tests how constants are unfolded in OptForSize mode. + +; 0x4e487b7100000000000000000000000000000000000000000000000000000000 +define void @test1() #1 { +; CHECK-LABEL: test1: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH4 0x4E487B71 +; CHECK-NEXT: PUSH1 0xE0 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 35408467139433450592217433187231851964531694900788300625387963629091585785856) + unreachable +} + +; 0x00000000FFFFFF00000000000000000000000000000000000000000000000000 +define void @test2() #1 { +; CHECK-LABEL: test2: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH3 0xFFFFFF +; CHECK-NEXT: PUSH1 0xC8 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 26959945060212595535676739545057538332474541900337578698310774947840) + unreachable +} + +; 0x00000000FFFFFFFFFFFF00000000000000000000000000000000000000000000 +define void @test3() #1 { +; CHECK-LABEL: test3: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH6 0xFFFFFFFFFFFF +; CHECK-NEXT: PUSH1 0xB0 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 26959946667150544013695710968965983276947947528216596309908473774080) + unreachable +} + +; 0x00000000FFFFFFFFFFFFFF000000000000000000000000000000000000000000 +define void @test4() #1 { +; CHECK-LABEL: test4: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH7 0xFFFFFFFFFFFFFF +; CHECK-NEXT: PUSH1 0xA8 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 26959946667150639420522595930308483613493827247172119449184879247360) + unreachable +} + +; 0xFFFFFF0000000000000000000000000000000000000000000000000000000000 +define void @test5() #1 { +; CHECK-LABEL: test5: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 0xE8 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 115792082335569848633007197573932045576244532214531591869071028845388905840640) + unreachable +} + +; 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 +define void @test6() #1 { +; CHECK-LABEL: test6: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 0xE0 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 115792089210356248756420345214020892766250353992003419616917011526809519390720) + unreachable +} + +; 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +define void @test7() #1 { +; CHECK-LABEL: test7: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 -1) + unreachable +} + + +; 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000 +define void @test8() #1 { +; CHECK-LABEL: test8: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 0x81 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: SHR +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 57896044618658097711785492504343953926294709965899343556265417396524796608512) + unreachable +} + +; 0x000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFF +define void @test11() #1 { +; CHECK-LABEL: test11: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 0xC0 +; CHECK-NEXT: SHR +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 18446744073709551615) + unreachable +} + +; 0x000000000000000000000000000000000000000000000000FFFFFFFAFFFFFFFF +define void @test12() #1 { +; CHECK-LABEL: test12: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH8 0xFFFFFFFAFFFFFFFF +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 18446744052234715135) + unreachable +} + +; 0x000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFE0 +define void @test13() #1 { +; CHECK-LABEL: test13: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH8 0xFFFFFFFFFFFFFFE0 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 18446744073709551584) + unreachable +} + +; 0x00000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFE0 +define void @test14() #1 { +; CHECK-LABEL: test14: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH10 0xFFFFFFFFFFFFFFFFFFE0 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 1208925819614629174706144) + unreachable +} + +; 0x00000000000000000000000000000000000000FFFFFFFFFFFFFFFFAFFFFFFFFF +define void @test15() #1 { +; CHECK-LABEL: test15: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH13 0xFFFFFFFFFFFFFFFFAFFFFFFFFF +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 20282409603651670423603653902335) + unreachable +} + +; 0x00000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +define void @test16() #1 { +; CHECK-LABEL: test16: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 0x54 +; CHECK-NEXT: SHR +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 5986310706507378352962293074805895248510699696029695) + unreachable +} + +; 0x00000000007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFAE0000000000000000000000 +define void @test17() #1 { +; CHECK-LABEL: test17: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x28 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 0x82 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH1 0x29 +; CHECK-NEXT: SHR +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 52656145834278593348959013841835216134069776894924259991723442176) + unreachable +} + +; 0x0000000000000000000000000000000100000000000000000000000000000000 +define void @test18() #1 { +; CHECK-LABEL: test18: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: PUSH1 0x80 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 340282366920938463463374607431768211456) + unreachable +} + +; 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE +define void @test19() #1 { +; CHECK-LABEL: test19: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 115792089237316195423570985008687907853269984665640564039457584007913129639934) + unreachable +} + +; 0xFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +define void @test20() #1 { +; CHECK-LABEL: test20: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: PUSH1 0x9B +; CHECK-NEXT: SHL +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 115792089237316195423570985008642235927103393949446698888435200163548881747967) + unreachable +} + +; 0x00000000000001000000000000000000000000000000000000000000000000FF +define void @test21() #1 { +; CHECK-LABEL: test21: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH26 0x1000000000000000000000000000000000000000000000000FF +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 1606938044258990275541962092341162602522202993782792835301631) + unreachable +} + +attributes #1 = { minsize nofree nounwind noreturn optsize } diff --git a/llvm/test/CodeGen/EVM/constant-unfolding-speed-and-Oz.ll b/llvm/test/CodeGen/EVM/constant-unfolding-speed-and-Oz.ll new file mode 100644 index 000000000000..fb77fb8848bf --- /dev/null +++ b/llvm/test/CodeGen/EVM/constant-unfolding-speed-and-Oz.ll @@ -0,0 +1,32 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc -O3 < %s | FileCheck %s + +; Test that we are using different unfold for speed and size tests. + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +define i256 @test_speed() { +; CHECK-LABEL: test_speed: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH3 0xFFFFFF +; CHECK-NEXT: PUSH1 0xE8 +; CHECK-NEXT: SHL +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + ret i256 115792082335569848633007197573932045576244532214531591869071028845388905840640 +} + +define i256 @test_size() minsize { +; CHECK-LABEL: test_size: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 0xE8 +; CHECK-NEXT: SHL +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + ret i256 115792082335569848633007197573932045576244532214531591869071028845388905840640 +} diff --git a/llvm/test/CodeGen/EVM/constant-unfolding.ll b/llvm/test/CodeGen/EVM/constant-unfolding.ll new file mode 100644 index 000000000000..51f6030dcdc6 --- /dev/null +++ b/llvm/test/CodeGen/EVM/constant-unfolding.ll @@ -0,0 +1,115 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc -O3 < %s | FileCheck %s +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare void @llvm.evm.return(ptr addrspace(1), i256) noreturn + +; This file tests how constants are unfolded in non-OptForSize mode. + +; 0x4e487b7100000000000000000000000000000000000000000000000000000000 +define void @test1() #1 { +; CHECK-LABEL: test1: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH4 0x4E487B71 +; CHECK-NEXT: PUSH1 0xE0 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 35408467139433450592217433187231851964531694900788300625387963629091585785856) + unreachable +} + +; 0x00000000FFFFFF00000000000000000000000000000000000000000000000000 +define void @test2() #1 { +; CHECK-LABEL: test2: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH3 0xFFFFFF +; CHECK-NEXT: PUSH1 0xC8 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 26959945060212595535676739545057538332474541900337578698310774947840) + unreachable +} + +; 0xFFFFFF0000000000000000000000000000000000000000000000000000000000 +define void @test3() #1 { +; CHECK-LABEL: test3: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH3 0xFFFFFF +; CHECK-NEXT: PUSH1 0xE8 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 115792082335569848633007197573932045576244532214531591869071028845388905840640) + unreachable +} + +; 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 +define void @test4() #1 { +; CHECK-LABEL: test4: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH4 0xFFFFFFFF +; CHECK-NEXT: PUSH1 0xE0 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 115792089210356248756420345214020892766250353992003419616917011526809519390720) + unreachable +} + +; 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +define void @test5() #1 { +; CHECK-LABEL: test5: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 -1) + unreachable +} + +; 0x0000000000000000000000000000000100000000000000000000000000000000 +define void @test6() #1 { +; CHECK-LABEL: test6: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: PUSH1 0x80 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 340282366920938463463374607431768211456) + unreachable +} + +; 0x000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +define void @test7() #1 { +; CHECK-LABEL: test7: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 0x60 +; CHECK-NEXT: SHR +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 1461501637330902918203684832716283019655932542975) + unreachable +} + +attributes #1 = { nofree nounwind noreturn } diff --git a/llvm/test/CodeGen/EVM/context-remat.ll b/llvm/test/CodeGen/EVM/context-remat.ll new file mode 100644 index 000000000000..465f178e3d63 --- /dev/null +++ b/llvm/test/CodeGen/EVM/context-remat.ll @@ -0,0 +1,293 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc -O3 < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare void @use(i256) + +define i256 @test_address() { +; CHECK-LABEL: test_address: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH4 @.FUNC_RET0 +; CHECK-NEXT: ADDRESS +; CHECK-NEXT: PUSH4 @use +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ADDRESS +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %ret = call i256 @llvm.evm.address() + call void @use(i256 %ret) + ret i256 %ret +} + +define i256 @test_origin() { +; CHECK-LABEL: test_origin: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ORIGIN +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH4 @use +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET1: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %ret = call i256 @llvm.evm.origin() + call void @use(i256 %ret) + ret i256 %ret +} + +define i256 @test_caller() { +; CHECK-LABEL: test_caller: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH4 @.FUNC_RET2 +; CHECK-NEXT: CALLER +; CHECK-NEXT: PUSH4 @use +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CALLER +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %ret = call i256 @llvm.evm.caller() + call void @use(i256 %ret) + ret i256 %ret +} + +define i256 @test_callvalue() { +; CHECK-LABEL: test_callvalue: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH4 @.FUNC_RET3 +; CHECK-NEXT: CALLVALUE +; CHECK-NEXT: PUSH4 @use +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CALLVALUE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %ret = call i256 @llvm.evm.callvalue() + call void @use(i256 %ret) + ret i256 %ret +} + +define i256 @test_calldatasize() { +; CHECK-LABEL: test_calldatasize: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH4 @.FUNC_RET4 +; CHECK-NEXT: CALLDATASIZE +; CHECK-NEXT: PUSH4 @use +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET4: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CALLDATASIZE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %ret = call i256 @llvm.evm.calldatasize() + call void @use(i256 %ret) + ret i256 %ret +} + +define i256 @test_codesize() { +; CHECK-LABEL: test_codesize: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH4 @.FUNC_RET5 +; CHECK-NEXT: CODESIZE +; CHECK-NEXT: PUSH4 @use +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET5: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CODESIZE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %ret = call i256 @llvm.evm.codesize() + call void @use(i256 %ret) + ret i256 %ret +} + +define i256 @test_gasprice() { +; CHECK-LABEL: test_gasprice: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: GASPRICE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET6 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH4 @use +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET6: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %ret = call i256 @llvm.evm.gasprice() + call void @use(i256 %ret) + ret i256 %ret +} + +define i256 @test_coinbase() { +; CHECK-LABEL: test_coinbase: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: COINBASE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET7 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH4 @use +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET7: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %ret = call i256 @llvm.evm.coinbase() + call void @use(i256 %ret) + ret i256 %ret +} + +define i256 @test_timestamp() { +; CHECK-LABEL: test_timestamp: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: TIMESTAMP +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET8 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH4 @use +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET8: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %ret = call i256 @llvm.evm.timestamp() + call void @use(i256 %ret) + ret i256 %ret +} + +define i256 @test_number() { +; CHECK-LABEL: test_number: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: NUMBER +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET9 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH4 @use +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET9: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %ret = call i256 @llvm.evm.number() + call void @use(i256 %ret) + ret i256 %ret +} + +define i256 @test_difficulty() { +; CHECK-LABEL: test_difficulty: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DIFFICULTY +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET10 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH4 @use +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET10: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %ret = call i256 @llvm.evm.difficulty() + call void @use(i256 %ret) + ret i256 %ret +} + +define i256 @test_gaslimit() { +; CHECK-LABEL: test_gaslimit: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: GASLIMIT +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET11 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH4 @use +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET11: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %ret = call i256 @llvm.evm.gaslimit() + call void @use(i256 %ret) + ret i256 %ret +} + +define i256 @test_chainid() { +; CHECK-LABEL: test_chainid: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CHAINID +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET12 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH4 @use +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET12: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %ret = call i256 @llvm.evm.chainid() + call void @use(i256 %ret) + ret i256 %ret +} + +define i256 @test_basefee() { +; CHECK-LABEL: test_basefee: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: BASEFEE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET13 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH4 @use +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET13: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %ret = call i256 @llvm.evm.basefee() + call void @use(i256 %ret) + ret i256 %ret +} + +define i256 @test_blobbasefee() { +; CHECK-LABEL: test_blobbasefee: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: BLOBBASEFEE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET14 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH4 @use +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET14: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %ret = call i256 @llvm.evm.blobbasefee() + call void @use(i256 %ret) + ret i256 %ret +} + +declare i256 @llvm.evm.address() +declare i256 @llvm.evm.origin() +declare i256 @llvm.evm.caller() +declare i256 @llvm.evm.callvalue() +declare i256 @llvm.evm.calldatasize() +declare i256 @llvm.evm.codesize() +declare i256 @llvm.evm.gasprice() +declare i256 @llvm.evm.coinbase() +declare i256 @llvm.evm.timestamp() +declare i256 @llvm.evm.number() +declare i256 @llvm.evm.difficulty() +declare i256 @llvm.evm.gaslimit() +declare i256 @llvm.evm.chainid() +declare i256 @llvm.evm.basefee() +declare i256 @llvm.evm.blobbasefee() diff --git a/llvm/test/CodeGen/EVM/cse-intrinsic.ll b/llvm/test/CodeGen/EVM/cse-intrinsic.ll new file mode 100644 index 000000000000..c2dd7efcdce8 --- /dev/null +++ b/llvm/test/CodeGen/EVM/cse-intrinsic.ll @@ -0,0 +1,1051 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2 +; RUN: opt -O3 -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @addmod_dce(i256 %rs1, i256 %rs2, i256 %rs3) nounwind { +; CHECK-LABEL: define noundef i256 @addmod_dce +; CHECK-SAME: (i256 [[RS1:%.*]], i256 [[RS2:%.*]], i256 [[RS3:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.addmod(i256 %rs1, i256 %rs2, i256 %rs3) + ret i256 0 +} + +define i256 @sha3_dce(ptr addrspace(1) %offset, i256 %size) nounwind { +; CHECK-LABEL: define noundef i256 @sha3_dce +; CHECK-SAME: (ptr addrspace(1) nocapture readnone [[OFFSET:%.*]], i256 [[SIZE:%.*]]) local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.sha3(ptr addrspace(1) %offset, i256 %size) + ret i256 0 +} + +define i256 @pc_dce() nounwind { +; CHECK-LABEL: define noundef i256 @pc_dce +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.pc() + ret i256 0 +} + +define i256 @gas_dce() nounwind { +; CHECK-LABEL: define noundef i256 @gas_dce +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.gas() + ret i256 0 +} + +define i256 @msize_dce() nounwind { +; CHECK-LABEL: define noundef i256 @msize_dce +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.msize() + ret i256 0 +} + +define i256 @balance_dce(i256 %rs1) nounwind { +; CHECK-LABEL: define noundef i256 @balance_dce +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.balance(i256 %rs1) + ret i256 0 +} + +define i256 @selfbalance_dce() nounwind { +; CHECK-LABEL: define noundef i256 @selfbalance_dce +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.selfbalance() + ret i256 0 +} + +define i256 @returndatasize_dce() nounwind { +; CHECK-LABEL: define noundef i256 @returndatasize_dce +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.returndatasize() + ret i256 0 +} + +define i256 @origin_dce() nounwind { +; CHECK-LABEL: define noundef i256 @origin_dce +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.origin() + ret i256 0 +} + +define i256 @gasprice_dce() nounwind { +; CHECK-LABEL: define noundef i256 @gasprice_dce +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.gasprice() + ret i256 0 +} + +define i256 @blockhash_dce(i256 %rs1) nounwind { +; CHECK-LABEL: define noundef i256 @blockhash_dce +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.blockhash(i256 %rs1) + ret i256 0 +} + +define i256 @blobhash_dce(i256 %rs1) nounwind { +; CHECK-LABEL: define noundef i256 @blobhash_dce +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.blobhash(i256 %rs1) + ret i256 0 +} + +define i256 @coinbase_dce() nounwind { +; CHECK-LABEL: define noundef i256 @coinbase_dce +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.coinbase() + ret i256 0 +} + +define i256 @timestamp_dce() nounwind { +; CHECK-LABEL: define noundef i256 @timestamp_dce +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.timestamp() + ret i256 0 +} + +define i256 @number_dce() nounwind { +; CHECK-LABEL: define noundef i256 @number_dce +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.number() + ret i256 0 +} + +define i256 @difficulty_dce() nounwind { +; CHECK-LABEL: define noundef i256 @difficulty_dce +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.difficulty() + ret i256 0 +} + +define i256 @gaslimit_dce() nounwind { +; CHECK-LABEL: define noundef i256 @gaslimit_dce +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.gaslimit() + ret i256 0 +} + +define i256 @chainid_dce() nounwind { +; CHECK-LABEL: define noundef i256 @chainid_dce +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.chainid() + ret i256 0 +} + +define i256 @basefee_dce() nounwind { +; CHECK-LABEL: define noundef i256 @basefee_dce +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.basefee() + ret i256 0 +} + +define i256 @blobbasefee_dce() nounwind { +; CHECK-LABEL: define noundef i256 @blobbasefee_dce +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.blobbasefee() + ret i256 0 +} + +define i256 @addmod(i256 %rs1, i256 %rs2, i256 %rs3) nounwind { +; CHECK-LABEL: define i256 @addmod +; CHECK-SAME: (i256 [[RS1:%.*]], i256 [[RS2:%.*]], i256 [[RS3:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.addmod(i256 [[RS1]], i256 [[RS2]], i256 [[RS3]]) +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.addmod(i256 %rs1, i256 %rs2, i256 %rs3) + %res2 = call i256 @llvm.evm.addmod(i256 %rs1, i256 %rs2, i256 %rs3) + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @mulmod(i256 %rs1, i256 %rs2, i256 %rs3) nounwind { +; CHECK-LABEL: define i256 @mulmod +; CHECK-SAME: (i256 [[RS1:%.*]], i256 [[RS2:%.*]], i256 [[RS3:%.*]]) local_unnamed_addr #[[ATTR1]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.mulmod(i256 [[RS1]], i256 [[RS2]], i256 [[RS3]]) +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.mulmod(i256 %rs1, i256 %rs2, i256 %rs3) + %res2 = call i256 @llvm.evm.mulmod(i256 %rs1, i256 %rs2, i256 %rs3) + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @exp(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: define i256 @exp +; CHECK-SAME: (i256 [[RS1:%.*]], i256 [[RS2:%.*]]) local_unnamed_addr #[[ATTR1]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.exp(i256 [[RS1]], i256 [[RS2]]) +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.exp(i256 %rs1, i256 %rs2) + %res2 = call i256 @llvm.evm.exp(i256 %rs1, i256 %rs2) + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @sha3(ptr addrspace(1) %offset, i256 %size) nounwind { +; CHECK-LABEL: define i256 @sha3 +; CHECK-SAME: (ptr addrspace(1) nocapture readonly [[OFFSET:%.*]], i256 [[SIZE:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) [[OFFSET]], i256 [[SIZE]]) +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.sha3(ptr addrspace(1) %offset, i256 %size) + %res2 = call i256 @llvm.evm.sha3(ptr addrspace(1) %offset, i256 %size) + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @signextend(i256 %bytesize, i256 %val) nounwind { +; CHECK-LABEL: define i256 @signextend +; CHECK-SAME: (i256 [[BYTESIZE:%.*]], i256 [[VAL:%.*]]) local_unnamed_addr #[[ATTR1]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.signextend(i256 [[BYTESIZE]], i256 [[VAL]]) +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.signextend(i256 %bytesize, i256 %val) + %res2 = call i256 @llvm.evm.signextend(i256 %bytesize, i256 %val) + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @byte(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: define i256 @byte +; CHECK-SAME: (i256 [[RS1:%.*]], i256 [[RS2:%.*]]) local_unnamed_addr #[[ATTR1]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.byte(i256 [[RS1]], i256 [[RS2]]) +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.byte(i256 %rs1, i256 %rs2) + %res2 = call i256 @llvm.evm.byte(i256 %rs1, i256 %rs2) + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @pc() nounwind { +; CHECK-LABEL: define i256 @pc +; CHECK-SAME: () local_unnamed_addr #[[ATTR3:[0-9]+]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.pc() +; CHECK-NEXT: [[RES2:%.*]] = tail call i256 @llvm.evm.pc() +; CHECK-NEXT: [[RET:%.*]] = add i256 [[RES2]], [[RES1]] +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.pc() + %res2 = call i256 @llvm.evm.pc() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @gas() nounwind { +; CHECK-LABEL: define i256 @gas +; CHECK-SAME: () local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.gas() +; CHECK-NEXT: [[RES2:%.*]] = tail call i256 @llvm.evm.gas() +; CHECK-NEXT: [[RET:%.*]] = add i256 [[RES2]], [[RES1]] +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.gas() + %res2 = call i256 @llvm.evm.gas() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @msize() nounwind { +; CHECK-LABEL: define i256 @msize +; CHECK-SAME: () local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.msize() +; CHECK-NEXT: [[RES2:%.*]] = tail call i256 @llvm.evm.msize() +; CHECK-NEXT: [[RET:%.*]] = add i256 [[RES2]], [[RES1]] +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.msize() + %res2 = call i256 @llvm.evm.msize() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @address() nounwind { +; CHECK-LABEL: define i256 @address +; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.address() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.address() + %res2 = call i256 @llvm.evm.address() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @origin() nounwind { +; CHECK-LABEL: define i256 @origin +; CHECK-SAME: () local_unnamed_addr #[[ATTR4:[0-9]+]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.origin() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.origin() + %res2 = call i256 @llvm.evm.origin() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @caller() nounwind { +; CHECK-LABEL: define i256 @caller +; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.caller() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.caller() + %res2 = call i256 @llvm.evm.caller() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @balance(i256 %rs1) nounwind { +; CHECK-LABEL: define i256 @balance +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR5:[0-9]+]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.balance(i256 [[RS1]]) +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.balance(i256 %rs1) + %res2 = call i256 @llvm.evm.balance(i256 %rs1) + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @balance_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @balance_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6:[0-9]+]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.balance(i256 [[ADDR]]) +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.balance(i256 [[ADDR]]) +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.balance(i256 %addr) + %v2 = call i256 @llvm.evm.balance(i256 %addr) + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.balance(i256 %addr) + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @origin_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @origin_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.origin() +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.origin() +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.origin() + %v2 = call i256 @llvm.evm.origin() + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.origin() + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @gasprice_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @gasprice_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.gasprice() +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.gasprice() +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.gasprice() + %v2 = call i256 @llvm.evm.gasprice() + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.gasprice() + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @blockhash_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @blockhash_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.blockhash(i256 [[ADDR]]) +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.blockhash(i256 [[ADDR]]) +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.blockhash(i256 %addr) + %v2 = call i256 @llvm.evm.blockhash(i256 %addr) + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.blockhash(i256 %addr) + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @blobhash_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @blobhash_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.blobhash(i256 [[ADDR]]) +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.blobhash(i256 [[ADDR]]) +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.blobhash(i256 %addr) + %v2 = call i256 @llvm.evm.blobhash(i256 %addr) + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.blobhash(i256 %addr) + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @coinbase_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @coinbase_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.coinbase() +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.coinbase() +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.coinbase() + %v2 = call i256 @llvm.evm.coinbase() + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.coinbase() + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @timestamp_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @timestamp_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.timestamp() +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.timestamp() +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.timestamp() + %v2 = call i256 @llvm.evm.timestamp() + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.timestamp() + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @number_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @number_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.number() +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.number() +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.number() + %v2 = call i256 @llvm.evm.number() + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.number() + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @difficulty_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @difficulty_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.difficulty() +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.difficulty() +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.difficulty() + %v2 = call i256 @llvm.evm.difficulty() + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.difficulty() + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @gaslimit_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @gaslimit_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.gaslimit() +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.gaslimit() +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.gaslimit() + %v2 = call i256 @llvm.evm.gaslimit() + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.gaslimit() + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @chainid_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @chainid_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.chainid() +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.chainid() +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.chainid() + %v2 = call i256 @llvm.evm.chainid() + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.chainid() + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @basefee_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @basefee_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.basefee() +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.basefee() +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.basefee() + %v2 = call i256 @llvm.evm.basefee() + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.basefee() + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @blobbasefee_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @blobbasefee_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.blobbasefee() +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.blobbasefee() +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.blobbasefee() + %v2 = call i256 @llvm.evm.blobbasefee() + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.blobbasefee() + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + +define i256 @calldatasize() nounwind { +; CHECK-LABEL: define i256 @calldatasize +; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.calldatasize() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.calldatasize() + %res2 = call i256 @llvm.evm.calldatasize() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @calldataload(ptr addrspace(2) %rs1) nounwind { +; CHECK-LABEL: define i256 @calldataload +; CHECK-SAME: (ptr addrspace(2) [[RS1:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.calldataload(ptr addrspace(2) [[RS1]]) +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.calldataload(ptr addrspace(2) %rs1) + %res2 = call i256 @llvm.evm.calldataload(ptr addrspace(2) %rs1) + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @callvalue() nounwind { +; CHECK-LABEL: define i256 @callvalue +; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.callvalue() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.callvalue() + %res2 = call i256 @llvm.evm.callvalue() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @codesize() nounwind { +; CHECK-LABEL: define i256 @codesize +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.codesize() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.codesize() + %res2 = call i256 @llvm.evm.codesize() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @gasprice() nounwind { +; CHECK-LABEL: define i256 @gasprice +; CHECK-SAME: () local_unnamed_addr #[[ATTR5]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.gasprice() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.gasprice() + %res2 = call i256 @llvm.evm.gasprice() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @extcodesize(i256 %rs1) nounwind { +; CHECK-LABEL: define i256 @extcodesize +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR5]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.extcodesize(i256 [[RS1]]) +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.extcodesize(i256 %rs1) + %res2 = call i256 @llvm.evm.extcodesize(i256 %rs1) + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @extcodesize_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @extcodesize_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.extcodesize(i256 [[ADDR]]) +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.extcodesize(i256 [[ADDR]]) +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.extcodesize(i256 %addr) + %v2 = call i256 @llvm.evm.extcodesize(i256 %addr) + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.extcodesize(i256 %addr) + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + +define i256 @extcodehash(i256 %rs1) nounwind { +; CHECK-LABEL: define i256 @extcodehash +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR5]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.extcodehash(i256 [[RS1]]) +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.extcodehash(i256 %rs1) + %res2 = call i256 @llvm.evm.extcodehash(i256 %rs1) + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @extcodehash_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @extcodehash_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.extcodehash(i256 [[ADDR]]) +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.extcodehash(i256 [[ADDR]]) +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.extcodehash(i256 %addr) + %v2 = call i256 @llvm.evm.extcodehash(i256 %addr) + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.extcodehash(i256 %addr) + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @extcodecopy_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @extcodecopy_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6]] { +; CHECK-NEXT: tail call void @llvm.evm.extcodecopy(i256 [[ADDR]], ptr addrspace(1) null, ptr addrspace(4) null, i256 32) +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.extcodehash(i256 [[ADDR]]) +; CHECK-NEXT: tail call void @llvm.evm.extcodecopy(i256 [[ADDR]], ptr addrspace(1) null, ptr addrspace(4) null, i256 32) +; CHECK-NEXT: [[V2:%.*]] = tail call i256 @llvm.evm.extcodehash(i256 [[ADDR]]) +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.extcodehash(i256 [[ADDR]]) +; CHECK-NEXT: [[TMP:%.*]] = add i256 [[V2]], [[V1]] +; CHECK-NEXT: [[RET:%.*]] = add i256 [[TMP]], [[V3]] +; CHECK-NEXT: tail call void @llvm.evm.extcodecopy(i256 [[ADDR]], ptr addrspace(1) null, ptr addrspace(4) null, i256 32) +; CHECK-NEXT: ret i256 [[RET]] +; + call void @llvm.evm.extcodecopy(i256 %addr, ptr addrspace(1) null, ptr addrspace(4) null, i256 32) + %v1 = call i256 @llvm.evm.extcodehash(i256 %addr) + call void @llvm.evm.extcodecopy(i256 %addr, ptr addrspace(1) null, ptr addrspace(4) null, i256 32) + %v2 = call i256 @llvm.evm.extcodehash(i256 %addr) + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.extcodehash(i256 %addr) + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + call void @llvm.evm.extcodecopy(i256 %addr, ptr addrspace(1) null, ptr addrspace(4) null, i256 32) + ret i256 %ret +} + +define i256 @returndatasize() nounwind { +; CHECK-LABEL: define i256 @returndatasize +; CHECK-SAME: () local_unnamed_addr #[[ATTR5]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.returndatasize() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.returndatasize() + %res2 = call i256 @llvm.evm.returndatasize() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @blockhash(i256 %rs1) nounwind { +; CHECK-LABEL: define i256 @blockhash +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR5]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.blockhash(i256 [[RS1]]) +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.blockhash(i256 %rs1) + %res2 = call i256 @llvm.evm.blockhash(i256 %rs1) + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @blobhash(i256 %rs1) nounwind { +; CHECK-LABEL: define i256 @blobhash +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR5]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.blobhash(i256 [[RS1]]) +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.blobhash(i256 %rs1) + %res2 = call i256 @llvm.evm.blobhash(i256 %rs1) + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @coinbase() nounwind { +; CHECK-LABEL: define i256 @coinbase +; CHECK-SAME: () local_unnamed_addr #[[ATTR5]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.coinbase() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.coinbase() + %res2 = call i256 @llvm.evm.coinbase() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @timestamp() nounwind { +; CHECK-LABEL: define i256 @timestamp +; CHECK-SAME: () local_unnamed_addr #[[ATTR5]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.timestamp() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.timestamp() + %res2 = call i256 @llvm.evm.timestamp() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @number() nounwind { +; CHECK-LABEL: define i256 @number +; CHECK-SAME: () local_unnamed_addr #[[ATTR5]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.number() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.number() + %res2 = call i256 @llvm.evm.number() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @difficulty() nounwind { +; CHECK-LABEL: define i256 @difficulty +; CHECK-SAME: () local_unnamed_addr #[[ATTR5]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.difficulty() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.difficulty() + %res2 = call i256 @llvm.evm.difficulty() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @gaslimit() nounwind { +; CHECK-LABEL: define i256 @gaslimit +; CHECK-SAME: () local_unnamed_addr #[[ATTR5]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.gaslimit() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.gaslimit() + %res2 = call i256 @llvm.evm.gaslimit() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @chainid() nounwind { +; CHECK-LABEL: define i256 @chainid +; CHECK-SAME: () local_unnamed_addr #[[ATTR5]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.chainid() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.chainid() + %res2 = call i256 @llvm.evm.chainid() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @selfbalance() nounwind { +; CHECK-LABEL: define i256 @selfbalance +; CHECK-SAME: () local_unnamed_addr #[[ATTR5]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.selfbalance() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.selfbalance() + %res2 = call i256 @llvm.evm.selfbalance() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @selfbalance_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @selfbalance_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.selfbalance() +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.selfbalance() +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.selfbalance() + %v2 = call i256 @llvm.evm.selfbalance() + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.selfbalance() + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + +define i256 @basefee() nounwind { +; CHECK-LABEL: define i256 @basefee +; CHECK-SAME: () local_unnamed_addr #[[ATTR5]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.basefee() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.basefee() + %res2 = call i256 @llvm.evm.basefee() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @blobbasefee() nounwind { +; CHECK-LABEL: define i256 @blobbasefee +; CHECK-SAME: () local_unnamed_addr #[[ATTR5]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.blobbasefee() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.blobbasefee() + %res2 = call i256 @llvm.evm.blobbasefee() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define void @log0(ptr addrspace(1) %off, i256 %size) nounwind { +; CHECK-LABEL: define void @log0 +; CHECK-SAME: (ptr addrspace(1) nocapture readonly [[OFF:%.*]], i256 [[SIZE:%.*]]) local_unnamed_addr #[[ATTR7:[0-9]+]] { +; CHECK-NEXT: tail call void @llvm.evm.log0(ptr addrspace(1) [[OFF]], i256 [[SIZE]]) +; CHECK-NEXT: tail call void @llvm.evm.log0(ptr addrspace(1) [[OFF]], i256 [[SIZE]]) +; CHECK-NEXT: ret void +; + call void @llvm.evm.log0(ptr addrspace(1) %off, i256 %size) + call void @llvm.evm.log0(ptr addrspace(1) %off, i256 %size) + ret void +} + +define void @log1(ptr addrspace(1) %off, i256 %size, i256 %t1) nounwind { +; CHECK-LABEL: define void @log1 +; CHECK-SAME: (ptr addrspace(1) nocapture readonly [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]]) local_unnamed_addr #[[ATTR7]] { +; CHECK-NEXT: tail call void @llvm.evm.log1(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]]) +; CHECK-NEXT: tail call void @llvm.evm.log1(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]]) +; CHECK-NEXT: ret void +; + call void @llvm.evm.log1(ptr addrspace(1) %off, i256 %size, i256 %t1) + call void @llvm.evm.log1(ptr addrspace(1) %off, i256 %size, i256 %t1) + ret void +} + +define void @log2(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2) nounwind { +; CHECK-LABEL: define void @log2 +; CHECK-SAME: (ptr addrspace(1) nocapture readonly [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]], i256 [[T2:%.*]]) local_unnamed_addr #[[ATTR7]] { +; CHECK-NEXT: tail call void @llvm.evm.log2(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]], i256 [[T2]]) +; CHECK-NEXT: tail call void @llvm.evm.log2(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]], i256 [[T2]]) +; CHECK-NEXT: ret void +; + call void @llvm.evm.log2(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2) + call void @llvm.evm.log2(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2) + ret void +} + +define void @log3(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3) nounwind { +; CHECK-LABEL: define void @log3 +; CHECK-SAME: (ptr addrspace(1) nocapture readonly [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]], i256 [[T2:%.*]], i256 [[T3:%.*]]) local_unnamed_addr #[[ATTR7]] { +; CHECK-NEXT: tail call void @llvm.evm.log3(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]], i256 [[T2]], i256 [[T3]]) +; CHECK-NEXT: tail call void @llvm.evm.log3(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]], i256 [[T2]], i256 [[T3]]) +; CHECK-NEXT: ret void +; + call void @llvm.evm.log3(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3) + call void @llvm.evm.log3(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3) + ret void +} + +define void @log4(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3, i256 %t4) nounwind { +; CHECK-LABEL: define void @log4 +; CHECK-SAME: (ptr addrspace(1) nocapture readonly [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]], i256 [[T2:%.*]], i256 [[T3:%.*]], i256 [[T4:%.*]]) local_unnamed_addr #[[ATTR7]] { +; CHECK-NEXT: tail call void @llvm.evm.log4(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]], i256 [[T2]], i256 [[T3]], i256 [[T4]]) +; CHECK-NEXT: tail call void @llvm.evm.log4(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]], i256 [[T2]], i256 [[T3]], i256 [[T4]]) +; CHECK-NEXT: ret void +; + call void @llvm.evm.log4(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3, i256 %t4) + call void @llvm.evm.log4(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3, i256 %t4) + ret void +} + +declare i256 @llvm.evm.addmod(i256, i256, i256) +declare i256 @llvm.evm.mulmod(i256, i256, i256) +declare i256 @llvm.evm.exp(i256, i256) +declare i256 @llvm.evm.sha3(ptr addrspace(1), i256) +declare i256 @llvm.evm.signextend(i256, i256) +declare i256 @llvm.evm.byte(i256, i256) +declare i256 @llvm.evm.pc() +declare i256 @llvm.evm.gas() +declare i256 @llvm.evm.msize() +declare i256 @llvm.evm.address() +declare i256 @llvm.evm.origin() +declare i256 @llvm.evm.caller() +declare i256 @llvm.evm.balance(i256) +declare i256 @llvm.evm.calldatasize() +declare i256 @llvm.evm.calldataload(ptr addrspace(2)) +declare i256 @llvm.evm.callvalue() +declare i256 @llvm.evm.codesize() +declare i256 @llvm.evm.gasprice() +declare i256 @llvm.evm.extcodesize(i256) +declare i256 @llvm.evm.extcodehash(i256) +declare void @llvm.evm.extcodecopy(i256, ptr addrspace(1), ptr addrspace(4), i256) +declare i256 @llvm.evm.blockhash(i256) +declare i256 @llvm.evm.blobhash(i256) +declare i256 @llvm.evm.returndatasize() +declare i256 @llvm.evm.coinbase() +declare i256 @llvm.evm.timestamp() +declare i256 @llvm.evm.number() +declare i256 @llvm.evm.difficulty() +declare i256 @llvm.evm.gaslimit() +declare i256 @llvm.evm.chainid() +declare i256 @llvm.evm.selfbalance() +declare i256 @llvm.evm.basefee() +declare i256 @llvm.evm.blobbasefee() +declare void @llvm.evm.log0(ptr addrspace(1), i256) +declare void @llvm.evm.log1(ptr addrspace(1), i256, i256) +declare void @llvm.evm.log2(ptr addrspace(1), i256, i256, i256) +declare void @llvm.evm.log3(ptr addrspace(1), i256, i256, i256, i256) +declare void @llvm.evm.log4(ptr addrspace(1), i256, i256, i256, i256, i256) +declare i256 @llvm.evm.call(i256, i256, i256, ptr addrspace(1), i256, ptr addrspace(1), i256) diff --git a/llvm/test/CodeGen/EVM/dag-combine-select.ll b/llvm/test/CodeGen/EVM/dag-combine-select.ll new file mode 100644 index 000000000000..05103d202278 --- /dev/null +++ b/llvm/test/CodeGen/EVM/dag-combine-select.ll @@ -0,0 +1,335 @@ +; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5 +; RUN: llc -stop-after=finalize-isel < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +; select C, X, 0 -> X * C +define i256 @sel_x_or_0(i1 %cond, i256 %x) { + ; CHECK-LABEL: name: sel_x_or_0 + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL + ; CHECK-NEXT: RET killed [[MUL]] +%val = select i1 %cond, i256 %x, i256 0 +ret i256 %val +} + +; select C, 0, Y -> not C * Y +define i256 @sel_0_or_y(i1 %cond, i256 %y) { + ; CHECK-LABEL: name: sel_0_or_y + ; CHECK: [[ISZERO:%[0-9]+]]:gpr = ISZERO + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL killed [[ISZERO]], killed {{.*}} + ; CHECK-NEXT: RET killed [[MUL]], implicit-def dead $arguments +%val = select i1 %cond, i256 0, i256 %y +ret i256 %val +} + +; select C, X, 1 -> C * X + not C +define i256 @sel_x_or_1(i1 %cond, i256 %x) { + ; CHECK-LABEL: name: sel_x_or_1 + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL {{.*}}, [[COND:%[0-9]+]] + ; CHECK-NEXT: [[ISZERO:%[0-9]+]]:gpr = ISZERO [[COND]] + ; CHECK-NEXT: [[ADD:%[0-9]+]]:gpr = ADD killed [[ISZERO]], killed [[MUL]] + ; CHECK-NEXT: RET killed [[ADD]] +%val = select i1 %cond, i256 %x, i256 1 +ret i256 %val +} + +; select C, 1, Y -> C + not C * Y +define i256 @sel_1_or_y(i1 %cond, i256 %y) { + ; CHECK-LABEL: name: sel_1_or_y + ; CHECK: [[ISZERO:%[0-9]+]]:gpr = ISZERO [[COND:%[0-9]+]] + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL {{.*}}, killed [[ISZERO]] + ; CHECK-NEXT: [[ADD:%[0-9]+]]:gpr = ADD [[COND]], killed [[MUL]] + ; CHECK-NEXT: RET killed [[ADD]] +%val = select i1 %cond, i256 1, i256 %y +ret i256 %val +} + +; select C, X, -1 -> (C - 1) | X* +define i256 @sel_x_or_m1(i1 %cond, i256 %x) { + ; CHECK-LABEL: name: sel_x_or_m1 + ; CHECK: [[CONST_1:%[0-9]+]]:gpr = CONST_I256 i256 1 + ; CHECK-NEXT: [[AND:%[0-9]+]]:gpr = AND {{.*}}, [[CONST_1]] + ; CHECK-NEXT: [[SUB:%[0-9]+]]:gpr = SUB killed [[AND]], [[CONST_1]] + ; CHECK-NEXT: [[X:%[0-9]+]]:gpr = COPY killed {{.*}} + ; CHECK-NEXT: [[OR:%[0-9]+]]:gpr = OR killed [[SUB]], killed [[X]] + ; CHECK-NEXT: RET killed [[OR]] + %val = select i1 %cond, i256 %x, i256 -1 + ret i256 %val +} + +; select C, -1, Y -> -C | Y* +define i256 @sel_m1_or_y(i1 %cond, i256 %y) { + ; CHECK-LABEL: name: sel_m1_or_y + ; CHECK: [[CONST_1:%[0-9]+]]:gpr = CONST_I256 i256 1 + ; CHECK-NEXT: [[AND:%[0-9]+]]:gpr = AND {{.*}}, killed [[CONST_1]] + ; CHECK-NEXT: [[CONST_0:%[0-9]+]]:gpr = CONST_I256 i256 0 + ; CHECK-NEXT: [[SUB:%[0-9]+]]:gpr = SUB killed [[CONST_0]], killed [[AND]] + ; CHECK-NEXT: [[Y:%[0-9]+]]:gpr = COPY killed {{.*}} + ; CHECK-NEXT: [[OR:%[0-9]+]]:gpr = OR killed [[SUB]], killed [[Y]] + ; CHECK-NEXT: RET killed [[OR]] + %val = select i1 %cond, i256 -1, i256 %y + ret i256 %val +} + +; select C, (xor Y, X), Y -> xor Y, (select C, X, 0) -> xor Y, X * C +define i256 @select_xor(i256 %A, i256 %B, i1 %cond) { + ; CHECK-LABEL: name: select_xor + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL {{.*}}, killed {{.*}} + ; CHECK-NEXT: [[XOR:%[0-9]+]]:gpr = XOR {{.*}}, killed [[MUL]] + ; CHECK-NEXT: RET killed [[XOR]] + %c = xor i256 %B, %A + %2 = select i1 %cond, i256 %c, i256 %B + ret i256 %2 +} + +; select C, Y, (xor Y, X) -> xor Y, (select not C, X, 0) -> xor Y, X * not C +define i256 @select_xor_swapped(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_xor_swapped + ; CHECK: [[NOTC:%[0-9]+]]:gpr = ISZERO killed {{.*}} + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL killed [[NOTC]], killed {{.*}} + ; CHECK-NEXT: [[XOR:%[0-9]+]]:gpr = XOR {{.*}}, killed [[MUL]] + ; CHECK-NEXT: RET killed [[XOR]] + %c = xor i256 %A, %B + %res = select i1 %cond, i256 %B, i256 %c + ret i256 %res +} + +; commutability check +define i256 @select_xor_2(i256 %A, i256 %B, i1 %cond) { + ; CHECK-LABEL: name: select_xor_2 + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL {{.*}}, killed {{.*}} + ; CHECK-NEXT: [[XOR:%[0-9]+]]:gpr = XOR {{.*}}, killed [[MUL]] + ; CHECK-NEXT: RET killed [[XOR]] + %c = xor i256 %B, %A + %2 = select i1 %cond, i256 %c, i256 %A + ret i256 %2 +} + +; select C, (or Y, X), Y -> or Y, (select C, X, 0) -> or Y, X * C +define i256 @select_or(i256 %A, i256 %B, i1 %cond) { + ; CHECK-LABEL: name: select_or + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL {{.*}}, killed {{.*}} + ; CHECK-NEXT: [[OR:%[0-9]+]]:gpr = OR {{.*}}, killed [[MUL]] + ; CHECK-NEXT: RET killed [[OR]] + %c = or i256 %B, %A + %2 = select i1 %cond, i256 %c, i256 %B + ret i256 %2 +} + +; select C, Y, (or Y, X) -> or Y, (select not C, X, 0) -> or Y, X * not C +define i256 @select_or_swapped(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_or_swapped + ; CHECK: [[NOTC:%[0-9]+]]:gpr = ISZERO killed {{.*}} + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL killed [[NOTC]], killed {{.*}} + ; CHECK-NEXT: [[OR:%[0-9]+]]:gpr = OR {{.*}}, killed [[MUL]] + ; CHECK-NEXT: RET killed [[OR]] + %c = or i256 %A, %B + %res = select i1 %cond, i256 %B, i256 %c + ret i256 %res +} + +; commutability check +define i256 @select_or_2(i256 %A, i256 %B, i1 %cond) { + ; CHECK-LABEL: name: select_or_2 + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL {{.*}}, killed {{.*}} + ; CHECK-NEXT: [[OR:%[0-9]+]]:gpr = OR {{.*}}, killed [[MUL]] + ; CHECK-NEXT: RET killed [[OR]] + %c = or i256 %B, %A + %2 = select i1 %cond, i256 %c, i256 %A + ret i256 %2 +} + +; select C, (add Y, X), Y -> add Y, (select C, X, 0) -> add Y, X * C +define i256 @select_add(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_add + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL {{.*}}, killed {{.*}} + ; CHECK-NEXT: [[ADD:%[0-9]+]]:gpr = ADD {{.*}}, killed [[MUL]] + ; CHECK-NEXT: RET killed [[ADD]] + %c = add i256 %A, %B + %res = select i1 %cond, i256 %c, i256 %A + ret i256 %res +} + +; select C, Y, (add Y, X) -> add Y, (select not C, X, 0) -> add Y, X * not C +define i256 @select_add_swapped(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_add_swapped + ; CHECK: [[NOTC:%[0-9]+]]:gpr = ISZERO killed {{.*}} + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL killed [[NOTC]], killed {{.*}} + ; CHECK-NEXT: [[ADD:%[0-9]+]]:gpr = ADD {{.*}}, killed [[MUL]] + ; CHECK-NEXT: RET killed [[ADD]] + %c = add i256 %A, %B + %res = select i1 %cond, i256 %A, i256 %c + ret i256 %res +} + +; commutability check +define i256 @select_add_2(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_add_2 + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL {{.*}}, killed {{.*}} + ; CHECK-NEXT: [[ADD:%[0-9]+]]:gpr = ADD {{.*}}, killed [[MUL]] + ; CHECK-NEXT: RET killed [[ADD]] + %c = add i256 %A, %B + %res = select i1 %cond, i256 %c, i256 %B + ret i256 %res +} + +; select C, (sub Y, X), Y -> sub Y, (select C, X, 0) -> sub Y, X * C +define i256 @select_sub(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_sub + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL {{.*}}, killed {{.*}} + ; CHECK-NEXT: [[SUB:%[0-9]+]]:gpr = SUB {{.*}}, killed [[MUL]] + ; CHECK-NEXT: RET killed [[SUB]] + %c = sub i256 %A, %B + %res = select i1 %cond, i256 %c, i256 %A + ret i256 %res +} + +; select C, Y, (sub Y, X) -> sub Y, (select not C, X, 0) -> sub Y, X * not C +define i256 @select_sub_swapped(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_sub + ; CHECK: [[NOTC:%[0-9]+]]:gpr = ISZERO killed {{.*}} + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL killed [[NOTC]], killed {{.*}} + ; CHECK-NEXT: [[SUB:%[0-9]+]]:gpr = SUB {{.*}}, killed [[MUL]] + ; CHECK-NEXT: RET killed [[SUB]] + %c = sub i256 %A, %B + %res = select i1 %cond, i256 %A, i256 %c + ret i256 %res +} + +; not commutative - normal select expansion +define i256 @select_sub_2(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_sub_2 + ; CHECK-NOT: MUL + ; CHECK: JUMPI + %c = sub i256 %A, %B + %res = select i1 %cond, i256 %c, i256 %B + ret i256 %res +} + +; negative - select(and) expands normally +define i256 @select_and(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_and + ; CHECK-NOT: MUL + ; CHECK: JUMPI + %c = and i256 %A, %B + %res = select i1 %cond, i256 %c, i256 %B + ret i256 %res +} + +; negative - select(and) expands normally +define i256 @select_and_2(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_and_2 + ; CHECK-NOT: MUL + ; CHECK: JUMPI + %c = and i256 %A, %B + %res = select i1 %cond, i256 %A, i256 %c + ret i256 %res +} + +; select C, (shl Y, X), Y -> shl Y, (select C, X, 0) -> shl Y, X * C +define i256 @select_shl(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_shl + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL {{.*}}, killed {{.*}} + ; SHL shift, val + ; CHECK-NEXT: [[SHL:%[0-9]+]]:gpr = SHL killed [[MUL]], {{.*}} + ; CHECK-NEXT: RET killed [[SHL]] + %c = shl i256 %A, %B + %res = select i1 %cond, i256 %c, i256 %A + ret i256 %res +} + +; select C, Y, (shl Y, X) -> shl Y, (select not C, X, 0) +define i256 @select_shl_swapped(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_shl_swapped + ; CHECK: [[NOTC:%[0-9]+]]:gpr = ISZERO killed {{.*}} + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL killed [[NOTC]], killed {{.*}} + ; SHR shift, val + ; CHECK-NEXT: [[SHL:%[0-9]+]]:gpr = SHL killed [[MUL]], {{.*}} + ; CHECK-NEXT: RET killed [[SHL]] + %c = shl i256 %A, %B + %res = select i1 %cond, i256 %A, i256 %c + ret i256 %res +} + +; not commutative - normal select expansion +define i256 @select_shl_2(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_shl_2 + ; CHECK-NOT: MUL + ; CHECK: JUMPI + %c = shl i256 %A, %B + %res = select i1 %cond, i256 %c, i256 %B + ret i256 %res +} + +; select C, (ashr Y, X), Y -> ashr Y, (select C, X, 0) -> ashr Y, X * C +define i256 @select_ashr(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_ashr + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL {{.*}}, killed {{.*}} + ; SAR shift, val + ; CHECK-NEXT: [[SAR:%[0-9]+]]:gpr = SAR killed [[MUL]], {{.*}} + ; CHECK-NEXT: RET killed [[SAR]] + %c = ashr i256 %A, %B + %res = select i1 %cond, i256 %c, i256 %A + ret i256 %res +} + +; select C, Y, (ashr Y, X) -> ashr Y, (select not C, X, 0) +define i256 @select_ashr_swapped(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_ashr_swapped + ; CHECK: [[NOTC:%[0-9]+]]:gpr = ISZERO killed {{.*}} + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL killed [[NOTC]], killed {{.*}} + ; SHR shift, val + ; CHECK-NEXT: [[SAR:%[0-9]+]]:gpr = SAR killed [[MUL]], {{.*}} + ; CHECK-NEXT: RET killed [[SAR]] + %c = ashr i256 %A, %B + %res = select i1 %cond, i256 %A, i256 %c + ret i256 %res +} + +; not commutative - normal select expansion +define i256 @select_ashr_2(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_ashr_2 + ; CHECK-NOT: MUL + ; CHECK: JUMPI + %c = ashr i256 %A, %B + %res = select i1 %cond, i256 %c, i256 %B + ret i256 %res +} + +; select C, (lshr Y, X), Y -> shl Y, (select C, X, 0) +define i256 @select_lshr(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_lshr + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL {{.*}}, killed {{.*}} + ; SHR shift, val + ; CHECK-NEXT: [[SHR:%[0-9]+]]:gpr = SHR killed [[MUL]], {{.*}} + ; CHECK-NEXT: RET killed [[SHR]] + %c = lshr i256 %A, %B + %res = select i1 %cond, i256 %c, i256 %A + ret i256 %res +} + +; select C, Y, (lshr Y, X) -> lshr Y, (select not C, X, 0) +define i256 @select_lshr_swapped(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_lshr_swapped + ; CHECK: [[NOTC:%[0-9]+]]:gpr = ISZERO killed {{.*}} + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL killed [[NOTC]], killed {{.*}} + ; SHR shift, val + ; CHECK-NEXT: [[SHR:%[0-9]+]]:gpr = SHR killed [[MUL]], {{.*}} + ; CHECK-NEXT: RET killed [[SHR]] + %c = lshr i256 %A, %B + %res = select i1 %cond, i256 %A, i256 %c + ret i256 %res +} + +; not commutative - normal select expansion +define i256 @select_lshr_2(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_lshr_2 + ; CHECK-NOT: MUL + ; CHECK: JUMPI + %c = lshr i256 %A, %B + %res = select i1 %cond, i256 %c, i256 %B + ret i256 %res +} +;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: +; CHECK: {{.*}} diff --git a/llvm/test/CodeGen/EVM/data-sections-in-code-glob-order.ll b/llvm/test/CodeGen/EVM/data-sections-in-code-glob-order.ll new file mode 100644 index 000000000000..1833a6ee81cf --- /dev/null +++ b/llvm/test/CodeGen/EVM/data-sections-in-code-glob-order.ll @@ -0,0 +1,62 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc < %s | FileCheck %s +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +@data1 = private unnamed_addr addrspace(4) constant [5 x i8] c"xQm7A" +@data2 = private unnamed_addr addrspace(4) constant [5 x i8] c"T2bLp" +@data3 = private unnamed_addr addrspace(4) constant [5 x i8] c"rZ8Wd" +@data4 = private unnamed_addr addrspace(4) constant [5 x i8] c"NfC3y" +@data5 = private unnamed_addr addrspace(4) constant [5 x i8] c"qHs0J" + +declare void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(4) noalias nocapture readonly, i256, i1 immarg) + +define void @test() noreturn { +; CHECK-LABEL: test: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x5 +; CHECK-NEXT: PUSH4 @code_data_section +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: CODECOPY +; CHECK-NEXT: PUSH1 0x5 +; CHECK-NEXT: PUSH4 @code_data_section+5 +; CHECK-NEXT: PUSH1 0x8 +; CHECK-NEXT: CODECOPY +; CHECK-NEXT: PUSH1 0x5 +; CHECK-NEXT: PUSH4 @code_data_section+10 +; CHECK-NEXT: PUSH1 0x10 +; CHECK-NEXT: CODECOPY +; CHECK-NEXT: PUSH1 0x5 +; CHECK-NEXT: PUSH4 @code_data_section+15 +; CHECK-NEXT: PUSH1 0x18 +; CHECK-NEXT: CODECOPY +; CHECK-NEXT: PUSH1 0x5 +; CHECK-NEXT: PUSH4 @code_data_section+20 +; CHECK-NEXT: PUSH1 0x20 +; CHECK-NEXT: CODECOPY +; CHECK-NEXT: PUSH1 0x80 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT +; CHECK: INVALID +; CHECK-LABEL: code_data_section: +; CHECK-NEXT: .byte 0x78, 0x51, 0x6d, 0x37 +; CHECK-NEXT: .byte 0x41, 0x54, 0x32, 0x62 +; CHECK-NEXT: .byte 0x4c, 0x70, 0x72, 0x5a +; CHECK-NEXT: .byte 0x38, 0x57, 0x64, 0x4e +; CHECK-NEXT: .byte 0x66, 0x43, 0x33, 0x79 +; CHECK-NEXT: .byte 0x71, 0x48, 0x73, 0x30 +; CHECK-NEXT: .byte 0x4a + + call void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) null, ptr addrspace(4) @data1, i256 5, i1 false) + %dst2 = inttoptr i256 8 to ptr addrspace(1) + call void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) %dst2, ptr addrspace(4) @data2, i256 5, i1 false) + %dst3 = inttoptr i256 16 to ptr addrspace(1) + call void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) %dst3, ptr addrspace(4) @data3, i256 5, i1 false) + %dst4 = inttoptr i256 24 to ptr addrspace(1) + call void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) %dst4, ptr addrspace(4) @data4, i256 5, i1 false) + %dst5 = inttoptr i256 32 to ptr addrspace(1) + call void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) %dst5, ptr addrspace(4) @data5, i256 5, i1 false) + call void @llvm.evm.revert(ptr addrspace(1) null, i256 128) + unreachable +} diff --git a/llvm/test/CodeGen/EVM/data-sections-in-code.ll b/llvm/test/CodeGen/EVM/data-sections-in-code.ll new file mode 100644 index 000000000000..3cc146344acd --- /dev/null +++ b/llvm/test/CodeGen/EVM/data-sections-in-code.ll @@ -0,0 +1,52 @@ +; RUN: llc < %s | FileCheck %s +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +@data1 = private unnamed_addr addrspace(4) constant [13 x i8] c"hello world\0A\00" +@data2 = private unnamed_addr addrspace(4) constant [13 x i8] c"hello world\0A\00" +@data3 = private unnamed_addr addrspace(4) constant [5 x i8] c"world" +@data4 = private unnamed_addr addrspace(4) constant [7 x i8] c"another" + +declare void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(4) noalias nocapture readonly, i256, i1 immarg) + +define void @test() noreturn { +; CHECK-LABEL: test: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0xC +; CHECK-NEXT: PUSH4 @code_data_section +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: CODECOPY +; CHECK-NEXT: PUSH1 0xD +; CHECK-NEXT: PUSH4 @code_data_section +; CHECK-NEXT: PUSH1 0x20 +; CHECK-NEXT: CODECOPY +; CHECK-NEXT: PUSH1 0x5 +; CHECK-NEXT: PUSH4 @code_data_section+6 +; CHECK-NEXT: PUSH1 0x40 +; CHECK-NEXT: CODECOPY +; CHECK-NEXT: PUSH1 0x7 +; CHECK-NEXT: PUSH4 @code_data_section+13 +; CHECK-NEXT: PUSH1 0x60 +; CHECK-NEXT: CODECOPY +; CHECK-NEXT: PUSH1 0x80 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT +; CHECK: INVALID +; CHECK-LABEL: code_data_section: +; CHECK-NEXT: .byte 0x68, 0x65, 0x6c, 0x6c +; CHECK-NEXT: .byte 0x6f, 0x20, 0x77, 0x6f +; CHECK-NEXT: .byte 0x72, 0x6c, 0x64, 0x0a +; CHECK-NEXT: .byte 0x00, 0x61, 0x6e, 0x6f +; CHECK-NEXT: .byte 0x74, 0x68, 0x65, 0x72 + + call void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) null, ptr addrspace(4) @data1, i256 12, i1 false) + %dst2 = inttoptr i256 32 to ptr addrspace(1) + call void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) %dst2, ptr addrspace(4) @data2, i256 13, i1 false) + %dst3 = inttoptr i256 64 to ptr addrspace(1) + call void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) %dst3, ptr addrspace(4) @data3, i256 5, i1 false) + %dst4 = inttoptr i256 96 to ptr addrspace(1) + call void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) %dst4, ptr addrspace(4) @data4, i256 7, i1 false) + call void @llvm.evm.revert(ptr addrspace(1) null, i256 128) + unreachable +} diff --git a/llvm/test/CodeGen/EVM/div.ll b/llvm/test/CodeGen/EVM/div.ll new file mode 100644 index 000000000000..a5b04c26d47a --- /dev/null +++ b/llvm/test/CodeGen/EVM/div.ll @@ -0,0 +1,42 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @udivrrr(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: udivrrr: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DIV +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = udiv i256 %rs1, %rs2 + ret i256 %res +} + +define i256 @sdivrrr(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: sdivrrr: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SDIV +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = sdiv i256 %rs1, %rs2 + ret i256 %res +} + +define i256 @sdivrri(i256 %rs1) nounwind { +; CHECK-LABEL: sdivrri: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = sdiv i256 %rs1, 0 + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/dont-avoid-shift-transformations.ll b/llvm/test/CodeGen/EVM/dont-avoid-shift-transformations.ll new file mode 100644 index 000000000000..7db854e4c5bf --- /dev/null +++ b/llvm/test/CodeGen/EVM/dont-avoid-shift-transformations.ll @@ -0,0 +1,149 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc -O3 < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +; Check the following conversion in TargetLowering::SimplifySetCC +; (X & 8) != 0 --> (X & 8) >> 3 +define i256 @test1(i256 %x) { +; CHECK-LABEL: test1: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x20 +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 0x5 +; CHECK-NEXT: SHR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP +entry: + %and = and i256 %x, 32 + %cmp = icmp ne i256 %and, 0 + %conv = zext i1 %cmp to i256 + ret i256 %conv +} + +; Check the following conversion in TargetLowering::SimplifySetCC +; (X & 8) == 8 --> (X & 8) >> 3 +define i256 @test2(i256 %x) { +; CHECK-LABEL: test2: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x20 +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 0x5 +; CHECK-NEXT: SHR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP +entry: + %and = and i256 %x, 32 + %cmp = icmp eq i256 %and, 32 + %conv = zext i1 %cmp to i256 + ret i256 %conv +} + +; Check the following conversion in DAGCombiner::SimplifySelectCC +; (select_cc seteq (and x, y), 0, 0, A) -> (and (shr (shl x)) A) +define i256 @test3(i256 %x, i256 %a) { +; CHECK-LABEL: test3: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0xF4 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: SAR +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP +entry: + %and = and i256 %x, 2048 + %cmp = icmp eq i256 %and, 0 + %cond = select i1 %cmp, i256 0, i256 %a + ret i256 %cond +} + +; Check the following conversion in DAGCombiner foldExtendedSignBitTest +; sext i1 (setgt iN X, -1) --> sra (not X), (N - 1) +define i256 @test4(i256 %x) { +; CHECK-LABEL: test4: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: SAR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP +entry: + %cmp = icmp sgt i256 %x, -1 + %cond = sext i1 %cmp to i256 + ret i256 %cond +} + +; Check the following conversion in DAGCombiner foldExtendedSignBitTest +; zext i1 (setgt iN X, -1) --> srl (not X), (N - 1) +define i256 @test5(i256 %x) { +; CHECK-LABEL: test5: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: SHR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP +entry: + %cmp = icmp sgt i256 %x, -1 + %cond = zext i1 %cmp to i256 + ret i256 %cond +} + +; Check the following conversion in DAGCombiner::foldSelectCCToShiftAnd +; select_cc setlt X, 0, A, 0 -> and (sra X, size(X)-1), A +define i256 @test6(i256 %x, i256 %a) { +; CHECK-LABEL: test6: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: SAR +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP +entry: + %cmp = icmp slt i256 %x, 0 + %cond = select i1 %cmp, i256 %a, i256 0 + ret i256 %cond +} + +; Check the following conversion in DAGCombiner::foldSelectCCToShiftAnd +; select_cc setlt X, 0, A, 0 -> "and (srl X, C2), A" iff A is a single-bit +define i256 @test7(i256 %x) { +; CHECK-LABEL: test7: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: SHR +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: SHL +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP +entry: + %cmp = icmp slt i256 %x, 0 + %cond = select i1 %cmp, i256 2, i256 0 + ret i256 %cond +} + +; Check the following conversion in DAGCombiner::SimplifySelectCC +; select C, 16, 0 -> shl C, 4 +define i256 @test8(i256 %a, i256 %b) { +; CHECK-LABEL: test8: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SGT +; CHECK-NEXT: PUSH1 0x5 +; CHECK-NEXT: SHL +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP +entry: + %cmp = icmp sgt i256 %a, %b + %cond = select i1 %cmp, i256 32, i256 0 + ret i256 %cond +} diff --git a/llvm/test/CodeGen/EVM/evm-dse-free-ptr.ll b/llvm/test/CodeGen/EVM/evm-dse-free-ptr.ll new file mode 100644 index 000000000000..b847f178ebb5 --- /dev/null +++ b/llvm/test/CodeGen/EVM/evm-dse-free-ptr.ll @@ -0,0 +1,78 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -aa-pipeline=default -passes=dse -S < %s | FileCheck %s +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare i256 @llvm.evm.callvalue() +declare i256 @llvm.evm.calldatasize() +declare void @llvm.evm.return(ptr addrspace(1) readonly, i256) +declare void @llvm.evm.revert(ptr addrspace(1) readonly, i256) + +; Check that DSE eliminates the initialization of unused free memory pointer: +; store i256 128, ptr addrspace(1) inttoptr (i256 64 to ptr addrspace(1)), align 64 + +define void @remove_free_ptr() noreturn { +; CHECK-LABEL: define void @remove_free_ptr( +; CHECK-SAME: ) #[[ATTR2:[0-9]+]] { +; CHECK-NEXT: [[TRY:.*:]] +; CHECK-NEXT: [[CALLVALUE_I:%.*]] = tail call i256 @llvm.evm.callvalue() +; CHECK-NEXT: [[COMPARISON_RESULT_I:%.*]] = icmp eq i256 [[CALLVALUE_I]], 0 +; CHECK-NEXT: br i1 [[COMPARISON_RESULT_I]], label %"block_rt_1/0.i", label %"block_rt_2/0.i" +; CHECK: "block_rt_1/0.i": +; CHECK-NEXT: [[CALLDATASIZE_I:%.*]] = tail call i256 @llvm.evm.calldatasize() +; CHECK-NEXT: [[COMPARISON_RESULT7_I:%.*]] = icmp ult i256 [[CALLDATASIZE_I]], 4 +; CHECK-NEXT: br i1 [[COMPARISON_RESULT7_I]], label %"block_rt_2/0.i", label %[[SHIFT_RIGHT_NON_OVERFLOW_I:.*]] +; CHECK: "block_rt_2/0.i": +; CHECK-NEXT: tail call void @llvm.evm.revert(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 null, i256 0) +; CHECK-NEXT: unreachable +; CHECK: "block_rt_3/0.i": +; CHECK-NEXT: store i256 42, ptr addrspace(1) inttoptr (i256 128 to ptr addrspace(1)), align 128 +; CHECK-NEXT: tail call void @llvm.evm.return(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 inttoptr (i256 128 to ptr addrspace(1)), i256 32) +; CHECK-NEXT: unreachable +; CHECK: "block_rt_4/0.i": +; CHECK-NEXT: store i256 99, ptr addrspace(1) inttoptr (i256 128 to ptr addrspace(1)), align 128 +; CHECK-NEXT: tail call void @llvm.evm.return(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 inttoptr (i256 128 to ptr addrspace(1)), i256 32) +; CHECK-NEXT: unreachable +; CHECK: [[SHIFT_RIGHT_NON_OVERFLOW_I]]: +; CHECK-NEXT: [[CALLDATA_LOAD_RESULT_I:%.*]] = load i256, ptr addrspace(2) null, align 4294967296 +; CHECK-NEXT: [[SHIFT_RIGHT_NON_OVERFLOW_RESULT_I:%.*]] = lshr i256 [[CALLDATA_LOAD_RESULT_I]], 224 +; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw i256 [[SHIFT_RIGHT_NON_OVERFLOW_RESULT_I]] to i32 +; CHECK-NEXT: switch i32 [[TRUNC]], label %"block_rt_2/0.i" [ +; CHECK-NEXT: i32 1039457780, label %"block_rt_3/0.i" +; CHECK-NEXT: i32 1519042605, label %"block_rt_4/0.i" +; CHECK-NEXT: ] +; +try: + store i256 128, ptr addrspace(1) inttoptr (i256 64 to ptr addrspace(1)), align 64 + %callvalue.i = tail call i256 @llvm.evm.callvalue() + %comparison_result.i = icmp eq i256 %callvalue.i, 0 + br i1 %comparison_result.i, label %"block_rt_1/0.i", label %"block_rt_2/0.i" + +"block_rt_1/0.i": + %calldatasize.i = tail call i256 @llvm.evm.calldatasize() + %comparison_result7.i = icmp ult i256 %calldatasize.i, 4 + br i1 %comparison_result7.i, label %"block_rt_2/0.i", label %shift_right_non_overflow.i + +"block_rt_2/0.i": + tail call void @llvm.evm.revert(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 null, i256 0) + unreachable + +"block_rt_3/0.i": + store i256 42, ptr addrspace(1) inttoptr (i256 128 to ptr addrspace(1)), align 128 + tail call void @llvm.evm.return(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 inttoptr (i256 128 to ptr addrspace(1)), i256 32) + unreachable + +"block_rt_4/0.i": + store i256 99, ptr addrspace(1) inttoptr (i256 128 to ptr addrspace(1)), align 128 + tail call void @llvm.evm.return(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 inttoptr (i256 128 to ptr addrspace(1)), i256 32) + unreachable + +shift_right_non_overflow.i: + %calldata_load_result.i = load i256, ptr addrspace(2) null, align 4294967296 + %shift_right_non_overflow_result.i = lshr i256 %calldata_load_result.i, 224 + %trunc = trunc nuw i256 %shift_right_non_overflow_result.i to i32 + switch i32 %trunc, label %"block_rt_2/0.i" [ + i32 1039457780, label %"block_rt_3/0.i" + i32 1519042605, label %"block_rt_4/0.i" + ] +} diff --git a/llvm/test/CodeGen/EVM/evm-dse.ll b/llvm/test/CodeGen/EVM/evm-dse.ll new file mode 100644 index 000000000000..71424aa76ce8 --- /dev/null +++ b/llvm/test/CodeGen/EVM/evm-dse.ll @@ -0,0 +1,57 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -aa-pipeline=default -passes=dse -S < %s | FileCheck %s +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare i256 @llvm.evm.calldatasize() +declare void @llvm.evm.return(ptr addrspace(1) readonly, i256) +declare void @llvm.evm.revert(ptr addrspace(1) readonly, i256) +declare void @llvm.memcpy.p1.p3.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(3) noalias nocapture readonly, i256, i1 immarg) + +; This test verifies that unused heap stores can be safely eliminated before revert or return intrinsics. +; It also checks that stores to Storage or Transient Storage prior to a revert are eliminated. +define void @test(ptr addrspace(3) %src) noreturn { +; CHECK-LABEL: define void @test( +; CHECK-SAME: ptr addrspace(3) [[SRC:%.*]]) #[[ATTR3:[0-9]+]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[CALLDATASIZE:%.*]] = tail call i256 @llvm.evm.calldatasize() +; CHECK-NEXT: br label %[[IF_JOIN22:.*]] +; CHECK: [[IF_JOIN22]]: +; CHECK-NEXT: store i256 1, ptr addrspace(5) null, align 32 +; CHECK-NEXT: [[COMPARISON_RESULT_I:%.*]] = icmp ugt i256 [[CALLDATASIZE]], 4 +; CHECK-NEXT: br i1 [[COMPARISON_RESULT_I]], label %[[FUN_EXIT:.*]], label %[[IF_JOIN:.*]] +; CHECK: [[IF_JOIN]]: +; CHECK-NEXT: store i256 7, ptr addrspace(6) null, align 32 +; CHECK-NEXT: tail call void @llvm.evm.return(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 null, i256 32) +; CHECK-NEXT: unreachable +; CHECK: [[FUN_EXIT]]: +; CHECK-NEXT: tail call void @llvm.evm.revert(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 null, i256 0) +; CHECK-NEXT: unreachable +; +entry: + ; To be removed + store i256 128, ptr addrspace(1) inttoptr (i256 64 to ptr addrspace(1)), align 32 + %calldatasize = tail call i256 @llvm.evm.calldatasize() + br label %if_join22 + +if_join22: + store i256 1, ptr addrspace(5) null, align 32 + %comparison_result.i = icmp ugt i256 %calldatasize, 4 + br i1 %comparison_result.i, label %fun_exit, label %if_join + +if_join: + store i256 7, ptr addrspace(6) null, align 32 + ; To be removed + call void @llvm.memcpy.p1.p3.i256(ptr addrspace(1) inttoptr (i256 128 to ptr addrspace(1)), ptr addrspace(3) %src, i256 32, i1 false) + tail call void @llvm.evm.return(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 null, i256 32) + unreachable + +fun_exit: + ; To be removed + store i256 10, ptr addrspace(1) null, align 32 + ; To Be removed + store i256 2, ptr addrspace(5) null, align 32 + store i256 3, ptr addrspace(6) null, align 32 + tail call void @llvm.evm.revert(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 null, i256 0) + unreachable +} diff --git a/llvm/test/CodeGen/EVM/evm-peephole-negative.mir b/llvm/test/CodeGen/EVM/evm-peephole-negative.mir new file mode 100644 index 000000000000..20243b0166ac --- /dev/null +++ b/llvm/test/CodeGen/EVM/evm-peephole-negative.mir @@ -0,0 +1,85 @@ +# RUN: llc -x mir -mtriple=evm-unknown-unknown -run-pass=evm-peephole < %s | FileCheck %s + +--- | + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm-unknown-unknown" + + define void @iszero_fallthrough() { + ret void + } + + define void @iszero_iszero_fallthrough() { + ret void + } + + define void @iszero_iszero_or_fallthrough() { + ret void + } +... +--- +name: iszero_fallthrough +liveins: + - { reg: '$arguments', virtual-reg: '' } + - { reg: '$value_stack', virtual-reg: '' } +body: | + bb.0: + successors: %bb.1(0x80000000) + liveins: $arguments, $value_stack + + ISZERO_S + + bb.1: + successors: + liveins: $value_stack + +... +--- +name: iszero_iszero_fallthrough +liveins: + - { reg: '$arguments', virtual-reg: '' } + - { reg: '$value_stack', virtual-reg: '' } +body: | + bb.0: + successors: %bb.1(0x80000000) + liveins: $arguments, $value_stack + + ISZERO_S + ISZERO_S + + bb.1: + successors: + liveins: $value_stack + +... +--- +name: iszero_iszero_or_fallthrough +liveins: + - { reg: '$arguments', virtual-reg: '' } + - { reg: '$value_stack', virtual-reg: '' } +body: | + bb.0: + successors: %bb.1(0x80000000) + liveins: $arguments, $value_stack + + ISZERO_S + ISZERO_S + OR_S + + bb.1: + successors: + liveins: $value_stack + +... + +# CHECK-LABEL: name: iszero_fallthrough +# CHECK: ISZERO_S +# +# CHECK-LABEL: name: iszero_iszero_fallthrough +# CHECK: ISZERO_S +# CHECK-NEXT: ISZERO_S +# +# CHECK-LABEL: name: iszero_iszero_or_fallthrough +# CHECK: ISZERO_S +# CHECK-NEXT: ISZERO_S +# CHECK-NEXT: OR_S + diff --git a/llvm/test/CodeGen/EVM/evm-recursive.ll b/llvm/test/CodeGen/EVM/evm-recursive.ll new file mode 100644 index 000000000000..5d14b31d5ec8 --- /dev/null +++ b/llvm/test/CodeGen/EVM/evm-recursive.ll @@ -0,0 +1,39 @@ +; RUN: opt -passes=evm-mark-recursive-functions -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +; CHECK: define i256 @indirect_recursive2(i256 %x) #[[RECURSIVE:[0-9]+]] { +define i256 @indirect_recursive2(i256 %x) { +entry: + %call = call i256 @indirect_recursive1(i256 %x) + ret i256 %call +} + +; CHECK: define i256 @indirect_recursive1(i256 %y) #[[RECURSIVE:[0-9]+]] { +define i256 @indirect_recursive1(i256 %y) { +entry: + %call = call i256 @indirect_recursive2(i256 %y) + ret i256 %call +} + +; CHECK: define i256 @recursive(i256 %z) #[[RECURSIVE:[0-9]+]] { +define i256 @recursive(i256 %z) { +entry: + %call = call i256 @recursive(i256 %z) + ret i256 %call +} + +; CHECK: define i256 @calls_recursive(i256 %a) { +define i256 @calls_recursive(i256 %a) { +entry: + %call = call i256 @recursive(i256 %a) + ret i256 %call +} + +; CHECK: define i256 @non_recursive() { +define i256 @non_recursive() { + ret i256 1 +} + +; CHECK: attributes #[[RECURSIVE]] = { "evm-recursive" } diff --git a/llvm/test/CodeGen/EVM/evmaa-before-basicaa.ll b/llvm/test/CodeGen/EVM/evmaa-before-basicaa.ll new file mode 100644 index 000000000000..a7ad2453b059 --- /dev/null +++ b/llvm/test/CodeGen/EVM/evmaa-before-basicaa.ll @@ -0,0 +1,17 @@ +; REQUIRES: asserts +; RUN: opt -aa-pipeline=default -passes='require' -debug-pass-manager -disable-output -S < %s 2>&1 | FileCheck %s +; RUN: llc --debug-only='aa' -o /dev/null %s 2>&1 | FileCheck %s -check-prefix=LEGACY + +; In default AA pipeline, EVMAA should run before BasicAA to reduce compile time for EVM backend +target triple = "evm" + +; CHECK: Running analysis: EVMAA on foo +; CHECK-NEXT: Running analysis: BasicAA on foo + +; LEGACY: AAResults register Early ExternalAA: EVM Address space based Alias Analysis Wrapper +; LEGACY-NEXT: AAResults register BasicAA +define void @foo(){ +entry: + ret void +} + diff --git a/llvm/test/CodeGen/EVM/finalize-stack-frames.mir b/llvm/test/CodeGen/EVM/finalize-stack-frames.mir new file mode 100644 index 000000000000..5ab7c46a6bde --- /dev/null +++ b/llvm/test/CodeGen/EVM/finalize-stack-frames.mir @@ -0,0 +1,78 @@ +# RUN: llc -x mir -run-pass=evm-finalize-stack-frames -evm-stack-region-offset=128 -evm-stack-region-size=96 < %s | FileCheck %s +# RUN: llc -x mir -run-pass=evm-finalize-stack-frames -evm-stack-region-offset=128 -evm-stack-region-size=128 < %s 2>&1 | FileCheck --check-prefix=CHECK-MOREALLOC %s +# RUN: not --crash llc -x mir -run-pass=evm-finalize-stack-frames < %s 2>&1 | FileCheck --check-prefix=CHECK-NOALLOC %s +# RUN: not --crash llc -x mir -run-pass=evm-finalize-stack-frames -evm-stack-region-size=96 < %s 2>&1 | FileCheck --check-prefix=CHECK-NOSTART %s +# RUN: not --crash llc -x mir -run-pass=evm-finalize-stack-frames -evm-stack-region-offset=129 -evm-stack-region-size=96 < %s 2>&1 | FileCheck --check-prefix=CHECK-NOMOD %s + +# CHECK-MOREALLOC: warning: allocated stack region size: 128 is larger than the total stack size: 96 +# CHECK-NOALLOC: LLVM ERROR: Total stack size: 96 is larger than the allocated stack region size: 0 +# CHECK-NOSTART: LLVM ERROR: Stack region offset must be set when stack region size is set. Use --evm-stack-region-offset to set the offset. +# CHECK-NOMOD: LLVM ERROR: Stack region offset must be a multiple of 32 bytes. + +--- | + source_filename = "test.ll" + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm" + + define void @foo() { + ret void + } + + define void @bar() { + ret void + } + +... +--- +name: foo +alignment: 1 +stack: + - { id: 0, name: '', type: spill-slot, offset: 0, size: 32, alignment: 32, + stack-id: default, callee-saved-register: '', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 1, name: '', type: spill-slot, offset: 0, size: 32, alignment: 32, + stack-id: default, callee-saved-register: '', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } +machineFunctionInfo: + isStackified: true + numberOfParameters: 0 + hasPushDeployAddress: false +body: | + bb.0 (%ir-block.0): + liveins: $arguments + + ; CHECK-LABEL: name: foo + ; CHECK: liveins: $arguments + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH1_S i256 128 + ; CHECK-NEXT: PUSH1_S i256 160 + ; CHECK-NEXT: PseudoRET + PUSH_FRAME %stack.0 + PUSH_FRAME %stack.1 + PseudoRET + +... +--- +name: bar +alignment: 1 +stack: + - { id: 0, name: '', type: spill-slot, offset: 0, size: 32, alignment: 32, + stack-id: default, callee-saved-register: '', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } +machineFunctionInfo: + isStackified: true + numberOfParameters: 0 + hasPushDeployAddress: false +body: | + bb.0 (%ir-block.0): + liveins: $arguments + + ; CHECK-LABEL: name: bar + ; CHECK: liveins: $arguments + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH1_S i256 192 + ; CHECK-NEXT: PseudoRET + PUSH_FRAME %stack.0 + PseudoRET + +... diff --git a/llvm/test/CodeGen/EVM/fold-signextend.ll b/llvm/test/CodeGen/EVM/fold-signextend.ll new file mode 100644 index 000000000000..559d46381321 --- /dev/null +++ b/llvm/test/CodeGen/EVM/fold-signextend.ll @@ -0,0 +1,53 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -passes=instcombine -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @test_const(i256 %x) { +; CHECK-LABEL: define i256 @test_const( +; CHECK-SAME: i256 [[X:%.*]]) { +; CHECK-NEXT: [[SIGNEXT1:%.*]] = call i256 @llvm.evm.signextend(i256 15, i256 [[X]]) +; CHECK-NEXT: ret i256 [[SIGNEXT1]] +; + %signext1 = call i256 @llvm.evm.signextend(i256 15, i256 %x) + %signext2 = call i256 @llvm.evm.signextend(i256 15, i256 %signext1) + ret i256 %signext2 +} + +define i256 @test_const_ne(i256 %x) { +; CHECK-LABEL: define i256 @test_const_ne( +; CHECK-SAME: i256 [[X:%.*]]) { +; CHECK-NEXT: [[SIGNEXT1:%.*]] = call i256 @llvm.evm.signextend(i256 15, i256 [[X]]) +; CHECK-NEXT: [[SIGNEXT2:%.*]] = call i256 @llvm.evm.signextend(i256 10, i256 [[SIGNEXT1]]) +; CHECK-NEXT: ret i256 [[SIGNEXT2]] +; + %signext1 = call i256 @llvm.evm.signextend(i256 15, i256 %x) + %signext2 = call i256 @llvm.evm.signextend(i256 10, i256 %signext1) + ret i256 %signext2 +} + +define i256 @test_var(i256 %b, i256 %x) { +; CHECK-LABEL: define i256 @test_var( +; CHECK-SAME: i256 [[B:%.*]], i256 [[X:%.*]]) { +; CHECK-NEXT: [[SIGNEXT1:%.*]] = call i256 @llvm.evm.signextend(i256 [[B]], i256 [[X]]) +; CHECK-NEXT: ret i256 [[SIGNEXT1]] +; + %signext1 = call i256 @llvm.evm.signextend(i256 %b, i256 %x) + %signext2 = call i256 @llvm.evm.signextend(i256 %b, i256 %signext1) + ret i256 %signext2 +} + +define i256 @test_var_ne(i256 %b1, i256 %b2, i256 %x) { +; CHECK-LABEL: define i256 @test_var_ne( +; CHECK-SAME: i256 [[B1:%.*]], i256 [[B2:%.*]], i256 [[X:%.*]]) { +; CHECK-NEXT: [[SIGNEXT1:%.*]] = call i256 @llvm.evm.signextend(i256 [[B1]], i256 [[X]]) +; CHECK-NEXT: [[SIGNEXT2:%.*]] = call i256 @llvm.evm.signextend(i256 [[B2]], i256 [[SIGNEXT1]]) +; CHECK-NEXT: ret i256 [[SIGNEXT2]] +; + %signext1 = call i256 @llvm.evm.signextend(i256 %b1, i256 %x) + %signext2 = call i256 @llvm.evm.signextend(i256 %b2, i256 %signext1) + ret i256 %signext2 +} + +declare i256 @llvm.evm.signextend(i256, i256) diff --git a/llvm/test/CodeGen/EVM/folding-lshr-with-and-to-byte.ll b/llvm/test/CodeGen/EVM/folding-lshr-with-and-to-byte.ll new file mode 100644 index 000000000000..64bf565ccd42 --- /dev/null +++ b/llvm/test/CodeGen/EVM/folding-lshr-with-and-to-byte.ll @@ -0,0 +1,90 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc < %s | FileCheck %s +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +; Check that we do the following transformation: +; AND (SRL imm, v), 0xFF' -> 'BYTE (31 - imm / 8), v', +; where imm % 8 == 0, and imm / 8 < 32. + +define i256 @byte_lshr_0(i256 %0) { +; CHECK-LABEL: byte_lshr_0: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %r = and i256 %0, 255 + ret i256 %r +} + +define i256 @byte_lshr_8(i256 %0) { +; CHECK-LABEL: byte_lshr_8: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1E +; CHECK-NEXT: BYTE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %s = lshr i256 %0, 8 + %r = and i256 %s, 255 + ret i256 %r +} + +define i256 @byte_lshr_16(i256 %0) { +; CHECK-LABEL: byte_lshr_16: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1D +; CHECK-NEXT: BYTE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %s = lshr i256 %0, 16 + %r = and i256 %s, 255 + ret i256 %r +} + +define i256 @byte_lshr_248(i256 %0) { +; CHECK-LABEL: byte_lshr_248: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: BYTE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %s = lshr i256 %0, 248 + %r = and i256 %s, 255 + ret i256 %r +} + +; Should not apply: imm = 9 (not divisible by 8) +define i256 @shift_not_multiple_of_8(i256 %0) { +; CHECK-LABEL: shift_not_multiple_of_8: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 0x9 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %s = lshr i256 %0, 9 + %r = and i256 %s, 255 + ret i256 %r +} + +; Should not apply: imm = 256 (256 / 8 > 31) +define i256 @shift_too_large(i256 %0) { +; CHECK-LABEL: shift_too_large: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %s = lshr i256 %0, 256 + %r = and i256 %s, 255 + ret i256 %r +} diff --git a/llvm/test/CodeGen/EVM/force-constant-unfolding.ll b/llvm/test/CodeGen/EVM/force-constant-unfolding.ll new file mode 100644 index 000000000000..4ba3f7817b9e --- /dev/null +++ b/llvm/test/CodeGen/EVM/force-constant-unfolding.ll @@ -0,0 +1,43 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc -O3 --evm-const-unfolding-size-reduction-threshold=0 --evm-const-unfolding-inst-num-limit=10 < %s | FileCheck %s +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare void @llvm.evm.return(ptr addrspace(1), i256) noreturn + +; Verifies that constant unfolding occurs in non-OptForSize mode when limits +; are overridden through options. + + +; 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000 +define void @test8() #1 { +; CHECK-LABEL: test8: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH16 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +; CHECK-NEXT: PUSH1 0x80 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 57896044618658097711785492504343953926294709965899343556265417396524796608512) + unreachable +} + +; 0x00000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +define void @test2() #1 { +; CHECK-LABEL: test2: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 0x54 +; CHECK-NEXT: SHR +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 5986310706507378352962293074805895248510699696029695) + unreachable +} + +attributes #1 = { nofree nounwind noreturn } diff --git a/llvm/test/CodeGen/EVM/icmp.ll b/llvm/test/CodeGen/EVM/icmp.ll new file mode 100644 index 000000000000..d39d70c10546 --- /dev/null +++ b/llvm/test/CodeGen/EVM/icmp.ll @@ -0,0 +1,194 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @icmp_eq(i256 %a, i256 %b) nounwind { +; CHECK-LABEL: icmp_eq: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: EQ +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP +; CHECK : ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK : EQ [[RES:\$[0-9]+]], [[IN1]], [[IN2]] + + %cmp = icmp eq i256 %a, %b + %res = zext i1 %cmp to i256 + ret i256 %res +} + +define i256 @icmp_big_imm_eq(i256 %a) nounwind { +; CHECK-LABEL: icmp_big_imm_eq: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH4 0x6057361D +; CHECK-NEXT: PUSH1 0xE0 +; CHECK-NEXT: SHL +; CHECK-NEXT: EQ +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP +; CHECK : CONST_I256 [[C1:\$[0-9]+]], [[[0-9]+]] +; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK : EQ [[RES:\$[0-9]+]], [[IN1]], [[IN2]] + + %cmp = icmp eq i256 %a, 43576122634770472758325941782982599838796957244005075818703754470792663924736 + %res = zext i1 %cmp to i256 + ret i256 %res +} + +define i256 @icmp_ne(i256 %a, i256 %b) nounwind { +; CHECK-LABEL: icmp_ne: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: EQ +; CHECK-NEXT: ISZERO +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP +; CHECK : ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK : EQ [[TMP1:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK : ISZERO [[RES:\$[0-9]+]], [[TMP1]] + + %cmp = icmp ne i256 %a, %b + %res = zext i1 %cmp to i256 + ret i256 %res +} + +define i256 @icmp_ugt(i256 %a, i256 %b) nounwind { +; CHECK-LABEL: icmp_ugt: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: GT +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP +; CHECK : ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK : GT [[RES:\$[0-9]+]], [[IN1]], [[IN2]] + + %cmp = icmp ugt i256 %a, %b + %res = zext i1 %cmp to i256 + ret i256 %res +} + +define i256 @icmp_uge(i256 %a, i256 %b) nounwind { +; CHECK-LABEL: icmp_uge: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: LT +; CHECK-NEXT: ISZERO +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP +; CHECK : ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK : LT [[TMP1:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK : ISZERO [[RES:\$[0-9]+]], [[TMP1]] + + %cmp = icmp uge i256 %a, %b + %res = zext i1 %cmp to i256 + ret i256 %res +} + +define i256 @icmp_ult(i256 %a, i256 %b) nounwind { +; CHECK-LABEL: icmp_ult: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: LT +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP +; CHECK : ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK : LT [[RES:\$[0-9]+]], [[IN1]], [[IN2]] + + %cmp = icmp ult i256 %a, %b + %res = zext i1 %cmp to i256 + ret i256 %res +} + +define i256 @icmp_ule(i256 %a, i256 %b) nounwind { +; CHECK-LABEL: icmp_ule: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: GT +; CHECK-NEXT: ISZERO +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP +; CHECK : ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK : GT [[TMP1:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK : ISZERO [[RES:\$[0-9]+]], [[TMP1]] + + %cmp = icmp ule i256 %a, %b + %res = zext i1 %cmp to i256 + ret i256 %res +} + +define i256 @icmp_sgt(i256 %a, i256 %b) nounwind { +; CHECK-LABEL: icmp_sgt: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SGT +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP +; CHECK : ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK : SGT [[RES:\$[0-9]+]], [[IN1]], [[IN2]] + + %cmp = icmp sgt i256 %a, %b + %res = zext i1 %cmp to i256 + ret i256 %res +} + +define i256 @icmp_sge(i256 %a, i256 %b) nounwind { +; CHECK-LABEL: icmp_sge: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SLT +; CHECK-NEXT: ISZERO +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP +; CHECK : ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK : SLT [[TMP1:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK : ISZERO [[RES:\$[0-9]+]], [[TMP1]] + + %cmp = icmp sge i256 %a, %b + %res = zext i1 %cmp to i256 + ret i256 %res +} + +define i256 @icmp_slt(i256 %a, i256 %b) nounwind { +; CHECK-LABEL: icmp_slt: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SLT +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP +; CHECK : ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK : SLT [[RES:\$[0-9]+]], [[IN1]], [[IN2]] + + %cmp = icmp slt i256 %a, %b + %res = zext i1 %cmp to i256 + ret i256 %res +} + +define i256 @icmp_sle(i256 %a, i256 %b) nounwind { +; CHECK-LABEL: icmp_sle: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SGT +; CHECK-NEXT: ISZERO +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP +; CHECK : ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK : SGT [[TMP1:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK : ISZERO [[RES:\$[0-9]+]], [[TMP1]] + + %cmp = icmp sle i256 %a, %b + %res = zext i1 %cmp to i256 + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/inline-in-bb-unreachable-terminated.ll b/llvm/test/CodeGen/EVM/inline-in-bb-unreachable-terminated.ll new file mode 100644 index 000000000000..c038c76d37b6 --- /dev/null +++ b/llvm/test/CodeGen/EVM/inline-in-bb-unreachable-terminated.ll @@ -0,0 +1,79 @@ +; RUN: opt -passes=inline -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +; CHECK-NOT: define private void @callee_inline +define private void @callee_inline(i256 %arg) { +entry: + %itptr = inttoptr i256 %arg to ptr addrspace(5) + %load = load i256, ptr addrspace(5) %itptr, align 1 + %and = and i256 %load, 1461501637330902918203684832716283019655932542975 + %cmp = icmp eq i256 %and, 0 + br i1 %cmp, label %bb1, label %bb2 + +bb1: + store i256 32754936060235842233766999496646880210696060726502698976450834618736336437248, ptr addrspace(1) null, align 32 + tail call void @llvm.evm.revert(ptr addrspace(1) null, i256 32) + unreachable + +bb2: + ret void +} + +; CHECK-LABEL: define void @caller_inline1 +define void @caller_inline1(i256 %arg) { +entry: +; CHECK-NOT: call void @callee_inline(i256 %arg) + call void @callee_inline(i256 %arg) + %itptr = inttoptr i256 %arg to ptr addrspace(5) + %load = load i256, ptr addrspace(5) %itptr, align 1 + store i256 %load, ptr addrspace(1) null, align 32 + call void @llvm.evm.return(ptr addrspace(1) null, i256 32) + unreachable +} + +; CHECK-LABEL: define void @caller_inline2 +define void @caller_inline2(i256 %arg1, i256 %arg2) { +entry: +; CHECK-NOT: call void @callee_inline(i256 %arg2) + call void @callee_inline(i256 %arg2) + %itptr = inttoptr i256 %arg2 to ptr addrspace(5) + %load = load i256, ptr addrspace(5) %itptr, align 1 + store i256 %load, ptr addrspace(1) null, align 32 + call void @llvm.evm.return(ptr addrspace(1) null, i256 32) + unreachable +} + +; CHECK-LABEL: define private void @callee_noinline +define private void @callee_noinline() { +entry: + store i256 128, ptr addrspace(5) inttoptr (i256 32 to ptr addrspace(5)), align 32 + store i256 128, ptr addrspace(5) inttoptr (i256 64 to ptr addrspace(5)), align 32 + store i256 128, ptr addrspace(5) inttoptr (i256 96 to ptr addrspace(5)), align 32 + store i256 128, ptr addrspace(5) inttoptr (i256 128 to ptr addrspace(5)), align 32 + store i256 128, ptr addrspace(5) inttoptr (i256 160 to ptr addrspace(5)), align 32 + store i256 128, ptr addrspace(5) inttoptr (i256 192 to ptr addrspace(5)), align 32 + store i256 128, ptr addrspace(5) inttoptr (i256 224 to ptr addrspace(5)), align 32 + store i256 128, ptr addrspace(5) inttoptr (i256 256 to ptr addrspace(5)), align 32 + store i256 128, ptr addrspace(5) inttoptr (i256 288 to ptr addrspace(5)), align 32 + ret void +} + +; CHECK-LABEL: define void @caller_noinline1 +define void @caller_noinline1() { +entry: +; CHECK: call void @callee_noinline() + call void @callee_noinline() + call void @llvm.evm.return(ptr addrspace(1) null, i256 0) + unreachable +} + +; CHECK-LABEL: define void @caller_noinline2 +define void @caller_noinline2() { +entry: +; CHECK: call void @callee_noinline() + call void @callee_noinline() + call void @llvm.evm.return(ptr addrspace(1) null, i256 0) + unreachable +} diff --git a/llvm/test/CodeGen/EVM/intrinsic.ll b/llvm/test/CodeGen/EVM/intrinsic.ll new file mode 100644 index 000000000000..f30f77ae881c --- /dev/null +++ b/llvm/test/CodeGen/EVM/intrinsic.ll @@ -0,0 +1,686 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @sdiv(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: sdiv: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SDIV +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.sdiv(i256 %rs1, i256 %rs2) + ret i256 %res +} + +define i256 @div(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: div: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DIV +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.div(i256 %rs1, i256 %rs2) + ret i256 %res +} + +define i256 @smod(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: smod: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SMOD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.smod(i256 %rs1, i256 %rs2) + ret i256 %res +} + +define i256 @mod(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: mod: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MOD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.mod(i256 %rs1, i256 %rs2) + ret i256 %res +} + +define i256 @shl(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: shl: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SHL +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.shl(i256 %rs1, i256 %rs2) + ret i256 %res +} + +define i256 @shr(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: shr: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SHR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.shr(i256 %rs1, i256 %rs2) + ret i256 %res +} + +define i256 @sar(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: sar: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SAR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.sar(i256 %rs1, i256 %rs2) + ret i256 %res +} + +define i256 @addmod(i256 %rs1, i256 %rs2, i256 %rs3) nounwind { +; CHECK-LABEL: addmod: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ADDMOD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.addmod(i256 %rs1, i256 %rs2, i256 %rs3) + ret i256 %res +} + +define i256 @mulmod(i256 %rs1, i256 %rs2, i256 %rs3) nounwind { +; CHECK-LABEL: mulmod: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MULMOD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.mulmod(i256 %rs1, i256 %rs2, i256 %rs3) + ret i256 %res +} + +define i256 @exp(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: exp: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: EXP +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.exp(i256 %rs1, i256 %rs2) + ret i256 %res +} + +define i256 @sha3(ptr addrspace(1) %offset, i256 %size) nounwind { +; CHECK-LABEL: sha3: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: KECCAK256 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.sha3(ptr addrspace(1) %offset, i256 %size) + ret i256 %res +} + +define i256 @signextend(i256 %bytesize, i256 %val) nounwind { +; CHECK-LABEL: signextend: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SIGNEXTEND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.signextend(i256 %bytesize, i256 %val) + ret i256 %res +} + +define i256 @byte(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: byte: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: BYTE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.byte(i256 %rs1, i256 %rs2) + ret i256 %res +} + +define i256 @pc() nounwind { +; CHECK-LABEL: pc: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PC +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.pc() + ret i256 %res +} + +define i256 @msize() nounwind { +; CHECK-LABEL: msize: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MSIZE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.msize() + ret i256 %res +} + +define i256 @address() nounwind { +; CHECK-LABEL: address: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ADDRESS +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.address() + ret i256 %res +} + +define i256 @origin() nounwind { +; CHECK-LABEL: origin: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ORIGIN +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.origin() + ret i256 %res +} + +define i256 @caller() nounwind { +; CHECK-LABEL: caller: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CALLER +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.caller() + ret i256 %res +} + +define i256 @balance(i256 %rs1) nounwind { +; CHECK-LABEL: balance: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: BALANCE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.balance(i256 %rs1) + ret i256 %res +} + +define i256 @calldatasize() nounwind { +; CHECK-LABEL: calldatasize: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CALLDATASIZE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.calldatasize() + ret i256 %res +} + +define i256 @calldataload(ptr addrspace(2) %rs1) nounwind { +; CHECK-LABEL: calldataload: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CALLDATALOAD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.calldataload(ptr addrspace(2) %rs1) + ret i256 %res +} + +define i256 @callvalue() nounwind { +; CHECK-LABEL: callvalue: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CALLVALUE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.callvalue() + ret i256 %res +} + +define i256 @codesize() nounwind { +; CHECK-LABEL: codesize: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CODESIZE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.codesize() + ret i256 %res +} + +define i256 @gasprice() nounwind { +; CHECK-LABEL: gasprice: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: GASPRICE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.gasprice() + ret i256 %res +} + +define i256 @extcodesize(i256 %rs1) nounwind { +; CHECK-LABEL: extcodesize: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: EXTCODESIZE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.extcodesize(i256 %rs1) + ret i256 %res +} + +define void @extcodecopy(i256 %addr, ptr addrspace(1) %dst, ptr addrspace(4) %src, i256 %size) nounwind { +; CHECK-LABEL: extcodecopy: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: EXTCODECOPY +; CHECK-NEXT: JUMP + + call void @llvm.evm.extcodecopy(i256 %addr, ptr addrspace(1) %dst, ptr addrspace(4) %src, i256 %size) + ret void +} + +define i256 @extcodehash(i256 %rs1) nounwind { +; CHECK-LABEL: extcodehash: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: EXTCODEHASH +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.extcodehash(i256 %rs1) + ret i256 %res +} + +define i256 @returndatasize() nounwind { +; CHECK-LABEL: returndatasize: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: RETURNDATASIZE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.returndatasize() + ret i256 %res +} + +define i256 @blockhash(i256 %rs1) nounwind { +; CHECK-LABEL: blockhash: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: BLOCKHASH +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.blockhash(i256 %rs1) + ret i256 %res +} + +define i256 @blobhash(i256 %rs1) nounwind { +; CHECK-LABEL: blobhash: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: BLOBHASH +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.blobhash(i256 %rs1) + ret i256 %res +} + +define i256 @coinbase() nounwind { +; CHECK-LABEL: coinbase: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: COINBASE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.coinbase() + ret i256 %res +} + +define i256 @timestamp() nounwind { +; CHECK-LABEL: timestamp: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: TIMESTAMP +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.timestamp() + ret i256 %res +} + +define i256 @number() nounwind { +; CHECK-LABEL: number: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: NUMBER +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.number() + ret i256 %res +} + +define i256 @difficulty() nounwind { +; CHECK-LABEL: difficulty: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DIFFICULTY +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.difficulty() + ret i256 %res +} + +define i256 @gaslimit() nounwind { +; CHECK-LABEL: gaslimit: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: GASLIMIT +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.gaslimit() + ret i256 %res +} + +define i256 @chainid() nounwind { +; CHECK-LABEL: chainid: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CHAINID +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.chainid() + ret i256 %res +} + +define i256 @selfbalance() nounwind { +; CHECK-LABEL: selfbalance: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SELFBALANCE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.selfbalance() + ret i256 %res +} + +define i256 @basefee() nounwind { +; CHECK-LABEL: basefee: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: BASEFEE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.basefee() + ret i256 %res +} + +define i256 @blobbasefee() nounwind { +; CHECK-LABEL: blobbasefee: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: BLOBBASEFEE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.evm.blobbasefee() + ret i256 %res +} + +define void @log0(ptr addrspace(1) %off, i256 %size) nounwind { +; CHECK-LABEL: log0: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: LOG0 +; CHECK-NEXT: JUMP + + call void @llvm.evm.log0(ptr addrspace(1) %off, i256 %size) + ret void +} + +define void @log1(ptr addrspace(1) %off, i256 %size, i256 %t1) nounwind { +; CHECK-LABEL: log1: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: LOG1 +; CHECK-NEXT: JUMP + + call void @llvm.evm.log1(ptr addrspace(1) %off, i256 %size, i256 %t1) + ret void +} + +define void @log2(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2) nounwind { +; CHECK-LABEL: log2: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: LOG2 +; CHECK-NEXT: JUMP + + call void @llvm.evm.log2(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2) + ret void +} + +define void @log3(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3) nounwind { +; CHECK-LABEL: log3: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: LOG3 +; CHECK-NEXT: JUMP + + call void @llvm.evm.log3(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3) + ret void +} + +define void @log4(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3, i256 %t4) nounwind { +; CHECK-LABEL: log4: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: LOG4 +; CHECK-NEXT: JUMP + + call void @llvm.evm.log4(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3, i256 %t4) + ret void +} + +define i256 @create(i256 %val, ptr addrspace(1) %off, i256 %size) nounwind { +; CHECK-LABEL: create: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CREATE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %ret = call i256 @llvm.evm.create(i256 %val, ptr addrspace(1) %off, i256 %size) + ret i256 %ret +} + +define i256 @call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %arg_off, i256 %arg_size, ptr addrspace(1) %ret_off, i256 %ret_size) nounwind { +; CHECK-LABEL: call: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CALL +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %ret = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %arg_off, i256 %arg_size, ptr addrspace(1) %ret_off, i256 %ret_size) + ret i256 %ret +} + +define i256 @delegatecall(i256 %gas, i256 %addr, ptr addrspace(1) %arg_off, i256 %arg_size, ptr addrspace(1) %ret_off, i256 %ret_size) nounwind { +; CHECK-LABEL: delegatecall: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DELEGATECALL +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %ret = call i256 @llvm.evm.delegatecall(i256 %gas, i256 %addr, ptr addrspace(1) %arg_off, i256 %arg_size, ptr addrspace (1) %ret_off, i256 %ret_size) + ret i256 %ret +} + +define i256 @create2(i256 %val, ptr addrspace(1) %off, i256 %size, i256 %salt) nounwind { +; CHECK-LABEL: create2: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CREATE2 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %ret = call i256 @llvm.evm.create2(i256 %val, ptr addrspace(1) %off, i256 %size, i256 %salt) + ret i256 %ret +} + +define i256 @staticcall(i256 %gas, i256 %addr, ptr addrspace(1) %arg_off, i256 %arg_size, ptr addrspace(1) %ret_off, i256 %ret_size) nounwind { +; CHECK-LABEL: staticcall: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: STATICCALL +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %ret = call i256 @llvm.evm.staticcall(i256 %gas, i256 %addr, ptr addrspace(1) %arg_off, i256 %arg_size, ptr addrspace(1) %ret_off, i256 %ret_size) + ret i256 %ret +} + +define void @selfdestruct(i256 %addr) nounwind { +; CHECK-LABEL: selfdestruct: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SELFDESTRUCT +; CHECK-NEXT: JUMP + + call void @llvm.evm.selfdestruct(i256 %addr) + ret void +} + +define void @return(ptr addrspace(1) %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: return: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: RETURN +; CHECK-NEXT: JUMP + + call void @llvm.evm.return(ptr addrspace(1) %rs1, i256 %rs2) + ret void +} + +define void @revert(ptr addrspace(1) %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: revert: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: REVERT +; CHECK-NEXT: JUMP + + call void @llvm.evm.revert(ptr addrspace(1) %rs1, i256 %rs2) + ret void +} + +define void @invalid() nounwind { +; CHECK-LABEL: invalid: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: INVALID +; CHECK-NEXT: JUMP + + call void @llvm.evm.invalid() + ret void +} + +declare i256 @llvm.evm.sdiv(i256, i256) +declare i256 @llvm.evm.div(i256, i256) +declare i256 @llvm.evm.mod(i256, i256) +declare i256 @llvm.evm.smod(i256, i256) +declare i256 @llvm.evm.shl(i256, i256) +declare i256 @llvm.evm.shr(i256, i256) +declare i256 @llvm.evm.sar(i256, i256) +declare i256 @llvm.evm.addmod(i256, i256, i256) +declare i256 @llvm.evm.mulmod(i256, i256, i256) +declare i256 @llvm.evm.exp(i256, i256) +declare i256 @llvm.evm.sha3(ptr addrspace(1), i256) +declare i256 @llvm.evm.signextend(i256, i256) +declare i256 @llvm.evm.byte(i256, i256) +declare i256 @llvm.evm.pc() +declare i256 @llvm.evm.msize() +declare i256 @llvm.evm.address() +declare i256 @llvm.evm.origin() +declare i256 @llvm.evm.caller() +declare i256 @llvm.evm.balance(i256) +declare i256 @llvm.evm.calldatasize() +declare i256 @llvm.evm.calldataload(ptr addrspace(2)) +declare i256 @llvm.evm.callvalue() +declare i256 @llvm.evm.codesize() +declare i256 @llvm.evm.gasprice() +declare i256 @llvm.evm.extcodesize(i256) +declare void @llvm.evm.extcodecopy(i256, ptr addrspace(1), ptr addrspace(4), i256) +declare i256 @llvm.evm.extcodehash(i256) +declare i256 @llvm.evm.blockhash(i256) +declare i256 @llvm.evm.blobhash(i256) +declare i256 @llvm.evm.returndatasize() +declare i256 @llvm.evm.coinbase() +declare i256 @llvm.evm.timestamp() +declare i256 @llvm.evm.number() +declare i256 @llvm.evm.difficulty() +declare i256 @llvm.evm.gaslimit() +declare i256 @llvm.evm.chainid() +declare i256 @llvm.evm.selfbalance() +declare i256 @llvm.evm.basefee() +declare i256 @llvm.evm.blobbasefee() +declare void @llvm.evm.log0(ptr addrspace(1), i256) +declare void @llvm.evm.log1(ptr addrspace(1), i256, i256) +declare void @llvm.evm.log2(ptr addrspace(1), i256, i256, i256) +declare void @llvm.evm.log3(ptr addrspace(1), i256, i256, i256, i256) +declare void @llvm.evm.log4(ptr addrspace(1), i256, i256, i256, i256, i256) +declare i256 @llvm.evm.create(i256, ptr addrspace(1), i256) +declare i256 @llvm.evm.call(i256, i256, i256, ptr addrspace(1), i256, ptr addrspace(1), i256) +declare i256 @llvm.evm.delegatecall(i256, i256, ptr addrspace(1), i256, ptr addrspace(1), i256) +declare i256 @llvm.evm.create2(i256, ptr addrspace(1), i256, i256) +declare i256 @llvm.evm.staticcall(i256, i256, ptr addrspace(1), i256, ptr addrspace(1), i256) +declare void @llvm.evm.selfdestruct(i256) +declare void @llvm.evm.return(ptr addrspace(1), i256) +declare void @llvm.evm.revert(ptr addrspace(1), i256) +declare void @llvm.evm.invalid() +declare void @llvm.evm.pop(i256) diff --git a/llvm/test/CodeGen/EVM/jumpdest-in-entry-point.ll b/llvm/test/CodeGen/EVM/jumpdest-in-entry-point.ll new file mode 100644 index 000000000000..ebab82b6d3bf --- /dev/null +++ b/llvm/test/CodeGen/EVM/jumpdest-in-entry-point.ll @@ -0,0 +1,42 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc -O3 < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare void @llvm.evm.return(ptr addrspace(1), i256) + +define void @__entry() noreturn "evm-entry-function" { +; CHECK-LABEL: __entry: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: MLOAD +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.BB0_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: .BB0_2: ; %bb1 +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: MSTORE +; CHECK-NEXT: PUSH1 0x20 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + %load = load i256, ptr addrspace(1) null, align 64 + %cmp = icmp eq i256 %load, 0 + br i1 %cmp, label %bb2, label %bb1 + +bb1: + br label %bb2 + +bb2: + %phi = phi i256 [ 0, %entry ], [ %load, %bb1 ] + store i256 %phi, ptr addrspace(1) null, align 64 + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 32) + unreachable +} diff --git a/llvm/test/CodeGen/EVM/jumps-are-expensive.ll b/llvm/test/CodeGen/EVM/jumps-are-expensive.ll new file mode 100644 index 000000000000..95bf38e6ada6 --- /dev/null +++ b/llvm/test/CodeGen/EVM/jumps-are-expensive.ll @@ -0,0 +1,47 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc -O3 < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare i256 @bar() + +define i256 @test(i256 %a, i256 %b) { +; CHECK-LABEL: test: +; CHECK: ; %bb.0: ; %bb1 +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: ISZERO +; CHECK-NEXT: ISZERO +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: ISZERO +; CHECK-NEXT: ISZERO +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH4 @.BB0_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %bb3 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB0_2: ; %bb4 +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH4 @.FUNC_RET0 +; CHECK-NEXT: PUSH4 @bar +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP +bb1: + %0 = icmp eq i256 %a, 0 + %1 = icmp eq i256 %b, 0 + %or = or i1 %0, %1 + br i1 %or, label %bb3, label %bb4 + +bb3: + ret i256 0 + +bb4: + %2 = call i256 @bar() + ret i256 %2 +} diff --git a/llvm/test/CodeGen/EVM/lit.local.cfg b/llvm/test/CodeGen/EVM/lit.local.cfg new file mode 100644 index 000000000000..505218ffec16 --- /dev/null +++ b/llvm/test/CodeGen/EVM/lit.local.cfg @@ -0,0 +1,2 @@ +if not 'EVM' in config.root.targets: + config.unsupported = True diff --git a/llvm/test/CodeGen/EVM/load-narrowing-disable.ll b/llvm/test/CodeGen/EVM/load-narrowing-disable.ll new file mode 100644 index 000000000000..d3ac55a6e495 --- /dev/null +++ b/llvm/test/CodeGen/EVM/load-narrowing-disable.ll @@ -0,0 +1,22 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i1 @simplify_setcc(ptr addrspace(1) %glob) { +; CHECK-LABEL: simplify_setcc: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x2 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: MLOAD +; CHECK-NEXT: AND +; CHECK-NEXT: ISZERO +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %val = load i256, ptr addrspace(1) %glob, align 32 + %val.and = and i256 %val, 2 + %cmp = icmp eq i256 %val.and, 0 + ret i1 %cmp +} diff --git a/llvm/test/CodeGen/EVM/logical.ll b/llvm/test/CodeGen/EVM/logical.ll new file mode 100644 index 000000000000..dad3129034c7 --- /dev/null +++ b/llvm/test/CodeGen/EVM/logical.ll @@ -0,0 +1,55 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @andrrr(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: andrrr: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = and i256 %rs1, %rs2 + ret i256 %res +} + +define i256 @orrrr(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: orrrr: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: OR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = or i256 %rs1, %rs2 + ret i256 %res +} + +define i256 @xorrrr(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: xorrrr: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: XOR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = xor i256 %rs1, %rs2 + ret i256 %res +} + +define i256 @notrrr(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: notrrr: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: POP +; CHECK-NEXT: NOT +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = xor i256 %rs1, -1 + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/machine-function-info.ll b/llvm/test/CodeGen/EVM/machine-function-info.ll new file mode 100644 index 000000000000..e9878084d454 --- /dev/null +++ b/llvm/test/CodeGen/EVM/machine-function-info.ll @@ -0,0 +1,38 @@ +; RUN: llc -stop-before=finalize-isel < %s | FileCheck --check-prefix=CHECK-PARAMS %s +; RUN: llc -stop-before=finalize-isel < %s | FileCheck --check-prefix=CHECK-PUSHDEPLOY %s +; RUN: llc -stop-after=evm-backward-propagation-stackification < %s | FileCheck --check-prefix=CHECK-STACKIFIED %s + +; Check that the machine function info is correctly set up for different functions. + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare i256 @llvm.evm.pushdeployaddress() + +define i256 @params(i256 %arg1, i256 %arg2) { +; CHECK-PARAMS-LABEL: name: params +; CHECK-PARAMS: machineFunctionInfo: +; CHECK-PARAMS: isStackified: false +; CHECK-PARAMS: numberOfParameters: 2 +; CHECK-PARAMS: hasPushDeployAddress: false + ret i256 %arg1 +} + +define void @pushdeploy() noreturn { +; CHECK-PUSHDEPLOY-LABEL: name: pushdeploy +; CHECK-PUSHDEPLOY: machineFunctionInfo: +; CHECK-PUSHDEPLOY: isStackified: false +; CHECK-PUSHDEPLOY: numberOfParameters: 0 +; CHECK-PUSHDEPLOY: hasPushDeployAddress: true + %push = call i256 @llvm.evm.pushdeployaddress() + unreachable +} + +define void @stackified() { +; CHECK-STACKIFIED-LABEL: name: stackified +; CHECK-STACKIFIED: machineFunctionInfo: +; CHECK-STACKIFIED: isStackified: true +; CHECK-STACKIFIED: numberOfParameters: 0 +; CHECK-STACKIFIED: hasPushDeployAddress: false + ret void +} diff --git a/llvm/test/CodeGen/EVM/machine-function-info.mir b/llvm/test/CodeGen/EVM/machine-function-info.mir new file mode 100644 index 000000000000..ef5ba70b2f5b --- /dev/null +++ b/llvm/test/CodeGen/EVM/machine-function-info.mir @@ -0,0 +1,37 @@ +# RUN: llc -x mir -run-pass=evm-backward-propagation-stackification < %s | FileCheck %s + +# Test that we can properly read the machine function info from MIR and +# that isStackified is changed to true after stackification. + +--- | + ; ModuleID = 'test.ll' + source_filename = "test.ll" + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm" + + define i256 @test(i256 %arg1, i256 %arg2) { + ret i256 %arg1 + } + +... +# CHECK-LABEL: name: test +# CHECK: machineFunctionInfo: +# CHECK: isStackified: true +# CHECK: numberOfParameters: 2 +# CHECK: hasPushDeployAddress: false +--- +name: test +alignment: 1 +tracksRegLiveness: true +machineFunctionInfo: + isStackified: false + numberOfParameters: 2 + hasPushDeployAddress: false +body: | + bb.0 (%ir-block.0): + liveins: $arguments, $value_stack + + %0:gpr = ARGUMENT 0, implicit $arguments, implicit-def $value_stack, implicit $value_stack + RET %0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + +... diff --git a/llvm/test/CodeGen/EVM/machine-sink-cheap-instructions.ll b/llvm/test/CodeGen/EVM/machine-sink-cheap-instructions.ll new file mode 100644 index 000000000000..c805bae8992e --- /dev/null +++ b/llvm/test/CodeGen/EVM/machine-sink-cheap-instructions.ll @@ -0,0 +1,82 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc -O3 < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare i256 @bar() + +define i256 @test1(i256 %arg) { +; CHECK-LABEL: test1: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0xA +; CHECK-NEXT: EQ +; CHECK-NEXT: PUSH4 @.BB0_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: PUSH4 @.BB0_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB0_2: ; %bb1 +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH4 @.FUNC_RET0 +; CHECK-NEXT: PUSH4 @bar +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: .BB0_3: ; %bb2 +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP +entry: + %cmp = icmp eq i256 %arg, 10 + br i1 %cmp, label %bb1, label %bb2 + +bb1: + %call = tail call i256 @bar() + br label %bb2 + +bb2: + %phi = phi i256 [ %call, %bb1 ], [ 0, %entry ] + ret i256 %phi +} + +define i256 @test2(i256 %arg) { +; CHECK-LABEL: test2: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0xA +; CHECK-NEXT: EQ +; CHECK-NEXT: PUSH4 @.BB1_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: ADDRESS +; CHECK-NEXT: PUSH4 @.BB1_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB1_2: ; %bb1 +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH4 @.FUNC_RET1 +; CHECK-NEXT: PUSH4 @bar +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET1: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: .BB1_3: ; %bb2 +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP +entry: + %address = call i256 @llvm.evm.address() + %cmp = icmp eq i256 %arg, 10 + br i1 %cmp, label %bb1, label %bb2 + +bb1: + %call = tail call i256 @bar() + br label %bb2 + +bb2: + %phi = phi i256 [ %call, %bb1 ], [ %address, %entry ] + ret i256 %phi +} + +declare i256 @llvm.evm.address() diff --git a/llvm/test/CodeGen/EVM/mem_call_data.ll b/llvm/test/CodeGen/EVM/mem_call_data.ll new file mode 100644 index 000000000000..bd21b079c92e --- /dev/null +++ b/llvm/test/CodeGen/EVM/mem_call_data.ll @@ -0,0 +1,17 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @mload(ptr addrspace(2) %offset) nounwind { +; CHECK-LABEL: mload: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CALLDATALOAD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %val = load i256, ptr addrspace(2) %offset, align 32 + ret i256 %val +} diff --git a/llvm/test/CodeGen/EVM/memintrinsics-no-expansion.ll b/llvm/test/CodeGen/EVM/memintrinsics-no-expansion.ll new file mode 100644 index 000000000000..ddd545957b0e --- /dev/null +++ b/llvm/test/CodeGen/EVM/memintrinsics-no-expansion.ll @@ -0,0 +1,30 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: opt -O3 -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(2) noalias nocapture readonly, i256, i1 immarg) +declare void @llvm.memcpy.p1.p3.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(3) noalias nocapture readonly, i256, i1 immarg) +declare void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(4) noalias nocapture readonly, i256, i1 immarg) + +define fastcc void @calldata_to_heap() { +; CHECK-LABEL: calldata_to_heap +; CHECK: llvm.memcpy.p1.p2.i256 + call void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) null, ptr addrspace(2) null, i256 4, i1 false) + ret void +} + +define fastcc void @returndata_to_heap() { +; CHECK-LABEL: returndata_to_heap +; CHECK: llvm.memcpy.p1.p3.i256 + call void @llvm.memcpy.p1.p3.i256(ptr addrspace(1) null, ptr addrspace(3) null, i256 4, i1 false) + ret void +} + +define fastcc void @code_to_heap() { +; CHECK-LABEL: code_to_heap +; CHECK: llvm.memcpy.p1.p4.i256 + call void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) null, ptr addrspace(4) null, i256 4, i1 false) + ret void +} diff --git a/llvm/test/CodeGen/EVM/memintrinsics-opt.ll b/llvm/test/CodeGen/EVM/memintrinsics-opt.ll new file mode 100644 index 000000000000..f7cb81728858 --- /dev/null +++ b/llvm/test/CodeGen/EVM/memintrinsics-opt.ll @@ -0,0 +1,14 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: opt -O3 -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare void @llvm.memcpy.p0.p0.i256(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i256, i1 immarg) + +define fastcc void @huge-copysize0(ptr %dest, ptr %src) { +; CHECK-LABEL: huge-copysize0 +; CHECK: tail call void @llvm.memcpy.p0.p0.i256(ptr align 1 %dest, ptr align 1 %src, i256 81129638414606681695789005144064, i1 false) + call void @llvm.memcpy.p0.p0.i256(ptr %dest, ptr %src, i256 81129638414606681695789005144064, i1 false) + ret void +} diff --git a/llvm/test/CodeGen/EVM/memintrinsics.ll b/llvm/test/CodeGen/EVM/memintrinsics.ll new file mode 100644 index 000000000000..e697d7f645b0 --- /dev/null +++ b/llvm/test/CodeGen/EVM/memintrinsics.ll @@ -0,0 +1,142 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc -O3 < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare void @llvm.memcpy.p1.p1.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(1) noalias nocapture readonly, i256, i1 immarg) +declare void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(2) noalias nocapture readonly, i256, i1 immarg) +declare void @llvm.memcpy.p1.p3.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(3) noalias nocapture readonly, i256, i1 immarg) +declare void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(4) noalias nocapture readonly, i256, i1 immarg) + +declare void @llvm.memmove.p1.p1.i256(ptr addrspace(1) nocapture writeonly, ptr addrspace(1) nocapture readonly, i256, i1 immarg) +declare void @llvm.memmove.p1.p2.i256(ptr addrspace(1) nocapture writeonly, ptr addrspace(2) nocapture readonly, i256, i1 immarg) +declare void @llvm.memmove.p1.p3.i256(ptr addrspace(1) nocapture writeonly, ptr addrspace(3) nocapture readonly, i256, i1 immarg) +declare void @llvm.memmove.p1.p4.i256(ptr addrspace(1) nocapture writeonly, ptr addrspace(4) nocapture readonly, i256, i1 immarg) +declare void @llvm.memmove.p1.p1.i8(ptr addrspace(1) nocapture writeonly, ptr addrspace(1) nocapture readonly, i8, i1 immarg) + +define fastcc void @memmove-imm8(ptr addrspace(1) %dest, ptr addrspace(1) %src) { + call void @llvm.memmove.p1.p1.i8(ptr addrspace(1) %dest, ptr addrspace(1) %src, i8 77, i1 false) + ret void +} + +define fastcc void @memmove-imm8-arg(ptr addrspace(1) %dest, ptr addrspace(1) %src, i8 %size) { + call void @llvm.memmove.p1.p1.i8(ptr addrspace(1) %dest, ptr addrspace(1) %src, i8 %size, i1 false) + ret void +} + +define fastcc void @huge-copysize0(ptr addrspace(1) %dest, ptr addrspace(1) %src) { + call void @llvm.memmove.p1.p1.i256(ptr addrspace(1) %dest, ptr addrspace(1) %src, i256 81129638414606681695789005144064, i1 false) + ret void +} + +define fastcc void @huge-copysize1(ptr addrspace(1) %dest, ptr addrspace(1) %src) { + call void @llvm.memmove.p1.p1.i256(ptr addrspace(1) %dest, ptr addrspace(1) %src, i256 81129638414606681695789005144065, i1 false) + ret void +} + +define fastcc void @huge-movesize1(ptr addrspace(1) %dest, ptr addrspace(1) %src) { + call void @llvm.memmove.p1.p1.i256(ptr addrspace(1) %dest, ptr addrspace(1) %src, i256 81129638414606681695789005144065, i1 false) + ret void +} + +define fastcc void @normal-known-size(ptr addrspace(1) %dest, ptr addrspace(1) %src) { + call void @llvm.memmove.p1.p1.i256(ptr addrspace(1) %dest, ptr addrspace(1) %src, i256 1024, i1 false) + ret void +} + +define fastcc void @normal-known-size-2(ptr addrspace(1) %dest, ptr addrspace(1) %src) { + call void @llvm.memmove.p1.p1.i256(ptr addrspace(1) %dest, ptr addrspace(1) %src, i256 1060, i1 false) + ret void +} + +define fastcc void @heap_to_heap(ptr addrspace(1) %dest, ptr addrspace(1) %src, i256 %len) { +; CHECK-LABEL: heap_to_heap: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MCOPY +; CHECK-NEXT: JUMP + + call void @llvm.memcpy.p1.p1.i256(ptr addrspace(1) %dest, ptr addrspace(1) %src, i256 %len, i1 false) + ret void +} + +define fastcc void @calldata_to_heap(ptr addrspace(1) %dest, ptr addrspace(2) %src, i256 %len) { +; CHECK-LABEL: calldata_to_heap: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CALLDATACOPY +; CHECK-NEXT: JUMP + + call void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) %dest, ptr addrspace(2) %src, i256 %len, i1 false) + ret void +} + +define fastcc void @move_calldata_to_heap(ptr addrspace(1) %dest, ptr addrspace(2) %src, i256 %len) { +; CHECK-LABEL: move_calldata_to_heap: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CALLDATACOPY +; CHECK-NEXT: JUMP + + call void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) %dest, ptr addrspace(2) %src, i256 %len, i1 false) + ret void +} + +define fastcc void @calldata_to_heap_csize(ptr addrspace(1) %dest, ptr addrspace(2) %src) { +; CHECK-LABEL: calldata_to_heap_csize: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 0x2A +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: CALLDATACOPY +; CHECK-NEXT: JUMP + + call void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) %dest, ptr addrspace(2) %src, i256 42, i1 false) + ret void +} + +define fastcc void @returndata_to_heap(ptr addrspace(1) %dest, ptr addrspace(3) %src, i256 %len) { +; CHECK-LABEL: returndata_to_heap: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: RETURNDATACOPY +; CHECK-NEXT: JUMP + + call void @llvm.memcpy.p1.p3.i256(ptr addrspace(1) %dest, ptr addrspace(3) %src, i256 %len, i1 false) + ret void +} + +define fastcc void @move_returndata_to_heap(ptr addrspace(1) %dest, ptr addrspace(3) %src, i256 %len) { +; CHECK-LABEL: move_returndata_to_heap: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: RETURNDATACOPY +; CHECK-NEXT: JUMP + + call void @llvm.memmove.p1.p3.i256(ptr addrspace(1) %dest, ptr addrspace(3) %src, i256 %len, i1 false) + ret void +} + +define fastcc void @code_to_heap(ptr addrspace(1) %dest, ptr addrspace(4) %src, i256 %len) { +; CHECK-LABEL: code_to_heap: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CODECOPY +; CHECK-NEXT: JUMP + + call void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) %dest, ptr addrspace(4) %src, i256 %len, i1 false) + ret void +} + +define fastcc void @move_code_to_heap(ptr addrspace(1) %dest, ptr addrspace(4) %src, i256 %len) { +; CHECK-LABEL: move_code_to_heap: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CODECOPY +; CHECK-NEXT: JUMP + + call void @llvm.memmove.p1.p4.i256(ptr addrspace(1) %dest, ptr addrspace(4) %src, i256 %len, i1 false) + ret void +} diff --git a/llvm/test/CodeGen/EVM/memory.ll b/llvm/test/CodeGen/EVM/memory.ll new file mode 100644 index 000000000000..4c0a2410ca80 --- /dev/null +++ b/llvm/test/CodeGen/EVM/memory.ll @@ -0,0 +1,41 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define void @mstore8(ptr addrspace(1) %offset, i256 %val) nounwind { +; CHECK-LABEL: mstore8: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MSTORE8 +; CHECK-NEXT: JUMP + + call void @llvm.evm.mstore8(ptr addrspace(1) %offset, i256 %val) + ret void +} + +define void @mstore(ptr addrspace(1) %offset, i256 %val) nounwind { +; CHECK-LABEL: mstore: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MSTORE +; CHECK-NEXT: JUMP + + store i256 %val, ptr addrspace(1) %offset, align 32 + ret void +} + +define i256 @mload(ptr addrspace(1) %offset) nounwind { +; CHECK-LABEL: mload: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %val = load i256, ptr addrspace(1) %offset, align 32 + ret i256 %val +} + +declare void @llvm.evm.mstore8(ptr addrspace(1), i256) diff --git a/llvm/test/CodeGen/EVM/merge-blocks.ll b/llvm/test/CodeGen/EVM/merge-blocks.ll new file mode 100644 index 000000000000..b25f17077407 --- /dev/null +++ b/llvm/test/CodeGen/EVM/merge-blocks.ll @@ -0,0 +1,318 @@ +; RUN: opt -S -mtriple=evm -passes="mergebb,simplifycfg" < %s | FileCheck %s + +declare void @dummy() + +define i32 @basic(i32 %x, i32* %p) { +; CHECK-LABEL: @basic( +; CHECK-NEXT: [[SWITCH:%.*]] = icmp ult i32 [[X:%.*]], 2 +; CHECK-NEXT: br i1 [[SWITCH]], label [[BB1:%.*]], label [[BB3:%.*]] +; CHECK: bb1: +; CHECK-NEXT: store i32 0, ptr [[P:%.*]], align 4 +; CHECK-NEXT: br label [[EXIT:%.*]] +; CHECK: bb3: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[EXIT]] +; CHECK: exit: +; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 0, [[BB1]] ], [ 1, [[BB3]] ] +; CHECK-NEXT: ret i32 [[PHI]] +; + switch i32 %x, label %bb3 [ + i32 0, label %bb1 + i32 1, label %bb2 + ] + +bb1: + store i32 0, i32* %p + br label %exit + +bb2: + store i32 0, i32* %p + br label %exit + +bb3: + call void @dummy() + br label %exit + +exit: + %phi = phi i32 [ 0, %bb1 ], [ 0, %bb2 ], [ 1, %bb3] + ret i32 %phi +} + +; nonnull present in block blocks, keep it. +define i32 @metadata_nonnull_keep(i32 %x, i32** %p1, i32** %p2) { +; CHECK-LABEL: @metadata_nonnull_keep( +; CHECK-NEXT: [[SWITCH:%.*]] = icmp ult i32 [[X:%.*]], 2 +; CHECK-NEXT: br i1 [[SWITCH]], label [[BB1:%.*]], label [[BB3:%.*]] +; CHECK: bb1: +; CHECK-NEXT: [[V1:%.*]] = load ptr, ptr [[P1:%.*]], align 4, !nonnull !0 +; CHECK-NEXT: store ptr [[V1]], ptr [[P2:%.*]], align 32 +; CHECK-NEXT: br label [[EXIT:%.*]] +; CHECK: bb3: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[EXIT]] +; CHECK: exit: +; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 0, [[BB1]] ], [ 1, [[BB3]] ] +; CHECK-NEXT: ret i32 [[PHI]] +; + switch i32 %x, label %bb3 [ + i32 0, label %bb1 + i32 1, label %bb2 + ] + +bb1: + %v1 = load i32*, i32** %p1, align 4, !nonnull !{} + store i32* %v1, i32** %p2 + br label %exit + +bb2: + %v2 = load i32*, i32** %p1, align 4, !nonnull !{} + store i32* %v2, i32** %p2 + br label %exit + +bb3: + call void @dummy() + br label %exit + +exit: + %phi = phi i32 [ 0, %bb1 ], [ 0, %bb2 ], [ 1, %bb3] + ret i32 %phi +} + +; nonnull only present in one of the blocks, drop it. +define i32 @metadata_nonnull_drop(i32 %x, i32** %p1, i32** %p2) { +; CHECK-LABEL: @metadata_nonnull_drop( +; CHECK-NEXT: [[SWITCH:%.*]] = icmp ult i32 [[X:%.*]], 2 +; CHECK-NEXT: br i1 [[SWITCH]], label [[BB1:%.*]], label [[BB3:%.*]] +; CHECK: bb1: +; CHECK-NEXT: [[V1:%.*]] = load ptr, ptr [[P1:%.*]], align 4 +; CHECK-NEXT: store ptr [[V1]], ptr [[P2:%.*]], align 32 +; CHECK-NEXT: br label [[EXIT:%.*]] +; CHECK: bb3: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[EXIT]] +; CHECK: exit: +; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 0, [[BB1]] ], [ 1, [[BB3]] ] +; CHECK-NEXT: ret i32 [[PHI]] +; + switch i32 %x, label %bb3 [ + i32 0, label %bb1 + i32 1, label %bb2 + ] + +bb1: + %v1 = load i32*, i32** %p1, align 4, !nonnull !{} + store i32* %v1, i32** %p2 + br label %exit + +bb2: + %v2 = load i32*, i32** %p1, align 4 + store i32* %v2, i32** %p2 + br label %exit + +bb3: + call void @dummy() + br label %exit + +exit: + %phi = phi i32 [ 0, %bb1 ], [ 0, %bb2 ], [ 1, %bb3] + ret i32 %phi +} + +; The union of both range metadatas should be taken. +define i32 @metadata_range(i32 %x, i32* %p1, i32* %p2) { +; CHECK-LABEL: @metadata_range( +; CHECK-NEXT: [[SWITCH:%.*]] = icmp ult i32 [[X:%.*]], 2 +; CHECK-NEXT: br i1 [[SWITCH]], label [[BB1:%.*]], label [[BB3:%.*]] +; CHECK: bb1: +; CHECK-NEXT: [[V1:%.*]] = load i32, ptr [[P1:%.*]], align 4, !range !1 +; CHECK-NEXT: store i32 [[V1]], ptr [[P2:%.*]], align 4 +; CHECK-NEXT: br label [[EXIT:%.*]] +; CHECK: bb3: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[EXIT]] +; CHECK: exit: +; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 0, [[BB1]] ], [ 1, [[BB3]] ] +; CHECK-NEXT: ret i32 [[PHI]] +; + switch i32 %x, label %bb3 [ + i32 0, label %bb1 + i32 1, label %bb2 + ] + +bb1: + %v1 = load i32, i32* %p1, align 4, !range !{i32 0, i32 10} + store i32 %v1, i32* %p2 + br label %exit + +bb2: + %v2 = load i32, i32* %p1, align 4, !range !{i32 5, i32 15} + store i32 %v2, i32* %p2 + br label %exit + +bb3: + call void @dummy() + br label %exit + +exit: + %phi = phi i32 [ 0, %bb1 ], [ 0, %bb2 ], [ 1, %bb3] + ret i32 %phi +} + +; Only the common nuw flag may be preserved. +define i32 @attributes(i32 %x, i32 %y) { +; CHECK-LABEL: @attributes( +; CHECK-NEXT: [[SWITCH:%.*]] = icmp ult i32 [[X:%.*]], 2 +; CHECK-NEXT: br i1 [[SWITCH]], label [[BB1:%.*]], label [[BB3:%.*]] +; CHECK: bb1: +; CHECK-NEXT: [[A:%.*]] = add nuw i32 [[Y:%.*]], 1 +; CHECK-NEXT: br label [[EXIT:%.*]] +; CHECK: bb3: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[EXIT]] +; CHECK: exit: +; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ [[A]], [[BB1]] ], [ 1, [[BB3]] ] +; CHECK-NEXT: ret i32 [[PHI]] +; + switch i32 %x, label %bb3 [ + i32 0, label %bb1 + i32 1, label %bb2 + ] + +bb1: + %a = add nuw nsw i32 %y, 1 + br label %exit + +bb2: + %b = add nuw i32 %y, 1 + br label %exit + +bb3: + call void @dummy() + br label %exit + +exit: + %phi = phi i32 [ %a, %bb1 ], [ %b, %bb2 ], [ 1, %bb3] + ret i32 %phi +} + +; Don't try to merge with the entry block. +define void @entry_block() { +; CHECK-LABEL: @entry_block( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[LOOP:%.*]] +; CHECK: loop: +; CHECK-NEXT: br label [[LOOP]] +; +entry: + br label %loop + +loop: + br label %loop +} + +; For phi nodes, we need to check that incoming blocks match. +define i32 @phi_blocks(i32 %x, i32* %p) { +; CHECK-LABEL: @phi_blocks( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[SWITCH:%.*]] = icmp ult i32 [[X:%.*]], 2 +; CHECK-NEXT: br i1 [[SWITCH]], label [[EXIT:%.*]], label [[BB3:%.*]] +; CHECK: bb3: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[EXIT]] +; CHECK: exit: +; CHECK-NEXT: [[PHI3:%.*]] = phi i32 [ 1, [[BB3]] ], [ 0, [[ENTRY:%.*]] ] +; CHECK-NEXT: ret i32 [[PHI3]] +; +entry: + switch i32 %x, label %bb3 [ + i32 0, label %bb1.split + i32 1, label %bb2 + ] + +bb1.split: + br label %bb1 + +bb1: + %phi1 = phi i32 [ 0, %bb1.split ] + br label %exit + +bb2: + %phi2 = phi i32 [ 0, %entry ] + br label %exit + +bb3: + call void @dummy() + br label %exit + +exit: + %phi3 = phi i32 [ %phi1, %bb1 ], [ %phi2, %bb2 ], [ 1, %bb3] + ret i32 %phi3 +} + +; This requires merging bb3,4,5,6 before bb1,2. +define i32 @two_level(i32 %x, i32 %y, i32 %z) { +; CHECK-LABEL: @two_level( +; CHECK-NEXT: switch i32 [[Z:%.*]], label [[BB7:%.*]] [ +; CHECK-NEXT: i32 0, label [[BB1:%.*]] +; CHECK-NEXT: i32 1, label [[BB2:%.*]] +; CHECK-NEXT: ] +; CHECK: bb1: +; CHECK-NEXT: [[SWITCH:%.*]] = icmp ult i32 [[X:%.*]], 2 +; CHECK-NEXT: br i1 [[SWITCH]], label [[BB3:%.*]], label [[BB7]] +; CHECK: bb2: +; CHECK-NEXT: [[SWITCH1:%.*]] = icmp ult i32 [[X]], 2 +; CHECK-NEXT: br i1 [[SWITCH1]], label [[BB3]], label [[BB7]] +; CHECK: bb3: +; CHECK-NEXT: [[A:%.*]] = add i32 [[Y:%.*]], 1 +; CHECK-NEXT: br label [[EXIT:%.*]] +; CHECK: bb7: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[EXIT]] +; CHECK: exit: +; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ [[A]], [[BB3]] ], [ 0, [[BB7]] ] +; CHECK-NEXT: ret i32 [[PHI]] +; + switch i32 %z, label %bb7 [ + i32 0, label %bb1 + i32 1, label %bb2 + ] + +bb1: + switch i32 %x, label %bb7 [ + i32 0, label %bb3 + i32 1, label %bb4 + ] + +bb2: + switch i32 %x, label %bb7 [ + i32 0, label %bb5 + i32 1, label %bb6 + ] + +bb3: + %a = add i32 %y, 1 + br label %exit + +bb4: + %b = add i32 %y, 1 + br label %exit + +bb5: + %c = add i32 %y, 1 + br label %exit + +bb6: + %d = add i32 %y, 1 + br label %exit + +bb7: + call void @dummy() + br label %exit + +exit: + %phi = phi i32 [ %a, %bb3 ], [ %b, %bb4 ], [ %c, %bb5 ], [ %d, %bb6 ], [ 0, %bb7 ] + ret i32 %phi +} + +; CHECK: !1 = !{i32 0, i32 15} + diff --git a/llvm/test/CodeGen/EVM/mergebb-crash.ll b/llvm/test/CodeGen/EVM/mergebb-crash.ll new file mode 100644 index 000000000000..7a81b038279b --- /dev/null +++ b/llvm/test/CodeGen/EVM/mergebb-crash.ll @@ -0,0 +1,31 @@ +; RUN: opt -mtriple=evm -passes=mergebb -S < %s | FileCheck %s + +define void @test() { +; CHECK-LABEL: @test( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[RETURN_LOOPEXIT:%.*]] +; CHECK: return.loopexit: +; CHECK-NEXT: [[VAR_Y7_US:%.*]] = alloca i256, align 32 +; CHECK-NEXT: unreachable +; +entry: + br label %return.loopexit + +return.loopexit: ; preds = %entry + %var_y7.us = alloca i256, align 32 + store i256 1, ptr null, align 4294967296 + br label %return + +return: ; preds = %for_join499, %if_join349, %return.loopexit + %var_y7.lcssa = phi ptr [ null, %if_join349 ], [ null, %return.loopexit ], [ %var_y7, %for_join499 ] + ret void + +if_join349: ; No predecessors! + %var_y7 = alloca i256, align 32 + store i256 1, ptr null, align 4294967296 + br label %return + +for_join499: ; No predecessors! + br label %return +} + diff --git a/llvm/test/CodeGen/EVM/mod.ll b/llvm/test/CodeGen/EVM/mod.ll new file mode 100644 index 000000000000..fe176b5362b8 --- /dev/null +++ b/llvm/test/CodeGen/EVM/mod.ll @@ -0,0 +1,42 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @umodrrr(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: umodrrr: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MOD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = urem i256 %rs1, %rs2 + ret i256 %res +} + +define i256 @smodrrr(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: smodrrr: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SMOD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = srem i256 %rs1, %rs2 + ret i256 %res +} + +define i256 @smodrri(i256 %rs1) nounwind { +; CHECK-LABEL: smodrri: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = srem i256 %rs1, 0 + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/module-layout.ll b/llvm/test/CodeGen/EVM/module-layout.ll new file mode 100644 index 000000000000..4a9e47714fbb --- /dev/null +++ b/llvm/test/CodeGen/EVM/module-layout.ll @@ -0,0 +1,20 @@ +; RUN: not --crash llc -O3 < %s 2>&1 | FileCheck %s +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare void @llvm.evm.return(ptr addrspace(1), i256) + +; CHECK: LLVM ERROR: Entry function '__entry' isn't the first function in the module. + +define private fastcc i256 @fun_fib(i256 %0) noinline { +entry: + %res = add i256 %0, 1 + ret i256 %res +} + +define void @__entry() noreturn "evm-entry-function" { +entry: + %fun_res = tail call fastcc i256 @fun_fib(i256 7) + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 %fun_res) + unreachable +} diff --git a/llvm/test/CodeGen/EVM/mul.ll b/llvm/test/CodeGen/EVM/mul.ll new file mode 100644 index 000000000000..54a8f728a40b --- /dev/null +++ b/llvm/test/CodeGen/EVM/mul.ll @@ -0,0 +1,31 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @mulrrr(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: mulrrr: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MUL +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = mul i256 %rs1, %rs2 + ret i256 %res +} + +define i256 @mulrri(i256 %rs1) nounwind { +; CHECK-LABEL: mulrri: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH3 0x7A11F +; CHECK-NEXT: NOT +; CHECK-NEXT: MUL +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = mul i256 %rs1, -500000 + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/no-mul-fold-to-overflow-int.ll b/llvm/test/CodeGen/EVM/no-mul-fold-to-overflow-int.ll new file mode 100644 index 000000000000..980c0de56a61 --- /dev/null +++ b/llvm/test/CodeGen/EVM/no-mul-fold-to-overflow-int.ll @@ -0,0 +1,34 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: opt -O3 -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare void @llvm.evm.revert(ptr addrspace(1), i256) + +define fastcc i256 @checked_mul(i256 %0) { +; CHECK-LABEL: checked_mul +; CHECK-NOT: @llvm.smul.with.overflow +entry: + %multiplication_result = mul i256 100, %0 + %division_signed_is_divided_int_min = icmp eq i256 %multiplication_result, -57896044618658097711785492504343953926634992332820282019728792003956564819968 + br label %division_signed_non_overflow + +division_signed_non_overflow: + %division_signed_result_non_zero = sdiv i256 %multiplication_result, 100 + br label %division_signed_join + +division_signed_join: + %comparison_result = icmp eq i256 %0, %division_signed_result_non_zero + %comparison_result_extended = zext i1 %comparison_result to i256 + %comparison_result3 = icmp eq i256 %comparison_result_extended, 0 + %comparison_result_extended4 = zext i1 %comparison_result3 to i256 + br i1 %comparison_result3, label %if_main, label %if_join + +if_main: + call void @llvm.evm.revert(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 null, i256 36) + unreachable + +if_join: + ret i256 %multiplication_result +} diff --git a/llvm/test/CodeGen/EVM/no-small-constant-unfolding.ll b/llvm/test/CodeGen/EVM/no-small-constant-unfolding.ll new file mode 100644 index 000000000000..9ff6f98276b8 --- /dev/null +++ b/llvm/test/CodeGen/EVM/no-small-constant-unfolding.ll @@ -0,0 +1,51 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc -O3 -stats < %s | FileCheck %s +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare void @llvm.evm.return(ptr addrspace(1), i256) noreturn + +; This file tests that small constants are not unfolded. + +; CHECK-NOT: evm-constant-unfolding + +; 0x0000000000000000000000000000000000000000000000000000000000000000 +define void @test1() #1 { +; CHECK-LABEL: test1: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 0) + unreachable +} + +; 0x00000000000000000000000000000000000000000000000000000000000000FF +define void @test2() #1 { +; CHECK-LABEL: test2: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 255) + unreachable +} + +; 0x00000000000000000000000000000000000000000000000000000000FFFFFFFF +define void @test3() #1 { +; CHECK-LABEL: test3: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH4 0xFFFFFFFF +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 4294967295) + unreachable +} + +attributes #1 = { minsize nofree nounwind noreturn optsize } diff --git a/llvm/test/CodeGen/EVM/push0-over-dup.ll b/llvm/test/CodeGen/EVM/push0-over-dup.ll new file mode 100644 index 000000000000..977981328a21 --- /dev/null +++ b/llvm/test/CodeGen/EVM/push0-over-dup.ll @@ -0,0 +1,16 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define { i256, i256 } @test() { +; CHECK-LABEL: test: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: JUMP + ret { i256, i256 } zeroinitializer +} diff --git a/llvm/test/CodeGen/EVM/select-const.ll b/llvm/test/CodeGen/EVM/select-const.ll new file mode 100644 index 000000000000..62f060633d22 --- /dev/null +++ b/llvm/test/CodeGen/EVM/select-const.ll @@ -0,0 +1,440 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @select_const_int_easy(i1 %a) { +; CHECK-LABEL: select_const_int_easy: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = select i1 %a, i256 1, i256 0 + ret i256 %1 +} + +define i256 @select_const_int_one_away(i1 %a) { +; CHECK-LABEL: select_const_int_one_away: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 0x4 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = select i1 %a, i256 3, i256 4 + ret i256 %1 +} + +define i256 @select_const_int_pow2_zero(i1 %a) { +; CHECK-LABEL: select_const_int_pow2_zero: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 0x2 +; CHECK-NEXT: SHL +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = select i1 %a, i256 4, i256 0 + ret i256 %1 +} + +define i256 @select_const_int_harder(i1 %a) { +; CHECK-LABEL: select_const_int_harder: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x26 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 0x5 +; CHECK-NEXT: SHL +; CHECK-NEXT: XOR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = select i1 %a, i256 6, i256 38 + ret i256 %1 +} + +define i256 @select_eq_zero_negone(i256 %a, i256 %b) { +; CHECK-LABEL: select_eq_zero_negone: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: EQ +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp eq i256 %a, %b + %2 = select i1 %1, i256 -1, i256 0 + ret i256 %2 +} + +define i256 @select_ne_zero_negone(i256 %a, i256 %b) { +; CHECK-LABEL: select_ne_zero_negone: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: EQ +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp ne i256 %a, %b + %2 = select i1 %1, i256 -1, i256 0 + ret i256 %2 +} + +define i256 @select_sgt_zero_negone(i256 %a, i256 %b) { +; CHECK-LABEL: select_sgt_zero_negone: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SGT +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp sgt i256 %a, %b + %2 = select i1 %1, i256 -1, i256 0 + ret i256 %2 +} + +define i256 @select_slt_zero_negone(i256 %a, i256 %b) { +; CHECK-LABEL: select_slt_zero_negone: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SLT +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp slt i256 %a, %b + %2 = select i1 %1, i256 -1, i256 0 + ret i256 %2 +} + +define i256 @select_sge_zero_negone(i256 %a, i256 %b) { +; CHECK-LABEL: select_sge_zero_negone: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SLT +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp sge i256 %a, %b + %2 = select i1 %1, i256 -1, i256 0 + ret i256 %2 +} + +define i256 @select_sle_zero_negone(i256 %a, i256 %b) { +; CHECK-LABEL: select_sle_zero_negone: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SGT +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp sle i256 %a, %b + %2 = select i1 %1, i256 -1, i256 0 + ret i256 %2 +} + +define i256 @select_ugt_zero_negone(i256 %a, i256 %b) { +; CHECK-LABEL: select_ugt_zero_negone: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: GT +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp ugt i256 %a, %b + %2 = select i1 %1, i256 -1, i256 0 + ret i256 %2 +} + +define i256 @select_ult_zero_negone(i256 %a, i256 %b) { +; CHECK-LABEL: select_ult_zero_negone: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: LT +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp ult i256 %a, %b + %2 = select i1 %1, i256 -1, i256 0 + ret i256 %2 +} + +define i256 @select_uge_zero_negone(i256 %a, i256 %b) { +; CHECK-LABEL: select_uge_zero_negone: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: LT +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp uge i256 %a, %b + %2 = select i1 %1, i256 -1, i256 0 + ret i256 %2 +} + +define i256 @select_ule_zero_negone(i256 %a, i256 %b) { +; CHECK-LABEL: select_ule_zero_negone: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: GT +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp ule i256 %a, %b + %2 = select i1 %1, i256 -1, i256 0 + ret i256 %2 +} + +define i256 @select_eq_1_2(i256 %a, i256 %b) { +; CHECK-LABEL: select_eq_1_2: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: EQ +; CHECK-NEXT: PUSH1 0x2 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp eq i256 %a, %b + %2 = select i1 %1, i256 1, i256 2 + ret i256 %2 +} + +define i256 @select_ne_1_2(i256 %a, i256 %b) { +; CHECK-LABEL: select_ne_1_2: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: EQ +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH1 0x2 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp ne i256 %a, %b + %2 = select i1 %1, i256 1, i256 2 + ret i256 %2 +} + +define i256 @select_eq_2_1(i256 %a, i256 %b) { +; CHECK-LABEL: select_eq_2_1: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: EQ +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp eq i256 %a, %b + %2 = select i1 %1, i256 2, i256 1 + ret i256 %2 +} + +define i256 @select_ne_2_1(i256 %a, i256 %b) { +; CHECK-LABEL: select_ne_2_1: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: EQ +; CHECK-NEXT: ISZERO +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp ne i256 %a, %b + %2 = select i1 %1, i256 2, i256 1 + ret i256 %2 +} + +define i256 @select_eq_10000_10001(i256 %a, i256 %b) { +; CHECK-LABEL: select_eq_10000_10001: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: EQ +; CHECK-NEXT: PUSH2 0x2712 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp eq i256 %a, %b + %2 = select i1 %1, i256 10001, i256 10002 + ret i256 %2 +} + +define i256 @select_ne_10001_10002(i256 %a, i256 %b) { +; CHECK-LABEL: select_ne_10001_10002: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: EQ +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH2 0x2712 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp ne i256 %a, %b + %2 = select i1 %1, i256 10001, i256 10002 + ret i256 %2 +} + +define i256 @select_196_184(i1 %a) { +; CHECK-LABEL: select_196_184: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0xC +; CHECK-NEXT: PUSH1 0xB8 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: AND +; CHECK-NEXT: MUL +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = select i1 %a, i256 196, i256 184 + ret i256 %1 +} + +define i256 @select_184_196(i1 %a) { +; CHECK-LABEL: select_184_196: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0xC +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: AND +; CHECK-NEXT: MUL +; CHECK-NEXT: PUSH1 0xC4 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = select i1 %a, i256 184, i256 196 + ret i256 %1 +} + +define i256 @select_n196_n184(i1 %a) { +; CHECK-LABEL: select_n196_n184: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0xC +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: AND +; CHECK-NEXT: MUL +; CHECK-NEXT: PUSH1 0xB7 +; CHECK-NEXT: NOT +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = select i1 %a, i256 -196, i256 -184 + ret i256 %1 +} + +define i256 @select_n184_n196(i1 %a) { +; CHECK-LABEL: select_n184_n196: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0xC +; CHECK-NEXT: PUSH1 0xC4 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: AND +; CHECK-NEXT: MUL +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = select i1 %a, i256 -184, i256 -196 + ret i256 %1 +} + +define i256 @select_var_12345(i1 %a, i256 %b) { +; CHECK-LABEL: select_var_12345: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH2 0x3039 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: AND +; CHECK-NEXT: MUL +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = select i1 %a, i256 %b, i256 12345 + ret i256 %1 +} + +define i256 @select_12345_var(i1 %a, i256 %b) { +; CHECK-LABEL: select_12345_var: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH2 0x3039 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: AND +; CHECK-NEXT: MUL +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = select i1 %a, i256 12345, i256 %b + ret i256 %1 +} + +define i256 @select_var_n12345(i1 %a, i256 %b) { +; CHECK-LABEL: select_var_n12345: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH2 0x3039 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: AND +; CHECK-NEXT: MUL +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = select i1 %a, i256 %b, i256 -12345 + ret i256 %1 +} + +define i256 @select_n12345_var(i1 %a, i256 %b) { +; CHECK-LABEL: select_n12345_var: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH2 0x3038 +; CHECK-NEXT: NOT +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: AND +; CHECK-NEXT: MUL +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = select i1 %a, i256 -12345, i256 %b + ret i256 %1 +} diff --git a/llvm/test/CodeGen/EVM/select-normalize.ll b/llvm/test/CodeGen/EVM/select-normalize.ll new file mode 100644 index 000000000000..0e8d464ff0b9 --- /dev/null +++ b/llvm/test/CodeGen/EVM/select-normalize.ll @@ -0,0 +1,131 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @select_and(i256 %a0, i256 %a1, i256 %a2, i256 %a3, i256 %a4, i256 %a5) { +; CHECK-LABEL: select_and: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: LT +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: LT +; CHECK-NEXT: AND +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB0_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH4 @.BB0_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB0_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: .BB0_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %cmp1 = icmp ult i256 %a0, %a1 + %cmp2 = icmp ult i256 %a2, %a3 + %and = and i1 %cmp1, %cmp2 + %select = select i1 %and, i256 %a4, i256 %a5 + ret i256 %select +} + +define i256 @select_or(i256 %a0, i256 %a1, i256 %a2, i256 %a3, i256 %a4, i256 %a5) { +; CHECK-LABEL: select_or: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: LT +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: LT +; CHECK-NEXT: OR +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB1_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH4 @.BB1_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB1_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: .BB1_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %cmp1 = icmp ult i256 %a0, %a1 + %cmp2 = icmp ult i256 %a2, %a3 + %or = or i1 %cmp1, %cmp2 + %select = select i1 %or, i256 %a4, i256 %a5 + ret i256 %select +} + +define i256 @select_select_to_and(i1 %cond1, i1 %cond2, i256 %a, i256 %b) { +; CHECK-LABEL: select_select_to_and: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: AND +; CHECK-NEXT: AND +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB2_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH4 @.BB2_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB2_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: .BB2_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %select1 = select i1 %cond1, i256 %a, i256 %b + %select2 = select i1 %cond2, i256 %select1, i256 %b + ret i256 %select2 +} + +define i256 @select_select_to_or(i1 %cond1, i1 %cond2, i256 %a, i256 %b) { +; CHECK-LABEL: select_select_to_or: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: OR +; CHECK-NEXT: AND +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB3_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH4 @.BB3_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB3_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: .BB3_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %select1 = select i1 %cond1, i256 %a, i256 %b + %select2 = select i1 %cond2, i256 %a, i256 %select1 + ret i256 %select2 +} diff --git a/llvm/test/CodeGen/EVM/select.ll b/llvm/test/CodeGen/EVM/select.ll new file mode 100644 index 000000000000..95fd2bf114f5 --- /dev/null +++ b/llvm/test/CodeGen/EVM/select.ll @@ -0,0 +1,34 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @select(i256 %v1, i256 %v2, i256 %v3, i256 %v4) { +; CHECK-LABEL: select: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: EQ +; CHECK-NEXT: PUSH4 @.BB0_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH4 @.BB0_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB0_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: .BB0_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + + %1 = icmp ne i256 %v3, %v4 + %2 = select i1 %1, i256 %v1, i256 %v2 + ret i256 %2 +} diff --git a/llvm/test/CodeGen/EVM/setcc-with-0-selection.ll b/llvm/test/CodeGen/EVM/setcc-with-0-selection.ll new file mode 100644 index 000000000000..ea8fb84d7f54 --- /dev/null +++ b/llvm/test/CodeGen/EVM/setcc-with-0-selection.ll @@ -0,0 +1,27 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc < %s | FileCheck %s +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +define i1 @test_eq(i256 %x) { +; CHECK-LABEL: test_eq: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ISZERO +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %cmp = icmp eq i256 %x, 0 + ret i1 %cmp +} + +define i1 @test_ne(i256 %x) { +; CHECK-LABEL: test_ne: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ISZERO +; CHECK-NEXT: ISZERO +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %cmp = icmp ne i256 %x, 0 + ret i1 %cmp +} diff --git a/llvm/test/CodeGen/EVM/sext.ll b/llvm/test/CodeGen/EVM/sext.ll new file mode 100644 index 000000000000..3d0350ab82c6 --- /dev/null +++ b/llvm/test/CodeGen/EVM/sext.ll @@ -0,0 +1,101 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @sexti1(i1 %rs1) nounwind { +; CHECK-LABEL: sexti1: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = sext i1 %rs1 to i256 + ret i256 %res +} + +define i256 @sexti8(i8 %rs1) nounwind { +; CHECK-LABEL: sexti8: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SIGNEXTEND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = sext i8 %rs1 to i256 + ret i256 %res +} + +define i256 @sexti16(i16 %rs1) nounwind { +; CHECK-LABEL: sexti16: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: SIGNEXTEND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = sext i16 %rs1 to i256 + ret i256 %res +} + +define i256 @sexti32(i32 %rs1) nounwind { +; CHECK-LABEL: sexti32: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x3 +; CHECK-NEXT: SIGNEXTEND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = sext i32 %rs1 to i256 + ret i256 %res +} + +define i256 @sexti64(i64 %rs1) nounwind { +; CHECK-LABEL: sexti64: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x7 +; CHECK-NEXT: SIGNEXTEND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = sext i64 %rs1 to i256 + ret i256 %res +} + +define i256 @sexti128(i128 %rs1) nounwind { +; CHECK-LABEL: sexti128: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0xF +; CHECK-NEXT: SIGNEXTEND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = sext i128 %rs1 to i256 + ret i256 %res +} + +; Check that 'sext' also gets lowered for types not declared in MVT. +define i256 @sexti40(i40 %rs1) nounwind { +; CHECK-LABEL: sexti40: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0xD8 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH1 0xD8 +; CHECK-NEXT: SAR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = sext i40 %rs1 to i256 + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/sha3-constant-folding.ll b/llvm/test/CodeGen/EVM/sha3-constant-folding.ll new file mode 100644 index 000000000000..debb1e3394cc --- /dev/null +++ b/llvm/test/CodeGen/EVM/sha3-constant-folding.ll @@ -0,0 +1,231 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: opt -O3 -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +; Check that we don't fold the sha3 call if optimizing for size. +define i256 @sha3_test_optsize() minsize { +; CHECK-LABEL: define i256 @sha3_test_optsize +; CHECK-SAME: () local_unnamed_addr #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: store i256 304594385234, ptr addrspace(1) null, align 4294967296 +; CHECK-NEXT: store i256 56457598675863654, ptr addrspace(1) inttoptr (i256 32 to ptr addrspace(1)), align 32 +; CHECK-NEXT: [[HASH:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) null, i256 64) +; CHECK-NEXT: ret i256 [[HASH]] +; +entry: + store i256 304594385234, ptr addrspace(1) null, align 4294967296 + store i256 56457598675863654, ptr addrspace(1) inttoptr (i256 32 to ptr addrspace(1)), align 32 + %hash = call i256 @llvm.evm.sha3(ptr addrspace(1) null, i256 64) + ret i256 %hash +} + + +; Both the store instructions and the sha3 call has constexpr addresses. +define i256 @sha3_test_1() nounwind { +; CHECK-LABEL: define noundef i256 @sha3_test_1 +; CHECK-SAME: () local_unnamed_addr #[[ATTR1:[0-9]+]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: store i256 304594385234, ptr addrspace(1) null, align 4294967296 +; CHECK-NEXT: store i256 56457598675863654, ptr addrspace(1) inttoptr (i256 32 to ptr addrspace(1)), align 32 +; CHECK-NEXT: ret i256 -53675409633959416604748946233496653964072736789863655143901645101595015023086 +; +entry: + store i256 304594385234, ptr addrspace(1) null, align 4294967296 + store i256 56457598675863654, ptr addrspace(1) inttoptr (i256 32 to ptr addrspace(1)), align 32 + %hash = call i256 @llvm.evm.sha3(ptr addrspace(1) null, i256 64) + ret i256 %hash +} + +; Both the store instructions and the sha3 call has runtime addresses. +define i256 @sha3_test_2(ptr addrspace(1) nocapture %addr) nounwind { +; CHECK-LABEL: define noundef i256 @sha3_test_2 +; CHECK-SAME: (ptr addrspace(1) nocapture writeonly [[ADDR:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: store i256 304594385234, ptr addrspace(1) [[ADDR]], align 1 +; CHECK-NEXT: [[NEXT_ADDR:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 32 +; CHECK-NEXT: store i256 56457598675863654, ptr addrspace(1) [[NEXT_ADDR]], align 1 +; CHECK-NEXT: ret i256 -53675409633959416604748946233496653964072736789863655143901645101595015023086 +; +entry: + store i256 304594385234, ptr addrspace(1) %addr, align 1 + %next_addr = getelementptr i256, ptr addrspace(1) %addr, i256 1 + store i256 56457598675863654, ptr addrspace(1) %next_addr, align 1 + %hash = call i256 @llvm.evm.sha3(ptr addrspace(1) %addr, i256 64) + ret i256 %hash +} + +; Store instructions don't cover sha3 memory location, so no constant folding. +define i256 @sha3_test_3(ptr addrspace(1) nocapture %addr) nounwind { +; CHECK-LABEL: define i256 @sha3_test_3 +; CHECK-SAME: (ptr addrspace(1) nocapture [[ADDR:%.*]]) local_unnamed_addr #[[ATTR3:[0-9]+]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: store i256 0, ptr addrspace(1) [[ADDR]], align 1 +; CHECK-NEXT: [[NEXT_ADDR:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 32 +; CHECK-NEXT: store i256 304594385234, ptr addrspace(1) [[NEXT_ADDR]], align 1 +; CHECK-NEXT: [[NEXT_ADDR2:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 96 +; CHECK-NEXT: store i256 56457598675863654, ptr addrspace(1) [[NEXT_ADDR2]], align 1 +; CHECK-NEXT: [[HASH:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) [[ADDR]], i256 96) +; CHECK-NEXT: ret i256 [[HASH]] +; +entry: + store i256 0, ptr addrspace(1) %addr, align 1 + %next_addr = getelementptr i256, ptr addrspace(1) %addr, i256 1 + store i256 304594385234, ptr addrspace(1) %next_addr, align 1 + %next_addr2 = getelementptr i256, ptr addrspace(1) %addr, i256 3 + store i256 56457598675863654, ptr addrspace(1) %next_addr2, align 1 + %hash = call i256 @llvm.evm.sha3(ptr addrspace(1) %addr, i256 96) + ret i256 %hash +} + +; The second store partially overlaps sha3 memory location, +; so no constant folding. +define i256 @sha3_test_4(ptr addrspace(1) nocapture %addr) nounwind { +; CHECK-LABEL: define i256 @sha3_test_4 +; CHECK-SAME: (ptr addrspace(1) nocapture [[ADDR:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: store i256 0, ptr addrspace(1) [[ADDR]], align 1 +; CHECK-NEXT: [[NEXT_ADDR:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 32 +; CHECK-NEXT: store i512 304594385234, ptr addrspace(1) [[NEXT_ADDR]], align 1 +; CHECK-NEXT: [[HASH:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) [[ADDR]], i256 64) +; CHECK-NEXT: ret i256 [[HASH]] +; +entry: + store i256 0, ptr addrspace(1) %addr, align 1 + %next_addr = getelementptr i256, ptr addrspace(1) %addr, i256 1 + store i512 304594385234, ptr addrspace(1) %next_addr, align 1 + %hash = call i256 @llvm.evm.sha3(ptr addrspace(1) %addr, i256 64) + ret i256 %hash +} + +; Store instructions have different store sizes. +define i256 @sha3_test_5(ptr addrspace(1) nocapture %addr) nounwind { +; CHECK-LABEL: define noundef i256 @sha3_test_5 +; CHECK-SAME: (ptr addrspace(1) nocapture writeonly [[ADDR:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: store i128 0, ptr addrspace(1) [[ADDR]], align 1 +; CHECK-NEXT: [[NEXT_ADDR:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 16 +; CHECK-NEXT: store i128 304594385234, ptr addrspace(1) [[NEXT_ADDR]], align 1 +; CHECK-NEXT: [[NEXT_ADDR2:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 32 +; CHECK-NEXT: store i256 56457598675863654, ptr addrspace(1) [[NEXT_ADDR2]], align 1 +; CHECK-NEXT: ret i256 -53675409633959416604748946233496653964072736789863655143901645101595015023086 +; +entry: + store i128 0, ptr addrspace(1) %addr, align 1 + %next_addr = getelementptr i8, ptr addrspace(1) %addr, i256 16 + store i128 304594385234, ptr addrspace(1) %next_addr, align 1 + %next_addr2 = getelementptr i8, ptr addrspace(1) %addr, i256 32 + store i256 56457598675863654, ptr addrspace(1) %next_addr2, align 1 + %hash = call i256 @llvm.evm.sha3(ptr addrspace(1) %addr, i256 64) + ret i256 %hash +} + +; Only the first store is used for the constant folding. +define i256 @sha3_test_6(ptr addrspace(1) nocapture %addr) nounwind { +; CHECK-LABEL: define noundef i256 @sha3_test_6 +; CHECK-SAME: (ptr addrspace(1) nocapture writeonly [[ADDR:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: store i256 304594385234, ptr addrspace(1) [[ADDR]], align 1 +; CHECK-NEXT: [[NEXT_ADDR:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 32 +; CHECK-NEXT: store i256 56457598675863654, ptr addrspace(1) [[NEXT_ADDR]], align 1 +; CHECK-NEXT: ret i256 -1651279235167815098054286291856006982035426946965232889084721396369881222887 +; +entry: + store i256 304594385234, ptr addrspace(1) %addr, align 1 + %next_addr = getelementptr i256, ptr addrspace(1) %addr, i256 1 + store i256 56457598675863654, ptr addrspace(1) %next_addr, align 1 + %hash = call i256 @llvm.evm.sha3(ptr addrspace(1) %addr, i256 32) + ret i256 %hash +} + +; The second sha3 call gets folded, but not the first one because there is +; non-analyzable clobber. +define i256 @sha3_test_7(ptr addrspace(1) nocapture %addr, ptr addrspace(1) nocapture %addr2) nounwind { +; CHECK-LABEL: define i256 @sha3_test_7 +; CHECK-SAME: (ptr addrspace(1) nocapture [[ADDR:%.*]], ptr addrspace(1) nocapture writeonly [[ADDR2:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: store i256 304594385234, ptr addrspace(1) [[ADDR]], align 1 +; CHECK-NEXT: [[NEXT_ADDR:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 32 +; CHECK-NEXT: store i256 111, ptr addrspace(1) [[ADDR2]], align 1 +; CHECK-NEXT: store i256 56457598675863654, ptr addrspace(1) [[NEXT_ADDR]], align 1 +; CHECK-NEXT: [[HASH1:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) [[ADDR]], i256 64) +; CHECK-NEXT: [[HASH:%.*]] = add i256 [[HASH1]], 28454950007360609575222453380260700122861180288886985272557645317297017637223 +; CHECK-NEXT: ret i256 [[HASH]] +; +entry: + store i256 304594385234, ptr addrspace(1) %addr, align 1 + %next_addr = getelementptr i256, ptr addrspace(1) %addr, i256 1 + store i256 111, ptr addrspace(1) %addr2, align 1 + store i256 56457598675863654, ptr addrspace(1) %next_addr, align 1 + %hash1 = call i256 @llvm.evm.sha3(ptr addrspace(1) %addr, i256 64) + %hash2 = call i256 @llvm.evm.sha3(ptr addrspace(1) %next_addr, i256 32) + %hash = add i256 %hash1, %hash2 + ret i256 %hash +} + +; Memory locations of store instructions do alias with each other, so no +; constant folding. Theoretically we can support this case. TODO: CPR-1370. +define i256 @sha3_test_8(ptr addrspace(1) nocapture %addr) nounwind { +; CHECK-LABEL: define i256 @sha3_test_8 +; CHECK-SAME: (ptr addrspace(1) nocapture [[ADDR:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: store i256 0, ptr addrspace(1) [[ADDR]], align 1 +; CHECK-NEXT: [[NEXT_ADDR:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 31 +; CHECK-NEXT: store i256 304594385234, ptr addrspace(1) [[NEXT_ADDR]], align 1 +; CHECK-NEXT: [[NEXT_ADDR2:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 63 +; CHECK-NEXT: store i8 17, ptr addrspace(1) [[NEXT_ADDR2]], align 1 +; CHECK-NEXT: [[HASH:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) [[ADDR]], i256 64) +; CHECK-NEXT: ret i256 [[HASH]] +; +entry: + store i256 0, ptr addrspace(1) %addr, align 1 + %next_addr = getelementptr i8, ptr addrspace(1) %addr, i256 31 + store i256 304594385234, ptr addrspace(1) %next_addr, align 1 + %next_addr2 = getelementptr i8, ptr addrspace(1) %addr, i256 63 + store i8 17, ptr addrspace(1) %next_addr2, align 1 + %hash = call i256 @llvm.evm.sha3(ptr addrspace(1) %addr, i256 64) + ret i256 %hash +} + +; We have two sha3 calls where the second call gets folded on the second iteration. +define i256 @sha3_test_9(ptr addrspace(1) %addr) nounwind { +; CHECK-LABEL: define noundef i256 @sha3_test_9 +; CHECK-SAME: (ptr addrspace(1) nocapture writeonly [[ADDR:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: store i256 304594385234, ptr addrspace(1) [[ADDR]], align 1 +; CHECK-NEXT: [[NEXT_ADDR:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 32 +; CHECK-NEXT: store i256 -53675409633959416604748946233496653964072736789863655143901645101595015023076, ptr addrspace(1) [[NEXT_ADDR]], align 1 +; CHECK-NEXT: store i256 111111111111, ptr addrspace(1) [[ADDR]], align 1 +; CHECK-NEXT: ret i256 -28502626979061174856046376292559402895813043346926066817140289137910599757723 +; +entry: + store i256 304594385234, ptr addrspace(1) %addr, align 1 + %next_addr = getelementptr i256, ptr addrspace(1) %addr, i256 1 + store i256 56457598675863654, ptr addrspace(1) %next_addr, align 1 + %hash = call i256 @llvm.evm.sha3(ptr addrspace(1) %addr, i256 64) + %sum = add i256 %hash, 10 + store i256 %sum, ptr addrspace(1) %next_addr, align 1 + store i256 111111111111, ptr addrspace(1) %addr, align 1 + %hash2 = call i256 @llvm.evm.sha3(ptr addrspace(1) %addr, i256 64) + ret i256 %hash2 +} + +; Offset of the second store is too big (requires > 64 bits), so no constant folding. +define i256 @sha3_test_10() nounwind { +; CHECK-LABEL: define i256 @sha3_test_10 +; CHECK-SAME: () local_unnamed_addr #[[ATTR4:[0-9]+]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: store i256 304594385234, ptr addrspace(1) null, align 4294967296 +; CHECK-NEXT: store i256 56457598675863654, ptr addrspace(1) inttoptr (i256 18446744073709551616 to ptr addrspace(1)), align 4294967296 +; CHECK-NEXT: [[HASH:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) null, i256 64) +; CHECK-NEXT: ret i256 [[HASH]] +; +entry: + store i256 304594385234, ptr addrspace(1) null, align 4294967296 + store i256 56457598675863654, ptr addrspace(1) inttoptr (i256 18446744073709551616 to ptr addrspace(1)), align 32 + %hash = call i256 @llvm.evm.sha3(ptr addrspace(1) null, i256 64) + ret i256 %hash +} + +declare i256 @llvm.evm.sha3(ptr addrspace(1), i256) diff --git a/llvm/test/CodeGen/EVM/shift.ll b/llvm/test/CodeGen/EVM/shift.ll new file mode 100644 index 000000000000..6c0729a37134 --- /dev/null +++ b/llvm/test/CodeGen/EVM/shift.ll @@ -0,0 +1,44 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @shl(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: shl: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SHL +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = shl i256 %rs1, %rs2 + ret i256 %res +} + +define i256 @shr(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: shr: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SHR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = lshr i256 %rs1, %rs2 + ret i256 %res +} + +define i256 @sar(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: sar: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SAR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = ashr i256 %rs1, %rs2 + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/signextload.ll b/llvm/test/CodeGen/EVM/signextload.ll new file mode 100644 index 000000000000..30c6bce33e08 --- /dev/null +++ b/llvm/test/CodeGen/EVM/signextload.ll @@ -0,0 +1,80 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @load_signexti8(ptr addrspace(1) %ptr) nounwind { +; CHECK-LABEL: load_signexti8: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH1 0xF8 +; CHECK-NEXT: SAR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %load = load i8, ptr addrspace(1) %ptr + %sext = sext i8 %load to i256 + ret i256 %sext +} + +define i256 @load_signexti16(ptr addrspace(1) %ptr) nounwind { +; CHECK-LABEL: load_signexti16: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH1 0xF0 +; CHECK-NEXT: SAR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %load = load i16, ptr addrspace(1) %ptr + %sext = sext i16 %load to i256 + ret i256 %sext +} + +define i256 @load_signexti32(ptr addrspace(1) %ptr) nounwind { +; CHECK-LABEL: load_signexti32: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH1 0xE0 +; CHECK-NEXT: SAR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %load = load i32, ptr addrspace(1) %ptr + %sext = sext i32 %load to i256 + ret i256 %sext +} + +define i256 @load_signexti64(ptr addrspace(1) %ptr) nounwind { +; CHECK-LABEL: load_signexti64: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH1 0xC0 +; CHECK-NEXT: SAR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %load = load i64, ptr addrspace(1) %ptr + %sext = sext i64 %load to i256 + ret i256 %sext +} + +define i256 @load_signexti128(ptr addrspace(1) %ptr) nounwind { +; CHECK-LABEL: load_signexti128: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH1 0x80 +; CHECK-NEXT: SAR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %load = load i128, ptr addrspace(1) %ptr + %sext = sext i128 %load to i256 + ret i256 %sext +} diff --git a/llvm/test/CodeGen/EVM/single-use-expr-mulivalue.mir b/llvm/test/CodeGen/EVM/single-use-expr-mulivalue.mir new file mode 100644 index 000000000000..bdb47ec7a9c8 --- /dev/null +++ b/llvm/test/CodeGen/EVM/single-use-expr-mulivalue.mir @@ -0,0 +1,39 @@ +# RUN: llc -x mir -run-pass evm-single-use-expressions -verify-machineinstrs < %s | FileCheck %s + +--- | + + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm" + declare void @multival() + define void @dont_move_mulivalue() { ret void } + +... +--- +# This test ensures that we do not move FCALL instruction +# after the SIGNEXTEND one. +# +# CHECK-LABEL: bb.1: +# CHECK: %3:gpr, %4:gpr = FCALL @multival, %0 +# CHECK-NEXT: %5:gpr = SIGNEXTEND %1, %4 +# CHECK-NEXT: %6:gpr = AND %3, %2 +name: dont_move_mulivalue +tracksRegLiveness: true +registers: + - { id: 0, class: gpr } +body: | + bb.0: + + %0:gpr = SELFBALANCE implicit-def dead $arguments + %1:gpr = SELFBALANCE implicit-def dead $arguments + %2:gpr = SELFBALANCE implicit-def dead $arguments + + bb.1: + + %3:gpr, %4:gpr = FCALL @multival, %0:gpr, implicit-def dead $arguments, implicit $sp + %5:gpr = SIGNEXTEND %1:gpr, %4:gpr, implicit-def dead $arguments + %6:gpr = AND %3:gpr, %2:gpr, implicit-def dead $arguments + + bb.2: + + RET %5:gpr, %6:gpr +... diff --git a/llvm/test/CodeGen/EVM/single-use-expressions-rematerialization.mir b/llvm/test/CodeGen/EVM/single-use-expressions-rematerialization.mir new file mode 100644 index 000000000000..b9de7053c4b8 --- /dev/null +++ b/llvm/test/CodeGen/EVM/single-use-expressions-rematerialization.mir @@ -0,0 +1,61 @@ +# RUN: llc -x mir -run-pass=evm-single-use-expressions < %s | FileCheck %s + +--- | + ; ModuleID = 'rematerialize_calldataload.ll' + source_filename = "rematerialize_calldataload.ll" + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm-unknown-unknown" + + define void @dont_rematerialize_redefinition() { + ret void + } + + define void @rematerialize_calldataload() { + ret void + } + +... +--- +# CHECK-LABEL: name: dont_rematerialize_redefinition +# CHECK: CALLDATALOAD %{{[0-9]+}} +# CHECK-NOT: CALLDATALOAD %{{[0-9]+}} +name: dont_rematerialize_redefinition +tracksRegLiveness: true +body: | + bb.0: + liveins: $arguments + + %0:gpr = ARGUMENT 0, implicit $arguments + %2:gpr = CONST_I256 i256 1, implicit-def dead $arguments + JUMP %bb.2, implicit-def $arguments + + bb.1: + %4:gpr = MUL %2, %0, implicit-def dead $arguments + RET %4, implicit-def dead $arguments + + bb.2: + %2:gpr = CALLDATALOAD %2, implicit-def dead $arguments + %4:gpr = ADD %2, %2, implicit-def dead $arguments + JUMP %bb.1, implicit-def $arguments + +... +--- +# CHECK-LABEL: name: rematerialize_calldataload +# CHECK: [[K1:%[0-9]+]]:gpr = CONST_I256 i256 1 +# CHECK-NEXT: [[L1:%[0-9]+]]:gpr = CALLDATALOAD [[K1]] +# CHECK-NEXT: [[K2:%[0-9]+]]:gpr = CONST_I256 i256 1 +# CHECK-NEXT: [[L2:%[0-9]+]]:gpr = CALLDATALOAD [[K2]] +# CHECK-NEXT: [[SUM:%[0-9]+]]:gpr = ADD [[L2]], [[L1]] +name: rematerialize_calldataload +tracksRegLiveness: true +body: | + bb.0: + liveins: $arguments + + %0:gpr = ARGUMENT 0, implicit $arguments + %2:gpr = CONST_I256 i256 1, implicit-def dead $arguments + %3:gpr = CALLDATALOAD %2, implicit-def dead $arguments + %4:gpr = ADD %3, %3, implicit-def dead $arguments + RET %4, implicit-def dead $arguments + +... diff --git a/llvm/test/CodeGen/EVM/solx-issue-136-bugpoint.ll b/llvm/test/CodeGen/EVM/solx-issue-136-bugpoint.ll new file mode 100644 index 000000000000..90512a4d0579 --- /dev/null +++ b/llvm/test/CodeGen/EVM/solx-issue-136-bugpoint.ll @@ -0,0 +1,54 @@ +; RUN: llc < %s + +; EVMSingleUseExpression rematerializes CALLDATALOAD when loading from a +; constant address. +; Before the fix, this broke when CALLDATALOAD redefined its input virtual +; register. +; Reduced from issue #136; minimized with bugpoint. +; https://github.com/matter-labs/solx/issues/136 +; Triggers the coalescer to coalesce the CALLDATALOAD def and its use. +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +define void @__entry() { +entry: + switch i32 poison, label %if_join [ + i32 306754293, label %switch_case_branch_1_block + i32 1826699332, label %switch_case_branch_2_block + i32 -974522369, label %switch_case_branch_3_block + i32 -171475469, label %switch_case_branch_4_block + ] + +if_join: ; preds = %entry + unreachable + +switch_case_branch_1_block: ; preds = %entry + unreachable + +switch_case_branch_2_block: ; preds = %entry + unreachable + +switch_case_branch_3_block: ; preds = %entry + %calldata_load_result.i = load i256, ptr addrspace(2) inttoptr (i256 4 to ptr addrspace(2)), align 4 + %calldata_load_result.i51 = load i256, ptr addrspace(2) inttoptr (i256 68 to ptr addrspace(2)), align 4 + br i1 poison, label %switch_join_block.i, label %checked_div_t_int256.exit.i + +switch_join_block.i: ; preds = %switch_case_branch_3_block + %comparison_result6.i.i = icmp eq i256 %calldata_load_result.i51, -1 + %comparison_result9.i.i = icmp eq i256 %calldata_load_result.i, -57896044618658097711785492504343953926634992332820282019728792003956564819968 + %and_result21.i.i = and i1 %comparison_result9.i.i, %comparison_result6.i.i + br i1 %and_result21.i.i, label %if_main12.i.i, label %checked_div_t_int256.exit.i + +if_main12.i.i: ; preds = %switch_join_block.i + unreachable + +checked_div_t_int256.exit.i: ; preds = %switch_join_block.i, %switch_case_branch_3_block + %expr_38.0.i7078 = phi i256 [ %calldata_load_result.i51, %switch_join_block.i ], [ 68, %switch_case_branch_3_block ] + %division_signed_result_non_zero.i.i = sdiv i256 %calldata_load_result.i, %expr_38.0.i7078 + %remainder_signed_result_non_zero.i.i = srem i256 %division_signed_result_non_zero.i.i, %calldata_load_result.i + store i256 %remainder_signed_result_non_zero.i.i, ptr addrspace(1) inttoptr (i256 128 to ptr addrspace(1)), align 128 + unreachable + +switch_case_branch_4_block: ; preds = %entry + unreachable +} diff --git a/llvm/test/CodeGen/EVM/stack-ops-commutable.ll b/llvm/test/CodeGen/EVM/stack-ops-commutable.ll new file mode 100644 index 000000000000..851274ffd6f2 --- /dev/null +++ b/llvm/test/CodeGen/EVM/stack-ops-commutable.ll @@ -0,0 +1,416 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define void @no_manipulations_needed_with_junk(i256 %a1, i256 %a2, i256 %a3) noreturn { +; CHECK-LABEL: no_manipulations_needed_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %x1 = add i256 %a1, %a2 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x1) + unreachable +} + +define void @no_manipulations_needed_with_junk_eq(i256 %a1, i256 %a2, i256 %a3) noreturn { +; CHECK-LABEL: no_manipulations_needed_with_junk_eq: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: EQ +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %cmp = icmp eq i256 %a1, %a2 + %x1 = zext i1 %cmp to i256 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x1) + unreachable + +} + +define i256 @no_manipulations_needed_no_junk_addmod(i256 %a1, i256 %a2, i256 %a3) { +; CHECK-LABEL: no_manipulations_needed_no_junk_addmod: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ADDMOD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = call i256 @llvm.evm.addmod(i256 %a2, i256 %a1, i256 %a3) + ret i256 %x1 +} + +define i256 @no_manipulations_needed_no_junk_mulmod(i256 %a1, i256 %a2, i256 %a3) { +; CHECK-LABEL: no_manipulations_needed_no_junk_mulmod: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MULMOD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = call i256 @llvm.evm.mulmod(i256 %a2, i256 %a1, i256 %a3) + ret i256 %x1 +} + +define i256 @no_manipulations_needed_no_junk_and(i256 %a1, i256 %a2) { +; CHECK-LABEL: no_manipulations_needed_no_junk_and: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = and i256 %a2, %a1 + ret i256 %x1 +} + +define i256 @no_manipulations_needed_no_junk_or(i256 %a1, i256 %a2) { +; CHECK-LABEL: no_manipulations_needed_no_junk_or: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: OR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = or i256 %a2, %a1 + ret i256 %x1 +} + +define i256 @no_manipulations_needed_no_junk_xor(i256 %a1, i256 %a2) { +; CHECK-LABEL: no_manipulations_needed_no_junk_xor: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: XOR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = xor i256 %a2, %a1 + ret i256 %x1 +} + +define i256 @no_manipulations_needed_no_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: no_manipulations_needed_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = add i256 %a1, %a2 + ret i256 %x1 +} + +define void @reorder_with_junk(i256 %a1, i256 %a2, i256 %a3) noreturn { +; CHECK-LABEL: reorder_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %x1 = add i256 %a2, %a1 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x1) + unreachable +} + +define i256 @reorder_no_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: reorder_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = add i256 %a2, %a1 + ret i256 %x1 +} + +define void @swap_first_with_junk(i256 %a1, i256 %a2, i256 %a3) noreturn { +; CHECK-LABEL: swap_first_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %x1 = add i256 %a3, %a2 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x1) + unreachable +} + +define i256 @two_commutable(i256 %a1, i256 %a2, i256 %a3) { +; CHECK-LABEL: two_commutable: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: ADD +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = add i256 %a3, %a2 + %x2 = add i256 %a1, %x1 + ret i256 %x2 +} + +define void @swap_second_with_junk(i256 %a1, i256 %a2, i256 %a3, i256 %a4) noreturn { +; CHECK-LABEL: swap_second_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %x1 = add i256 %a1, %a4 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x1) + unreachable +} + +define i256 @swap_first_no_junk(i256 %a1, i256 %a2, i256 %a3, i256 %a4) nounwind { +; CHECK-LABEL: swap_first_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = add i256 %a1, %a4 + ret i256 %x1 +} + +define i256 @swap_second_no_junk(i256 %a1, i256 %a2, i256 %a3, i256 %a4) nounwind { +; CHECK-LABEL: swap_second_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = add i256 %a4, %a1 + ret i256 %x1 +} + +define void @first_arg_alive_with_junk(i256 %a1, i256 %a2, i256 %a3) noreturn { +; CHECK-LABEL: first_arg_alive_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH1 0x4 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SUB +; CHECK-NEXT: DIV +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %x1 = add i256 %a1, %a2 + %x2 = sub i256 %a1, 4 + %x3 = udiv i256 %x2, %x1 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x3) + unreachable +} + +define i256 @first_arg_alive_no_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: first_arg_alive_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH1 0x4 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SUB +; CHECK-NEXT: DIV +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = add i256 %a1, %a2 + %x2 = sub i256 %a1, 4 + %x3 = udiv i256 %x2, %x1 + ret i256 %x3 +} + +define void @second_arg_alive_with_junk(i256 %a1, i256 %a2, i256 %a3) noreturn { +; CHECK-LABEL: second_arg_alive_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH1 0x4 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: SWAP4 +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SUB +; CHECK-NEXT: DIV +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %x1 = add i256 %a1, %a2 + %x2 = sub i256 %a2, 4 + %x3 = udiv i256 %x2, %x1 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x3) + unreachable +} + +define i256 @second_arg_alive_no_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: second_arg_alive_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH1 0x4 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: SWAP4 +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SUB +; CHECK-NEXT: DIV +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = add i256 %a1, %a2 + %x2 = sub i256 %a2, 4 + %x3 = udiv i256 %x2, %x1 + ret i256 %x3 +} + +define void @both_arg_alive_with_junk(i256 %a1, i256 %a2, i256 %a3) noreturn { +; CHECK-LABEL: both_arg_alive_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: POP +; CHECK-NEXT: DUP2 +; CHECK-NEXT: DIV +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: ADD +; CHECK-NEXT: ADD +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %x1 = add i256 %a1, %a2 + %x2 = udiv i256 %a2, %a1 + %x3 = add i256 %x1, %x2 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x3) + unreachable +} + +define i256 @both_arg_alive_no_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: both_arg_alive_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: POP +; CHECK-NEXT: DUP2 +; CHECK-NEXT: DIV +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: ADD +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = add i256 %a1, %a2 + %x2 = udiv i256 %a2, %a1 + %x3 = add i256 %x1, %x2 + ret i256 %x3 +} + +define i256 @same_arg_dead_with_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: same_arg_dead_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT +; CHECK-NEXT: JUMP + %x1 = add i256 %a2, %a2 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x1) + ret i256 %x1 +} + +define void @commutable_not_in_function_entry() noreturn { +; CHECK-LABEL: commutable_not_in_function_entry: +; CHECK: ; %bb.0: ; %enter +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: CALLDATALOAD +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: .BB22_1: ; %header +; CHECK-NEXT: ; =>This Inner Loop Header: Depth=1 +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 0x3 +; CHECK-NEXT: SIGNEXTEND +; CHECK-NEXT: SLT +; CHECK-NEXT: PUSH4 @.BB22_3 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.2: ; %do +; CHECK-NEXT: ; in Loop: Header=BB22_1 Depth=1 +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: MUL +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.BB22_1 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB22_3: ; %exit +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH4 0xFFFFFFFF +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: MSTORE +; CHECK-NEXT: PUSH1 0x20 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN + +enter: + %offset = inttoptr i256 0 to ptr addrspace(2) + %load = call i256 @llvm.evm.calldataload(ptr addrspace(2) %offset) + %calldata = trunc i256 %load to i32 + br label %header + +header: + %phi = phi i32 [ %calldata, %enter ], [ %inc, %do ] + %phi2 = phi i32 [ 1, %enter ], [ %mul, %do ] + %cmp = icmp sgt i32 %phi, 0 + br i1 %cmp, label %do, label %exit + +do: + %mul = mul nsw i32 %phi2, %phi + %inc = add nsw i32 %phi, -1 + br label %header + +exit: + %res = zext i32 %phi2 to i256 + store i256 %res, ptr addrspace(1) null, align 4 + call void @llvm.evm.return(ptr addrspace(1) null, i256 32) + unreachable +} + +declare i256 @llvm.evm.addmod(i256, i256, i256) +declare i256 @llvm.evm.mulmod(i256, i256, i256) +declare i256 @llvm.evm.calldataload(ptr addrspace(2)) +declare void @llvm.evm.return(ptr addrspace(1), i256) +declare void @llvm.evm.revert(ptr addrspace(1), i256) diff --git a/llvm/test/CodeGen/EVM/stack-ops.ll b/llvm/test/CodeGen/EVM/stack-ops.ll new file mode 100644 index 000000000000..e401dc15df3c --- /dev/null +++ b/llvm/test/CodeGen/EVM/stack-ops.ll @@ -0,0 +1,354 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define void @no_manipulations_needed_with_junk(i256 %a1, i256 %a2, i256 %a3) noreturn { +; CHECK-LABEL: no_manipulations_needed_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: SUB +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %x1 = sub i256 %a1, %a2 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x1) + unreachable +} + +define i256 @no_manipulations_needed_no_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: no_manipulations_needed_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = sub i256 %a1, %a2 + ret i256 %x1 +} + +define void @reorder_with_junk(i256 %a1, i256 %a2, i256 %a3) noreturn { +; CHECK-LABEL: reorder_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: SUB +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %x1 = sub i256 %a2, %a1 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x1) + unreachable +} + +define i256 @reorder_no_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: reorder_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = sub i256 %a2, %a1 + ret i256 %x1 +} + +define void @swap_first_with_junk(i256 %a1, i256 %a2, i256 %a3) noreturn { +; CHECK-LABEL: swap_first_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SUB +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %x1 = sub i256 %a3, %a2 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x1) + unreachable +} + +define void @swap_second_with_junk(i256 %a1, i256 %a2, i256 %a3, i256 %a4) noreturn { +; CHECK-LABEL: swap_second_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: POP +; CHECK-NEXT: SUB +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %x1 = sub i256 %a1, %a4 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x1) + unreachable +} + +define i256 @swap_first_no_junk(i256 %a1, i256 %a2, i256 %a3, i256 %a4) nounwind { +; CHECK-LABEL: swap_first_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = sub i256 %a3, %a2 + ret i256 %x1 +} + +define i256 @swap_second_no_junk(i256 %a1, i256 %a2, i256 %a3, i256 %a4) nounwind { +; CHECK-LABEL: swap_second_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: POP +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = sub i256 %a1, %a4 + ret i256 %x1 +} + +define void @swap_both_with_junk(i256 %a1, i256 %a2, i256 %a3, i256 %a4) noreturn { +; CHECK-LABEL: swap_both_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: POP +; CHECK-NEXT: SUB +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %x1 = sub i256 %a4, %a1 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x1) + unreachable +} + +define i256 @swap_both_no_junk(i256 %a1, i256 %a2, i256 %a3, i256 %a4) nounwind { +; CHECK-LABEL: swap_both_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: POP +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = sub i256 %a4, %a1 + ret i256 %x1 +} + +define void @first_arg_alive_with_junk(i256 %a1, i256 %a2, i256 %a3) noreturn { +; CHECK-LABEL: first_arg_alive_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH1 0x4 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SUB +; CHECK-NEXT: DIV +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %x1 = sub i256 %a1, %a2 + %x2 = sub i256 %a1, 4 + %x3 = udiv i256 %x2, %x1 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x3) + unreachable +} + +define i256 @first_arg_alive_no_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: first_arg_alive_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH1 0x4 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SUB +; CHECK-NEXT: DIV +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = sub i256 %a1, %a2 + %x2 = sub i256 %a1, 4 + %x3 = udiv i256 %x2, %x1 + ret i256 %x3 +} + +define void @second_arg_alive_with_junk(i256 %a1, i256 %a2, i256 %a3) noreturn { +; CHECK-LABEL: second_arg_alive_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP2 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: POP +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 0x4 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SUB +; CHECK-NEXT: DIV +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %x1 = sub i256 %a1, %a2 + %x2 = sub i256 %a2, 4 + %x3 = udiv i256 %x2, %x1 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x3) + unreachable +} + +define i256 @second_arg_alive_no_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: second_arg_alive_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP2 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: POP +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 0x4 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SUB +; CHECK-NEXT: DIV +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = sub i256 %a1, %a2 + %x2 = sub i256 %a2, 4 + %x3 = udiv i256 %x2, %x1 + ret i256 %x3 +} + +define void @both_arg_alive_with_junk(i256 %a1, i256 %a2, i256 %a3) noreturn { +; CHECK-LABEL: both_arg_alive_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: POP +; CHECK-NEXT: DUP2 +; CHECK-NEXT: DIV +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SUB +; CHECK-NEXT: ADD +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %x1 = sub i256 %a1, %a2 + %x2 = udiv i256 %a2, %a1 + %x3 = add i256 %x1, %x2 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x3) + unreachable +} + +define i256 @both_arg_alive_no_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: both_arg_alive_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: POP +; CHECK-NEXT: DUP2 +; CHECK-NEXT: DIV +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SUB +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = sub i256 %a1, %a2 + %x2 = udiv i256 %a2, %a1 + %x3 = add i256 %x1, %x2 + ret i256 %x3 +} + +define i256 @same_arg_dead_with_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: same_arg_dead_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT +; CHECK-NEXT: JUMP + %x1 = add i256 %a2, %a2 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x1) + ret i256 %x1 +} + +define i256 @same_arg_dead_no_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: same_arg_dead_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = add i256 %a2, %a2 + ret i256 %x1 +} + +define i256 @same_arg_alive_with_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: same_arg_alive_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: DUP1 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT +; CHECK-NEXT: JUMP + %x1 = add i256 %a2, %a2 + %x2 = add i256 %a2, %x1 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x2) + ret i256 %x2 +} + +define i256 @same_arg_alive_no_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: same_arg_alive_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: DUP1 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = add i256 %a2, %a2 + %x2 = add i256 %a2, %x1 + ret i256 %x2 +} + +declare void @llvm.evm.revert(ptr addrspace(1), i256) diff --git a/llvm/test/CodeGen/EVM/stack-too-deep-1.ll b/llvm/test/CodeGen/EVM/stack-too-deep-1.ll new file mode 100644 index 000000000000..044647aa6521 --- /dev/null +++ b/llvm/test/CodeGen/EVM/stack-too-deep-1.ll @@ -0,0 +1,190 @@ +; REQUIRES: asserts +; RUN: llc -evm-stack-region-offset=128 -evm-stack-region-size=32 --debug-only=evm-stack-solver < %s 2>&1 | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +; Check that the stack solver detects unreachable slots, generates spills for them, and +; succesfully compiles the function. Also, check that we allocated the exact amount of +; stack space needed for the function, without any warnings about allocated stack region size. + +; CHECK: Unreachable slots found: 2, iteration: 1 +; CHECK: Spilling 1 registers +; CHECK-NOT: warning: allocated stack region size: + +define dso_local fastcc void @main() unnamed_addr { +entry: + br label %"block_rt_19/0" + +"block_rt_19/0": ; preds = %entry + %addition_result2158 = add nuw nsw i256 1, 20 + %and_result2674 = and i256 %addition_result2158, 255 + %trunc = trunc i256 %addition_result2158 to i8 + %comparison_result3799.not = icmp eq i256 %and_result2674, 3 + %comparison_result6184.not = icmp eq i256 %and_result2674, 24 + %comparison_result7522.not = icmp eq i256 %and_result2674, 26 + %comparison_result9235.not = icmp eq i256 %and_result2674, 28 + br label %"block_rt_44/7" + +"block_rt_43/7": ; preds = %remainder_join12933, %"block_rt_54/7.thread", %"block_rt_44/7" + unreachable + +"block_rt_44/7": ; preds = %"block_rt_19/0" + switch i8 %trunc, label %"block_rt_51/7" [ + i8 1, label %"block_rt_43/7" + i8 21, label %conditional_rt_49_join_block2921 + i8 11, label %"block_rt_46/7" + ] + +"block_rt_46/7": ; preds = %"block_rt_51/7", %"block_rt_44/7" + unreachable + +"block_rt_51/7": ; preds = %"block_rt_57/7", %"block_rt_44/7" + %stack_var_010.1 = phi i256 [ %multiplication_result1217, %"block_rt_57/7" ], [ 1, %"block_rt_44/7" ] + switch i8 %trunc, label %"block_rt_54/7.thread" [ + i8 22, label %"block_rt_46/7" + i8 33, label %"block_rt_54/7" + ] + +"block_rt_54/7": ; preds = %"block_rt_51/7" + %comparison_result3562 = icmp ugt i256 %stack_var_010.1, 1 + unreachable + +"block_rt_56/7.outer": ; preds = %"block_rt_56/7.preheader", %"block_rt_70/7" + %stack_var_011.8.ph = phi i256 [ 0, %"block_rt_56/7.preheader" ], [ %addition_result2182, %"block_rt_70/7" ] + br label %"block_rt_56/7" + +"block_rt_56/7": ; preds = %"block_rt_64/7", %"block_rt_56/7.outer" + %stack_var_011.8 = phi i256 [ %addition_result2182, %"block_rt_64/7" ], [ %stack_var_011.8.ph, %"block_rt_56/7.outer" ] + %and_result2179 = and i256 %stack_var_011.8, 255 + %addition_result2182 = add nuw nsw i256 %and_result2179, 2 + br label %"block_rt_64/7" + +"block_rt_57/7": ; preds = %"block_rt_64/7" + %multiplication_result1217 = shl nuw nsw i256 %stack_var_010.1, 1 + br label %"block_rt_51/7" + +"block_rt_64/7": ; preds = %"block_rt_56/7" + switch i8 %trunc, label %conditional_rt_68_join_block5874 [ + i8 12, label %"block_rt_56/7" + i8 23, label %"block_rt_57/7" + ] + +"block_rt_70/7": ; preds = %conditional_rt_68_join_block5874 + %comparison_result6061 = icmp ugt i256 %and_result5861, 2 + %or.cond = or i1 %comparison_result6184.not, %comparison_result6061 + br i1 %or.cond, label %"block_rt_56/7.outer", label %"block_rt_73/7" + +"block_rt_73/7": ; preds = %"block_rt_80/7", %"block_rt_70/7" + %stack_var_013.2 = phi i256 [ %addition_result6585, %"block_rt_80/7" ], [ 10, %"block_rt_70/7" ] + %and_result6465 = and i256 %stack_var_013.2, 255 + br i1 false, label %"block_rt_123/8", label %conditional_rt_74_join_block6475 + +"block_rt_80/7": ; preds = %"block_rt_86/7" + %addition_result6585 = add nuw nsw i256 %stack_var_013.2, 1 + br label %"block_rt_73/7" + +"block_rt_86/7": ; preds = %remainder_join12901 + br i1 %comparison_result7522.not, label %"block_rt_80/7", label %"block_rt_88/7.outer" + +"block_rt_88/7.outer": ; preds = %"block_rt_101/7", %"block_rt_86/7" + %stack_var_008.19.ph = phi i256 [ poison, %"block_rt_86/7" ], [ %stack_var_008.20, %"block_rt_101/7" ] + %stack_var_015.1.ph = phi i256 [ 10, %"block_rt_86/7" ], [ %addition_result2206, %"block_rt_101/7" ] + %and_result7774 = and i256 %stack_var_015.1.ph, 255 + switch i8 %trunc, label %"block_rt_92/7" [ + i8 7, label %conditional_rt_90_join_block7798 + i8 27, label %"block_rt_131/7.backedge" + ] + +"block_rt_89/7": ; preds = %"block_rt_95/7" + unreachable + +"block_rt_131/7.outer": ; preds = %conditional_rt_74_join_block6475 + br label %"block_rt_131/7" + +"block_rt_92/7": ; preds = %"block_rt_88/7.outer" + %addition_result2206 = add nuw nsw i256 %and_result7774, 1 + br label %"block_rt_95/7" + +"block_rt_95/7": ; preds = %"block_rt_92/7" + %comparison_result8476.not = icmp eq i256 %addition_result2206, 16 + br i1 %comparison_result8476.not, label %"block_rt_89/7", label %"block_rt_96/7" + +"block_rt_96/7": ; preds = %"block_rt_95/7" + switch i8 %trunc, label %"block_rt_101/7.preheader" [ + i8 31, label %"block_rt_97/7" + i8 32, label %"block_rt_99/7" + ] + +"block_rt_97/7": ; preds = %"block_rt_96/7" + unreachable + +"block_rt_99/7": ; preds = %"block_rt_96/7" + unreachable + +"block_rt_101/7.preheader": ; preds = %"block_rt_96/7" + br label %"block_rt_101/7" + +"block_rt_101/7": ; preds = %"block_rt_103/7", %"block_rt_101/7.preheader" + %stack_var_008.20 = phi i256 [ %stack_var_008.21, %"block_rt_103/7" ], [ %stack_var_008.19.ph, %"block_rt_101/7.preheader" ] + %or.cond20853 = or i1 %comparison_result9235.not, false + br i1 %or.cond20853, label %"block_rt_88/7.outer", label %remainder_join12933 + +"block_rt_103/7": ; preds = %remainder_join12933, %"block_rt_108/7" + %stack_var_008.21 = phi i256 [ %addition_result10002, %"block_rt_108/7" ], [ %stack_var_008.20, %remainder_join12933 ] + br label %"block_rt_101/7" + +"block_rt_108/7": ; preds = %remainder_join12933 + %and_result9999 = and i256 %stack_var_008.20, 18446744073709551615 + %addition_result10002 = add nuw nsw i256 %and_result9999, 1 + br label %"block_rt_103/7" + +"block_rt_123/8": ; preds = %conditional_rt_74_join_block6475, %"block_rt_73/7" + %addition_result12201 = add nuw nsw i256 %and_result5861, 1 + br label %conditional_rt_68_join_block5874 + +"block_rt_131/7": ; preds = %"block_rt_131/7.backedge", %"block_rt_131/7.outer" + %stack_var_014.1 = phi i256 [ 7, %"block_rt_131/7.outer" ], [ %subtraction_result7209, %"block_rt_131/7.backedge" ] + %and_result7206 = and i256 %stack_var_014.1, 255 + %subtraction_result7209 = add nsw i256 %and_result7206, -1 + br label %remainder_join12901 + +conditional_rt_49_join_block2921: ; preds = %"block_rt_44/7" + unreachable + +"block_rt_54/7.thread": ; preds = %"block_rt_51/7" + br i1 %comparison_result3799.not, label %"block_rt_43/7", label %"block_rt_56/7.preheader" + +"block_rt_56/7.preheader": ; preds = %"block_rt_54/7.thread" + br label %"block_rt_56/7.outer" + +conditional_rt_68_join_block5874: ; preds = %"block_rt_123/8", %"block_rt_64/7" + %stack_var_012.2 = phi i256 [ %addition_result12201, %"block_rt_123/8" ], [ 1, %"block_rt_64/7" ] + %and_result5861 = and i256 %stack_var_012.2, 255 + br label %"block_rt_70/7" + +conditional_rt_74_join_block6475: ; preds = %"block_rt_73/7" + switch i8 %trunc, label %"block_rt_131/7.outer" [ + i8 5, label %conditional_rt_76_join_block6491 + i8 25, label %"block_rt_123/8" + ] + +conditional_rt_76_join_block6491: ; preds = %conditional_rt_74_join_block6475 + unreachable + +conditional_rt_90_join_block7798: ; preds = %"block_rt_88/7.outer" + unreachable + +remainder_join12901: ; preds = %"block_rt_131/7" + %remainder_result_non_zero12904 = and i256 %subtraction_result7209, 1 + br i1 poison, label %"block_rt_131/7.backedge", label %"block_rt_86/7" + +"block_rt_131/7.backedge": ; preds = %remainder_join12901, %"block_rt_88/7.outer" + br label %"block_rt_131/7" + +remainder_join12933: ; preds = %"block_rt_101/7" + switch i8 %trunc, label %"block_rt_108/7" [ + i8 8, label %"block_rt_43/7" + i8 13, label %"block_rt_103/7" + ] +} diff --git a/llvm/test/CodeGen/EVM/stack-too-deep-2.ll b/llvm/test/CodeGen/EVM/stack-too-deep-2.ll new file mode 100644 index 000000000000..03e3ea1c5826 --- /dev/null +++ b/llvm/test/CodeGen/EVM/stack-too-deep-2.ll @@ -0,0 +1,245 @@ +; REQUIRES: asserts +; RUN: llc -evm-stack-region-offset=128 -evm-stack-region-size=192 --debug-only=evm-stack-solver < %s 2>&1 | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare i256 @checked_mul_uint8(i256) + +; Check that the stack solver detects unreachable slots, generates spills for them, and +; succesfully compiles the function. Also, check that we allocated the exact amount of +; stack space needed for the function, without any warnings about allocated stack region size. + +; CHECK: Unreachable slots found: 30, iteration: 1 +; CHECK: Spilling 2 registers +; CHECK: Unreachable slots found: 20, iteration: 2 +; CHECK: Spilling 1 registers +; CHECK: Unreachable slots found: 8, iteration: 3 +; CHECK: Spilling 1 registers +; CHECK: Unreachable slots found: 6, iteration: 4 +; CHECK: Spilling 1 registers +; CHECK: Unreachable slots found: 2, iteration: 5 +; CHECK: Spilling 1 registers +; CHECK-NOT: warning: allocated stack region size: + +define fastcc i256 @fun_test_462(i256 %0) unnamed_addr { +entry: + %and_result3 = and i256 %0, 255 + %trunc = trunc i256 %0 to i8 + %comparison_result38 = icmp eq i256 %and_result3, 22 + %comparison_result45 = icmp eq i256 %and_result3, 33 + %comparison_result68 = icmp eq i256 %and_result3, 3 + %comparison_result113 = icmp eq i256 %and_result3, 4 + %comparison_result126 = icmp eq i256 %and_result3, 24 + %comparison_result187 = icmp eq i256 %and_result3, 26 + %comparison_result237 = icmp eq i256 %and_result3, 31 + %comparison_result255 = icmp eq i256 %and_result3, 32 + %comparison_result282 = icmp eq i256 %and_result3, 28 + %comparison_result330 = icmp eq i256 %and_result3, 6 + br label %for_condition + +return.loopexit572.split.loop.exit599: ; preds = %if_join42 + %var_cnt.2.mux.le = select i1 %spec.select, i256 %var_cnt.2, i256 30 + br label %return + +return: ; preds = %if_join295, %for_join195, %for_body193, %for_body132, %if_join110, %checked_mul_uint8_1420.exit, %for_body, %for_condition, %return.loopexit572.split.loop.exit599 + %return_pointer.0 = phi i256 [ 10, %checked_mul_uint8_1420.exit ], [ %var_cnt.2.mux.le, %return.loopexit572.split.loop.exit599 ], [ 80, %if_join295 ], [ 70, %for_body193 ], [ 60, %for_join195 ], [ 50, %for_body132 ], [ 40, %if_join110 ], [ 0, %for_body ], [ %var_cnt.0, %for_condition ] + ret i256 %return_pointer.0 + +for_condition: ; preds = %for_increment, %entry + %var_i.0 = phi i256 [ 0, %entry ], [ %addition_result348, %for_increment ] + %var_cnt.0 = phi i256 [ 0, %entry ], [ %var_cnt.1, %for_increment ] + %comparison_result = icmp ult i256 %var_i.0, 2 + br i1 %comparison_result, label %for_body, label %return + +for_body: ; preds = %for_condition + switch i8 %trunc, label %for_condition22 [ + i8 1, label %checked_mul_uint8_1420.exit + i8 21, label %return + i8 11, label %for_increment + ] + +for_increment: ; preds = %for_condition22, %for_body + %var_cnt.1 = phi i256 [ %var_cnt.0, %for_body ], [ %var_cnt.2, %for_condition22 ] + %addition_result348 = add nuw nsw i256 %var_i.0, 1 + br label %for_condition + +checked_mul_uint8_1420.exit: ; preds = %for_body + br label %return + +for_condition22: ; preds = %for_join65, %for_body + %var_j.0 = phi i256 [ %checked_mul_uint8_call, %for_join65 ], [ 1, %for_body ] + %var_cnt.2 = phi i256 [ %var_cnt.3.lcssa541, %for_join65 ], [ %var_cnt.0, %for_body ] + %comparison_result29 = icmp ugt i256 %var_j.0, 3 + %or.cond = or i1 %comparison_result38, %comparison_result29 + br i1 %or.cond, label %for_increment, label %if_join42 + +if_join42: ; preds = %for_condition22 + %comparison_result55 = icmp ugt i256 %var_j.0, 1 + %spec.select = and i1 %comparison_result45, %comparison_result55 + %brmerge = or i1 %spec.select, %comparison_result68 + br i1 %brmerge, label %return.loopexit572.split.loop.exit599, label %for_condition62.outer + +for_condition62.outer: ; preds = %if_join117, %if_join42 + %var_p.0.ph = phi i256 [ 0, %if_join42 ], [ %addition_result.i, %if_join117 ] + %var_cnt.3.ph = phi i256 [ %var_cnt.2, %if_join42 ], [ %var_cnt.5, %if_join117 ] + br label %for_condition62 + +for_condition62: ; preds = %if_join84, %for_condition62.outer + %var_p.0 = phi i256 [ %addition_result.i, %if_join84 ], [ %var_p.0.ph, %for_condition62.outer ] + %and_result.i410 = and i256 %var_p.0, 255 + %comparison_result.i = icmp ugt i256 %and_result.i410, 253 + br i1 %comparison_result.i, label %shift_left_non_overflow.i411, label %checked_add_uint8.exit + +for_join65: ; preds = %if_join84, %checked_add_uint8.exit + %var_cnt.3.lcssa541 = phi i256 [ %var_cnt.2, %if_join84 ], [ %var_cnt.3.ph, %checked_add_uint8.exit ] + %checked_mul_uint8_call = tail call fastcc i256 @checked_mul_uint8(i256 %var_j.0) + br label %for_condition22 + +shift_left_non_overflow.i411: ; preds = %if_join309, %for_condition62 + unreachable + +checked_add_uint8.exit: ; preds = %for_condition62 + %addition_result.i = add nuw nsw i256 %and_result.i410, 2 + %and_result78 = and i256 %addition_result.i, 7 + %comparison_result80 = icmp eq i256 %and_result78, 0 + br i1 %comparison_result80, label %for_join65, label %if_join84 + +if_join84: ; preds = %checked_add_uint8.exit + %trunc2 = trunc i256 %and_result78 to i8 + switch i8 %trunc2, label %if_join110 [ + i8 12, label %for_condition62 + i8 23, label %for_join65 + ] + +if_join110: ; preds = %increment_uint8.exit, %if_join84 + %var_h.0 = phi i256 [ %addition_result.i414, %increment_uint8.exit ], [ 1, %if_join84 ] + %var_cnt.5 = phi i256 [ %var_cnt.6.lcssa, %increment_uint8.exit ], [ %var_cnt.3.ph, %if_join84 ] + br i1 %comparison_result113, label %return, label %if_join117 + +if_join117: ; preds = %if_join110 + %comparison_result119 = icmp ugt i256 %var_h.0, 2 + %or.cond401 = or i1 %comparison_result126, %comparison_result119 + br i1 %or.cond401, label %for_condition62.outer, label %for_condition131 + +for_condition131: ; preds = %for_join159, %if_join117 + %var_k.0 = phi i256 [ %addition_result336, %for_join159 ], [ 10, %if_join117 ] + %var_cnt.6 = phi i256 [ %var_cnt.7.ph645, %for_join159 ], [ %var_cnt.5, %if_join117 ] + %comparison_result137 = icmp ult i256 %var_k.0, 12 + br i1 %comparison_result137, label %for_body132, label %increment_uint8.exit + +for_body132: ; preds = %for_condition131 + %trunc3 = trunc i256 %var_cnt.6 to i8 + switch i8 %trunc3, label %if_join167.outer [ + i8 5, label %return + i8 25, label %increment_uint8.exit + ] + +increment_uint8.exit: ; preds = %for_body132, %for_condition131 + %var_cnt.6.lcssa = phi i256 [ %var_cnt.6, %for_condition131 ], [ %var_cnt.5, %for_body132 ] + %addition_result.i414 = add nuw nsw i256 %var_h.0, 1 + br label %if_join110 + +for_join159: ; preds = %if_join184, %if_join167 + %addition_result336 = add nuw nsw i256 %var_k.0, 1 + br label %for_condition131 + +if_join167.outer: ; preds = %if_join167.outer.backedge, %for_body132 + %var_x.0.ph = phi i256 [ %addition_result, %if_join167.outer.backedge ], [ 7, %for_body132 ] + %var_cnt.7.ph645 = phi i256 [ %var_cnt.7.ph645.be, %if_join167.outer.backedge ], [ %var_cnt.6, %for_body132 ] + br label %if_join167 + +if_join167: ; preds = %if_join175, %if_join167.outer + %var_x.0 = phi i256 [ %addition_result, %if_join175 ], [ %var_x.0.ph, %if_join167.outer ] + %and_result161 = and i256 %var_x.0, 255 + %addition_result = add nsw i256 %and_result161, -1 + %comparison_result171 = icmp eq i256 %addition_result, 0 + br i1 %comparison_result171, label %for_join159, label %if_join175 + +if_join175: ; preds = %if_join167 + %and_result177 = and i256 %addition_result, 1 + %comparison_result180 = icmp eq i256 %and_result177, 0 + br i1 %comparison_result180, label %if_join167, label %if_join184 + +if_join184: ; preds = %if_join175 + br i1 %comparison_result187, label %for_join159, label %for_condition192.outer + +for_condition192.outer: ; preds = %for_condition271, %if_join184 + %var_y.0.ph = phi i256 [ 10, %if_join184 ], [ %addition_result.i427, %for_condition271 ] + %var_cnt.9.ph = phi i256 [ %var_cnt.7.ph645, %if_join184 ], [ %var_cnt.11, %for_condition271 ] + br label %for_condition192 + +for_condition192: ; preds = %for_condition192.backedge, %for_condition192.outer + %var_y.0 = phi i256 [ %var_y.0.ph, %for_condition192.outer ], [ %addition_result.i427, %for_condition192.backedge ] + %comparison_result198 = icmp ult i256 %var_y.0, 17 + br i1 %comparison_result198, label %for_body193, label %for_join195 + +for_body193: ; preds = %for_condition192 + switch i8 %trunc, label %checked_add_t_uint8.exit [ + i8 7, label %return + i8 27, label %if_join167.outer.backedge + ] + +for_join195: ; preds = %if_join228, %for_condition192 + br i1 %comparison_result330, label %return, label %if_join167.outer.backedge + +if_join167.outer.backedge: ; preds = %if_join252, %for_join195, %for_body193 + %var_cnt.7.ph645.be = phi i256 [ %var_cnt.9.ph, %for_join195 ], [ %var_cnt.7.ph645, %for_body193 ], [ %var_cnt.9.ph, %if_join252 ] + br label %if_join167.outer + +checked_add_t_uint8.exit: ; preds = %for_body193 + %addition_result.i427 = add nuw nsw i256 %var_y.0, 1 + %remainder_result_non_zero.lhs.trunc = trunc nuw i256 %addition_result.i427 to i8 + %remainder_result_non_zero429 = urem i8 %remainder_result_non_zero.lhs.trunc, 3 + %comparison_result224 = icmp eq i8 %remainder_result_non_zero429, 0 + br i1 %comparison_result224, label %for_condition192.backedge, label %if_join228 + +if_join228: ; preds = %checked_add_t_uint8.exit + %comparison_result230 = icmp eq i256 %addition_result.i427, 16 + br i1 %comparison_result230, label %for_join195, label %if_join234 + +if_join234: ; preds = %if_join228 + %comparison_result247 = icmp ugt i256 %var_y.0, 10 + %spec.select402 = and i1 %comparison_result237, %comparison_result247 + br i1 %spec.select402, label %for_condition192.backedge, label %if_join252 + +for_condition192.backedge: ; preds = %if_join234, %checked_add_t_uint8.exit + br label %for_condition192 + +if_join252: ; preds = %if_join234 + %comparison_result265 = icmp ugt i256 %var_y.0, 12 + %spec.select403 = and i1 %comparison_result255, %comparison_result265 + br i1 %spec.select403, label %if_join167.outer.backedge, label %for_condition271 + +for_condition271: ; preds = %for_increment273, %if_join252 + %var_l.0 = phi i256 [ %addition_result326, %for_increment273 ], [ 0, %if_join252 ] + %var_cnt.11 = phi i256 [ %var_cnt.12, %for_increment273 ], [ %var_cnt.9.ph, %if_join252 ] + %comparison_result277 = icmp ugt i256 %var_l.0, 3 + %or.cond404 = or i1 %comparison_result282, %comparison_result277 + br i1 %or.cond404, label %for_condition192.outer, label %if_join286 + +for_increment273: ; preds = %if_join318, %if_join295, %if_join286 + %var_cnt.12 = phi i256 [ %var_cnt.11, %if_join286 ], [ %addition_result312, %if_join318 ], [ %var_cnt.11, %if_join295 ] + %addition_result326 = add nuw nsw i256 %var_l.0, 1 + br label %for_condition271 + +if_join286: ; preds = %for_condition271 + %and_result288 = and i256 %var_l.0, 1 + %comparison_result291 = icmp eq i256 %and_result288, 0 + br i1 %comparison_result291, label %for_increment273, label %if_join295 + +if_join295: ; preds = %if_join286 + switch i8 %trunc, label %if_join309 [ + i8 8, label %return + i8 13, label %for_increment273 + ] + +if_join309: ; preds = %if_join295 + %and_result311 = and i256 %var_cnt.11, 18446744073709551615 + %comparison_result314 = icmp eq i256 %and_result311, 18446744073709551615 + br i1 %comparison_result314, label %shift_left_non_overflow.i411, label %if_join318 + +if_join318: ; preds = %if_join309 + %addition_result312 = add nuw nsw i256 %and_result311, 1 + br label %for_increment273 +} diff --git a/llvm/test/CodeGen/EVM/stack-too-deep-3.ll b/llvm/test/CodeGen/EVM/stack-too-deep-3.ll new file mode 100644 index 000000000000..52489c51fb8b --- /dev/null +++ b/llvm/test/CodeGen/EVM/stack-too-deep-3.ll @@ -0,0 +1,92 @@ +; REQUIRES: asserts +; RUN: llc -evm-stack-region-offset=128 -evm-stack-region-size=32 --debug-only=evm-stack-solver < %s 2>&1 | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare void @llvm.memmove.p1.p1.i256(ptr addrspace(1) nocapture writeonly, ptr addrspace(1) nocapture readonly, i256, i1 immarg) #0 + +; CHECK: warning: allocated stack region size: 32 is larger than the total stack size: 0 + +define dso_local fastcc void @main() unnamed_addr { +entry: + br i1 poison, label %"block_rt_7/0", label %"block_rt_2/0" + +"block_rt_2/0": ; preds = %conditional_rt_187_join_block, %conditional_rt_181_join_block, %"block_rt_158/3", %entry + unreachable + +"block_rt_7/0": ; preds = %entry + %calldata_load_result2781 = load i256, ptr addrspace(2) inttoptr (i256 4 to ptr addrspace(2)), align 4 + %addition_result2798 = add nuw nsw i256 %calldata_load_result2781, 4 + %calldataload_pointer2604 = inttoptr i256 %addition_result2798 to ptr addrspace(2) + %calldata_load_result2605 = load i256, ptr addrspace(2) %calldataload_pointer2604, align 1 + %addition_result2659 = add nuw nsw i256 %calldata_load_result2781, 36 + %addition_result1186 = add nsw i256 0, -99 + %subtraction_result1906 = add nsw i256 0, -31 + br label %conditional_rt_187_join_block + +"block_rt_158/3": ; preds = %conditional_rt_181_join_block + %addition_result3239 = add i256 0, %addition_result3756 + %calldataload_pointer3244 = inttoptr i256 %addition_result3239 to ptr addrspace(2) + %calldata_load_result3245 = load i256, ptr addrspace(2) %calldataload_pointer3244, align 1 + %addition_result3251 = add i256 %addition_result3239, 32 + %shift_left_non_overflow_result3390 = shl nuw nsw i256 %calldata_load_result3245, 5 + %subtraction_result3395 = sub i256 0, %shift_left_non_overflow_result3390 + %comparison_result3399.not = icmp sgt i256 %addition_result3251, %subtraction_result3395 + br i1 %comparison_result3399.not, label %"block_rt_2/0", label %"block_rt_160/3" + +"block_rt_160/3": ; preds = %"block_rt_158/3" + %memory_store_pointer3530 = inttoptr i256 %stack_var_021.06950 to ptr addrspace(1) + %addition_result3535 = add i256 %stack_var_021.0.in6947, 96 + %memory_store_pointer3538 = inttoptr i256 %addition_result3535 to ptr addrspace(1) + store i256 %calldata_load_result3245, ptr addrspace(1) %memory_store_pointer3538, align 1 + %addition_result3694 = add i256 %shift_left_non_overflow_result3390, %stack_var_021.06950 + %addition_result3972 = add i256 %stack_var_022.06948, 32 + %addition_result3978 = add i256 %stack_var_017.26946, 32 + %stack_var_021.0 = add i256 %addition_result3694, 64 + br i1 %comparison_result3909.not, label %conditional_rt_181_join_block, label %"block_rt_181/0" + +"block_rt_181/0": ; preds = %"block_rt_160/3" + %addition_result4058 = add i256 %stack_var_012.36954, 32 + %addition_result4064 = add i256 %stack_var_011.36953, 32 + %addition_result4044 = add nuw i256 %stack_var_013.36955, 1 + %comparison_result4003.not = icmp ult i256 %addition_result4044, %calldata_load_result2605 + br i1 %comparison_result4003.not, label %conditional_rt_187_join_block, label %"block_rt_187/0.loopexit" + +"block_rt_187/0.loopexit": ; preds = %"block_rt_181/0" + %addition_result451 = add i256 %stack_var_021.0, 64 + %mcopy_destination455 = inttoptr i256 %addition_result451 to ptr addrspace(1) + tail call void @llvm.memmove.p1.p1.i256(ptr addrspace(1) align 1 %mcopy_destination455, ptr addrspace(1) align 1 poison, i256 poison, i1 false) + unreachable + +"block_rt_188/0": ; preds = %conditional_rt_187_join_block + %addition_result4054 = add i256 0, %addition_result2659 + br label %conditional_rt_181_join_block + +conditional_rt_181_join_block: ; preds = %"block_rt_188/0", %"block_rt_160/3" + %stack_var_021.06950 = phi i256 [ poison, %"block_rt_188/0" ], [ %stack_var_021.0, %"block_rt_160/3" ] + %comparison_result3909.not = phi i1 [ true, %"block_rt_188/0" ], [ false, %"block_rt_160/3" ] + %stack_var_022.06948 = phi i256 [ %addition_result4054, %"block_rt_188/0" ], [ %addition_result3972, %"block_rt_160/3" ] + %stack_var_021.0.in6947 = phi i256 [ %stack_var_010.26952, %"block_rt_188/0" ], [ %addition_result3694, %"block_rt_160/3" ] + %stack_var_017.26946 = phi i256 [ %stack_var_010.26952, %"block_rt_188/0" ], [ %addition_result3978, %"block_rt_160/3" ] + %subtraction_result3918 = sub i256 %stack_var_021.06950, %stack_var_010.26952 + %memory_store_pointer3922 = inttoptr i256 %stack_var_017.26946 to ptr addrspace(1) + store i256 %subtraction_result3918, ptr addrspace(1) %memory_store_pointer3922, align 1 + %calldataload_pointer1898 = inttoptr i256 %stack_var_022.06948 to ptr addrspace(2) + %addition_result3756 = add i256 0, %addition_result4054 + %addition_result1811 = sub i256 %subtraction_result1906, %addition_result3756 + %comparison_result1815.not = icmp slt i256 0, %addition_result1811 + br i1 %comparison_result1815.not, label %"block_rt_158/3", label %"block_rt_2/0" + +conditional_rt_187_join_block: ; preds = %"block_rt_181/0", %"block_rt_7/0" + %stack_var_013.36955 = phi i256 [ 0, %"block_rt_7/0" ], [ %addition_result4044, %"block_rt_181/0" ] + %stack_var_012.36954 = phi i256 [ %addition_result2659, %"block_rt_7/0" ], [ %addition_result4058, %"block_rt_181/0" ] + %stack_var_011.36953 = phi i256 [ 224, %"block_rt_7/0" ], [ %addition_result4064, %"block_rt_181/0" ] + %stack_var_010.26952 = phi i256 [ poison, %"block_rt_7/0" ], [ %stack_var_021.0, %"block_rt_181/0" ] + %memory_store_pointer4021 = inttoptr i256 %stack_var_011.36953 to ptr addrspace(1) + %calldataload_pointer4024 = inttoptr i256 %stack_var_012.36954 to ptr addrspace(2) + %comparison_result4030.not = icmp slt i256 0, %addition_result1186 + br i1 %comparison_result4030.not, label %"block_rt_188/0", label %"block_rt_2/0" +} + +attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } diff --git a/llvm/test/CodeGen/EVM/stdlib-call-ellision.ll b/llvm/test/CodeGen/EVM/stdlib-call-ellision.ll new file mode 100644 index 000000000000..6bdc9aa65cf8 --- /dev/null +++ b/llvm/test/CodeGen/EVM/stdlib-call-ellision.ll @@ -0,0 +1,102 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: opt -O2 -S < %s | FileCheck %s + +; UNSUPPORTED: evm + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @test__addmod(i256 %arg1, i256 %arg2, i256 %modulo) { +; CHECK: @llvm.evm.addmod + %res = call i256 @__addmod(i256 %arg1, i256 %arg2, i256 %modulo) + ret i256 %res +} + +define i256 @test__mulmod(i256 %arg1, i256 %arg2, i256 %modulo) { +; CHECK: @llvm.evm.mulmod + %res = call i256 @__mulmod(i256 %arg1, i256 %arg2, i256 %modulo) + ret i256 %res +} + +define i256 @test__signextend(i256 %bytesize, i256 %val) { +; CHECK: @llvm.evm.signextend + %res = call i256 @__signextend(i256 %bytesize, i256 %val) + ret i256 %res +} + +define i256 @test__exp(i256 %base, i256 %exp) { +; CHECK: @llvm.evm.exp + %res = call i256 @__exp(i256 %base, i256 %exp) + ret i256 %res +} + +define i256 @test__byte(i256 %index, i256 %val) { +; CHECK: @llvm.evm.byte + %res = call i256 @__byte(i256 %index, i256 %val) + ret i256 %res +} + +define i256 @test__sdiv(i256 %dividend, i256 %divisor) { +; CHECK: @llvm.evm.sdiv + %res = call i256 @__sdiv(i256 %dividend, i256 %divisor) + ret i256 %res +} + +define i256 @test__div(i256 %dividend, i256 %divisor) { +; CHECK: @llvm.evm.div + %res = call i256 @__div(i256 %dividend, i256 %divisor) + ret i256 %res +} + +define i256 @test__smod(i256 %val, i256 %mod) { +; CHECK: @llvm.evm.smod + %res = call i256 @__smod(i256 %val, i256 %mod) + ret i256 %res +} + +define i256 @test__mod(i256 %val, i256 %mod) { +; CHECK: @llvm.evm.mod + %res = call i256 @__mod(i256 %val, i256 %mod) + ret i256 %res +} + +define i256 @test__shl(i256 %shift, i256 %val) { +; CHECK: @llvm.evm.shl + %res = call i256 @__shl(i256 %shift, i256 %val) + ret i256 %res +} + +define i256 @test__shr(i256 %shift, i256 %val) { +; CHECK: @llvm.evm.shr + %res = call i256 @__shr(i256 %shift, i256 %val) + ret i256 %res +} + +define i256 @test__sar(i256 %shift, i256 %val) { +; CHECK: @llvm.evm.sar + %res = call i256 @__sar(i256 %shift, i256 %val) + ret i256 %res +} + +define i256 @test__sha3(ptr addrspace(1) %offset, i256 %len) { +; CHECK: @llvm.evm.sha3 + %res = call i256 @__sha3(ptr addrspace(1) %offset, i256 %len, i1 undef) + ret i256 %res +} + +declare i256 @__addmod(i256, i256, i256) #0 +declare i256 @__mulmod(i256, i256, i256) #0 +declare i256 @__signextend(i256, i256) #0 +declare i256 @__exp(i256, i256) #0 +declare i256 @__byte(i256, i256) #0 +declare i256 @__sdiv(i256, i256) #0 +declare i256 @__div(i256, i256) #0 +declare i256 @__smod(i256, i256) #0 +declare i256 @__mod(i256, i256) #0 +declare i256 @__shl(i256, i256) #0 +declare i256 @__shr(i256, i256) #0 +declare i256 @__sar(i256, i256) #0 +declare i256 @__sha3(ptr addrspace(1), i256, i1) #1 + +attributes #0 = { alwaysinline mustprogress nofree norecurse nosync nounwind readnone willreturn } +attributes #1 = { alwaysinline argmemonly readonly nofree null_pointer_is_valid } diff --git a/llvm/test/CodeGen/EVM/storage.ll b/llvm/test/CodeGen/EVM/storage.ll new file mode 100644 index 000000000000..95755edca9e3 --- /dev/null +++ b/llvm/test/CodeGen/EVM/storage.ll @@ -0,0 +1,28 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define void @sstore(ptr addrspace(5) %key, i256 %val) nounwind { +; CHECK-LABEL: sstore: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SSTORE +; CHECK-NEXT: JUMP + + store i256 %val, ptr addrspace(5) %key, align 32 + ret void +} + +define i256 @sload(ptr addrspace(5) %key) nounwind { +; CHECK-LABEL: sload: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SLOAD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %val = load i256, ptr addrspace(5) %key, align 32 + ret i256 %val +} diff --git a/llvm/test/CodeGen/EVM/sub.ll b/llvm/test/CodeGen/EVM/sub.ll new file mode 100644 index 000000000000..9d4be1cd3b92 --- /dev/null +++ b/llvm/test/CodeGen/EVM/sub.ll @@ -0,0 +1,31 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @subrrr(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: subrrr: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = sub i256 %rs1, %rs2 + ret i256 %res +} + +define i256 @addrri(i256 %rs1) nounwind { +; CHECK-LABEL: addrri: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x3 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = add i256 %rs1, -3 + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/sue-add-remat-bug.ll b/llvm/test/CodeGen/EVM/sue-add-remat-bug.ll new file mode 100644 index 000000000000..9d9f7d09f226 --- /dev/null +++ b/llvm/test/CodeGen/EVM/sue-add-remat-bug.ll @@ -0,0 +1,39 @@ +; RUN: llc -O3 < %s + +; This test case is reduced with llvm-reduce. +; Before the fix, we were hitting an assert during +; stackification in EVMStackModel::getStackSlot. This +; was caused by not updating the live interval of ADD +; operand during rematerialization in EVMSingleUseExpression pass. + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +; Function Attrs: nounwind willreturn memory(argmem: read) +declare i256 @llvm.evm.sha3(ptr addrspace(1), i256) #0 + +; Function Attrs: nounwind willreturn +declare i256 @llvm.evm.call(i256, i256, i256, ptr addrspace(1), i256, ptr addrspace(1), i256) #1 + +; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) +declare void @llvm.memmove.p1.p1.i256(ptr addrspace(1) nocapture writeonly, ptr addrspace(1) nocapture readonly, i256, i1 immarg) #2 + +define void @__entry() { +entry: + %abi_decode_bytest_bytest_bytest_bytes_call406 = call fastcc { i256, i256, i256, i256 } @abi_decode_bytest_bytest_bytest_bytes() + %abi_decode_bytest_bytest_bytest_bytes_call406.fca.0.extract = extractvalue { i256, i256, i256, i256 } %abi_decode_bytest_bytest_bytest_bytes_call406, 0 + %abi_decode_bytest_bytest_bytest_bytes_call406.fca.2.extract = extractvalue { i256, i256, i256, i256 } %abi_decode_bytest_bytest_bytest_bytes_call406, 2 + %addition_result419 = add i256 %abi_decode_bytest_bytest_bytest_bytes_call406.fca.2.extract, 1 + %keccak256_input_offset_pointer = inttoptr i256 %addition_result419 to ptr addrspace(1) + %keccak256 = tail call i256 @llvm.evm.sha3(ptr addrspace(1) %keccak256_input_offset_pointer, i256 0) + tail call void @llvm.memmove.p1.p1.i256(ptr addrspace(1) null, ptr addrspace(1) %keccak256_input_offset_pointer, i256 0, i1 false) + %call_input_offset_pointer829 = inttoptr i256 %abi_decode_bytest_bytest_bytest_bytes_call406.fca.0.extract to ptr addrspace(1) + %call831 = tail call i256 @llvm.evm.call(i256 0, i256 0, i256 0, ptr addrspace(1) %call_input_offset_pointer829, i256 0, ptr addrspace(1) null, i256 0) + ret void +} + +declare fastcc { i256, i256, i256, i256 } @abi_decode_bytest_bytest_bytest_bytes() + +attributes #0 = { nounwind willreturn memory(argmem: read) } +attributes #1 = { nounwind willreturn } +attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } diff --git a/llvm/test/CodeGen/EVM/truncstore.ll b/llvm/test/CodeGen/EVM/truncstore.ll new file mode 100644 index 000000000000..ffe38b654569 --- /dev/null +++ b/llvm/test/CodeGen/EVM/truncstore.ll @@ -0,0 +1,39 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define void @storei8(i8 %val, ptr addrspace(1) %glob) nounwind { +; CHECK-LABEL: storei8: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: MSTORE8 +; CHECK-NEXT: JUMP + + store i8 %val, ptr addrspace(1) %glob + ret void +} + +define void @storei32(i32 %val, ptr addrspace(1) %glob) nounwind { +; CHECK-LABEL: storei32: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP2 +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH1 0x20 +; CHECK-NEXT: SHR +; CHECK-NEXT: PUSH1 0x20 +; CHECK-NEXT: SHL +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 0xE0 +; CHECK-NEXT: SHL +; CHECK-NEXT: OR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: MSTORE +; CHECK-NEXT: JUMP + + store i32 %val, ptr addrspace(1) %glob + ret void +} diff --git a/llvm/test/CodeGen/EVM/tstorage.ll b/llvm/test/CodeGen/EVM/tstorage.ll new file mode 100644 index 000000000000..d7ab2b3ce9ac --- /dev/null +++ b/llvm/test/CodeGen/EVM/tstorage.ll @@ -0,0 +1,28 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define void @tstore(ptr addrspace(6) %key, i256 %val) nounwind { +; CHECK-LABEL: tstore: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: TSTORE +; CHECK-NEXT: JUMP + + store i256 %val, ptr addrspace(6) %key, align 32 + ret void +} + +define i256 @tload(ptr addrspace(6) %key) nounwind { +; CHECK-LABEL: tload: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: TLOAD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %val = load i256, ptr addrspace(6) %key, align 32 + ret i256 %val +} diff --git a/llvm/test/CodeGen/EVM/unused_function_arguments.ll b/llvm/test/CodeGen/EVM/unused_function_arguments.ll new file mode 100644 index 000000000000..a28d1bc37496 --- /dev/null +++ b/llvm/test/CodeGen/EVM/unused_function_arguments.ll @@ -0,0 +1,56 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @foo(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: foo: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: POP +; CHECK-NEXT: DUP1 +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %x1 = add i256 %a1, %a1 + ret i256 %x1 +} + +define i256 @wat(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: wat: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %x1 = add i256 %a2, %a2 + ret i256 %x1 +} + +define i256 @bar() nounwind { +; CHECK-LABEL: bar: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH4 @.FUNC_RET0 +; CHECK-NEXT: PUSH1 0x3 +; CHECK-NEXT: PUSH1 0x2 +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: PUSH4 @foo +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @foo(i256 1, i256 2, i256 3) + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/zero_any_extload.ll b/llvm/test/CodeGen/EVM/zero_any_extload.ll new file mode 100644 index 000000000000..de8f3edc5b81 --- /dev/null +++ b/llvm/test/CodeGen/EVM/zero_any_extload.ll @@ -0,0 +1,150 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i8 @load_anyext_i8(ptr addrspace(1) %ptr) nounwind { +; CHECK-LABEL: load_anyext_i8: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: BYTE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %load = load i8, ptr addrspace(1) %ptr + ret i8 %load +} + +define i16 @load_anyext_i16(ptr addrspace(1) %ptr) nounwind { +; CHECK-LABEL: load_anyext_i16: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH1 0xF0 +; CHECK-NEXT: SHR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %load = load i16, ptr addrspace(1) %ptr + ret i16 %load +} + +define i32 @load_anyext_i32(ptr addrspace(1) %ptr) nounwind { +; CHECK-LABEL: load_anyext_i32: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH1 0xE0 +; CHECK-NEXT: SHR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %load = load i32, ptr addrspace(1) %ptr + ret i32 %load +} + +define i64 @load_anyext_i64(ptr addrspace(1) %ptr) nounwind { +; CHECK-LABEL: load_anyext_i64: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH1 0xC0 +; CHECK-NEXT: SHR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %load = load i64, ptr addrspace(1) %ptr + ret i64 %load +} + +define i128 @load_anyext_i128(ptr addrspace(1) %ptr) nounwind { +; CHECK-LABEL: load_anyext_i128: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH1 0x80 +; CHECK-NEXT: SHR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %load = load i128, ptr addrspace(1) %ptr + ret i128 %load +} + +define i256 @load_zeroext_i8(ptr addrspace(1) %ptr) nounwind { +; CHECK-LABEL: load_zeroext_i8: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: BYTE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %load = load i8, ptr addrspace(1) %ptr + %zext = zext i8 %load to i256 + ret i256 %zext +} + +define i256 @load_zeroext_i16(ptr addrspace(1) %ptr) nounwind { +; CHECK-LABEL: load_zeroext_i16: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH1 0xF0 +; CHECK-NEXT: SHR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %load = load i16, ptr addrspace(1) %ptr + %zext = zext i16 %load to i256 + ret i256 %zext +} + +define i256 @load_zeroext_i32(ptr addrspace(1) %ptr) nounwind { +; CHECK-LABEL: load_zeroext_i32: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH1 0xE0 +; CHECK-NEXT: SHR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %load = load i32, ptr addrspace(1) %ptr + %zext = zext i32 %load to i256 + ret i256 %zext +} + +define i256 @load_zeroext_i64(ptr addrspace(1) %ptr) nounwind { +; CHECK-LABEL: load_zeroext_i64: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH1 0xC0 +; CHECK-NEXT: SHR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %load = load i64, ptr addrspace(1) %ptr + %zext = zext i64 %load to i256 + ret i256 %zext +} + +define i256 @load_zeroext_i128(ptr addrspace(1) %ptr) nounwind { +; CHECK-LABEL: load_zeroext_i128: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH1 0x80 +; CHECK-NEXT: SHR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %load = load i128, ptr addrspace(1) %ptr + %zext = zext i128 %load to i256 + ret i256 %zext +} diff --git a/llvm/test/CodeGen/EVM/zext.ll b/llvm/test/CodeGen/EVM/zext.ll new file mode 100644 index 000000000000..11bf90173d33 --- /dev/null +++ b/llvm/test/CodeGen/EVM/zext.ll @@ -0,0 +1,70 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @zexti8(i8 %rs1) nounwind { +; CHECK-LABEL: zexti8: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = zext i8 %rs1 to i256 + ret i256 %res +} + +define i256 @zexti16(i16 %rs1) nounwind { +; CHECK-LABEL: zexti16: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH2 0xFFFF +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = zext i16 %rs1 to i256 + ret i256 %res +} + +define i256 @zexti32(i32 %rs1) nounwind { +; CHECK-LABEL: zexti32: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH4 0xFFFFFFFF +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = zext i32 %rs1 to i256 + ret i256 %res +} + +define i256 @zexti64(i64 %rs1) nounwind { +; CHECK-LABEL: zexti64: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH8 0xFFFFFFFFFFFFFFFF +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = zext i64 %rs1 to i256 + ret i256 %res +} + +define i256 @zexti128(i128 %rs1) nounwind { +; CHECK-LABEL: zexti128: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH16 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = zext i128 %rs1 to i256 + ret i256 %res +} diff --git a/llvm/test/CodeGen/Generic/2002-04-16-StackFrameSizeAlignment.ll b/llvm/test/CodeGen/Generic/2002-04-16-StackFrameSizeAlignment.ll index 3b4eef89a8d4..50a8b8eeab21 100644 --- a/llvm/test/CodeGen/Generic/2002-04-16-StackFrameSizeAlignment.ll +++ b/llvm/test/CodeGen/Generic/2002-04-16-StackFrameSizeAlignment.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} ; Compiling this file produces: ; Sparc.cpp:91: failed assertion `(offset - OFFSET) % getStackFrameSizeAlignment() == 0' diff --git a/llvm/test/CodeGen/Generic/2003-05-27-phifcmpd.ll b/llvm/test/CodeGen/Generic/2003-05-27-phifcmpd.ll index 6fb17991e739..9fd10b2ec46f 100644 --- a/llvm/test/CodeGen/Generic/2003-05-27-phifcmpd.ll +++ b/llvm/test/CodeGen/Generic/2003-05-27-phifcmpd.ll @@ -1,5 +1,8 @@ ; RUN: llc < %s +; EVM doesn't support floats. +; UNSUPPORTED: target=evm{{.*}} + define void @QRiterate(i32 %p.1, double %tmp.212) { entry: %tmp.184 = icmp sgt i32 %p.1, 0 ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/2003-05-27-useboolinotherbb.ll b/llvm/test/CodeGen/Generic/2003-05-27-useboolinotherbb.ll index 14bb00048d20..21069c3391e3 100644 --- a/llvm/test/CodeGen/Generic/2003-05-27-useboolinotherbb.ll +++ b/llvm/test/CodeGen/Generic/2003-05-27-useboolinotherbb.ll @@ -1,3 +1,5 @@ +; UNSUPPORTED: target=evm{{.*}} +; EVM doesn't support floats. ; RUN: llc < %s define void @QRiterate(double %tmp.212) { diff --git a/llvm/test/CodeGen/Generic/2003-05-27-usefsubasbool.ll b/llvm/test/CodeGen/Generic/2003-05-27-usefsubasbool.ll index cc0eb5cd1374..1c075f2f0105 100644 --- a/llvm/test/CodeGen/Generic/2003-05-27-usefsubasbool.ll +++ b/llvm/test/CodeGen/Generic/2003-05-27-usefsubasbool.ll @@ -1,3 +1,5 @@ +; EVM doesn't support floats. +; UNSUPPORTED: target=evm{{.*}} ; RUN: llc < %s define void @QRiterate(double %tmp.212) { diff --git a/llvm/test/CodeGen/Generic/2003-05-28-ManyArgs.ll b/llvm/test/CodeGen/Generic/2003-05-28-ManyArgs.ll index 3f64fca908bd..a82ece62ba6c 100644 --- a/llvm/test/CodeGen/Generic/2003-05-28-ManyArgs.ll +++ b/llvm/test/CodeGen/Generic/2003-05-28-ManyArgs.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} ;; Date: May 28, 2003. ;; From: test/Programs/External/SPEC/CINT2000/175.vpr.llvm.bc diff --git a/llvm/test/CodeGen/Generic/2003-05-30-BadFoldGEP.ll b/llvm/test/CodeGen/Generic/2003-05-30-BadFoldGEP.ll index db578830a3ba..fe93d3c78087 100644 --- a/llvm/test/CodeGen/Generic/2003-05-30-BadFoldGEP.ll +++ b/llvm/test/CodeGen/Generic/2003-05-30-BadFoldGEP.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} ;; Date: May 28, 2003. ;; From: test/Programs/External/SPEC/CINT2000/254.gap.llvm.bc diff --git a/llvm/test/CodeGen/Generic/2003-05-30-BadPreselectPhi.ll b/llvm/test/CodeGen/Generic/2003-05-30-BadPreselectPhi.ll index 9dcfde5063c0..4fb5223bc9c3 100644 --- a/llvm/test/CodeGen/Generic/2003-05-30-BadPreselectPhi.ll +++ b/llvm/test/CodeGen/Generic/2003-05-30-BadPreselectPhi.ll @@ -1,4 +1,7 @@ +; XFAIL: target=evm{{.*}} +; TODO: CPR-921 Needs proger GA wrapping to be implemented. ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} ;; Date: May 28, 2003. ;; From: test/Programs/SingleSource/richards_benchmark.c diff --git a/llvm/test/CodeGen/Generic/2003-07-06-BadIntCmp.ll b/llvm/test/CodeGen/Generic/2003-07-06-BadIntCmp.ll index 010097a03650..5ee532d5de19 100644 --- a/llvm/test/CodeGen/Generic/2003-07-06-BadIntCmp.ll +++ b/llvm/test/CodeGen/Generic/2003-07-06-BadIntCmp.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} ;; Date: May 28, 2003. ;; From: test/Programs/MultiSource/Olden-perimeter/maketree.c diff --git a/llvm/test/CodeGen/Generic/2003-07-07-BadLongConst.ll b/llvm/test/CodeGen/Generic/2003-07-07-BadLongConst.ll index fe0b43d26fe4..ba15eb48d97f 100644 --- a/llvm/test/CodeGen/Generic/2003-07-07-BadLongConst.ll +++ b/llvm/test/CodeGen/Generic/2003-07-07-BadLongConst.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} @.str_1 = internal constant [42 x i8] c" ui = %u (0x%x)\09\09UL-ui = %lld (0x%llx)\0A\00" ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/2003-07-08-BadCastToBool.ll b/llvm/test/CodeGen/Generic/2003-07-08-BadCastToBool.ll index b76384551b22..5ca6cd6805f5 100644 --- a/llvm/test/CodeGen/Generic/2003-07-08-BadCastToBool.ll +++ b/llvm/test/CodeGen/Generic/2003-07-08-BadCastToBool.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} ;; Date: Jul 8, 2003. ;; From: test/Programs/MultiSource/Olden-perimeter diff --git a/llvm/test/CodeGen/Generic/2003-07-29-BadConstSbyte.ll b/llvm/test/CodeGen/Generic/2003-07-29-BadConstSbyte.ll index 1f7f9891e2de..09cf50708878 100644 --- a/llvm/test/CodeGen/Generic/2003-07-29-BadConstSbyte.ll +++ b/llvm/test/CodeGen/Generic/2003-07-29-BadConstSbyte.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} ; Bug: PR31341 diff --git a/llvm/test/CodeGen/Generic/2004-05-09-LiveVarPartialRegister.ll b/llvm/test/CodeGen/Generic/2004-05-09-LiveVarPartialRegister.ll index fbffaa257ed0..df59a0ace848 100644 --- a/llvm/test/CodeGen/Generic/2004-05-09-LiveVarPartialRegister.ll +++ b/llvm/test/CodeGen/Generic/2004-05-09-LiveVarPartialRegister.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} + @global_long_1 = linkonce global i64 7 ; [#uses=1] @global_long_2 = linkonce global i64 49 ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/2005-04-09-GlobalInPHI.ll b/llvm/test/CodeGen/Generic/2005-04-09-GlobalInPHI.ll index 33599fa3954a..6ef75a8c3580 100644 --- a/llvm/test/CodeGen/Generic/2005-04-09-GlobalInPHI.ll +++ b/llvm/test/CodeGen/Generic/2005-04-09-GlobalInPHI.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} + %struct.TypHeader = type { i32, ptr, [3 x i8], i8 } @.str_67 = external global [4 x i8] ; [#uses=1] @.str_87 = external global [17 x i8] ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/2005-10-18-ZeroSizeStackObject.ll b/llvm/test/CodeGen/Generic/2005-10-18-ZeroSizeStackObject.ll index 942592fc7214..33f8a37f3dac 100644 --- a/llvm/test/CodeGen/Generic/2005-10-18-ZeroSizeStackObject.ll +++ b/llvm/test/CodeGen/Generic/2005-10-18-ZeroSizeStackObject.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} define void @test() { %X = alloca { } ; [#uses=0] diff --git a/llvm/test/CodeGen/Generic/2005-12-01-Crash.ll b/llvm/test/CodeGen/Generic/2005-12-01-Crash.ll index cd9e6800ab5b..41a146c14a12 100644 --- a/llvm/test/CodeGen/Generic/2005-12-01-Crash.ll +++ b/llvm/test/CodeGen/Generic/2005-12-01-Crash.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} + @str = external global [36 x i8] ; [#uses=0] @str.upgrd.1 = external global [29 x i8] ; [#uses=0] @str1 = external global [29 x i8] ; [#uses=0] diff --git a/llvm/test/CodeGen/Generic/2006-02-12-InsertLibcall.ll b/llvm/test/CodeGen/Generic/2006-02-12-InsertLibcall.ll index 6812d4d4b4eb..53135f7b40ca 100644 --- a/llvm/test/CodeGen/Generic/2006-02-12-InsertLibcall.ll +++ b/llvm/test/CodeGen/Generic/2006-02-12-InsertLibcall.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} + @G = external global i32 ; [#uses=1] define void @encode_one_frame(i64 %tmp.2i) { diff --git a/llvm/test/CodeGen/Generic/2006-03-01-dagcombineinfloop.ll b/llvm/test/CodeGen/Generic/2006-03-01-dagcombineinfloop.ll index 8d406df1242d..7449b9639f10 100644 --- a/llvm/test/CodeGen/Generic/2006-03-01-dagcombineinfloop.ll +++ b/llvm/test/CodeGen/Generic/2006-03-01-dagcombineinfloop.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} + ; Infinite loop in the dag combiner, reduced from 176.gcc. %struct._obstack_chunk = type { ptr, ptr, [4 x i8] } %struct.anon = type { i32 } diff --git a/llvm/test/CodeGen/Generic/2006-06-13-ComputeMaskedBitsCrash.ll b/llvm/test/CodeGen/Generic/2006-06-13-ComputeMaskedBitsCrash.ll index 6daa5e0c9b7e..edf45d0235f4 100644 --- a/llvm/test/CodeGen/Generic/2006-06-13-ComputeMaskedBitsCrash.ll +++ b/llvm/test/CodeGen/Generic/2006-06-13-ComputeMaskedBitsCrash.ll @@ -1,5 +1,6 @@ ; RUN: llc < %s -O0 - +; UNSUPPORTED: target=evm{{.*}} + %struct.cl_perfunc_opts = type { i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32 } @cl_pf_opts = external global %struct.cl_perfunc_opts ; [#uses=2] diff --git a/llvm/test/CodeGen/Generic/2006-06-28-SimplifySetCCCrash.ll b/llvm/test/CodeGen/Generic/2006-06-28-SimplifySetCCCrash.ll index 2eb768517476..1bd0facb30e7 100644 --- a/llvm/test/CodeGen/Generic/2006-06-28-SimplifySetCCCrash.ll +++ b/llvm/test/CodeGen/Generic/2006-06-28-SimplifySetCCCrash.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} + %struct.rtunion = type { i64 } %struct.rtx_def = type { i16, i8, i8, [1 x %struct.rtunion] } @ix86_cpu = external global i32 ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/2006-07-03-schedulers.ll b/llvm/test/CodeGen/Generic/2006-07-03-schedulers.ll index b3d2e95f644e..0675ab0dc19b 100644 --- a/llvm/test/CodeGen/Generic/2006-07-03-schedulers.ll +++ b/llvm/test/CodeGen/Generic/2006-07-03-schedulers.ll @@ -1,3 +1,5 @@ +; EVM doesn't support floats. +; UNSUPPORTED: target=evm{{.*}} ; RUN: llc < %s -pre-RA-sched=default ; RUN: llc < %s -pre-RA-sched=list-burr ; RUN: llc < %s -pre-RA-sched=fast diff --git a/llvm/test/CodeGen/Generic/2007-01-15-LoadSelectCycle.ll b/llvm/test/CodeGen/Generic/2007-01-15-LoadSelectCycle.ll index 0bd23db7c62c..1d667d736a70 100644 --- a/llvm/test/CodeGen/Generic/2007-01-15-LoadSelectCycle.ll +++ b/llvm/test/CodeGen/Generic/2007-01-15-LoadSelectCycle.ll @@ -1,5 +1,6 @@ ; RUN: llc < %s ; PR1114 +; UNSUPPORTED: target=evm{{.*}} declare i1 @foo() diff --git a/llvm/test/CodeGen/Generic/2007-04-08-MultipleFrameIndices.ll b/llvm/test/CodeGen/Generic/2007-04-08-MultipleFrameIndices.ll index 7feeca65e312..c2ac533da4c3 100644 --- a/llvm/test/CodeGen/Generic/2007-04-08-MultipleFrameIndices.ll +++ b/llvm/test/CodeGen/Generic/2007-04-08-MultipleFrameIndices.ll @@ -1,4 +1,7 @@ ; RUN: llc -no-integrated-as < %s + +; EVM doesn't support vararg. +; XFAIL: target=evm{{.*}} ; PR1308 ; PR1557 diff --git a/llvm/test/CodeGen/Generic/2007-04-27-InlineAsm-X-Dest.ll b/llvm/test/CodeGen/Generic/2007-04-27-InlineAsm-X-Dest.ll index f85cda05b073..1096fa11a6ab 100644 --- a/llvm/test/CodeGen/Generic/2007-04-27-InlineAsm-X-Dest.ll +++ b/llvm/test/CodeGen/Generic/2007-04-27-InlineAsm-X-Dest.ll @@ -1,5 +1,8 @@ ; RUN: llc -no-integrated-as < %s +; EVM doesn't support inline assembly yet. +; UNSUPPORTED: target=evm{{.*}} + ; Test that we can have an "X" output constraint. define void @test(ptr %t) { diff --git a/llvm/test/CodeGen/Generic/2007-04-27-LargeMemObject.ll b/llvm/test/CodeGen/Generic/2007-04-27-LargeMemObject.ll index 5143fb21a215..4288dd3ccb33 100644 --- a/llvm/test/CodeGen/Generic/2007-04-27-LargeMemObject.ll +++ b/llvm/test/CodeGen/Generic/2007-04-27-LargeMemObject.ll @@ -1,5 +1,8 @@ ; RUN: llc -no-integrated-as < %s +; EVM doesn't support inline asm. +; UNSUPPORTED: target=evm{{.*}} + %struct..0anon = type { [100 x i32] } define void @test() { diff --git a/llvm/test/CodeGen/Generic/2007-12-17-InvokeAsm.ll b/llvm/test/CodeGen/Generic/2007-12-17-InvokeAsm.ll index 58415bf1e827..2b830f859c4f 100644 --- a/llvm/test/CodeGen/Generic/2007-12-17-InvokeAsm.ll +++ b/llvm/test/CodeGen/Generic/2007-12-17-InvokeAsm.ll @@ -1,3 +1,5 @@ +; UNSUPPORTED: target=evm{{.*}} +; EVM doesn't support inline asm. ; RUN: llc -no-integrated-as < %s ; The test uses inline assembly with x86-specific constraints. diff --git a/llvm/test/CodeGen/Generic/2007-12-31-UnusedSelector.ll b/llvm/test/CodeGen/Generic/2007-12-31-UnusedSelector.ll index 755eebd93b61..89a9c8d6a178 100644 --- a/llvm/test/CodeGen/Generic/2007-12-31-UnusedSelector.ll +++ b/llvm/test/CodeGen/Generic/2007-12-31-UnusedSelector.ll @@ -1,5 +1,6 @@ ; RUN: llc < %s ; PR1833 +; UNSUPPORTED: target=evm{{.*}} %struct.__class_type_info_pseudo = type { %struct.__type_info_pseudo } %struct.__type_info_pseudo = type { ptr, ptr } diff --git a/llvm/test/CodeGen/Generic/2008-01-25-dag-combine-mul.ll b/llvm/test/CodeGen/Generic/2008-01-25-dag-combine-mul.ll index a0388c46ece2..b75f15938c49 100644 --- a/llvm/test/CodeGen/Generic/2008-01-25-dag-combine-mul.ll +++ b/llvm/test/CodeGen/Generic/2008-01-25-dag-combine-mul.ll @@ -3,6 +3,7 @@ ; XCore default subtarget does not support 8-byte alignment on stack. ; XFAIL: target=xcore{{.*}} +; UNSUPPORTED: target=evm{{.*}} define i32 @f(ptr %pc) { entry: diff --git a/llvm/test/CodeGen/Generic/2008-01-30-LoadCrash.ll b/llvm/test/CodeGen/Generic/2008-01-30-LoadCrash.ll index fc8297035a50..7c13062228d0 100644 --- a/llvm/test/CodeGen/Generic/2008-01-30-LoadCrash.ll +++ b/llvm/test/CodeGen/Generic/2008-01-30-LoadCrash.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} @letters.3100 = external constant [63 x i8] ; [#uses=2] diff --git a/llvm/test/CodeGen/Generic/2008-02-04-Ctlz.ll b/llvm/test/CodeGen/Generic/2008-02-04-Ctlz.ll index 43815237c67e..3ee420a0389e 100644 --- a/llvm/test/CodeGen/Generic/2008-02-04-Ctlz.ll +++ b/llvm/test/CodeGen/Generic/2008-02-04-Ctlz.ll @@ -1,5 +1,8 @@ ; RUN: llc < %s +; TODO: Check cttz, ctlz, ctpop, ConstantPool lowering. +; XFAIL: target=evm{{.*}} + @.str = internal constant [14 x i8] c"%lld %d %d %d\00" define i32 @main(i64 %arg) nounwind { diff --git a/llvm/test/CodeGen/Generic/2008-02-20-MatchingMem.ll b/llvm/test/CodeGen/Generic/2008-02-20-MatchingMem.ll index 0a21c39b2058..a479c02bd5a6 100644 --- a/llvm/test/CodeGen/Generic/2008-02-20-MatchingMem.ll +++ b/llvm/test/CodeGen/Generic/2008-02-20-MatchingMem.ll @@ -1,4 +1,6 @@ ; RUN: llc -no-integrated-as < %s +; EVM doesn't support inline asm. +; UNSUPPORTED: target=evm{{.*}} ; PR1133 define void @test(ptr %X) nounwind { entry: diff --git a/llvm/test/CodeGen/Generic/2008-02-25-NegateZero.ll b/llvm/test/CodeGen/Generic/2008-02-25-NegateZero.ll index 7284e0d606b8..ac3292032edf 100644 --- a/llvm/test/CodeGen/Generic/2008-02-25-NegateZero.ll +++ b/llvm/test/CodeGen/Generic/2008-02-25-NegateZero.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; EVM doesn't support floats. +; UNSUPPORTED: target=evm{{.*}} ; rdar://5763967 define void @test() { diff --git a/llvm/test/CodeGen/Generic/2008-08-07-PtrToInt-SmallerInt.ll b/llvm/test/CodeGen/Generic/2008-08-07-PtrToInt-SmallerInt.ll index b0282f32b81a..5b60f4bffeae 100644 --- a/llvm/test/CodeGen/Generic/2008-08-07-PtrToInt-SmallerInt.ll +++ b/llvm/test/CodeGen/Generic/2008-08-07-PtrToInt-SmallerInt.ll @@ -1,4 +1,7 @@ ; RUN: llc < %s + +; TODO: CPR-920 support operators +; UNSUPPORTED: target=evm{{.*}} ; PR2603 %struct.A = type { i8 } %struct.B = type { i8, [1 x i8] } diff --git a/llvm/test/CodeGen/Generic/2009-03-29-SoftFloatVectorExtract.ll b/llvm/test/CodeGen/Generic/2009-03-29-SoftFloatVectorExtract.ll index b09859def617..96283deb3410 100644 --- a/llvm/test/CodeGen/Generic/2009-03-29-SoftFloatVectorExtract.ll +++ b/llvm/test/CodeGen/Generic/2009-03-29-SoftFloatVectorExtract.ll @@ -1,4 +1,5 @@ ; XFAIL: target={{.*}}-aix{{.*}} +; UNSUPPORTED: target=evm{{.*}} ; RUN: llc < %s ; PR3899 diff --git a/llvm/test/CodeGen/Generic/2009-04-28-i128-cmp-crash.ll b/llvm/test/CodeGen/Generic/2009-04-28-i128-cmp-crash.ll index 605fe346c9d3..bed186acb538 100644 --- a/llvm/test/CodeGen/Generic/2009-04-28-i128-cmp-crash.ll +++ b/llvm/test/CodeGen/Generic/2009-04-28-i128-cmp-crash.ll @@ -2,6 +2,7 @@ ; rdar://6836460 ; rdar://7516906 ; PR5963 +; UNSUPPORTED: target=evm{{.*}} define i32 @test(ptr %P) nounwind { entry: diff --git a/llvm/test/CodeGen/Generic/2009-11-16-BadKillsCrash.ll b/llvm/test/CodeGen/Generic/2009-11-16-BadKillsCrash.ll index feec0966feb4..b7db8fdb4252 100644 --- a/llvm/test/CodeGen/Generic/2009-11-16-BadKillsCrash.ll +++ b/llvm/test/CodeGen/Generic/2009-11-16-BadKillsCrash.ll @@ -1,3 +1,5 @@ +; XFAIL: target=evm{{.*}} +; UNSUPPORTED: target=evm{{.*}} ; RUN: llc < %s ; PR5495 diff --git a/llvm/test/CodeGen/Generic/2010-11-04-BigByval.ll b/llvm/test/CodeGen/Generic/2010-11-04-BigByval.ll index 189b9319c3f5..9c3e72090121 100644 --- a/llvm/test/CodeGen/Generic/2010-11-04-BigByval.ll +++ b/llvm/test/CodeGen/Generic/2010-11-04-BigByval.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; EVM doesn't support byval yet. +; XFAIL: target=evm{{.*}} ; PR7170 ; The test is intentionally disabled only for the NVPTX target diff --git a/llvm/test/CodeGen/Generic/2010-ZeroSizedArg.ll b/llvm/test/CodeGen/Generic/2010-ZeroSizedArg.ll index 0a3c507cee7e..aad045b89751 100644 --- a/llvm/test/CodeGen/Generic/2010-ZeroSizedArg.ll +++ b/llvm/test/CodeGen/Generic/2010-ZeroSizedArg.ll @@ -2,7 +2,7 @@ ; PR4975 ; NVPTX does not support zero sized type arg -; UNSUPPORTED: target=nvptx{{.*}} +; UNSUPPORTED: target=nvptx{{.*}}, target=evm{{.*}} %0 = type <{ [0 x i32] }> %union.T0 = type { } diff --git a/llvm/test/CodeGen/Generic/2011-07-07-ScheduleDAGCrash.ll b/llvm/test/CodeGen/Generic/2011-07-07-ScheduleDAGCrash.ll index f5f2276ac820..f72452fc0cbf 100644 --- a/llvm/test/CodeGen/Generic/2011-07-07-ScheduleDAGCrash.ll +++ b/llvm/test/CodeGen/Generic/2011-07-07-ScheduleDAGCrash.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} ; This caused ScheduleDAG to crash in EmitPhysRegCopy when searching ; the uses of a copy to a physical register without ignoring non-data diff --git a/llvm/test/CodeGen/Generic/2012-06-08-APIntCrash.ll b/llvm/test/CodeGen/Generic/2012-06-08-APIntCrash.ll index f08923669ece..ce3c4ec680df 100644 --- a/llvm/test/CodeGen/Generic/2012-06-08-APIntCrash.ll +++ b/llvm/test/CodeGen/Generic/2012-06-08-APIntCrash.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} define void @test1(ptr %ptr) { diff --git a/llvm/test/CodeGen/Generic/2013-03-20-APFloatCrash.ll b/llvm/test/CodeGen/Generic/2013-03-20-APFloatCrash.ll index a1aed0e3a4b6..32d3fe3f35b9 100644 --- a/llvm/test/CodeGen/Generic/2013-03-20-APFloatCrash.ll +++ b/llvm/test/CodeGen/Generic/2013-03-20-APFloatCrash.ll @@ -1,3 +1,6 @@ +; UNSUPPORTED: target=evm{{.*}} +; EVM doesn't support floats. + ; RUN: llc < %s define internal i1 @f(float %s) { diff --git a/llvm/test/CodeGen/Generic/2014-02-05-OpaqueConstants.ll b/llvm/test/CodeGen/Generic/2014-02-05-OpaqueConstants.ll index cec0b32f8c4c..da33efc5cbb5 100644 --- a/llvm/test/CodeGen/Generic/2014-02-05-OpaqueConstants.ll +++ b/llvm/test/CodeGen/Generic/2014-02-05-OpaqueConstants.ll @@ -1,6 +1,7 @@ ; Test that opaque constants are not creating an infinite DAGCombine loop ; RUN: llc < %s ; XFAIL: target=r600{{.*}} +; UNSUPPORTED: target=evm{{.*}} @a = common global ptr null, align 8 @c = common global i32 0, align 4 diff --git a/llvm/test/CodeGen/Generic/APIntLoadStore.ll b/llvm/test/CodeGen/Generic/APIntLoadStore.ll index ed904a08e24b..6813d6984831 100644 --- a/llvm/test/CodeGen/Generic/APIntLoadStore.ll +++ b/llvm/test/CodeGen/Generic/APIntLoadStore.ll @@ -1,5 +1,8 @@ ; RUN: llc < %s > %t +; TODO: CPR-916 Fix the test. +; UNSUPPORTED: target=evm{{.*}} + ; NVPTX does not support arbitrary integer types and has acceptable subset tested in NVPTX/APIntLoadStore.ll ; UNSUPPORTED: target=nvptx{{.*}} diff --git a/llvm/test/CodeGen/Generic/APIntParam.ll b/llvm/test/CodeGen/Generic/APIntParam.ll index 39f856d9dd14..8332ea398d24 100644 --- a/llvm/test/CodeGen/Generic/APIntParam.ll +++ b/llvm/test/CodeGen/Generic/APIntParam.ll @@ -1,5 +1,8 @@ ; RUN: llc < %s > %t +; TODO: CPR-916 Fix the test. +; UNSUPPORTED: target=evm{{.*}} + ; NVPTX does not support arbitrary integer types and has acceptable subset tested in NVPTX/APIntParam.ll ; UNSUPPORTED: target=nvptx{{.*}} diff --git a/llvm/test/CodeGen/Generic/APIntSextParam.ll b/llvm/test/CodeGen/Generic/APIntSextParam.ll index d34c88e2cb87..0fbe8d99271a 100644 --- a/llvm/test/CodeGen/Generic/APIntSextParam.ll +++ b/llvm/test/CodeGen/Generic/APIntSextParam.ll @@ -1,5 +1,8 @@ ; RUN: llc < %s > %t +; TODO: CPR-916 Fix the test. +; UNSUPPORTED: target=evm{{.*}} + ; NVPTX does not support arbitrary integer types and has acceptable subset tested in NVPTX/APIntSextParam.ll ; UNSUPPORTED: target=nvptx{{.*}} diff --git a/llvm/test/CodeGen/Generic/APIntZextParam.ll b/llvm/test/CodeGen/Generic/APIntZextParam.ll index 7fb0791c5dcc..b610ec8308a0 100644 --- a/llvm/test/CodeGen/Generic/APIntZextParam.ll +++ b/llvm/test/CodeGen/Generic/APIntZextParam.ll @@ -1,5 +1,8 @@ ; RUN: llc < %s > %t +; TODO: CPR-916 Fix the test. +; UNSUPPORTED: target=evm{{.*}} + ; NVPTX does not support arbitrary integer types and has acceptable subset tested in NVPTX/APIntZextParam.ll ; UNSUPPORTED: target=nvptx{{.*}} diff --git a/llvm/test/CodeGen/Generic/ConstantExprLowering.ll b/llvm/test/CodeGen/Generic/ConstantExprLowering.ll index ac44d9a90882..9b12aafdd1d3 100644 --- a/llvm/test/CodeGen/Generic/ConstantExprLowering.ll +++ b/llvm/test/CodeGen/Generic/ConstantExprLowering.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} @.str_1 = internal constant [16 x i8] c"%d %d %d %d %d\0A\00" ; [#uses=1] @XA = external global i32 ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/ForceStackAlign.ll b/llvm/test/CodeGen/Generic/ForceStackAlign.ll index 7993b3eff65b..cd7e48246dba 100644 --- a/llvm/test/CodeGen/Generic/ForceStackAlign.ll +++ b/llvm/test/CodeGen/Generic/ForceStackAlign.ll @@ -11,6 +11,9 @@ ; NVPTX can only select dynamic_stackalloc on sm_52+ and with ptx73+ ; XFAIL: target=nvptx{{.*}} +; EVM realignment not supported. +; UNSUPPORTED: target=evm{{.*}} + define i32 @f(ptr %p) nounwind { entry: %0 = load i8, ptr %p diff --git a/llvm/test/CodeGen/Generic/MIRDebugify/check-line-and-variables.ll b/llvm/test/CodeGen/Generic/MIRDebugify/check-line-and-variables.ll index 56c7cf45705a..26842b6ff5f7 100644 --- a/llvm/test/CodeGen/Generic/MIRDebugify/check-line-and-variables.ll +++ b/llvm/test/CodeGen/Generic/MIRDebugify/check-line-and-variables.ll @@ -1,5 +1,6 @@ ; RUN: llc -debugify-check-and-strip-all-safe -o - %s 2>&1 | FileCheck %s ; RUN: llc --experimental-debuginfo-iterators=false -debugify-check-and-strip-all-safe -o - %s 2>&1 | FileCheck %s +; UNSUPPORTED: target=evm{{.*}} ; ModuleID = 'main.c' source_filename = "main.c" diff --git a/llvm/test/CodeGen/Generic/PBQP.ll b/llvm/test/CodeGen/Generic/PBQP.ll index 31fc4e653d7b..dccafe51afff 100644 --- a/llvm/test/CodeGen/Generic/PBQP.ll +++ b/llvm/test/CodeGen/Generic/PBQP.ll @@ -1,4 +1,5 @@ ; RUN: llc -regalloc=pbqp < %s +; UNSUPPORTED: target=evm{{.*}} define i32 @foo() { entry: diff --git a/llvm/test/CodeGen/Generic/add-with-overflow-128.ll b/llvm/test/CodeGen/Generic/add-with-overflow-128.ll index 389e6511b6c7..f0add0e18e29 100644 --- a/llvm/test/CodeGen/Generic/add-with-overflow-128.ll +++ b/llvm/test/CodeGen/Generic/add-with-overflow-128.ll @@ -3,6 +3,9 @@ ; NVPTX fails to LowerFormalArguments for arg type i96 ; the arg byte size must be one of the {16, 8, 4, 2} ; XFAIL: target=nvptx{{.*}} +; UNSUPPORTED: target=evm{{.*}} + +; XFAIL: target=evm{{.*}} @ok = internal constant [4 x i8] c"%d\0A\00" @no = internal constant [4 x i8] c"no\0A\00" diff --git a/llvm/test/CodeGen/Generic/add-with-overflow-24.ll b/llvm/test/CodeGen/Generic/add-with-overflow-24.ll index cd05fefc91b9..842314a95796 100644 --- a/llvm/test/CodeGen/Generic/add-with-overflow-24.ll +++ b/llvm/test/CodeGen/Generic/add-with-overflow-24.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} @ok = internal constant [4 x i8] c"%d\0A\00" @no = internal constant [4 x i8] c"no\0A\00" diff --git a/llvm/test/CodeGen/Generic/add-with-overflow.ll b/llvm/test/CodeGen/Generic/add-with-overflow.ll index fa459f64b039..9d95116531c3 100644 --- a/llvm/test/CodeGen/Generic/add-with-overflow.ll +++ b/llvm/test/CodeGen/Generic/add-with-overflow.ll @@ -1,5 +1,6 @@ ; RUN: llc < %s ; RUN: llc < %s -fast-isel +; UNSUPPORTED: target=evm{{.*}} @ok = internal constant [4 x i8] c"%d\0A\00" @no = internal constant [4 x i8] c"no\0A\00" diff --git a/llvm/test/CodeGen/Generic/allow-check.ll b/llvm/test/CodeGen/Generic/allow-check.ll index a08488959862..ba7a1399aa8f 100644 --- a/llvm/test/CodeGen/Generic/allow-check.ll +++ b/llvm/test/CodeGen/Generic/allow-check.ll @@ -6,6 +6,7 @@ ; XFAIL: target=nvptx{{.*}} ; XFAIL: target=sparc{{.*}} ; XFAIL: target=hexagon-{{.*}} +; UNSUPPORTED: target=evm{{.*}} ; RUN: llc < %s -O3 -global-isel=0 -fast-isel=0 ; RUN: llc < %s -O3 -global-isel=1 -fast-isel=0 diff --git a/llvm/test/CodeGen/Generic/asm-large-immediate.ll b/llvm/test/CodeGen/Generic/asm-large-immediate.ll index 67a7a1e75a83..cbccc678e132 100644 --- a/llvm/test/CodeGen/Generic/asm-large-immediate.ll +++ b/llvm/test/CodeGen/Generic/asm-large-immediate.ll @@ -1,5 +1,8 @@ ; RUN: llc -no-integrated-as < %s | FileCheck %s +; EVM doesn't support inline asm yet. +; UNSUPPORTED: target=evm{{.*}} + define void @test() { entry: ; CHECK: /* result: 68719476738 */ diff --git a/llvm/test/CodeGen/Generic/badFoldGEP.ll b/llvm/test/CodeGen/Generic/badFoldGEP.ll index d9bf877fe881..f45af74ccbe7 100644 --- a/llvm/test/CodeGen/Generic/badFoldGEP.ll +++ b/llvm/test/CodeGen/Generic/badFoldGEP.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} ;; GetMemInstArgs() folded the two getElementPtr instructions together, ;; producing an illegal getElementPtr. That's because the type generated diff --git a/llvm/test/CodeGen/Generic/badarg6.ll b/llvm/test/CodeGen/Generic/badarg6.ll index 3ddad36e0941..cd20c1fcb6c3 100644 --- a/llvm/test/CodeGen/Generic/badarg6.ll +++ b/llvm/test/CodeGen/Generic/badarg6.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} ; On this code, llc did not pass the sixth argument (%reg321) to printf. ; It passed the first five in %o0 - %o4, but never initialized %o5. diff --git a/llvm/test/CodeGen/Generic/bool-to-double.ll b/llvm/test/CodeGen/Generic/bool-to-double.ll index 81350a40b4db..5a2ffe3ea62e 100644 --- a/llvm/test/CodeGen/Generic/bool-to-double.ll +++ b/llvm/test/CodeGen/Generic/bool-to-double.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; EVM doesn't support floats +; UNSUPPORTED: target=evm{{.*}} define double @test(i1 %X) { %Y = uitofp i1 %X to double ; [#uses=1] ret double %Y diff --git a/llvm/test/CodeGen/Generic/builtin-expect.ll b/llvm/test/CodeGen/Generic/builtin-expect.ll index adeeea6c32cf..528b091b169e 100644 --- a/llvm/test/CodeGen/Generic/builtin-expect.ll +++ b/llvm/test/CodeGen/Generic/builtin-expect.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} define i32 @test1(i32 %x) nounwind uwtable ssp { entry: diff --git a/llvm/test/CodeGen/Generic/cast-fp.ll b/llvm/test/CodeGen/Generic/cast-fp.ll index a205cce6c9aa..21248a430fe8 100644 --- a/llvm/test/CodeGen/Generic/cast-fp.ll +++ b/llvm/test/CodeGen/Generic/cast-fp.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; EVM doesn't support floats +; UNSUPPORTED: target=evm{{.*}} @a_fstr = internal constant [8 x i8] c"a = %f\0A\00" ; [#uses=1] @a_lstr = internal constant [10 x i8] c"a = %lld\0A\00" ; [#uses=1] @a_dstr = internal constant [8 x i8] c"a = %d\0A\00" ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/constindices.ll b/llvm/test/CodeGen/Generic/constindices.ll index 0e8b53fb36b2..7c4d3951bcb3 100644 --- a/llvm/test/CodeGen/Generic/constindices.ll +++ b/llvm/test/CodeGen/Generic/constindices.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; EVM doesn't support floats. +; UNSUPPORTED: target=evm{{.*}} ; Test that a sequence of constant indices are folded correctly ; into the equivalent offset at compile-time. diff --git a/llvm/test/CodeGen/Generic/crash.ll b/llvm/test/CodeGen/Generic/crash.ll index a1de27503e7e..cb798898a581 100644 --- a/llvm/test/CodeGen/Generic/crash.ll +++ b/llvm/test/CodeGen/Generic/crash.ll @@ -1,4 +1,6 @@ ; RUN: llc %s -o - +; EVM doesn't support floats. +; UNSUPPORTED: target=evm{{.*}} ; PR6332 %struct.AVCodecTag = type {} diff --git a/llvm/test/CodeGen/Generic/dag-combine-crash.ll b/llvm/test/CodeGen/Generic/dag-combine-crash.ll index bfc5be913ef4..d4ce66c5651a 100644 --- a/llvm/test/CodeGen/Generic/dag-combine-crash.ll +++ b/llvm/test/CodeGen/Generic/dag-combine-crash.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} define void @main() { if.end: diff --git a/llvm/test/CodeGen/Generic/dag-combine-ossfuzz-crash.ll b/llvm/test/CodeGen/Generic/dag-combine-ossfuzz-crash.ll index d2402aadba18..59eb93cb722a 100644 --- a/llvm/test/CodeGen/Generic/dag-combine-ossfuzz-crash.ll +++ b/llvm/test/CodeGen/Generic/dag-combine-ossfuzz-crash.ll @@ -1,5 +1,6 @@ ; RUN: llc < %s ; XFAIL: target=avr{{.*}} +; UNSUPPORTED: target=evm{{.*}} ; llc built with address sanitizer crashes because of a dangling node pointer ; oss-fuzz - DAGCombiner::useDivRem (5011) diff --git a/llvm/test/CodeGen/Generic/dwarf-md5.ll b/llvm/test/CodeGen/Generic/dwarf-md5.ll index 6cee2420a989..13fe5b16ebf4 100644 --- a/llvm/test/CodeGen/Generic/dwarf-md5.ll +++ b/llvm/test/CodeGen/Generic/dwarf-md5.ll @@ -1,4 +1,6 @@ ; XFAIL: target={{.*}}-aix{{.*}} +; EVM doesn't support dwarf yet +; UNSUPPORTED: target=evm{{.*}} ; MD5 checksums provided by IR should be passed through to asm. ; They'll be emitted to an object file only for DWARF 5 or later. diff --git a/llvm/test/CodeGen/Generic/dwarf-source.ll b/llvm/test/CodeGen/Generic/dwarf-source.ll index 55eba5e9d71a..3d0c2ec813bc 100644 --- a/llvm/test/CodeGen/Generic/dwarf-source.ll +++ b/llvm/test/CodeGen/Generic/dwarf-source.ll @@ -1,4 +1,6 @@ ; XFAIL: target={{.*}}-aix{{.*}} +; EVM doesn't support dwarf yet +; UNSUPPORTED: target=evm{{.*}} ; Source text provided by IR should be passed through to asm. ; It is emitted to an object file only for DWARF 5 or later. diff --git a/llvm/test/CodeGen/Generic/empty-load-store.ll b/llvm/test/CodeGen/Generic/empty-load-store.ll index dbd199956296..988214055565 100644 --- a/llvm/test/CodeGen/Generic/empty-load-store.ll +++ b/llvm/test/CodeGen/Generic/empty-load-store.ll @@ -2,7 +2,7 @@ ; PR2612 ; Triggers a crash on assertion as NVPTX does not support 0-sized arrays. -; UNSUPPORTED: target=nvptx{{.*}} +; UNSUPPORTED: target=nvptx{{.*}}, target=evm{{.*}} @current_foo = internal global { } zeroinitializer diff --git a/llvm/test/CodeGen/Generic/exception-handling.ll b/llvm/test/CodeGen/Generic/exception-handling.ll index 81fe962ae60c..37fcce74f62f 100644 --- a/llvm/test/CodeGen/Generic/exception-handling.ll +++ b/llvm/test/CodeGen/Generic/exception-handling.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; EVM doesn't support EH. +; UNSUPPORTED: target=evm{{.*}} ; PR10733 declare void @_Znam() diff --git a/llvm/test/CodeGen/Generic/fastcall.ll b/llvm/test/CodeGen/Generic/fastcall.ll index c42099eccdf3..eb9b90b06947 100644 --- a/llvm/test/CodeGen/Generic/fastcall.ll +++ b/llvm/test/CodeGen/Generic/fastcall.ll @@ -1,6 +1,7 @@ ; Test fastcc works. Test from bug 2770. ; RUN: llc < %s -relocation-model=pic +; UNSUPPORTED: target=evm{{.*}} %struct.__gcov_var = type { i32 } @__gcov_var = external global %struct.__gcov_var diff --git a/llvm/test/CodeGen/Generic/fp-to-int-invalid.ll b/llvm/test/CodeGen/Generic/fp-to-int-invalid.ll index 632e6b3fc044..0c5adf11c16f 100644 --- a/llvm/test/CodeGen/Generic/fp-to-int-invalid.ll +++ b/llvm/test/CodeGen/Generic/fp-to-int-invalid.ll @@ -1,3 +1,5 @@ +; EVM doesn't support floats. +; UNSUPPORTED: target=evm{{.*}} ; RUN: llc < %s ; PR4057 define void @test_cast_float_to_char(ptr %result) nounwind { diff --git a/llvm/test/CodeGen/Generic/fp_to_int.ll b/llvm/test/CodeGen/Generic/fp_to_int.ll index ad944132d338..3a31ce55bb10 100644 --- a/llvm/test/CodeGen/Generic/fp_to_int.ll +++ b/llvm/test/CodeGen/Generic/fp_to_int.ll @@ -1,3 +1,5 @@ +; EVM doesn't support floats. +; UNSUPPORTED: target=evm{{.*}} ; RUN: llc < %s define i8 @test1(double %X) { diff --git a/llvm/test/CodeGen/Generic/fpoperations.ll b/llvm/test/CodeGen/Generic/fpoperations.ll index 53dd307db249..b58174ec35e0 100644 --- a/llvm/test/CodeGen/Generic/fpoperations.ll +++ b/llvm/test/CodeGen/Generic/fpoperations.ll @@ -1,3 +1,5 @@ +; EVM doesn't support floats. +; UNSUPPORTED: target=evm{{.*}} ; RUN: llc < %s | FileCheck %s ; This test checks default lowering of the intrinsics operating floating point diff --git a/llvm/test/CodeGen/Generic/fpowi-promote.ll b/llvm/test/CodeGen/Generic/fpowi-promote.ll index cb7dfc7036ba..31f85aaab71c 100644 --- a/llvm/test/CodeGen/Generic/fpowi-promote.ll +++ b/llvm/test/CodeGen/Generic/fpowi-promote.ll @@ -1,3 +1,5 @@ +; EVM doesn't support floats. +; UNSUPPORTED: target=evm{{.*}} ; RUN: llc < %s ; PR1239 diff --git a/llvm/test/CodeGen/Generic/fwdtwice.ll b/llvm/test/CodeGen/Generic/fwdtwice.ll index 9d6e943b05d4..966b38b0311f 100644 --- a/llvm/test/CodeGen/Generic/fwdtwice.ll +++ b/llvm/test/CodeGen/Generic/fwdtwice.ll @@ -1,5 +1,6 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} ;; ;; Test the sequence: ;; cast -> setle 0, %cast -> br %cond diff --git a/llvm/test/CodeGen/Generic/global-ret0.ll b/llvm/test/CodeGen/Generic/global-ret0.ll index 78586fa6a461..f2d0343c01ab 100644 --- a/llvm/test/CodeGen/Generic/global-ret0.ll +++ b/llvm/test/CodeGen/Generic/global-ret0.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} @g = global i32 0 ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/hello.ll b/llvm/test/CodeGen/Generic/hello.ll index 65848c7e5d6c..dd4c19c27c8e 100644 --- a/llvm/test/CodeGen/Generic/hello.ll +++ b/llvm/test/CodeGen/Generic/hello.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} @.str_1 = internal constant [7 x i8] c"hello\0A\00" ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/i128-addsub.ll b/llvm/test/CodeGen/Generic/i128-addsub.ll index e61658ed2430..7a5e3e6f1e1f 100644 --- a/llvm/test/CodeGen/Generic/i128-addsub.ll +++ b/llvm/test/CodeGen/Generic/i128-addsub.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} define void @test_add(i64 %AL, i64 %AH, i64 %BL, i64 %BH, ptr %RL, ptr %RH) { entry: diff --git a/llvm/test/CodeGen/Generic/inline-asm-mem-clobber.ll b/llvm/test/CodeGen/Generic/inline-asm-mem-clobber.ll index 8156dfdd74eb..705bd89b491a 100644 --- a/llvm/test/CodeGen/Generic/inline-asm-mem-clobber.ll +++ b/llvm/test/CodeGen/Generic/inline-asm-mem-clobber.ll @@ -3,6 +3,9 @@ ; XCore default subtarget does not support 8-byte alignment on stack. ; XFAIL: target=xcore{{.*}} +; EVM doesn't support inline asm yet. +; UNSUPPORTED: target=evm{{.*}} + @G = common global i32 0, align 4 define i32 @foo(ptr %p) nounwind uwtable { diff --git a/llvm/test/CodeGen/Generic/inline-asm-special-strings.ll b/llvm/test/CodeGen/Generic/inline-asm-special-strings.ll index 5f9f34d1e78c..44813f16c9ed 100644 --- a/llvm/test/CodeGen/Generic/inline-asm-special-strings.ll +++ b/llvm/test/CodeGen/Generic/inline-asm-special-strings.ll @@ -1,4 +1,6 @@ ; RUN: llc -no-integrated-as < %s | FileCheck %s +; EVM doesn't support inline asm yet. +; UNSUPPORTED: target=evm{{.*}} define void @bar() nounwind { ; CHECK: foo 0 0{{$}} diff --git a/llvm/test/CodeGen/Generic/intrinsics.ll b/llvm/test/CodeGen/Generic/intrinsics.ll index 82e2b3ec7dee..70805a19b1ff 100644 --- a/llvm/test/CodeGen/Generic/intrinsics.ll +++ b/llvm/test/CodeGen/Generic/intrinsics.ll @@ -1,6 +1,13 @@ +; UNSUPPORTED: target=evm{{.*}} +; EVM doesn't support floats. +; invariant and sideeffect have nothing to do with the codegen. ; RUN: llc < %s ; RUN: llc -O0 < %s +; EVM doesn't support floats. +; invariant and sideeffect have nothing to do with the codegen. +; UNSUPPORTED: target=evm{{.*}} + ; NVPTX can't select sinf(float)/sin(double) ; XFAIL: target=nvptx{{.*}} diff --git a/llvm/test/CodeGen/Generic/invalid-memcpy.ll b/llvm/test/CodeGen/Generic/invalid-memcpy.ll index 1ea95af7ab0c..06258de43f04 100644 --- a/llvm/test/CodeGen/Generic/invalid-memcpy.ll +++ b/llvm/test/CodeGen/Generic/invalid-memcpy.ll @@ -1,5 +1,7 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} + ; This testcase is invalid (the alignment specified for memcpy is ; greater than the alignment guaranteed for Qux or C.0.1173), but it ; should compile, not crash the code generator. diff --git a/llvm/test/CodeGen/Generic/isunord.ll b/llvm/test/CodeGen/Generic/isunord.ll index ebbba010793b..60154f7ee110 100644 --- a/llvm/test/CodeGen/Generic/isunord.ll +++ b/llvm/test/CodeGen/Generic/isunord.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; EVM doesn't support floats. +; UNSUPPORTED: target=evm{{.*}} declare i1 @llvm.isunordered.f64(double, double) diff --git a/llvm/test/CodeGen/Generic/live-debug-label.ll b/llvm/test/CodeGen/Generic/live-debug-label.ll index 3121b8700ed1..28e5a03a6c59 100644 --- a/llvm/test/CodeGen/Generic/live-debug-label.ll +++ b/llvm/test/CodeGen/Generic/live-debug-label.ll @@ -10,6 +10,9 @@ ; XFAIL: target=riscv{{.*}} ; XFAIL: target=amdgcn-{{.*}} +; The LiveDebugValuesID pass that handles DBG_LABEL instructions is disabled for EVM. +; XFAIL: target=evm{{.*}} + ; Generated with "clang++ -g -O1 -S -emit-llvm" ; ; inline bool bar(char c) { diff --git a/llvm/test/CodeGen/Generic/llc-start-stop.ll b/llvm/test/CodeGen/Generic/llc-start-stop.ll index b02472473a00..3bf01dc0aa09 100644 --- a/llvm/test/CodeGen/Generic/llc-start-stop.ll +++ b/llvm/test/CodeGen/Generic/llc-start-stop.ll @@ -1,5 +1,7 @@ ; NVPTX customizes the list of passes so the test cannot find what it expects ; XFAIL: target=nvptx{{.*}} +; EVM customizes the list of passes so the test cannot find what it expects +; XFAIL: target=evm{{.*}} ; Note: -verify-machineinstrs is used in order to make this test compatible with EXPENSIVE_CHECKS. ; RUN: llc < %s -debug-pass=Structure -stop-after=loop-reduce -verify-machineinstrs -o /dev/null 2>&1 \ diff --git a/llvm/test/CodeGen/Generic/llvm-ct-intrinsics.ll b/llvm/test/CodeGen/Generic/llvm-ct-intrinsics.ll index aee06435cdfb..345a019098e7 100644 --- a/llvm/test/CodeGen/Generic/llvm-ct-intrinsics.ll +++ b/llvm/test/CodeGen/Generic/llvm-ct-intrinsics.ll @@ -1,6 +1,9 @@ ; Make sure this testcase is supported by all code generators ; RUN: llc < %s +; TODO: Check cttz, ctlz, ctpop, ConstantPool lowering. +; XFAIL: target=evm{{.*}} + declare i64 @llvm.ctpop.i64(i64) declare i32 @llvm.ctpop.i32(i32) diff --git a/llvm/test/CodeGen/Generic/multiple-return-values-cross-block-with-invoke.ll b/llvm/test/CodeGen/Generic/multiple-return-values-cross-block-with-invoke.ll index 6cc2b4040d18..e9a4fe5a1955 100644 --- a/llvm/test/CodeGen/Generic/multiple-return-values-cross-block-with-invoke.ll +++ b/llvm/test/CodeGen/Generic/multiple-return-values-cross-block-with-invoke.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} declare { i64, double } @wild() define void @foo(ptr %p, ptr %q) nounwind personality ptr @__gxx_personality_v0 { diff --git a/llvm/test/CodeGen/Generic/negintconst.ll b/llvm/test/CodeGen/Generic/negintconst.ll index c357765636fe..7e0b3c2e81f1 100644 --- a/llvm/test/CodeGen/Generic/negintconst.ll +++ b/llvm/test/CodeGen/Generic/negintconst.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} ; Test that a negative constant smaller than 64 bits (e.g., int) ; is correctly implemented with sign-extension. diff --git a/llvm/test/CodeGen/Generic/pr12507.ll b/llvm/test/CodeGen/Generic/pr12507.ll index 80bfecf98417..1a2848ef6f13 100644 --- a/llvm/test/CodeGen/Generic/pr12507.ll +++ b/llvm/test/CodeGen/Generic/pr12507.ll @@ -1,7 +1,7 @@ ; RUN: llc < %s ; NVPTX failed to lower arg i160, as size > 64 -; UNSUPPORTED: target=nvptx{{.*}} +; UNSUPPORTED: target=nvptx{{.*}}, target=evm{{.*}} @c = external global i32, align 4 diff --git a/llvm/test/CodeGen/Generic/pr2625.ll b/llvm/test/CodeGen/Generic/pr2625.ll index e54862438896..def48e43d598 100644 --- a/llvm/test/CodeGen/Generic/pr2625.ll +++ b/llvm/test/CodeGen/Generic/pr2625.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} + ; PR2625 define i32 @main(ptr) { diff --git a/llvm/test/CodeGen/Generic/pr33094.ll b/llvm/test/CodeGen/Generic/pr33094.ll index 65089a39c756..072d764d0d19 100644 --- a/llvm/test/CodeGen/Generic/pr33094.ll +++ b/llvm/test/CodeGen/Generic/pr33094.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} ; PR33094 ; Make sure that a constant extractvalue doesn't cause a crash in diff --git a/llvm/test/CodeGen/Generic/print-add.ll b/llvm/test/CodeGen/Generic/print-add.ll index c1d776857bb8..30333083f1f2 100644 --- a/llvm/test/CodeGen/Generic/print-add.ll +++ b/llvm/test/CodeGen/Generic/print-add.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} @.str_1 = internal constant [4 x i8] c"%d\0A\00" ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/print-arith-fp.ll b/llvm/test/CodeGen/Generic/print-arith-fp.ll index 87a2e2ee3d35..42e8e758c278 100644 --- a/llvm/test/CodeGen/Generic/print-arith-fp.ll +++ b/llvm/test/CodeGen/Generic/print-arith-fp.ll @@ -1,3 +1,5 @@ +; EVM doesn't support floats. +; UNSUPPORTED: target=evm{{.*}}, target=evm{{.*}} ; RUN: llc < %s @a_str = internal constant [8 x i8] c"a = %f\0A\00" ; [#uses=1] @b_str = internal constant [8 x i8] c"b = %f\0A\00" ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/print-arith-int.ll b/llvm/test/CodeGen/Generic/print-arith-int.ll index 401e789ea190..3dd624c150bf 100644 --- a/llvm/test/CodeGen/Generic/print-arith-int.ll +++ b/llvm/test/CodeGen/Generic/print-arith-int.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} + @a_str = internal constant [8 x i8] c"a = %d\0A\00" ; [#uses=1] @b_str = internal constant [8 x i8] c"b = %d\0A\00" ; [#uses=1] @add_str = internal constant [12 x i8] c"a + b = %d\0A\00" ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/print-int.ll b/llvm/test/CodeGen/Generic/print-int.ll index c6291ab47972..1468b8f6a4a0 100644 --- a/llvm/test/CodeGen/Generic/print-int.ll +++ b/llvm/test/CodeGen/Generic/print-int.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} @.str_1 = internal constant [4 x i8] c"%d\0A\00" ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/print-mul-exp.ll b/llvm/test/CodeGen/Generic/print-mul-exp.ll index 6e17a23047bc..f66d142ab82a 100644 --- a/llvm/test/CodeGen/Generic/print-mul-exp.ll +++ b/llvm/test/CodeGen/Generic/print-mul-exp.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} @a_str = internal constant [8 x i8] c"a = %d\0A\00" ; [#uses=1] @a_mul_str = internal constant [13 x i8] c"a * %d = %d\0A\00" ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/print-mul.ll b/llvm/test/CodeGen/Generic/print-mul.ll index 9b3b48966915..99c1482f614c 100644 --- a/llvm/test/CodeGen/Generic/print-mul.ll +++ b/llvm/test/CodeGen/Generic/print-mul.ll @@ -1,4 +1,7 @@ +; XFAIL: target=evm{{.*}} +; TODO: CPR-921 Needs proger GA wrapping to be implemented. ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} @a_str = internal constant [8 x i8] c"a = %d\0A\00" ; [#uses=1] @b_str = internal constant [8 x i8] c"b = %d\0A\00" ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/print-shift.ll b/llvm/test/CodeGen/Generic/print-shift.ll index b15dd64681da..348f9919a1f7 100644 --- a/llvm/test/CodeGen/Generic/print-shift.ll +++ b/llvm/test/CodeGen/Generic/print-shift.ll @@ -1,5 +1,9 @@ +; XFAIL: target=evm{{.*}} +; TODO: CPR-921 Needs proger GA wrapping to be implemented. ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} + @a_str = internal constant [8 x i8] c"a = %d\0A\00" ; [#uses=1] @b_str = internal constant [8 x i8] c"b = %d\0A\00" ; [#uses=1] @a_shl_str = internal constant [14 x i8] c"a << %d = %d\0A\00" ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/ptr-annotate.ll b/llvm/test/CodeGen/Generic/ptr-annotate.ll index 5047593af462..a2d71aded116 100644 --- a/llvm/test/CodeGen/Generic/ptr-annotate.ll +++ b/llvm/test/CodeGen/Generic/ptr-annotate.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} ; PR15253 diff --git a/llvm/test/CodeGen/Generic/select-cc.ll b/llvm/test/CodeGen/Generic/select-cc.ll index 2b39fc17a55d..7a2eec4bf0fe 100644 --- a/llvm/test/CodeGen/Generic/select-cc.ll +++ b/llvm/test/CodeGen/Generic/select-cc.ll @@ -1,3 +1,5 @@ +; EVM doesn't support vectors. +; UNSUPPORTED: target=evm{{.*}} ; RUN: llc < %s define <2 x double> @vector_select(<2 x double> %x, <2 x double> %y) nounwind { diff --git a/llvm/test/CodeGen/Generic/select.ll b/llvm/test/CodeGen/Generic/select.ll index 7658f2809de1..8a97eabf261d 100644 --- a/llvm/test/CodeGen/Generic/select.ll +++ b/llvm/test/CodeGen/Generic/select.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} %Domain = type { ptr, i32, ptr, i32, i32, ptr, ptr } @AConst = constant i32 123 ; [#uses=1] @@ -119,21 +120,24 @@ define i1 @boolexpr(i1 %b, i32 %N) { ret i1 %b3 } +; EVM local begin +; EVM doesn't support floats ; Test branch on floating point comparison ; -define void @testfloatbool(float %x, float %y) { - br label %Top - -Top: ; preds = %Top, %0 - %p = fadd float %x, %y ; [#uses=1] - %z = fsub float %x, %y ; [#uses=1] - %b = fcmp ole float %p, %z ; [#uses=2] - %c = xor i1 %b, true ; [#uses=0] - br i1 %b, label %Top, label %goon - -goon: ; preds = %Top - ret void -} +;define void @testfloatbool(float %x, float %y) { +; br label %Top +; +;Top: ; preds = %Top, %0 +; %p = fadd float %x, %y ; [#uses=1] +; %z = fsub float %x, %y ; [#uses=1] +; %b = fcmp ole float %p, %z ; [#uses=2] +; %c = xor i1 %b, true ; [#uses=0] +; br i1 %b, label %Top, label %goon +; +;goon: ; preds = %Top +; ret void +;} +; EVM local end ; Test cases where an LLVM instruction requires no machine @@ -184,10 +188,13 @@ define i32 @checkFoldGEP(ptr %D, i64 %idx) { ret i32 %reg820 } +; EVM local begin +; EVM doesn't support vectors ; Test case for scalarising a 1 element vselect ; -define <1 x i32> @checkScalariseVSELECT(<1 x i32> %a, <1 x i32> %b) { - %cond = icmp uge <1 x i32> %a, %b - %s = select <1 x i1> %cond, <1 x i32> %a, <1 x i32> %b - ret <1 x i32> %s -} +;define <1 x i32> @checkScalariseVSELECT(<1 x i32> %a, <1 x i32> %b) { +; %cond = icmp uge <1 x i32> %a, %b +; %s = select <1 x i1> %cond, <1 x i32> %a, <1 x i32> %b +; ret <1 x i32> %s +;} +; EVM local end diff --git a/llvm/test/CodeGen/Generic/selection-dag-determinism.ll b/llvm/test/CodeGen/Generic/selection-dag-determinism.ll index 1adff3d61ba2..2853957dec1e 100644 --- a/llvm/test/CodeGen/Generic/selection-dag-determinism.ll +++ b/llvm/test/CodeGen/Generic/selection-dag-determinism.ll @@ -7,6 +7,7 @@ ; RUN: cmp %t1.o %t3.o ; RUN: cmp %t1.o %t4.o ; RUN: cmp %t1.o %t5.o +; UNSUPPORTED: target=evm{{.*}} ; Regression test for nondeterminism introduced in https://reviews.llvm.org/D57694 diff --git a/llvm/test/CodeGen/Generic/stacksave-restore.ll b/llvm/test/CodeGen/Generic/stacksave-restore.ll index a2c1ba4d62b0..6be545a2effb 100644 --- a/llvm/test/CodeGen/Generic/stacksave-restore.ll +++ b/llvm/test/CodeGen/Generic/stacksave-restore.ll @@ -1,3 +1,5 @@ +; EVM doesn't support dynamic_stackalloc yet. +; UNSUPPORTED: target=evm{{.*}} ; RUN: llc < %s ; NVPTX can not select llvm.stacksave (dynamic_stackalloc) and llvm.stackrestore diff --git a/llvm/test/CodeGen/Generic/storetrunc-fp.ll b/llvm/test/CodeGen/Generic/storetrunc-fp.ll index b952eced4aab..21ff2dd9cd13 100644 --- a/llvm/test/CodeGen/Generic/storetrunc-fp.ll +++ b/llvm/test/CodeGen/Generic/storetrunc-fp.ll @@ -1,3 +1,5 @@ +; Unsupported because of floats. +; UNSUPPORTED: target=evm{{.*}} ; RUN: llc < %s define void @foo(double %a, double %b, ptr %fp) { diff --git a/llvm/test/CodeGen/Generic/trap.ll b/llvm/test/CodeGen/Generic/trap.ll index 67d1a7a347f3..ffd0726caf04 100644 --- a/llvm/test/CodeGen/Generic/trap.ll +++ b/llvm/test/CodeGen/Generic/trap.ll @@ -1,4 +1,8 @@ +; XFAIL: target=evm{{.*}} +; TODO: CPR-918 Lower trap to panic ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} + define i32 @test() noreturn nounwind { entry: tail call void @llvm.trap( ) diff --git a/llvm/test/CodeGen/Generic/undef-phi.ll b/llvm/test/CodeGen/Generic/undef-phi.ll index 0e221fe612ab..dcdb2abebe81 100644 --- a/llvm/test/CodeGen/Generic/undef-phi.ll +++ b/llvm/test/CodeGen/Generic/undef-phi.ll @@ -1,4 +1,7 @@ +; XFAIL: target=evm{{.*}} +; TODO: CPR-922 Fix ; RUN: llc < %s -verify-machineinstrs -verify-coalescing +; UNSUPPORTED: target=evm{{.*}} ; ; This function has a PHI with one undefined input. Verify that PHIElimination ; inserts an IMPLICIT_DEF instruction in the predecessor so all paths to the use diff --git a/llvm/test/CodeGen/Generic/v-split.ll b/llvm/test/CodeGen/Generic/v-split.ll index 26db28ef855a..5e404d35fc10 100644 --- a/llvm/test/CodeGen/Generic/v-split.ll +++ b/llvm/test/CodeGen/Generic/v-split.ll @@ -1,3 +1,5 @@ +; EVM doesn't support vectors and floats. +; UNSUPPORTED: target=evm{{.*}} ; RUN: llc < %s %f8 = type <8 x float> diff --git a/llvm/test/CodeGen/Generic/vector-casts.ll b/llvm/test/CodeGen/Generic/vector-casts.ll index 76accaa5e550..46908ca3585b 100644 --- a/llvm/test/CodeGen/Generic/vector-casts.ll +++ b/llvm/test/CodeGen/Generic/vector-casts.ll @@ -1,3 +1,5 @@ +; EVM doesn't support vector instructions. +; UNSUPPORTED: target=evm{{.*}} ; RUN: llc < %s ; PR2671 diff --git a/llvm/test/CodeGen/Generic/vector-constantexpr.ll b/llvm/test/CodeGen/Generic/vector-constantexpr.ll index 416367fa3b52..4088e173b64e 100644 --- a/llvm/test/CodeGen/Generic/vector-constantexpr.ll +++ b/llvm/test/CodeGen/Generic/vector-constantexpr.ll @@ -1,3 +1,5 @@ +; EVM doesn't support vector instructions. +; UNSUPPORTED: target=evm{{.*}} ; RUN: llc < %s define void @""(ptr %inregs, ptr %outregs) { diff --git a/llvm/test/CodeGen/Generic/vector-identity-shuffle.ll b/llvm/test/CodeGen/Generic/vector-identity-shuffle.ll index 9e3ca2c15744..2732df9f2753 100644 --- a/llvm/test/CodeGen/Generic/vector-identity-shuffle.ll +++ b/llvm/test/CodeGen/Generic/vector-identity-shuffle.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; EVM doesn't support vector instructions. +; UNSUPPORTED: target=evm{{.*}} define void @test(ptr %tmp2.i) { diff --git a/llvm/test/CodeGen/Generic/vector.ll b/llvm/test/CodeGen/Generic/vector.ll index 13bc12b8c7ae..88c0ddc98cb2 100644 --- a/llvm/test/CodeGen/Generic/vector.ll +++ b/llvm/test/CodeGen/Generic/vector.ll @@ -1,3 +1,5 @@ +; UNSUPPORTED: target=evm{{.*}} +; EVM doesn't support vector instructions and floats. ; Test that vectors are scalarized/lowered correctly. ; RUN: llc < %s diff --git a/llvm/test/CodeGen/MLRegAlloc/default-eviction-advisor.ll b/llvm/test/CodeGen/MLRegAlloc/default-eviction-advisor.ll index 337fbb767f36..e972702f6865 100644 --- a/llvm/test/CodeGen/MLRegAlloc/default-eviction-advisor.ll +++ b/llvm/test/CodeGen/MLRegAlloc/default-eviction-advisor.ll @@ -9,6 +9,8 @@ ; regalloc-enable-advisor is not enabled for NVPTX ; UNSUPPORTED: target=nvptx{{.*}} +; regalloc-enable-advisor is not enabled for EVM +; UNSUPPORTED: target=evm{{.*}} define void @f2(i64 %lhs, i64 %rhs, i64* %addr) { %sum = add i64 %lhs, %rhs diff --git a/llvm/test/CodeGen/MLRegAlloc/default-priority-advisor.ll b/llvm/test/CodeGen/MLRegAlloc/default-priority-advisor.ll index 75bae3166343..6d193adf6b68 100644 --- a/llvm/test/CodeGen/MLRegAlloc/default-priority-advisor.ll +++ b/llvm/test/CodeGen/MLRegAlloc/default-priority-advisor.ll @@ -8,7 +8,7 @@ ; RUN: llc -O2 -regalloc-enable-priority-advisor=default < %s 2>&1 | FileCheck %s --check-prefix=DEFAULT ; regalloc-enable-priority-advisor is not enabled for NVPTX -; UNSUPPORTED: target=nvptx{{.*}} +; UNSUPPORTED: target=nvptx{{.*}}, target=evm{{.*}} define void @f2(i64 %lhs, i64 %rhs, i64* %addr) { %sum = add i64 %lhs, %rhs diff --git a/llvm/test/CodeGen/NVPTX/NVPTXAA_before_BasicAA.ll b/llvm/test/CodeGen/NVPTX/NVPTXAA_before_BasicAA.ll new file mode 100644 index 000000000000..0d16b344e9f6 --- /dev/null +++ b/llvm/test/CodeGen/NVPTX/NVPTXAA_before_BasicAA.ll @@ -0,0 +1,17 @@ +; REQUIRES: asserts +; RUN: opt -aa-pipeline=default -passes='require' -debug-pass-manager -disable-output -S < %s 2>&1 | FileCheck %s +; RUN: llc --debug-only='aa' -o /dev/null %s 2>&1 | FileCheck %s -check-prefix=LEGACY + +; In default AA pipeline, NVPTXAA should run before BasicAA to reduce compile time for NVPTX backend +target triple = "nvptx64-nvidia-cuda" + +; CHECK: Running analysis: NVPTXAA on foo +; CHECK-NEXT: Running analysis: BasicAA on foo + +; LEGACY: AAResults register Early ExternalAA: NVPTX Address space based Alias Analysis Wrapper +; LEGACY-NEXT: AAResults register BasicAA +define void @foo(){ +entry: + ret void +} + diff --git a/llvm/test/CodeGen/RISCV/redundant-copy-from-tail-duplicate.ll b/llvm/test/CodeGen/RISCV/redundant-copy-from-tail-duplicate.ll new file mode 100644 index 000000000000..3d367ddc59bc --- /dev/null +++ b/llvm/test/CodeGen/RISCV/redundant-copy-from-tail-duplicate.ll @@ -0,0 +1,50 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 4 +; RUN: llc < %s -mtriple=riscv64 -mattr=+v | FileCheck %s + + +define signext i32 @sum(ptr %a, i32 signext %n, i1 %prof.min.iters.check, %0, %1) { +; CHECK-LABEL: sum: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: andi a2, a2, 1 +; CHECK-NEXT: beqz a2, .LBB0_4 +; CHECK-NEXT: # %bb.1: # %for.body.preheader +; CHECK-NEXT: li a3, 0 +; CHECK-NEXT: .LBB0_2: # %for.body +; CHECK-NEXT: # =>This Inner Loop Header: Depth=1 +; CHECK-NEXT: mv a2, a3 +; CHECK-NEXT: lw a3, 0(a0) +; CHECK-NEXT: addi a0, a0, 4 +; CHECK-NEXT: bnez a1, .LBB0_2 +; CHECK-NEXT: # %bb.3: # %for.end +; CHECK-NEXT: mv a0, a2 +; CHECK-NEXT: ret +; CHECK-NEXT: .LBB0_4: # %vector.ph +; CHECK-NEXT: vsetvli a0, zero, e32, m4, ta, ma +; CHECK-NEXT: vmv.s.x v8, zero +; CHECK-NEXT: vmv.v.i v12, 0 +; CHECK-NEXT: vsetivli zero, 1, e32, m4, ta, ma +; CHECK-NEXT: vredsum.vs v8, v12, v8, v0.t +; CHECK-NEXT: vmv.x.s a0, v8 +; CHECK-NEXT: ret +entry: + br i1 %prof.min.iters.check, label %for.body, label %vector.ph + +vector.ph: ; preds = %entry + %2 = tail call i32 @llvm.vp.reduce.add.nxv8i32(i32 0, zeroinitializer, %0, i32 1) + br label %for.end + +for.body: ; preds = %for.body, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.body ], [ 0, %entry ] + %red.05 = phi i32 [ %3, %for.body ], [ 0, %entry ] + %arrayidx = getelementptr i32, ptr %a, i64 %indvars.iv + %3 = load i32, ptr %arrayidx, align 4 + %indvars.iv.next = add i64 %indvars.iv, 1 + %exitcond.not = icmp eq i32 %n, 0 + br i1 %exitcond.not, label %for.end, label %for.body + +for.end: ; preds = %for.body, %vector.ph + %red.0.lcssa = phi i32 [ %2, %vector.ph ], [ %red.05, %for.body ] + ret i32 %red.0.lcssa +} + +declare i32 @llvm.vp.reduce.add.nxv8i32(i32, , , i32) diff --git a/llvm/test/CodeGen/RISCV/renamable-copy.mir b/llvm/test/CodeGen/RISCV/renamable-copy.mir new file mode 100644 index 000000000000..06f17f4edbcc --- /dev/null +++ b/llvm/test/CodeGen/RISCV/renamable-copy.mir @@ -0,0 +1,31 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +# RUN: llc -o - %s -mtriple=riscv32 -simplify-mir \ +# RUN: -run-pass=postrapseudos | FileCheck --check-prefix=RV32 %s +# RUN: llc -o - %s -mtriple=riscv64 -simplify-mir \ +# RUN: -run-pass=postrapseudos | FileCheck --check-prefix=RV64 %s + +--- | + define void @foo() { + entry: + ret void + } +... +--- +name: foo +body: | + bb.0.entry: + liveins: $x11 + ; RV32-LABEL: name: foo + ; RV32: liveins: $x11 + ; RV32-NEXT: {{ $}} + ; RV32-NEXT: $x10 = ADDI renamable $x11, 0 + ; RV32-NEXT: PseudoRET implicit $x10 + ; + ; RV64-LABEL: name: foo + ; RV64: liveins: $x11 + ; RV64-NEXT: {{ $}} + ; RV64-NEXT: $x10 = ADDI renamable $x11, 0 + ; RV64-NEXT: PseudoRET implicit $x10 + renamable $x10 = COPY renamable $x11 + PseudoRET implicit $x10 +... diff --git a/llvm/test/CodeGen/Thumb2/mve-fpclamptosat_vec.ll b/llvm/test/CodeGen/Thumb2/mve-fpclamptosat_vec.ll index 94210d795867..f2ac52689218 100644 --- a/llvm/test/CodeGen/Thumb2/mve-fpclamptosat_vec.ll +++ b/llvm/test/CodeGen/Thumb2/mve-fpclamptosat_vec.ll @@ -1012,60 +1012,21 @@ entry: define arm_aapcs_vfpcc <2 x i64> @stest_f16i64(<2 x half> %x) { ; CHECK-LABEL: stest_f16i64: ; CHECK: @ %bb.0: @ %entry -; CHECK-NEXT: .save {r4, r5, r6, r7, r8, r9, r10, lr} -; CHECK-NEXT: push.w {r4, r5, r6, r7, r8, r9, r10, lr} +; CHECK-NEXT: .save {r4, r5, r7, lr} +; CHECK-NEXT: push {r4, r5, r7, lr} ; CHECK-NEXT: .vsave {d8, d9} ; CHECK-NEXT: vpush {d8, d9} ; CHECK-NEXT: vmov.u16 r0, q0[1] ; CHECK-NEXT: vmov q4, q0 ; CHECK-NEXT: bl __fixhfti -; CHECK-NEXT: subs.w r7, r0, #-1 -; CHECK-NEXT: mvn r9, #-2147483648 -; CHECK-NEXT: sbcs.w r7, r1, r9 -; CHECK-NEXT: mov.w r10, #-2147483648 -; CHECK-NEXT: sbcs r7, r2, #0 -; CHECK-NEXT: sbcs r7, r3, #0 -; CHECK-NEXT: cset r7, lt -; CHECK-NEXT: cmp r7, #0 -; CHECK-NEXT: csel r3, r3, r7, ne -; CHECK-NEXT: csel r2, r2, r7, ne -; CHECK-NEXT: mov.w r7, #-1 -; CHECK-NEXT: csel r1, r1, r9, ne -; CHECK-NEXT: csel r4, r0, r7, ne -; CHECK-NEXT: rsbs r0, r4, #0 -; CHECK-NEXT: sbcs.w r0, r10, r1 -; CHECK-NEXT: sbcs.w r0, r7, r2 -; CHECK-NEXT: sbcs.w r0, r7, r3 -; CHECK-NEXT: cset r5, lt +; CHECK-NEXT: mov r4, r0 ; CHECK-NEXT: vmov.u16 r0, q4[0] -; CHECK-NEXT: cmp r5, #0 -; CHECK-NEXT: csel r8, r1, r10, ne +; CHECK-NEXT: mov r5, r1 ; CHECK-NEXT: bl __fixhfti -; CHECK-NEXT: subs.w r6, r0, #-1 -; CHECK-NEXT: sbcs.w r6, r1, r9 -; CHECK-NEXT: sbcs r6, r2, #0 -; CHECK-NEXT: sbcs r6, r3, #0 -; CHECK-NEXT: cset r6, lt -; CHECK-NEXT: cmp r6, #0 -; CHECK-NEXT: csel r0, r0, r7, ne -; CHECK-NEXT: csel r1, r1, r9, ne -; CHECK-NEXT: csel r3, r3, r6, ne -; CHECK-NEXT: csel r2, r2, r6, ne -; CHECK-NEXT: rsbs r6, r0, #0 -; CHECK-NEXT: sbcs.w r6, r10, r1 -; CHECK-NEXT: sbcs.w r2, r7, r2 -; CHECK-NEXT: sbcs.w r2, r7, r3 -; CHECK-NEXT: cset r2, lt -; CHECK-NEXT: cmp r2, #0 -; CHECK-NEXT: csel r1, r1, r10, ne -; CHECK-NEXT: cmp r5, #0 -; CHECK-NEXT: csel r3, r4, r5, ne -; CHECK-NEXT: cmp r2, #0 -; CHECK-NEXT: csel r0, r0, r2, ne -; CHECK-NEXT: vmov q0[2], q0[0], r0, r3 -; CHECK-NEXT: vmov q0[3], q0[1], r1, r8 +; CHECK-NEXT: vmov q0[2], q0[0], r0, r4 +; CHECK-NEXT: vmov q0[3], q0[1], r1, r5 ; CHECK-NEXT: vpop {d8, d9} -; CHECK-NEXT: pop.w {r4, r5, r6, r7, r8, r9, r10, pc} +; CHECK-NEXT: pop {r4, r5, r7, pc} entry: %conv = fptosi <2 x half> %x to <2 x i128> %0 = icmp slt <2 x i128> %conv, @@ -1105,46 +1066,28 @@ entry: define arm_aapcs_vfpcc <2 x i64> @ustest_f16i64(<2 x half> %x) { ; CHECK-LABEL: ustest_f16i64: ; CHECK: @ %bb.0: @ %entry -; CHECK-NEXT: .save {r4, r5, r6, r7, r8, r9, lr} -; CHECK-NEXT: push.w {r4, r5, r6, r7, r8, r9, lr} -; CHECK-NEXT: .pad #4 -; CHECK-NEXT: sub sp, #4 +; CHECK-NEXT: .save {r4, r5, r6, r7, r8, lr} +; CHECK-NEXT: push.w {r4, r5, r6, r7, r8, lr} ; CHECK-NEXT: .vsave {d8, d9} ; CHECK-NEXT: vpush {d8, d9} ; CHECK-NEXT: vmov.u16 r0, q0[1] ; CHECK-NEXT: vmov q4, q0 ; CHECK-NEXT: bl __fixhfti -; CHECK-NEXT: subs r5, r2, #1 -; CHECK-NEXT: mov.w r8, #1 -; CHECK-NEXT: sbcs r5, r3, #0 -; CHECK-NEXT: mov.w r7, #0 -; CHECK-NEXT: cset r5, lt -; CHECK-NEXT: cmp r5, #0 -; CHECK-NEXT: csel r0, r0, r5, ne -; CHECK-NEXT: csel r3, r3, r5, ne -; CHECK-NEXT: csel r2, r2, r8, ne -; CHECK-NEXT: csel r4, r1, r5, ne +; CHECK-NEXT: mov r4, r1 ; CHECK-NEXT: rsbs r1, r0, #0 -; CHECK-NEXT: sbcs.w r1, r7, r4 -; CHECK-NEXT: sbcs.w r1, r7, r2 -; CHECK-NEXT: sbcs.w r1, r7, r3 +; CHECK-NEXT: mov.w r5, #0 +; CHECK-NEXT: sbcs.w r1, r5, r4 +; CHECK-NEXT: sbcs.w r1, r5, r2 +; CHECK-NEXT: sbcs.w r1, r5, r3 ; CHECK-NEXT: cset r6, lt ; CHECK-NEXT: cmp r6, #0 -; CHECK-NEXT: csel r9, r0, r6, ne +; CHECK-NEXT: csel r8, r0, r6, ne ; CHECK-NEXT: vmov.u16 r0, q4[0] ; CHECK-NEXT: bl __fixhfti -; CHECK-NEXT: subs r5, r2, #1 -; CHECK-NEXT: sbcs r5, r3, #0 -; CHECK-NEXT: cset r5, lt -; CHECK-NEXT: cmp r5, #0 -; CHECK-NEXT: csel r0, r0, r5, ne -; CHECK-NEXT: csel r2, r2, r8, ne -; CHECK-NEXT: csel r3, r3, r5, ne -; CHECK-NEXT: csel r1, r1, r5, ne -; CHECK-NEXT: rsbs r5, r0, #0 -; CHECK-NEXT: sbcs.w r5, r7, r1 -; CHECK-NEXT: sbcs.w r2, r7, r2 -; CHECK-NEXT: sbcs.w r2, r7, r3 +; CHECK-NEXT: rsbs r7, r0, #0 +; CHECK-NEXT: sbcs.w r7, r5, r1 +; CHECK-NEXT: sbcs.w r2, r5, r2 +; CHECK-NEXT: sbcs.w r2, r5, r3 ; CHECK-NEXT: cset r2, lt ; CHECK-NEXT: cmp r2, #0 ; CHECK-NEXT: csel r0, r0, r2, ne @@ -1152,11 +1095,10 @@ define arm_aapcs_vfpcc <2 x i64> @ustest_f16i64(<2 x half> %x) { ; CHECK-NEXT: csel r3, r4, r6, ne ; CHECK-NEXT: cmp r2, #0 ; CHECK-NEXT: csel r1, r1, r2, ne -; CHECK-NEXT: vmov q0[2], q0[0], r0, r9 +; CHECK-NEXT: vmov q0[2], q0[0], r0, r8 ; CHECK-NEXT: vmov q0[3], q0[1], r1, r3 ; CHECK-NEXT: vpop {d8, d9} -; CHECK-NEXT: add sp, #4 -; CHECK-NEXT: pop.w {r4, r5, r6, r7, r8, r9, pc} +; CHECK-NEXT: pop.w {r4, r5, r6, r7, r8, pc} entry: %conv = fptosi <2 x half> %x to <2 x i128> %0 = icmp slt <2 x i128> %conv, @@ -2119,60 +2061,21 @@ entry: define arm_aapcs_vfpcc <2 x i64> @stest_f16i64_mm(<2 x half> %x) { ; CHECK-LABEL: stest_f16i64_mm: ; CHECK: @ %bb.0: @ %entry -; CHECK-NEXT: .save {r4, r5, r6, r7, r8, r9, r10, lr} -; CHECK-NEXT: push.w {r4, r5, r6, r7, r8, r9, r10, lr} +; CHECK-NEXT: .save {r4, r5, r7, lr} +; CHECK-NEXT: push {r4, r5, r7, lr} ; CHECK-NEXT: .vsave {d8, d9} ; CHECK-NEXT: vpush {d8, d9} ; CHECK-NEXT: vmov.u16 r0, q0[1] ; CHECK-NEXT: vmov q4, q0 ; CHECK-NEXT: bl __fixhfti -; CHECK-NEXT: subs.w r7, r0, #-1 -; CHECK-NEXT: mvn r9, #-2147483648 -; CHECK-NEXT: sbcs.w r7, r1, r9 -; CHECK-NEXT: mov.w r10, #-2147483648 -; CHECK-NEXT: sbcs r7, r2, #0 -; CHECK-NEXT: sbcs r7, r3, #0 -; CHECK-NEXT: cset r7, lt -; CHECK-NEXT: cmp r7, #0 -; CHECK-NEXT: csel r3, r3, r7, ne -; CHECK-NEXT: csel r2, r2, r7, ne -; CHECK-NEXT: mov.w r7, #-1 -; CHECK-NEXT: csel r1, r1, r9, ne -; CHECK-NEXT: csel r4, r0, r7, ne -; CHECK-NEXT: rsbs r0, r4, #0 -; CHECK-NEXT: sbcs.w r0, r10, r1 -; CHECK-NEXT: sbcs.w r0, r7, r2 -; CHECK-NEXT: sbcs.w r0, r7, r3 -; CHECK-NEXT: cset r5, lt +; CHECK-NEXT: mov r4, r0 ; CHECK-NEXT: vmov.u16 r0, q4[0] -; CHECK-NEXT: cmp r5, #0 -; CHECK-NEXT: csel r8, r1, r10, ne +; CHECK-NEXT: mov r5, r1 ; CHECK-NEXT: bl __fixhfti -; CHECK-NEXT: subs.w r6, r0, #-1 -; CHECK-NEXT: sbcs.w r6, r1, r9 -; CHECK-NEXT: sbcs r6, r2, #0 -; CHECK-NEXT: sbcs r6, r3, #0 -; CHECK-NEXT: cset r6, lt -; CHECK-NEXT: cmp r6, #0 -; CHECK-NEXT: csel r0, r0, r7, ne -; CHECK-NEXT: csel r1, r1, r9, ne -; CHECK-NEXT: csel r3, r3, r6, ne -; CHECK-NEXT: csel r2, r2, r6, ne -; CHECK-NEXT: rsbs r6, r0, #0 -; CHECK-NEXT: sbcs.w r6, r10, r1 -; CHECK-NEXT: sbcs.w r2, r7, r2 -; CHECK-NEXT: sbcs.w r2, r7, r3 -; CHECK-NEXT: cset r2, lt -; CHECK-NEXT: cmp r2, #0 -; CHECK-NEXT: csel r1, r1, r10, ne -; CHECK-NEXT: cmp r5, #0 -; CHECK-NEXT: csel r3, r4, r5, ne -; CHECK-NEXT: cmp r2, #0 -; CHECK-NEXT: csel r0, r0, r2, ne -; CHECK-NEXT: vmov q0[2], q0[0], r0, r3 -; CHECK-NEXT: vmov q0[3], q0[1], r1, r8 +; CHECK-NEXT: vmov q0[2], q0[0], r0, r4 +; CHECK-NEXT: vmov q0[3], q0[1], r1, r5 ; CHECK-NEXT: vpop {d8, d9} -; CHECK-NEXT: pop.w {r4, r5, r6, r7, r8, r9, r10, pc} +; CHECK-NEXT: pop {r4, r5, r7, pc} entry: %conv = fptosi <2 x half> %x to <2 x i128> %spec.store.select = call <2 x i128> @llvm.smin.v2i128(<2 x i128> %conv, <2 x i128> ) @@ -2209,51 +2112,34 @@ entry: define arm_aapcs_vfpcc <2 x i64> @ustest_f16i64_mm(<2 x half> %x) { ; CHECK-LABEL: ustest_f16i64_mm: ; CHECK: @ %bb.0: @ %entry -; CHECK-NEXT: .save {r4, r5, r6, r7, lr} -; CHECK-NEXT: push {r4, r5, r6, r7, lr} -; CHECK-NEXT: .pad #4 -; CHECK-NEXT: sub sp, #4 +; CHECK-NEXT: .save {r4, r5, r6, lr} +; CHECK-NEXT: push {r4, r5, r6, lr} ; CHECK-NEXT: .vsave {d8, d9} ; CHECK-NEXT: vpush {d8, d9} ; CHECK-NEXT: vmov.u16 r0, q0[1] ; CHECK-NEXT: vmov q4, q0 ; CHECK-NEXT: bl __fixhfti -; CHECK-NEXT: mov r4, r1 -; CHECK-NEXT: subs r1, r2, #1 -; CHECK-NEXT: sbcs r1, r3, #0 -; CHECK-NEXT: cset r6, lt -; CHECK-NEXT: cmp r6, #0 -; CHECK-NEXT: csel r5, r0, r6, ne -; CHECK-NEXT: csel r7, r3, r6, ne +; CHECK-NEXT: mov r4, r0 ; CHECK-NEXT: vmov.u16 r0, q4[0] -; CHECK-NEXT: cmp r7, #0 -; CHECK-NEXT: it mi -; CHECK-NEXT: movmi r5, #0 +; CHECK-NEXT: mov r5, r1 +; CHECK-NEXT: mov r6, r3 ; CHECK-NEXT: bl __fixhfti -; CHECK-NEXT: subs r2, #1 -; CHECK-NEXT: sbcs r2, r3, #0 -; CHECK-NEXT: cset r2, lt -; CHECK-NEXT: cmp r2, #0 -; CHECK-NEXT: csel r3, r3, r2, ne -; CHECK-NEXT: csel r0, r0, r2, ne +; CHECK-NEXT: cmp r6, #0 +; CHECK-NEXT: it mi +; CHECK-NEXT: movmi r4, #0 ; CHECK-NEXT: cmp r3, #0 ; CHECK-NEXT: it mi ; CHECK-NEXT: movmi r0, #0 ; CHECK-NEXT: cmp r6, #0 -; CHECK-NEXT: vmov q0[2], q0[0], r0, r5 -; CHECK-NEXT: csel r6, r4, r6, ne -; CHECK-NEXT: cmp r7, #0 +; CHECK-NEXT: vmov q0[2], q0[0], r0, r4 ; CHECK-NEXT: it mi -; CHECK-NEXT: movmi r6, #0 -; CHECK-NEXT: cmp r2, #0 -; CHECK-NEXT: csel r1, r1, r2, ne +; CHECK-NEXT: movmi r5, #0 ; CHECK-NEXT: cmp r3, #0 ; CHECK-NEXT: it mi ; CHECK-NEXT: movmi r1, #0 -; CHECK-NEXT: vmov q0[3], q0[1], r1, r6 +; CHECK-NEXT: vmov q0[3], q0[1], r1, r5 ; CHECK-NEXT: vpop {d8, d9} -; CHECK-NEXT: add sp, #4 -; CHECK-NEXT: pop {r4, r5, r6, r7, pc} +; CHECK-NEXT: pop {r4, r5, r6, pc} entry: %conv = fptosi <2 x half> %x to <2 x i128> %spec.store.select = call <2 x i128> @llvm.smin.v2i128(<2 x i128> %conv, <2 x i128> ) diff --git a/llvm/test/DebugInfo/Generic/lit.local.cfg b/llvm/test/DebugInfo/Generic/lit.local.cfg index 7ce6a3d39231..c4d57ec41426 100644 --- a/llvm/test/DebugInfo/Generic/lit.local.cfg +++ b/llvm/test/DebugInfo/Generic/lit.local.cfg @@ -1,4 +1,4 @@ if not config.target_triple: config.unsupported = True -elif config.target_triple.startswith(("nvptx", "xcore")): +elif config.target_triple.startswith(("nvptx", "evm", "xcore")): config.unsupported = True diff --git a/llvm/test/Feature/optnone-llc.ll b/llvm/test/Feature/optnone-llc.ll index 7e6678f9cdc4..cf9801756b4f 100644 --- a/llvm/test/Feature/optnone-llc.ll +++ b/llvm/test/Feature/optnone-llc.ll @@ -7,6 +7,7 @@ ; REQUIRES: asserts, default_triple ; UNSUPPORTED: target=nvptx{{.*}} +; XFAIL: target=evm{{.*}} ; This test verifies that we don't run Machine Function optimizations ; on optnone functions, and that we can turn off FastISel. diff --git a/llvm/test/Linker/subprogram-linkonce-weak.ll b/llvm/test/Linker/subprogram-linkonce-weak.ll index 0d4eae90cfa1..e8e2b242c4f5 100644 --- a/llvm/test/Linker/subprogram-linkonce-weak.ll +++ b/llvm/test/Linker/subprogram-linkonce-weak.ll @@ -6,6 +6,7 @@ ; ; Bug 47131 ; XFAIL: target=sparc{{.*}} +; XFAIL: target=evm{{.*}} ; ; This testcase tests the following flow: ; - File A defines a linkonce version of @foo which has inlined into @bar. diff --git a/llvm/test/MC/AsmParser/include.ll b/llvm/test/MC/AsmParser/include.ll index 3321f0a6a287..733985d59b78 100644 --- a/llvm/test/MC/AsmParser/include.ll +++ b/llvm/test/MC/AsmParser/include.ll @@ -2,6 +2,9 @@ ; UNSUPPORTED: target={{.*}}-zos{{.*}},target=nvptx{{.*}} ; REQUIRES: default_triple +; EVM/EVM doesn't support inline asm (yet). +; XFAIL: target=evm{{.*}}, target=evm{{.*}} + module asm ".include \22module.x\22" define void @f() { diff --git a/llvm/test/MC/EVM/data-linker-symbol-relocs.ll b/llvm/test/MC/EVM/data-linker-symbol-relocs.ll new file mode 100644 index 000000000000..efac51802a69 --- /dev/null +++ b/llvm/test/MC/EVM/data-linker-symbol-relocs.ll @@ -0,0 +1,52 @@ +; RUN: llc -O2 -filetype=obj --mtriple=evm %s -o - | llvm-objdump -r --headers - | FileCheck %s + +; CHECK: .symbol_name__linker_symbol__$65c539c969b08d62684b6c973d3f0f4f37a85c2d0cbe39fffa3022cb69aac23a$__ 0000000b + +; CHECK: RELOCATION RECORDS FOR [.text]: +; CHECK-NEXT: OFFSET TYPE VALUE +; CHECK-NEXT: {{d*}} R_EVM_DATA __linker_symbol__$65c539c969b08d62684b6c973d3f0f4f37a85c2d0cbe39fffa3022cb69aac23a$__0 +; CHECK-NEXT: {{d*}} R_EVM_DATA __linker_symbol__$65c539c969b08d62684b6c973d3f0f4f37a85c2d0cbe39fffa3022cb69aac23a$__1 +; CHECK-NEXT: {{d*}} R_EVM_DATA __linker_symbol__$65c539c969b08d62684b6c973d3f0f4f37a85c2d0cbe39fffa3022cb69aac23a$__2 +; CHECK-NEXT: {{d*}} R_EVM_DATA __linker_symbol__$65c539c969b08d62684b6c973d3f0f4f37a85c2d0cbe39fffa3022cb69aac23a$__3 +; CHECK-NEXT: {{d*}} R_EVM_DATA __linker_symbol__$65c539c969b08d62684b6c973d3f0f4f37a85c2d0cbe39fffa3022cb69aac23a$__4 +; CHECK-NEXT: {{d*}} R_EVM_DATA __dataoffset__$0336fd807c0716a535e520df5b63ecc41ba7984875fdfa2241fcf3c8d0107e26$__ +; CHECK-NEXT: {{d*}} R_EVM_DATA __datasize__$0336fd807c0716a535e520df5b63ecc41ba7984875fdfa2241fcf3c8d0107e26$__ +; CHECK-NEXT: {{d*}} R_EVM_DATA __linker_symbol__$65c539c969b08d62684b6c973d3f0f4f37a85c2d0cbe39fffa3022cb69aac23a$__0 +; CHECK-NEXT: {{d*}} R_EVM_DATA __linker_symbol__$65c539c969b08d62684b6c973d3f0f4f37a85c2d0cbe39fffa3022cb69aac23a$__1 +; CHECK-NEXT: {{d*}} R_EVM_DATA __linker_symbol__$65c539c969b08d62684b6c973d3f0f4f37a85c2d0cbe39fffa3022cb69aac23a$__2 +; CHECK-NEXT: {{d*}} R_EVM_DATA __linker_symbol__$65c539c969b08d62684b6c973d3f0f4f37a85c2d0cbe39fffa3022cb69aac23a$__3 +; CHECK-NEXT: {{d*}} R_EVM_DATA __linker_symbol__$65c539c969b08d62684b6c973d3f0f4f37a85c2d0cbe39fffa3022cb69aac23a$__4 +; CHECK-NEXT: {{d*}} R_EVM_DATA __dataoffset__$0336fd807c0716a535e520df5b63ecc41ba7984875fdfa2241fcf3c8d0107e26$__ +; CHECK-NEXT: {{d*}} R_EVM_DATA __datasize__$0336fd807c0716a535e520df5b63ecc41ba7984875fdfa2241fcf3c8d0107e26$__ + +; TODO: CRP-1575. Rewrite the test in assembly. +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +; Function Attrs: nounwind +declare i256 @llvm.evm.datasize(metadata) +declare i256 @llvm.evm.dataoffset(metadata) +declare i256 @llvm.evm.linkersymbol(metadata) + +define i256 @foo() { +entry: + %deployed_size = tail call i256 @llvm.evm.datasize(metadata !1) + %deployed_off = tail call i256 @llvm.evm.dataoffset(metadata !1) + %lib_addr = call i256 @llvm.evm.linkersymbol(metadata !2) + %tmp = sub i256 %deployed_size, %deployed_off + %res = sub i256 %tmp, %lib_addr + ret i256 %res +} + +define i256 @bar() { +entry: + %deployed_size = tail call i256 @llvm.evm.datasize(metadata !1) + %deployed_off = tail call i256 @llvm.evm.dataoffset(metadata !1) + %lib_addr = call i256 @llvm.evm.linkersymbol(metadata !2) + %tmp = sub i256 %deployed_size, %deployed_off + %res = sub i256 %tmp, %lib_addr + ret i256 %res +} + +!1 = !{!"D_105_deployed"} +!2 = !{!"library_id2"} diff --git a/llvm/test/MC/EVM/glob-symbol-with-offset.ll b/llvm/test/MC/EVM/glob-symbol-with-offset.ll new file mode 100644 index 000000000000..53688f986bf4 --- /dev/null +++ b/llvm/test/MC/EVM/glob-symbol-with-offset.ll @@ -0,0 +1,36 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc -filetype=obj %s -o - | llvm-objdump --disassemble - | FileCheck %s +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +@data1 = private unnamed_addr addrspace(4) constant [13 x i8] c"hello world\0A\00" +@data2 = private unnamed_addr addrspace(4) constant [5 x i8] c"world" + +declare void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(4) noalias nocapture readonly, i256, i1 immarg) + +define void @test() noreturn { + +; CHECK: 00000000 : +; CHECK-NEXT: 0: 5b JUMPDEST +; CHECK-NEXT: 1: 60 0d PUSH1 0xD +; CHECK-NEXT: 3: 60 13 PUSH1 0x13 +; CHECK-NEXT: 5: 5f PUSH0 +; CHECK-NEXT: 6: 39 CODECOPY +; CHECK-NEXT: 7: 60 05 PUSH1 0x5 +; CHECK-NEXT: 9: 60 19 PUSH1 0x19 +; CHECK-NEXT: b: 60 40 PUSH1 0x40 +; CHECK-NEXT: d: 39 CODECOPY +; CHECK-NEXT: e: 60 80 PUSH1 0x80 +; CHECK-NEXT: 10: 5f PUSH0 +; CHECK-NEXT: 11: fd REVERT +; CHECK-NEXT: 12: fe INVALID +; CHECK: 00000013 : +; CHECK-NEXT: 13: 68 65 6c 6c 6f 20 77 6f 72 6c +; CHECK-NEXT: 1d: 64 0a 00 + + call void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) null, ptr addrspace(4) @data1, i256 13, i1 false) + %dst = inttoptr i256 64 to ptr addrspace(1) + call void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) %dst, ptr addrspace(4) @data2, i256 5, i1 false) + call void @llvm.evm.revert(ptr addrspace(1) null, i256 128) + unreachable +} diff --git a/llvm/test/MC/EVM/imm-hex-output.ll b/llvm/test/MC/EVM/imm-hex-output.ll new file mode 100644 index 000000000000..9083715cc9ad --- /dev/null +++ b/llvm/test/MC/EVM/imm-hex-output.ll @@ -0,0 +1,23 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc --mtriple=evm %s -o - | FileCheck %s + +; CHECK-NOT: .note.GNU-stack + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare void @llvm.evm.return(ptr addrspace(1), i256) + +define void @foo() { +; CHECK-LABEL: foo: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH4 0xFFFFFFFF +; CHECK-NEXT: PUSH1 0xE0 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH1 0x40 +; CHECK-NEXT: RETURN + tail call void @llvm.evm.return( ptr addrspace(1) inttoptr (i256 64 to ptr addrspace(1)), i256 -26959946667150639794667015087019630673637144422540572481103610249216) + unreachable +} diff --git a/llvm/test/MC/EVM/immutable-symbols-clashing.ll b/llvm/test/MC/EVM/immutable-symbols-clashing.ll new file mode 100644 index 000000000000..933f6eedc833 --- /dev/null +++ b/llvm/test/MC/EVM/immutable-symbols-clashing.ll @@ -0,0 +1,15 @@ +; RUN: not --crash llc -O2 -filetype=obj --mtriple=evm %s 2>&1 | FileCheck %s + +; CHECK: LLVM ERROR: MC: duplicating immutable label __load_immutable__imm_id.1 + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare i256 @llvm.evm.loadimmutable(metadata) + +define i256 @__load_immutable__imm_id.1() { + %ret = call i256 @llvm.evm.loadimmutable(metadata !1) + ret i256 %ret +} + +!1 = !{!"imm_id"} diff --git a/llvm/test/MC/EVM/immutable-symbols.ll b/llvm/test/MC/EVM/immutable-symbols.ll new file mode 100644 index 000000000000..0471b94f0897 --- /dev/null +++ b/llvm/test/MC/EVM/immutable-symbols.ll @@ -0,0 +1,22 @@ +; RUN: llc -O2 -filetype=obj --mtriple=evm %s -o - | llvm-objdump --syms - | FileCheck %s + +; CHECK: SYMBOL TABLE: +; CHECK: {{d*}} l .text 00000000 __load_immutable__imm_id.1 +; CHECK: {{d*}} l .text 00000000 __load_immutable__imm_id.2 + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare i256 @llvm.evm.loadimmutable(metadata) + +define i256 @foo() { + %ret = call i256 @llvm.evm.loadimmutable(metadata !1) + ret i256 %ret +} + +define i256 @bar() { + %ret = call i256 @llvm.evm.loadimmutable(metadata !1) + ret i256 %ret +} + +!1 = !{!"imm_id"} diff --git a/llvm/test/MC/EVM/invalid-instr.ll b/llvm/test/MC/EVM/invalid-instr.ll new file mode 100644 index 000000000000..d4acef60d8a0 --- /dev/null +++ b/llvm/test/MC/EVM/invalid-instr.ll @@ -0,0 +1,15 @@ +; RUN: llc -O2 -filetype=obj --mtriple=evm %s -o - | llvm-objdump --no-leading-addr --disassemble - | FileCheck %s + +; CHECK-LABEL: : +; CHECK: fe INVALID + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare void @llvm.evm.return(ptr addrspace(1), i256) + +define void @test() noreturn { +entry: + tail call void @llvm.evm.return(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 null, i256 0) + unreachable +} diff --git a/llvm/test/MC/EVM/push-deploy-address-not-used.ll b/llvm/test/MC/EVM/push-deploy-address-not-used.ll new file mode 100644 index 000000000000..44d2154e0e68 --- /dev/null +++ b/llvm/test/MC/EVM/push-deploy-address-not-used.ll @@ -0,0 +1,31 @@ +; RUN: llc -O2 -filetype=obj --mtriple=evm %s -o - | llvm-objdump --no-leading-addr --disassemble - | FileCheck %s + +; CHECK-LABEL: : +; CHECK-NEXT: 73 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 PUSH20 0 + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare i256 @llvm.evm.pushdeployaddress() +declare i256 @llvm.evm.address() +declare void @llvm.evm.revert(ptr addrspace(1), i256) +declare void @llvm.evm.return(ptr addrspace(1), i256) + +define void @test() noreturn { +entry: + store i256 128, ptr addrspace(1) inttoptr (i256 64 to ptr addrspace(1)), align 64 + %deploy_addr = tail call i256 @llvm.evm.pushdeployaddress() + %curr_addr = tail call i256 @llvm.evm.address() + %cmp = icmp eq i256 %curr_addr, 0 + br i1 %cmp, label %exit, label %error + +error: + tail call void @llvm.evm.revert(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 null, i256 0) + unreachable + +exit: + tail call void @llvm.evm.return(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 null, i256 0) + unreachable +} + +!1 = !{!"imm_id"} diff --git a/llvm/test/MC/EVM/push-deploy-address.ll b/llvm/test/MC/EVM/push-deploy-address.ll new file mode 100644 index 000000000000..5694faf6a68c --- /dev/null +++ b/llvm/test/MC/EVM/push-deploy-address.ll @@ -0,0 +1,37 @@ +; RUN: llc -O2 -filetype=obj --mtriple=evm %s -o - | llvm-objdump --no-leading-addr --disassemble - | FileCheck %s + +; CHECK-LABEL: : +; CHECK-NEXT: 73 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 PUSH20 0 + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare i256 @llvm.evm.calldatasize() +declare i256 @llvm.evm.pushdeployaddress() +declare i256 @llvm.evm.address() +declare void @llvm.evm.revert(ptr addrspace(1), i256) +declare void @llvm.evm.return(ptr addrspace(1), i256) + +define void @test() noreturn { +entry: + store i256 128, ptr addrspace(1) inttoptr (i256 64 to ptr addrspace(1)), align 64 + %curr_addr = tail call i256 @llvm.evm.address() + %calldatasize = tail call i256 @llvm.evm.calldatasize() + %cmp_calldata = icmp ult i256 %calldatasize, 4 + br i1 %cmp_calldata, label %error, label %check_deploy + +check_deploy: + %deploy_addr = tail call i256 @llvm.evm.pushdeployaddress() + %cmp = icmp eq i256 %deploy_addr, %curr_addr + br i1 %cmp, label %exit, label %error + +error: + tail call void @llvm.evm.revert(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 null, i256 0) + unreachable + +exit: + tail call void @llvm.evm.return(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 null, i256 0) + unreachable +} + +!1 = !{!"imm_id"} diff --git a/llvm/test/MC/EVM/undefined-symbol-error.ll b/llvm/test/MC/EVM/undefined-symbol-error.ll new file mode 100644 index 000000000000..af3e7ab4478a --- /dev/null +++ b/llvm/test/MC/EVM/undefined-symbol-error.ll @@ -0,0 +1,13 @@ +; RUN: not --crash llc -filetype=obj --mtriple=evm %s 2>&1 | FileCheck %s + +; CHECK: LLVM ERROR: unable to evaluate offset to undefined symbol 'foo' + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare void @foo() + +define void @bar() { + call void @foo() + ret void +} diff --git a/llvm/test/TableGen/VarLenDecoder.td b/llvm/test/TableGen/VarLenDecoder.td index 5cf0bf891185..3bf2ecd3a688 100644 --- a/llvm/test/TableGen/VarLenDecoder.td +++ b/llvm/test/TableGen/VarLenDecoder.td @@ -46,10 +46,10 @@ def FOO32 : MyVarInst { ); } -// CHECK: MCD::OPC_ExtractField, 3, 5, // Inst{7-3} ... -// CHECK-NEXT: MCD::OPC_FilterValue, 8, 4, 0, 0, // Skip to: 12 +// CHECK: MCD::OPC_ExtractField, 3, 5, 0, // Inst{7-3} ... +// CHECK-NEXT: MCD::OPC_FilterValue, 8, 4, 0, 0, // Skip to: 13 // CHECK-NEXT: MCD::OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 0, // Opcode: FOO16 -// CHECK-NEXT: MCD::OPC_FilterValue, 9, 4, 0, 0, // Skip to: 21 +// CHECK-NEXT: MCD::OPC_FilterValue, 9, 4, 0, 0, // Skip to: 22 // CHECK-NEXT: MCD::OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, // Opcode: FOO32 // CHECK-NEXT: MCD::OPC_Fail, diff --git a/llvm/test/TableGen/long-offset-decode.td b/llvm/test/TableGen/long-offset-decode.td new file mode 100644 index 000000000000..65a00fdc6922 --- /dev/null +++ b/llvm/test/TableGen/long-offset-decode.td @@ -0,0 +1,27 @@ +// RUN: llvm-tblgen -gen-disassembler -I %p/../../include %s | FileCheck %s + +// Checks decoding of long instructions with the bit width > 256. + +include "llvm/Target/Target.td" + +def archInstrInfo : InstrInfo { } + +def arch : Target { + let InstructionSet = archInstrInfo; +} + +class TestInstruction : Instruction { + let OutOperandList = (outs); + let InOperandList = (ins); + field bits<264> Inst; +} + +def InstA : TestInstruction { + let Size = 33; + let Inst{263-256} = 0x7E; + let AsmString = "InstA"; +} + +// CHECK: /* 0 */ MCD::OPC_CheckField, 128, 2, 8, 0, 126, 4, 0, 0, // Skip to: 13 +// CHECK-NEXT: /* 9 */ MCD::OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 0, // Opcode: InstA +// CHECK-NEXT: /* 13 */ MCD::OPC_Fail, diff --git a/llvm/test/TableGen/trydecode-emission.td b/llvm/test/TableGen/trydecode-emission.td index 20d2446eeac7..e7c3194e1f24 100644 --- a/llvm/test/TableGen/trydecode-emission.td +++ b/llvm/test/TableGen/trydecode-emission.td @@ -33,11 +33,11 @@ def InstB : TestInstruction { let hasCompleteDecoder = 0; } -// CHECK: /* 0 */ MCD::OPC_ExtractField, 4, 4, // Inst{7-4} ... -// CHECK-NEXT: /* 3 */ MCD::OPC_FilterValue, 0, 18, 0, 0, // Skip to: 26 -// CHECK-NEXT: /* 8 */ MCD::OPC_CheckField, 2, 2, 0, 7, 0, 0, // Skip to: 22 -// CHECK-NEXT: /* 15 */ MCD::OPC_TryDecode, {{[0-9]+}}, {{[0-9]+}}, 0, 0, 0, 0, // Opcode: InstB, skip to: 22 -// CHECK-NEXT: /* 22 */ MCD::OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, // Opcode: InstA -// CHECK-NEXT: /* 26 */ MCD::OPC_Fail, +// CHECK: /* 0 */ MCD::OPC_ExtractField, 4, 4, 0, // Inst{7-4} ... +// CHECK-NEXT: /* 4 */ MCD::OPC_FilterValue, 0, 19, 0, 0, // Skip to: 28 +// CHECK-NEXT: /* 9 */ MCD::OPC_CheckField, 2, 2, 0, 0, 7, 0, 0, // Skip to: 24 +// CHECK-NEXT: /* 17 */ MCD::OPC_TryDecode, {{[0-9]+}}, {{[0-9]+}}, 0, 0, 0, 0, // Opcode: InstB, skip to: 24 +// CHECK-NEXT: /* 24 */ MCD::OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, // Opcode: InstA +// CHECK-NEXT: /* 28 */ MCD::OPC_Fail, // CHECK: if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; } diff --git a/llvm/test/TableGen/trydecode-emission2.td b/llvm/test/TableGen/trydecode-emission2.td index 0584034e4123..012deec76d1e 100644 --- a/llvm/test/TableGen/trydecode-emission2.td +++ b/llvm/test/TableGen/trydecode-emission2.td @@ -30,15 +30,15 @@ def InstB : TestInstruction { let hasCompleteDecoder = 0; } -// CHECK: /* 0 */ MCD::OPC_ExtractField, 2, 1, // Inst{2} ... -// CHECK-NEXT: /* 3 */ MCD::OPC_FilterValue, 0, 36, 0, 0, // Skip to: 44 -// CHECK-NEXT: /* 8 */ MCD::OPC_ExtractField, 5, 3, // Inst{7-5} ... -// CHECK-NEXT: /* 11 */ MCD::OPC_FilterValue, 0, 28, 0, 0, // Skip to: 44 -// CHECK-NEXT: /* 16 */ MCD::OPC_CheckField, 0, 2, 3, 7, 0, 0, // Skip to: 30 -// CHECK-NEXT: /* 23 */ MCD::OPC_TryDecode, {{[0-9]+}}, {{[0-9]+}}, 0, 0, 0, 0, // Opcode: InstB, skip to: 30 -// CHECK-NEXT: /* 30 */ MCD::OPC_CheckField, 3, 2, 0, 7, 0, 0, // Skip to: 44 -// CHECK-NEXT: /* 37 */ MCD::OPC_TryDecode, {{[0-9]+}}, {{[0-9]+}}, 1, 0, 0, 0, // Opcode: InstA, skip to: 44 -// CHECK-NEXT: /* 44 */ MCD::OPC_Fail, +// CHECK: /* 0 */ MCD::OPC_ExtractField, 2, 1, 0, // Inst{2} ... +// CHECK-NEXT: /* 4 */ MCD::OPC_FilterValue, 0, 39, 0, 0, // Skip to: 48 +// CHECK-NEXT: /* 9 */ MCD::OPC_ExtractField, 5, 3, 0, // Inst{7-5} ... +// CHECK-NEXT: /* 13 */ MCD::OPC_FilterValue, 0, 30, 0, 0, // Skip to: 48 +// CHECK-NEXT: /* 18 */ MCD::OPC_CheckField, 0, 2, 0, 3, 7, 0, 0, // Skip to: 33 +// CHECK-NEXT: /* 26 */ MCD::OPC_TryDecode, {{[0-9]+}}, {{[0-9]+}}, 0, 0, 0, 0, // Opcode: InstB, skip to: 33 +// CHECK-NEXT: /* 33 */ MCD::OPC_CheckField, 3, 2, 0, 0, 7, 0, 0, // Skip to: 48 +// CHECK-NEXT: /* 41 */ MCD::OPC_TryDecode, {{[0-9]+}}, {{[0-9]+}}, 1, 0, 0, 0, // Opcode: InstA, skip to: 48 +// CHECK-NEXT: /* 48 */ MCD::OPC_Fail, // CHECK: if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; } // CHECK: if (!Check(S, DecodeInstA(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; } diff --git a/llvm/test/TableGen/trydecode-emission3.td b/llvm/test/TableGen/trydecode-emission3.td index 4c5be7e1af22..f986218f929a 100644 --- a/llvm/test/TableGen/trydecode-emission3.td +++ b/llvm/test/TableGen/trydecode-emission3.td @@ -34,11 +34,11 @@ def InstB : TestInstruction { let AsmString = "InstB"; } -// CHECK: /* 0 */ MCD::OPC_ExtractField, 4, 4, // Inst{7-4} ... -// CHECK-NEXT: /* 3 */ MCD::OPC_FilterValue, 0, 18, 0, 0, // Skip to: 26 -// CHECK-NEXT: /* 8 */ MCD::OPC_CheckField, 2, 2, 0, 7, 0, 0, // Skip to: 22 -// CHECK-NEXT: /* 15 */ MCD::OPC_TryDecode, {{[0-9]+}}, {{[0-9]+}}, 0, 0, 0, 0, // Opcode: InstB, skip to: 22 -// CHECK-NEXT: /* 22 */ MCD::OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, // Opcode: InstA -// CHECK-NEXT: /* 26 */ MCD::OPC_Fail, +// CHECK: /* 0 */ MCD::OPC_ExtractField, 4, 4, 0, // Inst{7-4} ... +// CHECK-NEXT: /* 4 */ MCD::OPC_FilterValue, 0, 19, 0, 0, // Skip to: 28 +// CHECK-NEXT: /* 9 */ MCD::OPC_CheckField, 2, 2, 0, 0, 7, 0, 0, // Skip to: 24 +// CHECK-NEXT: /* 17 */ MCD::OPC_TryDecode, {{[0-9]+}}, {{[0-9]+}}, 0, 0, 0, 0, // Opcode: InstB, skip to: 24 +// CHECK-NEXT: /* 24 */ MCD::OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, // Opcode: InstA +// CHECK-NEXT: /* 28 */ MCD::OPC_Fail, // CHECK: if (!Check(S, DecodeInstBOp(MI, tmp, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; } diff --git a/llvm/test/TableGen/trydecode-emission4.td b/llvm/test/TableGen/trydecode-emission4.td index 1e51ba5e4076..162405947865 100644 --- a/llvm/test/TableGen/trydecode-emission4.td +++ b/llvm/test/TableGen/trydecode-emission4.td @@ -33,12 +33,12 @@ def InstB : TestInstruction { } -// CHECK: /* 0 */ MCD::OPC_ExtractField, 250, 3, 4, // Inst{509-506} ... -// CHECK-NEXT: /* 4 */ MCD::OPC_FilterValue, 0, 19, 0, 0, // Skip to: 28 -// CHECK-NEXT: /* 9 */ MCD::OPC_CheckField, 248, 3, 2, 0, 7, 0, 0, // Skip to: 24 -// CHECK-NEXT: /* 17 */ MCD::OPC_TryDecode, {{[0-9]+}}, {{[0-9]+}}, 0, 0, 0, 0, // Opcode: InstB, skip to: 24 -// CHECK-NEXT: /* 24 */ MCD::OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, // Opcode: InstA -// CHECK-NEXT: /* 28 */ MCD::OPC_Fail, +// CHECK: /* 0 */ MCD::OPC_ExtractField, 250, 3, 4, 0, // Inst{509-506} ... +// CHECK-NEXT: /* 5 */ MCD::OPC_FilterValue, 0, 20, 0, 0, // Skip to: 30 +// CHECK-NEXT: /* 10 */ MCD::OPC_CheckField, 248, 3, 2, 0, 0, 7, 0, 0, // Skip to: 26 +// CHECK-NEXT: /* 19 */ MCD::OPC_TryDecode, {{[0-9]+}}, {{[0-9]+}}, 0, 0, 0, 0, // Opcode: InstB, skip to: 26 +// CHECK-NEXT: /* 26 */ MCD::OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, // Opcode: InstA +// CHECK-NEXT: /* 30 */ MCD::OPC_Fail, // CHECK: if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; } diff --git a/llvm/test/Transforms/BranchFolding/2007-10-19-InlineAsmDirectives.ll b/llvm/test/Transforms/BranchFolding/2007-10-19-InlineAsmDirectives.ll index 43fcc6051210..e50f90ad94b2 100644 --- a/llvm/test/Transforms/BranchFolding/2007-10-19-InlineAsmDirectives.ll +++ b/llvm/test/Transforms/BranchFolding/2007-10-19-InlineAsmDirectives.ll @@ -1,5 +1,6 @@ ; RUN: opt < %s -O3 | llc -no-integrated-as | FileCheck %s ; REQUIRES: default_triple +; UNSUPPORTED: target=evm{{.*}} ;; We don't want branch folding to fold asm directives. diff --git a/llvm/test/Transforms/ConstantHoisting/ARM/apint-assert.ll b/llvm/test/Transforms/ConstantHoisting/ARM/apint-assert.ll new file mode 100644 index 000000000000..6f165f53a57b --- /dev/null +++ b/llvm/test/Transforms/ConstantHoisting/ARM/apint-assert.ll @@ -0,0 +1,18 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -S -passes=consthoist -mtriple=armv4t-unknown-linux-gnueabi < %s | FileCheck %s + +define i1 @test(i32 %arg) optsize { +; CHECK-LABEL: define i1 @test( +; CHECK-SAME: i32 [[ARG:%.*]]) #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[CONST:%.*]] = bitcast i32 380633088 to i32 +; CHECK-NEXT: [[CONST_MAT:%.*]] = add i32 [[CONST]], -381681664 +; CHECK-NEXT: [[SHR_MASK:%.*]] = and i32 [[ARG]], [[CONST_MAT]] +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[SHR_MASK]], [[CONST]] +; CHECK-NEXT: ret i1 [[CMP]] +; +entry: + %shr.mask = and i32 %arg, -1048576 + %cmp = icmp eq i32 %shr.mask, 380633088 + ret i1 %cmp +} diff --git a/llvm/test/Transforms/CorrelatedValuePropagation/urem.ll b/llvm/test/Transforms/CorrelatedValuePropagation/urem.ll index ec6461e29f19..e69deaa73d73 100644 --- a/llvm/test/Transforms/CorrelatedValuePropagation/urem.ll +++ b/llvm/test/Transforms/CorrelatedValuePropagation/urem.ll @@ -462,4 +462,13 @@ join: ret i8 %res } +define i1 @urem_i1() { +; CHECK-LABEL: @urem_i1( +; CHECK-NEXT: [[REM:%.*]] = urem i1 false, false +; CHECK-NEXT: ret i1 [[REM]] +; + %rem = urem i1 false, false + ret i1 %rem +} + declare void @use(i1) diff --git a/llvm/test/Transforms/IndVarSimplify/pr102597.ll b/llvm/test/Transforms/IndVarSimplify/pr102597.ll new file mode 100644 index 000000000000..9de614524444 --- /dev/null +++ b/llvm/test/Transforms/IndVarSimplify/pr102597.ll @@ -0,0 +1,44 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -S -passes=indvars < %s | FileCheck %s + +; The %tobool condition should not be optimized away. +define void @test() { +; CHECK-LABEL: define void @test() { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: br label %[[LOOP:.*]] +; CHECK: [[LOOP]]: +; CHECK-NEXT: [[IV:%.*]] = phi i128 [ 3, %[[ENTRY]] ], [ [[IV_DEC:%.*]], %[[LOOP_LATCH:.*]] ] +; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i128 [[IV]], 0 +; CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOOP_LATCH]], label %[[IF:.*]] +; CHECK: [[IF]]: +; CHECK-NEXT: call void @foo() +; CHECK-NEXT: br label %[[LOOP_LATCH]] +; CHECK: [[LOOP_LATCH]]: +; CHECK-NEXT: [[IV_DEC]] = add nsw i128 [[IV]], -1 +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i128 [[IV]], 0 +; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT:.*]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret void +; +entry: + br label %loop + +loop: + %iv = phi i128 [ 3, %entry ], [ %iv.dec, %loop.latch ] + %tobool = icmp ne i128 %iv, 0 + br i1 %tobool, label %loop.latch, label %if + +if: + call void @foo() + br label %loop.latch + +loop.latch: + %iv.dec = add nsw i128 %iv, -1 + %cmp = icmp sgt i128 %iv, 0 + br i1 %cmp, label %loop, label %exit + +exit: + ret void +} + +declare void @foo() diff --git a/llvm/test/Transforms/InstCombine/ashr-lshr.ll b/llvm/test/Transforms/InstCombine/ashr-lshr.ll index c2a4f3541267..a81cd47b1cd4 100644 --- a/llvm/test/Transforms/InstCombine/ashr-lshr.ll +++ b/llvm/test/Transforms/InstCombine/ashr-lshr.ll @@ -61,6 +61,18 @@ define i32 @ashr_lshr2(i32 %x, i32 %y) { ret i32 %ret } +define i128 @ashr_lshr2_i128(i128 %x, i128 %y) { +; CHECK-LABEL: @ashr_lshr2_i128( +; CHECK-NEXT: [[CMP1:%.*]] = ashr i128 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: ret i128 [[CMP1]] +; + %cmp = icmp sgt i128 %x, 5 + %l = lshr i128 %x, %y + %r = ashr exact i128 %x, %y + %ret = select i1 %cmp, i128 %l, i128 %r + ret i128 %ret +} + define <2 x i32> @ashr_lshr_splat_vec(<2 x i32> %x, <2 x i32> %y) { ; CHECK-LABEL: @ashr_lshr_splat_vec( ; CHECK-NEXT: [[CMP12:%.*]] = ashr <2 x i32> [[X:%.*]], [[Y:%.*]] diff --git a/llvm/test/Transforms/InstCombine/conditional-variable-length-signext-after-high-bit-extract.ll b/llvm/test/Transforms/InstCombine/conditional-variable-length-signext-after-high-bit-extract.ll index 5569af452de4..b00b3a289de4 100644 --- a/llvm/test/Transforms/InstCombine/conditional-variable-length-signext-after-high-bit-extract.ll +++ b/llvm/test/Transforms/InstCombine/conditional-variable-length-signext-after-high-bit-extract.ll @@ -1137,3 +1137,18 @@ define i32 @n290_or_with_wrong_magic(i32 %data, i32 %nbits) { %signextended = or i32 %high_bits_extracted, %magic ret i32 %signextended } + +define i32 @bitwidth_does_not_fit(i3 %arg) { +; CHECK-LABEL: @bitwidth_does_not_fit( +; CHECK-NEXT: [[NEG:%.*]] = sub i3 0, [[ARG:%.*]] +; CHECK-NEXT: [[NEG_EXT:%.*]] = zext i3 [[NEG]] to i32 +; CHECK-NEXT: [[SHR:%.*]] = lshr i32 1, [[NEG_EXT]] +; CHECK-NEXT: [[INC:%.*]] = add nuw nsw i32 [[SHR]], 1 +; CHECK-NEXT: ret i32 [[INC]] +; + %neg = sub i3 0, %arg + %neg.ext = zext i3 %neg to i32 + %shr = lshr i32 1, %neg.ext + %inc = add i32 %shr, 1 + ret i32 %inc +} diff --git a/llvm/test/Transforms/InstCombine/gep-large-negative-offset.ll b/llvm/test/Transforms/InstCombine/gep-large-negative-offset.ll new file mode 100644 index 000000000000..fe79746f87e5 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/gep-large-negative-offset.ll @@ -0,0 +1,23 @@ +; RUN: opt -S < %s -passes=instcombine | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S32-a:256:256-ni:1" + +define ptr @test_neg() { +; CHECK-LABEL: @test_neg( +; CHECK-NEXT: entry: +; CHECK-NEXT: ret ptr inttoptr (i256 192 to ptr) +; +entry: + %gep = getelementptr inbounds i8, ptr inttoptr (i256 224 to ptr), i256 -32 + ret ptr %gep +} + +define ptr addrspace(1) @test_inner_neg() { +; CHECK-LABEL: @test_inner_neg( +; CHECK-NEXT: entry: +; CHECK-NEXT: ret ptr addrspace(1) getelementptr (i8, ptr addrspace(1) null, i256 -22) +; +entry: + %gep = getelementptr i8, ptr addrspace(1) getelementptr inbounds (i8, ptr addrspace(1) null, i256 -32), i256 10 + ret ptr addrspace(1) %gep +} diff --git a/llvm/test/Transforms/InstCombine/icmp-add.ll b/llvm/test/Transforms/InstCombine/icmp-add.ll index baa6f3d51a40..b4256ff6ad55 100644 --- a/llvm/test/Transforms/InstCombine/icmp-add.ll +++ b/llvm/test/Transforms/InstCombine/icmp-add.ll @@ -79,6 +79,19 @@ bb: ret i1 %i4 } +define i1 @cvt_icmp_0_zext_plus_zext_eq_i2(i1 %a, i1 %b) { +; CHECK-LABEL: @cvt_icmp_0_zext_plus_zext_eq_i2( +; CHECK-NEXT: [[TMP1:%.*]] = or i1 [[A:%.*]], [[B:%.*]] +; CHECK-NEXT: [[CMP:%.*]] = xor i1 [[TMP1]], true +; CHECK-NEXT: ret i1 [[CMP]] +; + %a.ext = zext i1 %a to i2 + %b.ext = zext i1 %b to i2 + %add = add i2 %a.ext, %b.ext + %cmp = icmp eq i2 %add, 0 + ret i1 %cmp +} + define i1 @cvt_icmp_1_zext_plus_zext_eq(i1 %arg, i1 %arg1) { ; CHECK-LABEL: @cvt_icmp_1_zext_plus_zext_eq( ; CHECK-NEXT: bb: diff --git a/llvm/test/Transforms/InstCombine/neg-not-iugt.ll b/llvm/test/Transforms/InstCombine/neg-not-iugt.ll new file mode 100644 index 000000000000..c8091fad7edf --- /dev/null +++ b/llvm/test/Transforms/InstCombine/neg-not-iugt.ll @@ -0,0 +1,137 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -passes=instcombine -S | FileCheck %s + +declare void @use(i32) + +define i1 @test1(i32 %arg) { +; CHECK-LABEL: @test1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[ARG:%.*]], 0 +; CHECK-NEXT: ret i1 [[CMP]] +; +entry: + %sub = sub i32 0, %arg + %xor = xor i32 %arg, -1 + %cmp = icmp ugt i32 %sub, %xor + ret i1 %cmp +} + +define i1 @test1_nuw(i32 %arg) { +; CHECK-LABEL: @test1_nuw( +; CHECK-NEXT: entry: +; CHECK-NEXT: ret i1 false +; +entry: + %sub = sub nuw i32 0, %arg + %xor = xor i32 %arg, -1 + %cmp = icmp ugt i32 %sub, %xor + ret i1 %cmp +} + +define i1 @test1_nsw(i32 %arg) { +; CHECK-LABEL: @test1_nsw( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[ARG:%.*]], 0 +; CHECK-NEXT: ret i1 [[CMP]] +; +entry: + %sub = sub nsw i32 0, %arg + %xor = xor i32 %arg, -1 + %cmp = icmp ugt i32 %sub, %xor + ret i1 %cmp +} + +define i1 @test1_nuw_nsw(i32 %arg) { +; CHECK-LABEL: @test1_nuw_nsw( +; CHECK-NEXT: entry: +; CHECK-NEXT: ret i1 false +; +entry: + %sub = sub nuw nsw i32 0, %arg + %xor = xor i32 %arg, -1 + %cmp = icmp ugt i32 %sub, %xor + ret i1 %cmp +} + +define i1 @test2(i32 %arg) { +; CHECK-LABEL: @test2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[ARG:%.*]], 0 +; CHECK-NEXT: ret i1 [[CMP]] +; +entry: + %sub = sub i32 0, %arg + %xor = xor i32 -1, %arg + %cmp = icmp ugt i32 %sub, %xor + ret i1 %cmp +} + +define i1 @test2_nuw(i32 %arg) { +; CHECK-LABEL: @test2_nuw( +; CHECK-NEXT: entry: +; CHECK-NEXT: ret i1 false +; +entry: + %sub = sub nuw i32 0, %arg + %xor = xor i32 -1, %arg + %cmp = icmp ugt i32 %sub, %xor + ret i1 %cmp +} + +define i1 @test2_nsw(i32 %arg) { +; CHECK-LABEL: @test2_nsw( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[ARG:%.*]], 0 +; CHECK-NEXT: ret i1 [[CMP]] +; +entry: + %sub = sub nsw i32 0, %arg + %xor = xor i32 -1, %arg + %cmp = icmp ugt i32 %sub, %xor + ret i1 %cmp +} + +define i1 @test2_nuw_nsw(i32 %arg) { +; CHECK-LABEL: @test2_nuw_nsw( +; CHECK-NEXT: entry: +; CHECK-NEXT: ret i1 false +; +entry: + %sub = sub nuw nsw i32 0, %arg + %xor = xor i32 -1, %arg + %cmp = icmp ugt i32 %sub, %xor + ret i1 %cmp +} + +define i1 @test3(i32 %arg) { +; CHECK-LABEL: @test3( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[SUB:%.*]] = sub i32 0, [[ARG:%.*]] +; CHECK-NEXT: call void @use(i32 [[SUB]]) +; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[ARG]], -1 +; CHECK-NEXT: call void @use(i32 [[XOR]]) +; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[ARG]], 0 +; CHECK-NEXT: ret i1 [[CMP]] +; +entry: + %sub = sub i32 0, %arg + call void @use(i32 %sub) + %xor = xor i32 -1, %arg + call void @use(i32 %xor) + %cmp = icmp ugt i32 %sub, %xor + ret i1 %cmp +} + +define i1 @test_no(i32 %arg1, i32 %arg2) { +; CHECK-LABEL: @test_no( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ADD:%.*]] = add i32 [[ARG1:%.*]], -1 +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[ADD]], %arg2 +; CHECK-NEXT: ret i1 [[CMP]] +; +entry: + %sub = sub i32 0, %arg1 + %xor = xor i32 %arg2, -1 + %cmp = icmp ugt i32 %sub, %xor + ret i1 %cmp +} diff --git a/llvm/test/Transforms/InstSimplify/fptoi-range.ll b/llvm/test/Transforms/InstSimplify/fptoi-range.ll index cc9605259aa8..95f2a9d50793 100644 --- a/llvm/test/Transforms/InstSimplify/fptoi-range.ll +++ b/llvm/test/Transforms/InstSimplify/fptoi-range.ll @@ -32,6 +32,15 @@ define i1 @f16_si16_max2(half %f) { ret i1 %c } +define i1 @f16_si128_max2(half %f) { +; CHECK-LABEL: @f16_si128_max2( +; CHECK-NEXT: ret i1 false +; + %i = fptosi half %f to i128 + %c = icmp sgt i128 %i, 65504 + ret i1 %c +} + define i1 @f16_si_min1(half %f) { ; CHECK-LABEL: @f16_si_min1( ; CHECK-NEXT: ret i1 true @@ -41,6 +50,15 @@ define i1 @f16_si_min1(half %f) { ret i1 %c } +define i1 @f16_si128_min1(half %f) { +; CHECK-LABEL: @f16_si128_min1( +; CHECK-NEXT: ret i1 true +; + %i = fptosi half %f to i128 + %c = icmp sge i128 %i, -65504 + ret i1 %c +} + define i1 @f16_si16_min1(half %f) { ; CHECK-LABEL: @f16_si16_min1( ; CHECK-NEXT: [[I:%.*]] = fptosi half [[F:%.*]] to i16 diff --git a/llvm/test/Transforms/LoopIdiom/X86/memset-size-compute.ll b/llvm/test/Transforms/LoopIdiom/X86/memset-size-compute.ll index ea2cfe74be26..6f052acda375 100644 --- a/llvm/test/Transforms/LoopIdiom/X86/memset-size-compute.ll +++ b/llvm/test/Transforms/LoopIdiom/X86/memset-size-compute.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes=loop-idiom -S %s | FileCheck %s +; XFAIL: * target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-apple-macosx11.0.0" diff --git a/llvm/test/Transforms/LoopIdiom/basic-address-space.ll b/llvm/test/Transforms/LoopIdiom/basic-address-space.ll index de55a46a164e..38e96c721891 100644 --- a/llvm/test/Transforms/LoopIdiom/basic-address-space.ll +++ b/llvm/test/Transforms/LoopIdiom/basic-address-space.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes=loop-idiom < %s -S | FileCheck %s +; XFAIL: target=evm{{.*}} target datalayout = "e-p:32:32:32-p1:64:64:64-p2:16:16:16-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:32-n8:16:32:64" target triple = "x86_64-apple-darwin10.0.0" diff --git a/llvm/test/Transforms/LoopIdiom/basic.ll b/llvm/test/Transforms/LoopIdiom/basic.ll index 87247aa76cfc..c84b59413443 100644 --- a/llvm/test/Transforms/LoopIdiom/basic.ll +++ b/llvm/test/Transforms/LoopIdiom/basic.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes=loop-idiom < %s -S | FileCheck %s +; XFAIL: target=evm{{.*}} target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" ; For @test11_pattern diff --git a/llvm/test/Transforms/LoopIdiom/debug-line.ll b/llvm/test/Transforms/LoopIdiom/debug-line.ll index eccfc38b9f86..ac6ef9b9e901 100644 --- a/llvm/test/Transforms/LoopIdiom/debug-line.ll +++ b/llvm/test/Transforms/LoopIdiom/debug-line.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes=loop-idiom < %s -S | FileCheck %s +; XFAIL: target=evm{{.*}} target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" target triple = "x86_64-apple-darwin10.0.0" diff --git a/llvm/test/Transforms/LoopIdiom/disable-options.ll b/llvm/test/Transforms/LoopIdiom/disable-options.ll index 823006113f9b..c84a64fb930a 100644 --- a/llvm/test/Transforms/LoopIdiom/disable-options.ll +++ b/llvm/test/Transforms/LoopIdiom/disable-options.ll @@ -9,6 +9,7 @@ ; RUN: opt -passes="loop-idiom" -aa-pipeline=basic-aa -disable-loop-idiom-memcpy < %s -S | FileCheck %s --check-prefix=DIS-MEMCPY ; RUN: opt -passes="loop-idiom" -aa-pipeline=basic-aa -disable-loop-idiom-memset < %s -S | FileCheck %s --check-prefix=DIS-MEMSET ; RUN: opt -passes="loop-idiom" -aa-pipeline=basic-aa -disable-loop-idiom-memset -disable-loop-idiom-memcpy < %s -S | FileCheck %s --check-prefix=DIS-ALL +; XFAIL: target=evm{{.*}} define void @test-memcpy(i64 %Size) nounwind ssp { ; DIS-NONE-LABEL: @test-memcpy( diff --git a/llvm/test/Transforms/LoopIdiom/int_sideeffect.ll b/llvm/test/Transforms/LoopIdiom/int_sideeffect.ll index 0304e08662d2..ec500e1dc393 100644 --- a/llvm/test/Transforms/LoopIdiom/int_sideeffect.ll +++ b/llvm/test/Transforms/LoopIdiom/int_sideeffect.ll @@ -1,4 +1,5 @@ ; RUN: opt -S < %s -passes=loop-idiom | FileCheck %s +; XFAIL: target=evm{{.*}} declare void @llvm.sideeffect() diff --git a/llvm/test/Transforms/LoopIdiom/lir-heurs-multi-block-loop.ll b/llvm/test/Transforms/LoopIdiom/lir-heurs-multi-block-loop.ll index 90aa09287fd6..a89c18dbdc6b 100644 --- a/llvm/test/Transforms/LoopIdiom/lir-heurs-multi-block-loop.ll +++ b/llvm/test/Transforms/LoopIdiom/lir-heurs-multi-block-loop.ll @@ -1,4 +1,5 @@ ; RUN: opt -passes=loop-idiom -use-lir-code-size-heurs=true < %s -S | FileCheck %s +; XFAIL: target=evm{{.*}} ; When compiling for codesize we avoid idiom recognition for a ; multi-block loop unless it is one of diff --git a/llvm/test/Transforms/LoopIdiom/memset-debugify-remarks.ll b/llvm/test/Transforms/LoopIdiom/memset-debugify-remarks.ll index 95859c0b49f4..d08fbf7c072d 100644 --- a/llvm/test/Transforms/LoopIdiom/memset-debugify-remarks.ll +++ b/llvm/test/Transforms/LoopIdiom/memset-debugify-remarks.ll @@ -1,6 +1,7 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes=debugify,loop-idiom,verify -pass-remarks=loop-idiom -pass-remarks-analysis=loop-idiom -pass-remarks-output=%t.yaml -verify-each -verify-dom-info -verify-loop-info < %s -S 2>&1 | FileCheck %s ; RUN: FileCheck --input-file=%t.yaml %s --check-prefixes=YAML +; XFAIL: target=evm{{.*}} ; RUN: opt -passes=debugify,loop-idiom,verify -pass-remarks=loop-idiom -pass-remarks-analysis=loop-idiom -pass-remarks-output=%t.yaml -verify-each -verify-dom-info -verify-loop-info < %s -S 2>&1 --try-experimental-debuginfo-iterators | FileCheck %s ; RUN: FileCheck --input-file=%t.yaml %s --check-prefixes=YAML diff --git a/llvm/test/Transforms/LoopIdiom/memset-pr52104.ll b/llvm/test/Transforms/LoopIdiom/memset-pr52104.ll index 0c7d1ec59b2c..7ce951638597 100644 --- a/llvm/test/Transforms/LoopIdiom/memset-pr52104.ll +++ b/llvm/test/Transforms/LoopIdiom/memset-pr52104.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes=loop-idiom < %s -S | FileCheck %s +; XFAIL: target=evm{{.*}} define void @f(ptr nocapture nonnull align 4 dereferenceable(20) %0, i32 %1) local_unnamed_addr #0 align 32 { ; CHECK-LABEL: @f( diff --git a/llvm/test/Transforms/LoopIdiom/memset-runtime-32bit.ll b/llvm/test/Transforms/LoopIdiom/memset-runtime-32bit.ll index c87397aabec7..abbf4ca4af1e 100644 --- a/llvm/test/Transforms/LoopIdiom/memset-runtime-32bit.ll +++ b/llvm/test/Transforms/LoopIdiom/memset-runtime-32bit.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes="function(loop(indvars,loop-idiom,loop-deletion),simplifycfg)" -S < %s | FileCheck %s +; XFAIL: target=evm{{.*}} ; Compile command: ; $ clang -m32 -fno-discard-value-names -O0 -S -emit-llvm -Xclang -disable-O0-optnone Code.c ; $ bin/opt -S -passes=mem2reg,loop-simplify,lcssa,loop-rotate \ diff --git a/llvm/test/Transforms/LoopIdiom/memset-runtime-64bit.ll b/llvm/test/Transforms/LoopIdiom/memset-runtime-64bit.ll index 6ce935044e95..dc0d807104bf 100644 --- a/llvm/test/Transforms/LoopIdiom/memset-runtime-64bit.ll +++ b/llvm/test/Transforms/LoopIdiom/memset-runtime-64bit.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes="function(loop(indvars,loop-idiom,loop-deletion),simplifycfg)" -S < %s | FileCheck %s +; XFAIL: target=evm{{.*}} ; Compile command: ; $ clang -m64 -fno-discard-value-names -O0 -S -emit-llvm -Xclang -disable-O0-optnone Code.c ; $ bin/opt -S -passes=mem2reg,loop-simplify,lcssa,loop-rotate \ diff --git a/llvm/test/Transforms/LoopIdiom/memset-runtime-debug.ll b/llvm/test/Transforms/LoopIdiom/memset-runtime-debug.ll index 6b952efb1e6d..64346d958d36 100644 --- a/llvm/test/Transforms/LoopIdiom/memset-runtime-debug.ll +++ b/llvm/test/Transforms/LoopIdiom/memset-runtime-debug.ll @@ -1,6 +1,7 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; REQUIRES: asserts ; RUN: opt < %s -S -debug -passes=loop-idiom 2>&1 | FileCheck %s +; XFAIL: target=evm{{.*}} ; The C code to generate this testcase: ; void test(int *ar, int n, int m) ; { diff --git a/llvm/test/Transforms/LoopIdiom/memset-tbaa.ll b/llvm/test/Transforms/LoopIdiom/memset-tbaa.ll index 61005c2ba20e..bb4be76b65b2 100644 --- a/llvm/test/Transforms/LoopIdiom/memset-tbaa.ll +++ b/llvm/test/Transforms/LoopIdiom/memset-tbaa.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes="loop-idiom" < %s -S | FileCheck %s +; XFAIL: target=evm{{.*}} define dso_local void @double_memset(ptr nocapture %p) { diff --git a/llvm/test/Transforms/LoopIdiom/memset.ll b/llvm/test/Transforms/LoopIdiom/memset.ll index 8b4f68b62664..ed69f011fce8 100644 --- a/llvm/test/Transforms/LoopIdiom/memset.ll +++ b/llvm/test/Transforms/LoopIdiom/memset.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes=loop-idiom < %s -S | FileCheck %s +; XFAIL: target=evm{{.*}} define dso_local void @double_memset(ptr nocapture %p, ptr noalias nocapture %q, i32 %n) { diff --git a/llvm/test/Transforms/LoopIdiom/non-integral-pointers.ll b/llvm/test/Transforms/LoopIdiom/non-integral-pointers.ll index 13ec5150f055..f5edd5bdb766 100644 --- a/llvm/test/Transforms/LoopIdiom/non-integral-pointers.ll +++ b/llvm/test/Transforms/LoopIdiom/non-integral-pointers.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -S -passes=loop-idiom < %s | FileCheck %s +; XFAIL: target=evm{{.*}} target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128-ni:4" target triple = "x86_64-unknown-linux-gnu" diff --git a/llvm/test/Transforms/LoopIdiom/pr70008.ll b/llvm/test/Transforms/LoopIdiom/pr70008.ll index 53ba0c4aaf20..8d65f3ff02e8 100644 --- a/llvm/test/Transforms/LoopIdiom/pr70008.ll +++ b/llvm/test/Transforms/LoopIdiom/pr70008.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 3 ; RUN: opt -S -passes=loop-idiom < %s | FileCheck %s +; XFAIL: target=evm{{.*}} ; Make sure we don't assert if the BECount is larger than 64 bits. diff --git a/llvm/test/Transforms/LoopIdiom/pr80954.ll b/llvm/test/Transforms/LoopIdiom/pr80954.ll index 55bdcb44c2c2..73218dedad6d 100644 --- a/llvm/test/Transforms/LoopIdiom/pr80954.ll +++ b/llvm/test/Transforms/LoopIdiom/pr80954.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4 ; RUN: opt -S -passes=loop-idiom < %s | FileCheck %s +; XFAIL: target=evm{{.*}} ; Make sure this does not assert in SCEVExpander. diff --git a/llvm/test/Transforms/LoopIdiom/reuse-cast.ll b/llvm/test/Transforms/LoopIdiom/reuse-cast.ll index 9d79165c44d2..42c72023072d 100644 --- a/llvm/test/Transforms/LoopIdiom/reuse-cast.ll +++ b/llvm/test/Transforms/LoopIdiom/reuse-cast.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes=loop-idiom -S %s | FileCheck %s +; XFAIL: target=evm{{.*}} define void @reuse_cast_1(ptr %ptr, i1 %c) { ; CHECK-LABEL: @reuse_cast_1( diff --git a/llvm/test/Transforms/LoopIdiom/struct-custom-dl.ll b/llvm/test/Transforms/LoopIdiom/struct-custom-dl.ll index 9794adcaeed6..4708c934ef6e 100644 --- a/llvm/test/Transforms/LoopIdiom/struct-custom-dl.ll +++ b/llvm/test/Transforms/LoopIdiom/struct-custom-dl.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes=loop-idiom < %s -S | FileCheck %s +; XFAIL: target=evm{{.*}} target datalayout = "e-p:40:64:64:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" %struct.foo = type { i32, i32 } diff --git a/llvm/test/Transforms/LoopIdiom/struct.ll b/llvm/test/Transforms/LoopIdiom/struct.ll index 73d569113f89..a4f98c0f7655 100644 --- a/llvm/test/Transforms/LoopIdiom/struct.ll +++ b/llvm/test/Transforms/LoopIdiom/struct.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes=loop-idiom < %s -S | FileCheck %s +; XFAIL: target=evm{{.*}} target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" target triple = "x86_64-apple-darwin10.0.0" diff --git a/llvm/test/Transforms/LoopIdiom/unroll-custom-dl.ll b/llvm/test/Transforms/LoopIdiom/unroll-custom-dl.ll index ac50c8716c73..c7bf6be014d6 100644 --- a/llvm/test/Transforms/LoopIdiom/unroll-custom-dl.ll +++ b/llvm/test/Transforms/LoopIdiom/unroll-custom-dl.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes=loop-idiom < %s -S | FileCheck %s +; XFAIL: target=evm{{.*}} target datalayout = "e-p:64:64:64:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" ; CHECK: @.memset_pattern = private unnamed_addr constant [4 x i32] [i32 2, i32 2, i32 2, i32 2], align 16 diff --git a/llvm/test/Transforms/LoopIdiom/unroll.ll b/llvm/test/Transforms/LoopIdiom/unroll.ll index 7c41310abdfd..0be99e21f25e 100644 --- a/llvm/test/Transforms/LoopIdiom/unroll.ll +++ b/llvm/test/Transforms/LoopIdiom/unroll.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes=loop-idiom < %s -S | FileCheck %s +; XFAIL: target=evm{{.*}} target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" ; CHECK: @.memset_pattern = private unnamed_addr constant [4 x i32] [i32 2, i32 2, i32 2, i32 2], align 16 diff --git a/llvm/test/Transforms/LoopStrengthReduce/X86/2011-11-29-postincphi.ll b/llvm/test/Transforms/LoopStrengthReduce/X86/2011-11-29-postincphi.ll index fbb9e2a7b6b8..82b3309a26fb 100644 --- a/llvm/test/Transforms/LoopStrengthReduce/X86/2011-11-29-postincphi.ll +++ b/llvm/test/Transforms/LoopStrengthReduce/X86/2011-11-29-postincphi.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py ; RUN: llc < %s | FileCheck %s +; XFAIL: * ; ; PR11431: handle a phi operand that is replaced by a postinc user. ; LSR first expands %t3 to %t2 in %phi diff --git a/llvm/test/Transforms/LoopStrengthReduce/X86/ivchain-X86.ll b/llvm/test/Transforms/LoopStrengthReduce/X86/ivchain-X86.ll index 39e2d6f1acca..b114460eda82 100644 --- a/llvm/test/Transforms/LoopStrengthReduce/X86/ivchain-X86.ll +++ b/llvm/test/Transforms/LoopStrengthReduce/X86/ivchain-X86.ll @@ -1,6 +1,7 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py ; RUN: llc < %s -O3 -mtriple=x86_64-unknown-unknown -mcpu=core2 | FileCheck %s -check-prefix=X64 ; RUN: llc < %s -O3 -mtriple=i686-unknown-unknown -mcpu=core2 | FileCheck %s -check-prefix=X32 +; XFAIL: target=evm{{.*}} ; @simple is the most basic chain of address induction variables. Chaining ; saves at least one register and avoids complex addressing and setup diff --git a/llvm/test/Transforms/MemCpyOpt/i256length.ll b/llvm/test/Transforms/MemCpyOpt/i256length.ll new file mode 100644 index 000000000000..5cb04d17800a --- /dev/null +++ b/llvm/test/Transforms/MemCpyOpt/i256length.ll @@ -0,0 +1,23 @@ +; RUN: opt -S -passes=memcpyopt < %s + +target triple = "evm-unknown-unknown" + +@ptr_calldata = private global ptr addrspace(3) null + +declare void @foo() + +; Function Attrs: argmemonly nocallback nofree nounwind willreturn +declare void @llvm.memcpy.p1.p1.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(1) noalias nocapture readonly, i256, i1 immarg) + +; Function Attrs: argmemonly nocallback nofree nounwind willreturn +declare void @llvm.memcpy.p1.p3.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(3) noalias nocapture readonly, i256, i1 immarg) + +; Function Attrs: nofree null_pointer_is_valid +define private void @__deploy() { +entry: + call void @foo() + %calldata_pointer = load ptr addrspace(3), ptr @ptr_calldata, align 32 + %calldata_source_pointer = getelementptr i8, ptr addrspace(3) %calldata_pointer, i256 0 + call void @llvm.memcpy.p1.p3.i256(ptr addrspace(1) align 1 null, ptr addrspace(3) align 1 %calldata_source_pointer, i256 32000000000000000000000000000000000000000000, i1 false) + unreachable +} diff --git a/llvm/test/Transforms/MergedLoadStoreMotion/st_sink_bugfix_22613.ll b/llvm/test/Transforms/MergedLoadStoreMotion/st_sink_bugfix_22613.ll index 604aba93ee16..46b9940e0e9f 100644 --- a/llvm/test/Transforms/MergedLoadStoreMotion/st_sink_bugfix_22613.ll +++ b/llvm/test/Transforms/MergedLoadStoreMotion/st_sink_bugfix_22613.ll @@ -3,6 +3,7 @@ target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" ; RUN: opt -O2 -S < %s | FileCheck %s +; XFAIL: target=evm{{.*}} ; CHECK-LABEL: main ; CHECK: memset diff --git a/llvm/test/Transforms/PhaseOrdering/X86/merge-functions.ll b/llvm/test/Transforms/PhaseOrdering/X86/merge-functions.ll index 708cdc9ca45e..38657b21da87 100644 --- a/llvm/test/Transforms/PhaseOrdering/X86/merge-functions.ll +++ b/llvm/test/Transforms/PhaseOrdering/X86/merge-functions.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes="default" -enable-merge-functions -S < %s | FileCheck %s +; XFAIL: * ; TODO: These two functions should get merged, but currently aren't, because ; the function merging pass is scheduled too early. diff --git a/llvm/test/Transforms/PhaseOrdering/X86/pr48844-br-to-switch-vectorization.ll b/llvm/test/Transforms/PhaseOrdering/X86/pr48844-br-to-switch-vectorization.ll index 40c42ffdfd10..01ac26d4d5e1 100644 --- a/llvm/test/Transforms/PhaseOrdering/X86/pr48844-br-to-switch-vectorization.ll +++ b/llvm/test/Transforms/PhaseOrdering/X86/pr48844-br-to-switch-vectorization.ll @@ -1,6 +1,7 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -S -O2 -mattr=avx < %s | FileCheck %s ; RUN: opt -S -passes="default" -mattr=avx < %s | FileCheck %s +; XFAIL: * target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" diff --git a/llvm/test/Transforms/PhaseOrdering/enable-loop-header-duplication-oz.ll b/llvm/test/Transforms/PhaseOrdering/enable-loop-header-duplication-oz.ll index 98b11578b49f..f402e2b2ba21 100644 --- a/llvm/test/Transforms/PhaseOrdering/enable-loop-header-duplication-oz.ll +++ b/llvm/test/Transforms/PhaseOrdering/enable-loop-header-duplication-oz.ll @@ -8,6 +8,7 @@ ; RUN: opt -passes='default' -S < %s | FileCheck %s --check-prefix=NOROTATION ; RUN: opt -passes='default' -S -enable-loop-header-duplication < %s | FileCheck %s --check-prefix=ROTATION ; RUN: opt -passes='default' -S < %s | FileCheck %s --check-prefix=ROTATION +; XFAIL: target=evm{{.*}} define void @test(i8* noalias nonnull align 1 %start, i8* %end) unnamed_addr { ; NOROTATION-LABEL: define void @test( diff --git a/llvm/test/Transforms/PhaseOrdering/memset-tail.ll b/llvm/test/Transforms/PhaseOrdering/memset-tail.ll index 489da5a1ed32..3136fe6fce41 100644 --- a/llvm/test/Transforms/PhaseOrdering/memset-tail.ll +++ b/llvm/test/Transforms/PhaseOrdering/memset-tail.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes='default' -S < %s | FileCheck %s +; XFAIL: target=evm{{.*}} define void @PR47852(ptr noundef %d, i32 noundef %c) { ; CHECK-LABEL: @PR47852( diff --git a/llvm/test/Transforms/SimplifyCFG/X86/MagicPointer.ll b/llvm/test/Transforms/SimplifyCFG/X86/MagicPointer.ll index 413406f921be..ba353c525736 100644 --- a/llvm/test/Transforms/SimplifyCFG/X86/MagicPointer.ll +++ b/llvm/test/Transforms/SimplifyCFG/X86/MagicPointer.ll @@ -1,6 +1,7 @@ ; Test that simplifycfg can create switch instructions from constant pointers. ; ; RUN: opt < %s -passes=simplifycfg -simplifycfg-require-and-preserve-domtree=1 -S | FileCheck %s +; XFAIL: * target datalayout = "e-p:64:64:64-p1:16:16:16-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" target triple = "x86_64-apple-darwin10.0.0" diff --git a/llvm/test/Transforms/SimplifyCFG/X86/switch-to-lookup-globals.ll b/llvm/test/Transforms/SimplifyCFG/X86/switch-to-lookup-globals.ll index 7c97827d108d..539a6bbfe428 100644 --- a/llvm/test/Transforms/SimplifyCFG/X86/switch-to-lookup-globals.ll +++ b/llvm/test/Transforms/SimplifyCFG/X86/switch-to-lookup-globals.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: -p ; RUN: opt -S -passes="simplifycfg" < %s | FileCheck %s +; XFAIL: * target triple = "x86_64-unknown-linux-gnu" diff --git a/llvm/test/lit.cfg.py b/llvm/test/lit.cfg.py index fe1262893212..1c39017089e9 100644 --- a/llvm/test/lit.cfg.py +++ b/llvm/test/lit.cfg.py @@ -479,7 +479,7 @@ def have_cxx_shared_library(): if config.target_triple: config.available_features.add("default_triple") # Direct object generation - if not config.target_triple.startswith(("nvptx", "xcore")): + if not config.target_triple.startswith(("nvptx", "evm", "xcore")): config.available_features.add("object-emission") if config.have_llvm_driver: diff --git a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/evm-basic.ll b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/evm-basic.ll new file mode 100644 index 000000000000..df99209b5291 --- /dev/null +++ b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/evm-basic.ll @@ -0,0 +1,6 @@ +; RUN: llc < %s -mtriple=evm | FileCheck %s + +define i256 @swap_second_no_junk(i256 %a1, i256 %a2, i256 %a3, i256 %a4) nounwind { + %x1 = sub i256 %a4, %a1 + ret i256 %x1 +} diff --git a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/evm-basic.ll.expected b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/evm-basic.ll.expected new file mode 100644 index 000000000000..4a4f1e007b5d --- /dev/null +++ b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/evm-basic.ll.expected @@ -0,0 +1,17 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=evm | FileCheck %s + +define i256 @swap_second_no_junk(i256 %a1, i256 %a2, i256 %a3, i256 %a4) nounwind { +; CHECK-LABEL: swap_second_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: POP +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = sub i256 %a4, %a1 + ret i256 %x1 +} diff --git a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/evm-basic.test b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/evm-basic.test new file mode 100644 index 000000000000..1cae7b26a8fd --- /dev/null +++ b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/evm-basic.test @@ -0,0 +1,4 @@ +# REQUIRES: evm-registered-target + +# RUN: cp -f %S/Inputs/evm-basic.ll %t.ll && %update_llc_test_checks %t.ll +# RUN: diff -u %S/Inputs/evm-basic.ll.expected %t.ll diff --git a/llvm/test/tools/llvm-ar/error-opening-permission.test b/llvm/test/tools/llvm-ar/error-opening-permission.test index b42f95329a3c..7b880b51b75f 100644 --- a/llvm/test/tools/llvm-ar/error-opening-permission.test +++ b/llvm/test/tools/llvm-ar/error-opening-permission.test @@ -1,7 +1,7 @@ ## Unsupported on windows as marking files "unreadable" ## is non-trivial on windows. -# UNSUPPORTED: system-windows -# REQUIRES: non-root-user +## Unsupported on evm as CI currently runs with root permission. +# UNSUPPORTED: system-windows, target=evm{{.*}} # RUN: rm -rf %t && mkdir -p %t # RUN: echo file1 > %t/1.txt diff --git a/llvm/test/tools/llvm-ar/extract.test b/llvm/test/tools/llvm-ar/extract.test index ccad703f3a14..ab22feb4c3ec 100644 --- a/llvm/test/tools/llvm-ar/extract.test +++ b/llvm/test/tools/llvm-ar/extract.test @@ -1,5 +1,4 @@ ## Test extract operation. -# XFAIL: system-darwin # RUN: rm -rf %t && mkdir -p %t/extracted/ diff --git a/llvm/test/tools/llvm-ar/print.test b/llvm/test/tools/llvm-ar/print.test index a27cab4a227e..7c2b40480c00 100644 --- a/llvm/test/tools/llvm-ar/print.test +++ b/llvm/test/tools/llvm-ar/print.test @@ -1,5 +1,4 @@ ## Test Print output -# XFAIL: system-darwin # RUN: rm -rf %t && mkdir -p %t # RUN: echo file1 > %t/1.txt diff --git a/llvm/test/tools/llvm-ifs/fail-file-write.test b/llvm/test/tools/llvm-ifs/fail-file-write.test index f13500f22620..3321f91e5347 100644 --- a/llvm/test/tools/llvm-ifs/fail-file-write.test +++ b/llvm/test/tools/llvm-ifs/fail-file-write.test @@ -1,7 +1,6 @@ ## Test failing to write output file on non-windows platforms. - -# UNSUPPORTED: system-windows -# REQUIRES: non-root-user +## Unsupported on evm as CI currently runs with root permission. +# UNSUPPORTED: system-windows, target=evm{{.*}} # RUN: rm -rf %t.TestDir # RUN: mkdir %t.TestDir # RUN: touch %t.TestDir/Output.TestFile diff --git a/llvm/test/tools/llvm-ranlib/error-opening-permission.test b/llvm/test/tools/llvm-ranlib/error-opening-permission.test index be56962112e6..7123742bdc11 100644 --- a/llvm/test/tools/llvm-ranlib/error-opening-permission.test +++ b/llvm/test/tools/llvm-ranlib/error-opening-permission.test @@ -1,6 +1,6 @@ ## Unsupported on windows as marking files "unreadable" is non-trivial on windows. -# UNSUPPORTED: system-windows -# REQUIRES: non-root-user +## Unsupported on evm as CI currently runs with root permission. +# UNSUPPORTED: system-windows, target=evm{{.*}} # RUN: rm -rf %t && split-file %s %t && cd %t # RUN: yaml2obj 1.yaml -o 1.o diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp index f0a22f1568be..e578217463d1 100644 --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -1284,6 +1284,9 @@ const EnumEntry ElfMachineType[] = { ENUM_ENT(EM_BPF, "EM_BPF"), ENUM_ENT(EM_VE, "NEC SX-Aurora Vector Engine"), ENUM_ENT(EM_LOONGARCH, "LoongArch"), + // EVM local begin + ENUM_ENT(EM_EVM, "EVM"), + // EVM local end }; const EnumEntry ElfSymbolBindings[] = { @@ -1746,6 +1749,11 @@ const EnumEntry ElfMips16SymOtherFlags[] = { const EnumEntry ElfRISCVSymOtherFlags[] = { LLVM_READOBJ_ENUM_ENT(ELF, STO_RISCV_VARIANT_CC)}; +// EVM local begin +const EnumEntry ElfEVMSymOtherFlags[] = { + LLVM_READOBJ_ENUM_ENT(ELF, STO_EVM_REFERENCE_SYMBOL)}; +// EVM local end + static const char *getElfMipsOptionsOdkType(unsigned Odk) { switch (Odk) { LLVM_READOBJ_ENUM_CASE(ELF, ODK_NULL); @@ -3485,6 +3493,11 @@ ELFDumper::getOtherFlagsFromSymbol(const Elf_Ehdr &Header, } else if (Header.e_machine == EM_RISCV) { SymOtherFlags.insert(SymOtherFlags.end(), std::begin(ElfRISCVSymOtherFlags), std::end(ElfRISCVSymOtherFlags)); + // EVM local begin + } else if (Header.e_machine == EM_EVM) { + SymOtherFlags.insert(SymOtherFlags.end(), std::begin(ElfEVMSymOtherFlags), + std::end(ElfEVMSymOtherFlags)); + // EVM local end } return SymOtherFlags; } @@ -4299,6 +4312,12 @@ void GNUELFDumper::printSymbol(const Elf_Sym &Symbol, unsigned SymIndex, Fields[5].Str.append(" | " + utohexstr(Other, /*LowerCase=*/true)); Fields[5].Str.append("]"); } + // EVM local begin + } else if (this->Obj.getHeader().e_machine == ELF::EM_EVM) { + uint8_t Other = Symbol.st_other & ~0x3; + if (Other == STO_EVM_REFERENCE_SYMBOL) + Fields[5].Str += " [REFERENCE_SYMBOL]"; + // EVM local end } else { Fields[5].Str += " []"; diff --git a/llvm/unittests/ADT/APFixedPointTest.cpp b/llvm/unittests/ADT/APFixedPointTest.cpp index ecb89fbf76c8..a0f43fdfcd7e 100644 --- a/llvm/unittests/ADT/APFixedPointTest.cpp +++ b/llvm/unittests/ADT/APFixedPointTest.cpp @@ -240,19 +240,20 @@ void CheckIntPart(const FixedPointSemantics &Sema, int64_t IntPart) { APFixedPoint ValWithFract( APInt(Sema.getWidth(), relativeShr(IntPart, Sema.getLsbWeight()) + FullFactPart, - Sema.isSigned()), + Sema.isSigned(), /*implicitTrunc=*/true), Sema); ASSERT_EQ(ValWithFract.getIntPart(), IntPart); // Just fraction - APFixedPoint JustFract(APInt(Sema.getWidth(), FullFactPart, Sema.isSigned()), + APFixedPoint JustFract(APInt(Sema.getWidth(), FullFactPart, Sema.isSigned(), + /*implicitTrunc=*/true), Sema); ASSERT_EQ(JustFract.getIntPart(), 0); // Whole number APFixedPoint WholeNum(APInt(Sema.getWidth(), relativeShr(IntPart, Sema.getLsbWeight()), - Sema.isSigned()), + Sema.isSigned(), /*implicitTrunc=*/true), Sema); ASSERT_EQ(WholeNum.getIntPart(), IntPart); @@ -260,7 +261,7 @@ void CheckIntPart(const FixedPointSemantics &Sema, int64_t IntPart) { if (Sema.isSigned()) { APFixedPoint Negative(APInt(Sema.getWidth(), relativeShr(IntPart, Sema.getLsbWeight()), - Sema.isSigned()), + Sema.isSigned(), /*implicitTrunc=*/true), Sema); ASSERT_EQ(Negative.getIntPart(), IntPart); } diff --git a/llvm/unittests/ADT/APIntTest.cpp b/llvm/unittests/ADT/APIntTest.cpp index eb4b847185f5..6058fb9c23dd 100644 --- a/llvm/unittests/ADT/APIntTest.cpp +++ b/llvm/unittests/ADT/APIntTest.cpp @@ -220,11 +220,12 @@ TEST(APIntTest, i256) { } TEST(APIntTest, i1) { - const APInt neg_two(1, static_cast(-2), true); + const APInt neg_two(1, static_cast(-2), true, + /*implicitTrunc=*/true); const APInt neg_one(1, static_cast(-1), true); const APInt zero(1, 0); const APInt one(1, 1); - const APInt two(1, 2); + const APInt two(1, 2, false, /*implicitTrunc=*/true); EXPECT_EQ(0, neg_two.getSExtValue()); EXPECT_EQ(-1, neg_one.getSExtValue()); @@ -1110,11 +1111,11 @@ TEST(APIntTest, fromString) { EXPECT_EQ(APInt(32, 3), APInt(32, "+11", 2)); EXPECT_EQ(APInt(32, 4), APInt(32, "+100", 2)); - EXPECT_EQ(APInt(32, uint64_t(-0LL)), APInt(32, "-0", 2)); - EXPECT_EQ(APInt(32, uint64_t(-1LL)), APInt(32, "-1", 2)); - EXPECT_EQ(APInt(32, uint64_t(-2LL)), APInt(32, "-10", 2)); - EXPECT_EQ(APInt(32, uint64_t(-3LL)), APInt(32, "-11", 2)); - EXPECT_EQ(APInt(32, uint64_t(-4LL)), APInt(32, "-100", 2)); + EXPECT_EQ(APInt(32, uint32_t(-0LL)), APInt(32, "-0", 2)); + EXPECT_EQ(APInt(32, uint32_t(-1LL)), APInt(32, "-1", 2)); + EXPECT_EQ(APInt(32, uint32_t(-2LL)), APInt(32, "-10", 2)); + EXPECT_EQ(APInt(32, uint32_t(-3LL)), APInt(32, "-11", 2)); + EXPECT_EQ(APInt(32, uint32_t(-4LL)), APInt(32, "-100", 2)); EXPECT_EQ(APInt(32, 0), APInt(32, "0", 8)); EXPECT_EQ(APInt(32, 1), APInt(32, "1", 8)); @@ -1130,12 +1131,12 @@ TEST(APIntTest, fromString) { EXPECT_EQ(APInt(32, +15), APInt(32, "+17", 8)); EXPECT_EQ(APInt(32, +16), APInt(32, "+20", 8)); - EXPECT_EQ(APInt(32, uint64_t(-0LL)), APInt(32, "-0", 8)); - EXPECT_EQ(APInt(32, uint64_t(-1LL)), APInt(32, "-1", 8)); - EXPECT_EQ(APInt(32, uint64_t(-7LL)), APInt(32, "-7", 8)); - EXPECT_EQ(APInt(32, uint64_t(-8LL)), APInt(32, "-10", 8)); - EXPECT_EQ(APInt(32, uint64_t(-15LL)), APInt(32, "-17", 8)); - EXPECT_EQ(APInt(32, uint64_t(-16LL)), APInt(32, "-20", 8)); + EXPECT_EQ(APInt(32, uint32_t(-0LL)), APInt(32, "-0", 8)); + EXPECT_EQ(APInt(32, uint32_t(-1LL)), APInt(32, "-1", 8)); + EXPECT_EQ(APInt(32, uint32_t(-7LL)), APInt(32, "-7", 8)); + EXPECT_EQ(APInt(32, uint32_t(-8LL)), APInt(32, "-10", 8)); + EXPECT_EQ(APInt(32, uint32_t(-15LL)), APInt(32, "-17", 8)); + EXPECT_EQ(APInt(32, uint32_t(-16LL)), APInt(32, "-20", 8)); EXPECT_EQ(APInt(32, 0), APInt(32, "0", 10)); EXPECT_EQ(APInt(32, 1), APInt(32, "1", 10)); @@ -1144,12 +1145,12 @@ TEST(APIntTest, fromString) { EXPECT_EQ(APInt(32, 19), APInt(32, "19", 10)); EXPECT_EQ(APInt(32, 20), APInt(32, "20", 10)); - EXPECT_EQ(APInt(32, uint64_t(-0LL)), APInt(32, "-0", 10)); - EXPECT_EQ(APInt(32, uint64_t(-1LL)), APInt(32, "-1", 10)); - EXPECT_EQ(APInt(32, uint64_t(-9LL)), APInt(32, "-9", 10)); - EXPECT_EQ(APInt(32, uint64_t(-10LL)), APInt(32, "-10", 10)); - EXPECT_EQ(APInt(32, uint64_t(-19LL)), APInt(32, "-19", 10)); - EXPECT_EQ(APInt(32, uint64_t(-20LL)), APInt(32, "-20", 10)); + EXPECT_EQ(APInt(32, uint32_t(-0LL)), APInt(32, "-0", 10)); + EXPECT_EQ(APInt(32, uint32_t(-1LL)), APInt(32, "-1", 10)); + EXPECT_EQ(APInt(32, uint32_t(-9LL)), APInt(32, "-9", 10)); + EXPECT_EQ(APInt(32, uint32_t(-10LL)), APInt(32, "-10", 10)); + EXPECT_EQ(APInt(32, uint32_t(-19LL)), APInt(32, "-19", 10)); + EXPECT_EQ(APInt(32, uint32_t(-20LL)), APInt(32, "-20", 10)); EXPECT_EQ(APInt(32, 0), APInt(32, "0", 16)); EXPECT_EQ(APInt(32, 1), APInt(32, "1", 16)); @@ -1158,12 +1159,12 @@ TEST(APIntTest, fromString) { EXPECT_EQ(APInt(32, 31), APInt(32, "1F", 16)); EXPECT_EQ(APInt(32, 32), APInt(32, "20", 16)); - EXPECT_EQ(APInt(32, uint64_t(-0LL)), APInt(32, "-0", 16)); - EXPECT_EQ(APInt(32, uint64_t(-1LL)), APInt(32, "-1", 16)); - EXPECT_EQ(APInt(32, uint64_t(-15LL)), APInt(32, "-F", 16)); - EXPECT_EQ(APInt(32, uint64_t(-16LL)), APInt(32, "-10", 16)); - EXPECT_EQ(APInt(32, uint64_t(-31LL)), APInt(32, "-1F", 16)); - EXPECT_EQ(APInt(32, uint64_t(-32LL)), APInt(32, "-20", 16)); + EXPECT_EQ(APInt(32, uint32_t(-0LL)), APInt(32, "-0", 16)); + EXPECT_EQ(APInt(32, uint32_t(-1LL)), APInt(32, "-1", 16)); + EXPECT_EQ(APInt(32, uint32_t(-15LL)), APInt(32, "-F", 16)); + EXPECT_EQ(APInt(32, uint32_t(-16LL)), APInt(32, "-10", 16)); + EXPECT_EQ(APInt(32, uint32_t(-31LL)), APInt(32, "-1F", 16)); + EXPECT_EQ(APInt(32, uint32_t(-32LL)), APInt(32, "-20", 16)); EXPECT_EQ(APInt(32, 0), APInt(32, "0", 36)); EXPECT_EQ(APInt(32, 1), APInt(32, "1", 36)); @@ -1172,12 +1173,12 @@ TEST(APIntTest, fromString) { EXPECT_EQ(APInt(32, 71), APInt(32, "1Z", 36)); EXPECT_EQ(APInt(32, 72), APInt(32, "20", 36)); - EXPECT_EQ(APInt(32, uint64_t(-0LL)), APInt(32, "-0", 36)); - EXPECT_EQ(APInt(32, uint64_t(-1LL)), APInt(32, "-1", 36)); - EXPECT_EQ(APInt(32, uint64_t(-35LL)), APInt(32, "-Z", 36)); - EXPECT_EQ(APInt(32, uint64_t(-36LL)), APInt(32, "-10", 36)); - EXPECT_EQ(APInt(32, uint64_t(-71LL)), APInt(32, "-1Z", 36)); - EXPECT_EQ(APInt(32, uint64_t(-72LL)), APInt(32, "-20", 36)); + EXPECT_EQ(APInt(32, uint32_t(-0LL)), APInt(32, "-0", 36)); + EXPECT_EQ(APInt(32, uint32_t(-1LL)), APInt(32, "-1", 36)); + EXPECT_EQ(APInt(32, uint32_t(-35LL)), APInt(32, "-Z", 36)); + EXPECT_EQ(APInt(32, uint32_t(-36LL)), APInt(32, "-10", 36)); + EXPECT_EQ(APInt(32, uint32_t(-71LL)), APInt(32, "-1Z", 36)); + EXPECT_EQ(APInt(32, uint32_t(-72LL)), APInt(32, "-20", 36)); } TEST(APIntTest, SaturatingMath) { @@ -1201,10 +1202,10 @@ TEST(APIntTest, SaturatingMath) { EXPECT_EQ(APInt(6, 31), AP_42.truncSSat(6)); EXPECT_EQ(APInt(5, 15), AP_42.truncSSat(5)); - EXPECT_EQ(APInt(8, -56), AP_200.truncSSat(8)); - EXPECT_EQ(APInt(7, -56), AP_200.truncSSat(7)); - EXPECT_EQ(APInt(6, -32), AP_200.truncSSat(6)); - EXPECT_EQ(APInt(5, -16), AP_200.truncSSat(5)); + EXPECT_EQ(APInt(8, -56, true), AP_200.truncSSat(8)); + EXPECT_EQ(APInt(7, -56, true), AP_200.truncSSat(7)); + EXPECT_EQ(APInt(6, -32, true), AP_200.truncSSat(6)); + EXPECT_EQ(APInt(5, -16, true), AP_200.truncSSat(5)); EXPECT_EQ(APInt(8, 200), AP_100.uadd_sat(AP_100)); EXPECT_EQ(APInt(8, 255), AP_100.uadd_sat(AP_200)); @@ -1212,52 +1213,53 @@ TEST(APIntTest, SaturatingMath) { EXPECT_EQ(APInt(8, 110), AP_10.sadd_sat(AP_100)); EXPECT_EQ(APInt(8, 127), AP_100.sadd_sat(AP_100)); - EXPECT_EQ(APInt(8, -128), (-AP_100).sadd_sat(-AP_100)); - EXPECT_EQ(APInt(8, -128), APInt(8, -128).sadd_sat(APInt(8, -128))); + EXPECT_EQ(APInt(8, -128, true), (-AP_100).sadd_sat(-AP_100)); + EXPECT_EQ(APInt(8, -128, true), + APInt(8, -128, true).sadd_sat(APInt(8, -128, true))); EXPECT_EQ(APInt(8, 90), AP_100.usub_sat(AP_10)); EXPECT_EQ(APInt(8, 0), AP_100.usub_sat(AP_200)); EXPECT_EQ(APInt(8, 0), APInt(8, 0).usub_sat(APInt(8, 255))); - EXPECT_EQ(APInt(8, -90), AP_10.ssub_sat(AP_100)); + EXPECT_EQ(APInt(8, -90, true), AP_10.ssub_sat(AP_100)); EXPECT_EQ(APInt(8, 127), AP_100.ssub_sat(-AP_100)); - EXPECT_EQ(APInt(8, -128), (-AP_100).ssub_sat(AP_100)); - EXPECT_EQ(APInt(8, -128), APInt(8, -128).ssub_sat(APInt(8, 127))); + EXPECT_EQ(APInt(8, -128, true), (-AP_100).ssub_sat(AP_100)); + EXPECT_EQ(APInt(8, -128, true), APInt(8, -128, true).ssub_sat(APInt(8, 127))); EXPECT_EQ(APInt(8, 250), APInt(8, 50).umul_sat(APInt(8, 5))); EXPECT_EQ(APInt(8, 255), APInt(8, 50).umul_sat(APInt(8, 6))); - EXPECT_EQ(APInt(8, 255), APInt(8, -128).umul_sat(APInt(8, 3))); - EXPECT_EQ(APInt(8, 255), APInt(8, 3).umul_sat(APInt(8, -128))); - EXPECT_EQ(APInt(8, 255), APInt(8, -128).umul_sat(APInt(8, -128))); + EXPECT_EQ(APInt(8, 255), APInt(8, -128, true).umul_sat(APInt(8, 3))); + EXPECT_EQ(APInt(8, 255), APInt(8, 3).umul_sat(APInt(8, -128, true))); + EXPECT_EQ(APInt(8, 255), APInt(8, -128, true).umul_sat(APInt(8, -128, true))); EXPECT_EQ(APInt(8, 125), APInt(8, 25).smul_sat(APInt(8, 5))); EXPECT_EQ(APInt(8, 127), APInt(8, 25).smul_sat(APInt(8, 6))); EXPECT_EQ(APInt(8, 127), APInt(8, 127).smul_sat(APInt(8, 127))); - EXPECT_EQ(APInt(8, -125), APInt(8, -25).smul_sat(APInt(8, 5))); - EXPECT_EQ(APInt(8, -125), APInt(8, 25).smul_sat(APInt(8, -5))); - EXPECT_EQ(APInt(8, 125), APInt(8, -25).smul_sat(APInt(8, -5))); + EXPECT_EQ(APInt(8, -125, true), APInt(8, -25, true).smul_sat(APInt(8, 5))); + EXPECT_EQ(APInt(8, -125, true), APInt(8, 25).smul_sat(APInt(8, -5, true))); + EXPECT_EQ(APInt(8, 125), APInt(8, -25, true).smul_sat(APInt(8, -5, true))); EXPECT_EQ(APInt(8, 125), APInt(8, 25).smul_sat(APInt(8, 5))); - EXPECT_EQ(APInt(8, -128), APInt(8, -25).smul_sat(APInt(8, 6))); - EXPECT_EQ(APInt(8, -128), APInt(8, 25).smul_sat(APInt(8, -6))); - EXPECT_EQ(APInt(8, 127), APInt(8, -25).smul_sat(APInt(8, -6))); + EXPECT_EQ(APInt(8, -128, true), APInt(8, -25, true).smul_sat(APInt(8, 6))); + EXPECT_EQ(APInt(8, -128, true), APInt(8, 25).smul_sat(APInt(8, -6, true))); + EXPECT_EQ(APInt(8, 127), APInt(8, -25, true).smul_sat(APInt(8, -6, true))); EXPECT_EQ(APInt(8, 127), APInt(8, 25).smul_sat(APInt(8, 6))); EXPECT_EQ(APInt(8, 128), APInt(8, 4).ushl_sat(APInt(8, 5))); EXPECT_EQ(APInt(8, 255), APInt(8, 4).ushl_sat(APInt(8, 6))); EXPECT_EQ(APInt(8, 128), APInt(8, 1).ushl_sat(APInt(8, 7))); EXPECT_EQ(APInt(8, 255), APInt(8, 1).ushl_sat(APInt(8, 8))); - EXPECT_EQ(APInt(8, 255), APInt(8, -128).ushl_sat(APInt(8, 2))); + EXPECT_EQ(APInt(8, 255), APInt(8, -128, true).ushl_sat(APInt(8, 2))); EXPECT_EQ(APInt(8, 255), APInt(8, 64).ushl_sat(APInt(8, 2))); - EXPECT_EQ(APInt(8, 255), APInt(8, 64).ushl_sat(APInt(8, -2))); + EXPECT_EQ(APInt(8, 255), APInt(8, 64).ushl_sat(APInt(8, -2, true))); EXPECT_EQ(APInt(8, 64), APInt(8, 4).sshl_sat(APInt(8, 4))); EXPECT_EQ(APInt(8, 127), APInt(8, 4).sshl_sat(APInt(8, 5))); EXPECT_EQ(APInt(8, 127), APInt(8, 1).sshl_sat(APInt(8, 8))); - EXPECT_EQ(APInt(8, -64), APInt(8, -4).sshl_sat(APInt(8, 4))); - EXPECT_EQ(APInt(8, -128), APInt(8, -4).sshl_sat(APInt(8, 5))); - EXPECT_EQ(APInt(8, -128), APInt(8, -4).sshl_sat(APInt(8, 6))); - EXPECT_EQ(APInt(8, -128), APInt(8, -1).sshl_sat(APInt(8, 7))); - EXPECT_EQ(APInt(8, -128), APInt(8, -1).sshl_sat(APInt(8, 8))); + EXPECT_EQ(APInt(8, -64, true), APInt(8, -4, true).sshl_sat(APInt(8, 4))); + EXPECT_EQ(APInt(8, -128, true), APInt(8, -4, true).sshl_sat(APInt(8, 5))); + EXPECT_EQ(APInt(8, -128, true), APInt(8, -4, true).sshl_sat(APInt(8, 6))); + EXPECT_EQ(APInt(8, -128, true), APInt(8, -1, true).sshl_sat(APInt(8, 7))); + EXPECT_EQ(APInt(8, -128, true), APInt(8, -1, true).sshl_sat(APInt(8, 8))); } TEST(APIntTest, FromArray) { @@ -1399,39 +1401,39 @@ TEST(APIntTest, toString) { S.clear(); isSigned = false; - APInt(8, 255, isSigned).toString(S, 2, isSigned, true); + APInt(8, 255).toString(S, 2, isSigned, true); EXPECT_EQ(std::string(S), "0b11111111"); S.clear(); - APInt(8, 255, isSigned).toString(S, 8, isSigned, true); + APInt(8, 255).toString(S, 8, isSigned, true); EXPECT_EQ(std::string(S), "0377"); S.clear(); - APInt(8, 255, isSigned).toString(S, 10, isSigned, true); + APInt(8, 255).toString(S, 10, isSigned, true); EXPECT_EQ(std::string(S), "255"); S.clear(); - APInt(8, 255, isSigned).toString(S, 16, isSigned, true, /*UpperCase=*/false); + APInt(8, 255).toString(S, 16, isSigned, true, /*UpperCase=*/false); EXPECT_EQ(std::string(S), "0xff"); S.clear(); - APInt(8, 255, isSigned).toString(S, 16, isSigned, true); + APInt(8, 255).toString(S, 16, isSigned, true); EXPECT_EQ(std::string(S), "0xFF"); S.clear(); - APInt(8, 255, isSigned).toString(S, 36, isSigned, false); + APInt(8, 255).toString(S, 36, isSigned, false); EXPECT_EQ(std::string(S), "73"); S.clear(); isSigned = true; - APInt(8, 255, isSigned).toString(S, 2, isSigned, true); + APInt(8, 255).toString(S, 2, isSigned, true); EXPECT_EQ(std::string(S), "-0b1"); S.clear(); - APInt(8, 255, isSigned).toString(S, 8, isSigned, true); + APInt(8, 255).toString(S, 8, isSigned, true); EXPECT_EQ(std::string(S), "-01"); S.clear(); - APInt(8, 255, isSigned).toString(S, 10, isSigned, true); + APInt(8, 255).toString(S, 10, isSigned, true); EXPECT_EQ(std::string(S), "-1"); S.clear(); - APInt(8, 255, isSigned).toString(S, 16, isSigned, true); + APInt(8, 255).toString(S, 16, isSigned, true); EXPECT_EQ(std::string(S), "-0x1"); S.clear(); - APInt(8, 255, isSigned).toString(S, 36, isSigned, false); + APInt(8, 255).toString(S, 36, isSigned, false); EXPECT_EQ(std::string(S), "-1"); S.clear(); @@ -1514,7 +1516,7 @@ TEST(APIntTest, Rotate) { EXPECT_EQ(APInt(32, 2), APInt(32, 1).rotl(APInt(33, 33))); EXPECT_EQ(APInt(32, (1 << 8)), APInt(32, 1).rotl(APInt(32, 40))); EXPECT_EQ(APInt(32, (1 << 30)), APInt(32, 1).rotl(APInt(31, 30))); - EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotl(APInt(31, 31))); + EXPECT_EQ(APInt(32, (1u << 31)), APInt(32, 1).rotl(APInt(31, 31))); EXPECT_EQ(APInt(32, 1), APInt(32, 1).rotl(APInt(1, 0))); EXPECT_EQ(APInt(32, 2), APInt(32, 1).rotl(APInt(1, 1))); @@ -1541,24 +1543,24 @@ TEST(APIntTest, Rotate) { EXPECT_EQ(APInt(8, 16), APInt(8, 1).rotr(4)); EXPECT_EQ(APInt(8, 1), APInt(8, 1).rotr(8)); - EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotr(33)); - EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotr(APInt(32, 33))); + EXPECT_EQ(APInt(32, (1u << 31)), APInt(32, 1).rotr(33)); + EXPECT_EQ(APInt(32, (1u << 31)), APInt(32, 1).rotr(APInt(32, 33))); - EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotr(33)); - EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotr(APInt(32, 33))); - EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotr(APInt(33, 33))); + EXPECT_EQ(APInt(32, (1u << 31)), APInt(32, 1).rotr(33)); + EXPECT_EQ(APInt(32, (1u << 31)), APInt(32, 1).rotr(APInt(32, 33))); + EXPECT_EQ(APInt(32, (1u << 31)), APInt(32, 1).rotr(APInt(33, 33))); EXPECT_EQ(APInt(32, (1 << 24)), APInt(32, 1).rotr(APInt(32, 40))); EXPECT_EQ(APInt(32, (1 << 2)), APInt(32, 1).rotr(APInt(31, 30))); EXPECT_EQ(APInt(32, (1 << 1)), APInt(32, 1).rotr(APInt(31, 31))); EXPECT_EQ(APInt(32, 1), APInt(32, 1).rotr(APInt(1, 0))); - EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotr(APInt(1, 1))); + EXPECT_EQ(APInt(32, (1u << 31)), APInt(32, 1).rotr(APInt(1, 1))); EXPECT_EQ(APInt(32, (1 << 28)), APInt(32, 1).rotr(APInt(3, 4))); EXPECT_EQ(APInt(32, 1), APInt(32, 1).rotr(APInt(64, 64))); - EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotr(APInt(64, 65))); + EXPECT_EQ(APInt(32, (1u << 31)), APInt(32, 1).rotr(APInt(64, 65))); EXPECT_EQ(APInt(7, 48), APInt(7, 3).rotr(APInt(7, 3))); EXPECT_EQ(APInt(7, 48), APInt(7, 3).rotr(APInt(7, 10))); @@ -1580,7 +1582,7 @@ TEST(APIntTest, Splat) { APInt ValB(3, 5); EXPECT_EQ(APInt(4, 0xD), APInt::getSplat(4, ValB)); - EXPECT_EQ(APInt(15, 0xDB6D), APInt::getSplat(15, ValB)); + EXPECT_EQ(APInt(15, 0x5B6D), APInt::getSplat(15, ValB)); } TEST(APIntTest, tcDecrement) { @@ -2857,7 +2859,7 @@ TEST(APIntTest, sext) { EXPECT_EQ(31U, i32_min.countr_zero()); EXPECT_EQ(32U, i32_min.popcount()); - APInt i32_neg1(APInt(32, ~uint64_t(0)).sext(63)); + APInt i32_neg1(APInt(32, ~uint32_t(0)).sext(63)); EXPECT_EQ(i32_neg1, i32_neg1.sext(63)); EXPECT_EQ(63U, i32_neg1.countl_one()); EXPECT_EQ(0U, i32_neg1.countr_zero()); @@ -2990,7 +2992,7 @@ TEST(APIntTest, RoundingUDiv) { TEST(APIntTest, RoundingSDiv) { for (int64_t Ai = -128; Ai <= 127; Ai++) { - APInt A(8, Ai); + APInt A(8, Ai, true); if (Ai != 0) { APInt Zero(8, 0); @@ -3003,7 +3005,7 @@ TEST(APIntTest, RoundingSDiv) { if (Bi == 0) continue; - APInt B(8, Bi); + APInt B(8, Bi, true); APInt QuoTowardZero = A.sdiv(B); { APInt Quo = APIntOps::RoundingSDiv(A, B, APInt::Rounding::UP); @@ -3062,10 +3064,10 @@ TEST(APIntTest, Average) { APInt Ap100(32, +100); APInt Ap101(32, +101); APInt Ap200(32, +200); - APInt Am1(32, -1); - APInt Am100(32, -100); - APInt Am101(32, -101); - APInt Am200(32, -200); + APInt Am1(32, -1, true); + APInt Am100(32, -100, true); + APInt Am101(32, -101, true); + APInt Am200(32, -200, true); APInt AmSMin = APInt::getSignedMinValue(32); APInt ApSMax = APInt::getSignedMaxValue(32); @@ -3075,7 +3077,7 @@ TEST(APIntTest, Average) { EXPECT_EQ(APIntOps::RoundingSDiv(Ap100 + Ap200, A2, APInt::Rounding::UP), APIntOps::avgCeilS(Ap100, Ap200)); - EXPECT_EQ(APInt(32, -150), APIntOps::avgFloorS(Am100, Am200)); + EXPECT_EQ(APInt(32, -150, true), APIntOps::avgFloorS(Am100, Am200)); EXPECT_EQ(APIntOps::RoundingSDiv(Am100 + Am200, A2, APInt::Rounding::DOWN), APIntOps::avgFloorS(Am100, Am200)); EXPECT_EQ(APIntOps::RoundingSDiv(Am100 + Am200, A2, APInt::Rounding::UP), @@ -3088,10 +3090,10 @@ TEST(APIntTest, Average) { EXPECT_EQ(APIntOps::RoundingSDiv(Ap100 + Ap101, A2, APInt::Rounding::UP), APIntOps::avgCeilS(Ap100, Ap101)); - EXPECT_EQ(APInt(32, -101), APIntOps::avgFloorS(Am100, Am101)); + EXPECT_EQ(APInt(32, -101, true), APIntOps::avgFloorS(Am100, Am101)); EXPECT_EQ(APIntOps::RoundingSDiv(Am100 + Am101, A2, APInt::Rounding::DOWN), APIntOps::avgFloorS(Am100, Am101)); - EXPECT_EQ(APInt(32, -100), APIntOps::avgCeilS(Am100, Am101)); + EXPECT_EQ(APInt(32, -100, true), APIntOps::avgCeilS(Am100, Am101)); EXPECT_EQ(APIntOps::RoundingSDiv(Am100 + Am101, A2, APInt::Rounding::UP), APIntOps::avgCeilS(Am100, Am101)); @@ -3303,7 +3305,8 @@ TEST(APIntTest, SolveQuadraticEquationWrap) { for (int B = Low; B != High; ++B) { for (int C = Low; C != High; ++C) { std::optional S = APIntOps::SolveQuadraticEquationWrap( - APInt(Width, A), APInt(Width, B), APInt(Width, C), Width); + APInt(Width, A, true), APInt(Width, B, true), + APInt(Width, C, true), Width); if (S) Validate(A, B, C, Width, S->getSExtValue()); } @@ -3398,10 +3401,10 @@ TEST(APIntTest, GetMostSignificantDifferentBitExaustive) { } TEST(APIntTest, SignbitZeroChecks) { - EXPECT_TRUE(APInt(8, -1).isNegative()); - EXPECT_FALSE(APInt(8, -1).isNonNegative()); - EXPECT_FALSE(APInt(8, -1).isStrictlyPositive()); - EXPECT_TRUE(APInt(8, -1).isNonPositive()); + EXPECT_TRUE(APInt(8, -1, true).isNegative()); + EXPECT_FALSE(APInt(8, -1, true).isNonNegative()); + EXPECT_FALSE(APInt(8, -1, true).isStrictlyPositive()); + EXPECT_TRUE(APInt(8, -1, true).isNonPositive()); EXPECT_FALSE(APInt(8, 0).isNegative()); EXPECT_TRUE(APInt(8, 0).isNonNegative()); @@ -3420,6 +3423,7 @@ TEST(APIntTest, ZeroWidth) { EXPECT_EQ(0U, ZW.getBitWidth()); EXPECT_EQ(0U, APInt(0, ArrayRef({0, 1, 2})).getBitWidth()); EXPECT_EQ(0U, APInt(0, "0", 10).getBitWidth()); + EXPECT_EQ(0U, APInt::getAllOnes(0).getBitWidth()); // Default constructor is single bit wide. EXPECT_EQ(1U, APInt().getBitWidth()); @@ -3553,4 +3557,13 @@ TEST(APIntTest, TryExt) { ASSERT_EQ(42, APInt(128, -1).trySExtValue().value_or(42)); } +// EVM local begin +#ifdef GTEST_HAS_DEATH_TEST +TEST(APIntTest, DiagnoseZExt) { + EXPECT_DEATH((void)APInt(32, -1, false), + "Value is not an N-bit unsigned value"); +} +#endif +// EVM local end + } // end anonymous namespace diff --git a/llvm/unittests/ADT/APSIntTest.cpp b/llvm/unittests/ADT/APSIntTest.cpp index f804eba3ca83..2d2a64433da9 100644 --- a/llvm/unittests/ADT/APSIntTest.cpp +++ b/llvm/unittests/ADT/APSIntTest.cpp @@ -74,14 +74,14 @@ TEST(APSIntTest, getExtValue) { EXPECT_TRUE(APSInt(APInt(3, 7), false).isSigned()); EXPECT_TRUE(APSInt(APInt(4, 7), true).isUnsigned()); EXPECT_TRUE(APSInt(APInt(4, 7), false).isSigned()); - EXPECT_TRUE(APSInt(APInt(4, -7), true).isUnsigned()); - EXPECT_TRUE(APSInt(APInt(4, -7), false).isSigned()); + EXPECT_TRUE(APSInt(APInt(4, -7, true), true).isUnsigned()); + EXPECT_TRUE(APSInt(APInt(4, -7, true), false).isSigned()); EXPECT_EQ(7, APSInt(APInt(3, 7), true).getExtValue()); EXPECT_EQ(-1, APSInt(APInt(3, 7), false).getExtValue()); EXPECT_EQ(7, APSInt(APInt(4, 7), true).getExtValue()); EXPECT_EQ(7, APSInt(APInt(4, 7), false).getExtValue()); - EXPECT_EQ(9, APSInt(APInt(4, -7), true).getExtValue()); - EXPECT_EQ(-7, APSInt(APInt(4, -7), false).getExtValue()); + EXPECT_EQ(9, APSInt(APInt(4, -7, true), true).getExtValue()); + EXPECT_EQ(-7, APSInt(APInt(4, -7, true), false).getExtValue()); } TEST(APSIntTest, tryExtValue) { ASSERT_EQ(-7, APSInt(APInt(64, -7), false).tryExtValue().value_or(42)); diff --git a/llvm/unittests/ADT/StringExtrasTest.cpp b/llvm/unittests/ADT/StringExtrasTest.cpp index 1fb1fea65779..eb202bad865d 100644 --- a/llvm/unittests/ADT/StringExtrasTest.cpp +++ b/llvm/unittests/ADT/StringExtrasTest.cpp @@ -296,11 +296,11 @@ TEST(StringExtrasTest, toStringAPInt) { EXPECT_EQ(toString(APInt(8, 255, isSigned), 36, isSigned, false), "73"); isSigned = true; - EXPECT_EQ(toString(APInt(8, 255, isSigned), 2, isSigned, true), "-0b1"); - EXPECT_EQ(toString(APInt(8, 255, isSigned), 8, isSigned, true), "-01"); - EXPECT_EQ(toString(APInt(8, 255, isSigned), 10, isSigned, true), "-1"); - EXPECT_EQ(toString(APInt(8, 255, isSigned), 16, isSigned, true), "-0x1"); - EXPECT_EQ(toString(APInt(8, 255, isSigned), 36, isSigned, false), "-1"); + EXPECT_EQ(toString(APInt(8, -1, isSigned), 2, isSigned, true), "-0b1"); + EXPECT_EQ(toString(APInt(8, -1, isSigned), 8, isSigned, true), "-01"); + EXPECT_EQ(toString(APInt(8, -1, isSigned), 10, isSigned, true), "-1"); + EXPECT_EQ(toString(APInt(8, -1, isSigned), 16, isSigned, true), "-0x1"); + EXPECT_EQ(toString(APInt(8, -1, isSigned), 36, isSigned, false), "-1"); } TEST(StringExtrasTest, toStringAPSInt) { diff --git a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp index a6a5ffda3cb7..7a76d2304ef6 100644 --- a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp +++ b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp @@ -982,7 +982,7 @@ TEST_F(ScalarEvolutionsTest, SCEVAddRecFromPHIwithLargeConstantAccum) { // entry: BranchInst::Create(LoopBB, EntryBB); // loop: - auto *MinInt32 = ConstantInt::get(Context, APInt(32, 0x80000000U, true)); + auto *MinInt32 = ConstantInt::get(Context, APInt(32, 0x80000000U)); auto *Int32_16 = ConstantInt::get(Context, APInt(32, 16)); auto *Br = BranchInst::Create( LoopBB, ExitBB, UndefValue::get(Type::getInt1Ty(Context)), LoopBB); @@ -1117,10 +1117,12 @@ TEST_F(ScalarEvolutionsTest, SCEVComputeConstantDifference) { LLVMContext C; SMDiagnostic Err; std::unique_ptr M = parseAssemblyString( - "define void @foo(i32 %sz, i32 %pp) { " + "define void @foo(i32 %sz, i32 %pp, i32 %x) { " "entry: " " %v0 = add i32 %pp, 0 " " %v3 = add i32 %pp, 3 " + " %vx = add i32 %pp, %x " + " %vx3 = add i32 %vx, 3 " " br label %loop.body " "loop.body: " " %iv = phi i32 [ %iv.next, %loop.body ], [ 0, %entry ] " @@ -1141,6 +1143,9 @@ TEST_F(ScalarEvolutionsTest, SCEVComputeConstantDifference) { runWithSE(*M, "foo", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) { auto *ScevV0 = SE.getSCEV(getInstructionByName(F, "v0")); // %pp auto *ScevV3 = SE.getSCEV(getInstructionByName(F, "v3")); // (3 + %pp) + auto *ScevVX = SE.getSCEV(getInstructionByName(F, "vx")); // (%pp + %x) + // (%pp + %x + 3) + auto *ScevVX3 = SE.getSCEV(getInstructionByName(F, "vx3")); auto *ScevIV = SE.getSCEV(getInstructionByName(F, "iv")); // {0,+,1} auto *ScevXA = SE.getSCEV(getInstructionByName(F, "xa")); // {%pp,+,1} auto *ScevYY = SE.getSCEV(getInstructionByName(F, "yy")); // {(3 + %pp),+,1} @@ -1162,6 +1167,7 @@ TEST_F(ScalarEvolutionsTest, SCEVComputeConstantDifference) { EXPECT_EQ(diff(ScevV0, ScevV3), -3); EXPECT_EQ(diff(ScevV0, ScevV0), 0); EXPECT_EQ(diff(ScevV3, ScevV3), 0); + EXPECT_EQ(diff(ScevVX3, ScevVX), 3); EXPECT_EQ(diff(ScevIV, ScevIV), 0); EXPECT_EQ(diff(ScevXA, ScevXB), 0); EXPECT_EQ(diff(ScevXA, ScevYY), -3); diff --git a/llvm/unittests/Analysis/UnrollAnalyzerTest.cpp b/llvm/unittests/Analysis/UnrollAnalyzerTest.cpp index 721d67f22f2f..0ff08d19957e 100644 --- a/llvm/unittests/Analysis/UnrollAnalyzerTest.cpp +++ b/llvm/unittests/Analysis/UnrollAnalyzerTest.cpp @@ -221,6 +221,8 @@ TEST(UnrollAnalyzerTest, PtrCmpSimplifications) { " %iv.0 = phi i8* [ %a, %entry ], [ %iv.1, %loop.body ]\n" " %iv2.0 = phi i8* [ %start.iv2, %entry ], [ %iv2.1, %loop.body ]\n" " %cmp = icmp eq i8* %iv2.0, %iv.0\n" + " %cmp2 = icmp slt i8* %iv2.0, %iv.0\n" + " %cmp3 = icmp ult i8* %iv2.0, %iv.0\n" " %iv.1 = getelementptr inbounds i8, i8* %iv.0, i64 1\n" " %iv2.1 = getelementptr inbounds i8, i8* %iv2.0, i64 1\n" " %exitcond = icmp ne i8* %iv.1, %limit\n" @@ -242,12 +244,21 @@ TEST(UnrollAnalyzerTest, PtrCmpSimplifications) { BasicBlock::iterator BBI = Header->begin(); std::advance(BBI, 2); - Instruction *Y1 = &*BBI; + Instruction *Cmp1 = &*BBI++; + Instruction *Cmp2 = &*BBI++; + Instruction *Cmp3 = &*BBI++; // Check simplification expected on the 5th iteration. // Check that "%cmp = icmp eq i8* %iv2.0, %iv.0" is simplified to 0. - auto I1 = SimplifiedValuesVector[5].find(Y1); + auto I1 = SimplifiedValuesVector[5].find(Cmp1); EXPECT_TRUE(I1 != SimplifiedValuesVector[5].end()); EXPECT_EQ(cast((*I1).second)->getZExtValue(), 0U); + // Check that "%cmp2 = icmp slt i8* %iv2.0, %iv.0" does not simplify + auto I2 = SimplifiedValuesVector[5].find(Cmp2); + EXPECT_TRUE(I2 == SimplifiedValuesVector[5].end()); + // Check that "%cmp3 = icmp ult i8* %iv2.0, %iv.0" is simplified to 0. + auto I3 = SimplifiedValuesVector[5].find(Cmp3); + EXPECT_TRUE(I3 != SimplifiedValuesVector[5].end()); + EXPECT_EQ(cast((*I1).second)->getZExtValue(), 0U); } TEST(UnrollAnalyzerTest, CastSimplifications) { diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp index 373a58d259af..42d0dd896d91 100644 --- a/llvm/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp +++ b/llvm/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp @@ -435,7 +435,13 @@ void TestAllForms() { EXPECT_EQ(AddrValue, toAddress(DieDG.find(Attr_Last), 0)); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version2Addr4AllForms) { +#else TEST(DWARFDebugInfo, TestDWARF32Version2Addr4AllForms) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 2, with 4 byte // addresses. typedef uint32_t AddrType; @@ -444,7 +450,13 @@ TEST(DWARFDebugInfo, TestDWARF32Version2Addr4AllForms) { TestAllForms<2, AddrType, RefAddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version2Addr8AllForms) { +#else TEST(DWARFDebugInfo, TestDWARF32Version2Addr8AllForms) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 2, with 4 byte // addresses. typedef uint64_t AddrType; @@ -453,7 +465,13 @@ TEST(DWARFDebugInfo, TestDWARF32Version2Addr8AllForms) { TestAllForms<2, AddrType, RefAddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version3Addr4AllForms) { +#else TEST(DWARFDebugInfo, TestDWARF32Version3Addr4AllForms) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 3, with 4 byte // addresses. typedef uint32_t AddrType; @@ -462,7 +480,13 @@ TEST(DWARFDebugInfo, TestDWARF32Version3Addr4AllForms) { TestAllForms<3, AddrType, RefAddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version3Addr8AllForms) { +#else TEST(DWARFDebugInfo, TestDWARF32Version3Addr8AllForms) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 3, with 8 byte // addresses. typedef uint64_t AddrType; @@ -471,7 +495,13 @@ TEST(DWARFDebugInfo, TestDWARF32Version3Addr8AllForms) { TestAllForms<3, AddrType, RefAddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version4Addr4AllForms) { +#else TEST(DWARFDebugInfo, TestDWARF32Version4Addr4AllForms) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 4, with 4 byte // addresses. typedef uint32_t AddrType; @@ -480,7 +510,13 @@ TEST(DWARFDebugInfo, TestDWARF32Version4Addr4AllForms) { TestAllForms<4, AddrType, RefAddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version4Addr8AllForms) { +#else TEST(DWARFDebugInfo, TestDWARF32Version4Addr8AllForms) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 4, with 8 byte // addresses. typedef uint64_t AddrType; @@ -489,7 +525,9 @@ TEST(DWARFDebugInfo, TestDWARF32Version4Addr8AllForms) { TestAllForms<4, AddrType, RefAddrType>(); } -#ifdef NO_SUPPORT_DEBUG_STR_OFFSETS +// EVM local begin +#if defined(NO_SUPPORT_DEBUG_STR_OFFSETS) || defined(_EVM) +// EVM local end TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version5Addr4AllForms) { #else TEST(DWARFDebugInfo, TestDWARF32Version5Addr4AllForms) { @@ -502,7 +540,9 @@ TEST(DWARFDebugInfo, TestDWARF32Version5Addr4AllForms) { TestAllForms<5, AddrType, RefAddrType>(); } -#ifdef NO_SUPPORT_DEBUG_STR_OFFSETS +// EVM local begin +#if defined(NO_SUPPORT_DEBUG_STR_OFFSETS) || defined(_EVM) +// EVM local end TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version5Addr8AllForms) { #else TEST(DWARFDebugInfo, TestDWARF32Version5Addr8AllForms) { @@ -603,42 +643,78 @@ template void TestChildren() { EXPECT_EQ(IntDieDG.getTag(), DW_TAG_base_type); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version2Addr4Children) { +#else TEST(DWARFDebugInfo, TestDWARF32Version2Addr4Children) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 2, with 4 byte // addresses. typedef uint32_t AddrType; TestChildren<2, AddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version2Addr8Children) { +#else TEST(DWARFDebugInfo, TestDWARF32Version2Addr8Children) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 2, with 8 byte // addresses. typedef uint64_t AddrType; TestChildren<2, AddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version3Addr4Children) { +#else TEST(DWARFDebugInfo, TestDWARF32Version3Addr4Children) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 3, with 4 byte // addresses. typedef uint32_t AddrType; TestChildren<3, AddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version3Addr8Children) { +#else TEST(DWARFDebugInfo, TestDWARF32Version3Addr8Children) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 3, with 8 byte // addresses. typedef uint64_t AddrType; TestChildren<3, AddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version4Addr4Children) { +#else TEST(DWARFDebugInfo, TestDWARF32Version4Addr4Children) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 4, with 4 byte // addresses. typedef uint32_t AddrType; TestChildren<4, AddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version4Addr8Children) { +#else TEST(DWARFDebugInfo, TestDWARF32Version4Addr8Children) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 4, with 8 byte // addresses. typedef uint64_t AddrType; @@ -853,42 +929,78 @@ template void TestReferences() { toDebugInfoReference(CU2ToCU1RefAddrDieDG.find(DW_AT_type), -1ULL)); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version2Addr4References) { +#else TEST(DWARFDebugInfo, TestDWARF32Version2Addr4References) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 2, with 4 byte // addresses. typedef uint32_t AddrType; TestReferences<2, AddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version2Addr8References) { +#else TEST(DWARFDebugInfo, TestDWARF32Version2Addr8References) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 2, with 8 byte // addresses. typedef uint64_t AddrType; TestReferences<2, AddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version3Addr4References) { +#else TEST(DWARFDebugInfo, TestDWARF32Version3Addr4References) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 3, with 4 byte // addresses. typedef uint32_t AddrType; TestReferences<3, AddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version3Addr8References) { +#else TEST(DWARFDebugInfo, TestDWARF32Version3Addr8References) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 3, with 8 byte // addresses. typedef uint64_t AddrType; TestReferences<3, AddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version4Addr4References) { +#else TEST(DWARFDebugInfo, TestDWARF32Version4Addr4References) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 4, with 4 byte // addresses. typedef uint32_t AddrType; TestReferences<4, AddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version4Addr8References) { +#else TEST(DWARFDebugInfo, TestDWARF32Version4Addr8References) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 4, with 8 byte // addresses. typedef uint64_t AddrType; @@ -1025,49 +1137,87 @@ template void TestAddresses() { EXPECT_EQ(HighPC, ActualHighPC); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version2Addr4Addresses) { +#else TEST(DWARFDebugInfo, TestDWARF32Version2Addr4Addresses) { +#endif +// EVM local end // Test that we can decode address values in DWARF32, version 2, with 4 byte // addresses. typedef uint32_t AddrType; TestAddresses<2, AddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version2Addr8Addresses) { +#else TEST(DWARFDebugInfo, TestDWARF32Version2Addr8Addresses) { +#endif +// EVM local end // Test that we can decode address values in DWARF32, version 2, with 8 byte // addresses. typedef uint64_t AddrType; TestAddresses<2, AddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version3Addr4Addresses) { +#else TEST(DWARFDebugInfo, TestDWARF32Version3Addr4Addresses) { +#endif +// EVM local end // Test that we can decode address values in DWARF32, version 3, with 4 byte // addresses. typedef uint32_t AddrType; TestAddresses<3, AddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version3Addr8Addresses) { +#else TEST(DWARFDebugInfo, TestDWARF32Version3Addr8Addresses) { +#endif +// EVM local end // Test that we can decode address values in DWARF32, version 3, with 8 byte // addresses. typedef uint64_t AddrType; TestAddresses<3, AddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version4Addr4Addresses) { +#else TEST(DWARFDebugInfo, TestDWARF32Version4Addr4Addresses) { +#endif +// EVM local end // Test that we can decode address values in DWARF32, version 4, with 4 byte // addresses. typedef uint32_t AddrType; TestAddresses<4, AddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version4Addr8Addresses) { +#else TEST(DWARFDebugInfo, TestDWARF32Version4Addr8Addresses) { +#endif +// EVM local end // Test that we can decode address values in DWARF32, version 4, with 8 byte // addresses. typedef uint64_t AddrType; TestAddresses<4, AddrType>(); } -#ifdef NO_SUPPORT_DEBUG_STR_OFFSETS +// EVM local begin +#if defined(NO_SUPPORT_DEBUG_STR_OFFSETS) || defined(_EVM) +// EVM local end TEST(DWARFDebugInfo, DISABLED_TestStringOffsets) { #else TEST(DWARFDebugInfo, TestStringOffsets) { @@ -1078,6 +1228,7 @@ TEST(DWARFDebugInfo, TestStringOffsets) { const char *String1 = "Hello"; const char *String2 = "World"; +// EVM local end auto ExpectedDG = dwarfgen::Generator::create(Triple, 5); ASSERT_THAT_EXPECTED(ExpectedDG, Succeeded()); @@ -1136,7 +1287,9 @@ TEST(DWARFDebugInfo, TestStringOffsets) { EXPECT_STREQ(String1, *Extracted3); } -#ifdef NO_SUPPORT_DEBUG_ADDR +// EVM local begin +#if defined(NO_SUPPORT_DEBUG_ADDR) || defined(_EVM) +// EVM local end TEST(DWARFDebugInfo, DISABLED_TestEmptyStringOffsets) { #else TEST(DWARFDebugInfo, TestEmptyStringOffsets) { @@ -1170,7 +1323,13 @@ TEST(DWARFDebugInfo, TestEmptyStringOffsets) { DwarfContext->getDWARFObj().getStrOffsetsSection().Data.empty()); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestRelations) { +#else TEST(DWARFDebugInfo, TestRelations) { +#endif +// EVM local end Triple Triple = getNormalizedDefaultTargetTriple(); if (!isConfigurationSupported(Triple)) GTEST_SKIP(); @@ -1357,7 +1516,13 @@ TEST(DWARFDebugInfo, TestDWARFDie) { EXPECT_FALSE(DefaultDie.getSibling().isValid()); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestChildIterators) { +#else TEST(DWARFDebugInfo, TestChildIterators) { +#endif +// EVM local end Triple Triple = getNormalizedDefaultTargetTriple(); if (!isConfigurationSupported(Triple)) GTEST_SKIP(); @@ -1422,7 +1587,13 @@ TEST(DWARFDebugInfo, TestChildIterators) { EXPECT_EQ(A.begin(), A.end()); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestChildIteratorsOnInvalidDie) { +#else TEST(DWARFDebugInfo, TestChildIteratorsOnInvalidDie) { +#endif +// EVM local end // Verify that an invalid DIE has no children. DWARFDie Invalid; auto begin = Invalid.begin(); @@ -1432,7 +1603,13 @@ TEST(DWARFDebugInfo, TestChildIteratorsOnInvalidDie) { EXPECT_EQ(begin, end); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestEmptyChildren) { +#else TEST(DWARFDebugInfo, TestEmptyChildren) { +#endif +// EVM local end const char *yamldata = "debug_abbrev:\n" " - Table:\n" " - Code: 0x00000001\n" @@ -1466,7 +1643,13 @@ TEST(DWARFDebugInfo, TestEmptyChildren) { EXPECT_EQ(CUDie.begin(), CUDie.end()); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestAttributeIterators) { +#else TEST(DWARFDebugInfo, TestAttributeIterators) { +#endif +// EVM local end Triple Triple = getNormalizedDefaultTargetTriple(); if (!isConfigurationSupported(Triple)) GTEST_SKIP(); @@ -1528,7 +1711,13 @@ TEST(DWARFDebugInfo, TestAttributeIterators) { EXPECT_EQ(E, ++I); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestFindRecurse) { +#else TEST(DWARFDebugInfo, TestFindRecurse) { +#endif +// EVM local end Triple Triple = getNormalizedDefaultTargetTriple(); if (!isConfigurationSupported(Triple)) GTEST_SKIP(); @@ -1618,7 +1807,13 @@ TEST(DWARFDebugInfo, TestFindRecurse) { EXPECT_EQ(AbsDieName, StringOpt.value_or(nullptr)); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestSelfRecursiveType) { +#else TEST(DWARFDebugInfo, TestSelfRecursiveType) { +#endif +// EVM local end typedef uint32_t AddrType; Triple Triple = getDefaultTargetTripleForAddrSize(sizeof(AddrType)); if (!isConfigurationSupported(Triple)) @@ -1654,7 +1849,13 @@ TEST(DWARFDebugInfo, TestSelfRecursiveType) { } } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDwarfToFunctions) { +#else TEST(DWARFDebugInfo, TestDwarfToFunctions) { +#endif +// EVM local end // Test all of the dwarf::toXXX functions that take a // std::optional and extract the values from it. uint64_t InvalidU64 = 0xBADBADBADBADBADB; @@ -1864,7 +2065,13 @@ TEST(DWARFDebugInfo, TestDwarfToFunctions) { // Test } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestFindAttrs) { +#else TEST(DWARFDebugInfo, TestFindAttrs) { +#endif +// EVM local end Triple Triple = getNormalizedDefaultTargetTriple(); if (!isConfigurationSupported(Triple)) GTEST_SKIP(); @@ -1927,7 +2134,9 @@ TEST(DWARFDebugInfo, TestFindAttrs) { EXPECT_EQ(DieMangled, toString(NameOpt, "")); } -#ifdef NO_SUPPORT_DEBUG_ADDR +// EVM local begin +#if defined(NO_SUPPORT_DEBUG_ADDR) || defined(_EVM) +// EVM local end TEST(DWARFDebugInfo, DISABLED_TestImplicitConstAbbrevs) { #else TEST(DWARFDebugInfo, TestImplicitConstAbbrevs) { @@ -2055,7 +2264,13 @@ TEST(DWARFDebugInfo, TestImplicitConstAbbrevs) { EXPECT_EQ(DIEs.find(Val2)->second, AbbrevPtrVal2); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARFDieRangeInfoContains) { +#else TEST(DWARFDebugInfo, TestDWARFDieRangeInfoContains) { +#endif +// EVM local end DWARFVerifier::DieRangeInfo Empty; ASSERT_TRUE(Empty.contains(Empty)); diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp index 980b627625ee..30b804bbc883 100644 --- a/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp +++ b/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp @@ -186,7 +186,13 @@ void checkDefaultPrologue(uint16_t Version, DwarfFormat Format, EXPECT_STREQ(*toString(Prologue.FileNames[0].Name), "a file"); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, DISABLED_GetOrParseLineTableAtInvalidOffset) { +#else TEST_F(DebugLineBasicFixture, GetOrParseLineTableAtInvalidOffset) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); generate(); @@ -208,7 +214,14 @@ TEST_F(DebugLineBasicFixture, GetOrParseLineTableAtInvalidOffset) { "offset 0x00000001 is not a valid debug line section offset")); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, + DISABLED_GetOrParseLineTableAtInvalidOffsetAfterData) { +#else TEST_F(DebugLineBasicFixture, GetOrParseLineTableAtInvalidOffsetAfterData) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -229,7 +242,9 @@ TEST_F(DebugLineBasicFixture, GetOrParseLineTableAtInvalidOffsetAfterData) { "offset 0x00000001 is not a valid debug line section offset")); } -#ifdef NO_SUPPORT_DEBUG_ADDR +// EVM local begin +#if defined(NO_SUPPORT_DEBUG_ADDR) || defined(_EVM) +// EVM local end TEST_P(DebugLineParameterisedFixture, DISABLED_PrologueGetLength) { #else TEST_P(DebugLineParameterisedFixture, PrologueGetLength) { @@ -256,7 +271,9 @@ TEST_P(DebugLineParameterisedFixture, PrologueGetLength) { EXPECT_EQ((*ExpectedLineTable)->Prologue.getLength(), ExpectedLength); } -#ifdef NO_SUPPORT_DEBUG_ADDR +// EVM local begin +#if defined(NO_SUPPORT_DEBUG_ADDR) || defined(_EVM) +// EVM local end TEST_P(DebugLineParameterisedFixture, DISABLED_GetOrParseLineTableValidTable) { #else TEST_P(DebugLineParameterisedFixture, GetOrParseLineTableValidTable) { @@ -326,7 +343,9 @@ TEST_P(DebugLineParameterisedFixture, GetOrParseLineTableValidTable) { // correctly. } -#ifdef NO_SUPPORT_DEBUG_ADDR +// EVM local begin +#if defined(NO_SUPPORT_DEBUG_ADDR) || defined(_EVM) +// EVM local end TEST_P(DebugLineParameterisedFixture, DISABLED_ClearLineValidTable) { #else TEST_P(DebugLineParameterisedFixture, ClearLineValidTable) { @@ -402,7 +421,13 @@ TEST_P(DebugLineParameterisedFixture, ClearLineValidTable) { EXPECT_EQ(Expected4->Sequences.size(), 2u); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, DISABLED_ErrorForReservedLength) { +#else TEST_F(DebugLineBasicFixture, ErrorForReservedLength) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -425,7 +450,14 @@ struct DebugLineUnsupportedVersionFixture : public TestWithParam, uint16_t Version; }; +// EVM local begin +#if defined(_EVM) +TEST_P(DebugLineUnsupportedVersionFixture, + DISABLED_ErrorForUnsupportedVersion) { +#else TEST_P(DebugLineUnsupportedVersionFixture, ErrorForUnsupportedVersion) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -447,7 +479,9 @@ INSTANTIATE_TEST_SUITE_P(UnsupportedVersionTestParams, Values(/*1 below min */ 1, /* 1 above max */ 6, /* Maximum possible */ 0xffff)); -#ifdef NO_SUPPORT_DEBUG_ADDR +// EVM local begin +#if defined(NO_SUPPORT_DEBUG_ADDR) || defined(_EVM) +// EVM local end TEST_F(DebugLineBasicFixture, DISABLED_ErrorForInvalidV5IncludeDirTable) { #else TEST_F(DebugLineBasicFixture, ErrorForInvalidV5IncludeDirTable) { @@ -492,7 +526,9 @@ TEST_F(DebugLineBasicFixture, ErrorForInvalidV5IncludeDirTable) { "found")); } -#ifdef NO_SUPPORT_DEBUG_ADDR +// EVM local begin +#if defined(NO_SUPPORT_DEBUG_ADDR) || defined(_EVM) +// EVM local end TEST_P(DebugLineParameterisedFixture, DISABLED_ErrorForTooLargePrologueLength) { #else TEST_P(DebugLineParameterisedFixture, ErrorForTooLargePrologueLength) { @@ -532,7 +568,9 @@ TEST_P(DebugLineParameterisedFixture, ErrorForTooLargePrologueLength) { .str())); } -#ifdef NO_SUPPORT_DEBUG_ADDR +// EVM local begin +#if defined(NO_SUPPORT_DEBUG_ADDR) || defined(_EVM) +// EVM local end TEST_P(DebugLineParameterisedFixture, DISABLED_ErrorForTooShortPrologueLength) { #else TEST_P(DebugLineParameterisedFixture, ErrorForTooShortPrologueLength) { @@ -590,7 +628,14 @@ INSTANTIATE_TEST_SUITE_P( std::make_pair(4, DWARF64), // Test v4 fields and DWARF64. std::make_pair(5, DWARF32), std::make_pair(5, DWARF64))); +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, + DISABLED_ErrorForExtendedOpcodeLengthSmallerThanExpected) { +#else TEST_F(DebugLineBasicFixture, ErrorForExtendedOpcodeLengthSmallerThanExpected) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -619,7 +664,14 @@ TEST_F(DebugLineBasicFixture, ErrorForExtendedOpcodeLengthSmallerThanExpected) { EXPECT_EQ((*ExpectedLineTable)->Rows[1].Discriminator, DW_LNS_negate_stmt); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, + DISABLED_ErrorForExtendedOpcodeLengthLargerThanExpected) { +#else TEST_F(DebugLineBasicFixture, ErrorForExtendedOpcodeLengthLargerThanExpected) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -648,7 +700,13 @@ TEST_F(DebugLineBasicFixture, ErrorForExtendedOpcodeLengthLargerThanExpected) { EXPECT_EQ((*ExpectedLineTable)->Rows[2].IsStmt, 1u); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, DISABLED_ErrorForUnitLengthTooLarge) { +#else TEST_F(DebugLineBasicFixture, ErrorForUnitLengthTooLarge) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -677,7 +735,13 @@ TEST_F(DebugLineBasicFixture, ErrorForUnitLengthTooLarge) { EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, DISABLED_ErrorForMismatchedAddressSize) { +#else TEST_F(DebugLineBasicFixture, ErrorForMismatchedAddressSize) { +#endif +// EVM local end if (!setupGenerator(4, 8)) GTEST_SKIP(); @@ -706,8 +770,15 @@ TEST_F(DebugLineBasicFixture, ErrorForMismatchedAddressSize) { EXPECT_EQ((*ExpectedLineTable)->Rows[1].Address.Address, Addr2); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, + DISABLED_ErrorForMismatchedAddressSizeUnsetInitialAddress) { +#else TEST_F(DebugLineBasicFixture, ErrorForMismatchedAddressSizeUnsetInitialAddress) { +#endif +// EVM local end if (!setupGenerator(4, 0)) GTEST_SKIP(); @@ -733,8 +804,15 @@ TEST_F(DebugLineBasicFixture, EXPECT_EQ((*ExpectedLineTable)->Rows[1].Address.Address, Addr2); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, + DISABLED_ErrorForUnsupportedAddressSizeInSetAddressLength) { +#else TEST_F(DebugLineBasicFixture, ErrorForUnsupportedAddressSizeInSetAddressLength) { +#endif +// EVM local end // Use DWARF v4, and 0 for data extractor address size so that the address // size is derived from the opcode length. if (!setupGenerator(4, 0)) @@ -766,7 +844,13 @@ TEST_F(DebugLineBasicFixture, EXPECT_EQ((*ExpectedLineTable)->Rows[0].Address.Address, 0u); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, DISABLED_ErrorForAddressSizeGreaterThanByteSize) { +#else TEST_F(DebugLineBasicFixture, ErrorForAddressSizeGreaterThanByteSize) { +#endif +// EVM local end // Use DWARF v4, and 0 for data extractor address size so that the address // size is derived from the opcode length. if (!setupGenerator(4, 0)) @@ -790,7 +874,9 @@ TEST_F(DebugLineBasicFixture, ErrorForAddressSizeGreaterThanByteSize) { ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded()); } -#ifdef NO_SUPPORT_DEBUG_ADDR +// EVM local begin +#if defined(NO_SUPPORT_DEBUG_ADDR) || defined(_EVM) +// EVM local end TEST_F(DebugLineBasicFixture, DISABLED_ErrorForUnsupportedAddressSizeDefinedInHeader) { #else @@ -834,7 +920,13 @@ TEST_F(DebugLineBasicFixture, ErrorForUnsupportedAddressSizeDefinedInHeader) { EXPECT_EQ((*ExpectedLineTable)->Rows[0].Address.Address, 0u); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, DISABLED_CallbackUsedForUnterminatedSequence) { +#else TEST_F(DebugLineBasicFixture, CallbackUsedForUnterminatedSequence) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -1048,7 +1140,13 @@ struct OpIndexFixture : Test, CommonFixture { } }; +// EVM local begin +#if defined(_EVM) +TEST_F(OpIndexFixture, DISABLED_OpIndexAdvance) { +#else TEST_F(OpIndexFixture, OpIndexAdvance) { +#endif +// EVM local end if (!setupGenerator(4, 4)) GTEST_SKIP(); @@ -1115,7 +1213,13 @@ TEST_F(OpIndexFixture, OpIndexAdvance) { VerifyRow((*Table)->Rows[5], 0x50, 4, 110); } +// EVM local begin +#if defined(_EVM) +TEST_F(OpIndexFixture, DISABLED_OpIndexReset) { +#else TEST_F(OpIndexFixture, OpIndexReset) { +#endif +// EVM local end if (!setupGenerator(4, 4)) GTEST_SKIP(); @@ -1178,7 +1282,13 @@ TEST_F(OpIndexFixture, OpIndexReset) { EXPECT_EQ((*Table)->Rows[7].OpIndex, 0u); } +// EVM local begin +#if defined(_EVM) +TEST_F(OpIndexFixture, DISABLED_MaxOpsZeroDwarf3) { +#else TEST_F(OpIndexFixture, MaxOpsZeroDwarf3) { +#endif +// EVM local end if (!setupGenerator(3, 4)) GTEST_SKIP(); @@ -1195,7 +1305,13 @@ TEST_F(OpIndexFixture, MaxOpsZeroDwarf3) { ASSERT_THAT_EXPECTED(Table, Succeeded()); } +// EVM local begin +#if defined(_EVM) +TEST_F(OpIndexFixture, DISABLED_MaxOpsZeroDwarf4) { +#else TEST_F(OpIndexFixture, MaxOpsZeroDwarf4) { +#endif +// EVM local end if (!setupGenerator(4, 4)) GTEST_SKIP(); @@ -1247,7 +1363,13 @@ struct LineRangeFixture : TestWithParam>, uint8_t LineRange; }; +// EVM local begin +#if defined(_EVM) +TEST_P(LineRangeFixture, DISABLED_LineRangeProblemsReportedCorrectly) { +#else TEST_P(LineRangeFixture, LineRangeProblemsReportedCorrectly) { +#endif +// EVM local end runTest(/*CheckAdvancePC=*/false, "but the prologue line_range value is 0. The address and line will " "not be adjusted"); @@ -1282,7 +1404,13 @@ struct BadMinInstLenFixture : TestWithParam>, uint8_t MinInstLength; }; +// EVM local begin +#if defined(_EVM) +TEST_P(BadMinInstLenFixture, DISABLED_MinInstLengthProblemsReportedCorrectly) { +#else TEST_P(BadMinInstLenFixture, MinInstLengthProblemsReportedCorrectly) { +#endif +// EVM local end runTest(/*CheckAdvancePC=*/true, "but the prologue minimum_instruction_length value is 0, which " "prevents any address advancing"); @@ -1293,7 +1421,13 @@ INSTANTIATE_TEST_SUITE_P( Values(std::make_tuple(0, true), // Test zero value (error). std::make_tuple(1, false))); // Test non-zero value (no error). +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, DISABLED_ParserParsesCorrectly) { +#else TEST_F(DebugLineBasicFixture, ParserParsesCorrectly) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -1320,7 +1454,13 @@ TEST_F(DebugLineBasicFixture, ParserParsesCorrectly) { EXPECT_FALSE(Unrecoverable); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, DISABLED_ParserSkipsCorrectly) { +#else TEST_F(DebugLineBasicFixture, ParserSkipsCorrectly) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -1341,7 +1481,13 @@ TEST_F(DebugLineBasicFixture, ParserSkipsCorrectly) { EXPECT_FALSE(Unrecoverable); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, DISABLED_ParserAlwaysDoneForEmptySection) { +#else TEST_F(DebugLineBasicFixture, ParserAlwaysDoneForEmptySection) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -1351,7 +1497,14 @@ TEST_F(DebugLineBasicFixture, ParserAlwaysDoneForEmptySection) { EXPECT_TRUE(Parser.done()); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, + DISABLED_ParserMarkedAsDoneForBadLengthWhenParsing) { +#else TEST_F(DebugLineBasicFixture, ParserMarkedAsDoneForBadLengthWhenParsing) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -1374,7 +1527,14 @@ TEST_F(DebugLineBasicFixture, ParserMarkedAsDoneForBadLengthWhenParsing) { "reserved unit length of value 0xfffffff0")); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, + DISABLED_ParserMarkedAsDoneForBadLengthWhenSkipping) { +#else TEST_F(DebugLineBasicFixture, ParserMarkedAsDoneForBadLengthWhenSkipping) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -1397,7 +1557,14 @@ TEST_F(DebugLineBasicFixture, ParserMarkedAsDoneForBadLengthWhenSkipping) { "reserved unit length of value 0xfffffff0")); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, + DISABLED_ParserReportsFirstErrorInEachTableWhenParsing) { +#else TEST_F(DebugLineBasicFixture, ParserReportsFirstErrorInEachTableWhenParsing) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -1423,7 +1590,14 @@ TEST_F(DebugLineBasicFixture, ParserReportsFirstErrorInEachTableWhenParsing) { "unsupported version 1")); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, + DISABLED_ParserReportsNonPrologueProblemsWhenParsing) { +#else TEST_F(DebugLineBasicFixture, ParserReportsNonPrologueProblemsWhenParsing) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -1455,8 +1629,15 @@ TEST_F(DebugLineBasicFixture, ParserReportsNonPrologueProblemsWhenParsing) { EXPECT_FALSE(Unrecoverable); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, + DISABLED_ParserReportsPrologueErrorsInEachTableWhenSkipping) { +#else TEST_F(DebugLineBasicFixture, ParserReportsPrologueErrorsInEachTableWhenSkipping) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -1482,7 +1663,14 @@ TEST_F(DebugLineBasicFixture, "unsupported version 1")); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, + DISABLED_ParserIgnoresNonPrologueErrorsWhenSkipping) { +#else TEST_F(DebugLineBasicFixture, ParserIgnoresNonPrologueErrorsWhenSkipping) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -1498,7 +1686,9 @@ TEST_F(DebugLineBasicFixture, ParserIgnoresNonPrologueErrorsWhenSkipping) { EXPECT_FALSE(Unrecoverable); } -#ifdef NO_SUPPORT_DEBUG_ADDR +// EVM local begin +#if defined(NO_SUPPORT_DEBUG_ADDR) || defined(_EVM) +// EVM local end TEST_F(DebugLineBasicFixture, DISABLED_VerboseOutput) { #else TEST_F(DebugLineBasicFixture, VerboseOutput) { @@ -1664,7 +1854,9 @@ struct TruncatedPrologueFixture StringRef ExpectedErr; }; -#ifdef NO_SUPPORT_DEBUG_ADDR +// EVM local begin +#if defined(NO_SUPPORT_DEBUG_ADDR) || defined(_EVM) +// EVM local end TEST_P(TruncatedPrologueFixture, DISABLED_ErrorForTruncatedPrologue) { #else TEST_P(TruncatedPrologueFixture, ErrorForTruncatedPrologue) { @@ -1845,7 +2037,14 @@ struct TruncatedExtendedOpcodeFixture uint64_t OpcodeLength; }; +// EVM local begin +#if defined(_EVM) +TEST_P(TruncatedExtendedOpcodeFixture, + DISABLED_ErrorForTruncatedExtendedOpcode) { +#else TEST_P(TruncatedExtendedOpcodeFixture, ErrorForTruncatedExtendedOpcode) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); LineTable < = setupTable(); @@ -1924,7 +2123,14 @@ INSTANTIATE_TEST_SUITE_P( "unexpected end of data at offset 0x35 while reading [0x32, " "0x36)"))); +// EVM local begin +#if defined(_EVM) +TEST_P(TruncatedStandardOpcodeFixture, + DISABLED_ErrorForTruncatedStandardOpcode) { +#else TEST_P(TruncatedStandardOpcodeFixture, ErrorForTruncatedStandardOpcode) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); LineTable < = setupTable(); @@ -1980,7 +2186,9 @@ INSTANTIATE_TEST_SUITE_P( "unable to decode LEB128 at offset 0x00000032: " "malformed uleb128, extends past end"))); -#ifdef NO_SUPPORT_DEBUG_ADDR +// EVM local begin +#if defined(NO_SUPPORT_DEBUG_ADDR) || defined(_EVM) +// EVM local end TEST_F(DebugLineBasicFixture, DISABLED_PrintPathsProperly) { #else TEST_F(DebugLineBasicFixture, PrintPathsProperly) { diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFDieManualExtractTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDieManualExtractTest.cpp index ea54f321e441..2ba1672c9106 100644 --- a/llvm/unittests/DebugInfo/DWARF/DWARFDieManualExtractTest.cpp +++ b/llvm/unittests/DebugInfo/DWARF/DWARFDieManualExtractTest.cpp @@ -21,7 +21,11 @@ using namespace utils; namespace { +#if defined(_EVM) +TEST(DWARFDie, DISABLED_manualExtractDump) { +#else TEST(DWARFDie, manualExtractDump) { +#endif typedef uint32_t AddrType; uint16_t Version = 4; Triple Triple = getDefaultTargetTripleForAddrSize(sizeof(AddrType)); diff --git a/llvm/unittests/IR/ConstantRangeTest.cpp b/llvm/unittests/IR/ConstantRangeTest.cpp index 0181e2ce6ac9..618b30999315 100644 --- a/llvm/unittests/IR/ConstantRangeTest.cpp +++ b/llvm/unittests/IR/ConstantRangeTest.cpp @@ -366,10 +366,10 @@ TEST_F(ConstantRangeTest, GetMinsAndMaxes) { EXPECT_EQ(Some.getSignedMax(), APInt(16, 0xaa9)); EXPECT_EQ(Wrap.getSignedMax(), APInt(16, INT16_MAX)); - EXPECT_EQ(Full.getSignedMin(), APInt(16, (uint64_t)INT16_MIN)); + EXPECT_EQ(Full.getSignedMin(), APInt(16, (uint16_t)INT16_MIN)); EXPECT_EQ(One.getSignedMin(), APInt(16, 0xa)); EXPECT_EQ(Some.getSignedMin(), APInt(16, 0xa)); - EXPECT_EQ(Wrap.getSignedMin(), APInt(16, (uint64_t)INT16_MIN)); + EXPECT_EQ(Wrap.getSignedMin(), APInt(16, (uint16_t)INT16_MIN)); // Found by Klee EXPECT_EQ(ConstantRange(APInt(4, 7), APInt(4, 0)).getSignedMax(), @@ -481,7 +481,7 @@ TEST_F(ConstantRangeTest, SExt) { APInt(20, INT16_MAX + 1, true))); EXPECT_EQ(ConstantRange(APInt(8, 120), APInt(8, 140)).signExtend(16), - ConstantRange(APInt(16, -128), APInt(16, 128))); + ConstantRange(APInt(16, -128, true), APInt(16, 128))); EXPECT_EQ(ConstantRange(APInt(16, 0x0200), APInt(16, 0x8000)).signExtend(19), ConstantRange(APInt(19, 0x0200), APInt(19, 0x8000))); @@ -510,7 +510,7 @@ TEST_F(ConstantRangeTest, IntersectWith) { EXPECT_TRUE(LHS.intersectWith(RHS) == LHS); // previous bug: intersection of [min, 3) and [2, max) should be 2 - LHS = ConstantRange(APInt(32, -2147483646), APInt(32, 3)); + LHS = ConstantRange(APInt(32, (uint32_t)-2147483646), APInt(32, 3)); RHS = ConstantRange(APInt(32, 2), APInt(32, 2147483646)); EXPECT_EQ(LHS.intersectWith(RHS), ConstantRange(APInt(32, 2))); @@ -738,45 +738,51 @@ TEST_F(ConstantRangeTest, AddWithNoWrap) { EXPECT_NE(Some.addWithNoWrap(Full, OBO::NoSignedWrap), Full); EXPECT_EQ(Full.addWithNoWrap(ConstantRange(APInt(16, 1), APInt(16, 2)), OBO::NoSignedWrap), - ConstantRange(APInt(16, INT16_MIN + 1), APInt(16, INT16_MIN))); + ConstantRange(APInt(16, INT16_MIN + 1, true), + APInt(16, INT16_MIN, true))); EXPECT_EQ(ConstantRange(APInt(16, 1), APInt(16, 2)) .addWithNoWrap(Full, OBO::NoSignedWrap), - ConstantRange(APInt(16, INT16_MIN + 1), APInt(16, INT16_MIN))); - EXPECT_EQ(Full.addWithNoWrap(ConstantRange(APInt(16, -1), APInt(16, 0)), + ConstantRange(APInt(16, INT16_MIN + 1, true), + APInt(16, INT16_MIN, true))); + EXPECT_EQ(Full.addWithNoWrap(ConstantRange(APInt(16, -1, true), APInt(16, 0)), OBO::NoSignedWrap), - ConstantRange(APInt(16, INT16_MIN), APInt(16, INT16_MAX))); + ConstantRange(APInt(16, INT16_MIN, true), APInt(16, INT16_MAX))); EXPECT_EQ(ConstantRange(APInt(8, 100), APInt(8, 120)) .addWithNoWrap(ConstantRange(APInt(8, 120), APInt(8, 123)), OBO::NoSignedWrap), ConstantRange(8, false)); - EXPECT_EQ(ConstantRange(APInt(8, -120), APInt(8, -100)) - .addWithNoWrap(ConstantRange(APInt(8, -110), APInt(8, -100)), - OBO::NoSignedWrap), + EXPECT_EQ(ConstantRange(APInt(8, -120, true), APInt(8, -100, true)) + .addWithNoWrap( + ConstantRange(APInt(8, -110, true), APInt(8, -100, true)), + OBO::NoSignedWrap), ConstantRange(8, false)); - EXPECT_EQ(ConstantRange(APInt(8, 0), APInt(8, 101)) - .addWithNoWrap(ConstantRange(APInt(8, -128), APInt(8, 28)), - OBO::NoSignedWrap), - ConstantRange(8, true)); - EXPECT_EQ(ConstantRange(APInt(8, 0), APInt(8, 101)) - .addWithNoWrap(ConstantRange(APInt(8, -120), APInt(8, 29)), - OBO::NoSignedWrap), - ConstantRange(APInt(8, -120), APInt(8, -128))); - EXPECT_EQ(ConstantRange(APInt(8, -50), APInt(8, 50)) + EXPECT_EQ( + ConstantRange(APInt(8, 0), APInt(8, 101)) + .addWithNoWrap(ConstantRange(APInt(8, -128, true), APInt(8, 28)), + OBO::NoSignedWrap), + ConstantRange(8, true)); + EXPECT_EQ( + ConstantRange(APInt(8, 0), APInt(8, 101)) + .addWithNoWrap(ConstantRange(APInt(8, -120, true), APInt(8, 29)), + OBO::NoSignedWrap), + ConstantRange(APInt(8, -120, true), APInt(8, -128, true))); + EXPECT_EQ(ConstantRange(APInt(8, -50, true), APInt(8, 50)) .addWithNoWrap(ConstantRange(APInt(8, 10), APInt(8, 20)), OBO::NoSignedWrap), - ConstantRange(APInt(8, -40), APInt(8, 69))); + ConstantRange(APInt(8, -40, true), APInt(8, 69))); EXPECT_EQ(ConstantRange(APInt(8, 10), APInt(8, 20)) - .addWithNoWrap(ConstantRange(APInt(8, -50), APInt(8, 50)), + .addWithNoWrap(ConstantRange(APInt(8, -50, true), APInt(8, 50)), OBO::NoSignedWrap), - ConstantRange(APInt(8, -40), APInt(8, 69))); - EXPECT_EQ(ConstantRange(APInt(8, 120), APInt(8, -10)) + ConstantRange(APInt(8, -40, true), APInt(8, 69))); + EXPECT_EQ(ConstantRange(APInt(8, 120), APInt(8, -10, true)) .addWithNoWrap(ConstantRange(APInt(8, 5), APInt(8, 20)), OBO::NoSignedWrap), ConstantRange(APInt(8, 125), APInt(8, 9))); - EXPECT_EQ(ConstantRange(APInt(8, 5), APInt(8, 20)) - .addWithNoWrap(ConstantRange(APInt(8, 120), APInt(8, -10)), - OBO::NoSignedWrap), - ConstantRange(APInt(8, 125), APInt(8, 9))); + EXPECT_EQ( + ConstantRange(APInt(8, 5), APInt(8, 20)) + .addWithNoWrap(ConstantRange(APInt(8, 120), APInt(8, -10, true)), + OBO::NoSignedWrap), + ConstantRange(APInt(8, 125), APInt(8, 9))); TestBinaryOpExhaustive( [](const ConstantRange &CR1, const ConstantRange &CR2) { @@ -821,15 +827,15 @@ TEST_F(ConstantRangeTest, AddWithNoWrap) { EXPECT_EQ(ConstantRange(APInt(8, 10), APInt(8, 20)) .addWithNoWrap(ConstantRange(APInt(8, 50), APInt(8, 200)), OBO::NoUnsignedWrap), - ConstantRange(APInt(8, 60), APInt(8, -37))); - EXPECT_EQ(ConstantRange(APInt(8, 20), APInt(8, -30)) + ConstantRange(APInt(8, 60), APInt(8, -37, true))); + EXPECT_EQ(ConstantRange(APInt(8, 20), APInt(8, -30, true)) .addWithNoWrap(ConstantRange(APInt(8, 5), APInt(8, 20)), OBO::NoUnsignedWrap), - ConstantRange(APInt(8, 25), APInt(8, -11))); + ConstantRange(APInt(8, 25), APInt(8, -11, true))); EXPECT_EQ(ConstantRange(APInt(8, 5), APInt(8, 20)) - .addWithNoWrap(ConstantRange(APInt(8, 20), APInt(8, -30)), + .addWithNoWrap(ConstantRange(APInt(8, 20), APInt(8, -30, true)), OBO::NoUnsignedWrap), - ConstantRange(APInt(8, 25), APInt(8, -11))); + ConstantRange(APInt(8, 25), APInt(8, -11, true))); TestBinaryOpExhaustive( [](const ConstantRange &CR1, const ConstantRange &CR2) { @@ -847,7 +853,7 @@ TEST_F(ConstantRangeTest, AddWithNoWrap) { EXPECT_EQ(ConstantRange(APInt(8, 50), APInt(8, 100)) .addWithNoWrap(ConstantRange(APInt(8, 20), APInt(8, 70)), OBO::NoSignedWrap), - ConstantRange(APInt(8, 70), APInt(8, -128))); + ConstantRange(APInt(8, 70), APInt(8, -128, true))); EXPECT_EQ(ConstantRange(APInt(8, 50), APInt(8, 100)) .addWithNoWrap(ConstantRange(APInt(8, 20), APInt(8, 70)), OBO::NoUnsignedWrap), @@ -855,17 +861,17 @@ TEST_F(ConstantRangeTest, AddWithNoWrap) { EXPECT_EQ(ConstantRange(APInt(8, 50), APInt(8, 100)) .addWithNoWrap(ConstantRange(APInt(8, 20), APInt(8, 70)), OBO::NoUnsignedWrap | OBO::NoSignedWrap), - ConstantRange(APInt(8, 70), APInt(8, -128))); + ConstantRange(APInt(8, 70), APInt(8, -128, true))); - EXPECT_EQ(ConstantRange(APInt(8, -100), APInt(8, -50)) + EXPECT_EQ(ConstantRange(APInt(8, -100, true), APInt(8, -50, true)) .addWithNoWrap(ConstantRange(APInt(8, 20), APInt(8, 30)), OBO::NoSignedWrap), - ConstantRange(APInt(8, -80), APInt(8, -21))); - EXPECT_EQ(ConstantRange(APInt(8, -100), APInt(8, -50)) + ConstantRange(APInt(8, -80, true), APInt(8, -21, true))); + EXPECT_EQ(ConstantRange(APInt(8, -100, true), APInt(8, -50, true)) .addWithNoWrap(ConstantRange(APInt(8, 20), APInt(8, 30)), OBO::NoUnsignedWrap), ConstantRange(APInt(8, 176), APInt(8, 235))); - EXPECT_EQ(ConstantRange(APInt(8, -100), APInt(8, -50)) + EXPECT_EQ(ConstantRange(APInt(8, -100, true), APInt(8, -50, true)) .addWithNoWrap(ConstantRange(APInt(8, 20), APInt(8, 30)), OBO::NoUnsignedWrap | OBO::NoSignedWrap), ConstantRange(APInt(8, 176), APInt(8, 235))); @@ -998,17 +1004,17 @@ TEST_F(ConstantRangeTest, Multiply) { ConstantRange(APInt(8, 250), APInt(8, 253))); // TODO: This should be return [-2, 0] - EXPECT_EQ(ConstantRange(APInt(8, -2)).multiply( - ConstantRange(APInt(8, 0), APInt(8, 2))), - ConstantRange(APInt(8, -2), APInt(8, 1))); + EXPECT_EQ(ConstantRange(APInt(8, -2, true)) + .multiply(ConstantRange(APInt(8, 0), APInt(8, 2))), + ConstantRange(APInt(8, -2, true), APInt(8, 1))); // Multiplication by -1 should give precise results. - EXPECT_EQ(ConstantRange(APInt(8, 3), APInt(8, -11)) - .multiply(ConstantRange(APInt(8, -1))), - ConstantRange(APInt(8, 12), APInt(8, -2))); - EXPECT_EQ(ConstantRange(APInt(8, -1)) - .multiply(ConstantRange(APInt(8, 3), APInt(8, -11))), - ConstantRange(APInt(8, 12), APInt(8, -2))); + EXPECT_EQ(ConstantRange(APInt(8, 3), APInt(8, -11, true)) + .multiply(ConstantRange(APInt(8, -1, true))), + ConstantRange(APInt(8, 12), APInt(8, -2, true))); + EXPECT_EQ(ConstantRange(APInt(8, -1, true)) + .multiply(ConstantRange(APInt(8, 3), APInt(8, -11, true))), + ConstantRange(APInt(8, 12), APInt(8, -2, true))); TestBinaryOpExhaustive( [](const ConstantRange &CR1, const ConstantRange &CR2) { @@ -1161,11 +1167,11 @@ TEST_F(ConstantRangeTest, SMax) { EXPECT_EQ(Empty.smax(Wrap), Empty); EXPECT_EQ(Empty.smax(One), Empty); EXPECT_EQ(Some.smax(Some), Some); - EXPECT_EQ(Some.smax(Wrap), ConstantRange(APInt(16, 0xa), - APInt(16, (uint64_t)INT16_MIN))); + EXPECT_EQ(Some.smax(Wrap), + ConstantRange(APInt(16, 0xa), APInt(16, (uint16_t)INT16_MIN))); EXPECT_EQ(Some.smax(One), Some); - EXPECT_EQ(Wrap.smax(One), ConstantRange(APInt(16, 0xa), - APInt(16, (uint64_t)INT16_MIN))); + EXPECT_EQ(Wrap.smax(One), + ConstantRange(APInt(16, 0xa), APInt(16, (uint16_t)INT16_MIN))); EXPECT_EQ(One.smax(One), One); TestBinaryOpExhaustive( @@ -1207,20 +1213,20 @@ TEST_F(ConstantRangeTest, UMin) { TEST_F(ConstantRangeTest, SMin) { EXPECT_EQ(Full.smin(Full), Full); EXPECT_EQ(Full.smin(Empty), Empty); - EXPECT_EQ(Full.smin(Some), ConstantRange(APInt(16, (uint64_t)INT16_MIN), - APInt(16, 0xaaa))); + EXPECT_EQ(Full.smin(Some), + ConstantRange(APInt(16, (uint16_t)INT16_MIN), APInt(16, 0xaaa))); EXPECT_EQ(Full.smin(Wrap), Full); EXPECT_EQ(Empty.smin(Empty), Empty); EXPECT_EQ(Empty.smin(Some), Empty); EXPECT_EQ(Empty.smin(Wrap), Empty); EXPECT_EQ(Empty.smin(One), Empty); EXPECT_EQ(Some.smin(Some), Some); - EXPECT_EQ(Some.smin(Wrap), ConstantRange(APInt(16, (uint64_t)INT16_MIN), - APInt(16, 0xaaa))); + EXPECT_EQ(Some.smin(Wrap), + ConstantRange(APInt(16, (uint16_t)INT16_MIN), APInt(16, 0xaaa))); EXPECT_EQ(Some.smin(One), One); EXPECT_EQ(Wrap.smin(Wrap), Wrap); - EXPECT_EQ(Wrap.smin(One), ConstantRange(APInt(16, (uint64_t)INT16_MIN), - APInt(16, 0xb))); + EXPECT_EQ(Wrap.smin(One), + ConstantRange(APInt(16, (uint16_t)INT16_MIN), APInt(16, 0xb))); EXPECT_EQ(One.smin(One), One); TestBinaryOpExhaustive( @@ -1296,8 +1302,8 @@ TEST_F(ConstantRangeTest, SDiv) { } // If there is a non-full signed envelope, that should be the result. - APInt SMin(Bits, Results.find_first() - Bias); - APInt SMax(Bits, Results.find_last() - Bias); + APInt SMin(Bits, Results.find_first() - Bias, true); + APInt SMax(Bits, Results.find_last() - Bias, true); ConstantRange Envelope = ConstantRange::getNonEmpty(SMin, SMax + 1); if (!Envelope.isFullSet()) { EXPECT_EQ(Envelope, CR); @@ -1316,8 +1322,8 @@ TEST_F(ConstantRangeTest, SDiv) { --LastPos; } - APInt WMax(Bits, LastNeg); - APInt WMin(Bits, LastPos); + APInt WMax(Bits, LastNeg, true); + APInt WMin(Bits, LastPos, true); ConstantRange Wrapped = ConstantRange::getNonEmpty(WMin, WMax + 1); EXPECT_EQ(Wrapped, CR); }); @@ -1370,8 +1376,8 @@ TEST_F(ConstantRangeTest, SRem) { EXPECT_EQ(Full.srem(Full), ConstantRange(APInt::getSignedMinValue(16) + 1, APInt::getSignedMinValue(16))); - ConstantRange PosMod(APInt(16, 10), APInt(16, 21)); // [10, 20] - ConstantRange NegMod(APInt(16, -20), APInt(16, -9)); // [-20, -10] + ConstantRange PosMod(APInt(16, 10), APInt(16, 21)); // [10, 20] + ConstantRange NegMod(APInt(16, -20, true), APInt(16, -9, true)); // [-20, -10] ConstantRange IntMinMod(APInt::getSignedMinValue(16)); ConstantRange Expected(16, true); @@ -1381,12 +1387,12 @@ TEST_F(ConstantRangeTest, SRem) { Expected = ConstantRange(APInt(16, 0), APInt(16, 20)); EXPECT_EQ(PosLargeLHS.srem(PosMod), Expected); EXPECT_EQ(PosLargeLHS.srem(NegMod), Expected); - ConstantRange NegLargeLHS(APInt(16, -40), APInt(16, 1)); - Expected = ConstantRange(APInt(16, -19), APInt(16, 1)); + ConstantRange NegLargeLHS(APInt(16, -40, true), APInt(16, 1)); + Expected = ConstantRange(APInt(16, -19, true), APInt(16, 1)); EXPECT_EQ(NegLargeLHS.srem(PosMod), Expected); EXPECT_EQ(NegLargeLHS.srem(NegMod), Expected); - ConstantRange PosNegLargeLHS(APInt(16, -32), APInt(16, 38)); - Expected = ConstantRange(APInt(16, -19), APInt(16, 20)); + ConstantRange PosNegLargeLHS(APInt(16, -32, true), APInt(16, 38)); + Expected = ConstantRange(APInt(16, -19, true), APInt(16, 20)); EXPECT_EQ(PosNegLargeLHS.srem(PosMod), Expected); EXPECT_EQ(PosNegLargeLHS.srem(NegMod), Expected); @@ -1395,11 +1401,11 @@ TEST_F(ConstantRangeTest, SRem) { EXPECT_EQ(PosLHS.srem(PosMod), PosLHS); EXPECT_EQ(PosLHS.srem(NegMod), PosLHS); EXPECT_EQ(PosLHS.srem(IntMinMod), PosLHS); - ConstantRange NegLHS(APInt(16, -15), APInt(16, 1)); + ConstantRange NegLHS(APInt(16, -15, true), APInt(16, 1)); EXPECT_EQ(NegLHS.srem(PosMod), NegLHS); EXPECT_EQ(NegLHS.srem(NegMod), NegLHS); EXPECT_EQ(NegLHS.srem(IntMinMod), NegLHS); - ConstantRange PosNegLHS(APInt(16, -12), APInt(16, 18)); + ConstantRange PosNegLHS(APInt(16, -12, true), APInt(16, 18)); EXPECT_EQ(PosNegLHS.srem(PosMod), PosNegLHS); EXPECT_EQ(PosNegLHS.srem(NegMod), PosNegLHS); EXPECT_EQ(PosNegLHS.srem(IntMinMod), PosNegLHS); @@ -1409,11 +1415,11 @@ TEST_F(ConstantRangeTest, SRem) { EXPECT_EQ(PosSmallLHS.srem(PosMod), PosSmallLHS); EXPECT_EQ(PosSmallLHS.srem(NegMod), PosSmallLHS); EXPECT_EQ(PosSmallLHS.srem(IntMinMod), PosSmallLHS); - ConstantRange NegSmallLHS(APInt(16, -7), APInt(16, -2)); + ConstantRange NegSmallLHS(APInt(16, -7, true), APInt(16, -2, true)); EXPECT_EQ(NegSmallLHS.srem(PosMod), NegSmallLHS); EXPECT_EQ(NegSmallLHS.srem(NegMod), NegSmallLHS); EXPECT_EQ(NegSmallLHS.srem(IntMinMod), NegSmallLHS); - ConstantRange PosNegSmallLHS(APInt(16, -3), APInt(16, 8)); + ConstantRange PosNegSmallLHS(APInt(16, -3, true), APInt(16, 8)); EXPECT_EQ(PosNegSmallLHS.srem(PosMod), PosNegSmallLHS); EXPECT_EQ(PosNegSmallLHS.srem(NegMod), PosNegSmallLHS); EXPECT_EQ(PosNegSmallLHS.srem(IntMinMod), PosNegSmallLHS); @@ -1527,9 +1533,9 @@ TEST_F(ConstantRangeTest, Ashr) { APInt(16, (0xaaa >> 0xa) + 1))); EXPECT_EQ(Some.ashr(Wrap), ConstantRange(APInt(16, 0), APInt(16, 0xaaa))); EXPECT_EQ(Wrap.ashr(Wrap), Full); - ConstantRange Neg(APInt(16, 0xf3f0, true), APInt(16, 0xf7f8, true)); - EXPECT_EQ(Neg.ashr(Small), ConstantRange(APInt(16, 0xfffc, true), - APInt(16, 0xfffe, true))); + ConstantRange Neg(APInt(16, 0xf3f0), APInt(16, 0xf7f8)); + EXPECT_EQ(Neg.ashr(Small), + ConstantRange(APInt(16, 0xfffc), APInt(16, 0xfffe))); } TEST(ConstantRange, MakeAllowedICmpRegion) { @@ -1572,23 +1578,23 @@ TEST(ConstantRange, MakeSatisfyingICmpRegion) { UnsignedSample), ConstantRange(APInt(8, 199), APInt(8, 0))); - ConstantRange SignedSample(APInt(8, -5), APInt(8, 5)); + ConstantRange SignedSample(APInt(8, -5, true), APInt(8, 5)); EXPECT_EQ( ConstantRange::makeSatisfyingICmpRegion(ICmpInst::ICMP_SLT, SignedSample), - ConstantRange(APInt(8, -128), APInt(8, -5))); + ConstantRange(APInt(8, -128, true), APInt(8, -5, true))); EXPECT_EQ( ConstantRange::makeSatisfyingICmpRegion(ICmpInst::ICMP_SLE, SignedSample), - ConstantRange(APInt(8, -128), APInt(8, -4))); + ConstantRange(APInt(8, -128, true), APInt(8, -4, true))); EXPECT_EQ( ConstantRange::makeSatisfyingICmpRegion(ICmpInst::ICMP_SGT, SignedSample), - ConstantRange(APInt(8, 5), APInt(8, -128))); + ConstantRange(APInt(8, 5), APInt(8, -128, true))); EXPECT_EQ( ConstantRange::makeSatisfyingICmpRegion(ICmpInst::ICMP_SGE, SignedSample), - ConstantRange(APInt(8, 4), APInt(8, -128))); + ConstantRange(APInt(8, 4), APInt(8, -128, true))); } void ICmpTestImpl(CmpInst::Predicate Pred) { @@ -1610,7 +1616,7 @@ TEST(ConstantRange, ICmp) { } TEST(ConstantRange, MakeGuaranteedNoWrapRegion) { - const int IntMin4Bits = 8; + const int IntMin4Bits = -8; const int IntMax4Bits = 7; typedef OverflowingBinaryOperator OBO; @@ -1719,7 +1725,7 @@ TEST(ConstantRange, MakeGuaranteedNoWrapRegion) { Instruction::Sub, OneToFive, OBO::NoUnsignedWrap), ConstantRange(APInt::getMinValue(32) + 5, APInt::getMinValue(32))); - ConstantRange MinusFiveToMinusTwo(APInt(32, -5), APInt(32, -1)); + ConstantRange MinusFiveToMinusTwo(APInt(32, -5, true), APInt(32, -1, true)); EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( Instruction::Add, MinusFiveToMinusTwo, OBO::NoSignedWrap), ConstantRange(APInt::getSignedMinValue(32) + 5, @@ -1733,10 +1739,9 @@ TEST(ConstantRange, MakeGuaranteedNoWrapRegion) { APInt::getSignedMaxValue(32) - 4)); EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( Instruction::Sub, MinusFiveToMinusTwo, OBO::NoUnsignedWrap), - ConstantRange(APInt::getMaxValue(32) - 1, - APInt::getMinValue(32))); + ConstantRange(APInt::getMaxValue(32) - 1, APInt::getMinValue(32))); - ConstantRange MinusOneToOne(APInt(32, -1), APInt(32, 2)); + ConstantRange MinusOneToOne(APInt(32, -1, true), APInt(32, 2)); EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( Instruction::Add, MinusOneToOne, OBO::NoSignedWrap), ConstantRange(APInt::getSignedMinValue(32) + 1, @@ -1784,7 +1789,7 @@ TEST(ConstantRange, MakeGuaranteedNoWrapRegion) { ConstantRange(APInt(32, 0), APInt(32, 1) + 1)); EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( Instruction::Shl, UpToBitWidth, OBO::NoSignedWrap), - ConstantRange(APInt(32, -1), APInt(32, 0) + 1)); + ConstantRange(APInt(32, -1, true), APInt(32, 0) + 1)); EXPECT_EQ( ConstantRange::makeGuaranteedNoWrapRegion( @@ -1805,34 +1810,36 @@ TEST(ConstantRange, MakeGuaranteedNoWrapRegion) { Instruction::Shl, IllegalShAmt, OBO::NoSignedWrap), ConstantRange::getFull(32)); - EXPECT_EQ( - ConstantRange::makeGuaranteedNoWrapRegion( - Instruction::Shl, ConstantRange(APInt(32, -32), APInt(32, 16) + 1), - OBO::NoUnsignedWrap), - ConstantRange::makeGuaranteedNoWrapRegion( - Instruction::Shl, ConstantRange(APInt(32, 0), APInt(32, 16) + 1), - OBO::NoUnsignedWrap)); - EXPECT_EQ( - ConstantRange::makeGuaranteedNoWrapRegion( - Instruction::Shl, ConstantRange(APInt(32, -32), APInt(32, 16) + 1), - OBO::NoSignedWrap), - ConstantRange::makeGuaranteedNoWrapRegion( - Instruction::Shl, ConstantRange(APInt(32, 0), APInt(32, 16) + 1), - OBO::NoSignedWrap)); + EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Shl, + ConstantRange(APInt(32, -32, true), APInt(32, 16) + 1), + OBO::NoUnsignedWrap), + ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Shl, + ConstantRange(APInt(32, 0), APInt(32, 16) + 1), + OBO::NoUnsignedWrap)); + EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Shl, + ConstantRange(APInt(32, -32, true), APInt(32, 16) + 1), + OBO::NoSignedWrap), + ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Shl, + ConstantRange(APInt(32, 0), APInt(32, 16) + 1), + OBO::NoSignedWrap)); EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( Instruction::Shl, - ConstantRange(APInt(32, -32), APInt(32, 16) + 1), + ConstantRange(APInt(32, -32, true), APInt(32, 16) + 1), OBO::NoUnsignedWrap), ConstantRange(APInt(32, 0), APInt(32, 65535) + 1)); EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( Instruction::Shl, - ConstantRange(APInt(32, -32), APInt(32, 16) + 1), + ConstantRange(APInt(32, -32, true), APInt(32, 16) + 1), OBO::NoSignedWrap), - ConstantRange(APInt(32, -32768), APInt(32, 32767) + 1)); + ConstantRange(APInt(32, -32768, true), APInt(32, 32767) + 1)); } -template +template void TestNoWrapRegionExhaustive(Instruction::BinaryOps BinOp, unsigned NoWrapKind, Fn OverflowFn) { for (unsigned Bits : {1, 5}) { @@ -1997,14 +2004,15 @@ TEST(ConstantRange, GetEquivalentICmp) { EXPECT_EQ(Pred, CmpInst::ICMP_NE); EXPECT_EQ(RHS, APInt(32, 0)); - EXPECT_TRUE(ConstantRange(APInt(32, -1)).getEquivalentICmp(Pred, RHS)); + EXPECT_TRUE(ConstantRange(APInt(32, -1, true)).getEquivalentICmp(Pred, RHS)); EXPECT_EQ(Pred, CmpInst::ICMP_EQ); - EXPECT_EQ(RHS, APInt(32, -1)); + EXPECT_EQ(RHS, APInt(32, -1, true)); - EXPECT_TRUE( - ConstantRange(APInt(32, -1)).inverse().getEquivalentICmp(Pred, RHS)); + EXPECT_TRUE(ConstantRange(APInt(32, -1, true)) + .inverse() + .getEquivalentICmp(Pred, RHS)); EXPECT_EQ(Pred, CmpInst::ICMP_NE); - EXPECT_EQ(RHS, APInt(32, -1)); + EXPECT_EQ(RHS, APInt(32, -1, true)); EnumerateInterestingConstantRanges([](const ConstantRange &CR) { unsigned Bits = CR.getBitWidth(); diff --git a/llvm/unittests/IR/MetadataTest.cpp b/llvm/unittests/IR/MetadataTest.cpp index 17573ca57e08..821d6f44e521 100644 --- a/llvm/unittests/IR/MetadataTest.cpp +++ b/llvm/unittests/IR/MetadataTest.cpp @@ -4224,16 +4224,18 @@ TEST_F(DIExpressionTest, foldConstant) { DIExpression *Expr; DIExpression *NewExpr; -#define EXPECT_FOLD_CONST(StartWidth, StartValue, EndWidth, EndValue, NumElts) \ - Int = ConstantInt::get(Context, APInt(StartWidth, StartValue)); \ - std::tie(NewExpr, NewInt) = Expr->constantFold(Int); \ - ASSERT_EQ(NewInt->getBitWidth(), EndWidth##u); \ - EXPECT_EQ(NewInt->getValue(), APInt(EndWidth, EndValue)); \ +#define EXPECT_FOLD_CONST(StartWidth, StartValue, StartIsSigned, EndWidth, \ + EndValue, EndIsSigned, NumElts) \ + Int = \ + ConstantInt::get(Context, APInt(StartWidth, StartValue, StartIsSigned)); \ + std::tie(NewExpr, NewInt) = Expr->constantFold(Int); \ + ASSERT_EQ(NewInt->getBitWidth(), EndWidth##u); \ + EXPECT_EQ(NewInt->getValue(), APInt(EndWidth, EndValue, EndIsSigned)); \ EXPECT_EQ(NewExpr->getNumElements(), NumElts##u) // Unfoldable expression should return the original unmodified Int/Expr. Expr = DIExpression::get(Context, {dwarf::DW_OP_deref}); - EXPECT_FOLD_CONST(32, 117, 32, 117, 1); + EXPECT_FOLD_CONST(32, 117, false, 32, 117, false, 1); EXPECT_EQ(NewExpr, Expr); EXPECT_EQ(NewInt, Int); EXPECT_TRUE(NewExpr->startsWithDeref()); @@ -4241,18 +4243,18 @@ TEST_F(DIExpressionTest, foldConstant) { // One unsigned bit-width conversion. Expr = DIExpression::get( Context, {dwarf::DW_OP_LLVM_convert, 72, dwarf::DW_ATE_unsigned}); - EXPECT_FOLD_CONST(8, 12, 72, 12, 0); + EXPECT_FOLD_CONST(8, 12, false, 72, 12, false, 0); // Two unsigned bit-width conversions (mask truncation). Expr = DIExpression::get( Context, {dwarf::DW_OP_LLVM_convert, 8, dwarf::DW_ATE_unsigned, dwarf::DW_OP_LLVM_convert, 16, dwarf::DW_ATE_unsigned}); - EXPECT_FOLD_CONST(32, -1, 16, 0xff, 0); + EXPECT_FOLD_CONST(32, -1, true, 16, 0xff, false, 0); // Sign extension. Expr = DIExpression::get( Context, {dwarf::DW_OP_LLVM_convert, 32, dwarf::DW_ATE_signed}); - EXPECT_FOLD_CONST(16, -1, 32, -1, 0); + EXPECT_FOLD_CONST(16, -1, true, 32, -1, true, 0); // Get non-foldable operations back in the new Expr. uint64_t Elements[] = {dwarf::DW_OP_deref, dwarf::DW_OP_stack_value}; @@ -4261,7 +4263,7 @@ TEST_F(DIExpressionTest, foldConstant) { Context, {dwarf::DW_OP_LLVM_convert, 32, dwarf::DW_ATE_signed}); Expr = DIExpression::append(Expr, Expected); ASSERT_EQ(Expr->getNumElements(), 5u); - EXPECT_FOLD_CONST(16, -1, 32, -1, 2); + EXPECT_FOLD_CONST(16, -1, true, 32, -1, true, 2); EXPECT_EQ(NewExpr->getElements(), Expected); #undef EXPECT_FOLD_CONST diff --git a/llvm/unittests/IR/PatternMatch.cpp b/llvm/unittests/IR/PatternMatch.cpp index 309fcc93996b..f2b15f411fcb 100644 --- a/llvm/unittests/IR/PatternMatch.cpp +++ b/llvm/unittests/IR/PatternMatch.cpp @@ -71,7 +71,7 @@ TEST_F(PatternMatchTest, SpecificIntEQ) { Value *Zero = ConstantInt::get(IntTy, 0); Value *One = ConstantInt::get(IntTy, 1); - Value *NegOne = ConstantInt::get(IntTy, -1); + Value *NegOne = Constant::getAllOnesValue(IntTy); EXPECT_TRUE( m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_EQ, APInt(BitWidth, 0)) @@ -93,15 +93,15 @@ TEST_F(PatternMatchTest, SpecificIntEQ) { m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_EQ, APInt(BitWidth, 1)) .match(NegOne)); - EXPECT_FALSE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_EQ, APInt(BitWidth, -1)) - .match(Zero)); - EXPECT_FALSE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_EQ, APInt(BitWidth, -1)) - .match(One)); - EXPECT_TRUE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_EQ, APInt(BitWidth, -1)) - .match(NegOne)); + EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_EQ, + APInt::getAllOnes(BitWidth)) + .match(Zero)); + EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_EQ, + APInt::getAllOnes(BitWidth)) + .match(One)); + EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_EQ, + APInt::getAllOnes(BitWidth)) + .match(NegOne)); } TEST_F(PatternMatchTest, SpecificIntNE) { @@ -110,7 +110,7 @@ TEST_F(PatternMatchTest, SpecificIntNE) { Value *Zero = ConstantInt::get(IntTy, 0); Value *One = ConstantInt::get(IntTy, 1); - Value *NegOne = ConstantInt::get(IntTy, -1); + Value *NegOne = Constant::getAllOnesValue(IntTy); EXPECT_FALSE( m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_NE, APInt(BitWidth, 0)) @@ -132,15 +132,15 @@ TEST_F(PatternMatchTest, SpecificIntNE) { m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_NE, APInt(BitWidth, 1)) .match(NegOne)); - EXPECT_TRUE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_NE, APInt(BitWidth, -1)) - .match(Zero)); - EXPECT_TRUE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_NE, APInt(BitWidth, -1)) - .match(One)); - EXPECT_FALSE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_NE, APInt(BitWidth, -1)) - .match(NegOne)); + EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_NE, + APInt::getAllOnes(BitWidth)) + .match(Zero)); + EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_NE, + APInt::getAllOnes(BitWidth)) + .match(One)); + EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_NE, + APInt::getAllOnes(BitWidth)) + .match(NegOne)); } TEST_F(PatternMatchTest, SpecificIntUGT) { @@ -149,7 +149,7 @@ TEST_F(PatternMatchTest, SpecificIntUGT) { Value *Zero = ConstantInt::get(IntTy, 0); Value *One = ConstantInt::get(IntTy, 1); - Value *NegOne = ConstantInt::get(IntTy, -1); + Value *NegOne = Constant::getAllOnesValue(IntTy); EXPECT_FALSE( m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGT, APInt(BitWidth, 0)) @@ -171,15 +171,15 @@ TEST_F(PatternMatchTest, SpecificIntUGT) { m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGT, APInt(BitWidth, 1)) .match(NegOne)); - EXPECT_FALSE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGT, APInt(BitWidth, -1)) - .match(Zero)); - EXPECT_FALSE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGT, APInt(BitWidth, -1)) - .match(One)); - EXPECT_FALSE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGT, APInt(BitWidth, -1)) - .match(NegOne)); + EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGT, + APInt::getAllOnes(BitWidth)) + .match(Zero)); + EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGT, + APInt::getAllOnes(BitWidth)) + .match(One)); + EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGT, + APInt::getAllOnes(BitWidth)) + .match(NegOne)); } TEST_F(PatternMatchTest, SignbitZeroChecks) { @@ -187,7 +187,7 @@ TEST_F(PatternMatchTest, SignbitZeroChecks) { Value *Zero = ConstantInt::get(IntTy, 0); Value *One = ConstantInt::get(IntTy, 1); - Value *NegOne = ConstantInt::get(IntTy, -1); + Value *NegOne = Constant::getAllOnesValue(IntTy); EXPECT_TRUE(m_Negative().match(NegOne)); EXPECT_FALSE(m_NonNegative().match(NegOne)); @@ -211,7 +211,7 @@ TEST_F(PatternMatchTest, SpecificIntUGE) { Value *Zero = ConstantInt::get(IntTy, 0); Value *One = ConstantInt::get(IntTy, 1); - Value *NegOne = ConstantInt::get(IntTy, -1); + Value *NegOne = Constant::getAllOnesValue(IntTy); EXPECT_TRUE( m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGE, APInt(BitWidth, 0)) @@ -233,15 +233,15 @@ TEST_F(PatternMatchTest, SpecificIntUGE) { m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGE, APInt(BitWidth, 1)) .match(NegOne)); - EXPECT_FALSE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGE, APInt(BitWidth, -1)) - .match(Zero)); - EXPECT_FALSE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGE, APInt(BitWidth, -1)) - .match(One)); - EXPECT_TRUE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGE, APInt(BitWidth, -1)) - .match(NegOne)); + EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGE, + APInt::getAllOnes(BitWidth)) + .match(Zero)); + EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGE, + APInt::getAllOnes(BitWidth)) + .match(One)); + EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGE, + APInt::getAllOnes(BitWidth)) + .match(NegOne)); } TEST_F(PatternMatchTest, SpecificIntULT) { @@ -250,7 +250,7 @@ TEST_F(PatternMatchTest, SpecificIntULT) { Value *Zero = ConstantInt::get(IntTy, 0); Value *One = ConstantInt::get(IntTy, 1); - Value *NegOne = ConstantInt::get(IntTy, -1); + Value *NegOne = Constant::getAllOnesValue(IntTy); EXPECT_FALSE( m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULT, APInt(BitWidth, 0)) @@ -272,15 +272,15 @@ TEST_F(PatternMatchTest, SpecificIntULT) { m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULT, APInt(BitWidth, 1)) .match(NegOne)); - EXPECT_TRUE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULT, APInt(BitWidth, -1)) - .match(Zero)); - EXPECT_TRUE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULT, APInt(BitWidth, -1)) - .match(One)); - EXPECT_FALSE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULT, APInt(BitWidth, -1)) - .match(NegOne)); + EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULT, + APInt::getAllOnes(BitWidth)) + .match(Zero)); + EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULT, + APInt::getAllOnes(BitWidth)) + .match(One)); + EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULT, + APInt::getAllOnes(BitWidth)) + .match(NegOne)); } TEST_F(PatternMatchTest, SpecificIntULE) { @@ -289,7 +289,7 @@ TEST_F(PatternMatchTest, SpecificIntULE) { Value *Zero = ConstantInt::get(IntTy, 0); Value *One = ConstantInt::get(IntTy, 1); - Value *NegOne = ConstantInt::get(IntTy, -1); + Value *NegOne = Constant::getAllOnesValue(IntTy); EXPECT_TRUE( m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULE, APInt(BitWidth, 0)) @@ -311,15 +311,15 @@ TEST_F(PatternMatchTest, SpecificIntULE) { m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULE, APInt(BitWidth, 1)) .match(NegOne)); - EXPECT_TRUE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULE, APInt(BitWidth, -1)) - .match(Zero)); - EXPECT_TRUE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULE, APInt(BitWidth, -1)) - .match(One)); - EXPECT_TRUE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULE, APInt(BitWidth, -1)) - .match(NegOne)); + EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULE, + APInt::getAllOnes(BitWidth)) + .match(Zero)); + EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULE, + APInt::getAllOnes(BitWidth)) + .match(One)); + EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULE, + APInt::getAllOnes(BitWidth)) + .match(NegOne)); } TEST_F(PatternMatchTest, SpecificIntSGT) { @@ -328,7 +328,7 @@ TEST_F(PatternMatchTest, SpecificIntSGT) { Value *Zero = ConstantInt::get(IntTy, 0); Value *One = ConstantInt::get(IntTy, 1); - Value *NegOne = ConstantInt::get(IntTy, -1); + Value *NegOne = Constant::getAllOnesValue(IntTy); EXPECT_FALSE( m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGT, APInt(BitWidth, 0)) @@ -350,15 +350,15 @@ TEST_F(PatternMatchTest, SpecificIntSGT) { m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGT, APInt(BitWidth, 1)) .match(NegOne)); - EXPECT_TRUE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGT, APInt(BitWidth, -1)) - .match(Zero)); - EXPECT_TRUE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGT, APInt(BitWidth, -1)) - .match(One)); - EXPECT_FALSE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGT, APInt(BitWidth, -1)) - .match(NegOne)); + EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGT, + APInt::getAllOnes(BitWidth)) + .match(Zero)); + EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGT, + APInt::getAllOnes(BitWidth)) + .match(One)); + EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGT, + APInt::getAllOnes(BitWidth)) + .match(NegOne)); } TEST_F(PatternMatchTest, SpecificIntSGE) { @@ -367,7 +367,7 @@ TEST_F(PatternMatchTest, SpecificIntSGE) { Value *Zero = ConstantInt::get(IntTy, 0); Value *One = ConstantInt::get(IntTy, 1); - Value *NegOne = ConstantInt::get(IntTy, -1); + Value *NegOne = Constant::getAllOnesValue(IntTy); EXPECT_TRUE( m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGE, APInt(BitWidth, 0)) @@ -389,15 +389,15 @@ TEST_F(PatternMatchTest, SpecificIntSGE) { m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGE, APInt(BitWidth, 1)) .match(NegOne)); - EXPECT_TRUE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGE, APInt(BitWidth, -1)) - .match(Zero)); - EXPECT_TRUE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGE, APInt(BitWidth, -1)) - .match(One)); - EXPECT_TRUE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGE, APInt(BitWidth, -1)) - .match(NegOne)); + EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGE, + APInt::getAllOnes(BitWidth)) + .match(Zero)); + EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGE, + APInt::getAllOnes(BitWidth)) + .match(One)); + EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGE, + APInt::getAllOnes(BitWidth)) + .match(NegOne)); } TEST_F(PatternMatchTest, SpecificIntSLT) { @@ -406,7 +406,7 @@ TEST_F(PatternMatchTest, SpecificIntSLT) { Value *Zero = ConstantInt::get(IntTy, 0); Value *One = ConstantInt::get(IntTy, 1); - Value *NegOne = ConstantInt::get(IntTy, -1); + Value *NegOne = Constant::getAllOnesValue(IntTy); EXPECT_FALSE( m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLT, APInt(BitWidth, 0)) @@ -428,15 +428,15 @@ TEST_F(PatternMatchTest, SpecificIntSLT) { m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLT, APInt(BitWidth, 1)) .match(NegOne)); - EXPECT_FALSE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLT, APInt(BitWidth, -1)) - .match(Zero)); - EXPECT_FALSE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLT, APInt(BitWidth, -1)) - .match(One)); - EXPECT_FALSE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLT, APInt(BitWidth, -1)) - .match(NegOne)); + EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLT, + APInt::getAllOnes(BitWidth)) + .match(Zero)); + EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLT, + APInt::getAllOnes(BitWidth)) + .match(One)); + EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLT, + APInt::getAllOnes(BitWidth)) + .match(NegOne)); } TEST_F(PatternMatchTest, SpecificIntSLE) { @@ -445,7 +445,7 @@ TEST_F(PatternMatchTest, SpecificIntSLE) { Value *Zero = ConstantInt::get(IntTy, 0); Value *One = ConstantInt::get(IntTy, 1); - Value *NegOne = ConstantInt::get(IntTy, -1); + Value *NegOne = Constant::getAllOnesValue(IntTy); EXPECT_TRUE( m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLE, APInt(BitWidth, 0)) @@ -467,15 +467,15 @@ TEST_F(PatternMatchTest, SpecificIntSLE) { m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLE, APInt(BitWidth, 1)) .match(NegOne)); - EXPECT_FALSE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLE, APInt(BitWidth, -1)) - .match(Zero)); - EXPECT_FALSE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLE, APInt(BitWidth, -1)) - .match(One)); - EXPECT_TRUE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLE, APInt(BitWidth, -1)) - .match(NegOne)); + EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLE, + APInt::getAllOnes(BitWidth)) + .match(Zero)); + EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLE, + APInt::getAllOnes(BitWidth)) + .match(One)); + EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLE, + APInt::getAllOnes(BitWidth)) + .match(NegOne)); } TEST_F(PatternMatchTest, Unless) { diff --git a/llvm/unittests/Passes/PassBuilderBindings/PassBuilderBindingsTest.cpp b/llvm/unittests/Passes/PassBuilderBindings/PassBuilderBindingsTest.cpp index ffa3fdaf6e7e..ef8d9c037f67 100644 --- a/llvm/unittests/Passes/PassBuilderBindings/PassBuilderBindingsTest.cpp +++ b/llvm/unittests/Passes/PassBuilderBindings/PassBuilderBindingsTest.cpp @@ -55,7 +55,11 @@ class PassBuilderCTest : public testing::Test { LLVMContextRef Context; }; +#if defined(_EVM) +TEST_F(PassBuilderCTest, DISABLED_Basic) { +#else TEST_F(PassBuilderCTest, Basic) { +#endif LLVMPassBuilderOptionsRef Options = LLVMCreatePassBuilderOptions(); LLVMPassBuilderOptionsSetLoopUnrolling(Options, 1); LLVMPassBuilderOptionsSetVerifyEach(Options, 1); @@ -69,7 +73,11 @@ TEST_F(PassBuilderCTest, Basic) { LLVMDisposePassBuilderOptions(Options); } +#if defined(_EVM) +TEST_F(PassBuilderCTest, DISABLED_InvalidPassIsError) { +#else TEST_F(PassBuilderCTest, InvalidPassIsError) { +#endif LLVMPassBuilderOptionsRef Options = LLVMCreatePassBuilderOptions(); LLVMErrorRef E1 = LLVMRunPasses(Module, "", TM, Options); LLVMErrorRef E2 = LLVMRunPasses(Module, "does-not-exist-pass", TM, Options); diff --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt index 631f2e6bf00d..0800a247eff1 100644 --- a/llvm/unittests/Support/CMakeLists.txt +++ b/llvm/unittests/Support/CMakeLists.txt @@ -50,6 +50,7 @@ add_llvm_unittest(SupportTests InstructionCostTest.cpp JSONTest.cpp KnownBitsTest.cpp + KECCAKTest.cpp LEB128Test.cpp LineIteratorTest.cpp LockFileManagerTest.cpp diff --git a/llvm/unittests/Support/CommandLineTest.cpp b/llvm/unittests/Support/CommandLineTest.cpp index 23f6081cd32a..02e2f99f4ff9 100644 --- a/llvm/unittests/Support/CommandLineTest.cpp +++ b/llvm/unittests/Support/CommandLineTest.cpp @@ -938,6 +938,11 @@ TEST(CommandLineTest, ResponseFiles) { } TEST(CommandLineTest, RecursiveResponseFiles) { + // EVM local begin + // Temporary disable on Windows due to issues with paths on MSYS2. + if (Triple(sys::getProcessTriple()).isOSWindows()) + GTEST_SKIP(); + // EVM local end vfs::InMemoryFileSystem FS; #ifdef _WIN32 const char *TestRoot = "C:\\"; diff --git a/llvm/unittests/Support/DivisionByConstantTest.cpp b/llvm/unittests/Support/DivisionByConstantTest.cpp index 2b17f98bb75b..715dded68ff0 100644 --- a/llvm/unittests/Support/DivisionByConstantTest.cpp +++ b/llvm/unittests/Support/DivisionByConstantTest.cpp @@ -32,7 +32,7 @@ APInt SignedDivideUsingMagic(APInt Numerator, APInt Divisor, unsigned Bits = Numerator.getBitWidth(); APInt Factor(Bits, 0); - APInt ShiftMask(Bits, -1); + APInt ShiftMask(Bits, -1, true); if (Divisor.isOne() || Divisor.isAllOnes()) { // If d is +1/-1, we just multiply the numerator by +1/-1. Factor = Divisor.getSExtValue(); diff --git a/llvm/unittests/Support/KECCAKTest.cpp b/llvm/unittests/Support/KECCAKTest.cpp new file mode 100644 index 000000000000..00527eca4a07 --- /dev/null +++ b/llvm/unittests/Support/KECCAKTest.cpp @@ -0,0 +1,69 @@ +//===-- KECCAKTest.cpp - KECCAK tests ---------------------------*- 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 unit tests for the KECCAK functions. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/KECCAK.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallString.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +static std::string toHex(ArrayRef Input) { + static const char *const LUT = "0123456789abcdef"; + const size_t Length = Input.size(); + + std::string Output; + Output.reserve(2 * Length); + for (size_t i = 0; i < Length; ++i) { + const unsigned char c = Input[i]; + Output.push_back(LUT[c >> 4]); + Output.push_back(LUT[c & 15]); + } + return Output; +} + +/// Tests an arbitrary set of bytes passed as \p Input. +void TestKECCAKSum(ArrayRef Input, StringRef Final) { + auto hash = KECCAK::KECCAK_256(Input); + auto hashStr = toHex(hash); + EXPECT_EQ(hashStr, Final); +} + +using KV = std::pair; + +TEST(KECCAKTest, KECCAK) { + const std::array testvectors{ + KV{"", + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"}, + KV{"a", + "3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb"}, + KV{"abc", + "4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45"}, + KV{"abcdbcdecdefdefgefghfghighijhijk", + "4b50e45e85ca4a0a9c089890faf83098c75b04fe0e0f9c5488effd1643711033"}, + KV{"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + "45d3b367a6904e6e8d502ee04999a7c27647f91fa845d456525fd352ae3d7371"}, + KV{"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklm" + "nopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", + "f519747ed599024f3882238e5ab43960132572b7345fbeb9a90769dafd21ad67"}}; + + for (auto input_expected : testvectors) { + const auto *const str = std::get<0>(input_expected); + const auto *const expected = std::get<1>(input_expected); + TestKECCAKSum({reinterpret_cast(str), strlen(str)}, + expected); + } +} + +} // end anonymous namespace diff --git a/llvm/unittests/Target/EVM/BytecodeSize.cpp b/llvm/unittests/Target/EVM/BytecodeSize.cpp new file mode 100644 index 000000000000..d012ecf3a54d --- /dev/null +++ b/llvm/unittests/Target/EVM/BytecodeSize.cpp @@ -0,0 +1,401 @@ +//===- BytecodeSize.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 "EVMCalculateModuleSize.h" +#include "llvm/CodeGen/MIRParser/MIRParser.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/IR/Module.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Target/TargetMachine.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +std::unique_ptr createTargetMachine() { + auto TT(Triple::normalize("evm--")); + std::string Error; + const Target *TheTarget = TargetRegistry::lookupTarget(TT, Error); + return std::unique_ptr(static_cast( + TheTarget->createTargetMachine(TT, "", "", TargetOptions(), std::nullopt, + std::nullopt, CodeGenOptLevel::Default))); +} + +class BytecodeSizeTest : public testing::Test { +protected: + static const char *MIRString; + static const char *MIRStringReadOnlyData; + LLVMContext Context; + std::unique_ptr TM; + std::unique_ptr MMI; + + static void SetUpTestCase() { + LLVMInitializeEVMTargetInfo(); + LLVMInitializeEVMTarget(); + LLVMInitializeEVMTargetMC(); + } + + std::unique_ptr parseMIR(StringRef MIR) { + std::unique_ptr MBuffer = MemoryBuffer::getMemBuffer(MIR); + std::unique_ptr Parser = + createMIRParser(std::move(MBuffer), Context); + if (!Parser) + report_fatal_error("null MIRParser"); + std::unique_ptr M = Parser->parseIRModule(); + if (!M) + report_fatal_error("parseIRModule failed"); + M->setTargetTriple(TM->getTargetTriple().getTriple()); + M->setDataLayout(TM->createDataLayout()); + if (Parser->parseMachineFunctions(*M, *MMI)) + report_fatal_error("parseMachineFunctions failed"); + + return M; + } + + void SetUp() override { + TM = createTargetMachine(); + MMI = std::make_unique(TM.get()); + } +}; + +TEST_F(BytecodeSizeTest, Test) { + std::unique_ptr M(parseMIR(MIRString)); + ASSERT_TRUE(EVM::calculateModuleCodeSize(*M, *MMI) == 131); + + std::unique_ptr M2(parseMIR(MIRStringReadOnlyData)); + // 23 = 21 (global variable initializers) + JUMPDEST + INVALID + ASSERT_TRUE(EVM::calculateModuleCodeSize(*M2, *MMI) == 23); +} + +const char *BytecodeSizeTest::MIRString = R"MIR( +--- | + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm-unknown-unknown" + + ; Function Attrs: nocallback noduplicate nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite) + declare i256 @llvm.evm.pushdeployaddress() #0 + + ; Function Attrs: noreturn + define void @egcd() #1 { + bb: + %deploy_addr = tail call i256 @llvm.evm.pushdeployaddress() + %addr_arg = inttoptr i256 0 to ptr addrspace(2) + %arg = call i256 @llvm.evm.calldataload(ptr addrspace(2) %addr_arg) + %addr_arg1 = inttoptr i256 32 to ptr addrspace(2) + %arg1 = call i256 @llvm.evm.calldataload(ptr addrspace(2) %addr_arg1) + %addr1 = inttoptr i256 32 to ptr addrspace(1) + store i256 %deploy_addr, ptr addrspace(1) %addr1, align 4 + %addr2 = inttoptr i256 64 to ptr addrspace(1) + store i256 0, ptr addrspace(1) %addr2, align 4 + %i = icmp eq i256 %arg1, 0 + br i1 %i, label %bb20, label %bb4.preheader + + bb4.preheader: ; preds = %bb + br label %bb4 + + bb4: ; preds = %bb4.preheader, %bb4 + %i5 = phi i256 [ %i9, %bb4 ], [ 1, %bb4.preheader ] + %i6 = phi i256 [ %i7, %bb4 ], [ %arg, %bb4.preheader ] + %i7 = phi i256 [ %i13, %bb4 ], [ %arg1, %bb4.preheader ] + %i8 = phi i256 [ %i17, %bb4 ], [ 1, %bb4.preheader ] + %i9 = phi i256 [ %i15, %bb4 ], [ 0, %bb4.preheader ] + %i10 = phi i256 [ %i8, %bb4 ], [ 0, %bb4.preheader ] + %i11 = sdiv i256 %i6, %i7 + %i12 = mul nsw i256 %i11, %i7 + %i13 = sub nsw i256 %i6, %i12 + %i14 = mul nsw i256 %i11, %i9 + %i15 = sub nsw i256 %i5, %i14 + %i16 = mul nsw i256 %i11, %i8 + %i17 = sub nsw i256 %i10, %i16 + %i18 = icmp eq i256 %i13, 0 + br i1 %i18, label %bb19, label %bb4 + + bb19: ; preds = %bb4 + store i256 %i9, ptr addrspace(1) %addr1, align 4 + store i256 %i8, ptr addrspace(1) %addr2, align 4 + br label %bb20 + + bb20: ; preds = %bb19, %bb + %i21 = phi i256 [ %i7, %bb19 ], [ %arg, %bb ] + store i256 %i21, ptr addrspace(1) null, align 4 + call void @llvm.evm.return(ptr addrspace(1) null, i256 96) + unreachable + } + + ; Function Attrs: noreturn nounwind + declare void @llvm.evm.return(ptr addrspace(1), i256) #2 + + ; Function Attrs: nounwind memory(argmem: read) + declare i256 @llvm.evm.calldataload(ptr addrspace(2)) #3 + + attributes #0 = { nocallback noduplicate nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite) } + attributes #1 = { noreturn } + attributes #2 = { noreturn nounwind } + attributes #3 = { nounwind memory(argmem: read) } + +... +--- +name: egcd +alignment: 1 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: false +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gpr, preferred-register: '' } + - { id: 1, class: gpr, preferred-register: '' } + - { id: 2, class: gpr, preferred-register: '' } + - { id: 3, class: gpr, preferred-register: '' } + - { id: 4, class: gpr, preferred-register: '' } + - { id: 5, class: gpr, preferred-register: '' } + - { id: 6, class: gpr, preferred-register: '' } + - { id: 7, class: gpr, preferred-register: '' } + - { id: 8, class: gpr, preferred-register: '' } + - { id: 9, class: gpr, preferred-register: '' } + - { id: 10, class: gpr, preferred-register: '' } + - { id: 11, class: gpr, preferred-register: '' } + - { id: 12, class: gpr, preferred-register: '' } + - { id: 13, class: gpr, preferred-register: '' } + - { id: 14, class: gpr, preferred-register: '' } + - { id: 15, class: gpr, preferred-register: '' } + - { id: 16, class: gpr, preferred-register: '' } + - { id: 17, class: gpr, preferred-register: '' } + - { id: 18, class: gpr, preferred-register: '' } + - { id: 19, class: gpr, preferred-register: '' } + - { id: 20, class: gpr, preferred-register: '' } + - { id: 21, class: gpr, preferred-register: '' } + - { id: 22, class: gpr, preferred-register: '' } + - { id: 23, class: gpr, preferred-register: '' } + - { id: 24, class: gpr, preferred-register: '' } + - { id: 25, class: gpr, preferred-register: '' } + - { id: 26, class: gpr, preferred-register: '' } + - { id: 27, class: gpr, preferred-register: '' } + - { id: 28, class: gpr, preferred-register: '' } + - { id: 29, class: gpr, preferred-register: '' } + - { id: 30, class: gpr, preferred-register: '' } + - { id: 31, class: gpr, preferred-register: '' } + - { id: 32, class: gpr, preferred-register: '' } + - { id: 33, class: gpr, preferred-register: '' } + - { id: 34, class: gpr, preferred-register: '' } + - { id: 35, class: gpr, preferred-register: '' } + - { id: 36, class: gpr, preferred-register: '' } + - { id: 37, class: gpr, preferred-register: '' } + - { id: 38, class: gpr, preferred-register: '' } + - { id: 39, class: gpr, preferred-register: '' } + - { id: 40, class: gpr, preferred-register: '' } + - { id: 41, class: gpr, preferred-register: '' } + - { id: 42, class: gpr, preferred-register: '' } +liveins: + - { reg: '$arguments', virtual-reg: '' } + - { reg: '$value_stack', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + isStackified: true + numberOfParameters: 0 + hasPushDeployAddress: true +body: | + bb.0.bb: + successors: %bb.1(0x40000000), %bb.2(0x40000000) + liveins: $arguments, $value_stack + + PUSH0_S + SWAP1_S + PUSH0_S + CALLDATALOAD_S + SWAP1_S + PUSH1_S i256 32 + CALLDATALOAD_S + SWAP1_S + PUSH0_S + PUSH1_S i256 64 + MSTORE_S + PUSH1_S i256 32 + MSTORE_S + PUSH1_S i256 32 + CALLDATALOAD_S + PseudoJUMPI %bb.2 + + bb.1: + successors: %bb.5(0x80000000) + liveins: $value_stack + + POP_S + POP_S + POP_S + PUSH0_S + CALLDATALOAD_S + PseudoJUMP %bb.5 + + bb.2.bb4.preheader: + successors: %bb.3(0x80000000) + liveins: $value_stack + + PUSH1_S i256 1 + SWAP3_S + SWAP2_S + SWAP3_S + SWAP2_S + PUSH1_S i256 1 + SWAP3_S + PUSH0_S + SWAP2_S + + bb.3.bb4: + successors: %bb.4(0x00000800), %bb.6(0x7ffff800) + liveins: $value_stack + + SWAP4_S + SWAP3_S + SWAP5_S + DUP6_S + DUP1_S + DUP3_S + SDIV_S + SWAP4_S + DUP6_S + DUP6_S + MUL_S + SWAP1_S + SUB_S + SWAP3_S + DUP7_S + DUP6_S + MUL_S + SWAP1_S + SUB_S + SWAP4_S + MUL_S + SWAP1_S + SUB_S + DUP1_S + SWAP3_S + DUP6_S + DUP8_S + SWAP2_S + DUP7_S + SWAP6_S + PseudoJUMP_UNLESS %bb.4 + + bb.6: + successors: %bb.3(0x80000000) + liveins: $value_stack + + SWAP3_S + SWAP6_S + POP_S + SWAP3_S + SWAP6_S + POP_S + SWAP6_S + POP_S + PseudoJUMP %bb.3 + + bb.4.bb19: + successors: %bb.5(0x80000000) + liveins: $value_stack + + POP_S + POP_S + POP_S + POP_S + POP_S + POP_S + PUSH1_S i256 64 + MSTORE_S + PUSH1_S i256 32 + MSTORE_S + + bb.5.bb20: + liveins: $value_stack + + PUSH0_S + MSTORE_S + PUSH1_S i256 96 + PUSH0_S + RETURN_S + +... +)MIR"; + +const char *BytecodeSizeTest::MIRStringReadOnlyData = R"MIR( +--- | + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm" + + @code_const.1 = private unnamed_addr addrspace(4) constant [7 x i8] c"global1" + @code_const.2 = private unnamed_addr addrspace(4) constant [7 x i8] c"global2" + @code_const.3 = private unnamed_addr addrspace(4) constant [7 x i8] c"global3" + + ; The following globals are never generated and should not be taken into account. + @heap_const = private unnamed_addr addrspace(1) constant [7 x i8] c"invalid" + @code_global = addrspace(4) global i256 0 + @code_global_ext = external addrspace(4) global i256 + + define void @test() noreturn { + unreachable + } + +... +--- +name: test +alignment: 1 +tracksRegLiveness: true +machineFunctionInfo: + isStackified: true + numberOfParameters: 0 + hasPushDeployAddress: false +body: | + bb.0 (%ir-block.0): + liveins: $arguments, $value_stack + +... +)MIR"; + +} // anonymous namespace diff --git a/llvm/unittests/Target/EVM/CMakeLists.txt b/llvm/unittests/Target/EVM/CMakeLists.txt new file mode 100644 index 000000000000..f9e30b1527b8 --- /dev/null +++ b/llvm/unittests/Target/EVM/CMakeLists.txt @@ -0,0 +1,27 @@ +include_directories( + ${LLVM_MAIN_SRC_DIR}/lib/Target/EVM + ${LLVM_BINARY_DIR}/lib/Target/EVM + ) + +set(LLVM_LINK_COMPONENTS + EVM + EVMDesc + EVMInfo + CodeGen + Core + MC + MIRParser + SelectionDAG + Support + Target + TargetParser +) + +add_llvm_target_unittest(EVMTests + BytecodeSize.cpp + SpillRegion.cpp + StackShuffler.cpp + StackModel.cpp + ) + +set_property(TARGET EVMTests PROPERTY FOLDER "Tests/UnitTests/TargetTests") diff --git a/llvm/unittests/Target/EVM/SpillRegion.cpp b/llvm/unittests/Target/EVM/SpillRegion.cpp new file mode 100644 index 000000000000..101196e492d9 --- /dev/null +++ b/llvm/unittests/Target/EVM/SpillRegion.cpp @@ -0,0 +1,34 @@ +//===---------- llvm/unittests/MC/AssemblerTest.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-c/ErrorHandling.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorHandling.h" +#include "gtest/gtest.h" +#include + +using namespace llvm; + +static void on_std(uint64_t SpillRegionSize) { + EXPECT_TRUE(SpillRegionSize == 32); +} + +static void on_std_exit(uint64_t SpillRegionSize) { + EXPECT_TRUE(SpillRegionSize == 64); + exit(1); +} + +class SpillRegionTest : public testing::Test {}; + +TEST_F(SpillRegionTest, Basic) { + LLVMInstallEVMStackErrorHandler(on_std); + EXPECT_DEATH(report_evm_stack_error("STD error", 32), "STD error"); + LLVMResetEVMStackErrorHandler(); + LLVMInstallEVMStackErrorHandler(on_std_exit); + EXPECT_DEATH(report_evm_stack_error("STD error", 64), ""); +} diff --git a/llvm/unittests/Target/EVM/StackModel.cpp b/llvm/unittests/Target/EVM/StackModel.cpp new file mode 100644 index 000000000000..a6f4afb5a793 --- /dev/null +++ b/llvm/unittests/Target/EVM/StackModel.cpp @@ -0,0 +1,147 @@ +//===---------- llvm/unittests/EVM/StackSlotBuilder.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 "EVMStackModel.h" +#include "EVMTargetMachine.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm/CodeGen/LiveIntervals.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Target/CodeGenCWrappers.h" +#include "llvm/Target/TargetMachine.h" +#include "gtest/gtest.h" + +using namespace llvm; + +static TargetMachine *unwrap(LLVMTargetMachineRef P) { + return reinterpret_cast(P); +} + +class EVMStackModelTest : public testing::Test { + void SetUp() override { + LLVMInitializeEVMTargetInfo(); + LLVMInitializeEVMTarget(); + LLVMInitializeEVMTargetMC(); + + LLVMTargetRef Target = nullptr; + const char *Triple = "evm"; + char *ErrMsg = nullptr; + if (LLVMGetTargetFromTriple(Triple, &Target, &ErrMsg)) { + FAIL() << "Failed to create target from the triple (" << Triple + << "): " << ErrMsg; + return; + } + ASSERT_TRUE(Target); + + // Construct a TargetMachine. + TM = + LLVMCreateTargetMachine(Target, Triple, "", "", LLVMCodeGenLevelDefault, + LLVMRelocDefault, LLVMCodeModelDefault); + Context = std::make_unique(); + Mod = std::make_unique("TestModule", *Context); + Mod->setDataLayout(unwrap(TM)->createDataLayout()); + const auto &LLVMTM = static_cast(*unwrap(TM)); + MMIWP = std::make_unique(&LLVMTM); + + Type *const ReturnType = Type::getVoidTy(Mod->getContext()); + FunctionType *FunctionType = FunctionType::get(ReturnType, false); + Function *const F = Function::Create( + FunctionType, GlobalValue::InternalLinkage, "TestFunction", Mod.get()); + MF = &MMIWP->getMMI().getOrCreateMachineFunction(*F); + + LISWP = std::make_unique(); + StackModel = std::make_unique( + *MF, LISWP->getLIS(), + MF->getSubtarget().stackDepthLimit()); + } + + void TearDown() override { LLVMDisposeTargetMachine(TM); } + +public: + LLVMTargetMachineRef TM = nullptr; + std::unique_ptr Context; + std::unique_ptr MMIWP; + std::unique_ptr LISWP; + std::unique_ptr Mod; + std::unique_ptr StackModel; + MachineFunction *MF = nullptr; +}; + +TEST_F(EVMStackModelTest, LiteralSlot) { + APInt Int0 = APInt(32, 0); + APInt Int42 = APInt(32, 42); + + auto *LiteralSlot0 = StackModel->getLiteralSlot(Int0); + auto *LiteralSlot0Copy = StackModel->getLiteralSlot(Int0); + EXPECT_TRUE(LiteralSlot0 == LiteralSlot0Copy); + + auto *LiteralSlot42 = StackModel->getLiteralSlot(Int42); + EXPECT_TRUE(LiteralSlot0 != LiteralSlot42); + EXPECT_TRUE(LiteralSlot0->getValue() != LiteralSlot42->getValue()); +} + +TEST_F(EVMStackModelTest, VariableSlot) { + MachineRegisterInfo &MRI = MF->getRegInfo(); + Register Reg1 = MRI.createVirtualRegister(&EVM::GPRRegClass); + Register Reg2 = MRI.createVirtualRegister(&EVM::GPRRegClass); + + auto *RegSlot1 = StackModel->getRegisterSlot(Reg1); + auto *RegSlot1Copy = StackModel->getRegisterSlot(Reg1); + EXPECT_TRUE(RegSlot1 == RegSlot1Copy); + + auto *RegSlot2 = StackModel->getRegisterSlot(Reg2); + EXPECT_TRUE(RegSlot1 != RegSlot2); + EXPECT_TRUE(RegSlot1->getReg() != RegSlot2->getReg()); +} + +TEST_F(EVMStackModelTest, SymbolSlot) { + MCInstrDesc MCID = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + auto *MI = MF->CreateMachineInstr(MCID, DebugLoc()); + auto MAI = MCAsmInfo(); + auto MC = std::make_unique(Triple("evm"), &MAI, nullptr, nullptr, + nullptr, nullptr, false); + MCSymbol *Sym1 = MC->createTempSymbol("sym1", false); + MCSymbol *Sym2 = MC->createTempSymbol("sym2", false); + + auto *SymSlot1 = StackModel->getSymbolSlot(Sym1, MI); + auto *SymSlot1Copy = StackModel->getSymbolSlot(Sym1, MI); + EXPECT_TRUE(SymSlot1 == SymSlot1Copy); + + auto *SymSlot2 = StackModel->getSymbolSlot(Sym2, MI); + EXPECT_TRUE(SymSlot1 != SymSlot2); + EXPECT_TRUE(SymSlot1->getSymbol() != SymSlot2->getSymbol()); +} + +TEST_F(EVMStackModelTest, CallerReturnSlot) { + const TargetInstrInfo *TII = MF->getSubtarget().getInstrInfo(); + auto *Call = MF->CreateMachineInstr(TII->get(EVM::FCALL), DebugLoc()); + auto *Call2 = MF->CreateMachineInstr(TII->get(EVM::FCALL), DebugLoc()); + + auto *RetSlot1 = StackModel->getCallerReturnSlot(Call); + auto *RetSlot1Copy = StackModel->getCallerReturnSlot(Call); + EXPECT_TRUE(RetSlot1 == RetSlot1Copy); + + auto *RetSlot2 = StackModel->getCallerReturnSlot(Call2); + EXPECT_TRUE(RetSlot1 != RetSlot2); + EXPECT_TRUE(RetSlot1->getCall() != RetSlot2->getCall()); +} + +TEST_F(EVMStackModelTest, CalleeReturnSlot) { + // Be sure the slot for callee return (the MF) is a single one. + EXPECT_TRUE(StackModel->getCalleeReturnSlot(MF) == + StackModel->getCalleeReturnSlot(MF)); +} + +TEST_F(EVMStackModelTest, UnusedSlot) { + // Be sure the UnusedSlot is a single one. + EXPECT_TRUE(EVMStackModel::getUnusedSlot() == EVMStackModel::getUnusedSlot()); +} diff --git a/llvm/unittests/Target/EVM/StackShuffler.cpp b/llvm/unittests/Target/EVM/StackShuffler.cpp new file mode 100644 index 000000000000..b37871eaadd3 --- /dev/null +++ b/llvm/unittests/Target/EVM/StackShuffler.cpp @@ -0,0 +1,199 @@ +//===---------- llvm/unittests/MC/AssemblerTest.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 "EVMRegisterInfo.h" +#include "EVMStackModel.h" +#include "EVMStackShuffler.h" +#include "EVMSubtarget.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm-c/TargetMachine.h" +#include "llvm/CodeGen/LiveIntervals.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Target/TargetMachine.h" +#include "gtest/gtest.h" +#include + +using namespace llvm; + +#include +static TargetMachine *unwrap(LLVMTargetMachineRef P) { + return reinterpret_cast(P); +} + +class LLDCTest : public testing::Test { + void SetUp() override { + LLVMInitializeEVMTargetInfo(); + LLVMInitializeEVMTarget(); + LLVMInitializeEVMTargetMC(); + + LLVMTargetRef Target = nullptr; + const char *Triple = "evm"; + char *ErrMsg = nullptr; + if (LLVMGetTargetFromTriple(Triple, &Target, &ErrMsg)) { + FAIL() << "Failed to create target from the triple (" << Triple + << "): " << ErrMsg; + return; + } + ASSERT_TRUE(Target); + + // Construct a TargetMachine. + TM = + LLVMCreateTargetMachine(Target, Triple, "", "", LLVMCodeGenLevelDefault, + LLVMRelocDefault, LLVMCodeModelDefault); + Context = std::make_unique(); + Mod = std::make_unique("TestModule", *Context); + Mod->setDataLayout(unwrap(TM)->createDataLayout()); + const auto &LLVMTM = static_cast(*unwrap(TM)); + MMIWP = std::make_unique(&LLVMTM); + + Type *const ReturnType = Type::getVoidTy(Mod->getContext()); + FunctionType *FunctionType = FunctionType::get(ReturnType, false); + Function *const F = Function::Create( + FunctionType, GlobalValue::InternalLinkage, "TestFunction", Mod.get()); + MF = &MMIWP->getMMI().getOrCreateMachineFunction(*F); + + LISWP = std::make_unique(); + StackModel = std::make_unique( + *MF, LISWP->getLIS(), + MF->getSubtarget().stackDepthLimit()); + } + + void TearDown() override { LLVMDisposeTargetMachine(TM); } + +public: + LLVMTargetMachineRef TM = nullptr; + MachineFunction *MF = nullptr; + std::unique_ptr Context; + std::unique_ptr Mod; + std::unique_ptr MMIWP; + std::unique_ptr LISWP; + std::unique_ptr StackModel; +}; + +TEST_F(LLDCTest, Basic) { + Stack SourceStack; + Stack TargetStack; + + MachineBasicBlock *MBB = MF->CreateMachineBasicBlock(nullptr); + MF->push_back(MBB); + + const TargetInstrInfo *TII = MF->getSubtarget().getInstrInfo(); + const MCInstrDesc &MCID = TII->get(EVM::SELFBALANCE); + MachineRegisterInfo &MRI = MF->getRegInfo(); + + auto CreateInstr = [&]() { + Register Reg = MRI.createVirtualRegister(&EVM::GPRRegClass); + MachineInstr *MI = BuildMI(MBB, DebugLoc(), MCID, Reg); + return std::pair(MI, Reg); + }; + SmallVector> Instrs; + for (unsigned I = 0; I < 17; ++I) + Instrs.emplace_back(CreateInstr()); + + // Create the source stack: + // [ %0 %1 %2 %3 %4 %5 %6 %7 %9 %10 %11 %12 %13 %14 %15 %16 RET RET %5 ] + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[0].second)); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[1].second)); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[2].second)); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[3].second)); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[4].second)); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[5].second)); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[6].second)); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[7].second)); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[9].second)); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[10].second)); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[11].second)); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[12].second)); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[13].second)); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[14].second)); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[15].second)); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[16].second)); + SourceStack.emplace_back(StackModel->getCalleeReturnSlot(MBB->getParent())); + SourceStack.emplace_back(StackModel->getCalleeReturnSlot(MBB->getParent())); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[5].second)); + + // [ %1 %0 %2 %3 %4 %5 %6 %7 %9 %10 %11 %12 %13 %14 %15 %16 RET Unused Unused + // ] + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[1].second)); + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[0].second)); + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[2].second)); + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[3].second)); + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[4].second)); + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[5].second)); + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[6].second)); + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[7].second)); + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[9].second)); + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[10].second)); + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[11].second)); + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[12].second)); + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[13].second)); + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[14].second)); + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[15].second)); + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[16].second)); + TargetStack.emplace_back(StackModel->getCalleeReturnSlot(MBB->getParent())); + TargetStack.emplace_back(EVMStackModel::getUnusedSlot()); + TargetStack.emplace_back(EVMStackModel::getUnusedSlot()); + + StringRef Reference("\ +[ %0 %1 %2 %3 %4 %5 %6 %7 %9 %10 %11 %12 %13 %14 %15 %16 RET RET %5 ]\n\ +POP\n\ +[ %0 %1 %2 %3 %4 %5 %6 %7 %9 %10 %11 %12 %13 %14 %15 %16 RET RET ]\n\ +SWAP16\n\ +[ %0 RET %2 %3 %4 %5 %6 %7 %9 %10 %11 %12 %13 %14 %15 %16 RET %1 ]\n\ +SWAP16\n\ +[ %0 %1 %2 %3 %4 %5 %6 %7 %9 %10 %11 %12 %13 %14 %15 %16 RET RET ]\n\ +POP\n\ +[ %0 %1 %2 %3 %4 %5 %6 %7 %9 %10 %11 %12 %13 %14 %15 %16 RET ]\n\ +SWAP15\n\ +[ %0 RET %2 %3 %4 %5 %6 %7 %9 %10 %11 %12 %13 %14 %15 %16 %1 ]\n\ +SWAP16\n\ +[ %1 RET %2 %3 %4 %5 %6 %7 %9 %10 %11 %12 %13 %14 %15 %16 %0 ]\n\ +SWAP15\n\ +[ %1 %0 %2 %3 %4 %5 %6 %7 %9 %10 %11 %12 %13 %14 %15 %16 RET ]\n\ +PUSH Unused\n\ +[ %1 %0 %2 %3 %4 %5 %6 %7 %9 %10 %11 %12 %13 %14 %15 %16 RET Unused ]\n\ +PUSH Unused\n\ +[ %1 %0 %2 %3 %4 %5 %6 %7 %9 %10 %11 %12 %13 %14 %15 %16 RET Unused Unused ]\n"); + + std::ostringstream Output; + calculateStack( + SourceStack, TargetStack, + MF->getSubtarget().stackDepthLimit(), + [&](unsigned SwapDepth) { // swap + Output << SourceStack.toString() << '\n'; + Output << "SWAP" << SwapDepth << '\n'; + }, + [&](const StackSlot *Slot) { // dupOrPush + Output << SourceStack.toString() << '\n'; + if (Slot->isRematerializable()) + Output << "PUSH " << Slot->toString() << '\n'; + else { + Stack TmpStack = SourceStack; + std::reverse(TmpStack.begin(), TmpStack.end()); + auto *It = std::find(TmpStack.begin(), TmpStack.end(), Slot); + if (It == TmpStack.end()) + FAIL() << "Invalid DUP operation."; + + auto Depth = std::distance(TmpStack.begin(), It); + Output << "DUP" << Depth + 1 << '\n'; + } + }, + [&]() { // pop + Output << SourceStack.toString() << '\n'; + Output << "POP" << '\n'; + }); + + Output << SourceStack.toString() << '\n'; + std::cerr << Output.str(); + EXPECT_TRUE(Reference == Output.str()); +} diff --git a/llvm/unittests/TargetParser/TripleTest.cpp b/llvm/unittests/TargetParser/TripleTest.cpp index ae5df4621df9..6811dcdc4402 100644 --- a/llvm/unittests/TargetParser/TripleTest.cpp +++ b/llvm/unittests/TargetParser/TripleTest.cpp @@ -72,7 +72,11 @@ TEST(TripleTest, BasicParsing) { EXPECT_EQ("d", T.getEnvironmentName().str()); } +#if defined(_EVM) +TEST(TripleTest, DISABLED_ParsedIDs) { +#else TEST(TripleTest, ParsedIDs) { +#endif Triple T; T = Triple("i386-apple-darwin"); @@ -1233,6 +1237,20 @@ TEST(TripleTest, ParsedIDs) { EXPECT_TRUE(T.isTime64ABI()); EXPECT_TRUE(T.isHardFloatABI()); + // EVM local begin + T = Triple("evm-unknown-unknown"); + EXPECT_EQ(Triple::evm, T.getArch()); + EXPECT_EQ(Triple::UnknownVendor, T.getVendor()); + EXPECT_EQ(Triple::UnknownOS, T.getOS()); + EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment()); + + T = Triple("evm"); + EXPECT_EQ(Triple::evm, T.getArch()); + EXPECT_EQ(Triple::UnknownVendor, T.getVendor()); + EXPECT_EQ(Triple::UnknownOS, T.getOS()); + EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment()); + // EVM local end + T = Triple("huh"); EXPECT_EQ(Triple::UnknownArch, T.getArch()); } @@ -1257,7 +1275,11 @@ static std::string Join(StringRef A, StringRef B, StringRef C, StringRef D) { return Str; } +#if defined(_EVM) +TEST(TripleTest, DISABLED_Normalization) { +#else TEST(TripleTest, Normalization) { +#endif EXPECT_EQ("unknown", Triple::normalize("")); EXPECT_EQ("unknown-unknown", Triple::normalize("-")); @@ -1431,7 +1453,13 @@ TEST(TripleTest, MutateName) { EXPECT_EQ("i386-pc-darwin", T.getTriple()); } +// EVM local begin +#if defined(_EVM) +TEST(TripleTest, DISABLED_BitWidthChecks) { +#else TEST(TripleTest, BitWidthChecks) { +#endif +// EVM local end Triple T; EXPECT_FALSE(T.isArch16Bit()); EXPECT_FALSE(T.isArch32Bit()); @@ -1623,9 +1651,21 @@ TEST(TripleTest, BitWidthChecks) { EXPECT_FALSE(T.isArch16Bit()); EXPECT_TRUE(T.isArch32Bit()); EXPECT_FALSE(T.isArch64Bit()); + + // EVM local begin + T.setArch(Triple::evm); + EXPECT_FALSE(T.isArch16Bit()); + EXPECT_FALSE(T.isArch32Bit()); + EXPECT_FALSE(T.isArch64Bit()); + EXPECT_TRUE(T.isEVM()); + // EVM local end } +#if defined(_EVM) +TEST(TripleTest, DISABLED_BitWidthArchVariants) { +#else TEST(TripleTest, BitWidthArchVariants) { +#endif Triple T; EXPECT_EQ(Triple::UnknownArch, T.get32BitArchVariant().getArch()); EXPECT_EQ(Triple::UnknownArch, T.get64BitArchVariant().getArch()); @@ -1827,7 +1867,11 @@ TEST(TripleTest, BitWidthArchVariants) { EXPECT_EQ(Triple::UnknownArch, T.get64BitArchVariant().getArch()); } +#if defined(_EVM) +TEST(TripleTest, DISABLED_EndianArchVariants) { +#else TEST(TripleTest, EndianArchVariants) { +#endif Triple T; EXPECT_EQ(Triple::UnknownArch, T.getBigEndianArchVariant().getArch()); EXPECT_EQ(Triple::UnknownArch, T.getLittleEndianArchVariant().getArch()); @@ -1978,6 +2022,11 @@ TEST(TripleTest, EndianArchVariants) { EXPECT_TRUE(T.isLittleEndian()); EXPECT_EQ(Triple::UnknownArch, T.getBigEndianArchVariant().getArch()); EXPECT_EQ(Triple::dxil, T.getLittleEndianArchVariant().getArch()); + + // EVM local begin + T.setArch(Triple::evm); + EXPECT_FALSE(T.isLittleEndian()); + // EVM local end } TEST(TripleTest, XROS) { diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp index f46a8d1e9f08..eaea8d2d6aa0 100644 --- a/llvm/utils/TableGen/DecoderEmitter.cpp +++ b/llvm/utils/TableGen/DecoderEmitter.cpp @@ -707,7 +707,11 @@ void Filter::emitTableEntry(DecoderTableInfo &TableInfo) const { raw_svector_ostream S(SBytes); encodeULEB128(StartBit, S); TableInfo.Table.insert(TableInfo.Table.end(), SBytes.begin(), SBytes.end()); - TableInfo.Table.push_back(NumBits); + + // EVM local begin + TableInfo.Table.push_back((uint8_t)NumBits); + TableInfo.Table.push_back((uint8_t)(NumBits >> 8)); + // EVM local end // A new filter entry begins a new scope for fixup resolution. TableInfo.FixupStack.emplace_back(); @@ -861,8 +865,12 @@ void DecoderEmitter::emitTable(formatted_raw_ostream &OS, DecoderTable &Table, assert(ErrMsg == nullptr && "ULEB128 value too large!"); I += emitULEB128(I, OS); + // EVM local begin + // 16-bit Len value unsigned Len = *I++; - OS << Len << ", // Inst{"; + Len |= (*I++) << 8; + OS << (Len & 0xFF) << ", " << (Len >> 8) << ", // Inst{"; + // EVM local end if (Len > 1) OS << (Start + Len - 1) << "-"; OS << Start << "} ...\n"; @@ -885,9 +893,12 @@ void DecoderEmitter::emitTable(formatted_raw_ostream &OS, DecoderTable &Table, OS.indent(Indentation) << "MCD::OPC_CheckField, "; // ULEB128 encoded start value. I += emitULEB128(I, OS); - // 8-bit length. + // EVM local begin + // 16-bit Len value unsigned Len = *I++; - OS << Len << ", "; + Len |= (*I++) << 8; + OS << (Len & 0xFF) << ", " << (Len >> 8) << ", "; + // EVM local begin // ULEB128 encoded field value. I += emitULEB128(I, OS); @@ -1470,7 +1481,10 @@ void FilterChooser::emitSingletonTableEntry(DecoderTableInfo &TableInfo, for (P = Buffer; *P >= 128; ++P) TableInfo.Table.push_back(*P); TableInfo.Table.push_back(*P); - TableInfo.Table.push_back(NumBits); + // EVM local begin + TableInfo.Table.push_back((uint8_t)NumBits); + TableInfo.Table.push_back((uint8_t)(NumBits >> 8)); + // EVM local end encodeULEB128(FieldVals[I - 1], Buffer); for (P = Buffer; *P >= 128; ++P) TableInfo.Table.push_back(*P); @@ -2270,7 +2284,10 @@ static DecodeStatus decodeInstruction(const uint8_t DecodeTable[], MCInst &MI, case MCD::OPC_ExtractField: { // Decode the start value. unsigned Start = decodeULEB128AndIncUnsafe(++Ptr); - unsigned Len = *Ptr++;)"; + // EVM local begin + unsigned Len = *Ptr++; + Len |= (*Ptr++) << 8;)"; + // EVM local end if (IsVarLenInst) OS << "\n makeUp(insn, Start + Len);"; OS << R"( @@ -2299,7 +2316,10 @@ static DecodeStatus decodeInstruction(const uint8_t DecodeTable[], MCInst &MI, case MCD::OPC_CheckField: { // Decode the start value. unsigned Start = decodeULEB128AndIncUnsafe(++Ptr); - unsigned Len = *Ptr;)"; + // EVM local begin + unsigned Len = *Ptr++; + Len |= (*Ptr) << 8;)"; + // EVM local end if (IsVarLenInst) OS << "\n makeUp(insn, Start + Len);"; OS << R"( diff --git a/llvm/utils/TableGen/SubtargetEmitter.cpp b/llvm/utils/TableGen/SubtargetEmitter.cpp index 1adefea5b035..c7d2ada57faa 100644 --- a/llvm/utils/TableGen/SubtargetEmitter.cpp +++ b/llvm/utils/TableGen/SubtargetEmitter.cpp @@ -2033,11 +2033,11 @@ void SubtargetEmitter::run(raw_ostream &OS) { if (NumFeatures) OS << Target << "FeatureKV, "; else - OS << "std::nullopt, "; + OS << "{}, "; if (NumProcs) OS << Target << "SubTypeKV, "; else - OS << "std::nullopt, "; + OS << "{}, "; OS << '\n'; OS.indent(22); OS << Target << "WriteProcResTable, " << Target << "WriteLatencyTable, " @@ -2139,11 +2139,11 @@ void SubtargetEmitter::run(raw_ostream &OS) { if (NumFeatures) OS << "ArrayRef(" << Target << "FeatureKV, " << NumFeatures << "), "; else - OS << "std::nullopt, "; + OS << "{}, "; if (NumProcs) OS << "ArrayRef(" << Target << "SubTypeKV, " << NumProcs << "), "; else - OS << "std::nullopt, "; + OS << "{}, "; OS << '\n'; OS.indent(24); OS << Target << "WriteProcResTable, " << Target << "WriteLatencyTable, " diff --git a/llvm/utils/UpdateTestChecks/asm.py b/llvm/utils/UpdateTestChecks/asm.py index f150098eaaee..595bd316555a 100644 --- a/llvm/utils/UpdateTestChecks/asm.py +++ b/llvm/utils/UpdateTestChecks/asm.py @@ -257,6 +257,12 @@ class string: flags=(re.M | re.S), ) +ASM_FUNCTION_EVM_RE = re.compile( + r'^_?(?P[^:]+):[ \t]*;+[ \t]*@"?(?P=func)"?\n[^:]*?' + r'(?P.*?)\n' + r'\s*; -- End function', + flags=(re.M | re.S)) + SCRUB_X86_SHUFFLES_RE = re.compile( r"^(\s*\w+) [^#\n]+#+ ((?:[xyz]mm\d+|mem)( \{%k\d+\}( \{z\})?)? = .*)$", flags=re.M ) @@ -526,6 +532,16 @@ def scrub_asm_loongarch(asm, args): return asm +def scrub_asm_evm(asm, args): + # Scrub runs of whitespace out of the assembly, but leave the leading + # whitespace in place. + asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm) + # Expand the tabs used for indentation. + asm = string.expandtabs(asm, 2) + # Strip trailing whitespace. + asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm) + return asm + # Returns a tuple of a scrub function and a function regex. Scrub function is # used to alter function body in some way, for example, remove trailing spaces. # Function regex is used to match function name, body, etc. in raw llc output. @@ -579,6 +595,7 @@ def get_run_handler(triple): "nvptx": (scrub_asm_nvptx, ASM_FUNCTION_NVPTX_RE), "loongarch32": (scrub_asm_loongarch, ASM_FUNCTION_LOONGARCH_RE), "loongarch64": (scrub_asm_loongarch, ASM_FUNCTION_LOONGARCH_RE), + "evm" : (scrub_asm_evm, ASM_FUNCTION_EVM_RE), } handler = None best_prefix = "" diff --git a/llvm/utils/git/code-format-helper-era-llvm.py b/llvm/utils/git/code-format-helper-era-llvm.py new file mode 100644 index 000000000000..fe61be99ea2e --- /dev/null +++ b/llvm/utils/git/code-format-helper-era-llvm.py @@ -0,0 +1,367 @@ +#!/usr/bin/env python3 +# +# ====- code-format-helper, runs code formatters from the ci or in a hook --*- python -*--==# +# +# 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 +# +# ==--------------------------------------------------------------------------------------==# + +import argparse +import os +import subprocess +import sys +from typing import List, Optional + +""" +This script is run by GitHub actions to ensure that the code in PR's conform to +the coding style of LLVM. It can also be installed as a pre-commit git hook to +check the coding style before submitting it. The canonical source of this script +is in the LLVM source tree under llvm/utils/git. + +For C/C++ code it uses clang-format and for Python code it uses darker (which +in turn invokes black). + +You can learn more about the LLVM coding style on llvm.org: +https://llvm.org/docs/CodingStandards.html + +You can install this script as a git hook by symlinking it to the .git/hooks +directory: + +ln -s $(pwd)/llvm/utils/git/code-format-helper.py .git/hooks/pre-commit + +You can control the exact path to clang-format or darker with the following +environment variables: $CLANG_FORMAT_PATH and $DARKER_FORMAT_PATH. +""" + + +class FormatArgs: + start_rev: str = None + end_rev: str = None + repo: str = None + changed_files: List[str] = [] + token: str = None + verbose: bool = True + issue_number: int = 0 + + def __init__(self, args: argparse.Namespace = None) -> None: + if not args is None: + self.start_rev = args.start_rev + self.end_rev = args.end_rev + self.repo = args.repo + self.token = args.token + self.changed_files = args.changed_files + self.issue_number = args.issue_number + + +class FormatHelper: + COMMENT_TAG = "" + name: str + friendly_name: str + + @property + def comment_tag(self) -> str: + return self.COMMENT_TAG.replace("fmt", self.name) + + @property + def instructions(self) -> str: + raise NotImplementedError() + + def has_tool(self) -> bool: + raise NotImplementedError() + + def format_run(self, changed_files: List[str], args: FormatArgs) -> Optional[str]: + raise NotImplementedError() + + def pr_comment_text_for_diff(self, diff: str) -> str: + return f""" +:warning: {self.friendly_name}, {self.name} found issues in your code. :warning: + +
+ +You can test this locally with the following command: + + +``````````bash +{self.instructions} +`````````` + +
+ +
+ +View the diff from {self.name} here. + + +``````````diff +{diff} +`````````` + +
+""" + + # TODO: any type should be replaced with the correct github type, but it requires refactoring to + # not require the github module to be installed everywhere. + def find_comment(self, pr: any) -> any: + for comment in pr.as_issue().get_comments(): + if self.comment_tag in comment.body: + return comment + return None + + def update_pr(self, comment_text: str, args: FormatArgs, create_new: bool) -> None: + import github + from github import IssueComment, PullRequest + + repo = github.Github(args.token).get_repo(args.repo) + pr = repo.get_issue(args.issue_number).as_pull_request() + + comment_text = self.comment_tag + "\n\n" + comment_text + + existing_comment = self.find_comment(pr) + if existing_comment: + existing_comment.edit(comment_text) + elif create_new: + pr.as_issue().create_comment(comment_text) + + def run(self, changed_files: List[str], args: FormatArgs) -> bool: + changed_files = [arg for arg in changed_files if "third-party" not in arg] + diff = self.format_run(changed_files, args) + should_update_gh = args.token is not None and args.repo is not None + + if diff is None: + if should_update_gh: + comment_text = ( + ":white_check_mark: With the latest revision " + f"this PR passed the {self.friendly_name}." + ) + self.update_pr(comment_text, args, create_new=False) + return True + elif len(diff) > 0: + if should_update_gh: + comment_text = self.pr_comment_text_for_diff(diff) + self.update_pr(comment_text, args, create_new=True) + else: + print( + f"Warning: {self.friendly_name}, {self.name} detected " + "some issues with your code formatting..." + ) + return False + else: + # The formatter failed but didn't output a diff (e.g. some sort of + # infrastructure failure). + comment_text = ( + f":warning: The {self.friendly_name} failed without printing " + "a diff. Check the logs for stderr output. :warning:" + ) + self.update_pr(comment_text, args, create_new=False) + return False + + +class ClangFormatHelper(FormatHelper): + name = "clang-format" + friendly_name = "C/C++ code formatter" + + @property + def instructions(self) -> str: + return " ".join(self.cf_cmd) + + def should_include_extensionless_file(self, path: str) -> bool: + return path.startswith("libcxx/include") + + def filter_changed_files(self, changed_files: List[str]) -> List[str]: + filtered_files = [] + for path in changed_files: + _, ext = os.path.splitext(path) + if ext in (".cpp", ".c", ".h", ".hpp", ".hxx", ".cxx", ".inc", ".cppm"): + filtered_files.append(path) + elif ext == "" and self.should_include_extensionless_file(path): + filtered_files.append(path) + return filtered_files + + @property + def clang_fmt_path(self) -> str: + if "CLANG_FORMAT_PATH" in os.environ: + return os.environ["CLANG_FORMAT_PATH"] + return "git-clang-format" + + def has_tool(self) -> bool: + cmd = [self.clang_fmt_path, "-h"] + proc = None + try: + proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except: + return False + return proc.returncode == 0 + + def format_run(self, changed_files: List[str], args: FormatArgs) -> Optional[str]: + cpp_files = self.filter_changed_files(changed_files) + if not cpp_files: + return None + + cf_cmd = [self.clang_fmt_path, "--diff"] + + if args.start_rev and args.end_rev: + cf_cmd.append(args.start_rev) + cf_cmd.append(args.end_rev) + + cf_cmd.append("--") + cf_cmd += cpp_files + + if args.verbose: + print(f"Running: {' '.join(cf_cmd)}") + self.cf_cmd = cf_cmd + proc = subprocess.run(cf_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + sys.stdout.write(proc.stderr.decode("utf-8")) + + if proc.returncode != 0: + # formatting needed, or the command otherwise failed + if args.verbose: + print(f"error: {self.name} exited with code {proc.returncode}") + # Print the diff in the log so that it is viewable there + print(proc.stdout.decode("utf-8")) + return proc.stdout.decode("utf-8") + else: + return None + + +class DarkerFormatHelper(FormatHelper): + name = "darker" + friendly_name = "Python code formatter" + + @property + def instructions(self) -> str: + return " ".join(self.darker_cmd) + + def filter_changed_files(self, changed_files: List[str]) -> List[str]: + filtered_files = [] + for path in changed_files: + name, ext = os.path.splitext(path) + if ext == ".py": + filtered_files.append(path) + + return filtered_files + + @property + def darker_fmt_path(self) -> str: + if "DARKER_FORMAT_PATH" in os.environ: + return os.environ["DARKER_FORMAT_PATH"] + return "darker" + + def has_tool(self) -> bool: + cmd = [self.darker_fmt_path, "--version"] + proc = None + try: + proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except: + return False + return proc.returncode == 0 + + def format_run(self, changed_files: List[str], args: FormatArgs) -> Optional[str]: + py_files = self.filter_changed_files(changed_files) + if not py_files: + return None + darker_cmd = [ + self.darker_fmt_path, + "--check", + "--diff", + ] + if args.start_rev and args.end_rev: + darker_cmd += ["-r", f"{args.start_rev}...{args.end_rev}"] + darker_cmd += py_files + if args.verbose: + print(f"Running: {' '.join(darker_cmd)}") + self.darker_cmd = darker_cmd + proc = subprocess.run( + darker_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + if args.verbose: + sys.stdout.write(proc.stderr.decode("utf-8")) + + if proc.returncode != 0: + # formatting needed, or the command otherwise failed + if args.verbose: + print(f"error: {self.name} exited with code {proc.returncode}") + # Print the diff in the log so that it is viewable there + print(proc.stdout.decode("utf-8")) + return proc.stdout.decode("utf-8") + else: + sys.stdout.write(proc.stdout.decode("utf-8")) + return None + + +ALL_FORMATTERS = (DarkerFormatHelper(), ClangFormatHelper()) + + +def hook_main(): + # fill out args + args = FormatArgs() + args.verbose = False + + # find the changed files + cmd = ["git", "diff", "--cached", "--name-only", "--diff-filter=d"] + proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + output = proc.stdout.decode("utf-8") + for line in output.splitlines(): + args.changed_files.append(line) + + failed_fmts = [] + for fmt in ALL_FORMATTERS: + if fmt.has_tool(): + if not fmt.run(args.changed_files, args): + failed_fmts.append(fmt.name) + else: + print(f"Couldn't find {fmt.name}, can't check " + fmt.friendly_name.lower()) + + if len(failed_fmts) > 0: + sys.exit(1) + + sys.exit(0) + + +if __name__ == "__main__": + script_path = os.path.abspath(__file__) + if ".git/hooks" in script_path: + hook_main() + sys.exit(0) + + parser = argparse.ArgumentParser() + parser.add_argument( + "--token", type=str, required=False, help="GitHub authentiation token" + ) + parser.add_argument( + "--repo", + type=str, + default=os.getenv("GITHUB_REPOSITORY", "llvm/llvm-project"), + help="The GitHub repository that we are working with in the form of / (e.g. llvm/llvm-project)", + ) + parser.add_argument("--issue-number", type=int, required=True) + parser.add_argument( + "--start-rev", + type=str, + required=True, + help="Compute changes from this revision.", + ) + parser.add_argument( + "--end-rev", type=str, required=True, help="Compute changes to this revision" + ) + parser.add_argument( + "--changed-files", + type=str, + help="Comma separated list of files that has been changed", + ) + + args = FormatArgs(parser.parse_args()) + + changed_files = [] + if args.changed_files: + changed_files = args.changed_files.split(",") + + failed_formatters = [] + for fmt in ALL_FORMATTERS: + if not fmt.run(changed_files, args): + failed_formatters.append(fmt.name) + + if len(failed_formatters) > 0: + print(f"error: some formatters failed: {' '.join(failed_formatters)}") + sys.exit(1) diff --git a/llvm/utils/git/requirements_formatting_era_llvm.txt b/llvm/utils/git/requirements_formatting_era_llvm.txt new file mode 100644 index 000000000000..ff744f0d4225 --- /dev/null +++ b/llvm/utils/git/requirements_formatting_era_llvm.txt @@ -0,0 +1,52 @@ +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile --output-file=llvm/utils/git/requirements_formatting.txt llvm/utils/git/requirements_formatting.txt.in +# +black==23.9.1 + # via + # -r llvm/utils/git/requirements_formatting.txt.in + # darker +certifi==2023.7.22 + # via requests +cffi==1.15.1 + # via + # cryptography + # pynacl +charset-normalizer==3.2.0 + # via requests +click==8.1.7 + # via black +cryptography==41.0.3 + # via pyjwt +darker==1.7.2 + # via -r llvm/utils/git/requirements_formatting.txt.in +deprecated==1.2.14 + # via pygithub +idna==3.4 + # via requests +mypy-extensions==1.0.0 + # via black +packaging==23.1 + # via black +pathspec==0.11.2 + # via black +platformdirs==3.10.0 + # via black +pycparser==2.21 + # via cffi +pygithub==1.59.1 + # via -r llvm/utils/git/requirements_formatting.txt.in +pyjwt[crypto]==2.8.0 + # via pygithub +pynacl==1.5.0 + # via pygithub +requests==2.31.0 + # via pygithub +toml==0.10.2 + # via darker +urllib3==2.0.4 + # via requests +wrapt==1.15.0 + # via deprecated