spirv-llvm-translator: vendor as full-fork recipe (path=source, patches baked)

This commit is contained in:
2026-08-01 04:44:10 +03:00
parent 8d4b0125f8
commit 9695e81f9c
1125 changed files with 233564 additions and 4 deletions
@@ -7,10 +7,12 @@ version = "0.3.1"
description = "SPIRV-LLVM-Translator (KhronosGroup) — LLVMSPIRVLib, LLVM IR <-> SPIR-V. Host-built against llvm-native (LLVM 21), consumed by Mesa's native clc tools."
[source]
git = "https://github.com/KhronosGroup/SPIRV-LLVM-Translator.git"
# KhronosGroup tracks LLVM releases on llvm_release_NN0 branches; 21 -> 210.
branch = "llvm_release_210"
shallow_clone = true
# Vendored local fork (full-fork model): builds from the committed source/
# tree (offline, reproducible). source/ = pristine upstream + the patches below
# BAKED IN. .patch files kept tracked so a version bump can re-apply them via
# sync-recipe-source.sh.
# upstream: https://github.com/KhronosGroup/SPIRV-LLVM-Translator.git
path = "source"
[build]
template = "custom"
@@ -0,0 +1,2 @@
BasedOnStyle: LLVM
@@ -0,0 +1,41 @@
Checks: |
-*,
clang-diagnostic-*,
llvm-*,
-llvm-header-guard,
misc-*,
-misc-const-correctness,
-misc-include-cleaner,
-misc-no-recursion,
-misc-non-private-member-variables-in-classes,
-misc-unused-parameters,
-misc-use-anonymous-namespace,
readability-identifier-naming
WarningsAsErrors: |
llvm-*,
-llvm-header-guard,
misc-*,
-misc-const-correctness,
-misc-include-cleaner,
-misc-no-recursion,
-misc-non-private-member-variables-in-classes,
-misc-unused-parameters,
-misc-use-anonymous-namespace,
readability-identifier-naming
CheckOptions:
- key: readability-identifier-naming.ClassCase
value: CamelCase
- key: readability-identifier-naming.EnumCase
value: CamelCase
- key: readability-identifier-naming.FunctionCase
value: camelBack
- key: readability-identifier-naming.MemberCase
value: CamelCase
- key: readability-identifier-naming.ParameterCase
value: CamelCase
- key: readability-identifier-naming.UnionCase
value: CamelCase
- key: readability-identifier-naming.VariableCase
value: CamelCase
- key: llvm-namespace-comment.ShortNamespaceLines
value: '25'
@@ -0,0 +1,186 @@
name: Backport on Comment
# Example use: /backport llvm_release_190
on:
issue_comment:
types: [created]
permissions:
contents: write
pull-requests: write
issues: read
jobs:
backport:
if: >
github.event.issue.pull_request != null &&
startsWith(github.event.comment.body, '/backport ')
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Configure Git
run: |
git config user.name "${{ github.actor }}"
git config user.email "${{ github.actor }}@users.noreply.github.com"
- name: Ensure PR is merged
uses: actions/github-script@v7
with:
script: |
const pr = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number
});
if (!pr.data.merged) {
core.setFailed('PR #' + context.issue.number + ' is not merged.');
}
- name: Parse backport command
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
result-encoding: string
script: |
const body = context.payload.comment.body.trim();
const msg = body.match(/^\/backport\s+(llvm_release_[0-9]+)$/);
if (!msg) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: 'Invalid backport command. Expected `/backport llvm_release_<digits>`'
});
throw new Error('Invalid backport command.');
}
core.exportVariable('TARGET', msg[1]);
- name: Notify attempt
uses: actions/github-script@v7
with:
script: |
const target = process.env.TARGET;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `Attempting to create backport to \`${target}\`...`
});
- name: Create backport branch
run: |
git fetch origin ${{ env.TARGET }}:${{ env.TARGET }}
git checkout -b backport/pr-${{ github.event.issue.number }}-to-${{ env.TARGET }} origin/${{ env.TARGET }}
- name: Get commit sha
id: merge_sha
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
result-encoding: string
script: |
const pr = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number
});
// FIXME: handle PRs that are merged with "Rebase and Merge" strategy
const sha = pr.data.merge_commit_sha;
if (!sha) {
throw new Error(`No merge_commit_sha found.`);
}
return sha;
- name: Cherry-pick commit
id: cherry
run: |
conflict=false
SHA="${{ steps.merge_sha.outputs.result }}"
echo "Cherry-picking squash-merge commit $SHA"
if git cherry-pick "$SHA"; then
echo "Cherry-picked $SHA"
else
echo "Conflict on $SHA"
conflict=true
echo "CONFLICT_SHA=$SHA" >> $GITHUB_ENV
fi
echo "CONFLICT=$conflict" >> $GITHUB_ENV
- name: Notify conflict
if: env.CONFLICT == 'true'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const sha = process.env.CONFLICT_SHA;
const target = process.env.TARGET;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `Backport to \`${target}\` failed due to conflicts on commit \`${sha}\`. Please backport manually.`
});
- name: Stop on conflict
if: env.CONFLICT == 'true'
run: exit 0
- name: Push backport branch
if: env.CONFLICT == 'false'
run: git push --set-upstream origin HEAD
- name: Prepare PR
if: env.CONFLICT == 'false'
id: prinfo
run: |
echo "BODY<<EOF" >> $GITHUB_ENV
echo "Backport of PR #${{ github.event.issue.number }} into \`${{ env.TARGET }}\`." >> $GITHUB_ENV
echo "" >> $GITHUB_ENV
echo "All commits applied cleanly." >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
echo "LABELS=backport" >> $GITHUB_ENV
- name: Create Pull Request
if: env.CONFLICT == 'false'
id: create_pr
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
result-encoding: string
script: |
const {data: pr} = await github.rest.pulls.create({
owner: context.repo.owner,
repo: context.repo.repo,
head: `backport/pr-${context.issue.number}-to-${process.env.TARGET}`,
base: process.env.TARGET,
title: `[Backport to ${process.env.TARGET}] ${context.payload.issue.title}`,
body: process.env.BODY
});
return pr.html_url;
- name: Notify success
if: env.CONFLICT == 'false'
uses: actions/github-script@v7
env:
PR_URL: ${{ steps.create_pr.outputs.result }}
with:
script: |
const target = process.env.TARGET;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `Success. Backport PR created: ${process.env.PR_URL}`
});
- name: Notify workflows
if: env.CONFLICT == 'false'
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
event-type: backport-complete
@@ -0,0 +1,131 @@
# This workflow is intended to check if PR conforms with coding standards used
# in the project.
#
# Documentation for GitHub Actions:
# [workflow-syntax]: https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions
# [context-and-expression-syntax]: https://docs.github.com/en/free-pro-team@latest/actions/reference/context-and-expression-syntax-for-github-actions
# [workflow-commands]: https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions
name: Check code style
on:
pull_request:
branches:
- main
- llvm_release_*
paths-ignore: # no need to check formatting for:
- 'docs/**' # documentation
- 'test/**' # tests
- '**.md' # README
- '**.txt' # CMakeLists.txt
- '**/check-**-build.yml' # build workflows
- '**/backport-to-branch.yml' # backport workflow
- '**/patch-release.yaml' # patch release workflow
repository_dispatch:
types: [backport-complete]
branches:
- main
- llvm_release_*
paths-ignore: # no need to check formatting for:
- 'docs/**' # documentation
- 'test/**' # tests
- '**.md' # README
- '**.txt' # CMakeLists.txt
- '**/check-**-build.yml' # build workflows
- '**/backport-to-branch.yml' # backport workflow
- '**/patch-release.yaml' # patch release workflow
env:
# We need compile command database in order to perform clang-tidy check. So,
# in order to perform configure step we need to setup llvm-dev package. This
# env variable used to specify desired version of it
LLVM_VERSION: 21
jobs:
clang-format-and-tidy:
name: clang-format & clang-tidy
runs-on: ubuntu-22.04
steps:
- name: Checkout sources
uses: actions/checkout@v4
with:
# In order to gather diff from PR we need to fetch not only the latest
# commit. Depth of 2 is enough, because GitHub Actions supply us with
# merge commit as {{ github.sha }}, i.e. the second commit is a merge
# base between target branch and PR
fetch-depth: 2
- name: Gather list of changes
id: gather-list-of-changes
run: |
git diff -U0 --no-color ${{ github.sha }}^ -- include lib \
':(exclude)include/LLVMSPIRVExtensions.inc' \
':(exclude)lib/SPIRV/libSPIRV/SPIRVErrorEnum.h' \
':(exclude)lib/SPIRV/libSPIRV/SPIRVOpCodeEnum.h' \
':(exclude)lib/SPIRV/libSPIRV/SPIRVOpCodeEnumInternal.h' \
> diff-to-inspect.txt
if [ -s diff-to-inspect.txt ]; then
# Here we set an output of our step, which is used later to either
# perform or skip further steps, i.e. there is no sense to install
# clang-format if PR hasn't changed .cpp files at all
# See [workflow-commands] for reference
echo 'HAS_CHANGES=true' >> "$GITHUB_OUTPUT"
fi
- name: Install dependencies
if: ${{ steps.gather-list-of-changes.outputs.HAS_CHANGES }}
run: |
# clang-tidy requires compile command database in order to be properly
# launched, so, we need to setup llvm package to perform cmake
# configuration step to generate that database
curl -L "https://apt.llvm.org/llvm-snapshot.gpg.key" | sudo apt-key add -
echo "deb https://apt.llvm.org/jammy/ llvm-toolchain-jammy-21 main" | sudo tee -a /etc/apt/sources.list
sudo apt-get update
sudo apt-get install -yqq \
clang-format-${{ env.LLVM_VERSION }} clang-tidy-${{ env.LLVM_VERSION }} \
clang-tools-${{ env.LLVM_VERSION }} llvm-${{ env.LLVM_VERSION }}-dev \
libomp-${{ env.LLVM_VERSION }}-dev libllvmlibc-${{ env.LLVM_VERSION }}-dev \
mlir-${{ env.LLVM_VERSION }}-tools libpolly-${{ env.LLVM_VERSION }}-dev \
- name: Generate compile command database
if: ${{ steps.gather-list-of-changes.outputs.HAS_CHANGES }}
run: |
mkdir build && cd build
cmake -DCMAKE_CXX_COMPILER=clang++-${{ env.LLVM_VERSION }} \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Release -G "Unix Makefiles" ${{ github.workspace }}
- name: Run clang-format
if: ${{ steps.gather-list-of-changes.outputs.HAS_CHANGES }}
id: run-clang-format
run: |
cat diff-to-inspect.txt | /usr/share/clang/clang-format-${{ env.LLVM_VERSION }}/clang-format-diff.py \
-p1 -binary clang-format-${{ env.LLVM_VERSION }}
- name: Run clang-tidy
# By some reason, GitHub Actions automatically include "success()"
# expression into an "if" statement if it doesn't contain any of job
# status check functions. This is why this and following steps has
# "always()" and "failure()" in "if" conditions.
# See "Job status check functions" in [context-and-expression-syntax]
if: ${{ always() && steps.gather-list-of-changes.outputs.HAS_CHANGES }}
id: run-clang-tidy
run: |
cat diff-to-inspect.txt | /usr/lib/llvm-${{ env.LLVM_VERSION }}/share/clang/clang-tidy-diff.py \
-p1 -clang-tidy-binary clang-tidy-${{ env.LLVM_VERSION }} -quiet \
-path ${{ github.workspace}}/build
- name: Upload patch with clang-format fixes
uses: actions/upload-artifact@v4
if: ${{ failure() && steps.run-clang-format.outcome == 'failure' }}
with:
name: clang-format.patch
path: clang-format.patch
if-no-files-found: ignore
- name: Upload clang-tidy log
uses: actions/upload-artifact@v4
if: ${{ failure() && steps.run-clang-tidy.outcome == 'failure' }}
with:
name: clang-tidy.log
path: clang-tidy.log
if-no-files-found: ignore
@@ -0,0 +1,226 @@
# This workflow is intended to check that in-tree build of the translator is
# healthy and all tests pass. It is used in pre-commits and nightly builds.
#
# Documentation for GitHub Actions:
# [workflow-syntax]: https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions
# [context-and-expression-syntax]: https://docs.github.com/en/free-pro-team@latest/actions/reference/context-and-expression-syntax-for-github-actions
name: In-tree build & tests
on:
push:
branches:
# This check is expensive; do not run it after pushes to llvm_release_*
- main
paths-ignore: # no need to check build for:
- 'docs/**' # documentation
- '**.md' # README
- '**/check-code-style.yml' # check-code-style workflow
- '**/check-out-of-tree-build.yml' # check-out-of-tree-build workflow
- '**/check-out-of-tree-build.yml' # check-out-of-tree-build workflow
- '**/backport-to-branch.yml' # backport-to-branch workflow
- '**/patch-release.yaml' # patch-release workflow
pull_request:
branches:
- main
- llvm_release_*
paths-ignore: # no need to check build for:
- 'docs/**' # documentation
- '**.md' # README
- '**/check-code-style.yml' # check-code-style workflow
- '**/check-out-of-tree-build.yml' # check-out-of-tree-build workflow
- '**/backport-to-branch.yml' # backport-to-branch workflow
- '**/patch-release.yaml' # patch-release workflow
repository_dispatch:
types: [backport-complete]
branches:
- main
- llvm_release_*
paths-ignore: # no need to check formatting for:
- 'docs/**' # documentation
- '**.md' # README
- '**/check-code-style.yml' # check-code-style workflow
- '**/check-out-of-tree-build.yml' # check-out-of-tree-build workflow
- '**/backport-to-branch.yml' # backport-to-branch workflow
- '**/patch-release.yaml' # patch-release workflow
schedule:
# Ideally, we might want to simplify our regular nightly build as we
# probably don't need every configuration to be built every day: most of
# them are only necessary in pre-commits to avoid breakages
- cron: 0 0 * * *
env:
LLVM_VERSION: 21
jobs:
build_and_test_linux:
name: Linux
strategy:
matrix:
build_type: [Release, Debug]
shared_libs: [NoSharedLibs]
include:
- build_type: Release
shared_libs: EnableSharedLibs
fail-fast: false
runs-on: ubuntu-22.04
steps:
- name: Install dependencies
run: |
curl -L "https://apt.llvm.org/llvm-snapshot.gpg.key" | sudo apt-key add -
curl -L "https://packages.lunarg.com/lunarg-signing-key-pub.asc" | sudo apt-key add -
echo "deb https://apt.llvm.org/jammy/ llvm-toolchain-jammy-21 main" | sudo tee -a /etc/apt/sources.list
echo "deb https://packages.lunarg.com/vulkan jammy main" | sudo tee -a /etc/apt/sources.list
sudo apt-get update
sudo apt-get -yq --no-install-suggests --no-install-recommends install \
clang-${{ env.LLVM_VERSION }} \
spirv-tools
# Linux systems in GitHub Actions already have older versions of clang
# pre-installed. Make sure to override these with the relevant version.
sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-${{ env.LLVM_VERSION }} 1000
- name: Checkout LLVM sources
uses: actions/checkout@v4
with:
repository: llvm/llvm-project
ref: release/21.x
path: llvm-project
- name: Checkout the translator sources
uses: actions/checkout@v4
with:
path: llvm-project/llvm/projects/SPIRV-LLVM-Translator
- name: Get tag for SPIR-V Headers
id: spirv-headers-tag
run: |
echo "spirv_headers_tag=$(cat llvm-project/llvm/projects/SPIRV-LLVM-Translator/spirv-headers-tag.conf)" >> $GITHUB_ENV
- name: Checkout SPIR-V Headers
uses: actions/checkout@v4
with:
repository: KhronosGroup/SPIRV-Headers
ref: ${{ env.spirv_headers_tag }}
path: llvm-project/llvm/projects/SPIRV-Headers
- name: Configure
run: |
mkdir build && cd build
# ON/OFF specifically weren't used as a values for shared_libs matrix
# field to improve usability of PR page: instead of (Release, ON) a
# job will be displayed as (Release, EnableSharedLibs)
SHARED_LIBS=OFF
if [[ "${{ matrix.shared_libs }}" == "EnableSharedLibs" ]]; then
SHARED_LIBS=ON
fi
cmake ${{ github.workspace }}/llvm-project/llvm \
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \
-DBUILD_SHARED_LIBS=${SHARED_LIBS} \
-DLLVM_TARGETS_TO_BUILD="X86" \
-DSPIRV_SKIP_CLANG_BUILD=ON \
-DSPIRV_SKIP_DEBUG_INFO_TESTS=ON \
-DLLVM_LIT_ARGS="-sv --no-progress-bar" \
-G "Unix Makefiles"
- name: Build
run: |
cd build
make llvm-spirv -j$(nproc)
- name: Build tests & test
run: |
cd build
make check-llvm-spirv -j$(nproc)
build_windows:
name: Windows
strategy:
matrix:
build_type: [Release]
fail-fast: false
runs-on: windows-latest
steps:
- name: Checkout LLVM sources
uses: actions/checkout@v4
with:
repository: llvm/llvm-project
ref: release/21.x
path: llvm-project
- name: Checkout the translator sources
uses: actions/checkout@v4
with:
path: llvm-project\\llvm\\projects\\SPIRV-LLVM-Translator
- name: Get tag for SPIR-V Headers
id: spirv-headers-tag
run: |
echo "spirv_headers_tag=$(type llvm-project\\llvm\\projects\\SPIRV-LLVM-Translator\\spirv-headers-tag.conf)" >> $GITHUB_ENV
- name: Checkout SPIR-V Headers
uses: actions/checkout@v4
with:
repository: KhronosGroup/SPIRV-Headers
ref: ${{ env.spirv_headers_tag }}
path: llvm-project\\llvm\\projects\\SPIRV-Headers
- name: Configure
shell: bash
run: |
mkdir build && cd build
cmake ..\\llvm-project\\llvm \
-Thost=x64 \
-DCMAKE_BUILD_TYPE=Release \
-DLLVM_TARGETS_TO_BUILD="X86" \
-DSPIRV_SKIP_CLANG_BUILD=ON \
-DSPIRV_SKIP_DEBUG_INFO_TESTS=ON \
-DLLVM_LIT_ARGS="-sv --no-progress-bar"
- name: Build
shell: bash
run: |
cd build
cmake --build . --config ${{ matrix.build_type }} --target llvm-spirv -j2
# FIXME: Testing is disabled at the moment as it requires clang to be present
# - name: Build tests & test
# shell: bash
# run: |
# cd build
# cmake --build . --config Release --target check-llvm-spirv -j2
build_and_test_macosx:
name: macOS
strategy:
matrix:
build_type: [Release]
fail-fast: false
runs-on: macos-latest
continue-on-error: true
steps:
- name: Checkout LLVM sources
uses: actions/checkout@v4
with:
repository: llvm/llvm-project
ref: release/21.x
path: llvm-project
- name: Checkout the translator sources
uses: actions/checkout@v4
with:
path: llvm-project/llvm/projects/SPIRV-LLVM-Translator
- name: Get tag for SPIR-V Headers
id: spirv-headers-tag
run: |
echo "spirv_headers_tag=$(cat llvm-project/llvm/projects/SPIRV-LLVM-Translator/spirv-headers-tag.conf)" >> $GITHUB_ENV
- name: Checkout SPIR-V Headers
uses: actions/checkout@v4
with:
repository: KhronosGroup/SPIRV-Headers
ref: ${{ env.spirv_headers_tag }}
path: llvm-project/llvm/projects/SPIRV-Headers
- name: Configure
run: |
mkdir build && cd build
cmake ${{ github.workspace }}/llvm-project/llvm \
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \
-DLLVM_TARGETS_TO_BUILD="X86" \
-DSPIRV_SKIP_CLANG_BUILD=ON \
-DSPIRV_SKIP_DEBUG_INFO_TESTS=ON \
-DLLVM_LIT_ARGS="-sv --no-progress-bar" \
-G "Unix Makefiles"
- name: Build
run: |
cd build
make llvm-spirv -j$(sysctl -n hw.logicalcpu)
# FIXME: Testing is disabled at the moment as it requires clang to be present
# - name: Build tests & test
# run: |
# cd build
# make check-llvm-spirv -j2
@@ -0,0 +1,111 @@
# This workflow is intended to check that out-of-tree build of the translator is
# healthy and all tests pass. It is used in pre-commits and nightly builds.
#
# Documentation for GitHub Actions:
# [workflow-syntax]: https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions
# [context-and-expression-syntax]: https://docs.github.com/en/free-pro-team@latest/actions/reference/context-and-expression-syntax-for-github-actions
name: Out-of-tree build & tests
on:
push:
branches:
- main
- llvm_release_*
paths-ignore: # no need to check build for:
- 'docs/**' # documentation
- '**.md' # README
- '**/check-code-style.yml' # check-code-style workflow
- '**/check-in-tree-build.yml' # check-in-tree-build workflow
- '**/backport-to-branch.yml' # backport-to-branch workflow
- '**/patch-release.yaml' # patch-release workflow
pull_request:
branches:
- main
- llvm_release_*
paths-ignore: # no need to check build for:
- 'docs/**' # documentation
- '**.md' # README
- '**/check-code-style.yml' # check-code-style workflow
- '**/check-in-tree-build.yml' # check-in-tree-build workflow
- '**/backport-to-branch.yml' # backport-to-branch workflow
- '**/patch-release.yaml' # patch-release workflow
repository_dispatch:
types: [backport-complete]
branches:
- main
- llvm_release_*
paths-ignore: # no need to check formatting for:
- 'docs/**' # documentation
- '**.md' # README
- '**/check-code-style.yml' # check-code-style workflow
- '**/check-in-tree-build.yml' # check-in-tree-build workflow
- '**/backport-to-branch.yml' # backport-to-branch workflow
- '**/patch-release.yaml' # patch-release workflow
schedule:
- cron: 0 0 * * *
env:
LLVM_VERSION: 21
jobs:
build_and_test:
name: Linux
strategy:
matrix:
build_type: [Release, Debug]
fail-fast: false
runs-on: ubuntu-22.04
steps:
- name: Install dependencies
run: |
curl -L "https://apt.llvm.org/llvm-snapshot.gpg.key" | sudo apt-key add -
curl -L "https://packages.lunarg.com/lunarg-signing-key-pub.asc" | sudo apt-key add -
echo "deb https://apt.llvm.org/jammy/ llvm-toolchain-jammy-21 main" | sudo tee -a /etc/apt/sources.list
echo "deb https://packages.lunarg.com/vulkan jammy main" | sudo tee -a /etc/apt/sources.list
sudo apt-get update
sudo apt-get -yq --no-install-suggests --no-install-recommends install \
clang-${{ env.LLVM_VERSION }} \
clang-tools-${{ env.LLVM_VERSION }} \
llvm-${{ env.LLVM_VERSION }}-dev \
libllvmlibc-${{ env.LLVM_VERSION }}-dev \
libomp-${{ env.LLVM_VERSION }}-dev \
llvm-${{ env.LLVM_VERSION }}-tools \
mlir-${{ env.LLVM_VERSION }}-tools \
libpolly-${{ env.LLVM_VERSION }}-dev \
spirv-tools
# Linux systems in GitHub Actions already have older versions of clang
# pre-installed. Make sure to override these with the relevant version.
sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-${{ env.LLVM_VERSION }} 1000
- name: Checkout the translator sources
uses: actions/checkout@v4
with:
path: SPIRV-LLVM-Translator
- name: Get tag for SPIR-V Headers
id: spirv-headers-tag
run: |
echo "spirv_headers_tag=$(cat SPIRV-LLVM-Translator/spirv-headers-tag.conf)" >> $GITHUB_ENV
- name: Checkout SPIR-V Headers
uses: actions/checkout@v4
with:
repository: KhronosGroup/SPIRV-Headers
ref: ${{ env.spirv_headers_tag }}
path: SPIRV-Headers
- name: Configure
run: |
mkdir build && cd build
cmake ${{ github.workspace }}/SPIRV-LLVM-Translator \
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \
-DCMAKE_CXX_FLAGS="-Werror" \
-DLLVM_INCLUDE_TESTS=ON \
-DLLVM_EXTERNAL_LIT="/usr/lib/llvm-${{ env.LLVM_VERSION }}/build/utils/lit/lit.py" \
-DLLVM_EXTERNAL_SPIRV_HEADERS_SOURCE_DIR=${{ github.workspace }}/SPIRV-Headers \
-G "Unix Makefiles"
- name: Build
run: |
cd build
make llvm-spirv -j$(nproc)
- name: Build tests & test
run: |
cd build
make check-llvm-spirv -j$(nproc)
@@ -0,0 +1,88 @@
name: Automated release
on:
workflow_dispatch:
schedule:
# First day of every month
- cron: '0 0 1 * *'
jobs:
setup:
runs-on: ubuntu-latest
outputs:
latest_branch: ${{steps.latest_branch.outputs.latest_branch}}
branches_json: ${{steps.release_branches.outputs.branches_json}}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get latest llvm_release branch
id: latest_branch
run: |
git branch -r \
| grep 'llvm_release_' \
| sed -E 's/.*\/llvm_release_([0-9]+)/\1/' \
| sort -n -r \
| head -1 \
| xargs printf "latest_branch=llvm_release_%s" \
>> $GITHUB_OUTPUT
- name: Get branch list
id: release_branches
run: |
git branch -r \
| grep "origin/llvm_release_" \
| sed -E 's/\ *origin\/([^\ ]*)/\"\1\"/' \
| paste -sd',' \
| xargs -0 -d"\n" printf 'branches_json={"branch":[%s]}' \
>> $GITHUB_OUTPUT
release:
runs-on: ubuntu-latest
needs: setup
strategy:
matrix: ${{fromJson(needs.setup.outputs.branches_json)}}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ matrix.branch }}
fetch-depth: 0
- name: Get commits info
id: versions
run: |
export LATEST_VERSION=\
"$(git describe --tags --abbrev=0 --match 'v*')"
export LLVM_VERSION=$(echo $LATEST_VERSION \
| sed -E 's/(v[0-9]+\.[0-9]+)\.([0-9]+).*/\1/')
export PATCH=$(echo $LATEST_VERSION \
| sed -E 's/(v[0-9]+\.[0-9]+)\.([0-9]+).*/\2/')
echo "llvm_version=$LLVM_VERSION" >> $GITHUB_OUTPUT
echo "patch=$PATCH" >> $GITHUB_OUTPUT
echo "latest_version=${LATEST_VERSION}" >> $GITHUB_OUTPUT
echo "release_version=${LLVM_VERSION}.$((${PATCH}+1))" \
>> $GITHUB_OUTPUT
git rev-list ${LATEST_VERSION}..HEAD --count \
| xargs printf "commits_since_last_release=%d\n" >> $GITHUB_OUTPUT
git rev-parse HEAD | xargs printf "last_commit=%s\n" >> $GITHUB_OUTPUT
- name: Release
uses: softprops/action-gh-release@v2
if: ${{ steps.versions.outputs.commits_since_last_release != 0 }}
with:
# Setting tag to have format:
# %latest llvm version%.%latest patch + 1%
tag_name: ${{ steps.versions.outputs.release_version }}
# We have to set this so tag is set on the branch we are releasing
target_commitish: ${{ steps.versions.outputs.last_commit }}
# We don't want to mark patch releases latest unless it is latest
# major version
make_latest: >-
${{ needs.setup.outputs.latest_branch == matrix.branch }}
name: >
SPIR-V LLVM translator based on LLVM
${{ steps.versions.outputs.llvm_version }}
body: "Full Changelog: ${{ github.server_url }}/\
${{ github.repository }}/compare/\
${{ steps.versions.outputs.latest_version }}...\
${{ steps.versions.outputs.release_version }}"
@@ -0,0 +1,39 @@
#==============================================================================#
# This file specifies intentionally untracked files that git should ignore.
# See: http://www.kernel.org/pub/software/scm/git/docs/gitignore.html
#==============================================================================#
build/
#==============================================================================#
# File extensions to be ignored anywhere in the tree.
#==============================================================================#
# Temp files created by most text editors.
*~
# Merge files created by git.
*.orig
# Byte compiled python modules.
*.pyc
# vim swap files
.*.sw?
.sw?
#OS X specific files.
.DS_store
#==============================================================================#
# Explicit files to ignore (only matches one).
#==============================================================================#
# Various tag programs
/tags
/TAGS
/GPATH
/GRTAGS
/GSYMS
/GTAGS
.gitusers
autom4te.cache
cscope.files
cscope.out
autoconf/aclocal.m4
autoconf/autom4te.cache
compile_commands.json
@@ -0,0 +1 @@
SPIRV-LLVM-Translator.git
@@ -0,0 +1,176 @@
cmake_minimum_required(VERSION 3.13.4)
if(NOT DEFINED BASE_LLVM_VERSION)
set (BASE_LLVM_VERSION 21.1.0)
endif(NOT DEFINED BASE_LLVM_VERSION)
set(LLVM_SPIRV_VERSION ${BASE_LLVM_VERSION}.0)
include(FetchContent)
include(FindPkgConfig)
option(LLVM_SPIRV_INCLUDE_TESTS
"Generate build targets for the llvm-spirv lit tests."
${LLVM_INCLUDE_TESTS})
if (NOT DEFINED LLVM_SPIRV_BUILD_EXTERNAL)
# check if we build inside llvm or not
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
set(LLVM_SPIRV_BUILD_EXTERNAL YES)
endif(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
endif (NOT DEFINED LLVM_SPIRV_BUILD_EXTERNAL)
if(LLVM_SPIRV_BUILD_EXTERNAL)
# Make sure llvm-spirv gets built when building outside the llvm tree.
set(LLVM_BUILD_TOOLS ON)
endif(LLVM_SPIRV_BUILD_EXTERNAL)
# Download spirv.hpp from the official SPIRV-Headers repository.
# One can skip this step by manually setting
# LLVM_EXTERNAL_SPIRV_HEADERS_SOURCE_DIR path.
if(NOT DEFINED LLVM_TOOL_SPIRV_HEADERS_BUILD AND
NOT DEFINED LLVM_EXTERNAL_SPIRV_HEADERS_SOURCE_DIR)
set(LLVM_EXTERNAL_SPIRV_HEADERS_SOURCE_DIR
"${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Headers")
message(STATUS "SPIR-V Headers location is not specified. Will try to download
spirv.hpp from https://github.com/KhronosGroup/SPIRV-Headers into
${LLVM_EXTERNAL_SPIRV_HEADERS_SOURCE_DIR}")
file(READ spirv-headers-tag.conf SPIRV_HEADERS_TAG)
# Strip the potential trailing newline from tag
string(STRIP "${SPIRV_HEADERS_TAG}" SPIRV_HEADERS_TAG)
FetchContent_Declare(spirv-headers
GIT_REPOSITORY https://github.com/KhronosGroup/SPIRV-Headers.git
GIT_TAG ${SPIRV_HEADERS_TAG}
SOURCE_DIR ${LLVM_EXTERNAL_SPIRV_HEADERS_SOURCE_DIR}
)
FetchContent_MakeAvailable(spirv-headers)
else()
if(NOT DEFINED LLVM_EXTERNAL_SPIRV_HEADERS_SOURCE_DIR)
# This means LLVM_TOOL_SPIRV_HEADERS_BUILD is defined, therefore
# SPIRV-Headers exist as a subproject.
set(LLVM_EXTERNAL_SPIRV_HEADERS_SOURCE_DIR
"${CMAKE_SOURCE_DIR}/projects/SPIRV-Headers")
if(NOT EXISTS ${LLVM_EXTERNAL_SPIRV_HEADERS_SOURCE_DIR})
message(FATAL_ERROR "No location specified for SPIRV-Headers.
Try setting the LLVM_EXTERNAL_SPIRV_HEADERS_SOURCE_DIR
path or put the project into the llvm/projects folder
under the name 'SPIRV-Headers'")
endif()
endif()
message(STATUS "Using SPIR-V Headers from
${LLVM_EXTERNAL_SPIRV_HEADERS_SOURCE_DIR}")
endif()
if(LLVM_SPIRV_BUILD_EXTERNAL)
project(LLVM_SPIRV
VERSION
${LLVM_SPIRV_VERSION}
LANGUAGES
CXX
C
)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(LLVM_SPIRV_INCLUDE_TESTS)
set(LLVM_TEST_COMPONENTS
llvm-as
llvm-dis
)
endif(LLVM_SPIRV_INCLUDE_TESTS)
find_package(LLVM ${BASE_LLVM_VERSION} REQUIRED
COMPONENTS
Analysis
BitReader
BitWriter
CodeGen
Core
Passes
Support
TargetParser
TransformUtils
${LLVM_TEST_COMPONENTS}
)
set(CMAKE_MODULE_PATH
${CMAKE_MODULE_PATH}
${LLVM_CMAKE_DIR}
)
include(AddLLVM)
include(HandleLLVMOptions)
include(LLVM-Config)
message(STATUS "Found LLVM: ${LLVM_VERSION}")
option(CCACHE_ALLOWED "allow use of ccache" TRUE)
find_program(CCACHE_EXE_FOUND ccache)
if(CCACHE_EXE_FOUND AND CCACHE_ALLOWED)
message(STATUS "Found ccache: ${CCACHE_EXE_FOUND}")
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
endif()
endif()
is_llvm_target_library("SPIRV" spirv_present_result INCLUDED_TARGETS)
if(spirv_present_result)
message(STATUS "Found SPIR-V Backend")
set(SPIRV_BACKEND_FOUND TRUE)
add_compile_definitions(LLVM_SPIRV_BACKEND_TARGET_PRESENT)
endif()
set(LLVM_SPIRV_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include)
# first try locating SPIRV-Tools via pkgconfig (the old way)
pkg_search_module(SPIRV_TOOLS SPIRV-Tools)
if (NOT SPIRV_TOOLS_FOUND)
# then try locating SPIRV-Tools via cmake (the new way)
find_package(SPIRV-Tools)
find_package(SPIRV-Tools-tools)
if (SPIRV-Tools_FOUND AND SPIRV-Tools-tools_FOUND)
set(SPIRV_TOOLS_FOUND TRUE)
# check for the existance of library targets in the found packages
if(TARGET SPIRV-Tools-shared)
# use the shared libary target if present
set(SPIRV-Tools-library SPIRV-Tools-shared)
elseif(TARGET SPIRV-Tools-static)
# otherwise fallback to the static library target
set(SPIRV-Tools-library SPIRV-Tools-static)
else()
message(FATAL_ERROR "Found SPIRV-Tools package but neither "
"SPIRV-Tools-shared or SPIRV-Tools-static targets exist.")
endif()
set(SPIRV_TOOLS_LDFLAGS ${SPIRV-Tools-library})
get_target_property(SPIRV_TOOLS_INCLUDE_DIRS ${SPIRV-Tools-library} INTERFACE_INCLUDE_DIRECTORIES)
endif()
endif()
option(LLVM_SPIRV_ENABLE_LIBSPIRV_DIS "Enable --spirv-tools-dis support.")
if (NOT SPIRV_TOOLS_FOUND AND LLVM_SPIRV_ENABLE_LIBSPIRV_DIS)
message(STATUS "SPIRV-Tools not found; project will be built without "
"--spirv-tools-dis support.")
endif()
add_subdirectory(lib/SPIRV)
add_subdirectory(tools/llvm-spirv)
if(LLVM_SPIRV_INCLUDE_TESTS)
add_subdirectory(test)
endif(LLVM_SPIRV_INCLUDE_TESTS)
install(
FILES
${LLVM_SPIRV_INCLUDE_DIRS}/LLVMSPIRVLib.h
${LLVM_SPIRV_INCLUDE_DIRS}/LLVMSPIRVOpts.h
${LLVM_SPIRV_INCLUDE_DIRS}/LLVMSPIRVExtensions.inc
DESTINATION
${CMAKE_INSTALL_PREFIX}/include/LLVMSPIRVLib
)
configure_file(LLVMSPIRVLib.pc.in ${CMAKE_BINARY_DIR}/LLVMSPIRVLib.pc @ONLY)
install(
FILES
${CMAKE_BINARY_DIR}/LLVMSPIRVLib.pc
DESTINATION
${CMAKE_INSTALL_PREFIX}/lib${LLVM_LIBDIR_SUFFIX}/pkgconfig
)
@@ -0,0 +1 @@
A reminder that this issue tracker is managed by the Khronos Group. Interactions here should follow the Khronos Code of Conduct (https://www.khronos.org/developers/code-of-conduct), which prohibits aggressive or derogatory language. Please keep the discussion friendly and civil.
@@ -0,0 +1,111 @@
# Contribution guidelines
## If you have found a bug or would like to see a new feature
Please reach us by creating a [new issue].
Your bug report should include a proper description and steps to reproduce:
- attach the LLVM BC or SPV file you are trying to translate and the command you
launch
- any backtrace in case of crashes would be helpful
- please describe what goes wrong or what is unexpected during translation
For feature requests, please describe the feature you would like to see
implemented in the translator.
[new issue]: https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/new
## If you would like to contribute your change
Please open a [pull request]. If you are not sure whether your changes are
correct, you can either mark it as [draft] or create an issue to discuss the
problem and possible ways to fix it prior to publishing a PR.
It is okay to have several commits in the PR, but each of them should be
buildable and tests should pass. Maintainers can squash several commits
into a single one for you during merge, but if you would like to see several
commits in the git history, please let us know in PR description/comments so
maintainers will rebase your PR instead of squashing it.
Each functional change (new feature or bug fix) must be supplied with
corresponding tests. See [#testing-guidelines] for more information about
testing. NFC (non-functional change) PRs can be accepted without new tests.
Code changes should follow coding standards, which are inherited from [LLVM
Coding Standards]. Compliance of your code is checked automatically using
GitHub Actions. See [clang-format] and [clang-tidy] configs for more details
about coding standards.
## How to add an extension
First of all please make sure you have added a link to the
specification for the extension in your PR. Then to add definitions of
new Op Codes you shall modify [spirv.hpp], which is an external
dependency for this project. To do so, you should add new definitions
to [json grammar file], rebuild the header following the
[instructions] in [SPIR-V Headers repository] and push your changes
for review, i.e. make a PR. Once the PR is merged, a new spirv.hpp
will have to be downloaded during build of the translator; make sure
to update the hash for SPIRV-Headers in [spirv-headers-tag.conf]
so that tokens from your extension can be visible to the translator
build.
It's highly recommended to add the definitions to [SPIR-V Headers repository]
first, but if you don't want to bring it there yet, you can define new Op Codes
in the [internal SPIR-V header file].
For local testing you can copy your spirv.hpp variant to
`<PATH_TO_SPIRV_HEADERS>/include/spirv/unified1` and/or modify it
there. See [README.md](README.md#configuring-spir-v-headers) for build
instructions that should be employed with such modifications.
### Conditions to merge a PR
In order to get your PR merged, the following conditions must be met:
- If you are a first-time contributor, you have to sign the
[Contributor License Agreement]. Corresponding link and instructions will be
automatically posted into your PR.
- [GitHub CI testing] jobs must pass on your PR: this includes functional
testing and checking for complying with coding standards.
- You need to get approval from at least one contributor with merge rights.
As a contributor, you should expect that even an approved PR might still be left
open for a few days: this is needed, because the translator is being developed
by different vendors and individuals and we need to ensure that each interested
party is able to react to new changes and provide feedback.
Information below is a guideline for repo maintainers and can be used by
contributors to get some expectations about how long a PR has to be open before
it can be merged:
- For any significant change/redesign, the PR must be open for at least 5
working days, so everyone interested can step in to provide feedback, discuss
direction and help to find bugs.
- Ideally, there should be approvals from different vendors/individuals to get
it merged, particularly for larger changes.
- For regular changes/bug fixes, the PR must be open for at least 2-3 working
days, so everyone interested can step in for review and provide feedback.
- If the change is vendor-specific (bug fix in vendor extension implementation
or new vendor-specific extension support), then it is okay to merge PR
sooner.
- If the change affects or might affect several interested parties, the PR
must be left open for 2-3 working days and it would be good to see feedback
from different vendors/inviduals before merging.
- Tiny NFC changes or trivial build fixes (due to LLVM API changes) can be
submitted as soon as testing is finished and PR approved - no need to wait for
too long.
- In general, just use common sense to wait long enough to get feedback from
everyone who might be interested in the PR and don't hesitate to explicitly
mention individuals who might be interested in reviewing the PR.
[pull request]: https://github.com/KhronosGroup/SPIRV-LLVM-Translator/pulls
[draft]: https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests#draft-pull-requests
[LLVM Coding Standards]: https://llvm.org/docs/CodingStandards.html
[clang-format]: [.clang-format]
[clang-tidy]: [.clang-tidy]
[spirv.hpp]: https://github.com/KhronosGroup/SPIRV-Headers/blob/master/include/spirv/unified1/spirv.hpp
[json grammar file]: https://github.com/KhronosGroup/SPIRV-Headers/blob/master/include/spirv/unified1/spirv.core.grammar.json
[instructions]: https://github.com/KhronosGroup/SPIRV-Headers#generating-headers-from-the-json-grammar-for-the-spir-v-core-instruction-set
[SPIR-V Headers repository]: https://github.com/KhronosGroup/SPIRV-Headers
[internal SPIR-V header file]: https://github.com/KhronosGroup/SPIRV-LLVM-Translator/blob/main/lib/SPIRV/libSPIRV/spirv_internal.hpp
[Contributor License Agreement]: https://cla-assistant.io/KhronosGroup/SPIRV-LLVM-Translator
[GitHub CI testing]: https://github.com/KhronosGroup/SPIRV-LLVM-Translator/actions
@@ -0,0 +1,70 @@
==============================================================================
LLVM Release License
==============================================================================
University of Illinois/NCSA
Open Source License
Copyright (c) 2003-2014 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.
==============================================================================
Copyrights and Licenses for Third Party Software Distributed with LLVM:
==============================================================================
The LLVM software contains code written by third parties. Such software will
have its own individual LICENSE.TXT file in the directory in which it appears.
This file will describe the copyrights, license, and restrictions which apply
to that code.
The disclaimer of warranty in the University of Illinois Open Source License
applies to all code in the LLVM Distribution, and nothing in any of the
other licenses gives permission to use the names of the LLVM Team or the
University of Illinois to endorse or promote products derived from this
Software.
The following pieces of software have additional or alternate copyrights,
licenses, and/or restrictions:
Program Directory
------- ---------
Autoconf llvm/autoconf
llvm/projects/ModuleMaker/autoconf
Google Test llvm/utils/unittest/googletest
OpenBSD regex llvm/lib/Support/{reg*, COPYRIGHT.regex}
pyyaml tests llvm/test/YAMLParser/{*.data, LICENSE.TXT}
ARM contributions llvm/lib/Target/ARM/LICENSE.TXT
md5 contributions llvm/lib/Support/MD5.cpp llvm/include/llvm/Support/MD5.h
@@ -0,0 +1,12 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=${prefix}
libdir=${prefix}/lib@LLVM_LIBDIR_SUFFIX@
includedir=${prefix}/include
Name: LLVMSPIRVLib
Description: LLVM/SPIR-V bi-directional translator
Version: @LLVM_SPIRV_VERSION@
URL: https://github.com/KhronosGroup/SPIRV-LLVM-Translator
Libs: -L${libdir} -lLLVMSPIRVLib
Cflags: -I${includedir}
@@ -0,0 +1,283 @@
# LLVM/SPIR-V Bi-Directional Translator
[![Out-of-tree build & tests](https://github.com/KhronosGroup/SPIRV-LLVM-Translator/actions/workflows/check-out-of-tree-build.yml/badge.svg?branch=llvm_release_210&event=schedule)](https://github.com/KhronosGroup/SPIRV-LLVM-Translator/actions?query=workflow%3A%22Out-of-tree+build+%26+tests%22+event%3Aschedule)
[![In-tree build & tests](https://github.com/KhronosGroup/SPIRV-LLVM-Translator/actions/workflows/check-in-tree-build.yml/badge.svg?branch=llvm_release_210&event=schedule)](https://github.com/KhronosGroup/SPIRV-LLVM-Translator/actions?query=workflow%3A%22In-tree+build+%26+tests%22+event%3Aschedule)
This repository contains source code for the LLVM/SPIR-V Bi-Directional Translator, a library and tool for translation between LLVM IR and [SPIR-V](https://www.khronos.org/registry/spir-v/).
This project currently only supports the OpenCL/compute "flavour" of SPIR-V: it consumes and produces SPIR-V modules that declare the `Kernel` capability.
The LLVM/SPIR-V Bi-Directional Translator is open source software. You may freely distribute it under the terms of the license agreement found in LICENSE.txt.
## Directory Structure
The files/directories related to the translator:
* [include/LLVMSPIRVLib.h](include/LLVMSPIRVLib.h) - header file
* [lib/SPIRV](lib/SPIRV) - library for SPIR-V in-memory representation, decoder/encoder and LLVM/SPIR-V translator
* [tools/llvm-spirv](tools/llvm-spirv) - command line utility for translating between LLVM bitcode and SPIR-V binary
## Build Instructions
The `main` branch of this repo is aimed to be buildable with the latest
LLVM `main` revision.
### Build with pre-installed LLVM
The translator can be built with the latest(nightly) package of LLVM. For Ubuntu and Debian systems LLVM provides repositories with nightly builds at http://apt.llvm.org/. For example the latest package for Ubuntu 16.04 can be installed with the following commands:
```
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo add-apt-repository "deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial main"
sudo apt-get update
sudo apt-get install llvm-21-dev llvm-21-tools clang-21 libclang-21-dev
```
The installed version of LLVM will be used by default for out-of-tree build of the translator.
```
git clone https://github.com/KhronosGroup/SPIRV-LLVM-Translator.git
mkdir SPIRV-LLVM-Translator/build && cd SPIRV-LLVM-Translator/build
cmake ..
make llvm-spirv -j`nproc`
```
### Build with pre-built LLVM
If you have a custom build (based on the latest version) of LLVM libraries you
can link the translator against it.
```
git clone https://github.com/KhronosGroup/SPIRV-LLVM-Translator.git
mkdir SPIRV-LLVM-Translator/build && cd SPIRV-LLVM-Translator/build
cmake .. -DLLVM_DIR=<llvm_build_dir>/lib/cmake/llvm/
make llvm-spirv -j`nproc`
```
If the translator is used as part of another CMake project, you will need
to define `LLVM_SPIRV_BUILD_EXTERNAL`:
```
cmake .. -DLLVM_DIR=<llvm_build_dir>/lib/cmake/llvm/ -DLLVM_SPIRV_BUILD_EXTERNAL=YES
```
Where `llvm_build_dir` is the LLVM build directory.
### LLVM in-tree build
The translator can be built as a regular LLVM subproject. To do that you need to clone it into the `llvm/projects` or `llvm/tools` directory.
```
git clone https://github.com/llvm/llvm-project.git
cd llvm-project/llvm/projects
git clone https://github.com/KhronosGroup/SPIRV-LLVM-Translator.git
```
Run (or re-run) cmake as usual for LLVM. After that you should have `llvm-spirv` and `check-llvm-spirv` targets available.
```
mkdir llvm-project/build && cd llvm-project/build
cmake ../llvm -DLLVM_ENABLE_PROJECTS="clang"
make llvm-spirv -j`nproc`
```
Note on enabling the `clang` project: there are tests in the translator that depend
on `clang` binary, which makes clang a required dependency (search for
`LLVM_SPIRV_TEST_DEPS` in [test/CMakeLists.txt](test/CMakeLists.txt)) for
`check-llvm-spirv` target.
Building clang from sources takes time and resources and it can be avoided:
- if you are not interested in launching unit-tests for the translator after
build, you can disable generation of test targets by passing
`-DLLVM_SPIRV_INCLUDE_TESTS=OFF` option.
- if you are interested in launching unit-tests, but don't want to build clang
you can pass `-DSPIRV_SKIP_CLANG_BUILD` cmake option to avoid adding `clang`
as dependency for `check-llvm-spirv` target. However, LIT will search for
`clang` binary when tests are launched and it should be available at this
point.
- building and testing completely without `clang` is not supported at the
moment, see [KhronosGroup/SPIRV-LLVM-Translator#477](https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/477)
to track progress, discuss and contribute.
### Build with SPIRV-Tools
The translator can use [SPIRV-Tools](https://github.com/KhronosGroup/SPIRV-Tools) to generate assembly with widely adopted syntax.
This feature can be enabled by passing `-DLLVM_SPIRV_ENABLE_LIBSPIRV_DIS=ON` option.
If SPIRV-Tools have been installed prior to the build it will be detected and
used automatically. However it is also possible to enable use of SPIRV-Tools
from a custom location using the following instructions:
1. Checkout, build and install SPIRV-Tools using
[the following instructions](https://github.com/KhronosGroup/SPIRV-Tools#build).
Example using CMake with Ninja:
```
cmake -G Ninja <SPIRV-Tools source location> -DCMAKE_INSTALL_PREFIX=<SPIRV-Tools installation location>
ninja install
```
2. Point pkg-config to the SPIR-V tools installation when configuring the translator by setting
`PKG_CONFIG_PATH=<SPIRV-Tools installation location>/lib/pkgconfig/` variable
before the cmake line invocation.
Example:
```
PKG_CONFIG_PATH=<SPIRV-Tools installation location>/lib/pkgconfig/ cmake <other options>
```
To verify the SPIR-V Tools integration in the translator build, run the following line
```
llvm-spirv --spirv-tools-dis input.bc -o -
```
The output should be printed in the standard assembly syntax.
## Configuring SPIR-V Headers
The translator build is dependent on the official Khronos header file
`spirv.hpp` that maps SPIR-V extensions, decorations, instructions,
etc. onto numeric tokens. The official header version is available at
[KhronosGroup/SPIRV-Headers](https://github.com/KhronosGroup/SPIRV-Headers).
There are several options for accessing the header file:
- By default, the header file repository will be downloaded from
Khronos Group GitHub and put into `<build_dir>/SPIRV-Headers`.
- If you are building the translator in-tree, you can manually
download the SPIR-V Headers repo into `llvm/projects` - this
location will be automatically picked up by the LLVM build
scripts. Make sure the folder retains its default naming in
that of `SPIRV-Headers`.
- Any build type can also use an external installation of SPIR-V
Headers - if you have the headers downloaded somewhere in your
system and want to use that version, simply extend your CMake
command with `-DLLVM_EXTERNAL_PROJECTS="SPIRV-Headers"
-DLLVM_EXTERNAL_SPIRV_HEADERS_SOURCE_DIR=</path/to/headers_dir>` for in-tree
builds and just `-DLLVM_EXTERNAL_SPIRV_HEADERS_SOURCE_DIR=</path/to/headers_dir>`
for out-of-tree builds.
## Test instructions
All tests related to the translator are placed in the [test](test) directory. A number of the tests require spirv-as (part of SPIR-V Tools) to run, but the remainder of the tests can still be run without this. Optionally the tests can make use of spirv-val (part of SPIRV-Tools) in order to validate the generated SPIR-V against the official SPIR-V specification.
In case tests are failing due to SPIRV-Tools not supporting certain SPIR-V features, please get an updated package. The `PKG_CONFIG_PATH` environmental variable can be used to let cmake point to a custom installation.
Execute the following command inside the build directory to run translator tests:
```
make test
```
This requires that the `-DLLVM_SPIRV_INCLUDE_TESTS=ON` argument is
passed to CMake during the build step. Additionally,
`-DLLVM_EXTERNAL_LIT="/usr/lib/llvm-21/build/utils/lit/lit.py"` is
needed when building with a pre-installed version of LLVM.
The translator test suite can be disabled by passing
`-DLLVM_SPIRV_INCLUDE_TESTS=OFF` to CMake.
## Run Instructions for `llvm-spirv`
To translate between LLVM IR and SPIR-V:
1. Execute the following command to translate `input.bc` to `input.spv`
```
llvm-spirv input.bc
```
2. Execute the following command to translate `input.spv` to `input.bc`
```
llvm-spirv -r input.spv
```
Recommended options:
* `-spirv-target-env` - to specify target version of OpenCL builtins to translate to (default CL1.2)
3. Other options accepted by `llvm-spirv`
* `-o file_name` - to specify output name
* `-spirv-debug` - output debugging information
* `-spirv-text` - read/write SPIR-V in an internal textual format for debugging purpose. The textual format is not defined by SPIR-V spec.
* `--spirv-tools-dis` - print SPIR-V assembly in SPIRV-Tools format. Only available on [builds with SPIRV-Tools](#build-with-spirv-tools).
* `-help` - to see full list of options
Translation from LLVM IR to SPIR-V and then back to LLVM IR is not guaranteed to
produce the original LLVM IR. In particular, LLVM intrinsic call instructions
may get replaced by function calls to OpenCL builtins and metadata may be
dropped.
### Handling SPIR-V versions generated by the translator
There is one option to control the behavior of the translator with respect to
the version of the SPIR-V file which is being generated/consumed.
* `-spirv-max-version=` - this option allows restricting the
SPIRV-LLVM-Translator **not** to generate a SPIR-V with a version which is
higher than the one specified via this option.
If the `-r` option was also specified, the SPIRV-LLVM-Translator will reject
the input file and emit an error if the SPIR-V version in it is higher than
one specified via this option.
Allowed values are `1.0`, `1.1`, `1.2`, `1.3`, `1.4`, `1.5` and `1.6`.
More information can be found in
[SPIR-V versions and extensions handling](docs/SPIRVVersionsAndExtensionsHandling.rst)
### Handling SPIR-V extensions generated by the translator
By default, during SPIR-V generation, the translator doesn't use any extensions.
However, during SPIR-V consumption, the translator accepts input files that use
any known extensions.
If certain extensions are required to be enabled or disabled, the following
command line option can be used:
* ``--spirv-ext=`` - this options allows controlling which extensions are
allowed/disallowed
Valid value for this option is comma-separated list of extension names prefixed
with ``+`` or ``-`` - plus means allow to use extension, minus means disallow
to use extension. There is one more special value which can be used as extension
name in this option: ``all`` - it affects all extension which are known to the
translator.
If ``--spirv-ext`` contains the name of an extension which is not known for the
translator, it will emit an error.
More information can be found in
[SPIR-V versions and extensions handling](docs/SPIRVVersionsAndExtensionsHandling.rst)
## Branching strategy
Code on the main branch in this repository is intended to be compatible with
the main branch of the [llvm](https://github.com/llvm/llvm-project)
project. That is, for an OpenCL kernel compiled to llvm bitcode by the latest
git revision of Clang it should be possible to translate it to SPIR-V with the
llvm-spirv tool.
All new development should be done on the main branch.
To have versions compatible with released versions of LLVM and Clang,
corresponding tags are available in this repository. For example, to build
the translator with
[LLVM 7.0.0](https://github.com/llvm/llvm-project/tree/llvmorg-7.0.0)
one should use the
[v7.0.0-1](https://github.com/KhronosGroup/SPIRV-LLVM-Translator/tree/v7.0.0-1)
tag. The 7.x releases are maintained on the
[llvm_release_70](https://github.com/KhronosGroup/SPIRV-LLVM-Translator/tree/llvm_release_70)
branch. As a general rule, commits from the main branch may be backported to
the release branches as long as they do not depend on features from a later
LLVM/Clang release and there are no objections from the maintainer(s). There
is no guarantee that older release branches are proactively kept up to date
with main, but you can request specific commits on older release branches by
creating a pull request or raising an issue on GitHub.
## Releasing strategy
As mentioned earlier there are branches `llvm_release_*` that get backported
changes. Those changes if exists are released automatically by github CI on
monthly basis in a format `<llvm_major>.<llvm_minor>.<latest patch +1>`.
## Deprecation of "preview extensions"
In a case if a "preview extension" has to be deprecated, as the first step one
should disable support for forward translation of the extension in the main branch
(this will prevent new SPIR-V modules from being generated using the extension).
Meanwhile support reverse translation for the extension should be continued
(this retains compatibility with existing SPIR-V modules).
* Addition of deprecation warning for the extension is not required, yet recommended for extensions adding
instructions representable in LLVM IR only via SPIR-V friendly LLVM IR.
* We encourage backporting the changes to other branches to speed up removal, but this is not required.
After at least one release cycle one may remove support for reverse translation in the main branch as well,
at which point support for the "preview extension" is considered removed.
These are guidelines, not requirements, and we will consider exceptions on a case-by-case basis.
@@ -0,0 +1,238 @@
The SPIRV-LLVM-Translator will "lower" some LLVM intrinsic calls to another function or implementation
using one of the following four methods:
Method 1:
Variation A:
In transIntrinsicInst in SPIRVWriter, calls to LLVM intrinsics are replaced with a SPIRV ExtInst.
For example:
%0 = tail call i32 @llvm.ctlz.i32(i32 %x, i1 true)
is translated into SPIRV with an OpenCL ExtInst clz:
6 ExtInst 2 7 1 clz 5
The code in transIntrinsicInst to do this translation is:
case Intrinsic::ctlz:
case Intrinsic::cttz: {
SPIRVWord ExtOp = IID == Intrinsic::ctlz ? OpenCLLIB::Clz : OpenCLLIB::Ctz;
SPIRVType *Ty = transType(II->getType());
std::vector<SPIRVValue *> Ops(1, transValue(II->getArgOperand(0), BB));
return BM->addExtInst(Ty, BM->getExtInstSetId(SPIRVEIS_OpenCL), ExtOp, Ops,
BB);
}
When these ExtInst are reverse translated with (llvm-spirv -r) they are converted to calls:
%0 = call spir_func i32 @_Z3clzi(i32 %x) #0
Implementation of the spir_func is in an OpenCL library.
If reverse translation is done with (llvm-spirv -r --spirv-target-env=SPV-IR) the calls are converted to
SPIRV Friendly IR:
%0 = call spir_func i32 @_Z15__spirv_ocl_clzi(i32 %x)
This is the cleanest method of lowering. If a LLVM intrinsic naturally maps to a SPIRV instruction, and if there is an
external library that supports the instructions this way should be chosen.
-----------------------------------------------------------------------------------------------------------------------------------
Varation B:
Sometimes an intrinsic can be translated to an instruction that is only available with an extension. For example translating:
%ret = call i8 @llvm.bitreverse.i8(i8 %a)
when llvm-spirv is invoked with:
llvm-spirv --spirv-ext=+SPV_KHR_bit_instructions
is translated into SPIRV:
4 BitReverse 51 66 58
The code in transIntrinsicInst to do this translation is:
case Intrinsic::bitreverse: {
if (!BM->getErrorLog().checkError(
BM->isAllowedToUseExtension(ExtensionID::SPV_KHR_bit_instructions),
SPIRVEC_InvalidFunctionCall, II,
"Translation of llvm.bitreverse intrinsic requires "
"SPV_KHR_bit_instructions extension.")) {
return nullptr;
}
SPIRVType *Ty = transType(II->getType());
SPIRVValue *Op = transValue(II->getArgOperand(0), BB);
return BM->addUnaryInst(OpBitReverse, Ty, Op, BB);
}
Method 2:
Some intrinsics are emulated by basic operations in SPIRVWriter. For example:
%0 = call float @llvm.vector.reduce.fadd.v4float(float %sp, <4 x float> %v)
is emulated in SPIRV with:
5 VectorExtractDynamic 2 11 7 10
5 VectorExtractDynamic 2 13 7 12
5 VectorExtractDynamic 2 15 7 14
5 VectorExtractDynamic 2 17 7 16
5 FAdd 2 18 6 11
5 FAdd 2 19 18 13
5 FAdd 2 20 19 15
5 FAdd 2 21 20
The code in transIntrinsicInst to do this emulation is:
case Intrinsic::vector_reduce_add: {
Op Op;
if (IID == Intrinsic::vector_reduce_add) {
Op = OpIAdd;
}
VectorType *VecTy = cast<VectorType>(II->getArgOperand(0)->getType());
SPIRVValue *VecSVal = transValue(II->getArgOperand(0), BB);
SPIRVTypeInt *ResultSType =
BM->addIntegerType(VecTy->getElementType()->getIntegerBitWidth());
SPIRVTypeInt *I32STy = BM->addIntegerType(32);
unsigned VecSize = VecTy->getElementCount().getFixedValue();
SmallVector<SPIRVValue *, 16> Extracts(VecSize);
for (unsigned Idx = 0; Idx < VecSize; ++Idx) {
Extracts[Idx] = BM->addVectorExtractDynamicInst(
VecSVal, BM->addIntegerConstant(I32STy, Idx), BB);
}
unsigned Counter = VecSize >> 1;
while (Counter != 0) {
for (unsigned Idx = 0; Idx < Counter; ++Idx) {
Extracts[Idx] = BM->addBinaryInst(Op, ResultSType, Extracts[Idx << 1],
Extracts[(Idx << 1) + 1], BB);
}
Counter >>= 1;
}
if ((VecSize & 1) != 0) {
Extracts[0] = BM->addBinaryInst(Op, ResultSType, Extracts[0],
Extracts[VecSize - 1], BB);
}
return Extracts[0];
}
Method 3:
In SPIRVRegularizeLLVMPass, calls to LLVM intrinsics are replaced with a call to an emulation function.
The emulation function is created by LLVM API calls and will be translated to SPIRV. The calls to the emulation
functions and the emulation functions themselves will be translated to SPIRV. After reverse translation, the calls to the emulation
functions and the emulation functions themselves will appear in the LLVM IR.
For example, calls to llvm.bswap.i16:
%ret = call i16 @llvm.bswap.i16(i16 %0)
will be re-directed to an emulation function:
%ret = call i16 @spirv.llvm_bswap_i16(i16 %0)
The emulation function is constructed by the translator in SPIRVRegularizeLLVM (note that this code
handles all types):
case Intrinsic::bswap: {
BasicBlock *EntryBB = BasicBlock::Create(M->getContext(), "entry", F);
IRBuilder<> IRB(EntryBB);
auto *BSwap = IRB.CreateIntrinsic(Intrinsic::bswap, Intrinsic->getType(),
F->getArg(0));
IRB.CreateRet(BSwap);
IntrinsicLowering IL(M->getDataLayout());
IL.LowerIntrinsicCall(BSwap);
break;
}
This will produce a function like:
define i16 @spirv.llvm_bswap_i16(i16 %0) {
entry:
%bswap.2 = shl i16 %0, 8
%bswap.1 = lshr i16 %0, 8
%bswap.i16 = or i16 %bswap.2, %bswap.1
ret i16 %bswap.i16
}
After forward translation the emulation calls and functions will appear in SPIRV:
8 Name 24 "spirv.llvm_bswap_i16"
...
5 FunctionCall 8 26 24 22
...
5 Function 8 24 0 23
3 FunctionParameter 8 25
2 Label 42
5 ShiftLeftLogical 8 44 25 43
5 ShiftRightLogical 8 45 25 43
5 BitwiseOr 8 46 44 45
2 ReturnValue 46
In reverse translation, the lowering is undone. Calls are reverted to the original llvm.bswap.i16 intrinsic
%ret = call i16 @llvm.bswap.i16(i16 %0)
The emulation functions are deleted.
The functionality of the intrinsic is created by a call to LLVM's CreateIntrinsic, so the complexity within the
translator is small. However, this is effectively using the translator to insert a library function at translation
time.
Method 4:
In SPIRVLowerLLVMIntrinsicPass, calls to LLVM intrinsics are replaced with a call to an emulation function.
The emulation function is represented as a text string of LLVM assembly and is parsed and added to the LLVM IR
to be translated. The calls to the emulation functions and the emulation functions themselves will be translated
to SPIRV. After reverse translation, the calls to the emulation functions and the emulation functions themselves will appear
in the LLVM IR.
For example if SPV_KHR_bit_instructions is not enabled then bit instructions are not supported and llvm.bitreverse.i8
will be emulated (Note that this is the same intrinsic example used in section 1.B). Calls to it:
%ret = call i8 @llvm.bitreverse.i8(i8 %a)
will be re-directed to an emulation function:
%ret = call i8 @llvm_bitreverse_i8(i8 %a)
The emulation function is built into the translator. The source is recorded as a string in LLVMBitreverse.h (note that a separate
emulation function is needed for each type):
static const char LLVMBitreversei8[]{R"(
define zeroext i8 @llvm_bitreverse_i8(i8 %A) {
entry:
%and = shl i8 %A, 4
%shr = lshr i8 %A, 4
%or = or disjoint i8 %and, %shr
%and5 = shl i8 %or, 2
%shl6 = and i8 %and5, -52
%shr8 = lshr i8 %or, 2
%and9 = and i8 %shr8, 51
%or10 = or disjoint i8 %shl6, %and9
%and13 = shl i8 %or10, 1
%shl14 = and i8 %and13, -86
%shr16 = lshr i8 %or10, 1
%and17 = and i8 %shr16, 85
%or18 = or disjoint i8 %shl14, %and17
ret i8 %or18
}
)"};
The supported lowerings are recorded in a table in SPIRVLowerLLVMIntrinsic:
// LLVM Intrinsic Name Required Extension Forbidden Extension Module with
// emulation function
...
{ "llvm.bitreverse.i8", {NO_REQUIRED_EXTENSION, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversei8}},
...
This is the most flexible way of lowering, but it requires a lot of work to create emulation functions for all the necesary types.
The functionality of a call is provided by LLVM IR supplied as text. The complexity of the intrinsic functionality is inside the translator.
Each function signature variation for an intrinsic that needs to be lowered must be supplied by the developer. As with #2
this method is effectively using the translator to insert a library function at translation time.
@@ -0,0 +1,579 @@
================================
SPIR-V representation in LLVM IR
================================
.. contents::
:local:
Overview
========
As one of the goals of SPIR-V is to `"map easily to other IRs, including LLVM
IR" <https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#_goals>`_,
most of SPIR-V entities (global variables, constants, types, functions, basic
blocks, instructions) have straightforward counterparts in LLVM. Therefore the
focus of this document is those entities in SPIR-V which do not map to LLVM in
an obvious way. These include:
* SPIR-V types mapped to LLVM types
* SPIR-V instructions mapped to LLVM function calls
* SPIR-V extended instructions mapped to LLVM function calls
* SPIR-V builtin variables mapped to LLVM function calls or LLVM global variables
* SPIR-V instructions mapped to LLVM metadata
* SPIR-V types mapped to LLVM opaque types
* SPIR-V decorations mapped to LLVM metadata or named attributes
* Additional requirements for LLVM module
SPIR-V Types Mapped to LLVM Types
=================================
Limited to this section, we define the following common postfix.
* {Access} - Postifix indicating the access qualifier.
{Access} take integer literal values which are defined by the SPIR-V spec.
OpTypeImage
-----------
OpTypeImage is mapped to LLVM opaque type
spirv.Image._{SampledType}_{Dim}_{Depth}_{Arrayed}_{MS}_{Sampled}_{Format}_{Access}
and mangled as __spirv_Image__{SampledType}_{Dim}_{Depth}_{Arrayed}_{MS}_{Sampled}_{Format}_{Access},
where
* {SampledType}={float|half|int|uint|void} - Postfix indicating the sampled data type
- void for unknown sampled data type
* {Dim} - Postfix indicating the dimension of the image
* {Depth} - Postfix indicating whether the image is a depth image
* {Arrayed} - Postfix indicating whether the image is arrayed image
* {MS} - Postfix indicating whether the image is multi-sampled
* {Sampled} - Postfix indicating whether the image is associated with sampler
* {Format} - Postfix indicating the image format
Postfixes {Dim}, {Depth}, {Arrayed}, {MS}, {Sampled} and {Format} take integer
literal values which are defined by the SPIR-V spec.
OpTypeSampledImage
------------------
OpTypeSampledImage is mapped to LLVM opaque type
spirv.SampledImage._{Postfixes} and mangled as __spirv_SampledImage__{Postfixes},
where {Postfixes} are the same as the postfixes of the original image type, as
defined above in this section.
OpTypePipe
----------
OpTypePipe is mapped to LLVM opaque type
spirv.Pipe._{Access} and mangled as __spirv_Pipe__{Access}.
Other SPIR-V Types
------------------
* OpTypeEvent
* OpTypeDeviceEvent
* OpTypeReserveId
* OpTypeQueue
* OpTypeSampler
* OpTypePipeStorage (SPIR-V 1.1)
The above SPIR-V types are mapped to LLVM opaque type spirv.{TypeName} and
mangled as __spirv_{TypeName}, where {TypeName} is the name of the SPIR-V
type with "OpType" removed, e.g., OpTypeEvent is mapped to spirv.Event and
mangled as __spirv_Event.
Address spaces
--------------
The following
`SPIR-V storage classes <https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#Storage_Class>`_
are naturally represented as LLVM IR address spaces with the following mapping:
==================== ====================================
SPIR-V storage class LLVM IR address space
==================== ====================================
``Function`` No address space or ``addrspace(0)``
``CrossWorkgroup`` ``addrspace(1)``
``UniformConstant`` ``addrspace(2)``
``Workgroup`` ``addrspace(3)``
``Generic`` ``addrspace(4)``
==================== ====================================
SPIR-V extensions are allowed to add new storage classes. For example,
SPV_INTEL_usm_storage_classes extension adds ``DeviceOnlyINTEL`` and
``HostOnlyINTEL`` storage classes which are mapped to ``addrspace(5)`` and
``addrspace(6)`` respectively.
SPIR-V Instructions Mapped to LLVM Function Calls
=================================================
Some SPIR-V instructions which can be included in basic blocks do not have
corresponding LLVM instructions or intrinsics. These SPIR-V instructions are
represented by function calls in LLVM. The function corresponding to a SPIR-V
instruction is termed SPIR-V builtin function and its name is `IA64 mangled
<https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling>`_ with extensions
for SPIR-V specific types. The unmangled name of a SPIR-V builtin function
follows the convention
.. code-block:: c
__spirv_{OpCodeName}{_OptionalPostfixes}
where {OpCodeName} is the op code name of the SPIR-V instructions without the
"Op" prefix, e.g. EnqueueKernel. {OptionalPostfixes} are optional postfixes to
specify decorations for the SPIR-V instruction. The SPIR-V op code name and
each postfix does not contain "_".
SPIR-V builtin functions accepts all argument types accepted by the
corresponding SPIR-V instructions. The literal operands of extended
instruction are mapped to function call arguments with type i32.
Optional Postfixes for SPIR-V Builtin Function Names
----------------------------------------------------
SPIR-V builtin functions corresponding to the following SPIR-V instructions are
postfixed following the order specified as below:
* Instructions having identical argument types but different return types are postfixed with "_R{ReturnType}" where
- {ReturnType} = {ScalarType}|{VectorType}
- {ScalarType} = char|uchar|short|ushort|int|uint|long|ulong|half|float|double|bool
- {VectorType} = {ScalarType}{2|3|4|8|16}
* Instructions with saturation decoration are postfixed with "_sat"
* Instructions with floating point rounding mode decoration are postfixed with "_rtp|_rtn|_rtz|_rte"
SPIR-V Builtin Conversion Function Names
----------------------------------------
The unmangled names of SPIR-V builtin conversion functions follow the convention:
.. code-block:: c
__spirv_{ConversionOpCodeName}_R{ReturnType}{_sat}{_rtp|_rtn|_rtz|_rte}
where
* {ConversionOpCodeName} = ConvertFToU|ConvertFToS|ConvertUToF|ConvertUToS|UConvert|SConvert|FConvert|SatConvertSToU|SatConvertUToS
SPIR-V Builtin Reinterpret / Bitcast Function Names
---------------------------------------------------
The unmangled names of SPIR-V builtin reinterpret / bitcast functions follow the convention:
.. code-block:: c
__spirv_{BitcastOpCodeName}_R{ReturnType}
SPIR-V Builtin ImageSample Function Names
----------------------------------------
The unmangled names of SPIR-V builtin ImageSample functions follow the convention:
.. code-block:: c
__spirv_{ImageSampleOpCodeName}_R{ReturnType}
SPIR-V Builtin GenericCastToPtr Function Name
----------------------------------------
The unmangled names of SPIR-V builtin GenericCastToPtrExplicit function follow the convention:
.. code-block:: c
__spirv_GenericCastToPtrExplicit_To{Global|Local|Private}
SPIR-V Builtin BuildNDRange Function Name
----------------------------------------
The unmangled names of SPIR-V builtin BuildNDRange functions follow the convention:
.. code-block:: c
__spirv_{BuildNDRange}_{1|2|3}D
SPIR-V 1.1 Builtin CreatePipeFromPipeStorage Function Name
----------------------------------------------------------
The unmangled names of SPIR-V builtin CreatePipeFromPipeStorage function follow the convention:
.. code-block:: c
__spirv_CreatePipeFromPipeStorage_{read|write}
SPIR-V Extended Instructions Mapped to LLVM Function Calls
==========================================================
SPIR-V extended instructions are mapped to LLVM function calls. The function
name is IA64 mangled and the unmangled name has the format
.. code-block:: c
__spirv_{ExtendedInstructionSetName}_{ExtendedInstrutionName}{__OptionalPostfixes}
where {ExtendedInstructionSetName} for OpenCL is "ocl".
The translated functions accepts all argument types accepted by the
corresponding SPIR-V instructions. The literal operands of extended
instruction are mapped to function call arguments with type i32.
The optional postfixes take the same format as SPIR-V builtin functions. The first postfix
starts with two underscores to facilitate identification since extended instruction name
may contain underscore. The remaining postfixes start with one underscore.
OpenCL Extended Builtin Vector Load Function Names
--------------------------------------------------
The unmangled names of OpenCL extended vector load functions follow the convention:
.. code-block:: c
__spirv_ocl_{VectorLoadOpCodeName}__R{ReturnType}
where
* {VectorLoadOpCodeName} = vloadn|vload_half|vload_halfn|vloada_halfn
SPIR-V Builtin Variables Mapped to LLVM Function Calls or LLVM Global Variables
===============================================================================
By default each access of SPIR-V builtin variable's value is mapped to LLVM
function call. The unmangled names of these functions follow the convention:
.. code-block:: c
__spirv_BuiltIn{VariableName}
In case if SPIR-V builtin variable has vector type, the corresponding
LLVM function will have an integer argument, so each access of the variable's
scalar component is mapped to a function call with index argument, i.e.:
.. code-block:: llvm
; For scalar variables
; SPIR-V
OpDecorate %__spirv_BuiltInGlobalInvocationId BuiltIn GlobalInvocationId
%13 = OpLoad %uint %__spirv_BuiltInGlobalLinearId Aligned 4
; Will be transformed into the following LLVM IR:
%0 = call spir_func i32 @_Z29__spirv_BuiltInGlobalLinearIdv()
; For vector variables
; SPIRV
OpDecorate %__spirv_BuiltInGlobalInvocationId BuiltIn GlobalInvocationId
%14 = OpLoad %v3ulong %__spirv_BuiltInGlobalInvocationId Aligned 32
%15 = OpCompositeExtract %ulong %14 1
; Can be transformed into the following LLVM IR:
%0 = call spir_func i64 @_Z33__spirv_BuiltInGlobalInvocationIdi(i32 1)
; However SPIRV-LLVM translator will transform it to the following pattern:
%1 = call spir_func i64 @_Z33__spirv_BuiltInGlobalInvocationIdi(i32 0)
%2 = insertelement <3 x i64> poison, i64 %1, i32 0
%3 = call spir_func i64 @_Z33__spirv_BuiltInGlobalInvocationIdi(i32 1)
%4 = insertelement <3 x i64> %2, i64 %3, i32 1
%5 = call spir_func i64 @_Z33__spirv_BuiltInGlobalInvocationIdi(i32 2)
%6 = insertelement <3 x i64> %4, i64 %5, i32 2
%7 = extractelement <3 x i64> %6, i32 1
; In case some actions are performed with the variable's value in vector form.
SPIR-V builtin variables can also be mapped to LLVM global variables with
unmangled name __spirv_BuiltIn{Name}.
The representation with variables is closer to SPIR-V, so it is easier to
translate from SPIR-V to LLVM and back using it.
Hovewer in languages like OpenCL the functionality covered by SPIR-V builtin
variables is usually represented by builtin functions, so it is easier to
translate from/to SPIR-V friendly IR to/from LLVM IR produced from OpenCL-like
source languages. That is why both forms of mapping are supported.
SPIR-V instructions mapped to LLVM metadata
===========================================
SPIR-V specification allows multiple module scope instructions, whereas LLVM
named metadata must be unique, so encoding of such instructions has the
following format:
.. code-block:: llvm
!spirv.<OpCodeName> = !{!<InstructionMetadata1>, !<InstructionMetadata2>, ..}
!<InstructionMetadata1> = !{<Operand1>, <Operand2>, ..}
!<InstructionMetadata2> = !{<Operand1>, <Operand2>, ..}
+--------------------+---------------------------------------------------------+
| SPIR-V instruction | LLVM IR |
+====================+=========================================================+
| OpSource | .. code-block:: llvm |
| | |
| | !spirv.Source = !{!0} |
| | !0 = !{i32 3, i32 66048, !1} |
| | ; 3 - OpenCL_C |
| | ; 66048 = 0x10200 - OpenCL version 1.2 |
| | ; !1 - optional file id. |
| | !1 = !{!"/tmp/opencl/program.cl"} |
+--------------------+---------------------------------------------------------+
| OpSourceExtension | .. code-block:: llvm |
| | |
| | !spirv.SourceExtension = !{!0, !1} |
| | !0 = !{!"cl_khr_fp16"} |
| | !1 = !{!"cl_khr_gl_sharing"} |
+--------------------+---------------------------------------------------------+
| OpExtension | .. code-block:: llvm |
| | |
| | !spirv.Extension = !{!0} |
| | !0 = !{!"SPV_KHR_expect_assume"} |
+--------------------+---------------------------------------------------------+
| OpCapability | .. code-block:: llvm |
| | |
| | !spirv.Capability = !{!0} |
| | !0 = !{i32 10} ; Float64 - program uses doubles |
+--------------------+---------------------------------------------------------+
| OpExecutionMode | .. code-block:: llvm |
| | |
| | !spirv.ExecutionMode = !{!0} |
| | !0 = !{void ()* @worker, i32 30, i32 262149} |
| | ; Set execution mode with id 30 (VecTypeHint) and |
| | ; literal `262149` operand. |
+--------------------+---------------------------------------------------------+
| Generator's magic | .. code-block:: llvm |
| number - word # 2 | |
| in SPIR-V module | !spirv.Generator = !{!0} |
| | !0 = !{i16 6, i16 123} |
| | ; 6 - Generator Id, 123 - Generator Version |
+--------------------+---------------------------------------------------------+
For example:
.. code-block:: llvm
!spirv.Source = !{!0}
!spirv.SourceExtension = !{!2, !3}
!spirv.Extension = !{!2}
!spirv.Capability = !{!4}
!spirv.MemoryModel = !{!5}
!spirv.EntryPoint = !{!6 ,!7}
!spirv.ExecutionMode = !{!8, !9}
!spirv.Generator = !{!10 }
; 3 - OpenCL_C, 102000 - OpenCL version 1.2, !1 - optional file id.
!0 = !{i32 3, i32 102000, !1}
!1 = !{!"/tmp/opencl/program.cl"}
!2 = !{!"cl_khr_fp16"}
!3 = !{!"cl_khr_gl_sharing"}
!4 = !{i32 10} ; Float64 - program uses doubles
!5 = !{i32 1, i32 2} ; 1 - 32-bit addressing model, 2 - OpenCL memory model
!6 = !{i32 6, TBD, !"kernel1", TBD}
!7 = !{i32 6, TBD, !"kernel2", TBD}
!8 = !{!6, i32 18, i32 16, i32 1, i32 1} ; local size hint <16, 1, 1> for 'kernel1'
!9 = !{!7, i32 32} ; independent forward progress is required for 'kernel2'
!10 = !{i16 6, i16 123} ; 6 - Generator Id, 123 - Generator Version
Additional requirements for LLVM module
=======================================
Target triple and datalayout string
-----------------------------------
Target triple architecture must be ``spir`` (32-bit architecture) or ``spir64``
(64-bit architecture) and ``datalayout`` string must be aligned with OpenCL
environment specification requirements for data type sizes and alignments (e.g.
3-element vector must have 4-element vector alignment). For example:
.. code-block:: llvm
target datalayout = "e-p:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024"
target triple = "spir-unknown-unknown"
Target triple architecture is translated to
`addressing model operand <https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#_a_id_addressing_model_a_addressing_model>`_
of
`OpMemoryModel <https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#_a_id_mode_setting_a_mode_setting_instructions>`_
SPIR-V instruction.
- ``spir`` -> Physical32
- ``spir64`` -> Physical64
Calling convention
------------------
``OpEntryPoint`` information is represented in LLVM IR in calling convention.
A function with ``spir_kernel`` calling convention will be translated as an entry
point of the SPIR-V module.
Global variables
----------------
A global variable resides in an address space, and the default address space
in LLVM is zero. The SPIR-V storage class represented by the zero LLVM IR
address spaces is Function. However, SPIR-V global variable declarations are
``OpVariable`` instructions whose Storage Class cannot be ``Function``. This
means that global variable declarations must always have an address space
specified and that address space cannot be ``0``.
Function metadata
-----------------
Some kernel parameter information is stored in LLVM IR as a function metadata.
For example:
.. code-block:: llvm
!kernel_arg_addr_space !1
!kernel_arg_access_qual !2
!kernel_arg_type !3
!kernel_arg_base_type !4
!kernel_arg_type_qual !5
**NOTE**: All metadata from the example above are optional. Access qualifiers
are translated for image types, but they should be encoded in LLVM IR type name
rather than function metadata.
Function parameter, instruction and global variable decoration through metadata
-------------------------------------------------------------------------------
Function parameters, instructions and global variables can be decorated using LLVM
metadata through the metadata names ``spirv.ParameterDecorations`` and
``spirv.Decorations`` respectively. ``spirv.ParameterDecorations`` must be tied
to the kernel function while ``spirv.Decorations`` is tied directly to the
instruction or global variable.
A "decoration-node" is a metadata node consisting of one or more operands. The
first operand is an integer literal representing the SPIR-V decoration
identifier. The other operands are either an integer or string literal
representing the remaining extra operands of the corresponding SPIR-V
decoration.
A "decoration-list" is a metadata node consisting of references to zero or more
decoration-nodes.
``spirv.Decorations`` must refer to a decoration-list while
``spirv.ParameterDecorations`` must refer to a metadata node that contains N
references to decoration-lists, where N is the number of arguments of the
function the metadata is tied to.
``spirv.Decorations`` applied on a global variable example:
.. code-block:: llvm
@v = global i32 0, !spirv.Decorations !1
...
!1 = !{!2, !3} ; decoration-list with two decoration nodes
!2 = !{i32 22} ; decoration-node with no extra operands
!3 = !{i32 41, !"v", i32 0} ; decoration-node with 2 extra operands
decorates a global variable ``v`` with ``Constant`` and ``LinkageAttributes``
with extra operands ``"v"`` and ``Export`` in SPIR-V.
``spirv.Decorations`` applied on an instruction example:
.. code-block:: llvm
%idx = getelementptr inbounds i32, ptr addrspace(1) %b, i64 1, !spirv.Decorations !1
...
!1 = !{!2}
!2 = !{i32 6442, i32 1, i32 2} ; {CacheControlLoadINTEL, CacheLevel=1, Cached}
decorates getelementptr instruction with CacheControlLoadINTEL decoration with
extra operands ``i32 1`` and ``i32 2``.
``spirv.ParameterDecorations`` example:
.. code-block:: llvm
define spir_kernel void @k(float %a, float %b) #0 !spirv.ParameterDecorations !1
...
!1 = !{!2, !3} ; metadata node with 2 decoration-lists
!2 = !{} ; empty decoration-list
!3 = !{!4} ; decoration-list with one decoration node
!4 = !{i32 19} ; decoration-node with no extra operands
decorates the argument ``b`` of ``k`` with ``Restrict`` in SPIR-V while not
adding any decoration to argument ``a``.
Loop controls and loop metadata
-------------------------------
SPIR-V Loop controls that do not have corresponding `llvm.loop` metadata
can be decorated using LLVM metadata through the names
``spirv.loop.<LoopControlID>`` where ``LoopControlID`` corresponds to the name
of the SPIR-V loop control. This metadata should be applied to the latch-block's
branch instruction.
An example with the ``DependencyAccessesINTEL`` loop control:
.. code-block:: llvm
br i1 %cond, label %loop, label %exit !spirv.loop.dependency_accesses !4
...
!1 = distinct !{} ; metadata corresponding to distinct access group
!2 = distinct !{} ; metadata corresponding to distinct access group
!3 = distinct !{} ; metadata corresponding to distinct access group
!4 = !{!0, !1, !2, 0} ; metadata node grouping access groups for the corresponding DepenencyAccessesINTEL instance
Member decoration through pointer annotations
---------------------------------------------
Class members can be decorated using the ``llvm.ptr.annotation`` LLVM IR
intrinsic. Member decorations specified in ``llvm.ptr.annotation`` must be in
the second argument and must have the format ``{X}`` or ``{X:Y}`` where ``X`` is
either one of the reserved names or an integer literal representing the SPIR-V
decoration identifier and ``Y`` is 1 or more arguments separated by ",", where
each argument must be either a word (including numbers) or a string enclosed by
quotation marks. The ``llvm.ptr.annotation`` can contain any number decorations
following this format.
For example, both ``{5835:1,2,3}`` and ``{bank_bits:1,2,3}`` will result in the
``BankwidthINTEL`` decoration with literals 1, 2, and 3 attached to the
annotated member.
The translator accepts a number of reserved names that correspond to SPIR-V
member decorations.
+-----------------------+------------------+-----------------------------------+
| Decoration | Reserved Name | Note |
+=======================+==================+===================================+
| RegisterINTEL | register | Additional arguments are ignored, |
| | | but reverse translation will add |
| | | a 1 argument, i.e. |
| | | ``{register:1}``. |
+-----------------------+------------------+-----------------------------------+
| MemoryINTEL | memory | |
+-----------------------+------------------+-----------------------------------+
| NumbanksINTEL | numbanks | |
+-----------------------+------------------+-----------------------------------+
| BankwidthINTEL | bankwidth | |
+-----------------------+------------------+-----------------------------------+
| MaxPrivateCopiesINTEL | private_copies | |
+-----------------------+------------------+-----------------------------------+
| SinglepumpINTEL | pump | Reserved name is shared with |
| | | DoublepumpINTEL. SinglepumpINTEL |
| | | will be selected if the argument |
| | | is 2, i.e ``{pump:1}``. |
+-----------------------+------------------+-----------------------------------+
| DoublepumpINTEL | pump | Reserved name is shared with |
| | | SinglepumpINTEL. DoublepumpINTEL |
| | | will be selected if the argument |
| | | is 2, i.e ``{pump:2}``. |
+-----------------------+------------------+-----------------------------------+
| MaxReplicatesINTEL | max_replicates | |
+-----------------------+------------------+-----------------------------------+
| SimpleDualPortINTEL | simple_dual_port | Additional arguments are ignored, |
| | | but reverse translation will add |
| | | a 1 argument, i.e. |
| | | ``{simple_dual_port:1}``. |
+-----------------------+------------------+-----------------------------------+
| MergeINTEL | merge | Arguments of this are separated by|
| | | ":" rather than ",", i.e. |
| | | ``{merge:X:Y}``. |
+-----------------------+------------------+-----------------------------------+
| BankBitsINTEL | bank_bits | |
+-----------------------+------------------+-----------------------------------+
| ForcePow2DepthINTEL | force_pow2_depth | |
+-----------------------+------------------+-----------------------------------+
None of the special requirements imposed from using the reserved names apply to
using decoration identifiers directly.
During reverse translation, the translator prioritizes reserved names over
decoration identifiers, even if the member decoration was generated using the
corresponding decoration identifier. For example, this means that translating
``{5825}`` to SPIR-V and back to LLVM IR will result in ``{register:1}`` being
in the annotation string argument instead of the initial value.
Debug information extension
===========================
**TBD**
@@ -0,0 +1,244 @@
=======================================
SPIR-V versions and extensions handling
=======================================
.. contents::
:local:
Overview
========
This document describes how the translator makes decisions about using
instructions from different version of the SPIR-V core and extension
specifications.
Being able to control the resulting SPIR-V version is important: the target
consumer might be quite old, without support for new SPIR-V versions and there
must be the possibility to control which version of the SPIR-V specification
that will be used during translation.
SPIR-V extensions is another thing which must be controllable. Extensions
can update and re-define semantics and validation rules for existing SPIR-V
entries and it is important to ensure that the translator is able to generate
valid SPIR-V according to the core spec, without uses of any extensions if such
SPIR-V was requested by user.
For example, without such infrastructure it is impossible to disable use of
``SPV_KHR_no_integer_wrap_decoration`` - it will be always generated if
corresponding LLVM IR counterparts are encountered in input module.
It is worth mentioning that SPIR-V versions and extensions the handling of
SPIR-V versions and extension is mostly important for the SPIR-V generation
step. On the consumer side it is the responsibility of the consumer to analyze
the incoming SPIR-V file and reject it if it contains something that is not
supported by the consumer.
However, translator simplifies this step for downstream users by checking
version and extensions in SPIR-V module during ``readSpirv``/``readSpirvModule``
phases.
SPIR-V Versions
===============
SPIR-V Generation step
----------------------
By default translator selects version of generated SPIR-V file based on features
used in this file. For example, if it contains the ``dereferenceable`` LLVM IR
attribute, ``MaxByteOffset`` decoration will be generated and resulting SPIR-V
version will be raised to 1.1.
.. note::
There is no documentation about which exact features from newest
SPIR-V spec versions will be used by the translator. If you are interested
when or why a particular SPIR-V instruction is generated, please check this
in the source code. Consider this as an implementation detail and if you
disagree with something, you can always open an issue or submit pull request
- contributions are welcome!
There is one option to control the behavior of the translator with respect to
the version of the SPIR-V file which is being generated/consumed.
* ``--spirv-max-version=`` - instructs the translator to generate SPIR-V file
corresponding to any spec version which is less than or equal to the
specified one. Behavior of the translator is the same as by default with only
one exception: resulting SPIR-V version cannot be raised higher than
specified by this option.
Allowed values are ``1.0``, ``1.1``, ``1.2``, ``1.3``, ``1.4``, ``1.5`` and
``1.6``.
.. warning::
These two options are mutually exclusive and cannot be specified at the
same time.
If the translator encounters something that cannot be represented by set of
allowed SPIR-V versions (which might contain only one version), it does one of
the following things:
* ignores LLVM IR entity in the input file.
For example, the ``dereferenceable`` LLVM IR attribute can be ignored if it
is not allowed to generate SPIR-V 1.1 and higher.
* tries to represent LLVM IR entity with allowed instructions.
For example, ``OpPtrEqual`` can be used if SPIR-V 1.4 is not allowed and can
be emulated via ``OpConvertPtrToU`` + ``OpIEqual`` sequence.
* emits error if LLVM IR entity cannot be ignored and cannot be emulated using
available instructions.
For example, if global constructors/destructors
(represented by @llvm.global_ctors/@llvm.global_dtors) are present in a module
then the translator should emit error if it cannot use SPIR-V 1.1 and higher
where ``Initializer`` and ``Finalizer`` execution modes are described.
SPIR-V Consumption step
-----------------------
By default, translator consumes SPIR-V of any version which is supported.
This behavior, however, can be controlled via the same switches described in
the previous section.
If one of the switches present and translator encountered SPIR-V file
corresponding to a spec version which is not included into set of allowed
SPIR-V versions, translator emits error.
SPIR-V Extensions
=================
SPIR-V Generation step
----------------------
By default, translator doesn't use any extensions. If it required to enable
certain extension, the following command line option can be used:
* ``--spirv-ext=`` - allows to control list of allowed/disallowed extensions.
Valid value for this option is comma-separated list of extension names prefixed
with ``+`` or ``-`` - plus means allow to use extension, minus means disallow
to use extension. There is one more special value which can be used as extension
name in this option: ``all`` - it affects all extension which are known to the
translator.
If ``--spirv-ext`` contains name of extension which is not know for the
translator, it will emit error.
Examples:
* ``--spirv-ext=+SPV_KHR_no_integer_wrap_decoration,+SPV_INTEL_subgroups``
* ``--spirv-ext=+all,-SPV_INTEL_fpga_loop_controls``
.. warning::
Extension name cannot be allowed and disallowed at the same time: for inputs
like ``--spirv-ext=+SPV_INTEL_subgroups,-SPV_INTEL_subgroups`` translator
will emit error about invalid arguments.
.. note::
Since by default during SPIR-V generation all extensions are disabled, this
means that ``-all,`` is implicitly added at the beggining of the
``-spirv-ext`` value.
If the translator encounters something that cannot be represented by set of
allowed SPIR-V extensions (which might be empty), it does one of the following
things:
* ignores LLVM IR entity in the input file.
For example, ``nsw``/``nuw`` LLVM IR attributes can be ignored if it is not
allowed to generate SPIR-V 1.4 and ``SPV_KHR_no_integer_wrap_decoration``
extension is disallowed.
* tries to represent LLVM IR entity with allowed instructions.
Translator could translate calls to a new built-in functions defined by some
extensions as usual call instructions without using special SPIR-V
instructions.
However, this could result in a strange SPIR-V and most likely will lead to
errors during consumption. Having that, translator should emit errors if it
encounters a call to a built-in function from an extension which must be
represented as a special SPIR-V instruction from extension which wasn't
allowed to be used. I.e. if translator knows that this certain LLVM IR entity
belongs to an extension functionality and this extension is disallowed, it
should emit error rather than emulating it.
* emits error if LLVM IR entity cannot be ignored and cannot be emulated using
available instructions.
For example, new built-in types defined by
``cl_intel_device_side_avc_motion_estimation`` cannot be represented in SPIR-V
if ``SPV_INTEL_device_side_avc_motion_estimation`` is disallowed.
SPIR-V Consumption step
-----------------------
By default, translator consumes SPIR-V regardless of list extensions which are
used by the input file, i.e. all extensions are allowed by default during
consumption step.
.. note::
This is opposite to the generation step and this is done on purpose: to not
broke workflows of existing users of the translator.
.. note::
Since by default during SPIR-V consumption all extensions are enabled, this
means that ``+all,`` is implicitly added at the beggining of the
``-spirv-ext`` value.
This behavior, however, can be controlled via the same switches described in
the previous section.
If ``--spirv-ext`` switch presents, translator will emit error if it finds out
that input SPIR-V file uses disallowed extension.
.. note::
If the translator encounters unknown extension in the input SPIR-V file, it
will emit error regardless of ``-spirv-ext`` option value.
If one of the switches present and translator encountered SPIR-V file
corresponding to a spec version which is not included into set of allowed
SPIR-V versions, translator emits error.
How to control translator behavior when using it as library
===========================================================
When using translator as library it can be controlled via bunch of alternative
APIs that have additional argument: ``TranslatorOpts`` object which
encapsulates information about available SPIR-V versions and extensions.
List of new APIs is: ``readSpirvModule``, ``writeSpirv`` and ``readSpirv``.
.. note::
See ``LLVMSPIRVOpts.h`` for more details.
How to get ``TranslatorOpts`` object
------------------------------------
1. Default constructor. Equal to:
``--spirv-max-version=MaxKnownVersion --spirv-ext=-all``
.. note::
There is method ``TranslatorOpts::enableAllExtensions()`` that allows you
to quickly enable all known extensions if it is needed.
2. Constructor which accepts all parameters
Consumes both max SPIR-V version and optional map with extensions status
(i.e. which one is allowed and which one is disallowed)
Extensions status map
^^^^^^^^^^^^^^^^^^^^^
This map is defined as ``std::map<ExtensionID, bool>`` and it is intended to
show which extension is allowed to be used (``true`` as value) and which is not
(``false`` as value).
.. note::
If certain ``ExtensionID`` value is missed in the map, it automatically means
that extension is not allowed to be used.
This implies that by default, all extensions are disallowed.
@@ -0,0 +1,98 @@
#ifndef EXT
#define EXT(X)
#endif
EXT(SPV_EXT_shader_atomic_float16_add)
EXT(SPV_EXT_shader_atomic_float_add)
EXT(SPV_EXT_shader_atomic_float_min_max)
EXT(SPV_EXT_image_raw10_raw12)
EXT(SPV_KHR_no_integer_wrap_decoration)
EXT(SPV_KHR_float_controls)
EXT(SPV_KHR_linkonce_odr)
EXT(SPV_KHR_expect_assume)
EXT(SPV_KHR_integer_dot_product)
EXT(SPV_KHR_bit_instructions)
EXT(SPV_KHR_uniform_group_instructions)
EXT(SPV_KHR_subgroup_rotate)
EXT(SPV_KHR_non_semantic_info)
EXT(SPV_KHR_shader_clock)
EXT(SPV_KHR_cooperative_matrix)
EXT(SPV_KHR_untyped_pointers)
EXT(SPV_KHR_fma)
EXT(SPV_INTEL_subgroups)
EXT(SPV_INTEL_media_block_io)
EXT(SPV_INTEL_device_side_avc_motion_estimation)
EXT(SPV_INTEL_fpga_loop_controls)
EXT(SPV_INTEL_fpga_memory_attributes)
EXT(SPV_INTEL_fpga_memory_accesses)
EXT(SPV_INTEL_unstructured_loop_controls)
EXT(SPV_INTEL_fpga_reg)
EXT(SPV_INTEL_blocking_pipes)
EXT(SPV_INTEL_function_pointers)
EXT(SPV_INTEL_kernel_attributes)
EXT(SPV_INTEL_io_pipes)
EXT(SPV_INTEL_inline_assembly)
EXT(SPV_INTEL_arbitrary_precision_integers)
EXT(SPV_INTEL_optimization_hints)
EXT(SPV_INTEL_float_controls2)
EXT(SPV_INTEL_vector_compute)
EXT(SPV_INTEL_fast_composite) // TODO: to remove
EXT(SPV_INTEL_usm_storage_classes)
EXT(SPV_INTEL_fpga_buffer_location)
EXT(SPV_INTEL_arbitrary_precision_fixed_point)
EXT(SPV_INTEL_arbitrary_precision_floating_point)
EXT(SPV_INTEL_variable_length_array)
EXT(SPV_INTEL_fp_fast_math_mode)
EXT(SPV_INTEL_fpga_cluster_attributes)
EXT(SPV_INTEL_loop_fuse)
EXT(SPV_INTEL_long_composites)
EXT(SPV_EXT_optnone)
EXT(SPV_INTEL_optnone)
EXT(SPV_INTEL_fpga_dsp_control)
EXT(SPV_INTEL_memory_access_aliasing)
EXT(SPV_INTEL_fpga_invocation_pipelining_attributes)
EXT(SPV_INTEL_token_type)
EXT(SPV_INTEL_debug_module)
EXT(SPV_INTEL_runtime_aligned)
EXT(SPV_EXT_arithmetic_fence)
EXT(SPV_INTEL_arithmetic_fence)
EXT(SPV_INTEL_bfloat16_conversion)
EXT(SPV_INTEL_device_barrier)
EXT(SPV_INTEL_joint_matrix)
EXT(SPV_INTEL_hw_thread_queries)
EXT(SPV_INTEL_global_variable_decorations)
EXT(SPV_INTEL_global_variable_host_access)
EXT(SPV_INTEL_global_variable_fpga_decorations)
EXT(SPV_INTEL_complex_float_mul_div)
EXT(SPV_INTEL_split_barrier)
EXT(SPV_INTEL_masked_gather_scatter)
EXT(SPV_INTEL_tensor_float32_conversion)
EXT(SPV_EXT_relaxed_printf_string_address_space)
EXT(SPV_INTEL_fpga_argument_interfaces)
EXT(SPV_INTEL_fpga_latency_control)
EXT(SPV_INTEL_fp_max_error)
EXT(SPV_INTEL_cache_controls)
EXT(SPV_INTEL_subgroup_buffer_prefetch)
EXT(SPV_INTEL_subgroup_requirements)
EXT(SPV_INTEL_task_sequence)
EXT(SPV_INTEL_maximum_registers)
EXT(SPV_INTEL_bindless_images)
EXT(SPV_INTEL_2d_block_io)
EXT(SPV_INTEL_subgroup_matrix_multiply_accumulate)
EXT(SPV_INTEL_subgroup_matrix_multiply_accumulate_float4)
EXT(SPV_INTEL_subgroup_matrix_multiply_accumulate_float8)
EXT(SPV_INTEL_subgroup_scaled_matrix_multiply_accumulate)
EXT(SPV_KHR_bfloat16)
EXT(SPV_INTEL_bfloat16_arithmetic)
EXT(SPV_INTEL_ternary_bitwise_function)
EXT(SPV_INTEL_int4)
EXT(SPV_INTEL_function_variants)
EXT(SPV_INTEL_16bit_atomics)
EXT(SPV_EXT_float8)
EXT(SPV_INTEL_predicated_io)
EXT(SPV_INTEL_sigmoid)
EXT(SPV_INTEL_float4)
EXT(SPV_EXT_ocp_microscaling_types)
EXT(SPV_INTEL_fp_conversions)
EXT(SPV_INTEL_rounded_divide_sqrt)
EXT(SPV_EXT_long_vector)
@@ -0,0 +1,267 @@
//===- LLVMSPIRVLib.h - Read and write SPIR-V binary ------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
//
// 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 Advanced Micro Devices, Inc., 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.
//
//===----------------------------------------------------------------------===//
/// \file LLVMSPIRVLib.h
///
/// This files declares functions and passes for translating between LLVM and
/// SPIR-V.
///
///
//===----------------------------------------------------------------------===//
#ifndef SPIRV_H
#define SPIRV_H
#include "LLVMSPIRVOpts.h"
#include <cstdint>
#include <iostream>
#include <optional>
#include <string>
#include <vector>
namespace llvm {
// Pass initialization functions need to be declared before inclusion of
// PassSupport.h.
class PassRegistry;
void initializeLLVMToSPIRVLegacyPass(PassRegistry &);
void initializeOCLToSPIRVLegacyPass(PassRegistry &);
void initializeOCLTypeToSPIRVLegacyPass(PassRegistry &);
void initializeSPIRVLowerBoolLegacyPass(PassRegistry &);
void initializeSPIRVLowerConstExprLegacyPass(PassRegistry &);
void initializeSPIRVLowerOCLBlocksLegacyPass(PassRegistry &);
void initializeSPIRVLowerMemmoveLegacyPass(PassRegistry &);
void initializeSPIRVLowerLLVMIntrinsicLegacyPass(PassRegistry &);
void initializeSPIRVRegularizeLLVMLegacyPass(PassRegistry &);
void initializeSPIRVToOCL12LegacyPass(PassRegistry &);
void initializeSPIRVToOCL20LegacyPass(PassRegistry &);
void initializePreprocessMetadataLegacyPass(PassRegistry &);
void initializeSPIRVLowerBitCastToNonStandardTypeLegacyPass(PassRegistry &);
class ModulePass;
class FunctionPass;
} // namespace llvm
#include "llvm/IR/Module.h"
namespace SPIRV {
class SPIRVModule;
/// \brief Check if a string contains SPIR-V binary.
bool isSpirvBinary(const std::string &Img);
#ifdef _SPIRV_SUPPORT_TEXT_FMT
/// \brief Convert SPIR-V between binary and internal textual formats.
/// This function is not thread safe and should not be used in multi-thread
/// applications unless guarded by a critical section.
/// \returns true if succeeds.
bool convertSpirv(std::istream &IS, std::ostream &OS, std::string &ErrMsg,
bool FromText, bool ToText);
/// \brief Convert SPIR-V between binary and internal text formats.
/// This function is not thread safe and should not be used in multi-thread
/// applications unless guarded by a critical section.
bool convertSpirv(std::string &Input, std::string &Out, std::string &ErrMsg,
bool ToText);
/// \brief Check if a string contains SPIR-V in internal text format.
bool isSpirvText(std::string &Img);
#endif
/// \brief Load SPIR-V from istream as a SPIRVModule.
/// \returns null on failure.
std::unique_ptr<SPIRVModule> readSpirvModule(std::istream &IS,
std::string &ErrMsg);
/// \brief Load SPIR-V from istream as a SPIRVModule.
/// \returns null on failure.
std::unique_ptr<SPIRVModule> readSpirvModule(std::istream &IS,
const SPIRV::TranslatorOpts &Opts,
std::string &ErrMsg);
struct SPIRVModuleReport {
SPIRV::VersionNumber Version;
uint32_t MemoryModel;
uint32_t AddrModel;
std::vector<std::string> Extensions;
std::vector<std::string> ExtendedInstructionSets;
std::vector<uint32_t> Capabilities;
};
/// \brief Partially load SPIR-V from the stream and decode only selected
/// instructions that are needed to retrieve general information
/// about the module. If this call fails, readSPIRVModule is
/// expected to fail as well.
/// \returns nullopt on failure.
std::optional<SPIRVModuleReport> getSpirvReport(std::istream &IS);
std::optional<SPIRVModuleReport> getSpirvReport(std::istream &IS, int &ErrCode);
struct SPIRVModuleTextReport {
std::string Version;
std::string MemoryModel;
std::string AddrModel;
std::vector<std::string> Extensions;
std::vector<std::string> ExtendedInstructionSets;
std::vector<std::string> Capabilities;
};
/// \brief Create a human-readable form of the report returned by a call to
/// getSpirvReport by decoding its binary fields.
/// \returns String with the human-readable report.
SPIRVModuleTextReport formatSpirvReport(const SPIRVModuleReport &Report);
/// \brief Returns the message associated with the error code.
/// \returns empty string if no known error code is found.
std::string getErrorMessage(int ErrCode);
} // End namespace SPIRV
namespace llvm {
/// \brief Translate LLVM module to SPIR-V and write to ostream.
/// \returns true if succeeds.
bool writeSpirv(Module *M, std::ostream &OS, std::string &ErrMsg);
/// \brief Load SPIR-V from istream and translate to LLVM module.
/// \returns true if succeeds.
bool readSpirv(LLVMContext &C, std::istream &IS, Module *&M,
std::string &ErrMsg);
/// \brief Translate LLVM module to SPIR-V and write to ostream.
/// \returns true if succeeds.
bool writeSpirv(Module *M, const SPIRV::TranslatorOpts &Opts, std::ostream &OS,
std::string &ErrMsg);
/// \brief Load SPIR-V from istream and translate to LLVM module.
/// \returns true if succeeds.
bool readSpirv(LLVMContext &C, const SPIRV::TranslatorOpts &Opts,
std::istream &IS, Module *&M, std::string &ErrMsg);
/// \brief Partially load SPIR-V from the stream and decode only instructions
/// needed to get information about specialization constants.
/// \returns true if succeeds.
struct SpecConstInfoTy {
uint32_t ID;
uint32_t Size;
std::string Type;
};
bool getSpecConstInfo(std::istream &IS,
std::vector<SpecConstInfoTy> &SpecConstInfo);
/// \brief Convert a SPIRVModule into LLVM IR.
/// \returns null on failure.
std::unique_ptr<Module>
convertSpirvToLLVM(LLVMContext &C, SPIRV::SPIRVModule &BM, std::string &ErrMsg);
/// \brief Convert a SPIRVModule into LLVM IR using specified options
/// \returns null on failure.
std::unique_ptr<Module> convertSpirvToLLVM(LLVMContext &C,
SPIRV::SPIRVModule &BM,
const SPIRV::TranslatorOpts &Opts,
std::string &ErrMsg);
/// \brief Regularize LLVM module by removing entities not representable by
/// SPIRV.
bool regularizeLlvmForSpirv(Module *M, std::string &ErrMsg);
bool regularizeLlvmForSpirv(Module *M, std::string &ErrMsg,
const SPIRV::TranslatorOpts &Opts);
/// \brief Mangle OpenCL builtin function function name.
/// If any type in ArgTypes is a pointer type, it should be represented as a
/// TypedPointerType instead, to faithfully represent the pointer element types
/// for name mangling.
void mangleOpenClBuiltin(const std::string &UnmangledName,
ArrayRef<Type *> ArgTypes, std::string &MangledName);
/// Create a pass for translating LLVM to SPIR-V.
ModulePass *createLLVMToSPIRVLegacy(SPIRV::SPIRVModule *);
/// Create a pass for translating OCL C builtin functions to SPIR-V builtin
/// functions.
ModulePass *createOCLToSPIRVLegacy();
/// Create a pass for adapting OCL types for SPIRV.
ModulePass *createOCLTypeToSPIRVLegacy();
/// Create a pass for lowering cast instructions of i1 type.
ModulePass *createSPIRVLowerBoolLegacy();
/// Create a pass for lowering constant expressions to instructions.
ModulePass *createSPIRVLowerConstExprLegacy();
/// Create a pass for removing function pointers related to OCL 2.0 blocks
ModulePass *createSPIRVLowerOCLBlocksLegacy();
/// Create a pass for lowering llvm.memmove to llvm.memcpys with a temporary
/// variable.
ModulePass *createSPIRVLowerMemmoveLegacy();
/// Create a pass for lowering llvm intrinsics
ModulePass *
createSPIRVLowerLLVMIntrinsicLegacy(const SPIRV::TranslatorOpts &Opts);
/// Create a pass for regularize LLVM module to be translated to SPIR-V.
ModulePass *createSPIRVRegularizeLLVMLegacy();
/// Create a pass for translating SPIR-V Instructions to desired
/// representation in LLVM IR (OpenCL built-ins, SPIR-V Friendly IR, etc.)
ModulePass *createSPIRVBIsLoweringPass(Module &, SPIRV::BIsRepresentation);
/// Create a pass for translating SPIR-V builtin functions to OCL 1.2 builtin
/// functions.
ModulePass *createSPIRVToOCL12Legacy();
/// Create a pass for translating SPIR-V builtin functions to OCL 2.0 builtin
/// functions.
ModulePass *createSPIRVToOCL20Legacy();
/// Create a pass for translating SPIR 1.2/2.0 metadata to SPIR-V friendly
/// metadata.
ModulePass *createPreprocessMetadataLegacy();
/// Create and return a pass that writes the module to the specified
/// ostream.
ModulePass *createSPIRVWriterPass(std::ostream &Str);
/// Create and return a pass that writes the module to the specified
/// ostream.
ModulePass *createSPIRVWriterPass(std::ostream &Str,
const SPIRV::TranslatorOpts &Opts);
/// Create a pass for removing bitcast instructions to non-standard SPIR-V
/// types
FunctionPass *createSPIRVLowerBitCastToNonStandardTypeLegacy(
const SPIRV::TranslatorOpts &Opts);
} // namespace llvm
#endif // SPIRV_H
@@ -0,0 +1,366 @@
//===- LLVMSPIRVOpts.h - Specify options for translation --------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2019 Intel Corporation. All rights reserved.
//
// 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 Advanced Micro Devices, Inc., 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.
//
//===----------------------------------------------------------------------===//
/// \file LLVMSPIRVOpts.h
///
/// This files declares helper classes to handle SPIR-V versions and extensions.
///
//===----------------------------------------------------------------------===//
#ifndef SPIRV_LLVMSPIRVOPTS_H
#define SPIRV_LLVMSPIRVOPTS_H
#include <llvm/ADT/SmallVector.h>
#include <llvm/ADT/StringRef.h>
#include <cassert>
#include <cstdint>
#include <map>
#include <optional>
#include <unordered_map>
#include <vector>
namespace llvm {
class IntrinsicInst;
} // namespace llvm
namespace SPIRV {
/// SPIR-V versions known to translator.
enum class VersionNumber : uint32_t {
// See section 2.3 of SPIR-V spec: Physical Layout of a SPIR_V Module and
// Instruction
SPIRV_1_0 = 0x00010000,
SPIRV_1_1 = 0x00010100,
SPIRV_1_2 = 0x00010200,
SPIRV_1_3 = 0x00010300,
SPIRV_1_4 = 0x00010400,
SPIRV_1_5 = 0x00010500,
SPIRV_1_6 = 0x00010600,
MinimumVersion = SPIRV_1_0,
MaximumVersion = SPIRV_1_6
};
inline constexpr std::string_view formatVersionNumber(uint32_t Version) {
switch (Version) {
case static_cast<uint32_t>(VersionNumber::SPIRV_1_0):
return "1.0";
case static_cast<uint32_t>(VersionNumber::SPIRV_1_1):
return "1.1";
case static_cast<uint32_t>(VersionNumber::SPIRV_1_2):
return "1.2";
case static_cast<uint32_t>(VersionNumber::SPIRV_1_3):
return "1.3";
case static_cast<uint32_t>(VersionNumber::SPIRV_1_4):
return "1.4";
case static_cast<uint32_t>(VersionNumber::SPIRV_1_5):
return "1.5";
case static_cast<uint32_t>(VersionNumber::SPIRV_1_6):
return "1.6";
}
return "unknown";
}
inline bool isSPIRVVersionKnown(VersionNumber Ver) {
return Ver >= VersionNumber::MinimumVersion &&
Ver <= VersionNumber::MaximumVersion;
}
enum class ExtensionID : uint32_t {
First,
#define EXT(X) X,
#include "LLVMSPIRVExtensions.inc"
#undef EXT
Last,
};
enum class ExtInst : uint32_t { None, OpenCL };
enum class BIsRepresentation : uint32_t { OpenCL12, OpenCL20, SPIRVFriendlyIR };
enum class FPContractMode : uint32_t { On, Off, Fast };
enum class DebugInfoEIS : uint32_t {
SPIRV_Debug,
OpenCL_DebugInfo_100,
NonSemantic_Shader_DebugInfo_100,
NonSemantic_Shader_DebugInfo_200
};
enum class BuiltinFormat : uint32_t { Function, Global };
/// \brief Helper class to manage SPIR-V translation
class TranslatorOpts {
public:
// Unset optional means not directly specified by user
using ExtensionsStatusMap = std::map<ExtensionID, std::optional<bool>>;
using ArgList = llvm::SmallVector<llvm::StringRef, 4>;
TranslatorOpts() = default;
TranslatorOpts(VersionNumber Max, const ExtensionsStatusMap &Map = {})
: MaxVersion(Max), ExtStatusMap(Map) {}
bool isAllowedToUseVersion(VersionNumber RequestedVersion) const {
return RequestedVersion <= MaxVersion;
}
bool isAllowedToUseExtension(ExtensionID Extension) const {
auto I = ExtStatusMap.find(Extension);
if (ExtStatusMap.end() == I)
return false;
return I->second && *I->second;
}
void setAllowedToUseExtension(ExtensionID Extension, bool Allow = true) {
// Only allow using the extension if it has not already been disabled
auto I = ExtStatusMap.find(Extension);
if (I == ExtStatusMap.end() || !I->second || (*I->second) == true)
ExtStatusMap[Extension] = Allow;
}
std::vector<std::string>
getAllowedSPIRVExtensionNames(std::function<bool(ExtensionID)> &Filter) const;
VersionNumber getMaxVersion() const { return MaxVersion; }
bool isGenArgNameMDEnabled() const { return GenKernelArgNameMD; }
bool isSPIRVMemToRegEnabled() const { return SPIRVMemToReg; }
void setMemToRegEnabled(bool Mem2Reg) { SPIRVMemToReg = Mem2Reg; }
bool preserveAuxData() const { return PreserveAuxData; }
void setPreserveAuxData(bool ArgValue) { PreserveAuxData = ArgValue; }
void setGenKernelArgNameMDEnabled(bool ArgNameMD) {
GenKernelArgNameMD = ArgNameMD;
}
void enableAllExtensions();
void enableGenArgNameMD() { GenKernelArgNameMD = true; }
void setSpecConst(uint32_t SpecId, uint64_t SpecValue) {
ExternalSpecialization[SpecId] = SpecValue;
}
bool getSpecializationConstant(uint32_t SpecId, uint64_t &Value) const {
auto It = ExternalSpecialization.find(SpecId);
if (It == ExternalSpecialization.end())
return false;
Value = It->second;
return true;
}
void setExtInst(ExtInst Value) {
// --spirv-ext-inst supersedes --spirv-replace-fmuladd-with-ocl-mad
ReplaceLLVMFmulAddWithOpenCLMad = false;
ExtInstValue = Value;
}
ExtInst getExtInst() const { return ExtInstValue; }
void setDesiredBIsRepresentation(BIsRepresentation Value) {
DesiredRepresentationOfBIs = Value;
}
BIsRepresentation getDesiredBIsRepresentation() const {
return DesiredRepresentationOfBIs;
}
void setFPContractMode(FPContractMode Mode) { FPCMode = Mode; }
FPContractMode getFPContractMode() const { return FPCMode; }
bool isUnknownIntrinsicAllowed(llvm::IntrinsicInst *II) const noexcept;
bool isSPIRVAllowUnknownIntrinsicsEnabled() const noexcept;
void setSPIRVAllowUnknownIntrinsics(ArgList IntrinsicPrefixList) noexcept;
bool allowExtraDIExpressions() const noexcept {
return AllowExtraDIExpressions;
}
void setAllowExtraDIExpressionsEnabled(bool Allow) noexcept {
AllowExtraDIExpressions = Allow;
}
DebugInfoEIS getDebugInfoEIS() const { return DebugInfoVersion; }
void setDebugInfoEIS(DebugInfoEIS EIS) { DebugInfoVersion = EIS; }
bool shouldReplaceLLVMFmulAddWithOpenCLMad() const noexcept {
return ReplaceLLVMFmulAddWithOpenCLMad;
}
void setReplaceLLVMFmulAddWithOpenCLMad(bool Value) noexcept {
ReplaceLLVMFmulAddWithOpenCLMad = Value;
}
bool shouldPreserveOCLKernelArgTypeMetadataThroughString() const noexcept {
return PreserveOCLKernelArgTypeMetadataThroughString;
}
void setPreserveOCLKernelArgTypeMetadataThroughString(bool Value) noexcept {
PreserveOCLKernelArgTypeMetadataThroughString = Value;
}
bool shouldEmitFunctionPtrAddrSpace() const noexcept {
return EmitFunctionPtrAddrSpace;
}
void setEmitFunctionPtrAddrSpace(bool Value) noexcept {
EmitFunctionPtrAddrSpace = Value;
}
void setBuiltinFormat(BuiltinFormat Value) noexcept {
SPIRVBuiltinFormat = Value;
}
BuiltinFormat getBuiltinFormat() const noexcept { return SPIRVBuiltinFormat; }
void setUseLLVMTarget(bool Flag) noexcept { UseLLVMTarget = Flag; }
bool getUseLLVMTarget() const noexcept { return UseLLVMTarget; }
void setFnVarCategory(uint32_t Category) noexcept {
FnVarCategory = Category;
}
std::optional<uint32_t> getFnVarCategory() const noexcept {
return FnVarCategory;
}
void setFnVarFamily(uint32_t Family) noexcept { FnVarFamily = Family; }
std::optional<uint32_t> getFnVarFamily() const noexcept {
return FnVarFamily;
}
void setFnVarArch(uint32_t Arch) noexcept { FnVarArch = Arch; }
std::optional<uint32_t> getFnVarArch() const noexcept { return FnVarArch; }
void setFnVarTarget(uint32_t Target) noexcept { FnVarTarget = Target; }
std::optional<uint32_t> getFnVarTarget() const noexcept {
return FnVarTarget;
}
void setFnVarFeatures(std::vector<uint32_t> Features) noexcept {
FnVarFeatures = Features;
}
std::vector<uint32_t> getFnVarFeatures() const noexcept {
return FnVarFeatures;
}
void setFnVarCapabilities(std::vector<uint32_t> Capabilities) noexcept {
FnVarCapabilities = Capabilities;
}
std::vector<uint32_t> getFnVarCapabilities() const noexcept {
return FnVarCapabilities;
}
void setFnVarSpecEnable(bool Val) noexcept { FnVarSpecEnable = Val; }
bool getFnVarSpecEnable() const noexcept { return FnVarSpecEnable; }
void setFnVarSpvOut(std::string Val) noexcept { FnVarSpvOut = Val; }
std::string getFnVarSpvOut() const noexcept { return FnVarSpvOut; }
// Check that options passed to --fnvar-xxx flags make sense. Return true on
// success, false on failure.
bool validateFnVarOpts() const;
private:
// Common translation options
VersionNumber MaxVersion = VersionNumber::MaximumVersion;
ExtensionsStatusMap ExtStatusMap;
// SPIRVMemToReg option affects LLVM IR regularization phase
bool SPIRVMemToReg = false;
// SPIR-V to LLVM translation options
bool GenKernelArgNameMD = false;
std::unordered_map<uint32_t, uint64_t> ExternalSpecialization;
// Extended instruction set to use when translating from LLVM IR to SPIR-V
ExtInst ExtInstValue = ExtInst::None;
// Representation of built-ins, which should be used while translating from
// SPIR-V to back to LLVM IR
BIsRepresentation DesiredRepresentationOfBIs = BIsRepresentation::OpenCL12;
// Controls floating point contraction.
//
// - FPContractMode::On allows to choose a mode according to
// presence of fused LLVM intrinsics
//
// - FPContractMode::Off disables contratction for all entry points
//
// - FPContractMode::Fast allows *all* operations to be contracted
// for all entry points
FPContractMode FPCMode = FPContractMode::On;
// Unknown LLVM intrinsics will be translated as external function calls in
// SPIR-V
std::optional<ArgList> SPIRVAllowUnknownIntrinsics{};
// Enable support for extra DIExpression opcodes not listed in the SPIR-V
// DebugInfo specification.
bool AllowExtraDIExpressions = false;
DebugInfoEIS DebugInfoVersion = DebugInfoEIS::OpenCL_DebugInfo_100;
// Controls whether llvm.fmuladd.* should be replaced with mad from OpenCL
// extended instruction set or with a simple fmul + fadd
bool ReplaceLLVMFmulAddWithOpenCLMad = true;
// Add a workaround to preserve OpenCL kernel_arg_type and
// kernel_arg_type_qual metadata through OpString
bool PreserveOCLKernelArgTypeMetadataThroughString = false;
// Controls if CodeSectionINTEL can be emitted and consumed with a dedicated
// address space
bool EmitFunctionPtrAddrSpace = false;
bool PreserveAuxData = false;
std::optional<uint32_t> FnVarCategory = std::nullopt;
std::optional<uint32_t> FnVarFamily = std::nullopt;
std::optional<uint32_t> FnVarArch = std::nullopt;
std::optional<uint32_t> FnVarTarget = std::nullopt;
std::vector<uint32_t> FnVarFeatures = {};
std::vector<uint32_t> FnVarCapabilities = {};
std::string FnVarSpvOut = "";
bool FnVarSpecEnable = false;
BuiltinFormat SPIRVBuiltinFormat = BuiltinFormat::Function;
// Convert LLVM to SPIR-V using the LLVM SPIR-V Backend target
bool UseLLVMTarget = false;
};
} // namespace SPIRV
#endif // SPIRV_LLVMSPIRVOPTS_H
@@ -0,0 +1,73 @@
set(SRC_LIST
LLVMSPIRVOpts.cpp
LLVMToSPIRVDbgTran.cpp
Mangler/FunctionDescriptor.cpp
Mangler/Mangler.cpp
Mangler/ManglingUtils.cpp
Mangler/ParameterType.cpp
OCLToSPIRV.cpp
OCLTypeToSPIRV.cpp
OCLUtil.cpp
VectorComputeUtil.cpp
SPIRVBuiltinHelper.cpp
SPIRVLowerBitCastToNonStandardType.cpp
SPIRVLowerBool.cpp
SPIRVLowerConstExpr.cpp
SPIRVLowerMemmove.cpp
SPIRVLowerOCLBlocks.cpp
SPIRVLowerLLVMIntrinsic.cpp
SPIRVReader.cpp
SPIRVRegularizeLLVM.cpp
SPIRVToLLVMDbgTran.cpp
SPIRVToOCL.cpp
SPIRVToOCL12.cpp
SPIRVToOCL20.cpp
SPIRVTypeScavenger.cpp
SPIRVUtil.cpp
SPIRVWriter.cpp
SPIRVWriterPass.cpp
PassPlugin.cpp
PreprocessMetadata.cpp
libSPIRV/SPIRVBasicBlock.cpp
libSPIRV/SPIRVDebug.cpp
libSPIRV/SPIRVDecorate.cpp
libSPIRV/SPIRVEntry.cpp
libSPIRV/SPIRVFunction.cpp
libSPIRV/SPIRVInstruction.cpp
libSPIRV/SPIRVModule.cpp
libSPIRV/SPIRVStream.cpp
libSPIRV/SPIRVType.cpp
libSPIRV/SPIRVValue.cpp
libSPIRV/SPIRVError.cpp
libSPIRV/SPIRVFnVar.cpp
)
add_llvm_library(LLVMSPIRVLib
${SRC_LIST}
LINK_COMPONENTS
Analysis
BitWriter
CodeGen
Core
Demangle
IRReader
Linker
Passes
Support
TargetParser
TransformUtils
DEPENDS
intrinsics_gen
)
target_include_directories(LLVMSPIRVLib
PRIVATE
${LLVM_INCLUDE_DIRS}
${LLVM_SPIRV_INCLUDE_DIRS}
# TODO: Consider using SPIRV-Headers' as a header-only INTERFACE
# instead. Right now this runs into exporting issues with
# the LLVM in-tree builds.
${LLVM_EXTERNAL_SPIRV_HEADERS_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/libSPIRV
${CMAKE_CURRENT_SOURCE_DIR}/Mangler
)
@@ -0,0 +1,786 @@
//===- LLVMBitreverse.h - implementation of llvm.bitreverse -===//
//
// The LLVM/SPIRV Translator
//
// Copyright (c) 2024 The Khronos Group Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//===----------------------------------------------------------------------===//
//
// This file implements lowering of llvm.bitreverse.* into basic LLVM
// operations.
//
//===----------------------------------------------------------------------===//
// The IR below is manually modified IR which was produced by the
// commands:
//
// clang -emit-llvm bitreverse.c -S -O2
// cat bitreverse.ll | sed 's/ dso_local//' \
// | sed 's/ noundef//' \
// | sed 's/zeroext %/%/' \
// | sed 's/ local_unnamed_addr #[0-9]//' \
// | sed 's/, !tbaa !3//' \
// | grep -v "Function Attrs:"
//
// from the C code in LLVMIntrinsicEmulation/bitreverse.c with a custom clang
// that was modified to disable llvm.bitreverse.* intrinsic generation.
//
// A similar command was run on LLVMIntrinsicEmulation/small_bitreverse.c to
// produce functions to reverse 2-bit and 4-bit types.
//
// Manual modification was done to avoid coercing vector types into scalar
// types. For example, the original LLVM IR:
//
// define i32 @llvm_bitreverse_v4i8(i32 %a.coerce) {
// entry:
// %0 = bitcast i32 %a.coerce to <4 x i8>
// %shl = shl <4 x i8> %0, <i8 4, i8 4, i8 4, i8 4>
// %shr = lshr <4 x i8> %0, <i8 4, i8 4, i8 4, i8 4>
// ...
// %1 = bitcast <4 x i8> %or12 to i32
// ret i32 %1
// }
//
// was converted to:
//
// define <4 x i8> @llvm_bitreverse_v4i8(<4 x i8> %a) {
// entry:
// %shl = shl <4 x i8> %a, <i8 4, i8 4, i8 4, i8 4>
// %shr = lshr <4 x i8> %a, <i8 4, i8 4, i8 4, i8 4>
// ...
// ret <4 x i8> %or12
// }
#define GEN_CONST1(BASE_TYPE, VAL) #VAL
#define GEN_CONST2(BASE_TYPE, VAL) \
"<" #BASE_TYPE " " #VAL "," #BASE_TYPE " " #VAL ">"
#define GEN_CONST3(BASE_TYPE, VAL) \
"<" #BASE_TYPE " " #VAL "," #BASE_TYPE " " #VAL "," #BASE_TYPE " " #VAL ">"
#define GEN_CONST4(BASE_TYPE, VAL) \
"<" #BASE_TYPE " " #VAL "," #BASE_TYPE " " #VAL "," #BASE_TYPE " " #VAL \
"," #BASE_TYPE " " #VAL ">"
#define GEN_CONST8(BASE_TYPE, VAL) \
"<" #BASE_TYPE " " #VAL "," #BASE_TYPE " " #VAL "," #BASE_TYPE " " #VAL \
"," #BASE_TYPE " " #VAL "," #BASE_TYPE " " #VAL "," #BASE_TYPE " " #VAL \
"," #BASE_TYPE " " #VAL "," #BASE_TYPE " " #VAL ">"
#define GEN_CONST16(BASE_TYPE, VAL) \
"<" #BASE_TYPE " " #VAL "," #BASE_TYPE " " #VAL "," #BASE_TYPE " " #VAL \
"," #BASE_TYPE " " #VAL "," #BASE_TYPE " " #VAL "," #BASE_TYPE " " #VAL \
"," #BASE_TYPE " " #VAL "," #BASE_TYPE " " #VAL "," #BASE_TYPE " " #VAL \
"," #BASE_TYPE " " #VAL "," #BASE_TYPE " " #VAL "," #BASE_TYPE " " #VAL \
"," #BASE_TYPE " " #VAL "," #BASE_TYPE " " #VAL "," #BASE_TYPE " " #VAL \
"," #BASE_TYPE " " #VAL ">"
// clang-format off
#define MAKE_BITREVERSE_2BIT(SUFFIX,TYPE_STR,NUM_ELTS,BASE_TYPE) \
static const char LLVMBitreverse ## SUFFIX[]{" \n\
define " TYPE_STR " @llvm_bitreverse_" #SUFFIX "(" TYPE_STR " %A) { \n\
entry: \n\
%and = shl " TYPE_STR " %A, " GEN_CONST ## NUM_ELTS(BASE_TYPE,1) " \n\
%shr4 = lshr " TYPE_STR " %A, " GEN_CONST ## NUM_ELTS(BASE_TYPE,1) " \n\
%or = or disjoint " TYPE_STR " %and, %shr4 \n\
ret " TYPE_STR " %or \n\
} \n\
"}
MAKE_BITREVERSE_2BIT(i2, "i2", 1, i2);
MAKE_BITREVERSE_2BIT(v2i2, "<2 x i2>", 2, i2);
MAKE_BITREVERSE_2BIT(v3i2, "<3 x i2>", 3, i2);
MAKE_BITREVERSE_2BIT(v4i2, "<4 x i2>", 4, i2);
MAKE_BITREVERSE_2BIT(v8i2, "<8 x i2>", 8, i2);
MAKE_BITREVERSE_2BIT(v16i2, "<16 x i2>", 16, i2);
#define MAKE_BITREVERSE_4BIT(SUFFIX,TYPE_STR,NUM_ELTS,BASE_TYPE) \
static const char LLVMBitreverse ## SUFFIX[]{" \n\
define " TYPE_STR " @llvm_bitreverse_" #SUFFIX "(" TYPE_STR " %A) { \n\
entry: \n\
%and = shl " TYPE_STR " %A, " GEN_CONST ## NUM_ELTS(BASE_TYPE, 2) " \n\
%shr = lshr " TYPE_STR " %A, " GEN_CONST ## NUM_ELTS(BASE_TYPE, 2) " \n\
%or = or disjoint " TYPE_STR " %and, %shr \n\
%and2 = shl " TYPE_STR " %or, " GEN_CONST ## NUM_ELTS(BASE_TYPE, 1) " \n\
%shl3 = and " TYPE_STR " %and2, " GEN_CONST ## NUM_ELTS(BASE_TYPE,-6) " \n\
%shr4 = lshr " TYPE_STR " %or, " GEN_CONST ## NUM_ELTS(BASE_TYPE, 1) " \n\
%and5 = and " TYPE_STR " %shr4, " GEN_CONST ## NUM_ELTS(BASE_TYPE, 5) " \n\
%or6 = or disjoint " TYPE_STR " %shl3, %and5 \n\
ret " TYPE_STR " %or6 \n\
} \n\
"}
MAKE_BITREVERSE_4BIT(i4, "i4", 1, i4);
MAKE_BITREVERSE_4BIT(v2i4, "<2 x i4>", 2, i4);
MAKE_BITREVERSE_4BIT(v3i4, "<3 x i4>", 3, i4);
MAKE_BITREVERSE_4BIT(v4i4, "<4 x i4>", 4, i4);
MAKE_BITREVERSE_4BIT(v8i4, "<8 x i4>", 8, i4);
MAKE_BITREVERSE_4BIT(v16i4, "<16 x i4>", 16, i4);
// clang-format on
static const char LLVMBitreversei8[]{R"(
define zeroext i8 @llvm_bitreverse_i8(i8 %A) {
entry:
%and = shl i8 %A, 4
%shr = lshr i8 %A, 4
%or = or disjoint i8 %and, %shr
%and5 = shl i8 %or, 2
%shl6 = and i8 %and5, -52
%shr8 = lshr i8 %or, 2
%and9 = and i8 %shr8, 51
%or10 = or disjoint i8 %shl6, %and9
%and13 = shl i8 %or10, 1
%shl14 = and i8 %and13, -86
%shr16 = lshr i8 %or10, 1
%and17 = and i8 %shr16, 85
%or18 = or disjoint i8 %shl14, %and17
ret i8 %or18
}
)"};
static const char LLVMBitreversei16[]{R"(
define zeroext i16 @llvm_bitreverse_i16(i16 %A) {
entry:
%and = shl i16 %A, 8
%shr = lshr i16 %A, 8
%or = or disjoint i16 %and, %shr
%and5 = shl i16 %or, 4
%shl6 = and i16 %and5, -3856
%shr8 = lshr i16 %or, 4
%and9 = and i16 %shr8, 3855
%or10 = or disjoint i16 %shl6, %and9
%and13 = shl i16 %or10, 2
%shl14 = and i16 %and13, -13108
%shr16 = lshr i16 %or10, 2
%and17 = and i16 %shr16, 13107
%or18 = or disjoint i16 %shl14, %and17
%and21 = shl i16 %or18, 1
%shl22 = and i16 %and21, -21846
%shr24 = lshr i16 %or18, 1
%and25 = and i16 %shr24, 21845
%or26 = or disjoint i16 %shl22, %and25
ret i16 %or26
}
)"};
static const char LLVMBitreversei32[]{R"(
define i32 @llvm_bitreverse_i32(i32 %A) {
entry:
%and = shl i32 %A, 16
%shr = lshr i32 %A, 16
%or = or disjoint i32 %and, %shr
%and2 = shl i32 %or, 8
%shl3 = and i32 %and2, -16711936
%shr4 = lshr i32 %or, 8
%and5 = and i32 %shr4, 16711935
%or6 = or disjoint i32 %shl3, %and5
%and7 = shl i32 %or6, 4
%shl8 = and i32 %and7, -252645136
%shr9 = lshr i32 %or6, 4
%and10 = and i32 %shr9, 252645135
%or11 = or disjoint i32 %shl8, %and10
%and12 = shl i32 %or11, 2
%shl13 = and i32 %and12, -858993460
%shr14 = lshr i32 %or11, 2
%and15 = and i32 %shr14, 858993459
%or16 = or disjoint i32 %shl13, %and15
%and17 = shl i32 %or16, 1
%shl18 = and i32 %and17, -1431655766
%shr19 = lshr i32 %or16, 1
%and20 = and i32 %shr19, 1431655765
%or21 = or disjoint i32 %shl18, %and20
ret i32 %or21
}
)"};
static const char LLVMBitreversei64[]{R"(
define i64 @llvm_bitreverse_i64(i64 %A) {
entry:
%and = shl i64 %A, 32
%shr = lshr i64 %A, 32
%or = or disjoint i64 %and, %shr
%and2 = shl i64 %or, 16
%shl3 = and i64 %and2, -281470681808896
%shr4 = lshr i64 %or, 16
%and5 = and i64 %shr4, 281470681808895
%or6 = or disjoint i64 %shl3, %and5
%and7 = shl i64 %or6, 8
%shl8 = and i64 %and7, -71777214294589696
%shr9 = lshr i64 %or6, 8
%and10 = and i64 %shr9, 71777214294589695
%or11 = or disjoint i64 %shl8, %and10
%and12 = shl i64 %or11, 4
%shl13 = and i64 %and12, -1085102592571150096
%shr14 = lshr i64 %or11, 4
%and15 = and i64 %shr14, 1085102592571150095
%or16 = or disjoint i64 %shl13, %and15
%and17 = shl i64 %or16, 2
%shl18 = and i64 %and17, -3689348814741910324
%shr19 = lshr i64 %or16, 2
%and20 = and i64 %shr19, 3689348814741910323
%or21 = or disjoint i64 %shl18, %and20
%and22 = shl i64 %or21, 1
%shl23 = and i64 %and22, -6148914691236517206
%shr24 = lshr i64 %or21, 1
%and25 = and i64 %shr24, 6148914691236517205
%or26 = or disjoint i64 %shl23, %and25
ret i64 %or26
}
)"};
static const char LLVMBitreversev2i8[]{R"(
define <2 x i8> @llvm_bitreverse_v2i8(<2 x i8> %A) {
entry:
%shl = shl <2 x i8> %A, <i8 4, i8 4>
%shr = lshr <2 x i8> %A, <i8 4, i8 4>
%or = or disjoint <2 x i8> %shl, %shr
%and3 = shl <2 x i8> %or, <i8 2, i8 2>
%shl4 = and <2 x i8> %and3, <i8 -52, i8 -52>
%shr5 = lshr <2 x i8> %or, <i8 2, i8 2>
%and6 = and <2 x i8> %shr5, <i8 51, i8 51>
%or7 = or disjoint <2 x i8> %shl4, %and6
%and8 = shl <2 x i8> %or7, <i8 1, i8 1>
%shl9 = and <2 x i8> %and8, <i8 -86, i8 -86>
%shr10 = lshr <2 x i8> %or7, <i8 1, i8 1>
%and11 = and <2 x i8> %shr10, <i8 85, i8 85>
%or12 = or disjoint <2 x i8> %shl9, %and11
ret <2 x i8> %or12
}
)"};
static const char LLVMBitreversev2i16[]{R"(
define <2 x i16> @llvm_bitreverse_v2i16(<2 x i16> %A) {
entry:
%shl = shl <2 x i16> %A, <i16 8, i16 8>
%shr = lshr <2 x i16> %A, <i16 8, i16 8>
%or = or disjoint <2 x i16> %shl, %shr
%and3 = shl <2 x i16> %or, <i16 4, i16 4>
%shl4 = and <2 x i16> %and3, <i16 -3856, i16 -3856>
%shr5 = lshr <2 x i16> %or, <i16 4, i16 4>
%and6 = and <2 x i16> %shr5, <i16 3855, i16 3855>
%or7 = or disjoint <2 x i16> %shl4, %and6
%and8 = shl <2 x i16> %or7, <i16 2, i16 2>
%shl9 = and <2 x i16> %and8, <i16 -13108, i16 -13108>
%shr10 = lshr <2 x i16> %or7, <i16 2, i16 2>
%and11 = and <2 x i16> %shr10, <i16 13107, i16 13107>
%or12 = or disjoint <2 x i16> %shl9, %and11
%and13 = shl <2 x i16> %or12, <i16 1, i16 1>
%shl14 = and <2 x i16> %and13, <i16 -21846, i16 -21846>
%shr15 = lshr <2 x i16> %or12, <i16 1, i16 1>
%and16 = and <2 x i16> %shr15, <i16 21845, i16 21845>
%or17 = or disjoint <2 x i16> %shl14, %and16
ret <2 x i16> %or17
}
)"};
static const char LLVMBitreversev2i32[]{R"(
define <2 x i32> @llvm_bitreverse_v2i32(<2 x i32> %A) {
entry:
%shl = shl <2 x i32> %A, <i32 16, i32 16>
%shr = lshr <2 x i32> %A, <i32 16, i32 16>
%or = or disjoint <2 x i32> %shl, %shr
%and3 = shl <2 x i32> %or, <i32 8, i32 8>
%shl4 = and <2 x i32> %and3, <i32 -16711936, i32 -16711936>
%shr5 = lshr <2 x i32> %or, <i32 8, i32 8>
%and6 = and <2 x i32> %shr5, <i32 16711935, i32 16711935>
%or7 = or disjoint <2 x i32> %shl4, %and6
%and8 = shl <2 x i32> %or7, <i32 4, i32 4>
%shl9 = and <2 x i32> %and8, <i32 -252645136, i32 -252645136>
%shr10 = lshr <2 x i32> %or7, <i32 4, i32 4>
%and11 = and <2 x i32> %shr10, <i32 252645135, i32 252645135>
%or12 = or disjoint <2 x i32> %shl9, %and11
%and13 = shl <2 x i32> %or12, <i32 2, i32 2>
%shl14 = and <2 x i32> %and13, <i32 -858993460, i32 -858993460>
%shr15 = lshr <2 x i32> %or12, <i32 2, i32 2>
%and16 = and <2 x i32> %shr15, <i32 858993459, i32 858993459>
%or17 = or disjoint <2 x i32> %shl14, %and16
%and18 = shl <2 x i32> %or17, <i32 1, i32 1>
%shl19 = and <2 x i32> %and18, <i32 -1431655766, i32 -1431655766>
%shr20 = lshr <2 x i32> %or17, <i32 1, i32 1>
%and21 = and <2 x i32> %shr20, <i32 1431655765, i32 1431655765>
%or22 = or disjoint <2 x i32> %shl19, %and21
ret <2 x i32> %or22
}
)"};
static const char LLVMBitreversev2i64[]{R"(
define <2 x i64> @llvm_bitreverse_v2i64(<2 x i64> %A) {
entry:
%shl = shl <2 x i64> %A, <i64 32, i64 32>
%shr = lshr <2 x i64> %A, <i64 32, i64 32>
%or = or disjoint <2 x i64> %shl, %shr
%and2 = shl <2 x i64> %or, <i64 16, i64 16>
%shl3 = and <2 x i64> %and2, <i64 -281470681808896, i64 -281470681808896>
%shr4 = lshr <2 x i64> %or, <i64 16, i64 16>
%and5 = and <2 x i64> %shr4, <i64 281470681808895, i64 281470681808895>
%or6 = or disjoint <2 x i64> %shl3, %and5
%and7 = shl <2 x i64> %or6, <i64 8, i64 8>
%shl8 = and <2 x i64> %and7, <i64 -71777214294589696, i64 -71777214294589696>
%shr9 = lshr <2 x i64> %or6, <i64 8, i64 8>
%and10 = and <2 x i64> %shr9, <i64 71777214294589695, i64 71777214294589695>
%or11 = or disjoint <2 x i64> %shl8, %and10
%and12 = shl <2 x i64> %or11, <i64 4, i64 4>
%shl13 = and <2 x i64> %and12, <i64 -1085102592571150096, i64 -1085102592571150096>
%shr14 = lshr <2 x i64> %or11, <i64 4, i64 4>
%and15 = and <2 x i64> %shr14, <i64 1085102592571150095, i64 1085102592571150095>
%or16 = or disjoint <2 x i64> %shl13, %and15
%and17 = shl <2 x i64> %or16, <i64 2, i64 2>
%shl18 = and <2 x i64> %and17, <i64 -3689348814741910324, i64 -3689348814741910324>
%shr19 = lshr <2 x i64> %or16, <i64 2, i64 2>
%and20 = and <2 x i64> %shr19, <i64 3689348814741910323, i64 3689348814741910323>
%or21 = or disjoint <2 x i64> %shl18, %and20
%and22 = shl <2 x i64> %or21, <i64 1, i64 1>
%shl23 = and <2 x i64> %and22, <i64 -6148914691236517206, i64 -6148914691236517206>
%shr24 = lshr <2 x i64> %or21, <i64 1, i64 1>
%and25 = and <2 x i64> %shr24, <i64 6148914691236517205, i64 6148914691236517205>
%or26 = or disjoint <2 x i64> %shl23, %and25
ret <2 x i64> %or26
}
)"};
static const char LLVMBitreversev3i8[]{R"(
define <3 x i8> @llvm_bitreverse_v3i8(<3 x i8> %A) {
entry:
%shl = shl <3 x i8> %A, <i8 4, i8 4, i8 4>
%shr = lshr <3 x i8> %A, <i8 4, i8 4, i8 4>
%or = or disjoint <3 x i8> %shl, %shr
%and10 = shl <3 x i8> %or, <i8 2, i8 2, i8 2>
%shl11 = and <3 x i8> %and10, <i8 -52, i8 -52, i8 -52>
%shr14 = lshr <3 x i8> %or, <i8 2, i8 2, i8 2>
%and15 = and <3 x i8> %shr14, <i8 51, i8 51, i8 51>
%or16 = or disjoint <3 x i8> %shl11, %and15
%and20 = shl <3 x i8> %or16, <i8 1, i8 1, i8 1>
%shl21 = and <3 x i8> %and20, <i8 -86, i8 -86, i8 -86>
%shr24 = lshr <3 x i8> %or16, <i8 1, i8 1, i8 1>
%and25 = and <3 x i8> %shr24, <i8 85, i8 85, i8 85>
%or26 = or disjoint <3 x i8> %shl21, %and25
ret <3 x i8> %or26
}
)"};
static const char LLVMBitreversev3i16[]{R"(
define <3 x i16> @llvm_bitreverse_v3i16(<3 x i16> %A) {
entry:
%shl = shl <3 x i16> %A, <i16 8, i16 8, i16 8>
%shr = lshr <3 x i16> %A, <i16 8, i16 8, i16 8>
%or = or disjoint <3 x i16> %shl, %shr
%and10 = shl <3 x i16> %or, <i16 4, i16 4, i16 4>
%shl11 = and <3 x i16> %and10, <i16 -3856, i16 -3856, i16 -3856>
%shr14 = lshr <3 x i16> %or, <i16 4, i16 4, i16 4>
%and15 = and <3 x i16> %shr14, <i16 3855, i16 3855, i16 3855>
%or16 = or disjoint <3 x i16> %shl11, %and15
%and20 = shl <3 x i16> %or16, <i16 2, i16 2, i16 2>
%shl21 = and <3 x i16> %and20, <i16 -13108, i16 -13108, i16 -13108>
%shr24 = lshr <3 x i16> %or16, <i16 2, i16 2, i16 2>
%and25 = and <3 x i16> %shr24, <i16 13107, i16 13107, i16 13107>
%or26 = or disjoint <3 x i16> %shl21, %and25
%and30 = shl <3 x i16> %or26, <i16 1, i16 1, i16 1>
%shl31 = and <3 x i16> %and30, <i16 -21846, i16 -21846, i16 -21846>
%shr34 = lshr <3 x i16> %or26, <i16 1, i16 1, i16 1>
%and35 = and <3 x i16> %shr34, <i16 21845, i16 21845, i16 21845>
%or36 = or disjoint <3 x i16> %shl31, %and35
ret <3 x i16> %or36
}
)"};
static const char LLVMBitreversev3i32[]{R"(
define <3 x i32> @llvm_bitreverse_v3i32(<3 x i32> %A) {
entry:
%shl = shl <3 x i32> %A, <i32 16, i32 16, i32 16>
%shr = lshr <3 x i32> %A, <i32 16, i32 16, i32 16>
%or = or disjoint <3 x i32> %shl, %shr
%and8 = shl <3 x i32> %or, <i32 8, i32 8, i32 8>
%shl9 = and <3 x i32> %and8, <i32 -16711936, i32 -16711936, i32 -16711936>
%shr12 = lshr <3 x i32> %or, <i32 8, i32 8, i32 8>
%and13 = and <3 x i32> %shr12, <i32 16711935, i32 16711935, i32 16711935>
%or14 = or disjoint <3 x i32> %shl9, %and13
%and18 = shl <3 x i32> %or14, <i32 4, i32 4, i32 4>
%shl19 = and <3 x i32> %and18, <i32 -252645136, i32 -252645136, i32 -252645136>
%shr22 = lshr <3 x i32> %or14, <i32 4, i32 4, i32 4>
%and23 = and <3 x i32> %shr22, <i32 252645135, i32 252645135, i32 252645135>
%or24 = or disjoint <3 x i32> %shl19, %and23
%and28 = shl <3 x i32> %or24, <i32 2, i32 2, i32 2>
%shl29 = and <3 x i32> %and28, <i32 -858993460, i32 -858993460, i32 -858993460>
%shr32 = lshr <3 x i32> %or24, <i32 2, i32 2, i32 2>
%and33 = and <3 x i32> %shr32, <i32 858993459, i32 858993459, i32 858993459>
%or34 = or disjoint <3 x i32> %shl29, %and33
%and38 = shl <3 x i32> %or34, <i32 1, i32 1, i32 1>
%shl39 = and <3 x i32> %and38, <i32 -1431655766, i32 -1431655766, i32 -1431655766>
%shr42 = lshr <3 x i32> %or34, <i32 1, i32 1, i32 1>
%and43 = and <3 x i32> %shr42, <i32 1431655765, i32 1431655765, i32 1431655765>
%or44 = or disjoint <3 x i32> %shl39, %and43
ret <3 x i32> %or44
}
)"};
static const char LLVMBitreversev3i64[]{R"(
define <3 x i64> @llvm_bitreverse_v3i64(<3 x i64> %A) {
entry:
%shl = shl <3 x i64> %A, <i64 32, i64 32, i64 32>
%shr = lshr <3 x i64> %A, <i64 32, i64 32, i64 32>
%or = or disjoint <3 x i64> %shl, %shr
%and9 = shl <3 x i64> %or, <i64 16, i64 16, i64 16>
%shl10 = and <3 x i64> %and9, <i64 -281470681808896, i64 -281470681808896, i64 -281470681808896>
%shr13 = lshr <3 x i64> %or, <i64 16, i64 16, i64 16>
%and14 = and <3 x i64> %shr13, <i64 281470681808895, i64 281470681808895, i64 281470681808895>
%or15 = or disjoint <3 x i64> %shl10, %and14
%and19 = shl <3 x i64> %or15, <i64 8, i64 8, i64 8>
%shl20 = and <3 x i64> %and19, <i64 -71777214294589696, i64 -71777214294589696, i64 -71777214294589696>
%shr23 = lshr <3 x i64> %or15, <i64 8, i64 8, i64 8>
%and24 = and <3 x i64> %shr23, <i64 71777214294589695, i64 71777214294589695, i64 71777214294589695>
%or25 = or disjoint <3 x i64> %shl20, %and24
%and29 = shl <3 x i64> %or25, <i64 4, i64 4, i64 4>
%shl30 = and <3 x i64> %and29, <i64 -1085102592571150096, i64 -1085102592571150096, i64 -1085102592571150096>
%shr33 = lshr <3 x i64> %or25, <i64 4, i64 4, i64 4>
%and34 = and <3 x i64> %shr33, <i64 1085102592571150095, i64 1085102592571150095, i64 1085102592571150095>
%or35 = or disjoint <3 x i64> %shl30, %and34
%and39 = shl <3 x i64> %or35, <i64 2, i64 2, i64 2>
%shl40 = and <3 x i64> %and39, <i64 -3689348814741910324, i64 -3689348814741910324, i64 -3689348814741910324>
%shr43 = lshr <3 x i64> %or35, <i64 2, i64 2, i64 2>
%and44 = and <3 x i64> %shr43, <i64 3689348814741910323, i64 3689348814741910323, i64 3689348814741910323>
%or45 = or disjoint <3 x i64> %shl40, %and44
%and49 = shl <3 x i64> %or45, <i64 1, i64 1, i64 1>
%shl50 = and <3 x i64> %and49, <i64 -6148914691236517206, i64 -6148914691236517206, i64 -6148914691236517206>
%shr53 = lshr <3 x i64> %or45, <i64 1, i64 1, i64 1>
%and54 = and <3 x i64> %shr53, <i64 6148914691236517205, i64 6148914691236517205, i64 6148914691236517205>
%or55 = or disjoint <3 x i64> %shl50, %and54
ret <3 x i64> %or55
}
)"};
static const char LLVMBitreversev4i8[]{R"(
define <4 x i8> @llvm_bitreverse_v4i8(<4 x i8> %A) {
entry:
%shl = shl <4 x i8> %A, <i8 4, i8 4, i8 4, i8 4>
%shr = lshr <4 x i8> %A, <i8 4, i8 4, i8 4, i8 4>
%or = or disjoint <4 x i8> %shl, %shr
%and3 = shl <4 x i8> %or, <i8 2, i8 2, i8 2, i8 2>
%shl4 = and <4 x i8> %and3, <i8 -52, i8 -52, i8 -52, i8 -52>
%shr5 = lshr <4 x i8> %or, <i8 2, i8 2, i8 2, i8 2>
%and6 = and <4 x i8> %shr5, <i8 51, i8 51, i8 51, i8 51>
%or7 = or disjoint <4 x i8> %shl4, %and6
%and8 = shl <4 x i8> %or7, <i8 1, i8 1, i8 1, i8 1>
%shl9 = and <4 x i8> %and8, <i8 -86, i8 -86, i8 -86, i8 -86>
%shr10 = lshr <4 x i8> %or7, <i8 1, i8 1, i8 1, i8 1>
%and11 = and <4 x i8> %shr10, <i8 85, i8 85, i8 85, i8 85>
%or12 = or disjoint <4 x i8> %shl9, %and11
ret <4 x i8> %or12
}
)"};
static const char LLVMBitreversev4i16[]{R"(
define <4 x i16> @llvm_bitreverse_v4i16(<4 x i16> %A) {
entry:
%shl = shl <4 x i16> %A, <i16 8, i16 8, i16 8, i16 8>
%shr = lshr <4 x i16> %A, <i16 8, i16 8, i16 8, i16 8>
%or = or disjoint <4 x i16> %shl, %shr
%and3 = shl <4 x i16> %or, <i16 4, i16 4, i16 4, i16 4>
%shl4 = and <4 x i16> %and3, <i16 -3856, i16 -3856, i16 -3856, i16 -3856>
%shr5 = lshr <4 x i16> %or, <i16 4, i16 4, i16 4, i16 4>
%and6 = and <4 x i16> %shr5, <i16 3855, i16 3855, i16 3855, i16 3855>
%or7 = or disjoint <4 x i16> %shl4, %and6
%and8 = shl <4 x i16> %or7, <i16 2, i16 2, i16 2, i16 2>
%shl9 = and <4 x i16> %and8, <i16 -13108, i16 -13108, i16 -13108, i16 -13108>
%shr10 = lshr <4 x i16> %or7, <i16 2, i16 2, i16 2, i16 2>
%and11 = and <4 x i16> %shr10, <i16 13107, i16 13107, i16 13107, i16 13107>
%or12 = or disjoint <4 x i16> %shl9, %and11
%and13 = shl <4 x i16> %or12, <i16 1, i16 1, i16 1, i16 1>
%shl14 = and <4 x i16> %and13, <i16 -21846, i16 -21846, i16 -21846, i16 -21846>
%shr15 = lshr <4 x i16> %or12, <i16 1, i16 1, i16 1, i16 1>
%and16 = and <4 x i16> %shr15, <i16 21845, i16 21845, i16 21845, i16 21845>
%or17 = or disjoint <4 x i16> %shl14, %and16
ret <4 x i16> %or17
}
)"};
static const char LLVMBitreversev4i32[]{R"(
define <4 x i32> @llvm_bitreverse_v4i32(<4 x i32> %A) {
entry:
%shl = shl <4 x i32> %A, <i32 16, i32 16, i32 16, i32 16>
%shr = lshr <4 x i32> %A, <i32 16, i32 16, i32 16, i32 16>
%or = or disjoint <4 x i32> %shl, %shr
%and2 = shl <4 x i32> %or, <i32 8, i32 8, i32 8, i32 8>
%shl3 = and <4 x i32> %and2, <i32 -16711936, i32 -16711936, i32 -16711936, i32 -16711936>
%shr4 = lshr <4 x i32> %or, <i32 8, i32 8, i32 8, i32 8>
%and5 = and <4 x i32> %shr4, <i32 16711935, i32 16711935, i32 16711935, i32 16711935>
%or6 = or disjoint <4 x i32> %shl3, %and5
%and7 = shl <4 x i32> %or6, <i32 4, i32 4, i32 4, i32 4>
%shl8 = and <4 x i32> %and7, <i32 -252645136, i32 -252645136, i32 -252645136, i32 -252645136>
%shr9 = lshr <4 x i32> %or6, <i32 4, i32 4, i32 4, i32 4>
%and10 = and <4 x i32> %shr9, <i32 252645135, i32 252645135, i32 252645135, i32 252645135>
%or11 = or disjoint <4 x i32> %shl8, %and10
%and12 = shl <4 x i32> %or11, <i32 2, i32 2, i32 2, i32 2>
%shl13 = and <4 x i32> %and12, <i32 -858993460, i32 -858993460, i32 -858993460, i32 -858993460>
%shr14 = lshr <4 x i32> %or11, <i32 2, i32 2, i32 2, i32 2>
%and15 = and <4 x i32> %shr14, <i32 858993459, i32 858993459, i32 858993459, i32 858993459>
%or16 = or disjoint <4 x i32> %shl13, %and15
%and17 = shl <4 x i32> %or16, <i32 1, i32 1, i32 1, i32 1>
%shl18 = and <4 x i32> %and17, <i32 -1431655766, i32 -1431655766, i32 -1431655766, i32 -1431655766>
%shr19 = lshr <4 x i32> %or16, <i32 1, i32 1, i32 1, i32 1>
%and20 = and <4 x i32> %shr19, <i32 1431655765, i32 1431655765, i32 1431655765, i32 1431655765>
%or21 = or disjoint <4 x i32> %shl18, %and20
ret <4 x i32> %or21
}
)"};
static const char LLVMBitreversev4i64[]{R"(
define <4 x i64> @llvm_bitreverse_v4i64(<4 x i64> %A) {
entry:
%shl = shl <4 x i64> %A, <i64 32, i64 32, i64 32, i64 32>
%shr = lshr <4 x i64> %A, <i64 32, i64 32, i64 32, i64 32>
%or = or disjoint <4 x i64> %shl, %shr
%and2 = shl <4 x i64> %or, <i64 16, i64 16, i64 16, i64 16>
%shl3 = and <4 x i64> %and2, <i64 -281470681808896, i64 -281470681808896, i64 -281470681808896, i64 -281470681808896>
%shr4 = lshr <4 x i64> %or, <i64 16, i64 16, i64 16, i64 16>
%and5 = and <4 x i64> %shr4, <i64 281470681808895, i64 281470681808895, i64 281470681808895, i64 281470681808895>
%or6 = or disjoint <4 x i64> %shl3, %and5
%and7 = shl <4 x i64> %or6, <i64 8, i64 8, i64 8, i64 8>
%shl8 = and <4 x i64> %and7, <i64 -71777214294589696, i64 -71777214294589696, i64 -71777214294589696, i64 -71777214294589696>
%shr9 = lshr <4 x i64> %or6, <i64 8, i64 8, i64 8, i64 8>
%and10 = and <4 x i64> %shr9, <i64 71777214294589695, i64 71777214294589695, i64 71777214294589695, i64 71777214294589695>
%or11 = or disjoint <4 x i64> %shl8, %and10
%and12 = shl <4 x i64> %or11, <i64 4, i64 4, i64 4, i64 4>
%shl13 = and <4 x i64> %and12, <i64 -1085102592571150096, i64 -1085102592571150096, i64 -1085102592571150096, i64 -1085102592571150096>
%shr14 = lshr <4 x i64> %or11, <i64 4, i64 4, i64 4, i64 4>
%and15 = and <4 x i64> %shr14, <i64 1085102592571150095, i64 1085102592571150095, i64 1085102592571150095, i64 1085102592571150095>
%or16 = or disjoint <4 x i64> %shl13, %and15
%and17 = shl <4 x i64> %or16, <i64 2, i64 2, i64 2, i64 2>
%shl18 = and <4 x i64> %and17, <i64 -3689348814741910324, i64 -3689348814741910324, i64 -3689348814741910324, i64 -3689348814741910324>
%shr19 = lshr <4 x i64> %or16, <i64 2, i64 2, i64 2, i64 2>
%and20 = and <4 x i64> %shr19, <i64 3689348814741910323, i64 3689348814741910323, i64 3689348814741910323, i64 3689348814741910323>
%or21 = or disjoint <4 x i64> %shl18, %and20
%and22 = shl <4 x i64> %or21, <i64 1, i64 1, i64 1, i64 1>
%shl23 = and <4 x i64> %and22, <i64 -6148914691236517206, i64 -6148914691236517206, i64 -6148914691236517206, i64 -6148914691236517206>
%shr24 = lshr <4 x i64> %or21, <i64 1, i64 1, i64 1, i64 1>
%and25 = and <4 x i64> %shr24, <i64 6148914691236517205, i64 6148914691236517205, i64 6148914691236517205, i64 6148914691236517205>
%or26 = or disjoint <4 x i64> %shl23, %and25
ret <4 x i64> %or26
}
)"};
static const char LLVMBitreversev8i8[]{R"(
define <8 x i8> @llvm_bitreverse_v8i8(<8 x i8> %A) {
entry:
%shl = shl <8 x i8> %A, <i8 4, i8 4, i8 4, i8 4, i8 4, i8 4, i8 4, i8 4>
%shr = lshr <8 x i8> %A, <i8 4, i8 4, i8 4, i8 4, i8 4, i8 4, i8 4, i8 4>
%or = or disjoint <8 x i8> %shl, %shr
%and3 = shl <8 x i8> %or, <i8 2, i8 2, i8 2, i8 2, i8 2, i8 2, i8 2, i8 2>
%shl4 = and <8 x i8> %and3, <i8 -52, i8 -52, i8 -52, i8 -52, i8 -52, i8 -52, i8 -52, i8 -52>
%shr5 = lshr <8 x i8> %or, <i8 2, i8 2, i8 2, i8 2, i8 2, i8 2, i8 2, i8 2>
%and6 = and <8 x i8> %shr5, <i8 51, i8 51, i8 51, i8 51, i8 51, i8 51, i8 51, i8 51>
%or7 = or disjoint <8 x i8> %shl4, %and6
%and8 = shl <8 x i8> %or7, <i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1>
%shl9 = and <8 x i8> %and8, <i8 -86, i8 -86, i8 -86, i8 -86, i8 -86, i8 -86, i8 -86, i8 -86>
%shr10 = lshr <8 x i8> %or7, <i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1>
%and11 = and <8 x i8> %shr10, <i8 85, i8 85, i8 85, i8 85, i8 85, i8 85, i8 85, i8 85>
%or12 = or disjoint <8 x i8> %shl9, %and11
ret <8 x i8> %or12
}
)"};
static const char LLVMBitreversev8i16[]{R"(
define <8 x i16> @llvm_bitreverse_v8i16(<8 x i16> %A) {
entry:
%shl = shl <8 x i16> %A, <i16 8, i16 8, i16 8, i16 8, i16 8, i16 8, i16 8, i16 8>
%shr = lshr <8 x i16> %A, <i16 8, i16 8, i16 8, i16 8, i16 8, i16 8, i16 8, i16 8>
%or = or disjoint <8 x i16> %shl, %shr
%and2 = shl <8 x i16> %or, <i16 4, i16 4, i16 4, i16 4, i16 4, i16 4, i16 4, i16 4>
%shl3 = and <8 x i16> %and2, <i16 -3856, i16 -3856, i16 -3856, i16 -3856, i16 -3856, i16 -3856, i16 -3856, i16 -3856>
%shr4 = lshr <8 x i16> %or, <i16 4, i16 4, i16 4, i16 4, i16 4, i16 4, i16 4, i16 4>
%and5 = and <8 x i16> %shr4, <i16 3855, i16 3855, i16 3855, i16 3855, i16 3855, i16 3855, i16 3855, i16 3855>
%or6 = or disjoint <8 x i16> %shl3, %and5
%and7 = shl <8 x i16> %or6, <i16 2, i16 2, i16 2, i16 2, i16 2, i16 2, i16 2, i16 2>
%shl8 = and <8 x i16> %and7, <i16 -13108, i16 -13108, i16 -13108, i16 -13108, i16 -13108, i16 -13108, i16 -13108, i16 -13108>
%shr9 = lshr <8 x i16> %or6, <i16 2, i16 2, i16 2, i16 2, i16 2, i16 2, i16 2, i16 2>
%and10 = and <8 x i16> %shr9, <i16 13107, i16 13107, i16 13107, i16 13107, i16 13107, i16 13107, i16 13107, i16 13107>
%or11 = or disjoint <8 x i16> %shl8, %and10
%and12 = shl <8 x i16> %or11, <i16 1, i16 1, i16 1, i16 1, i16 1, i16 1, i16 1, i16 1>
%shl13 = and <8 x i16> %and12, <i16 -21846, i16 -21846, i16 -21846, i16 -21846, i16 -21846, i16 -21846, i16 -21846, i16 -21846>
%shr14 = lshr <8 x i16> %or11, <i16 1, i16 1, i16 1, i16 1, i16 1, i16 1, i16 1, i16 1>
%and15 = and <8 x i16> %shr14, <i16 21845, i16 21845, i16 21845, i16 21845, i16 21845, i16 21845, i16 21845, i16 21845>
%or16 = or disjoint <8 x i16> %shl13, %and15
ret <8 x i16> %or16
}
)"};
static const char LLVMBitreversev8i32[]{R"(
define <8 x i32> @llvm_bitreverse_v8i32(<8 x i32> %A) {
entry:
%shl = shl <8 x i32> %A, <i32 16, i32 16, i32 16, i32 16, i32 16, i32 16, i32 16, i32 16>
%shr = lshr <8 x i32> %A, <i32 16, i32 16, i32 16, i32 16, i32 16, i32 16, i32 16, i32 16>
%or = or disjoint <8 x i32> %shl, %shr
%and2 = shl <8 x i32> %or, <i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8>
%shl3 = and <8 x i32> %and2, <i32 -16711936, i32 -16711936, i32 -16711936, i32 -16711936, i32 -16711936, i32 -16711936, i32 -16711936, i32 -16711936>
%shr4 = lshr <8 x i32> %or, <i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8>
%and5 = and <8 x i32> %shr4, <i32 16711935, i32 16711935, i32 16711935, i32 16711935, i32 16711935, i32 16711935, i32 16711935, i32 16711935>
%or6 = or disjoint <8 x i32> %shl3, %and5
%and7 = shl <8 x i32> %or6, <i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4>
%shl8 = and <8 x i32> %and7, <i32 -252645136, i32 -252645136, i32 -252645136, i32 -252645136, i32 -252645136, i32 -252645136, i32 -252645136, i32 -252645136>
%shr9 = lshr <8 x i32> %or6, <i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4>
%and10 = and <8 x i32> %shr9, <i32 252645135, i32 252645135, i32 252645135, i32 252645135, i32 252645135, i32 252645135, i32 252645135, i32 252645135>
%or11 = or disjoint <8 x i32> %shl8, %and10
%and12 = shl <8 x i32> %or11, <i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2>
%shl13 = and <8 x i32> %and12, <i32 -858993460, i32 -858993460, i32 -858993460, i32 -858993460, i32 -858993460, i32 -858993460, i32 -858993460, i32 -858993460>
%shr14 = lshr <8 x i32> %or11, <i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2>
%and15 = and <8 x i32> %shr14, <i32 858993459, i32 858993459, i32 858993459, i32 858993459, i32 858993459, i32 858993459, i32 858993459, i32 858993459>
%or16 = or disjoint <8 x i32> %shl13, %and15
%and17 = shl <8 x i32> %or16, <i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1>
%shl18 = and <8 x i32> %and17, <i32 -1431655766, i32 -1431655766, i32 -1431655766, i32 -1431655766, i32 -1431655766, i32 -1431655766, i32 -1431655766, i32 -1431655766>
%shr19 = lshr <8 x i32> %or16, <i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1>
%and20 = and <8 x i32> %shr19, <i32 1431655765, i32 1431655765, i32 1431655765, i32 1431655765, i32 1431655765, i32 1431655765, i32 1431655765, i32 1431655765>
%or21 = or disjoint <8 x i32> %shl18, %and20
ret <8 x i32> %or21
}
)"};
static const char LLVMBitreversev8i64[]{R"(
define <8 x i64> @llvm_bitreverse_v8i64(<8 x i64> %A) {
entry:
%shl = shl <8 x i64> %A, <i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32>
%shr = lshr <8 x i64> %A, <i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32>
%or = or disjoint <8 x i64> %shl, %shr
%and2 = shl <8 x i64> %or, <i64 16, i64 16, i64 16, i64 16, i64 16, i64 16, i64 16, i64 16>
%shl3 = and <8 x i64> %and2, <i64 -281470681808896, i64 -281470681808896, i64 -281470681808896, i64 -281470681808896, i64 -281470681808896, i64 -281470681808896, i64 -281470681808896, i64 -281470681808896>
%shr4 = lshr <8 x i64> %or, <i64 16, i64 16, i64 16, i64 16, i64 16, i64 16, i64 16, i64 16>
%and5 = and <8 x i64> %shr4, <i64 281470681808895, i64 281470681808895, i64 281470681808895, i64 281470681808895, i64 281470681808895, i64 281470681808895, i64 281470681808895, i64 281470681808895>
%or6 = or disjoint <8 x i64> %shl3, %and5
%and7 = shl <8 x i64> %or6, <i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8>
%shl8 = and <8 x i64> %and7, <i64 -71777214294589696, i64 -71777214294589696, i64 -71777214294589696, i64 -71777214294589696, i64 -71777214294589696, i64 -71777214294589696, i64 -71777214294589696, i64 -71777214294589696>
%shr9 = lshr <8 x i64> %or6, <i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8>
%and10 = and <8 x i64> %shr9, <i64 71777214294589695, i64 71777214294589695, i64 71777214294589695, i64 71777214294589695, i64 71777214294589695, i64 71777214294589695, i64 71777214294589695, i64 71777214294589695>
%or11 = or disjoint <8 x i64> %shl8, %and10
%and12 = shl <8 x i64> %or11, <i64 4, i64 4, i64 4, i64 4, i64 4, i64 4, i64 4, i64 4>
%shl13 = and <8 x i64> %and12, <i64 -1085102592571150096, i64 -1085102592571150096, i64 -1085102592571150096, i64 -1085102592571150096, i64 -1085102592571150096, i64 -1085102592571150096, i64 -1085102592571150096, i64 -1085102592571150096>
%shr14 = lshr <8 x i64> %or11, <i64 4, i64 4, i64 4, i64 4, i64 4, i64 4, i64 4, i64 4>
%and15 = and <8 x i64> %shr14, <i64 1085102592571150095, i64 1085102592571150095, i64 1085102592571150095, i64 1085102592571150095, i64 1085102592571150095, i64 1085102592571150095, i64 1085102592571150095, i64 1085102592571150095>
%or16 = or disjoint <8 x i64> %shl13, %and15
%and17 = shl <8 x i64> %or16, <i64 2, i64 2, i64 2, i64 2, i64 2, i64 2, i64 2, i64 2>
%shl18 = and <8 x i64> %and17, <i64 -3689348814741910324, i64 -3689348814741910324, i64 -3689348814741910324, i64 -3689348814741910324, i64 -3689348814741910324, i64 -3689348814741910324, i64 -3689348814741910324, i64 -3689348814741910324>
%shr19 = lshr <8 x i64> %or16, <i64 2, i64 2, i64 2, i64 2, i64 2, i64 2, i64 2, i64 2>
%and20 = and <8 x i64> %shr19, <i64 3689348814741910323, i64 3689348814741910323, i64 3689348814741910323, i64 3689348814741910323, i64 3689348814741910323, i64 3689348814741910323, i64 3689348814741910323, i64 3689348814741910323>
%or21 = or disjoint <8 x i64> %shl18, %and20
%and22 = shl <8 x i64> %or21, <i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1>
%shl23 = and <8 x i64> %and22, <i64 -6148914691236517206, i64 -6148914691236517206, i64 -6148914691236517206, i64 -6148914691236517206, i64 -6148914691236517206, i64 -6148914691236517206, i64 -6148914691236517206, i64 -6148914691236517206>
%shr24 = lshr <8 x i64> %or21, <i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1>
%and25 = and <8 x i64> %shr24, <i64 6148914691236517205, i64 6148914691236517205, i64 6148914691236517205, i64 6148914691236517205, i64 6148914691236517205, i64 6148914691236517205, i64 6148914691236517205, i64 6148914691236517205>
%or26 = or disjoint <8 x i64> %shl23, %and25
ret <8 x i64> %or26
}
)"};
static const char LLVMBitreversev16i8[]{R"(
define <16 x i8> @llvm_bitreverse_v16i8(<16 x i8> %A) {
entry:
%shl = shl <16 x i8> %A, <i8 4, i8 4, i8 4, i8 4, i8 4, i8 4, i8 4, i8 4, i8 4, i8 4, i8 4, i8 4, i8 4, i8 4, i8 4, i8 4>
%shr = lshr <16 x i8> %A, <i8 4, i8 4, i8 4, i8 4, i8 4, i8 4, i8 4, i8 4, i8 4, i8 4, i8 4, i8 4, i8 4, i8 4, i8 4, i8 4>
%or = or disjoint <16 x i8> %shl, %shr
%and2 = shl <16 x i8> %or, <i8 2, i8 2, i8 2, i8 2, i8 2, i8 2, i8 2, i8 2, i8 2, i8 2, i8 2, i8 2, i8 2, i8 2, i8 2, i8 2>
%shl3 = and <16 x i8> %and2, <i8 -52, i8 -52, i8 -52, i8 -52, i8 -52, i8 -52, i8 -52, i8 -52, i8 -52, i8 -52, i8 -52, i8 -52, i8 -52, i8 -52, i8 -52, i8 -52>
%shr4 = lshr <16 x i8> %or, <i8 2, i8 2, i8 2, i8 2, i8 2, i8 2, i8 2, i8 2, i8 2, i8 2, i8 2, i8 2, i8 2, i8 2, i8 2, i8 2>
%and5 = and <16 x i8> %shr4, <i8 51, i8 51, i8 51, i8 51, i8 51, i8 51, i8 51, i8 51, i8 51, i8 51, i8 51, i8 51, i8 51, i8 51, i8 51, i8 51>
%or6 = or disjoint <16 x i8> %shl3, %and5
%and7 = shl <16 x i8> %or6, <i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1>
%shl8 = and <16 x i8> %and7, <i8 -86, i8 -86, i8 -86, i8 -86, i8 -86, i8 -86, i8 -86, i8 -86, i8 -86, i8 -86, i8 -86, i8 -86, i8 -86, i8 -86, i8 -86, i8 -86>
%shr9 = lshr <16 x i8> %or6, <i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1>
%and10 = and <16 x i8> %shr9, <i8 85, i8 85, i8 85, i8 85, i8 85, i8 85, i8 85, i8 85, i8 85, i8 85, i8 85, i8 85, i8 85, i8 85, i8 85, i8 85>
%or11 = or disjoint <16 x i8> %shl8, %and10
ret <16 x i8> %or11
}
)"};
static const char LLVMBitreversev16i16[]{R"(
define <16 x i16> @llvm_bitreverse_v16i16(<16 x i16> %A) {
entry:
%shl = shl <16 x i16> %A, <i16 8, i16 8, i16 8, i16 8, i16 8, i16 8, i16 8, i16 8, i16 8, i16 8, i16 8, i16 8, i16 8, i16 8, i16 8, i16 8>
%shr = lshr <16 x i16> %A, <i16 8, i16 8, i16 8, i16 8, i16 8, i16 8, i16 8, i16 8, i16 8, i16 8, i16 8, i16 8, i16 8, i16 8, i16 8, i16 8>
%or = or disjoint <16 x i16> %shl, %shr
%and2 = shl <16 x i16> %or, <i16 4, i16 4, i16 4, i16 4, i16 4, i16 4, i16 4, i16 4, i16 4, i16 4, i16 4, i16 4, i16 4, i16 4, i16 4, i16 4>
%shl3 = and <16 x i16> %and2, <i16 -3856, i16 -3856, i16 -3856, i16 -3856, i16 -3856, i16 -3856, i16 -3856, i16 -3856, i16 -3856, i16 -3856, i16 -3856, i16 -3856, i16 -3856, i16 -3856, i16 -3856, i16 -3856>
%shr4 = lshr <16 x i16> %or, <i16 4, i16 4, i16 4, i16 4, i16 4, i16 4, i16 4, i16 4, i16 4, i16 4, i16 4, i16 4, i16 4, i16 4, i16 4, i16 4>
%and5 = and <16 x i16> %shr4, <i16 3855, i16 3855, i16 3855, i16 3855, i16 3855, i16 3855, i16 3855, i16 3855, i16 3855, i16 3855, i16 3855, i16 3855, i16 3855, i16 3855, i16 3855, i16 3855>
%or6 = or disjoint <16 x i16> %shl3, %and5
%and7 = shl <16 x i16> %or6, <i16 2, i16 2, i16 2, i16 2, i16 2, i16 2, i16 2, i16 2, i16 2, i16 2, i16 2, i16 2, i16 2, i16 2, i16 2, i16 2>
%shl8 = and <16 x i16> %and7, <i16 -13108, i16 -13108, i16 -13108, i16 -13108, i16 -13108, i16 -13108, i16 -13108, i16 -13108, i16 -13108, i16 -13108, i16 -13108, i16 -13108, i16 -13108, i16 -13108, i16 -13108, i16 -13108>
%shr9 = lshr <16 x i16> %or6, <i16 2, i16 2, i16 2, i16 2, i16 2, i16 2, i16 2, i16 2, i16 2, i16 2, i16 2, i16 2, i16 2, i16 2, i16 2, i16 2>
%and10 = and <16 x i16> %shr9, <i16 13107, i16 13107, i16 13107, i16 13107, i16 13107, i16 13107, i16 13107, i16 13107, i16 13107, i16 13107, i16 13107, i16 13107, i16 13107, i16 13107, i16 13107, i16 13107>
%or11 = or disjoint <16 x i16> %shl8, %and10
%and12 = shl <16 x i16> %or11, <i16 1, i16 1, i16 1, i16 1, i16 1, i16 1, i16 1, i16 1, i16 1, i16 1, i16 1, i16 1, i16 1, i16 1, i16 1, i16 1>
%shl13 = and <16 x i16> %and12, <i16 -21846, i16 -21846, i16 -21846, i16 -21846, i16 -21846, i16 -21846, i16 -21846, i16 -21846, i16 -21846, i16 -21846, i16 -21846, i16 -21846, i16 -21846, i16 -21846, i16 -21846, i16 -21846>
%shr14 = lshr <16 x i16> %or11, <i16 1, i16 1, i16 1, i16 1, i16 1, i16 1, i16 1, i16 1, i16 1, i16 1, i16 1, i16 1, i16 1, i16 1, i16 1, i16 1>
%and15 = and <16 x i16> %shr14, <i16 21845, i16 21845, i16 21845, i16 21845, i16 21845, i16 21845, i16 21845, i16 21845, i16 21845, i16 21845, i16 21845, i16 21845, i16 21845, i16 21845, i16 21845, i16 21845>
%or16 = or disjoint <16 x i16> %shl13, %and15
ret <16 x i16> %or16
}
)"};
static const char LLVMBitreversev16i32[]{R"(
define <16 x i32> @llvm_bitreverse_v16i32(<16 x i32> %A) {
entry:
%shl = shl <16 x i32> %A, <i32 16, i32 16, i32 16, i32 16, i32 16, i32 16, i32 16, i32 16, i32 16, i32 16, i32 16, i32 16, i32 16, i32 16, i32 16, i32 16>
%shr = lshr <16 x i32> %A, <i32 16, i32 16, i32 16, i32 16, i32 16, i32 16, i32 16, i32 16, i32 16, i32 16, i32 16, i32 16, i32 16, i32 16, i32 16, i32 16>
%or = or disjoint <16 x i32> %shl, %shr
%and2 = shl <16 x i32> %or, <i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8>
%shl3 = and <16 x i32> %and2, <i32 -16711936, i32 -16711936, i32 -16711936, i32 -16711936, i32 -16711936, i32 -16711936, i32 -16711936, i32 -16711936, i32 -16711936, i32 -16711936, i32 -16711936, i32 -16711936, i32 -16711936, i32 -16711936, i32 -16711936, i32 -16711936>
%shr4 = lshr <16 x i32> %or, <i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8>
%and5 = and <16 x i32> %shr4, <i32 16711935, i32 16711935, i32 16711935, i32 16711935, i32 16711935, i32 16711935, i32 16711935, i32 16711935, i32 16711935, i32 16711935, i32 16711935, i32 16711935, i32 16711935, i32 16711935, i32 16711935, i32 16711935>
%or6 = or disjoint <16 x i32> %shl3, %and5
%and7 = shl <16 x i32> %or6, <i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4>
%shl8 = and <16 x i32> %and7, <i32 -252645136, i32 -252645136, i32 -252645136, i32 -252645136, i32 -252645136, i32 -252645136, i32 -252645136, i32 -252645136, i32 -252645136, i32 -252645136, i32 -252645136, i32 -252645136, i32 -252645136, i32 -252645136, i32 -252645136, i32 -252645136>
%shr9 = lshr <16 x i32> %or6, <i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4>
%and10 = and <16 x i32> %shr9, <i32 252645135, i32 252645135, i32 252645135, i32 252645135, i32 252645135, i32 252645135, i32 252645135, i32 252645135, i32 252645135, i32 252645135, i32 252645135, i32 252645135, i32 252645135, i32 252645135, i32 252645135, i32 252645135>
%or11 = or disjoint <16 x i32> %shl8, %and10
%and12 = shl <16 x i32> %or11, <i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2>
%shl13 = and <16 x i32> %and12, <i32 -858993460, i32 -858993460, i32 -858993460, i32 -858993460, i32 -858993460, i32 -858993460, i32 -858993460, i32 -858993460, i32 -858993460, i32 -858993460, i32 -858993460, i32 -858993460, i32 -858993460, i32 -858993460, i32 -858993460, i32 -858993460>
%shr14 = lshr <16 x i32> %or11, <i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2>
%and15 = and <16 x i32> %shr14, <i32 858993459, i32 858993459, i32 858993459, i32 858993459, i32 858993459, i32 858993459, i32 858993459, i32 858993459, i32 858993459, i32 858993459, i32 858993459, i32 858993459, i32 858993459, i32 858993459, i32 858993459, i32 858993459>
%or16 = or disjoint <16 x i32> %shl13, %and15
%and17 = shl <16 x i32> %or16, <i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1>
%shl18 = and <16 x i32> %and17, <i32 -1431655766, i32 -1431655766, i32 -1431655766, i32 -1431655766, i32 -1431655766, i32 -1431655766, i32 -1431655766, i32 -1431655766, i32 -1431655766, i32 -1431655766, i32 -1431655766, i32 -1431655766, i32 -1431655766, i32 -1431655766, i32 -1431655766, i32 -1431655766>
%shr19 = lshr <16 x i32> %or16, <i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1>
%and20 = and <16 x i32> %shr19, <i32 1431655765, i32 1431655765, i32 1431655765, i32 1431655765, i32 1431655765, i32 1431655765, i32 1431655765, i32 1431655765, i32 1431655765, i32 1431655765, i32 1431655765, i32 1431655765, i32 1431655765, i32 1431655765, i32 1431655765, i32 1431655765>
%or21 = or disjoint <16 x i32> %shl18, %and20
ret <16 x i32> %or21
}
)"};
static const char LLVMBitreversev16i64[]{R"(
define <16 x i64> @llvm_bitreverse_v16i64(<16 x i64> %A) {
entry:
%shl = shl <16 x i64> %A, <i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32>
%shr = lshr <16 x i64> %A, <i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32>
%or = or disjoint <16 x i64> %shl, %shr
%and2 = shl <16 x i64> %or, <i64 16, i64 16, i64 16, i64 16, i64 16, i64 16, i64 16, i64 16, i64 16, i64 16, i64 16, i64 16, i64 16, i64 16, i64 16, i64 16>
%shl3 = and <16 x i64> %and2, <i64 -281470681808896, i64 -281470681808896, i64 -281470681808896, i64 -281470681808896, i64 -281470681808896, i64 -281470681808896, i64 -281470681808896, i64 -281470681808896, i64 -281470681808896, i64 -281470681808896, i64 -281470681808896, i64 -281470681808896, i64 -281470681808896, i64 -281470681808896, i64 -281470681808896, i64 -281470681808896>
%shr4 = lshr <16 x i64> %or, <i64 16, i64 16, i64 16, i64 16, i64 16, i64 16, i64 16, i64 16, i64 16, i64 16, i64 16, i64 16, i64 16, i64 16, i64 16, i64 16>
%and5 = and <16 x i64> %shr4, <i64 281470681808895, i64 281470681808895, i64 281470681808895, i64 281470681808895, i64 281470681808895, i64 281470681808895, i64 281470681808895, i64 281470681808895, i64 281470681808895, i64 281470681808895, i64 281470681808895, i64 281470681808895, i64 281470681808895, i64 281470681808895, i64 281470681808895, i64 281470681808895>
%or6 = or disjoint <16 x i64> %shl3, %and5
%and7 = shl <16 x i64> %or6, <i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8>
%shl8 = and <16 x i64> %and7, <i64 -71777214294589696, i64 -71777214294589696, i64 -71777214294589696, i64 -71777214294589696, i64 -71777214294589696, i64 -71777214294589696, i64 -71777214294589696, i64 -71777214294589696, i64 -71777214294589696, i64 -71777214294589696, i64 -71777214294589696, i64 -71777214294589696, i64 -71777214294589696, i64 -71777214294589696, i64 -71777214294589696, i64 -71777214294589696>
%shr9 = lshr <16 x i64> %or6, <i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8>
%and10 = and <16 x i64> %shr9, <i64 71777214294589695, i64 71777214294589695, i64 71777214294589695, i64 71777214294589695, i64 71777214294589695, i64 71777214294589695, i64 71777214294589695, i64 71777214294589695, i64 71777214294589695, i64 71777214294589695, i64 71777214294589695, i64 71777214294589695, i64 71777214294589695, i64 71777214294589695, i64 71777214294589695, i64 71777214294589695>
%or11 = or disjoint <16 x i64> %shl8, %and10
%and12 = shl <16 x i64> %or11, <i64 4, i64 4, i64 4, i64 4, i64 4, i64 4, i64 4, i64 4, i64 4, i64 4, i64 4, i64 4, i64 4, i64 4, i64 4, i64 4>
%shl13 = and <16 x i64> %and12, <i64 -1085102592571150096, i64 -1085102592571150096, i64 -1085102592571150096, i64 -1085102592571150096, i64 -1085102592571150096, i64 -1085102592571150096, i64 -1085102592571150096, i64 -1085102592571150096, i64 -1085102592571150096, i64 -1085102592571150096, i64 -1085102592571150096, i64 -1085102592571150096, i64 -1085102592571150096, i64 -1085102592571150096, i64 -1085102592571150096, i64 -1085102592571150096>
%shr14 = lshr <16 x i64> %or11, <i64 4, i64 4, i64 4, i64 4, i64 4, i64 4, i64 4, i64 4, i64 4, i64 4, i64 4, i64 4, i64 4, i64 4, i64 4, i64 4>
%and15 = and <16 x i64> %shr14, <i64 1085102592571150095, i64 1085102592571150095, i64 1085102592571150095, i64 1085102592571150095, i64 1085102592571150095, i64 1085102592571150095, i64 1085102592571150095, i64 1085102592571150095, i64 1085102592571150095, i64 1085102592571150095, i64 1085102592571150095, i64 1085102592571150095, i64 1085102592571150095, i64 1085102592571150095, i64 1085102592571150095, i64 1085102592571150095>
%or16 = or disjoint <16 x i64> %shl13, %and15
%and17 = shl <16 x i64> %or16, <i64 2, i64 2, i64 2, i64 2, i64 2, i64 2, i64 2, i64 2, i64 2, i64 2, i64 2, i64 2, i64 2, i64 2, i64 2, i64 2>
%shl18 = and <16 x i64> %and17, <i64 -3689348814741910324, i64 -3689348814741910324, i64 -3689348814741910324, i64 -3689348814741910324, i64 -3689348814741910324, i64 -3689348814741910324, i64 -3689348814741910324, i64 -3689348814741910324, i64 -3689348814741910324, i64 -3689348814741910324, i64 -3689348814741910324, i64 -3689348814741910324, i64 -3689348814741910324, i64 -3689348814741910324, i64 -3689348814741910324, i64 -3689348814741910324>
%shr19 = lshr <16 x i64> %or16, <i64 2, i64 2, i64 2, i64 2, i64 2, i64 2, i64 2, i64 2, i64 2, i64 2, i64 2, i64 2, i64 2, i64 2, i64 2, i64 2>
%and20 = and <16 x i64> %shr19, <i64 3689348814741910323, i64 3689348814741910323, i64 3689348814741910323, i64 3689348814741910323, i64 3689348814741910323, i64 3689348814741910323, i64 3689348814741910323, i64 3689348814741910323, i64 3689348814741910323, i64 3689348814741910323, i64 3689348814741910323, i64 3689348814741910323, i64 3689348814741910323, i64 3689348814741910323, i64 3689348814741910323, i64 3689348814741910323>
%or21 = or disjoint <16 x i64> %shl18, %and20
%and22 = shl <16 x i64> %or21, <i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1>
%shl23 = and <16 x i64> %and22, <i64 -6148914691236517206, i64 -6148914691236517206, i64 -6148914691236517206, i64 -6148914691236517206, i64 -6148914691236517206, i64 -6148914691236517206, i64 -6148914691236517206, i64 -6148914691236517206, i64 -6148914691236517206, i64 -6148914691236517206, i64 -6148914691236517206, i64 -6148914691236517206, i64 -6148914691236517206, i64 -6148914691236517206, i64 -6148914691236517206, i64 -6148914691236517206>
%shr24 = lshr <16 x i64> %or21, <i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1>
%and25 = and <16 x i64> %shr24, <i64 6148914691236517205, i64 6148914691236517205, i64 6148914691236517205, i64 6148914691236517205, i64 6148914691236517205, i64 6148914691236517205, i64 6148914691236517205, i64 6148914691236517205, i64 6148914691236517205, i64 6148914691236517205, i64 6148914691236517205, i64 6148914691236517205, i64 6148914691236517205, i64 6148914691236517205, i64 6148914691236517205, i64 6148914691236517205>
%or26 = or disjoint <16 x i64> %shl23, %and25
ret <16 x i64> %or26
}
)"};
@@ -0,0 +1,109 @@
// clang-format off
/*
#include <stdlib.h>
#include <stdint.h>
#define MASK32LO 0x00000000FFFFFFFFLLU
#define MASK16LO 0x0000FFFF0000FFFFLLU
#define MASK8LO 0x00FF00FF00FF00FFLLU
#define MASK4LO 0x0F0F0F0F0F0F0F0FLLU
#define MASK2LO 0x3333333333333333LLU
#define MASK1LO 0x5555555555555555LLU
#define SWAP32(X,TYPE) (((X&((TYPE) MASK32LO))<<32) | (((X)>>32)&((TYPE) MASK32LO)))
#define SWAP16(X,TYPE) (((X&((TYPE) MASK16LO))<<16) | (((X)>>16)&((TYPE) MASK16LO)))
#define SWAP8(X,TYPE) (((X&((TYPE) MASK8LO ))<< 8) | (((X)>> 8)&((TYPE) MASK8LO)))
#define SWAP4(X,TYPE) (((X&((TYPE) MASK4LO ))<< 4) | (((X)>> 4)&((TYPE) MASK4LO)))
#define SWAP2(X,TYPE) (((X&((TYPE) MASK2LO ))<< 2) | (((X)>> 2)&((TYPE) MASK2LO)))
#define SWAP1(X,TYPE) (((X&((TYPE) MASK1LO ))<< 1) | (((X)>> 1)&((TYPE) MASK1LO)))
///////////////////////////////////////////////////////////////////////////////////////
// scalar
///////////////////////////////////////////////////////////////////////////////////////
uint8_t llvm_bitreverse_i8(uint8_t A) {
A=SWAP4(A,uint8_t);
A=SWAP2(A,uint8_t);
A=SWAP1(A,uint8_t);
return A;
}
uint16_t llvm_bitreverse_i16(uint16_t A) {
A=SWAP8(A,uint16_t);
A=SWAP4(A,uint16_t);
A=SWAP2(A,uint16_t);
A=SWAP1(A,uint16_t);
return A;
}
uint32_t llvm_bitreverse_i32(uint32_t A) {
A=SWAP16(A,uint32_t);
A=SWAP8(A,uint32_t);
A=SWAP4(A,uint32_t);
A=SWAP2(A,uint32_t);
A=SWAP1(A,uint32_t);
return A;
}
uint64_t llvm_bitreverse_i64(uint64_t A) {
A=SWAP32(A,uint64_t);
A=SWAP16(A,uint64_t);
A=SWAP8(A,uint64_t);
A=SWAP4(A,uint64_t);
A=SWAP2(A,uint64_t);
A=SWAP1(A,uint64_t);
return A;
}
///////////////////////////////////////////////////////////////////////////////////////
// vector
///////////////////////////////////////////////////////////////////////////////////////
#define GEN_VECTOR_BITREVERSE(LENGTH) \
typedef uint8_t uint8_t ## LENGTH __attribute__((ext_vector_type(LENGTH))); \
typedef uint16_t uint16_t ## LENGTH __attribute__((ext_vector_type(LENGTH))); \
typedef uint32_t uint32_t ## LENGTH __attribute__((ext_vector_type(LENGTH))); \
typedef uint64_t uint64_t ## LENGTH __attribute__((ext_vector_type(LENGTH))); \
\
uint8_t ## LENGTH llvm_bitreverse_v ## LENGTH ## i8 (uint8_t ## LENGTH A) { \
A=SWAP4(A,uint8_t); \
A=SWAP2(A,uint8_t); \
A=SWAP1(A,uint8_t); \
return A; \
} \
\
uint16_t ## LENGTH llvm_bitreverse_v ## LENGTH ## i16(uint16_t ## LENGTH A) { \
A=SWAP8(A,uint16_t); \
A=SWAP4(A,uint16_t); \
A=SWAP2(A,uint16_t); \
A=SWAP1(A,uint16_t); \
return A; \
} \
\
uint32_t ## LENGTH llvm_bitreverse_v ## LENGTH ## i32(uint32_t ## LENGTH A) { \
A=SWAP16(A,uint32_t); \
A=SWAP8(A,uint32_t); \
A=SWAP4(A,uint32_t); \
A=SWAP2(A,uint32_t); \
A=SWAP1(A,uint32_t); \
return A; \
} \
\
uint64_t ## LENGTH llvm_bitreverse_v ## LENGTH ## i64(uint64_t ## LENGTH A) { \
A=SWAP32(A,uint64_t); \
A=SWAP16(A,uint64_t); \
A=SWAP8(A,uint64_t); \
A=SWAP4(A,uint64_t); \
A=SWAP2(A,uint64_t); \
A=SWAP1(A,uint64_t); \
return A; \
}
GEN_VECTOR_BITREVERSE(2)
GEN_VECTOR_BITREVERSE(3)
GEN_VECTOR_BITREVERSE(4)
GEN_VECTOR_BITREVERSE(8)
GEN_VECTOR_BITREVERSE(16)
*/
// clang-format on
@@ -0,0 +1,29 @@
// clang-format off
/*
#include <stdlib.h>
#define MASK2LO 0x3333333333333333LLU
#define MASK1LO 0x5555555555555555LLU
#define SWAP2(X,TYPE) (((X&((TYPE) MASK2LO ))<< 2) | (((X)>> 2)&((TYPE) MASK2LO)))
#define SWAP1(X,TYPE) (((X&((TYPE) MASK1LO ))<< 1) | (((X)>> 1)&((TYPE) MASK1LO)))
#define uint2_t _BitInt(2)
#define uint4_t _BitInt(4)
///////////////////////////////////////////////////////////////////////////////////////
// scalar
///////////////////////////////////////////////////////////////////////////////////////
uint2_t llvm_bitreverse_i2(uint2_t A) {
A=SWAP1(A,uint2_t);
return A;
}
uint4_t llvm_bitreverse_i4(uint4_t A) {
A=SWAP2(A,uint4_t);
A=SWAP1(A,uint4_t);
return A;
}
*/
// clang-format on
@@ -0,0 +1,114 @@
//===- LLVMSPIRVOpts.cpp - Defines LLVM/SPIR-V options ----------*- C++ -*-===//
//
// The LLVM/SPIR-V Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2021 Intel Corporation. All rights reserved.
//
// 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 Advanced Micro Devices, Inc., 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.
//
//===----------------------------------------------------------------------===//
/// \file
///
/// This file provides definitions for LLVM/SPIR-V Translator's CLI
/// functionality.
///
//===----------------------------------------------------------------------===//
#include "LLVMSPIRVOpts.h"
#include "SPIRVEnum.h"
#include <llvm/ADT/SmallVector.h>
#include <llvm/ADT/StringRef.h>
#include <llvm/IR/IntrinsicInst.h>
#include <optional>
using namespace llvm;
using namespace SPIRV;
void TranslatorOpts::enableAllExtensions() {
#define EXT(X) ExtStatusMap[ExtensionID::X] = true;
#include "LLVMSPIRVExtensions.inc"
#undef EXT
}
bool TranslatorOpts::isUnknownIntrinsicAllowed(
IntrinsicInst *II) const noexcept {
if (!SPIRVAllowUnknownIntrinsics.has_value())
return false;
const auto &IntrinsicPrefixList = SPIRVAllowUnknownIntrinsics.value();
StringRef IntrinsicName = II->getCalledOperand()->getName();
for (const auto &Prefix : IntrinsicPrefixList) {
if (IntrinsicName.starts_with(Prefix)) // Also true if `Prefix` is empty
return true;
}
return false;
}
bool TranslatorOpts::isSPIRVAllowUnknownIntrinsicsEnabled() const noexcept {
return SPIRVAllowUnknownIntrinsics.has_value();
}
void TranslatorOpts::setSPIRVAllowUnknownIntrinsics(
TranslatorOpts::ArgList IntrinsicPrefixList) noexcept {
SPIRVAllowUnknownIntrinsics = IntrinsicPrefixList;
}
std::vector<std::string> TranslatorOpts::getAllowedSPIRVExtensionNames(
std::function<bool(SPIRV::ExtensionID)> &Filter) const {
std::vector<std::string> AllowExtNames;
AllowExtNames.reserve(ExtStatusMap.size());
for (const auto &It : ExtStatusMap) {
if (!It.second || !Filter(It.first))
continue;
std::string ExtName;
SPIRVMap<ExtensionID, std::string>::find(It.first, &ExtName);
AllowExtNames.emplace_back(ExtName);
}
return AllowExtNames;
}
bool TranslatorOpts::validateFnVarOpts() const {
if (getFnVarCategory() == std::nullopt &&
(getFnVarFamily() != std::nullopt || getFnVarArch() != std::nullopt)) {
errs() << "FnVar: Device category must be specified if the family or "
"architecture are specified.";
return false;
}
if (getFnVarFamily() == std::nullopt && getFnVarArch() != std::nullopt) {
errs() << "FnVar: Device family must be specified if the architecture is "
"specified.";
return false;
}
if (getFnVarTarget() == std::nullopt && !getFnVarFeatures().empty()) {
errs() << "Device target must be specified if the features are specified.";
return false;
}
return true;
}
@@ -0,0 +1,249 @@
//===- LLVMSaddWithOverflow.h - implementation of llvm.sadd.with.overflow -===//
//
// The LLVM/SPIRV Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2020 Intel Corporation. All rights reserved.
//
// 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 Intel Corporation, 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.
//
//===----------------------------------------------------------------------===//
//
// This file implements lowering of llvm.sadd.with.overflow.* into basic LLVM
// operations.
//
//===----------------------------------------------------------------------===//
// The IR below is slightly manually modified IR which was produced by Clang
// from the C++ code below. The modifications include:
// - adapting the return value, i.e. replacing `store` instructions for the c
// and o arguments with `insertvalue` instructions.
// - changing type of the `phi` instruction in the last basic block from `i8`
// to `i1`. That also requires change of the argument of the `phi` instruction
// and allowed to remove an unnecessary `sext` instruction.
//
// #include <stdlib.h>
// #include <stdint.h>
//
// const unsigned short i16_abs_pos_max = 0x7FFF; // 32767;
// const unsigned short i16_abs_neg_max = 0x8000; // 32768;
//
// void llvm_sadd_with_overflow_i16(int16_t a, int16_t b, int16_t& c, bool& o) {
// bool overflow = false;
// bool both_pos = (a>=0 && b>=0);
// bool both_neg = (a<0 && b<0);
// if (both_pos || both_neg) {
// // 32-bit integers are always supported in SPIR-V
// uint32_t x = (uint32_t)abs(a) + (uint32_t)abs(b);
// if (both_pos && x > i16_abs_pos_max ||
// both_neg && x > i16_abs_neg_max) {
// overflow = true;
// }
// }
// c = a + b;
// o = overflow;
// }
//
// const uint32_t i32_abs_pos_max = 0x7FFFFFFF; // 2147483647
// const uint32_t i32_abs_neg_max = 0x80000000; // 2147483648
// const int32_t i32_min = 0x80000000; // -2147483648
//
// void llvm_sadd_with_overflow_i32(int32_t a, int32_t b, int32_t& c, bool& o) {
// bool overflow = false;
// bool both_pos = (a>=0 && b>=0);
// bool both_neg = (a<0 && b<0);
// // if a or b is the most negative number we can't get its absolute value,
// // because it is out of range.
// if (both_neg && (a == i32_min || b == i32_min))
// overflow = true;
// else if (both_pos || both_neg) {
// uint32_t x = (uint32_t)abs(a) + (uint32_t)abs(b);
// if (both_pos && x > i32_abs_pos_max ||
// both_neg && x > 2147483648U) {
// overflow = true;
// }
// }
// c = a + b;
// o = overflow;
// }
//
// const uint64_t i64_abs_pos_max = 0x7fffffffffffffff; // 9223372036854775807
// const uint64_t i64_abs_neg_max = 0x8000000000000000; // 9223372036854775808
// const int64_t i64_min = 0x8000000000000000; // -9223372036854775808
//
// void llvm_sadd_with_overflow_i64(int64_t a, int64_t b, int64_t& c, bool& o) {
// bool overflow = false;
// bool both_pos = (a>=0 && b>=0);
// bool both_neg = (a<0 && b<0);
// // if a or b is the most negative number we can't get its absolute value,
// // because it is out of range.
// if (both_neg && (a == i64_min || b == i64_min))
// overflow = true;
// else if (both_pos || both_neg) {
// uint64_t x = (uint64_t)abs(a) + (uint64_t)abs(b);
// if (both_pos && x > i64_abs_pos_max ||
// both_neg && x > i64_abs_neg_max) {
// overflow = true;
// }
// }
// c = a + b;
// o = overflow;
// }
//
// const unsigned int abs_pos_max = 2147483647;
// const unsigned int abs_neg_max = 2147483648;
//
// void llvm_sadd_with_overflow_i32(int a, int b, int& c, bool& o) {
// bool overflow = false;
// bool both_pos = (a>=0 && b>=0);
// bool both_neg = (a<0 && b<0);
// if (both_pos || both_neg) {
// unsigned int x = (unsigned int)abs(a) + (unsigned int)abs(b);
// if (both_pos && x > abs_pos_max ||
// both_neg && x > abs_neg_max) {
// overflow = true;
// }
// }
// c = a + b;
// o = overflow;
// }
// Clang options: -emit-llvm -O2 -g0 -fno-discard-value-names
static const char LLVMSaddWithOverflow[]{R"(
define spir_func { i16, i1 } @llvm_sadd_with_overflow_i16(i16 %a, i16 %b) {
entry:
%conv = sext i16 %a to i32
%conv1 = sext i16 %b to i32
%0 = or i16 %b, %a
%1 = icmp sgt i16 %0, -1
%2 = and i16 %b, %a
%3 = icmp slt i16 %2, 0
%brmerge = or i1 %1, %3
br i1 %brmerge, label %if.then, label %if.end21
if.then: ; preds = %entry
%4 = icmp slt i32 %conv, 0
%neg = sub nsw i32 0, %conv
%5 = select i1 %4, i32 %neg, i32 %conv
%6 = icmp slt i32 %conv1, 0
%neg39 = sub nsw i32 0, %conv1
%7 = select i1 %6, i32 %neg39, i32 %conv1
%add = add nuw nsw i32 %7, %5
%cmp15 = icmp ugt i32 %add, 32767
%or.cond = and i1 %1, %cmp15
%cmp19 = icmp ugt i32 %add, 32768
%or.cond28 = and i1 %3, %cmp19
%or.cond40 = or i1 %or.cond, %or.cond28
br label %if.end21
if.end21: ; preds = %if.then, %entry
%overflow = phi i1 [ 0, %entry ], [ %or.cond40, %if.then ]
%add24 = add i16 %b, %a
%agg = insertvalue {i16, i1} poison, i16 %add24, 0
%res = insertvalue {i16, i1} %agg, i1 %overflow, 1
ret {i16, i1} %res
}
define spir_func { i32, i1 } @llvm_sadd_with_overflow_i32(i32 %a, i32 %b) {
entry:
%0 = or i32 %b, %a
%1 = icmp sgt i32 %0, -1
%2 = and i32 %b, %a
%3 = icmp slt i32 %2, 0
br i1 %3, label %land.lhs.true, label %if.else
land.lhs.true: ; preds = %entry
%cmp7 = icmp eq i32 %a, -2147483648
%cmp8 = icmp eq i32 %b, -2147483648
%or.cond = or i1 %cmp7, %cmp8
br i1 %or.cond, label %if.end23, label %if.then12
if.else: ; preds = %entry
br i1 %1, label %if.then12, label %if.end23
if.then12: ; preds = %land.lhs.true, %if.else
%4 = icmp slt i32 %a, 0
%neg = sub nsw i32 0, %a
%5 = select i1 %4, i32 %neg, i32 %a
%6 = icmp slt i32 %b, 0
%neg42 = sub nsw i32 0, %b
%7 = select i1 %6, i32 %neg42, i32 %b
%add = add nuw i32 %7, %5
%cmp16 = icmp slt i32 %add, 0
%or.cond27 = and i1 %1, %cmp16
%cmp20 = icmp ugt i32 %add, -2147483648
%or.cond28 = and i1 %3, %cmp20
%or.cond43 = or i1 %or.cond27, %or.cond28
br label %if.end23
if.end23: ; preds = %if.then12, %if.else, %land.lhs.true
%overflow = phi i1 [ 1, %land.lhs.true ], [ 0, %if.else ], [ %or.cond43, %if.then12 ]
%add24 = add nsw i32 %b, %a
%agg = insertvalue {i32, i1} poison, i32 %add24, 0
%res = insertvalue {i32, i1} %agg, i1 %overflow, 1
ret {i32, i1} %res
}
define spir_func { i64, i1 } @llvm_sadd_with_overflow_i64(i64 %a, i64 %b) {
entry:
%0 = or i64 %b, %a
%1 = icmp sgt i64 %0, -1
%2 = and i64 %b, %a
%3 = icmp slt i64 %2, 0
br i1 %3, label %land.lhs.true, label %if.else
land.lhs.true: ; preds = %entry
%cmp7 = icmp eq i64 %a, -9223372036854775808
%cmp8 = icmp eq i64 %b, -9223372036854775808
%or.cond = or i1 %cmp7, %cmp8
br i1 %or.cond, label %if.end23, label %if.then12
if.else: ; preds = %entry
br i1 %1, label %if.then12, label %if.end23
if.then12: ; preds = %land.lhs.true, %if.else
%neg.i = sub nsw i64 0, %a
%abscond.i = icmp slt i64 %a, 0
%abs.i = select i1 %abscond.i, i64 %neg.i, i64 %a
%neg.i43 = sub nsw i64 0, %b
%abscond.i44 = icmp slt i64 %b, 0
%abs.i45 = select i1 %abscond.i44, i64 %neg.i43, i64 %b
%add = add nuw i64 %abs.i45, %abs.i
%cmp16 = icmp slt i64 %add, 0
%or.cond27 = and i1 %1, %cmp16
%cmp20 = icmp ugt i64 %add, -9223372036854775808
%or.cond28 = and i1 %3, %cmp20
%or.cond42 = or i1 %or.cond27, %or.cond28
br label %if.end23
if.end23: ; preds = %if.then12, %if.else, %land.lhs.true
%overflow = phi i1 [ 1, %land.lhs.true ], [ 0, %if.else ], [ %or.cond42, %if.then12 ]
%add24 = add nsw i64 %b, %a
%agg = insertvalue {i64, i1} poison, i64 %add24, 0
%res = insertvalue {i64, i1} %agg, i1 %overflow, 1
ret {i64, i1} %res
}
)"};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,191 @@
//===- LLVMToSPIRVDbgTran.h - Converts LLVM DebugInfo to SPIR-V -*- C++ -*-===//
//
// The LLVM/SPIR-V Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2018 Intel Corporation. All rights reserved.
//
// 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 Intel Corporation, 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.
//
//===----------------------------------------------------------------------===//
//
// This file implements translation of debug info from LLVM metadata to SPIR-V
//
//===----------------------------------------------------------------------===//
#ifndef LLVMTOSPIRVDBGTRAN_HPP_
#define LLVMTOSPIRVDBGTRAN_HPP_
#include "SPIRVModule.h"
#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/Module.h"
using namespace llvm;
namespace SPIRV {
class LLVMToSPIRVBase;
class LLVMToSPIRVDbgTran {
public:
typedef std::vector<SPIRVWord> SPIRVWordVec;
LLVMToSPIRVDbgTran(Module *TM = nullptr, SPIRVModule *TBM = nullptr,
LLVMToSPIRVBase *Writer = nullptr)
: BM(TBM), M(TM), SPIRVWriter(Writer), VoidT(nullptr),
DebugInfoNone(nullptr) {}
void transDebugMetadata();
void setModule(Module *Mod) { M = Mod; }
// Mixing translation of regular instructions and debug info creates a mess.
// To avoid it we translate debug info intrinsics in two steps:
// 1. First time we meet debug info intrinsic during translation of a basic
// block. At this time we create corresponding SPIRV debug info instruction,
// but with dummy operands. Doing so we a) map llvm value to spirv value,
// b) get a place for SPIRV debug info intrinsic in SPIRV basic block.
// We also remember all debug intrinsics.
SPIRVValue *createDebugDeclarePlaceholder(const DbgVariableIntrinsic *DbgDecl,
SPIRVBasicBlock *BB);
SPIRVValue *createDebugValuePlaceholder(const DbgVariableIntrinsic *DbgValue,
SPIRVBasicBlock *BB);
private:
// 2. After translation of all regular instructions we deal with debug info.
// We iterate over debug intrinsics stored on the first step, get its mapped
// SPIRV instruction and tweak the operands.
void finalizeDebugDeclare(const DbgVariableIntrinsic *DbgDecl);
void finalizeDebugValue(const DbgVariableIntrinsic *DbgValue);
// Emit DebugScope and OpLine instructions
void transLocationInfo();
// Dispatcher
SPIRVEntry *transDbgEntry(const MDNode *DIEntry);
SPIRVEntry *transDbgEntryImpl(const MDNode *MDN);
// Helper methods
SPIRVType *getVoidTy();
SPIRVType *getInt32Ty();
SPIRVEntry *getScope(DIScope *SR);
SPIRVEntry *getGlobalVariable(const DIGlobalVariable *GV);
inline bool isNonSemanticDebugInfo();
void transformToConstant(std::vector<SPIRVWord> &Ops,
std::vector<SPIRVWord> Idxs);
// No debug info
SPIRVEntry *getDebugInfoNone();
SPIRVId getDebugInfoNoneId();
// Compilation unit
SPIRVEntry *transDbgCompileUnit(const DICompileUnit *CU);
/// The following methods (till the end of the file) implement translation
/// of debug instrtuctions described in the spec.
// Types
SPIRVEntry *transDbgBaseType(const DIBasicType *BT);
SPIRVEntry *transDbgPointerType(const DIDerivedType *PT);
SPIRVEntry *transDbgQualifiedType(const DIDerivedType *QT);
SPIRVEntry *transDbgArrayType(const DICompositeType *AT);
SPIRVEntry *transDbgArrayTypeOpenCL(const DICompositeType *AT);
SPIRVEntry *transDbgArrayTypeNonSemantic(const DICompositeType *AT);
SPIRVEntry *transDbgArrayTypeDynamic(const DICompositeType *AT);
SPIRVEntry *transDbgSubrangeType(const DISubrange *ST);
SPIRVEntry *transDbgStringType(const DIStringType *ST);
SPIRVEntry *transDbgTypeDef(const DIDerivedType *D);
SPIRVEntry *transDbgSubroutineType(const DISubroutineType *FT);
SPIRVEntry *transDbgEnumType(const DICompositeType *ET);
SPIRVEntry *transDbgCompositeType(const DICompositeType *CT);
SPIRVEntry *transDbgMemberType(const DIDerivedType *MT);
SPIRVEntry *transDbgMemberTypeOpenCL(const DIDerivedType *MT);
SPIRVEntry *transDbgMemberTypeNonSemantic(const DIDerivedType *MT);
SPIRVEntry *transDbgInheritance(const DIDerivedType *DT);
SPIRVEntry *transDbgPtrToMember(const DIDerivedType *DT);
// Templates
SPIRVEntry *transDbgTemplateParams(DITemplateParameterArray TPA,
const SPIRVEntry *Target);
SPIRVEntry *transDbgTemplateParameter(const DITemplateParameter *TP);
SPIRVEntry *
transDbgTemplateTemplateParameter(const DITemplateValueParameter *TP);
SPIRVEntry *transDbgTemplateParameterPack(const DITemplateValueParameter *TP);
// Global objects
SPIRVEntry *transDbgGlobalVariable(const DIGlobalVariable *GV);
SPIRVEntry *transDbgFunction(const DISubprogram *Func);
SPIRVEntry *transDbgFuncDefinition(SPIRVValue *SPVFunc, SPIRVEntry *DbgFunc);
SPIRVEntry *transDbgEntryPoint(const DISubprogram *Func, SPIRVEntry *DbgFunc);
// Location information
SPIRVEntry *transDbgScope(const DIScope *S);
SPIRVEntry *transDebugLoc(const DebugLoc &Loc, SPIRVBasicBlock *BB,
SPIRVInstruction *InsertBefore = nullptr);
SPIRVEntry *transDbgInlinedAt(const DILocation *D);
SPIRVEntry *transDbgInlinedAtNonSemanticShader200(const DILocation *D);
template <class T> SPIRVExtInst *getSource(const T *DIEntry);
SPIRVEntry *transDbgFileType(const DIFile *F);
// Generate instructions recording identifier and file where debug information
// was split to
void generateBuildIdentifierAndStoragePath(const DICompileUnit *DIEntry);
// Local Variables
SPIRVEntry *transDbgLocalVariable(const DILocalVariable *Var);
// DWARF expressions
SPIRVEntry *transDbgExpression(const DIExpression *Expr);
// Imported declarations and modules
SPIRVEntry *transDbgImportedEntry(const DIImportedEntity *IE);
// A module in programming language. Example - Fortran module, clang module.
SPIRVEntry *transDbgModule(const DIModule *IE);
// Flags
SPIRVWord mapDebugFlags(DINode::DIFlags DFlags);
SPIRVWord transDebugFlags(const DINode *DN);
SPIRVModule *BM;
Module *M;
LLVMToSPIRVBase *SPIRVWriter;
std::unordered_map<const MDNode *, SPIRVEntry *> MDMap;
std::unordered_map<std::string, SPIRVExtInst *> FileMap;
DebugInfoFinder DIF;
SPIRVType *VoidT = nullptr;
SPIRVType *Int32T = nullptr;
SPIRVEntry *DebugInfoNone;
std::unordered_map<const DICompileUnit *, SPIRVExtInst *> SPIRVCUMap;
std::vector<const DbgVariableIntrinsic *> DbgDeclareIntrinsics;
std::vector<const DbgVariableIntrinsic *> DbgValueIntrinsics;
inline static SPIRVExtInst *BuildIdentifierInsn{nullptr};
inline static SPIRVExtInst *StoragePathInsn{nullptr};
}; // class LLVMToSPIRVDbgTran
} // namespace SPIRV
#endif // LLVMTOSPIRVDBGTRAN_HPP_
@@ -0,0 +1,95 @@
//===---------------------- FunctionDescriptor.cpp -----------------------===//
//
// SPIR Tools
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
/*
* Contributed by: Intel Corporation.
*/
#include "FunctionDescriptor.h"
#include "ParameterType.h"
#include <sstream>
namespace SPIR {
std::string FunctionDescriptor::nullString() {
return std::string("<invalid>");
}
std::string FunctionDescriptor::toString() const {
std::stringstream Stream;
if (isNull()) {
return FunctionDescriptor::nullString();
}
Stream << Name << "(";
size_t ParamCount = Parameters.size();
if (ParamCount > 0) {
for (size_t I = 0; I < ParamCount - 1; ++I)
Stream << Parameters[I]->toString() << ", ";
Stream << Parameters[ParamCount - 1]->toString();
}
Stream << ")";
return Stream.str();
}
static bool equal(const TypeVector &L, const TypeVector &R) {
if (&L == &R)
return true;
if (L.size() != R.size())
return false;
TypeVector::const_iterator Itl = L.begin(), Itr = R.begin(), Endl = L.end();
while (Itl != Endl) {
if (!(*Itl)->equals(*Itr))
return false;
++Itl;
++Itr;
}
return true;
}
//
// FunctionDescriptor
//
bool FunctionDescriptor::operator==(const FunctionDescriptor &That) const {
if (this == &That)
return true;
if (Name != That.Name)
return false;
return equal(Parameters, That.Parameters);
}
bool FunctionDescriptor::operator<(const FunctionDescriptor &That) const {
int StrCmp = Name.compare(That.Name);
if (StrCmp)
return (StrCmp < 0);
size_t Len = Parameters.size(), ThatLen = That.Parameters.size();
if (Len != ThatLen)
return Len < ThatLen;
TypeVector::const_iterator It = Parameters.begin(), E = Parameters.end(),
Thatit = That.Parameters.begin();
while (It != E) {
int Cmp = (*It)->toString().compare((*Thatit)->toString());
if (Cmp)
return (Cmp < 0);
++Thatit;
++It;
}
return false;
}
bool FunctionDescriptor::isNull() const {
return (Name.empty() && Parameters.empty());
}
FunctionDescriptor FunctionDescriptor::null() {
FunctionDescriptor Fd;
Fd.Name = "";
return Fd;
}
} // namespace SPIR
@@ -0,0 +1,55 @@
//===----------------------- FunctionDescriptor.h ------------------------===//
//
// SPIR Tools
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
/*
* Contributed by: Intel Corporation.
*/
#ifndef SPIRV_MANGLER_FUNCTIONDESCRIPTOR_H
#define SPIRV_MANGLER_FUNCTIONDESCRIPTOR_H
#include "ParameterType.h"
#include "Refcount.h"
#include <string>
#include <vector>
namespace SPIR {
typedef std::vector<RefCount<ParamType>> TypeVector;
struct FunctionDescriptor {
/// @brief Returns a human readable string representation of the function's
/// prototype.
/// @returns std::string representing the function's prototype.
std::string toString() const;
/// The name of the function (stripped).
std::string Name;
/// Parameter list of the function.
TypeVector Parameters;
bool operator==(const FunctionDescriptor &) const;
/// @brief Enables function descriptors to serve as keys in stl maps.
bool operator<(const FunctionDescriptor &) const;
bool isNull() const;
/// @brief Create a singular value, that represents a 'null'
/// FunctionDescriptor.
static FunctionDescriptor null();
static std::string nullString();
};
template <typename T>
std::ostream &operator<<(T &O, const SPIR::FunctionDescriptor &Fd) {
O << Fd.toString();
return O;
}
} // namespace SPIR
#endif // SPIRV_MANGLER_FUNCTIONDESCRIPTOR_H
@@ -0,0 +1,230 @@
//===--------------------------- Mangler.cpp -----------------------------===//
//
// SPIR Tools
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
/*
* Contributed by: Intel Corporation.
*/
#include "FunctionDescriptor.h"
#include "ManglingUtils.h"
#include "NameMangleAPI.h"
#include "ParameterType.h"
#include <algorithm>
#include <sstream>
#include <string>
#include <unordered_map>
// According to IA64 name mangling spec,
// builtin vector types should not be substituted
// This is a workaround till this gets fixed in CLang
#define ENABLE_MANGLER_VECTOR_SUBSTITUTION 1
namespace SPIR {
class MangleVisitor : public TypeVisitor {
public:
MangleVisitor(SPIRversion Ver, std::stringstream &S)
: TypeVisitor(Ver), Stream(S), SeqId(0) {}
//
// mangle substitution methods
//
void mangleSequenceID(unsigned SeqID) {
if (SeqID == 1)
Stream << '0';
else if (SeqID > 1) {
std::string Bstr;
std::string Charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
SeqID--;
Bstr.reserve(7);
for (; SeqID != 0; SeqID /= 36)
Bstr += Charset.substr(SeqID % 36, 1);
std::reverse(Bstr.begin(), Bstr.end());
Stream << Bstr;
}
Stream << '_';
}
bool mangleSubstitution(const ParamType *Type, std::string TypeStr) {
size_t Fpos;
std::stringstream ThistypeStr;
ThistypeStr << TypeStr;
if ((Fpos = Stream.str().find(TypeStr)) != std::string::npos) {
const char *NType;
if (const PointerType *P = SPIR::dynCast<PointerType>(Type)) {
ThistypeStr << getPointeeMangling(P->getPointee());
}
#if defined(ENABLE_MANGLER_VECTOR_SUBSTITUTION)
else if (const VectorType *PVec = SPIR::dynCast<VectorType>(Type)) {
if ((NType = mangledPrimitiveStringfromName(
PVec->getScalarType()->toString())))
ThistypeStr << NType;
}
#endif
std::unordered_map<std::string, unsigned>::iterator I =
Substitutions.find(ThistypeStr.str());
if (I == Substitutions.end())
return false;
unsigned SeqID = I->second;
Stream << 'S';
mangleSequenceID(SeqID);
return true;
}
return false;
}
//
// Visit methods
//
MangleError visit(const PrimitiveType *T) override {
MangleError Me = MANGLE_SUCCESS;
std::string MangledPrimitive =
std::string(mangledPrimitiveString(T->getPrimitive()));
#if defined(SPIRV_SPIR20_MANGLING_REQUIREMENTS)
Stream << MangledPrimitive;
#else
// Builtin primitives such as int are not substitution candidates, but
// all other primitives are. Even though most of these do not appear
// repeatedly in builtin function signatures, we need to track them in
// the substitution map.
if (T->getPrimitive() >= PRIMITIVE_STRUCT_FIRST) {
if (!mangleSubstitution(T, MangledPrimitive)) {
size_t Index = Stream.str().size();
Stream << MangledPrimitive;
recordSubstitution(Stream.str().substr(Index));
}
} else {
Stream << MangledPrimitive;
}
#endif
return Me;
}
MangleError visit(const PointerType *P) override {
size_t Fpos = Stream.str().size();
MangleError Me = MANGLE_SUCCESS;
std::string AttrMangling = getPointerAttributesMangling(P);
if (!mangleSubstitution(P, "P" + AttrMangling)) {
// A pointee type is substituted when it is a user type, a vector type
// (but see a comment in the beginning of this file), a pointer type,
// or a primitive type with qualifiers (addr. space and/or CV qualifiers).
// So, stream "P", type qualifiers
Stream << "P" << AttrMangling;
// and the pointee type itself.
Me = P->getPointee()->accept(this);
// The type qualifiers plus a pointee type is a substitutable entity, but
// only when there are qualifiers in the first place.
if (!AttrMangling.empty())
recordSubstitution(Stream.str().substr(Fpos + 1));
// The complete pointer type is substitutable as well
recordSubstitution(Stream.str().substr(Fpos));
}
return Me;
}
MangleError visit(const VectorType *V) override {
size_t Index = Stream.str().size();
std::stringstream TypeStr;
TypeStr << "Dv" << V->getLength() << "_";
MangleError Me = MANGLE_SUCCESS;
#if defined(ENABLE_MANGLER_VECTOR_SUBSTITUTION)
if (!mangleSubstitution(V, TypeStr.str()))
#endif
{
Stream << TypeStr.str();
Me = V->getScalarType()->accept(this);
recordSubstitution(Stream.str().substr(Index));
}
return Me;
}
MangleError visit(const AtomicType *P) override {
MangleError Me = MANGLE_SUCCESS;
size_t Index = Stream.str().size();
const char *TypeStr = "U7_Atomic";
if (!mangleSubstitution(P, TypeStr)) {
Stream << TypeStr;
Me = P->getBaseType()->accept(this);
recordSubstitution(Stream.str().substr(Index));
}
return Me;
}
MangleError visit(const BlockType *P) override {
Stream << "U"
<< "13block_pointerFv";
if (P->getNumOfParams() == 0)
Stream << "v";
else
for (unsigned int I = 0; I < P->getNumOfParams(); ++I) {
MangleError Err = P->getParam(I)->accept(this);
if (Err != MANGLE_SUCCESS) {
return Err;
}
}
Stream << "E";
// "Add" the function type (FvvE) and U13block_pointerFvvE to the
// substitution table. We don't actually substitute this if it's present,
// but since the block type only occurs at most once in any function we care
// about, this should be sufficient.
SeqId += 2;
return MANGLE_SUCCESS;
}
MangleError visit(const UserDefinedType *PTy) override {
size_t Index = Stream.str().size();
std::string Name = PTy->toString();
if (!mangleSubstitution(PTy, Name)) {
Stream << Name.size() << Name;
recordSubstitution(Stream.str().substr(Index));
}
return MANGLE_SUCCESS;
}
private:
void recordSubstitution(const std::string &Str) {
Substitutions[Str] = SeqId++;
}
// Holds the mangled string representing the prototype of the function.
std::stringstream &Stream;
unsigned SeqId;
std::unordered_map<std::string, unsigned> Substitutions;
};
//
// NameMangler
//
NameMangler::NameMangler(SPIRversion Version) : SpirVersion(Version) {}
MangleError NameMangler::mangle(const FunctionDescriptor &Fd,
std::string &MangledName) {
if (Fd.isNull()) {
MangledName.assign(FunctionDescriptor::nullString());
return MANGLE_NULL_FUNC_DESCRIPTOR;
}
std::stringstream Ret;
Ret << "_Z" << Fd.Name.length() << Fd.Name;
MangleVisitor Visitor(SpirVersion, Ret);
for (unsigned int I = 0; I < Fd.Parameters.size(); ++I) {
MangleError Err = Fd.Parameters[I]->accept(&Visitor);
if (Err == MANGLE_TYPE_NOT_SUPPORTED) {
MangledName.assign("Type ");
MangledName.append(Fd.Parameters[I]->toString());
MangledName.append(" is not supported in ");
std::string Ver = getSPIRVersionAsString(SpirVersion);
MangledName.append(Ver);
return Err;
}
}
MangledName.assign(Ret.str());
return MANGLE_SUCCESS;
}
} // namespace SPIR
@@ -0,0 +1,322 @@
//===------------------------- ManglingUtils.cpp -------------------------===//
//
// SPIR Tools
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
/*
* Contributed by: Intel Corporation.
*/
#include "ManglingUtils.h"
namespace SPIR {
// String represenration for the primitive types.
static const char *PrimitiveNames[PRIMITIVE_NUM] = {
"bool",
"uchar",
"char",
"ushort",
"short",
"uint",
"int",
"ulong",
"long",
"half",
"float",
"double",
"__bf16",
"void",
"...",
"image1d_ro_t",
"image1d_array_ro_t",
"image1d_buffer_ro_t",
"image2d_ro_t",
"image2d_array_ro_t",
"image2d_depth_ro_t",
"image2d_array_depth_ro_t",
"image2d_msaa_ro_t",
"image2d_array_msaa_ro_t",
"image2d_msaa_depth_ro_t",
"image2d_array_msaa_depth_ro_t",
"image3d_ro_t",
"image1d_wo_t",
"image1d_array_wo_t",
"image1d_buffer_wo_t",
"image2d_wo_t",
"image2d_array_wo_t",
"image2d_depth_wo_t",
"image2d_array_depth_wo_t",
"image2d_msaa_wo_t",
"image2d_array_msaa_wo_t",
"image2d_msaa_depth_wo_t",
"image2d_array_msaa_depth_wo_t",
"image3d_wo_t",
"image1d_rw_t",
"image1d_array_rw_t",
"image1d_buffer_rw_t",
"image2d_rw_t",
"image2d_array_rw_t",
"image2d_depth_rw_t",
"image2d_array_depth_rw_t",
"image2d_msaa_rw_t",
"image2d_array_msaa_rw_t",
"image2d_msaa_depth_rw_t",
"image2d_array_msaa_depth_rw_t",
"image3d_rw_t",
"event_t",
"pipe_ro_t",
"pipe_wo_t",
"reserve_id_t",
"queue_t",
"ndrange_t",
"clk_event_t",
"sampler_t",
"kernel_enqueue_flags_t",
"clk_profiling_info",
"memory_order",
"memory_scope",
"intel_sub_group_avc_mce_payload_t",
"intel_sub_group_avc_ime_payload_t",
"intel_sub_group_avc_ref_payload_t",
"intel_sub_group_avc_sic_payload_t",
"intel_sub_group_avc_mce_result_t",
"intel_sub_group_avc_ime_result_t",
"intel_sub_group_avc_ref_result_t",
"intel_sub_group_avc_sic_result_t",
"intel_sub_group_avc_ime_result_single_reference_streamout_t",
"intel_sub_group_avc_ime_result_dual_reference_streamout_t",
"intel_sub_group_avc_ime_result_single_reference_streamin_t",
"intel_sub_group_avc_ime_result_dual_reference_streamin_t"
};
// clang-format off
const char *MangledTypes[PRIMITIVE_NUM] = {
"b", // BOOL
"h", // UCHAR
"c", // CHAR
"t", // USHORT
"s", // SHORT
"j", // UINT
"i", // INT
"m", // ULONG
"l", // LONG
"Dh", // HALF
"f", // FLOAT
"d", // DOUBLE
"DF16b", // __BF16
"v", // VOID
"z", // VarArg
"14ocl_image1d_ro", // PRIMITIVE_IMAGE1D_RO_T
"20ocl_image1d_array_ro", // PRIMITIVE_IMAGE1D_ARRAY_RO_T
"21ocl_image1d_buffer_ro", // PRIMITIVE_IMAGE1D_BUFFER_RO_T
"14ocl_image2d_ro", // PRIMITIVE_IMAGE2D_RO_T
"20ocl_image2d_array_ro", // PRIMITIVE_IMAGE2D_ARRAY_RO_T
"20ocl_image2d_depth_ro", // PRIMITIVE_IMAGE2D_DEPTH_RO_T
"26ocl_image2d_array_depth_ro", // PRIMITIVE_IMAGE2D_ARRAY_DEPTH_RO_T
"19ocl_image2d_msaa_ro", // PRIMITIVE_IMAGE2D_MSAA_RO_T
"25ocl_image2d_array_msaa_ro", // PRIMITIVE_IMAGE2D_ARRAY_MSAA_RO_T
"25ocl_image2d_msaa_depth_ro", // PRIMITIVE_IMAGE2D_MSAA_DEPTH_RO_T
"31ocl_image2d_array_msaa_depth_ro", // PRIMITIVE_IMAGE2D_ARRAY_MSAA_DEPTH_RO_T
"14ocl_image3d_ro", // PRIMITIVE_IMAGE3D_RO_T
"14ocl_image1d_wo", // PRIMITIVE_IMAGE1D_WO_T
"20ocl_image1d_array_wo", // PRIMITIVE_IMAGE1D_ARRAY_WO_T
"21ocl_image1d_buffer_wo", // PRIMITIVE_IMAGE1D_BUFFER_WO_T
"14ocl_image2d_wo", // PRIMITIVE_IMAGE2D_WO_T
"20ocl_image2d_array_wo", // PRIMITIVE_IMAGE2D_ARRAY_WO_T
"20ocl_image2d_depth_wo", // PRIMITIVE_IMAGE2D_DEPTH_WO_T
"26ocl_image2d_array_depth_wo", // PRIMITIVE_IMAGE2D_ARRAY_DEPTH_WO_T
"19ocl_image2d_msaa_wo", // PRIMITIVE_IMAGE2D_MSAA_WO_T
"25ocl_image2d_array_msaa_wo", // PRIMITIVE_IMAGE2D_ARRAY_MSAA_WO_T
"25ocl_image2d_msaa_depth_wo", // PRIMITIVE_IMAGE2D_MSAA_DEPTH_WO_T
"31ocl_image2d_array_msaa_depth_wo", // PRIMITIVE_IMAGE2D_ARRAY_MSAA_DEPTH_WO_T
"14ocl_image3d_wo", // PRIMITIVE_IMAGE3D_WO_T
"14ocl_image1d_rw", // PRIMITIVE_IMAGE1D_RW_T
"20ocl_image1d_array_rw", // PRIMITIVE_IMAGE1D_ARRAY_RW_T
"21ocl_image1d_buffer_rw", // PRIMITIVE_IMAGE1D_BUFFER_RW_T
"14ocl_image2d_rw", // PRIMITIVE_IMAGE2D_RW_T
"20ocl_image2d_array_rw", // PRIMITIVE_IMAGE2D_ARRAY_RW_T
"20ocl_image2d_depth_rw", // PRIMITIVE_IMAGE2D_DEPTH_RW_T
"26ocl_image2d_array_depth_rw", // PRIMITIVE_IMAGE2D_ARRAY_DEPTH_RW_T
"19ocl_image2d_msaa_rw", // PRIMITIVE_IMAGE2D_MSAA_RW_T
"25ocl_image2d_array_msaa_rw", // PRIMITIVE_IMAGE2D_ARRAY_MSAA_RW_T
"25ocl_image2d_msaa_depth_rw", // PRIMITIVE_IMAGE2D_MSAA_DEPTH_RW_T
"31ocl_image2d_array_msaa_depth_rw", // PRIMITIVE_IMAGE2D_ARRAY_MSAA_DEPTH_RW_T
"14ocl_image3d_rw", // PRIMITIVE_IMAGE3D_RW_T
"9ocl_event", // PRIMITIVE_EVENT_T
"11ocl_pipe_ro", // PRIMITIVE_PIPE_RO_T
"11ocl_pipe_wo", // PRIMITIVE_PIPE_WO_T
"13ocl_reserveid", // PRIMITIVE_RESERVE_ID_T
"9ocl_queue", // PRIMITIVE_QUEUE_T
"9ndrange_t", // PRIMITIVE_NDRANGE_T
"12ocl_clkevent", // PRIMITIVE_CLK_EVENT_T
"11ocl_sampler", // PRIMITIVE_SAMPLER_T
"i", // PRIMITIVE_KERNEL_ENQUEUE_FLAGS_T
"i", // PRIMITIVE_CLK_PROFILING_INFO
#if defined(SPIRV_SPIR20_MANGLING_REQUIREMENTS)
"i", // PRIMITIVE_MEMORY_ORDER
"i", // PRIMITIVE_MEMORY_SCOPE
#else
"12memory_order", // PRIMITIVE_MEMORY_ORDER
"12memory_scope", // PRIMITIVE_MEMORY_SCOPE
#endif
"37ocl_intel_sub_group_avc_mce_payload_t", // PRIMITIVE_SUB_GROUP_AVC_MCE_PAYLOAD_T
"37ocl_intel_sub_group_avc_ime_payload_t", // PRIMITIVE_SUB_GROUP_AVC_IME_PAYLOAD_T
"37ocl_intel_sub_group_avc_ref_payload_t", // PRIMITIVE_SUB_GROUP_AVC_REF_PAYLOAD_T
"37ocl_intel_sub_group_avc_sic_payload_t", // PRIMITIVE_SUB_GROUP_AVC_SIC_PAYLOAD_T
"36ocl_intel_sub_group_avc_mce_result_t", // PRIMITIVE_SUB_GROUP_AVC_MCE_RESULT_T
"36ocl_intel_sub_group_avc_ime_result_t", // PRIMITIVE_SUB_GROUP_AVC_IME_RESULT_T
"36ocl_intel_sub_group_avc_ref_result_t", // PRIMITIVE_SUB_GROUP_AVC_REF_RESULT_T
"36ocl_intel_sub_group_avc_sic_result_t", // PRIMITIVE_SUB_GROUP_AVC_REF_RESULT_T
"63ocl_intel_sub_group_avc_ime_result_single_reference_streamout_t", // PRIMITIVE_SUB_GROUP_AVC_IME_SINGLE_REF_STREAMOUT_T
"61ocl_intel_sub_group_avc_ime_result_dual_reference_streamout_t", // PRIMITIVE_SUB_GROUP_AVC_IME_DUAL_REF_STREAMOUT_T
"55ocl_intel_sub_group_avc_ime_single_reference_streamin_t", // PRIMITIVE_SUB_GROUP_AVC_IME_SINGLE_REF_STREAMIN_T
"53ocl_intel_sub_group_avc_ime_dual_reference_streamin_t" // PRIMITIVE_SUB_GROUP_AVC_IME_DUAL_REF_STREAMIN_T
};
// clang-format on
const char *ReadableAttribute[ATTR_NUM] = {
"restrict", "volatile", "const", "__private",
"__global", "__constant", "__local", "__generic",
};
const char *MangledAttribute[ATTR_NUM] = {
"r", "V", "K", "", "U3AS1", "U3AS2", "U3AS3", "U3AS4",
};
// SPIR supported version - stated version is oldest supported version.
static const SPIRversion PrimitiveSupportedVersions[PRIMITIVE_NUM] = {
SPIR12, // BOOL
SPIR12, // UCHAR
SPIR12, // CHAR
SPIR12, // USHORT
SPIR12, // SHORT
SPIR12, // UINT
SPIR12, // INT
SPIR12, // ULONG
SPIR12, // LONG
SPIR12, // HALF
SPIR12, // FLOAT
SPIR12, // DOUBLE
SPIR12, // __BF16
SPIR12, // VOID
SPIR12, // VarArg
SPIR12, // PRIMITIVE_IMAGE1D_RO_T
SPIR12, // PRIMITIVE_IMAGE1D_ARRAY_RO_T
SPIR12, // PRIMITIVE_IMAGE1D_BUFFER_RO_T
SPIR12, // PRIMITIVE_IMAGE2D_RO_T
SPIR12, // PRIMITIVE_IMAGE2D_ARRAY_RO_T
SPIR12, // PRIMITIVE_IMAGE2D_DEPTH_RO_T
SPIR12, // PRIMITIVE_IMAGE2D_ARRAY_DEPTH_RO_T
SPIR12, // PRIMITIVE_IMAGE2D_MSAA_RO_T
SPIR12, // PRIMITIVE_IMAGE2D_ARRAY_MSAA_RO_T
SPIR12, // PRIMITIVE_IMAGE2D_MSAA_DEPTH_RO_T
SPIR12, // PRIMITIVE_IMAGE2D_ARRAY_MSAA_DEPTH_RO_T
SPIR12, // PRIMITIVE_IMAGE3D_RO_T
SPIR12, // PRIMITIVE_IMAGE1D_WO_T
SPIR12, // PRIMITIVE_IMAGE1D_ARRAY_WO_T
SPIR12, // PRIMITIVE_IMAGE1D_BUFFER_WO_T
SPIR12, // PRIMITIVE_IMAGE2D_WO_T
SPIR12, // PRIMITIVE_IMAGE2D_ARRAY_WO_T
SPIR12, // PRIMITIVE_IMAGE2D_DEPTH_WO_T
SPIR12, // PRIMITIVE_IMAGE2D_ARRAY_DEPTH_WO_T
SPIR12, // PRIMITIVE_IMAGE2D_MSAA_WO_T
SPIR12, // PRIMITIVE_IMAGE2D_ARRAY_MSAA_WO_T
SPIR12, // PRIMITIVE_IMAGE2D_MSAA_DEPTH_WO_T
SPIR12, // PRIMITIVE_IMAGE2D_ARRAY_MSAA_DEPTH_WO_T
SPIR12, // PRIMITIVE_IMAGE3D_WO_T
SPIR12, // PRIMITIVE_IMAGE1D_RW_T
SPIR12, // PRIMITIVE_IMAGE1D_ARRAY_RW_T
SPIR12, // PRIMITIVE_IMAGE1D_BUFFER_RW_T
SPIR12, // PRIMITIVE_IMAGE2D_RW_T
SPIR12, // PRIMITIVE_IMAGE2D_ARRAY_RW_T
SPIR12, // PRIMITIVE_IMAGE2D_DEPTH_RW_T
SPIR12, // PRIMITIVE_IMAGE2D_ARRAY_DEPTH_RW_T
SPIR12, // PRIMITIVE_IMAGE2D_MSAA_RW_T
SPIR12, // PRIMITIVE_IMAGE2D_ARRAY_MSAA_RW_T
SPIR12, // PRIMITIVE_IMAGE2D_MSAA_DEPTH_RW_T
SPIR12, // PRIMITIVE_IMAGE2D_ARRAY_MSAA_DEPTH_RW_T
SPIR12, // PRIMITIVE_IMAGE3D_RW_T
SPIR12, // PRIMITIVE_EVENT_T
SPIR20, // PRIMITIVE_PIPE_RO_T
SPIR20, // PRIMITIVE_PIPE_WO_T
SPIR20, // PRIMITIVE_RESERVE_ID_T
SPIR20, // PRIMITIVE_QUEUE_T
SPIR20, // PRIMITIVE_NDRANGE_T
SPIR20, // PRIMITIVE_CLK_EVENT_T
SPIR12 // PRIMITIVE_SAMPLER_T
};
const char *mangledPrimitiveString(TypePrimitiveEnum T) {
return MangledTypes[T];
}
const char *readablePrimitiveString(TypePrimitiveEnum T) {
return PrimitiveNames[T];
}
const char *getMangledAttribute(TypeAttributeEnum Attribute) {
return MangledAttribute[Attribute];
}
const char *getReadableAttribute(TypeAttributeEnum Attribute) {
return ReadableAttribute[Attribute];
}
SPIRversion getSupportedVersion(TypePrimitiveEnum T) {
return PrimitiveSupportedVersions[T];
}
const char *mangledPrimitiveStringfromName(std::string Type) {
for (size_t I = 0; I < (sizeof(PrimitiveNames) / sizeof(PrimitiveNames[0]));
I++)
if (Type == PrimitiveNames[I])
return MangledTypes[I];
return NULL;
}
std::string getPointerAttributesMangling(const PointerType *P) {
std::string QualStr;
QualStr += getMangledAttribute((P->getAddressSpace()));
for (unsigned int I = ATTR_QUALIFIER_FIRST; I <= ATTR_QUALIFIER_LAST; I++) {
TypeAttributeEnum Qualifier = (TypeAttributeEnum)I;
if (P->hasQualifier(Qualifier)) {
QualStr += getMangledAttribute(Qualifier);
}
}
return QualStr;
}
std::string getPointeeMangling(RefParamType Pointee) {
std::string Mangling;
while (const PointerType *P = SPIR::dynCast<PointerType>(Pointee)) {
Mangling += "P" + getPointerAttributesMangling(P);
Pointee = P->getPointee();
}
if (const UserDefinedType *U = SPIR::dynCast<UserDefinedType>(Pointee)) {
std::string Name = U->toString();
Mangling += std::to_string(Name.size()) + Name;
} else if (const char *PrimitiveMangling =
mangledPrimitiveStringfromName(Pointee->toString())) {
Mangling += PrimitiveMangling;
}
return Mangling;
}
const char *getSPIRVersionAsString(SPIRversion Version) {
switch (Version) {
case SPIR12:
return "SPIR 1.2";
case SPIR20:
return "SPIR 2.0";
}
assert(false && "Unknown SPIR Version");
return "Unknown SPIR Version";
}
} // namespace SPIR
@@ -0,0 +1,35 @@
//===------------------------- ManglingUtils.h ---------------------------===//
//
// SPIR Tools
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
/*
* Contributed by: Intel Corporation.
*/
#ifndef SPIRV_MANGLER_MANGLINGUTILS_H
#define SPIRV_MANGLER_MANGLINGUTILS_H
#include "ParameterType.h"
namespace SPIR {
const char *mangledPrimitiveString(TypePrimitiveEnum Primitive);
const char *readablePrimitiveString(TypePrimitiveEnum Primitive);
const char *getMangledAttribute(TypeAttributeEnum Attribute);
const char *getReadableAttribute(TypeAttributeEnum Attribute);
SPIRversion getSupportedVersion(TypePrimitiveEnum T);
const char *getSPIRVersionAsString(SPIRversion Version);
const char *mangledPrimitiveStringfromName(std::string Type);
std::string getPointerAttributesMangling(const PointerType *P);
std::string getPointeeMangling(RefParamType Pointee);
} // namespace SPIR
#endif // SPIRV_MANGLER_MANGLINGUTILS_H
@@ -0,0 +1,41 @@
//===------------------------- NameMangleAPI.h ---------------------------===//
//
// SPIR Tools
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
/*
* Contributed by: Intel Corporation.
*/
#ifndef SPIRV_MANGLER_NAMEMANGLEAPI_H
#define SPIRV_MANGLER_NAMEMANGLEAPI_H
#include "FunctionDescriptor.h"
#include <string>
namespace SPIR {
struct NameMangler {
/// @brief Constructor.
/// @param SPIRversion spir version to mangle according to.
NameMangler(SPIRversion);
/// @brief Converts the given function descriptor to string that represents
/// the function's prototype.
/// The mangling algorithm is based on Itanium mangling algorithm
/// (http://sourcery.mentor.com/public/cxx-abi/abi.html#mangling), with
/// SPIR extensions.
/// @param FunctionDescriptor function to be mangled.
/// @param std::string the mangled name if the mangling succeeds,
/// the error otherwise.
/// @return MangleError enum representing the status - success or the error.
MangleError mangle(const FunctionDescriptor &, std::string &);
private:
SPIRversion SpirVersion;
};
} // namespace SPIR
#endif // SPIRV_MANGLER_NAMEMANGLEAPI_H
@@ -0,0 +1,234 @@
//===------------------------ ParameterType.cpp --------------------------===//
//
// SPIR Tools
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
/*
* Contributed by: Intel Corporation.
*/
#include "ParameterType.h"
#include "ManglingUtils.h"
#include <assert.h>
#include <cctype>
#include <sstream>
namespace SPIR {
//
// Primitive Type
//
PrimitiveType::PrimitiveType(TypePrimitiveEnum Primitive)
: ParamType(TYPE_ID_PRIMITIVE), Primitive(Primitive) {}
MangleError PrimitiveType::accept(TypeVisitor *Visitor) const {
if (getSupportedVersion(this->getPrimitive()) >= SPIR20 &&
Visitor->SpirVer < SPIR20) {
return MANGLE_TYPE_NOT_SUPPORTED;
}
return Visitor->visit(this);
}
std::string PrimitiveType::toString() const {
assert((Primitive >= PRIMITIVE_FIRST && Primitive <= PRIMITIVE_LAST) &&
"illegal primitive");
std::stringstream MyName;
MyName << readablePrimitiveString(Primitive);
return MyName.str();
}
bool PrimitiveType::equals(const ParamType *Type) const {
const PrimitiveType *P = SPIR::dynCast<PrimitiveType>(Type);
return P && (Primitive == P->Primitive);
}
//
// Pointer Type
//
PointerType::PointerType(const RefParamType Type)
: ParamType(TYPE_ID_POINTER), PType(Type) {
for (unsigned int I = ATTR_QUALIFIER_FIRST; I <= ATTR_QUALIFIER_LAST; I++) {
setQualifier((TypeAttributeEnum)I, false);
}
AddressSpace = ATTR_PRIVATE;
}
MangleError PointerType::accept(TypeVisitor *Visitor) const {
return Visitor->visit(this);
}
void PointerType::setAddressSpace(TypeAttributeEnum Attr) {
if (Attr < ATTR_ADDR_SPACE_FIRST || Attr > ATTR_ADDR_SPACE_LAST) {
return;
}
AddressSpace = Attr;
}
TypeAttributeEnum PointerType::getAddressSpace() const { return AddressSpace; }
void PointerType::setQualifier(TypeAttributeEnum Qual, bool Enabled) {
if (Qual < ATTR_QUALIFIER_FIRST || Qual > ATTR_QUALIFIER_LAST) {
return;
}
Qualifiers[Qual - ATTR_QUALIFIER_FIRST] = Enabled;
}
bool PointerType::hasQualifier(TypeAttributeEnum Qual) const {
if (Qual < ATTR_QUALIFIER_FIRST || Qual > ATTR_QUALIFIER_LAST) {
return false;
}
return Qualifiers[Qual - ATTR_QUALIFIER_FIRST];
}
std::string PointerType::toString() const {
std::stringstream MyName;
for (unsigned int I = ATTR_QUALIFIER_FIRST; I <= ATTR_QUALIFIER_LAST; I++) {
TypeAttributeEnum Qual = (TypeAttributeEnum)I;
if (hasQualifier(Qual)) {
MyName << getReadableAttribute(Qual) << " ";
}
}
MyName << getReadableAttribute(TypeAttributeEnum(AddressSpace)) << " ";
MyName << getPointee()->toString() << " *";
return MyName.str();
}
bool PointerType::equals(const ParamType *Type) const {
const PointerType *P = SPIR::dynCast<PointerType>(Type);
if (!P) {
return false;
}
if (getAddressSpace() != P->getAddressSpace()) {
return false;
}
for (unsigned int I = ATTR_QUALIFIER_FIRST; I <= ATTR_QUALIFIER_LAST; I++) {
TypeAttributeEnum Qual = (TypeAttributeEnum)I;
if (hasQualifier(Qual) != P->hasQualifier(Qual)) {
return false;
}
}
return (*getPointee()).equals(&*(P->getPointee()));
}
//
// Vector Type
//
VectorType::VectorType(const RefParamType Type, int Len)
: ParamType(TYPE_ID_VECTOR), PType(Type), Len(Len) {}
MangleError VectorType::accept(TypeVisitor *Visitor) const {
return Visitor->visit(this);
}
std::string VectorType::toString() const {
std::stringstream MyName;
MyName << getScalarType()->toString();
MyName << Len;
return MyName.str();
}
bool VectorType::equals(const ParamType *Type) const {
const VectorType *PVec = SPIR::dynCast<VectorType>(Type);
return PVec && (Len == PVec->Len) &&
(*getScalarType()).equals(&*(PVec->getScalarType()));
}
//
// Atomic Type
//
AtomicType::AtomicType(const RefParamType Type)
: ParamType(TYPE_ID_ATOMIC), PType(Type) {}
MangleError AtomicType::accept(TypeVisitor *Visitor) const {
if (Visitor->SpirVer < SPIR20) {
return MANGLE_TYPE_NOT_SUPPORTED;
}
return Visitor->visit(this);
}
std::string AtomicType::toString() const {
std::stringstream MyName;
MyName << "atomic_" << getBaseType()->toString();
return MyName.str();
}
bool AtomicType::equals(const ParamType *Type) const {
const AtomicType *A = dynCast<AtomicType>(Type);
return (A && (*getBaseType()).equals(&*(A->getBaseType())));
}
//
// Block Type
//
BlockType::BlockType() : ParamType(TYPE_ID_BLOCK) {}
MangleError BlockType::accept(TypeVisitor *Visitor) const {
if (Visitor->SpirVer < SPIR20) {
return MANGLE_TYPE_NOT_SUPPORTED;
}
return Visitor->visit(this);
}
std::string BlockType::toString() const {
std::stringstream MyName;
MyName << "void (";
for (unsigned int I = 0; I < getNumOfParams(); ++I) {
if (I > 0)
MyName << ", ";
MyName << Params[I]->toString();
}
MyName << ")*";
return MyName.str();
}
bool BlockType::equals(const ParamType *Type) const {
const BlockType *PBlock = dynCast<BlockType>(Type);
if (!PBlock || getNumOfParams() != PBlock->getNumOfParams()) {
return false;
}
for (unsigned int I = 0; I < getNumOfParams(); ++I) {
if (!getParam(I)->equals(&*PBlock->getParam(I))) {
return false;
}
}
return true;
}
//
// User Defined Type
//
UserDefinedType::UserDefinedType(const std::string &Name)
: ParamType(TYPE_ID_STRUCTURE), Name(Name) {}
MangleError UserDefinedType::accept(TypeVisitor *Visitor) const {
return Visitor->visit(this);
}
std::string UserDefinedType::toString() const {
std::stringstream MyName;
MyName << Name;
return MyName.str();
}
bool UserDefinedType::equals(const ParamType *PType) const {
const UserDefinedType *PTy = SPIR::dynCast<UserDefinedType>(PType);
return PTy && (Name == PTy->Name);
}
//
// Static enums
//
const TypeEnum PrimitiveType::EnumTy = TYPE_ID_PRIMITIVE;
const TypeEnum PointerType::EnumTy = TYPE_ID_POINTER;
const TypeEnum VectorType::EnumTy = TYPE_ID_VECTOR;
const TypeEnum AtomicType::EnumTy = TYPE_ID_ATOMIC;
const TypeEnum BlockType::EnumTy = TYPE_ID_BLOCK;
const TypeEnum UserDefinedType::EnumTy = TYPE_ID_STRUCTURE;
} // namespace SPIR
@@ -0,0 +1,492 @@
//===------------------------- ParameterType.h ---------------------------===//
//
// SPIR Tools
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
/*
* Contributed by: Intel Corporation.
*/
#ifndef SPIRV_MANGLER_PARAMETERTYPE_H
#define SPIRV_MANGLER_PARAMETERTYPE_H
#include "Refcount.h"
#include <string>
#include <vector>
// The Type class hierarchy models the different types in OCL.
namespace SPIR {
// Supported SPIR versions
enum SPIRversion { SPIR12 = 1, SPIR20 = 2 };
// Error Status values
enum MangleError {
MANGLE_SUCCESS,
MANGLE_TYPE_NOT_SUPPORTED,
MANGLE_NULL_FUNC_DESCRIPTOR
};
enum TypePrimitiveEnum {
PRIMITIVE_FIRST,
PRIMITIVE_BOOL = PRIMITIVE_FIRST,
PRIMITIVE_UCHAR,
PRIMITIVE_CHAR,
PRIMITIVE_USHORT,
PRIMITIVE_SHORT,
PRIMITIVE_UINT,
PRIMITIVE_INT,
PRIMITIVE_ULONG,
PRIMITIVE_LONG,
PRIMITIVE_HALF,
PRIMITIVE_FLOAT,
PRIMITIVE_DOUBLE,
PRIMITIVE_BFLOAT,
PRIMITIVE_VOID,
PRIMITIVE_VAR_ARG,
PRIMITIVE_STRUCT_FIRST,
PRIMITIVE_IMAGE1D_RO_T = PRIMITIVE_STRUCT_FIRST,
PRIMITIVE_IMAGE1D_ARRAY_RO_T,
PRIMITIVE_IMAGE1D_BUFFER_RO_T,
PRIMITIVE_IMAGE2D_RO_T,
PRIMITIVE_IMAGE2D_ARRAY_RO_T,
PRIMITIVE_IMAGE2D_DEPTH_RO_T,
PRIMITIVE_IMAGE2D_ARRAY_DEPTH_RO_T,
PRIMITIVE_IMAGE2D_MSAA_RO_T,
PRIMITIVE_IMAGE2D_ARRAY_MSAA_RO_T,
PRIMITIVE_IMAGE2D_MSAA_DEPTH_RO_T,
PRIMITIVE_IMAGE2D_ARRAY_MSAA_DEPTH_RO_T,
PRIMITIVE_IMAGE3D_RO_T,
PRIMITIVE_IMAGE1D_WO_T,
PRIMITIVE_IMAGE1D_ARRAY_WO_T,
PRIMITIVE_IMAGE1D_BUFFER_WO_T,
PRIMITIVE_IMAGE2D_WO_T,
PRIMITIVE_IMAGE2D_ARRAY_WO_T,
PRIMITIVE_IMAGE2D_DEPTH_WO_T,
PRIMITIVE_IMAGE2D_ARRAY_DEPTH_WO_T,
PRIMITIVE_IMAGE2D_MSAA_WO_T,
PRIMITIVE_IMAGE2D_ARRAY_MSAA_WO_T,
PRIMITIVE_IMAGE2D_MSAA_DEPTH_WO_T,
PRIMITIVE_IMAGE2D_ARRAY_MSAA_DEPTH_WO_T,
PRIMITIVE_IMAGE3D_WO_T,
PRIMITIVE_IMAGE1D_RW_T,
PRIMITIVE_IMAGE1D_ARRAY_RW_T,
PRIMITIVE_IMAGE1D_BUFFER_RW_T,
PRIMITIVE_IMAGE2D_RW_T,
PRIMITIVE_IMAGE2D_ARRAY_RW_T,
PRIMITIVE_IMAGE2D_DEPTH_RW_T,
PRIMITIVE_IMAGE2D_ARRAY_DEPTH_RW_T,
PRIMITIVE_IMAGE2D_MSAA_RW_T,
PRIMITIVE_IMAGE2D_ARRAY_MSAA_RW_T,
PRIMITIVE_IMAGE2D_MSAA_DEPTH_RW_T,
PRIMITIVE_IMAGE2D_ARRAY_MSAA_DEPTH_RW_T,
PRIMITIVE_IMAGE3D_RW_T,
PRIMITIVE_EVENT_T,
PRIMITIVE_PIPE_RO_T,
PRIMITIVE_PIPE_WO_T,
PRIMITIVE_RESERVE_ID_T,
PRIMITIVE_QUEUE_T,
PRIMITIVE_NDRANGE_T,
PRIMITIVE_CLK_EVENT_T,
PRIMITIVE_STRUCT_LAST = PRIMITIVE_CLK_EVENT_T,
PRIMITIVE_SAMPLER_T,
PRIMITIVE_KERNEL_ENQUEUE_FLAGS_T,
PRIMITIVE_CLK_PROFILING_INFO,
PRIMITIVE_MEMORY_ORDER,
PRIMITIVE_MEMORY_SCOPE,
PRIMITIVE_SUB_GROUP_AVC_MCE_PAYLOAD_T,
PRIMITIVE_SUB_GROUP_AVC_IME_PAYLOAD_T,
PRIMITIVE_SUB_GROUP_AVC_REF_PAYLOAD_T,
PRIMITIVE_SUB_GROUP_AVC_SIC_PAYLOAD_T,
PRIMITIVE_SUB_GROUP_AVC_MCE_RESULT_T,
PRIMITIVE_SUB_GROUP_AVC_IME_RESULT_T,
PRIMITIVE_SUB_GROUP_AVC_REF_RESULT_T,
PRIMITIVE_SUB_GROUP_AVC_SIC_RESULT_T,
PRIMITIVE_SUB_GROUP_AVC_IME_SINGLE_REF_STREAMOUT_T,
PRIMITIVE_SUB_GROUP_AVC_IME_DUAL_REF_STREAMOUT_T,
PRIMITIVE_SUB_GROUP_AVC_IME_SINGLE_REF_STREAMIN_T,
PRIMITIVE_SUB_GROUP_AVC_IME_DUAL_REF_STREAMIN_T,
PRIMITIVE_LAST = PRIMITIVE_SUB_GROUP_AVC_IME_DUAL_REF_STREAMIN_T,
PRIMITIVE_NONE,
// Keep this at the end.
PRIMITIVE_NUM = PRIMITIVE_NONE
};
enum TypeEnum {
TYPE_ID_PRIMITIVE,
TYPE_ID_POINTER,
TYPE_ID_VECTOR,
TYPE_ID_ATOMIC,
TYPE_ID_BLOCK,
TYPE_ID_STRUCTURE
};
enum TypeAttributeEnum {
ATTR_QUALIFIER_FIRST = 0,
ATTR_RESTRICT = ATTR_QUALIFIER_FIRST,
ATTR_VOLATILE,
ATTR_CONST,
ATTR_QUALIFIER_LAST = ATTR_CONST,
ATTR_ADDR_SPACE_FIRST,
ATTR_PRIVATE = ATTR_ADDR_SPACE_FIRST,
ATTR_GLOBAL,
ATTR_CONSTANT,
ATTR_LOCAL,
ATTR_GENERIC,
ATTR_GLOBAL_DEVICE,
ATTR_GLOBAL_HOST,
ATTR_ADDR_SPACE_LAST = ATTR_GLOBAL_HOST,
ATTR_NONE,
ATTR_NUM = ATTR_NONE
};
// Forward declaration for abstract structure.
struct ParamType;
typedef RefCount<ParamType> RefParamType;
// Forward declaration for abstract structure.
struct TypeVisitor;
struct ParamType {
/// @brief Constructor.
/// @param TypeEnum type id.
ParamType(TypeEnum TypeId) : TypeId(TypeId) {}
/// @brief Destructor.
virtual ~ParamType() {}
/// Abstract Methods ///
/// @brief Visitor service method. (see TypeVisitor for more details).
/// When overridden in subclasses, preform a 'double dispatch' to the
/// appropriate visit method in the given visitor.
/// @param TypeVisitor type visitor.
virtual MangleError accept(TypeVisitor *) const = 0;
/// @brief Returns a string representation of the underlying type.
/// @return type as string.
virtual std::string toString() const = 0;
/// @brief Returns true if given param type is equal to this type.
/// @param ParamType given param type.
/// @return true if given param type is equal to this type and false
/// otherwise.
virtual bool equals(const ParamType *) const = 0;
/// Common Base-Class Methods ///
/// @brief Returns type id of underlying type.
/// @return type id.
TypeEnum getTypeId() const { return TypeId; }
private:
// @brief Default Constructor.
ParamType();
protected:
/// An enumeration to identify the type id of this instance.
TypeEnum TypeId;
};
struct PrimitiveType : public ParamType {
/// An enumeration to identify the type id of this class.
const static TypeEnum EnumTy;
/// @brief Constructor.
/// @param TypePrimitiveEnum primitive id.
PrimitiveType(TypePrimitiveEnum);
/// Implementation of Abstract Methods ///
/// @brief Visitor service method. (see TypeVisitor for more details).
/// When overridden in subclasses, preform a 'double dispatch' to the
/// appropriate visit method in the given visitor.
/// @param TypeVisitor type visitor.
MangleError accept(TypeVisitor *) const override;
/// @brief Returns a string representation of the underlying type.
/// @return type as string.
std::string toString() const override;
/// @brief Returns true if given param type is equal to this type.
/// @param ParamType given param type.
/// @return true if given param type is equal to this type and false
/// otherwise.
bool equals(const ParamType *) const override;
/// Non-Common Methods ///
/// @brief Returns the primitive enumeration of the type.
/// @return primitive type.
TypePrimitiveEnum getPrimitive() const { return Primitive; }
protected:
/// An enumeration to identify the primitive type.
TypePrimitiveEnum Primitive;
};
struct PointerType : public ParamType {
/// An enumeration to identify the type id of this class.
const static TypeEnum EnumTy;
/// @brief Constructor.
/// @param RefParamType the type of pointee (that the pointer points at).
PointerType(const RefParamType Type);
/// Implementation of Abstract Methods ///
/// @brief Visitor service method. (see TypeVisitor for more details).
/// When overridden in subclasses, preform a 'double dispatch' to the
/// appropriate visit method in the given visitor.
/// @param TypeVisitor type visitor
MangleError accept(TypeVisitor *) const override;
/// @brief Returns a string representation of the underlying type.
/// @return type as string.
std::string toString() const override;
/// @brief Returns true if given param type is equal to this type.
/// @param ParamType given param type.
/// @return true if given param type is equal to this type and false
/// otherwise.
bool equals(const ParamType *) const override;
/// Non-Common Methods ///
/// @brief Returns the type the pointer is pointing at.
/// @return pointee type.
const RefParamType &getPointee() const { return PType; }
/// @brief Sets the address space attribute - default is __private
/// @param TypeAttributeEnum address space attribute id.
void setAddressSpace(TypeAttributeEnum Attr);
/// @brief Returns the pointer's address space.
/// @return pointer's address space.
TypeAttributeEnum getAddressSpace() const;
/// @brief Adds or removes a pointer's qualifier.
/// @param TypeAttributeEnum qual - qualifier to add/remove.
/// @param bool enabled - true if qualifier should exist false otherwise.
/// default is set to false.
void setQualifier(TypeAttributeEnum Qual, bool Enabled);
/// @brief Checks if the pointer has a certain qualifier.
/// @param TypeAttributeEnum qual - qualifier to check.
/// @return true if the qualifier exists and false otherwise.
bool hasQualifier(TypeAttributeEnum Qual) const;
private:
/// The type this pointer is pointing at.
RefParamType PType;
/// Array of the pointer's enabled type qualifiers.
bool Qualifiers[ATTR_QUALIFIER_LAST - ATTR_QUALIFIER_FIRST + 1];
/// Pointer's address space.
TypeAttributeEnum AddressSpace;
};
struct VectorType : public ParamType {
/// An enumeration to identify the type id of this class.
const static TypeEnum EnumTy;
/// @brief Constructor.
/// @param RefParamType the type of each scalar element in the vector.
/// @param int the length of the vector.
VectorType(const RefParamType Type, int Len);
/// Implementation of Abstract Methods ///
/// @brief Visitor service method. (see TypeVisitor for more details).
/// When overridden in subclasses, preform a 'double dispatch' to the
/// appropriate visit method in the given visitor.
/// @param TypeVisitor type visitor.
MangleError accept(TypeVisitor *) const override;
/// @brief Returns a string representation of the underlying type.
/// @return type as string.
std::string toString() const override;
/// @brief Returns true if given param type is equal to this type.
/// @param ParamType given param type.
/// @return true if given param type is equal to this type and false
/// otherwise.
bool equals(const ParamType *) const override;
/// Non-Common Methods ///
/// @brief Returns the type the vector is packing.
/// @return scalar type.
const RefParamType &getScalarType() const { return PType; }
/// @brief Returns the length of the vector type.
/// @return vector type length.
int getLength() const { return Len; }
private:
/// The scalar type of this vector type.
RefParamType PType;
/// The length of the vector.
int Len;
};
struct AtomicType : public ParamType {
/// an enumeration to identify the type id of this class
const static TypeEnum EnumTy;
/// @brief Constructor
/// @param RefParamType the type refernced as atomic.
AtomicType(const RefParamType Type);
/// Implementation of Abstract Methods ///
/// @brief visitor service method. (see TypeVisitor for more details).
/// When overridden in subclasses, preform a 'double dispatch' to the
/// appropriate visit method in the given visitor.
/// @param TypeVisitor type visitor
MangleError accept(TypeVisitor *) const override;
/// @brief returns a string representation of the underlying type.
/// @return type as string
std::string toString() const override;
/// @brief returns true if given param type is equal to this type.
/// @param ParamType given param type
/// @return true if given param type is equal to this type and false otherwise
bool equals(const ParamType *) const override;
/// Non-Common Methods ///
/// @brief returns the base type of the atomic parameter.
/// @return base type
const RefParamType &getBaseType() const { return PType; }
private:
/// the type this pointer is pointing at
RefParamType PType;
};
struct BlockType : public ParamType {
/// an enumeration to identify the type id of this class
const static TypeEnum EnumTy;
///@brief Constructor
BlockType();
/// Implementation of Abstract Methods ///
/// @brief visitor service method. (see TypeVisitor for more details).
/// When overridden in subclasses, preform a 'double dispatch' to the
/// appropriate visit method in the given visitor.
/// @param TypeVisitor type visitor
MangleError accept(TypeVisitor *) const override;
/// @brief returns a string representation of the underlying type.
/// @return type as string
std::string toString() const override;
/// @brief returns true if given param type is equal to this type.
/// @param ParamType given param type
/// @return true if given param type is equal to this type and false otherwise
bool equals(const ParamType *) const override;
/// Non-Common Methods ///
/// @brief returns the number of parameters of the block.
/// @return parameters count
unsigned int getNumOfParams() const { return (unsigned int)Params.size(); }
///@brief returns the type of parameter "index" of the block.
// @param index the sequential number of the queried parameter
///@return parameter type
const RefParamType &getParam(unsigned int Index) const {
assert(Params.size() > Index && "index is OOB");
return Params[Index];
}
///@brief set the type of parameter "index" of the block.
// @param index the sequential number of the queried parameter
// @param type the parameter type
void setParam(unsigned int Index, RefParamType Type) {
if (Index < getNumOfParams()) {
Params[Index] = Type;
} else if (Index == getNumOfParams()) {
Params.push_back(Type);
} else {
assert(false && "index is OOB");
}
}
protected:
/// an enumeration to identify the primitive type
std::vector<RefParamType> Params;
};
struct UserDefinedType : public ParamType {
/// An enumeration to identify the type id of this class.
const static TypeEnum EnumTy;
/// @brief Constructor.
UserDefinedType(const std::string &);
/// Implementation of Abstract Methods ///
/// @brief Visitor service method. (see TypeVisitor for more details).
/// When overridden in subclasses, preform a 'double dispatch' to the
/// appropriate visit method in the given visitor.
/// @param TypeVisitor type visitor.
MangleError accept(TypeVisitor *) const override;
/// @brief Returns a string representation of the underlying type.
/// @return type as string.
std::string toString() const override;
/// @brief Returns true if given param type is equal to this type.
/// @param ParamType given param type.
/// @return true if given param type is equal to this type and false
/// otherwise.
bool equals(const ParamType *) const override;
protected:
/// The name of the user defined type.
std::string Name;
};
/// @brief Can be overridden so an object of static type Type* will
/// dispatch the correct visit method according to its dynamic type.
struct TypeVisitor {
SPIRversion SpirVer;
TypeVisitor(SPIRversion Ver) : SpirVer(Ver) {}
virtual ~TypeVisitor() {}
virtual MangleError visit(const PrimitiveType *) = 0;
virtual MangleError visit(const VectorType *) = 0;
virtual MangleError visit(const PointerType *) = 0;
virtual MangleError visit(const AtomicType *) = 0;
virtual MangleError visit(const BlockType *) = 0;
virtual MangleError visit(const UserDefinedType *) = 0;
};
/// @brief Template dynamic cast function for ParamType derived classes.
/// @param ParamType given param type.
/// @return required casting type if given param type is an instance if
// that type, NULL otherwise.
template <typename T> T *dynCast(ParamType *PType) {
assert(PType && "dyn_cast does not support casting of NULL");
return (T::EnumTy == PType->getTypeId()) ? (T *)PType : NULL;
}
/// @brief Template dynamic cast function for ParamType derived classes
/// (the constant version).
/// @param ParamType given param type.
/// @return required casting type if given param type is an instance if
// that type, NULL otherwise.
template <typename T> const T *dynCast(const ParamType *PType) {
assert(PType && "dyn_cast does not support casting of NULL");
return (T::EnumTy == PType->getTypeId()) ? (const T *)PType : NULL;
}
} // namespace SPIR
#endif // SPIRV_MANGLER_PARAMETERTYPE_H
@@ -0,0 +1,16 @@
Contributed by: Intel Corporation.
SPIR Name Mangler
=================
The NameMangler Library Converts the given function descriptor to a string
that represents the function's prototype.
The mangling algorithm is based on clang 3.0 Itanium mangling algorithm
(http://sourcery.mentor.com/public/cxx-abi/abi.html#mangling).
The algorithm is adapted to support mangling of SPIR built-in
functions and was tested on SPIR built-ins only.
The mangler supports mangling according to SPIR 1.2 and SPIR 2.0
For usage examples see unittest/spir_name_mangler.
@@ -0,0 +1,100 @@
//===--------------------------- Refcount.h ------------------------------===//
//
// SPIR Tools
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
/*
* Contributed by: Intel Corporation
*/
#ifndef SPIRV_MANGLER_REFCOUNT_H
#define SPIRV_MANGLER_REFCOUNT_H
#include <assert.h>
namespace SPIR {
template <typename T> class RefCount {
public:
RefCount() : Count(0), Ptr(0) {}
RefCount(T *Ptr) : Ptr(Ptr) { Count = new int(1); }
RefCount(const RefCount<T> &Other) { cpy(Other); }
~RefCount() {
if (Count)
dispose();
}
RefCount &operator=(const RefCount<T> &Other) {
if (this == &Other)
return *this;
if (Count)
dispose();
cpy(Other);
return *this;
}
void init(T *Ptr) {
assert(!Ptr && "overrunning non NULL pointer");
assert(!Count && "overrunning non NULL pointer");
Count = new int(1);
this->Ptr = Ptr;
}
bool isNull() const { return (!Ptr); }
// Pointer access
const T &operator*() const {
sanity();
return *Ptr;
}
T &operator*() {
sanity();
return *Ptr;
}
operator T *() { return Ptr; }
operator const T *() const { return Ptr; }
T *operator->() { return Ptr; }
const T *operator->() const { return Ptr; }
private:
void sanity() const {
assert(Ptr && "NULL pointer");
assert(Count && "NULL ref counter");
assert(*Count && "zero ref counter");
}
void cpy(const RefCount<T> &Other) {
Count = Other.Count;
Ptr = Other.Ptr;
if (Count)
++*Count;
}
void dispose() {
sanity();
if (0 == --*Count) {
delete Count;
delete Ptr;
Ptr = 0;
Count = 0;
}
}
int *Count;
T *Ptr;
}; // End RefCount
} // namespace SPIR
#endif // SPIRV_MANGLER_REFCOUNT_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,317 @@
//=- OCLToSPIRV.h - OpenCL to SPIR-V builtin preprocessing pass -*- C++ -*-=//
//
// The LLVM/SPIR-V Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2022 The Khronos Group Inc.
//
// 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 Khronos Group, 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.
//
//===----------------------------------------------------------------------===//
//
// This file implements preprocessing of OpenCL C built-in functions into SPIR-V
// friendly IR form for further translation into SPIR-V
//
//===----------------------------------------------------------------------===//
#ifndef SPIRV_OCLTOSPIRV_H
#define SPIRV_OCLTOSPIRV_H
#include "OCLUtil.h"
#include "SPIRVBuiltinHelper.h"
#include "llvm/IR/InstVisitor.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Pass.h"
namespace SPIRV {
class OCLTypeToSPIRVBase;
class OCLToSPIRVBase : public InstVisitor<OCLToSPIRVBase>, BuiltinCallHelper {
public:
OCLToSPIRVBase()
: BuiltinCallHelper(ManglingRules::SPIRV), Ctx(nullptr), CLVer(0),
OCLTypeToSPIRVPtr(nullptr) {}
virtual ~OCLToSPIRVBase() {}
bool runOCLToSPIRV(Module &M);
virtual void visitCallInst(CallInst &CI);
/// Transform barrier/work_group_barrier/sub_group_barrier
/// to __spirv_ControlBarrier.
/// barrier(flag) =>
/// __spirv_ControlBarrier(workgroup, workgroup, map(flag))
/// work_group_barrier(scope, flag) =>
/// __spirv_ControlBarrier(workgroup, map(scope), map(flag))
/// sub_group_barrier(scope, flag) =>
/// __spirv_ControlBarrier(subgroup, map(scope), map(flag))
void visitCallBarrier(CallInst *CI);
/// Erase useless convert functions.
/// \return true if the call instruction is erased.
bool eraseUselessConvert(CallInst *Call, StringRef MangledName,
StringRef DeMangledName);
/// Transform convert_ to
/// __spirv_{CastOpName}_R{TargeTyName}{_sat}{_rt[p|n|z|e]}
void visitCallConvert(CallInst *CI, StringRef MangledName,
StringRef DemangledName);
/// Transform async_work_group{_strided}_copy.
/// async_work_group_copy(dst, src, n, event)
/// => async_work_group_strided_copy(dst, src, n, 1, event)
/// async_work_group_strided_copy(dst, src, n, stride, event)
/// => __spirv_AsyncGroupCopy(ScopeWorkGroup, dst, src, n, stride, event)
void visitCallAsyncWorkGroupCopy(CallInst *CI, StringRef DemangledName);
/// Transform OCL builtin function to SPIR-V builtin function.
void transBuiltin(CallInst *CI, OCLBuiltinTransInfo &Info);
/// Transform atomic_work_item_fence/mem_fence to __spirv_MemoryBarrier.
/// func(flag, order, scope) =>
/// __spirv_MemoryBarrier(map(scope), map(flag)|map(order))
void transMemoryBarrier(CallInst *CI, AtomicWorkItemFenceLiterals);
/// Transform all to __spirv_Op(All|Any). Note that the types mismatch so
// some extra code is emitted to convert between the two.
void visitCallAllAny(spv::Op OC, CallInst *CI);
/// Transform atomic_* to __spirv_Atomic*.
/// atomic_x(ptr_arg, args, order, scope) =>
/// __spirv_AtomicY(ptr_arg, map(order), map(scope), args)
void transAtomicBuiltin(CallInst *CI, OCLBuiltinTransInfo &Info);
/// Transform atomic_work_item_fence to __spirv_MemoryBarrier.
/// atomic_work_item_fence(flag, order, scope) =>
/// __spirv_MemoryBarrier(map(scope), map(flag)|map(order))
void visitCallAtomicWorkItemFence(CallInst *CI);
/// Transform atomic_compare_exchange call.
/// In atomic_compare_exchange, the expected value parameter is a pointer.
/// However in SPIR-V it is a value. The transformation adds a load
/// instruction, result of which is passed to atomic_compare_exchange as
/// argument.
/// The transformation adds a store instruction after the call, to update the
/// value in expected with the value pointed to by object. Though, it is not
/// necessary in case they are equal, this approach makes result code simpler.
/// Also ICmp instruction is added, because the call must return result of
/// comparison.
/// \returns the call instruction of atomic_compare_exchange_strong.
CallInst *visitCallAtomicCmpXchg(CallInst *CI);
/// Transform atomic_init.
/// atomic_init(p, x) => store p, x
void visitCallAtomicInit(CallInst *CI);
/// Transform legacy OCL 1.x atomic builtins to SPIR-V builtins for extensions
/// cl_khr_int64_base_atomics
/// cl_khr_int64_extended_atomics
/// Do nothing if the called function is not a legacy atomic builtin.
void visitCallAtomicLegacy(CallInst *CI, StringRef MangledName,
StringRef DemangledName);
/// Transform OCL 2.0 C++11 atomic builtins to SPIR-V builtins.
/// Do nothing if the called function is not a C++11 atomic builtin.
void visitCallAtomicCpp11(CallInst *CI, StringRef MangledName,
StringRef DemangledName);
/// Transform OCL builtin function to SPIR-V builtin function.
/// Assuming there is a simple name mapping without argument changes.
/// Should be called at last.
void visitCallBuiltinSimple(CallInst *CI, StringRef MangledName,
StringRef DemangledName);
/// Transform get_image_{width|height|depth|dim}.
/// get_image_xxx(...) =>
/// dimension = __spirv_ImageQuerySizeLod_R{ReturnType}(...);
/// return dimension.{x|y|z};
void visitCallGetImageSize(CallInst *CI, StringRef DemangledName);
/// Transform {work|sub}_group_x =>
/// __spirv_{OpName}
///
/// Special handling of work_group_broadcast.
/// work_group_broadcast(a, x, y, z)
/// =>
/// __spirv_GroupBroadcast(a, vec3(x, y, z))
void visitCallGroupBuiltin(CallInst *CI, StringRef DemangledName);
/// Transform mem_fence to __spirv_MemoryBarrier.
/// mem_fence(flag) => __spirv_MemoryBarrier(Workgroup, map(flag))
void visitCallMemFence(CallInst *CI, StringRef DemangledName);
void visitCallNDRange(CallInst *CI, StringRef DemangledName);
/// Transform read_image with sampler arguments.
/// read_image(image, sampler, ...) =>
/// sampled_image = __spirv_SampledImage(image, sampler);
/// return __spirv_ImageSampleExplicitLod_R{ReturnType}(sampled_image, ...);
void visitCallReadImageWithSampler(CallInst *CI, StringRef MangledName,
StringRef DemangledName);
/// Transform read_image with msaa image arguments.
/// Sample argument must be acoded as Image Operand.
void visitCallReadImageMSAA(CallInst *CI, StringRef MangledName);
/// Transform {read|write}_image without sampler arguments.
void visitCallReadWriteImage(CallInst *CI, StringRef DemangledName);
/// Transform to_{global|local|private}.
///
/// T* a = ...;
/// addr T* b = to_addr(a);
/// =>
/// i8* x = cast<i8*>(a);
/// addr i8* y = __spirv_GenericCastToPtr_ToAddr(x);
/// addr T* b = cast<addr T*>(y);
void visitCallToAddr(CallInst *CI, StringRef DemangledName);
/// Transform return type of relatinal built-in functions like isnan, isfinite
/// to boolean values.
void visitCallRelational(CallInst *CI, StringRef DemangledName);
/// Transform vector load/store functions to SPIR-V extended builtin
/// functions
/// {vload|vstore{a}}{_half}{n}{_rte|_rtz|_rtp|_rtn} =>
/// __spirv_ocl_{ExtendedInstructionOpCodeName}__R{ReturnType}
void visitCallVecLoadStore(CallInst *CI, StringRef MangledName,
StringRef DemangledName);
/// Transforms get_mem_fence built-in to SPIR-V function and aligns result
/// values with SPIR 1.2. get_mem_fence(ptr) => __spirv_GenericPtrMemSemantics
/// GenericPtrMemSemantics valid values are 0x100, 0x200 and 0x300, where is
/// SPIR 1.2 defines them as 0x1, 0x2 and 0x3, so this function adjusts
/// GenericPtrMemSemantics results to SPIR 1.2 values.
void visitCallGetFence(CallInst *CI, StringRef DemangledName);
/// Transforms OpDot instructions with a scalar type to a fmul instruction
void visitCallDot(CallInst *CI);
/// Transforms OpDot instructions with a vector or scalar (packed vector) type
/// to dot or dot_acc_sat instructions
void visitCallDot(CallInst *CI, StringRef MangledName,
StringRef DemangledName);
/// Transform clock_read_* calls to OpReadClockKHR instructions.
void visitCallClockRead(CallInst *CI, StringRef MangledName,
StringRef DemangledName);
/// Fixes for built-in functions with vector+scalar arguments that are
/// translated to the SPIR-V instructions where all arguments must have the
/// same type.
void visitCallScalToVec(CallInst *CI, StringRef MangledName,
StringRef DemangledName);
/// Transform get_image_channel_{order|data_type} built-in functions to
/// __spirv_ocl_{ImageQueryOrder|ImageQueryFormat}
void visitCallGetImageChannel(CallInst *CI, StringRef DemangledName,
unsigned int Offset);
/// Transform enqueue_kernel and kernel query built-in functions to
/// spirv-friendly format filling arguments, required for device-side enqueue
/// instructions, but missed in the original call
void visitCallEnqueueKernel(CallInst *CI, StringRef DemangledName);
void visitCallKernelQuery(CallInst *CI, StringRef DemangledName);
/// For cl_intel_subgroups block read built-ins:
void visitSubgroupBlockReadINTEL(CallInst *CI);
/// For cl_intel_subgroups block write built-ins:
void visitSubgroupBlockWriteINTEL(CallInst *CI);
/// For cl_intel_media_block_io built-ins:
void visitSubgroupImageMediaBlockINTEL(CallInst *CI, StringRef DemangledName);
// For cl_intel_device_side_avc_motion_estimation built-ins
void visitSubgroupAVCBuiltinCall(CallInst *CI, StringRef DemangledName);
void visitSubgroupAVCWrapperBuiltinCall(CallInst *CI, Op WrappedOC,
StringRef DemangledName);
void visitSubgroupAVCBuiltinCallWithSampler(CallInst *CI,
StringRef DemangledName);
/// For cl_intel_split_work_group_barrier built-ins:
void visitCallSplitBarrierINTEL(CallInst *CI, StringRef DemangledName);
void visitCallLdexp(CallInst *CI, StringRef MangledName,
StringRef DemangledName);
/// For cl_intel_convert_bfloat16_as_ushort
void visitCallConvertBFloat16AsUshort(CallInst *CI, StringRef DemangledName);
/// For cl_intel_convert_as_bfloat16_float
void visitCallConvertAsBFloat16Float(CallInst *CI, StringRef DemangledName);
void setOCLTypeToSPIRV(OCLTypeToSPIRVBase *OCLTypeToSPIRV) {
OCLTypeToSPIRVPtr = OCLTypeToSPIRV;
}
OCLTypeToSPIRVBase *getOCLTypeToSPIRV() { return OCLTypeToSPIRVPtr; }
private:
LLVMContext *Ctx;
unsigned CLVer; /// OpenCL version as major*10+minor
std::set<Instruction *> ValuesToDelete;
OCLTypeToSPIRVBase *OCLTypeToSPIRVPtr;
ConstantInt *addInt32(int I) { return getInt32(M, I); }
ConstantInt *addSizet(uint64_t I) { return getSizet(M, I); }
/// Get vector width from OpenCL vload* function name.
SPIRVWord getVecLoadWidth(const std::string &DemangledName);
/// Transform OpenCL vload/vstore function name.
void transVecLoadStoreName(std::string &DemangledName,
const std::string &Stem, bool AlwaysN);
void processSubgroupBlockReadWriteINTEL(CallInst *CI,
OCLBuiltinTransInfo &Info,
const Type *DataTy);
};
class OCLToSPIRVLegacy : public OCLToSPIRVBase, public llvm::ModulePass {
public:
OCLToSPIRVLegacy() : ModulePass(ID) {
initializeOCLToSPIRVLegacyPass(*PassRegistry::getPassRegistry());
}
bool runOnModule(Module &M) override;
void getAnalysisUsage(AnalysisUsage &AU) const override;
static char ID;
};
class OCLToSPIRVPass : public OCLToSPIRVBase,
public llvm::PassInfoMixin<OCLToSPIRVPass> {
public:
llvm::PreservedAnalyses run(llvm::Module &M,
llvm::ModuleAnalysisManager &MAM);
static bool isRequired() { return true; }
};
} // namespace SPIRV
#endif // SPIRV_OCLTOSPIRV_H
@@ -0,0 +1,324 @@
//===- OCLTypeToSPIRV.cpp - Adapt types from OCL for SPIRV ------*- C++ -*-===//
//
// The LLVM/SPIRV Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
//
// 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 Advanced Micro Devices, Inc., 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.
//
//===----------------------------------------------------------------------===//
//
// This file implements adaptation of OCL types for SPIR-V.
//
// It first maps kernel arguments of OCL opaque types to SPIR-V type, then
// propagates the mapping to the uses of the kernel arguments.
//
//===----------------------------------------------------------------------===//
#include "OCLTypeToSPIRV.h"
#include "OCLUtil.h"
#include "SPIRVInternal.h"
#include "llvm/Pass.h"
#include "llvm/Support/Debug.h"
#include <iterator>
#include <set>
#define DEBUG_TYPE "cltytospv"
using namespace llvm;
using namespace SPIRV;
using namespace OCLUtil;
namespace SPIRV {
char OCLTypeToSPIRVLegacy::ID = 0;
OCLTypeToSPIRVLegacy::OCLTypeToSPIRVLegacy() : ModulePass(ID) {
initializeOCLTypeToSPIRVLegacyPass(*PassRegistry::getPassRegistry());
}
void OCLTypeToSPIRVLegacy::getAnalysisUsage(AnalysisUsage &AU) const {
AU.setPreservesAll();
}
bool OCLTypeToSPIRVLegacy::runOnModule(Module &M) {
return runOCLTypeToSPIRV(M);
}
OCLTypeToSPIRVBase &OCLTypeToSPIRVPass::run(llvm::Module &M,
llvm::ModuleAnalysisManager &MAM) {
runOCLTypeToSPIRV(M);
return *this;
}
OCLTypeToSPIRVBase::OCLTypeToSPIRVBase()
: BuiltinCallHelper(ManglingRules::None), M(nullptr), Ctx(nullptr) {}
bool OCLTypeToSPIRVBase::runOCLTypeToSPIRV(Module &Module) {
LLVM_DEBUG(dbgs() << "Enter OCLTypeToSPIRV:\n");
initialize(Module);
M = &Module;
Ctx = &M->getContext();
AdaptedTy.clear();
WorkSet.clear();
auto Src = getSPIRVSource(&Module);
if (std::get<0>(Src) != spv::SourceLanguageOpenCL_C)
return false;
for (auto &F : Module.functions())
adaptArgumentsByMetadata(&F);
for (auto &F : Module.functions())
adaptFunctionArguments(&F);
adaptArgumentsBySamplerUse(Module);
while (!WorkSet.empty()) {
Function *F = *WorkSet.begin();
WorkSet.erase(WorkSet.begin());
adaptFunction(F);
}
return false;
}
void OCLTypeToSPIRVBase::addAdaptedType(Value *V, Type *Ty) {
LLVM_DEBUG(dbgs() << "[add adapted type] ";
V->printAsOperand(dbgs(), true, M);
dbgs() << " => " << *Ty << '\n');
AdaptedTy[V] = Ty;
}
void OCLTypeToSPIRVBase::addWork(Function *F) {
LLVM_DEBUG(dbgs() << "[add work] "; F->printAsOperand(dbgs(), true, M);
dbgs() << '\n');
WorkSet.insert(F);
}
/// Create a new function type if \param F has arguments in AdaptedTy, and
/// propagates the adapted arguments to functions called by \param F.
void OCLTypeToSPIRVBase::adaptFunction(Function *F) {
LLVM_DEBUG(dbgs() << "\n[work on function] ";
F->printAsOperand(dbgs(), true, M); dbgs() << '\n');
assert(AdaptedTy.count(F) == 0);
std::vector<Type *> ArgTys;
bool Changed = false;
for (auto &I : F->args()) {
auto Loc = AdaptedTy.find(&I);
auto Found = (Loc != AdaptedTy.end());
Changed |= Found;
ArgTys.push_back(Found ? Loc->second : I.getType());
if (Found) {
Type *Ty = Loc->second;
for (auto &U : I.uses()) {
if (auto *CI = dyn_cast<CallInst>(U.getUser())) {
auto ArgIndex = CI->getArgOperandNo(&U);
auto *CF = CI->getCalledFunction();
if (AdaptedTy.count(CF) == 0) {
addAdaptedType(CF->getArg(ArgIndex), Ty);
addWork(CF);
}
}
}
}
}
if (!Changed)
return;
auto *FT = F->getFunctionType();
FT = FunctionType::get(FT->getReturnType(), ArgTys, FT->isVarArg());
addAdaptedType(F, TypedPointerType::get(FT, 0));
}
// Handle functions with sampler arguments that don't get called by
// a kernel function.
void OCLTypeToSPIRVBase::adaptArgumentsBySamplerUse(Module &M) {
SmallPtrSet<Function *, 5> Processed;
std::function<void(Function *, unsigned)> TraceArg = [&](Function *F,
unsigned Idx) {
// If we have cycles in the call graph in the future, bail out
// if we've already processed this function.
if (Processed.insert(F).second == false)
return;
for (auto *U : F->users()) {
auto *CI = dyn_cast<CallInst>(U);
if (!CI)
continue;
auto *SamplerArg = CI->getArgOperand(Idx);
if (!isa<Argument>(SamplerArg) ||
AdaptedTy.count(SamplerArg) != 0) // Already traced this, move on.
continue;
addAdaptedType(SamplerArg, getSPIRVType(OpTypeSampler));
auto *Caller = cast<Argument>(SamplerArg)->getParent();
addWork(Caller);
TraceArg(Caller, cast<Argument>(SamplerArg)->getArgNo());
}
};
for (auto &F : M) {
if (!F.empty()) // not decl
continue;
auto MangledName = F.getName();
StringRef DemangledName;
if (!oclIsBuiltin(MangledName, DemangledName, false))
continue;
// Note: kSPIRVName::ConvertHandleToSampledImageINTEL contains
// kSPIRVName::SampledImage as a substring, but we still want to continue in
// this case.
if (DemangledName.find(kSPIRVName::SampledImage) == std::string::npos ||
DemangledName.find(kSPIRVName::ConvertHandleToSampledImageINTEL) !=
std::string::npos)
continue;
TraceArg(&F, 1);
}
}
void OCLTypeToSPIRVBase::adaptFunctionArguments(Function *F) {
auto *TypeMD = F->getMetadata(SPIR_MD_KERNEL_ARG_BASE_TYPE);
if (TypeMD)
return;
bool Changed = false;
auto *Arg = F->arg_begin();
SmallVector<Type *, 4> ParamTys;
// If we couldn't get any information from demangling, there is nothing that
// can be done.
if (!getParameterTypes(F, ParamTys))
return;
for (unsigned I = 0; I < F->arg_size(); ++I, ++Arg) {
StructType *NewTy = nullptr;
if (auto *TPT = dyn_cast<TypedPointerType>(ParamTys[I]))
NewTy = dyn_cast_or_null<StructType>(TPT->getElementType());
if (NewTy && NewTy->isOpaque()) {
auto STName = NewTy->getStructName();
if (!hasAccessQualifiedName(STName))
continue;
if (STName.starts_with(kSPR2TypeName::ImagePrefix)) {
auto Ty = STName.str();
auto Acc = getAccessQualifier(Ty);
auto Desc = getImageDescriptor(ParamTys[I]);
addAdaptedType(
&*Arg, getSPIRVType(OpTypeImage, Type::getVoidTy(*Ctx), Desc, Acc));
Changed = true;
}
}
}
if (Changed)
addWork(F);
}
/// Go through all kernel functions, get access qualifier for image and pipe
/// types and use them to map the function arguments to the SPIR-V type.
/// ToDo: Map other OpenCL opaque types to SPIR-V types.
void OCLTypeToSPIRVBase::adaptArgumentsByMetadata(Function *F) {
auto *TypeMD = F->getMetadata(SPIR_MD_KERNEL_ARG_BASE_TYPE);
if (!TypeMD)
return;
bool Changed = false;
auto *Arg = F->arg_begin();
for (unsigned I = 0, E = TypeMD->getNumOperands(); I != E; ++I, ++Arg) {
auto OCLTyStr = getMDOperandAsString(TypeMD, I);
if (OCLTyStr == OCL_TYPE_NAME_SAMPLER_T) {
addAdaptedType(&(*Arg), getSPIRVType(OpTypeSampler));
Changed = true;
} else if (OCLTyStr.starts_with("image") && OCLTyStr.ends_with("_t")) {
auto Ty = (Twine("opencl.") + OCLTyStr).str();
if (auto *STy = StructType::getTypeByName(F->getContext(), Ty)) {
auto *ImageTy = TypedPointerType::get(STy, SPIRAS_Global);
auto Desc = getImageDescriptor(ImageTy);
auto *AccMD = F->getMetadata(SPIR_MD_KERNEL_ARG_ACCESS_QUAL);
assert(AccMD && "Invalid access qualifier metadata");
auto Acc = SPIRSPIRVAccessQualifierMap::map(
getMDOperandAsString(AccMD, I).str());
addAdaptedType(
&*Arg, getSPIRVType(OpTypeImage, Type::getVoidTy(*Ctx), Desc, Acc));
Changed = true;
}
}
}
if (Changed)
addWork(F);
}
// OCL sampler, image and pipe type need to be regularized before converting
// to SPIRV types.
//
// OCL sampler type is represented as i32 in LLVM, however in SPIRV it is
// represented as OpTypeSampler. Also LLVM uses the same pipe type to
// represent pipe types with different underlying data types, however
// in SPIRV they are different types. OCL image and pipe types do not
// encode access qualifier, which is part of SPIRV types for image and pipe.
//
// The function types in LLVM need to be regularized before translating
// to SPIRV function types:
//
// sampler type as i32 -> opencl.sampler_t opaque type
// opencl.pipe_t opaque type with underlying opencl type x and access
// qualifier y -> opencl.pipe_t.x.y opaque type
// opencl.image_x opaque type with access qualifier y ->
// opencl.image_x.y opaque type
//
// The converter relies on kernel_arg_base_type to identify the sampler
// type, the underlying data type of pipe type, and access qualifier for
// image and pipe types. The FE is responsible to generate the correct
// kernel_arg_base_type metadata.
//
// Alternatively,the FE may choose to use opencl.sampler_t to represent
// sampler type, use opencl.pipe_t.x.y to represent pipe type with underlying
// opencl data type x and access qualifier y, and use opencl.image_x.y to
// represent image_x type with access qualifier y.
//
Type *OCLTypeToSPIRVBase::getAdaptedArgumentType(Function *F, unsigned ArgNo) {
Value *Arg = F->getArg(ArgNo);
auto Loc = AdaptedTy.find(Arg);
if (Loc == AdaptedTy.end())
return nullptr;
return Loc->second;
}
} // namespace SPIRV
AnalysisKey OCLTypeToSPIRVPass::Key;
INITIALIZE_PASS(OCLTypeToSPIRVLegacy, "cltytospv", "Adapt OCL types for SPIR-V",
false, true)
ModulePass *llvm::createOCLTypeToSPIRVLegacy() {
return new OCLTypeToSPIRVLegacy();
}
@@ -0,0 +1,101 @@
//===- OCLTypeToSPIRV.h - Adapt types from OCL for SPIRV --------*- C++ -*-===//
//
// The LLVM/SPIRV Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
//
// 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 Advanced Micro Devices, Inc., 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.
//
//===----------------------------------------------------------------------===//
//
// This file implements adaptation of OCL types for SPIRV. It does not modify
// the module. Instead, it returns adapted function type based on kernel
// argument metadata. Later LLVM/SPIRV translator will translate the adapted
// type instead of the original type.
//
//===----------------------------------------------------------------------===//
#ifndef SPIRV_OCLTYPETOSPIRV_H
#define SPIRV_OCLTYPETOSPIRV_H
#include "LLVMSPIRVLib.h"
#include "SPIRVBuiltinHelper.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Pass.h"
#include <map>
#include <set>
namespace SPIRV {
class OCLTypeToSPIRVBase : protected BuiltinCallHelper {
public:
OCLTypeToSPIRVBase();
bool runOCLTypeToSPIRV(llvm::Module &M);
/// Returns the adapted type of the corresponding argument for a function. If
/// the type is a pointer type, it will return a TypedPointerType instead.
llvm::Type *getAdaptedArgumentType(llvm::Function *F, unsigned ArgNo);
private:
llvm::Module *M;
llvm::LLVMContext *Ctx;
// Map of argument/Function -> adapted type (probably TypedPointerType)
std::unordered_map<llvm::Value *, llvm::Type *> AdaptedTy;
std::set<llvm::Function *> WorkSet; // Functions to be adapted
void adaptFunctionArguments(llvm::Function *F);
void adaptArgumentsByMetadata(llvm::Function *F);
void adaptArgumentsBySamplerUse(llvm::Module &M);
void adaptFunction(llvm::Function *F);
void addAdaptedType(llvm::Value *V, llvm::Type *Ty);
void addWork(llvm::Function *F);
};
class OCLTypeToSPIRVLegacy : public OCLTypeToSPIRVBase,
public llvm::ModulePass {
public:
OCLTypeToSPIRVLegacy();
void getAnalysisUsage(llvm::AnalysisUsage &AU) const override;
bool runOnModule(llvm::Module &M) override;
static char ID;
};
class OCLTypeToSPIRVPass : public OCLTypeToSPIRVBase,
public llvm::AnalysisInfoMixin<OCLTypeToSPIRVPass> {
public:
using Result = OCLTypeToSPIRVBase;
static llvm::AnalysisKey Key;
OCLTypeToSPIRVBase &run(llvm::Module &F, llvm::ModuleAnalysisManager &MAM);
};
} // namespace SPIRV
#endif // SPIRV_OCLTYPETOSPIRV_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,773 @@
//===- OCLUtil.h - OCL Utilities declarations -------------------*- C++ -*-===//
//
// The LLVM/SPIRV Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
//
// 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 Advanced Micro Devices, Inc., 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.
//
//===----------------------------------------------------------------------===//
//
// This file declares OCL utility functions.
//
//===----------------------------------------------------------------------===//
#ifndef SPIRV_OCLUTIL_H
#define SPIRV_OCLUTIL_H
#include "SPIRVInternal.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instructions.h"
#include "llvm/Support/Path.h"
#include <atomic>
#include <functional>
#include <tuple>
#include <type_traits>
#include <utility>
using namespace SPIRV;
using namespace llvm;
using namespace spv;
namespace SPIRV {
class BuiltinCallMutator;
} // namespace SPIRV
namespace OCLUtil {
///////////////////////////////////////////////////////////////////////////////
//
// Enums
//
///////////////////////////////////////////////////////////////////////////////
enum OCLMemFenceKind {
OCLMF_Local = 1,
OCLMF_Global = 2,
OCLMF_Image = 4,
};
// This enum declares extra constants for OpenCL mem_fence flag. It includes
// combinations of local/global/image flags.
enum OCLMemFenceExtendedKind {
OCLMFEx_Local = OCLMF_Local,
OCLMFEx_Global = OCLMF_Global,
OCLMFEx_Local_Global = OCLMF_Global | OCLMF_Local,
OCLMFEx_Image = OCLMF_Image,
OCLMFEx_Image_Local = OCLMF_Image | OCLMF_Local,
OCLMFEx_Image_Global = OCLMF_Image | OCLMF_Global,
OCLMFEx_Image_Local_Global = OCLMF_Image | OCLMF_Global | OCLMF_Local,
};
enum OCLScopeKind {
OCLMS_work_item,
OCLMS_work_group,
OCLMS_device,
OCLMS_all_svm_devices,
OCLMS_sub_group,
};
// The enum below declares constants corresponding to memory synchronization
// operations constants defined in
// https://www.khronos.org/registry/OpenCL/sdk/2.1/docs/man/xhtml/memory_order.html
// To avoid any inconsistence here, constants are explicitly initialized with
// the corresponding constants from 'std::memory_order' enum.
enum OCLMemOrderKind {
#if __cplusplus >= 202002L
OCLMO_relaxed = static_cast<int>(std::memory_order::relaxed),
OCLMO_acquire = static_cast<int>(std::memory_order::acquire),
OCLMO_release = static_cast<int>(std::memory_order::release),
OCLMO_acq_rel = static_cast<int>(std::memory_order::acq_rel),
OCLMO_seq_cst = static_cast<int>(std::memory_order::seq_cst)
#else
OCLMO_relaxed = std::memory_order::memory_order_relaxed,
OCLMO_acquire = std::memory_order::memory_order_acquire,
OCLMO_release = std::memory_order::memory_order_release,
OCLMO_acq_rel = std::memory_order::memory_order_acq_rel,
OCLMO_seq_cst = std::memory_order::memory_order_seq_cst
#endif
};
enum IntelFPGAMemoryAccessesVal {
BurstCoalesce = 0x1,
CacheSizeFlag = 0x2,
DontStaticallyCoalesce = 0x4,
PrefetchFlag = 0x8
};
///////////////////////////////////////////////////////////////////////////////
//
// Types
//
///////////////////////////////////////////////////////////////////////////////
typedef SPIRVMap<OCLMemFenceKind, MemorySemanticsMask> OCLMemFenceMap;
typedef SPIRVMap<OCLMemFenceExtendedKind, MemorySemanticsMask>
OCLMemFenceExtendedMap;
typedef SPIRVMap<OCLMemOrderKind, unsigned, MemorySemanticsMask> OCLMemOrderMap;
typedef SPIRVMap<OCLScopeKind, Scope> OCLMemScopeMap;
typedef SPIRVMap<std::string, Scope> OCLStrMemScopeMap;
typedef SPIRVMap<std::string, SPIRVGroupOperationKind>
SPIRSPIRVGroupOperationMap;
typedef SPIRVMap<std::string, SPIRVFPRoundingModeKind>
SPIRSPIRVFPRoundingModeMap;
typedef SPIRVMap<std::string, Op, SPIRVInstruction> OCLSPIRVBuiltinMap;
class OCL12Builtin;
typedef SPIRVMap<std::string, Op, OCL12Builtin> OCL12SPIRVBuiltinMap;
typedef SPIRVMap<std::string, SPIRVBuiltinVariableKind>
SPIRSPIRVBuiltinVariableMap;
/// Tuple of literals for atomic_work_item_fence (flag, order, scope)
typedef std::tuple<unsigned, OCLMemOrderKind, OCLScopeKind>
AtomicWorkItemFenceLiterals;
/// Tuple of literals for work_group_barrier or sub_group_barrier
/// (flag, mem_scope, exec_scope)
typedef std::tuple<unsigned, OCLScopeKind, OCLScopeKind> BarrierLiterals;
class OCLOpaqueType;
typedef SPIRVMap<std::string, Op, OCLOpaqueType> OCLOpaqueTypeOpCodeMap;
/// Information for translating OCL builtin.
struct OCLBuiltinTransInfo {
std::string UniqName;
std::string MangledName;
std::string Postfix; // Postfix to be added
/// Postprocessor of operands
std::function<void(BuiltinCallMutator &)> PostProc;
Type *RetTy; // Return type of the translated function
OCLBuiltinTransInfo() : RetTy(nullptr) {
PostProc = [](BuiltinCallMutator &) {};
}
};
///////////////////////////////////////////////////////////////////////////////
//
// Constants
//
///////////////////////////////////////////////////////////////////////////////
namespace kOCLBuiltinName {
const static char All[] = "all";
const static char Any[] = "any";
#define _SPIRV_OP(x, y) \
const static char ArbitraryFloat##x##INTEL[] = "intel_arbitrary_float_" #y;
_SPIRV_OP(Cast, cast)
_SPIRV_OP(CastFromInt, cast_from_int)
_SPIRV_OP(CastToInt, cast_to_int)
_SPIRV_OP(Add, add)
_SPIRV_OP(Sub, sub)
_SPIRV_OP(Mul, mul)
_SPIRV_OP(Div, div)
_SPIRV_OP(GT, gt)
_SPIRV_OP(GE, ge)
_SPIRV_OP(LT, lt)
_SPIRV_OP(LE, le)
_SPIRV_OP(EQ, eq)
_SPIRV_OP(Recip, recip)
_SPIRV_OP(RSqrt, rsqrt)
_SPIRV_OP(Cbrt, cbrt)
_SPIRV_OP(Hypot, hypot)
_SPIRV_OP(Sqrt, sqrt)
_SPIRV_OP(Log, log)
_SPIRV_OP(Log2, log2)
_SPIRV_OP(Log10, log10)
_SPIRV_OP(Log1p, log1p)
_SPIRV_OP(Exp, exp)
_SPIRV_OP(Exp2, exp2)
_SPIRV_OP(Exp10, exp10)
_SPIRV_OP(Expm1, expm1)
_SPIRV_OP(Sin, sin)
_SPIRV_OP(Cos, cos)
_SPIRV_OP(SinCos, sincos)
_SPIRV_OP(SinPi, sinpi)
_SPIRV_OP(CosPi, cospi)
_SPIRV_OP(SinCosPi, sincospi)
_SPIRV_OP(ASin, asin)
_SPIRV_OP(ASinPi, asinpi)
_SPIRV_OP(ACos, acos)
_SPIRV_OP(ACosPi, acospi)
_SPIRV_OP(ATan, atan)
_SPIRV_OP(ATanPi, atanpi)
_SPIRV_OP(ATan2, atan2)
_SPIRV_OP(Pow, pow)
_SPIRV_OP(PowR, powr)
_SPIRV_OP(PowN, pown)
#undef _SPIRV_OP
const static char AsyncWorkGroupCopy[] = "async_work_group_copy";
const static char AsyncWorkGroupStridedCopy[] = "async_work_group_strided_copy";
const static char AtomPrefix[] = "atom_";
const static char AtomCmpXchg[] = "atom_cmpxchg";
const static char AtomicPrefix[] = "atomic_";
const static char AtomicCmpXchg[] = "atomic_cmpxchg";
const static char AtomicCmpXchgStrong[] = "atomic_compare_exchange_strong";
const static char AtomicCmpXchgStrongExplicit[] =
"atomic_compare_exchange_strong_explicit";
const static char AtomicCmpXchgWeak[] = "atomic_compare_exchange_weak";
const static char AtomicCmpXchgWeakExplicit[] =
"atomic_compare_exchange_weak_explicit";
const static char AtomicInit[] = "atomic_init";
const static char AtomicWorkItemFence[] = "atomic_work_item_fence";
const static char Barrier[] = "barrier";
const static char Clamp[] = "clamp";
const static char ClockReadPrefix[] = "clock_read_";
const static char ConvertPrefix[] = "convert_";
const static char Dot[] = "dot";
const static char DotAccSat[] = "dot_acc_sat";
const static char Dot4x8PackedPrefix[] = "dot_4x8packed_";
const static char DotAccSat4x8PackedPrefix[] = "dot_acc_sat_4x8packed_";
const static char EnqueueKernel[] = "enqueue_kernel";
const static char FixedSqrtINTEL[] = "intel_arbitrary_fixed_sqrt";
const static char FixedRecipINTEL[] = "intel_arbitrary_fixed_recip";
const static char FixedRsqrtINTEL[] = "intel_arbitrary_fixed_rsqrt";
const static char FixedSinINTEL[] = "intel_arbitrary_fixed_sin";
const static char FixedCosINTEL[] = "intel_arbitrary_fixed_cos";
const static char FixedSinCosINTEL[] = "intel_arbitrary_fixed_sincos";
const static char FixedSinPiINTEL[] = "intel_arbitrary_fixed_sinpi";
const static char FixedCosPiINTEL[] = "intel_arbitrary_fixed_cospi";
const static char FixedSinCosPiINTEL[] = "intel_arbitrary_fixed_sincospi";
const static char FixedLogINTEL[] = "intel_arbitrary_fixed_log";
const static char FixedExpINTEL[] = "intel_arbitrary_fixed_exp";
const static char FMax[] = "fmax";
const static char FMin[] = "fmin";
const static char FPGARegIntel[] = "__builtin_intel_fpga_reg";
const static char GetFence[] = "get_fence";
const static char GetImageArraySize[] = "get_image_array_size";
const static char GetImageChannelOrder[] = "get_image_channel_order";
const static char GetImageChannelDataType[] = "get_image_channel_data_type";
const static char GetImageDepth[] = "get_image_depth";
const static char GetImageDim[] = "get_image_dim";
const static char GetImageHeight[] = "get_image_height";
const static char GetImageWidth[] = "get_image_width";
const static char IsFinite[] = "isfinite";
const static char IsNan[] = "isnan";
const static char IsNormal[] = "isnormal";
const static char IsInf[] = "isinf";
const static char Max[] = "max";
const static char MemFence[] = "mem_fence";
const static char ReadMemFence[] = "read_mem_fence";
const static char WriteMemFence[] = "write_mem_fence";
const static char Min[] = "min";
const static char Mix[] = "mix";
const static char NDRangePrefix[] = "ndrange_";
const static char Pipe[] = "pipe";
const static char ReadImage[] = "read_image";
const static char ReadPipe[] = "read_pipe";
const static char ReadPipeBlockingINTEL[] = "read_pipe_bl";
const static char RoundingPrefix[] = "_r";
const static char Sampled[] = "sampled_";
const static char SampledReadImage[] = "sampled_read_image";
const static char Signbit[] = "signbit";
const static char SmoothStep[] = "smoothstep";
const static char Step[] = "step";
const static char SubGroupPrefix[] = "sub_group_";
const static char SubGroupBarrier[] = "sub_group_barrier";
const static char SubPrefix[] = "sub_";
const static char ToGlobal[] = "to_global";
const static char ToLocal[] = "to_local";
const static char ToPrivate[] = "to_private";
const static char VLoadPrefix[] = "vload";
const static char VLoadAPrefix[] = "vloada";
const static char VLoadHalf[] = "vload_half";
const static char VStorePrefix[] = "vstore";
const static char VStoreAPrefix[] = "vstorea";
const static char WaitGroupEvent[] = "wait_group_events";
const static char WriteImage[] = "write_image";
const static char WorkGroupBarrier[] = "work_group_barrier";
const static char WritePipe[] = "write_pipe";
const static char WritePipeBlockingINTEL[] = "write_pipe_bl";
const static char WorkGroupPrefix[] = "work_group_";
const static char WorkGroupAll[] = "work_group_all";
const static char WorkGroupAny[] = "work_group_any";
const static char SubGroupAll[] = "sub_group_all";
const static char SubGroupAny[] = "sub_group_any";
const static char WorkPrefix[] = "work_";
const static char SubgroupBlockReadINTELPrefix[] = "intel_sub_group_block_read";
const static char SubgroupBlockWriteINTELPrefix[] =
"intel_sub_group_block_write";
const static char SubgroupImageMediaBlockINTELPrefix[] =
"intel_sub_group_media_block";
const static char SplitBarrierINTELPrefix[] = "intel_work_group_barrier_";
const static char LDEXP[] = "ldexp";
#define _SPIRV_OP(x) \
const static char ConvertBFloat16##x##AsUShort##x[] = \
"intel_convert_bfloat16" #x "_as_ushort" #x;
_SPIRV_OP()
_SPIRV_OP(2)
_SPIRV_OP(3)
_SPIRV_OP(4)
_SPIRV_OP(8)
_SPIRV_OP(16)
#undef _SPIRV_OP
#define _SPIRV_OP(x) \
const static char ConvertAsBFloat16##x##Float##x[] = \
"intel_convert_as_bfloat16" #x "_float" #x;
_SPIRV_OP()
_SPIRV_OP(2)
_SPIRV_OP(3)
_SPIRV_OP(4)
_SPIRV_OP(8)
_SPIRV_OP(16)
#undef _SPIRV_OP
} // namespace kOCLBuiltinName
/// Offset for OpenCL image channel order enumeration values.
const unsigned int OCLImageChannelOrderOffset = 0x10B0;
/// Offset for OpenCL image channel data type enumeration values.
const unsigned int OCLImageChannelDataTypeOffset = 0x10D0;
/// OCL 1.x atomic memory order when translated to 2.0 atomics.
const OCLMemOrderKind OCLLegacyAtomicMemOrder = OCLMO_relaxed;
/// OCL 1.x atomic memory scope when translated to 2.0 atomics.
const OCLScopeKind OCLLegacyAtomicMemScope = OCLMS_work_group;
namespace kOCLVer {
const unsigned CL12 = 102000;
const unsigned CL20 = 200000;
const unsigned CL30 = 300000;
const unsigned CLCXX10 = 100000;
const unsigned CLCXX2021 = 202100000;
} // namespace kOCLVer
namespace OclExt {
// clang-format off
enum Kind {
#define _SPIRV_OP(x) x,
_SPIRV_OP(cl_images)
_SPIRV_OP(cl_doubles)
_SPIRV_OP(cl_khr_int64_base_atomics)
_SPIRV_OP(cl_khr_int64_extended_atomics)
_SPIRV_OP(cl_khr_fp16)
_SPIRV_OP(cl_khr_gl_sharing)
_SPIRV_OP(cl_khr_gl_event)
_SPIRV_OP(cl_khr_d3d10_sharing)
_SPIRV_OP(cl_khr_media_sharing)
_SPIRV_OP(cl_khr_d3d11_sharing)
_SPIRV_OP(cl_khr_global_int32_base_atomics)
_SPIRV_OP(cl_khr_global_int32_extended_atomics)
_SPIRV_OP(cl_khr_local_int32_base_atomics)
_SPIRV_OP(cl_khr_local_int32_extended_atomics)
_SPIRV_OP(cl_khr_byte_addressable_store)
_SPIRV_OP(cl_khr_3d_image_writes)
_SPIRV_OP(cl_khr_gl_msaa_sharing)
_SPIRV_OP(cl_khr_depth_images)
_SPIRV_OP(cl_khr_gl_depth_images)
_SPIRV_OP(cl_khr_subgroups)
_SPIRV_OP(cl_khr_mipmap_image)
_SPIRV_OP(cl_khr_mipmap_image_writes)
_SPIRV_OP(cl_khr_egl_event)
_SPIRV_OP(cl_khr_srgb_image_writes)
_SPIRV_OP(cl_khr_extended_bit_ops)
#undef _SPIRV_OP
};
// clang-format on
} // namespace OclExt
namespace kOCLSubgroupsAVCIntel {
const static char Prefix[] = "intel_sub_group_avc_";
const static char MCEPrefix[] = "intel_sub_group_avc_mce_";
const static char IMEPrefix[] = "intel_sub_group_avc_ime_";
const static char REFPrefix[] = "intel_sub_group_avc_ref_";
const static char SICPrefix[] = "intel_sub_group_avc_sic_";
const static char TypePrefix[] = "opencl.intel_sub_group_avc_";
} // namespace kOCLSubgroupsAVCIntel
///////////////////////////////////////////////////////////////////////////////
//
// Functions
//
///////////////////////////////////////////////////////////////////////////////
/// Get instruction index for SPIR-V extended instruction for OpenCL.std
/// extended instruction set.
/// \param MangledName The mangled name of OpenCL builtin function.
/// \param DemangledName The demangled name of OpenCL builtin function if
/// not empty.
/// \return instruction index of extended instruction if the OpenCL builtin
/// function is translated to an extended instruction, otherwise ~0U.
unsigned getExtOp(StringRef MangledName, StringRef DemangledName = "");
/// Get literal arguments of call of atomic_work_item_fence.
AtomicWorkItemFenceLiterals getAtomicWorkItemFenceLiterals(CallInst *CI);
/// Get literal arguments of call of work_group_barrier or sub_group_barrier.
BarrierLiterals getBarrierLiterals(CallInst *CI);
/// Get number of memory order arguments for atomic builtin function.
size_t getAtomicBuiltinNumMemoryOrderArgs(StringRef Name);
/// Get number of memory order arguments for spirv atomic builtin function.
size_t getSPIRVAtomicBuiltinNumMemoryOrderArgs(Op OC);
/// Return true for OpenCL builtins which do compute operations
/// (like add, sub, min, max, inc, dec, ...) atomically
bool isComputeAtomicOCLBuiltin(StringRef DemangledName);
/// Get OCL version from metadata opencl.ocl.version.
/// \param AllowMulti Allows multiple operands if true.
/// \return OCL version encoded as Major*10^5+Minor*10^3+Rev,
/// e.g. 201000 for OCL 2.1, 200000 for OCL 2.0, 102000 for OCL 1.2,
/// 0 if metadata not found.
/// If there are multiple operands, check they are identical.
unsigned getOCLVersion(Module *M, bool AllowMulti = false);
/// Encode OpenCL version as Major*10^5+Minor*10^3+Rev.
unsigned encodeOCLVer(unsigned short Major, unsigned char Minor,
unsigned char Rev);
/// Decode OpenCL version which is encoded as Major*10^5+Minor*10^3+Rev
std::tuple<unsigned short, unsigned char, unsigned char>
decodeOCLVer(unsigned Ver);
/// Decode a MDNode assuming it contains three integer constants.
SmallVector<unsigned, 3> decodeMDNode(MDNode *N);
/// Get full path from debug info metadata
/// Return empty string if the path is not available.
template <typename T> std::string getFullPath(const T *Scope) {
if (!Scope)
return std::string();
StringRef Filename = Scope->getFilename();
auto Style = sys::path::Style::native;
if (sys::path::is_absolute(Filename, Style))
return Filename.str();
SmallString<16> DirName = Scope->getDirectory();
sys::path::append(DirName, Style, Filename.str());
return DirName.str().str();
}
/// Decode OpenCL vector type hint MDNode and encode it as SPIR-V execution
/// mode VecTypeHint.
unsigned transVecTypeHint(MDNode *Node);
/// Decode SPIR-V encoding of vector type hint execution mode.
Type *decodeVecTypeHint(LLVMContext &C, unsigned Code);
SPIRAddressSpace getOCLOpaqueTypeAddrSpace(Op OpCode);
SPIR::TypeAttributeEnum getOCLOpaqueTypeAddrSpace(SPIR::TypePrimitiveEnum Prim);
inline unsigned mapOCLMemSemanticToSPIRV(unsigned MemFenceFlag,
OCLMemOrderKind Order) {
return OCLMemOrderMap::map(Order) | mapBitMask<OCLMemFenceMap>(MemFenceFlag);
}
inline unsigned mapOCLMemFenceFlagToSPIRV(unsigned MemFenceFlag) {
return mapBitMask<OCLMemFenceMap>(MemFenceFlag);
}
inline std::pair<unsigned, OCLMemOrderKind>
mapSPIRVMemSemanticToOCL(unsigned Sema) {
return std::make_pair(
rmapBitMask<OCLMemFenceMap>(Sema),
OCLMemOrderMap::rmap(extractSPIRVMemOrderSemantic(Sema)));
}
inline OCLMemOrderKind mapSPIRVMemOrderToOCL(unsigned Sema) {
return OCLMemOrderMap::rmap(extractSPIRVMemOrderSemantic(Sema));
}
bool isPipeOrAddressSpaceCastBI(const StringRef MangledName);
bool isEnqueueKernelBI(const StringRef MangledName);
bool isKernelQueryBI(const StringRef MangledName);
/// Check that the type is the sampler_t
bool isSamplerTy(Type *Ty);
// Checks if the binary operator is an unfused fmul + fadd instruction.
bool isUnfusedMulAdd(BinaryOperator *B);
// Get data and vector size postfix for sugroup_block_{read|write} builtins
// as specified by cl_intel_subgroups* extensions.
// Scalar data assumed to be represented as vector of one element.
std::string getIntelSubgroupBlockDataPostfix(unsigned ElementBitSize,
unsigned VectorNumElements);
void insertImageNameAccessQualifier(SPIRVAccessQualifierKind Acc,
std::string &Name);
std::unique_ptr<SPIRV::BuiltinFuncMangleInfo> makeMangler(Function &F);
} // namespace OCLUtil
using namespace OCLUtil;
namespace SPIRV {
template <class KeyTy, class ValTy, class Identifier = void>
Instruction *
getOrCreateSwitchFunc(StringRef MapName, Value *V,
const SPIRVMap<KeyTy, ValTy, Identifier> &Map,
bool IsReverse, std::optional<int> DefaultCase,
Instruction *InsertPoint, int KeyMask = 0) {
static_assert(std::is_convertible<KeyTy, int>::value &&
std::is_convertible<ValTy, int>::value,
"Can map only integer values");
Type *Ty = V->getType();
assert(Ty && Ty->isIntegerTy() && "Can't map non-integer types");
Module *M = InsertPoint->getModule();
Function *F = getOrCreateFunction(M, Ty, Ty, MapName);
if (!F->empty()) // The switch function already exists. just call it.
return addCallInst(M, MapName, Ty, V, nullptr, InsertPoint);
F->setLinkage(GlobalValue::PrivateLinkage);
LLVMContext &Ctx = M->getContext();
BasicBlock *EntryBB = BasicBlock::Create(Ctx, "entry", F);
IRBuilder<> EntryIRB(EntryBB);
AllocaInst *Result = EntryIRB.CreateAlloca(Ty, nullptr, "result");
SwitchInst *SI;
F->arg_begin()->setName("key");
if (KeyMask) {
Value *MaskV = ConstantInt::get(Type::getInt32Ty(Ctx), KeyMask);
Value *NewKey = EntryIRB.CreateAnd(MaskV, F->arg_begin());
NewKey->setName("key.masked");
SI = EntryIRB.CreateSwitch(NewKey, EntryBB);
} else {
SI = EntryIRB.CreateSwitch(F->arg_begin(), EntryBB);
}
if (!DefaultCase) {
BasicBlock *DefaultBB = BasicBlock::Create(Ctx, "default", F);
IRBuilder<> DefaultIRB(DefaultBB);
DefaultIRB.CreateUnreachable();
SI->setDefaultDest(DefaultBB);
}
BasicBlock *ExitBB = BasicBlock::Create(Ctx, "exit", F);
BasicBlock *CaseBB = nullptr;
Map.foreach ([&](int Key, int Val) {
if (IsReverse)
std::swap(Key, Val);
CaseBB = BasicBlock::Create(Ctx, "case." + Twine(Key), F);
IRBuilder<> CaseIRB(CaseBB);
CaseIRB.CreateStore(CaseIRB.getInt32(Val), Result);
CaseIRB.CreateBr(ExitBB);
SI->addCase(EntryIRB.getInt32(Key), CaseBB);
if (Key == DefaultCase)
SI->setDefaultDest(CaseBB);
});
ExitBB->moveAfter(CaseBB);
IRBuilder<> ExitIRB(ExitBB);
LoadInst *RetVal = ExitIRB.CreateLoad(Ty, Result, "retVal");
ExitIRB.CreateRet(RetVal);
assert(SI->getDefaultDest() != EntryBB &&
"Invalid default destination in switch");
return addCallInst(M, MapName, Ty, V, nullptr, InsertPoint);
}
/// Maps LLVM SyncScope into SPIR-V Scope.
///
/// \param [in] Ctx Context for the LLVM SyncScope
/// \param [in] Id SyncScope::ID value which needs to be mapped to SPIR-V Scope
inline spv::Scope toSPIRVScope(const LLVMContext &Ctx, SyncScope::ID Id) {
// We follow Clang/LLVM convention by which the default is System scope, which
// in SPIR-V maps to CrossDevice scope. This is in order to ensure that the
// resulting SPIR-V is conservatively correct (i.e. always works), under the
// assumption that it is the responsibility of the higher level language to
// choose a narrower scope, if desired.
switch (Id) {
case SyncScope::SingleThread:
return spv::ScopeInvocation;
case SyncScope::System:
return spv::ScopeCrossDevice;
default:
SmallVector<StringRef> SSIDs;
Ctx.getSyncScopeNames(SSIDs);
spv::Scope S = ScopeCrossDevice; // Default to CrossDevice scope.
OCLStrMemScopeMap::find(SSIDs[Id].str(), &S);
return S;
}
}
/// Performs conversion from OpenCL memory_scope into SPIR-V Scope.
///
/// Supports both constant and non-constant values. To handle the latter case,
/// function with switch..case statement will be inserted into module which
/// \arg InsertBefore belongs to (in order to perform mapping at runtime)
///
/// \param [in] MemScope memory_scope value which needs to be translated
/// \param [in] DefaultCase default value for switch..case construct if
/// dynamic mapping is used
/// \param [in] InsertBefore insertion point for call into conversion function
/// which is generated if \arg MemScope is not a constant
/// \returns \c Value corresponding to SPIR-V Scope equivalent to OpenCL
/// memory_scope passed in \arg MemScope
Value *transOCLMemScopeIntoSPIRVScope(Value *MemScope,
std::optional<int> DefaultCase,
Instruction *InsertBefore);
/// Performs conversion from OpenCL memory_order into SPIR-V Memory Semantics.
///
/// Supports both constant and non-constant values. To handle the latter case,
/// function with switch..case statement will be inserted into module which
/// \arg InsertBefore belongs to (in order to perform mapping at runtime)
///
/// \param [in] MemOrder memory_scope value which needs to be translated
/// \param [in] DefaultCase default value for switch..case construct if
/// dynamic mapping is used
/// \param [in] InsertBefore insertion point for call into conversion function
/// which is generated if \arg MemOrder is not a constant
/// \returns \c Value corresponding to SPIR-V Memory Semantics equivalent to
/// OpenCL memory_order passed in \arg MemOrder
Value *transOCLMemOrderIntoSPIRVMemorySemantics(Value *MemOrder,
std::optional<int> DefaultCase,
Instruction *InsertBefore);
/// Performs conversion from SPIR-V Scope into OpenCL memory_scope.
///
/// Supports both constant and non-constant values. To handle the latter case,
/// function with switch..case statement will be inserted into module which
/// \arg InsertBefore belongs to (in order to perform mapping at runtime)
///
/// \param [in] MemScope Scope value which needs to be translated
/// \param [in] InsertBefore insertion point for call into conversion function
/// which is generated if \arg MemScope is not a constant
/// \returns \c Value corresponding to OpenCL memory_scope equivalent to SPIR-V
/// Scope passed in \arg MemScope
Value *transSPIRVMemoryScopeIntoOCLMemoryScope(Value *MemScope,
Instruction *InsertBefore);
/// Performs conversion from SPIR-V Memory Semantics into OpenCL memory_order.
///
/// Supports both constant and non-constant values. To handle the latter case,
/// function with switch..case statement will be inserted into module which
/// \arg InsertBefore belongs to (in order to perform mapping at runtime)
///
/// \param [in] MemorySemantics Memory Semantics value which needs to be
/// translated
/// \param [in] InsertBefore insertion point for call into conversion function
/// which is generated if \arg MemorySemantics is not a constant
/// \returns \c Value corresponding to OpenCL memory_order equivalent to SPIR-V
/// Memory Semantics passed in \arg MemorySemantics
Value *transSPIRVMemorySemanticsIntoOCLMemoryOrder(Value *MemorySemantics,
Instruction *InsertBefore);
/// Performs conversion from SPIR-V Memory Semantics into OpenCL
/// mem_fence_flags.
///
/// Supports both constant and non-constant values. To handle the latter case,
/// function with switch..case statement will be inserted into module which
/// \arg InsertBefore belongs to (in order to perform mapping at runtime)
///
/// \param [in] MemorySemantics Memory Semantics value which needs to be
/// translated
/// \param [in] InsertBefore insertion point for call into conversion function
/// which is generated if \arg MemorySemantics is not a constant
/// \returns \c Value corresponding to OpenCL mem_fence_flags equivalent to
/// SPIR-V Memory Semantics passed in \arg MemorySemantics
Value *transSPIRVMemorySemanticsIntoOCLMemFenceFlags(Value *MemorySemantics,
Instruction *InsertBefore);
class SPIRVSubgroupsAVCIntelInst;
typedef SPIRVMap<std::string, Op, SPIRVSubgroupsAVCIntelInst>
OCLSPIRVSubgroupAVCIntelBuiltinMap;
typedef SPIRVMap<AtomicRMWInst::BinOp, Op> LLVMSPIRVAtomicRmwOpCodeMap;
class SPIRVFixedPointIntelInst;
template <>
inline void SPIRVMap<std::string, Op, SPIRVFixedPointIntelInst>::init() {
#define _SPIRV_OP(x, y) add("intel_arbitrary_fixed_" #x, OpFixed##y##INTEL);
_SPIRV_OP(sqrt, Sqrt)
_SPIRV_OP(recip, Recip)
_SPIRV_OP(rsqrt, Rsqrt)
_SPIRV_OP(sin, Sin)
_SPIRV_OP(cos, Cos)
_SPIRV_OP(sincos, SinCos)
_SPIRV_OP(sinpi, SinPi)
_SPIRV_OP(cospi, CosPi)
_SPIRV_OP(sincospi, SinCosPi)
_SPIRV_OP(log, Log)
_SPIRV_OP(exp, Exp)
#undef _SPIRV_OP
}
typedef SPIRVMap<std::string, Op, SPIRVFixedPointIntelInst>
SPIRVFixedPointIntelMap;
class SPIRVArbFloatIntelInst;
template <>
inline void SPIRVMap<std::string, Op, SPIRVArbFloatIntelInst>::init() {
#define _SPIRV_OP(x, y) \
add("intel_arbitrary_float_" #y, OpArbitraryFloat##x##INTEL);
_SPIRV_OP(Cast, cast)
_SPIRV_OP(CastFromInt, cast_from_int)
_SPIRV_OP(CastToInt, cast_to_int)
_SPIRV_OP(Add, add)
_SPIRV_OP(Sub, sub)
_SPIRV_OP(Mul, mul)
_SPIRV_OP(Div, div)
_SPIRV_OP(GT, gt)
_SPIRV_OP(GE, ge)
_SPIRV_OP(LT, lt)
_SPIRV_OP(LE, le)
_SPIRV_OP(EQ, eq)
_SPIRV_OP(Recip, recip)
_SPIRV_OP(RSqrt, rsqrt)
_SPIRV_OP(Cbrt, cbrt)
_SPIRV_OP(Hypot, hypot)
_SPIRV_OP(Sqrt, sqrt)
_SPIRV_OP(Log, log)
_SPIRV_OP(Log2, log2)
_SPIRV_OP(Log10, log10)
_SPIRV_OP(Log1p, log1p)
_SPIRV_OP(Exp, exp)
_SPIRV_OP(Exp2, exp2)
_SPIRV_OP(Exp10, exp10)
_SPIRV_OP(Expm1, expm1)
_SPIRV_OP(Sin, sin)
_SPIRV_OP(Cos, cos)
_SPIRV_OP(SinCos, sincos)
_SPIRV_OP(SinPi, sinpi)
_SPIRV_OP(CosPi, cospi)
_SPIRV_OP(SinCosPi, sincospi)
_SPIRV_OP(ASin, asin)
_SPIRV_OP(ASinPi, asinpi)
_SPIRV_OP(ACos, acos)
_SPIRV_OP(ACosPi, acospi)
_SPIRV_OP(ATan, atan)
_SPIRV_OP(ATanPi, atanpi)
_SPIRV_OP(ATan2, atan2)
_SPIRV_OP(Pow, pow)
_SPIRV_OP(PowR, powr)
_SPIRV_OP(PowN, pown)
#undef _SPIRV_OP
}
typedef SPIRVMap<std::string, Op, SPIRVArbFloatIntelInst> SPIRVArbFloatIntelMap;
} // namespace SPIRV
#endif // SPIRV_OCLUTIL_H
@@ -0,0 +1,135 @@
//===- PassPlugin.cpp - Register SPIRV passes as plugin -------------------===//
//
// The LLVM/SPIR-V Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2024 The Khronos Group Inc.
//
// 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 Khronos Group, 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.
//
//===----------------------------------------------------------------------===//
//
// This file implements pass plugin to register llvm-spirv passes for opt tool.
//
//===----------------------------------------------------------------------===//
#include "OCLToSPIRV.h"
#include "PreprocessMetadata.h"
#include "SPIRVLowerBitCastToNonStandardType.h"
#include "SPIRVLowerBool.h"
#include "SPIRVLowerConstExpr.h"
#include "SPIRVLowerLLVMIntrinsic.h"
#include "SPIRVLowerMemmove.h"
#include "SPIRVLowerOCLBlocks.h"
#include "SPIRVRegularizeLLVM.h"
#include "SPIRVToOCL.h"
#include "SPIRVWriter.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"
using namespace llvm;
namespace {
PassPluginLibraryInfo getSPIRVPluginInfo() {
return {
LLVM_PLUGIN_API_VERSION, "SPIRV", LLVM_VERSION_STRING,
[](PassBuilder &PB) {
PB.registerAnalysisRegistrationCallback([](ModuleAnalysisManager &AM) {
AM.registerPass([] { return OCLTypeToSPIRVPass(); });
});
PB.registerPipelineParsingCallback(
[](StringRef Name, FunctionPassManager &PM,
ArrayRef<PassBuilder::PipelineElement>) {
if (Name == "spirv-lower-bitcast") {
PM.addPass(
SPIRVLowerBitCastToNonStandardTypePass(TranslatorOpts{}));
return true;
}
return false;
});
PB.registerPipelineParsingCallback(
[](StringRef Name, ModulePassManager &PM,
ArrayRef<PassBuilder::PipelineElement>) {
if (Name == "ocl-to-spirv") {
PM.addPass(OCLToSPIRVPass());
return true;
}
if (Name == "llvm-to-spirv") {
SPIRV::TranslatorOpts DefaultOpts;
DefaultOpts.enableAllExtensions();
SPIRVModule *BM = SPIRVModule::createSPIRVModule(DefaultOpts);
PM.addPass(LLVMToSPIRVPass(BM));
return true;
}
if (Name == "process-metadata") {
PM.addPass(PreprocessMetadataPass());
return true;
}
if (Name == "spirv-lower-bool") {
PM.addPass(SPIRVLowerBoolPass());
return true;
}
if (Name == "spirv-lower-constexpr") {
PM.addPass(SPIRVLowerConstExprPass());
return true;
}
if (Name == "spirv-lower-memmove") {
PM.addPass(SPIRVLowerMemmovePass());
return true;
}
if (Name == "spirv-lower-ocl-blocks") {
PM.addPass(SPIRVLowerOCLBlocksPass());
return true;
}
if (Name == "spirv-lower-llvm-intrinsic") {
PM.addPass(SPIRVLowerLLVMIntrinsicPass(TranslatorOpts{}));
return true;
}
if (Name == "spirv-regularize-llvm") {
PM.addPass(SPIRVRegularizeLLVMPass());
return true;
}
if (Name == "spirv-to-ocl12") {
PM.addPass(SPIRVToOCL12Pass());
return true;
}
if (Name == "spirv-to-ocl20") {
PM.addPass(SPIRVToOCL20Pass());
return true;
}
return false;
});
}};
}
} // namespace
extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo
llvmGetPassPluginInfo() {
return getSPIRVPluginInfo();
}
@@ -0,0 +1,385 @@
//===- PreprocessMetadata.cpp - - C++ -*-===//
//
// The LLVM/SPIRV Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
//
// 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 Advanced Micro Devices, Inc., 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.
//
//===----------------------------------------------------------------------===//
//
// This file implements preprocessing of LLVM IR metadata in order to perform
// further translation to SPIR-V.
//
//===----------------------------------------------------------------------===//
#include "PreprocessMetadata.h"
#include "OCLUtil.h"
#include "SPIRVInternal.h"
#include "SPIRVMDBuilder.h"
#include "SPIRVMDWalker.h"
#include "VectorComputeUtil.h"
#include "libSPIRV/SPIRVDebug.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/InstVisitor.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/TargetParser/Triple.h"
#define DEBUG_TYPE "clmdtospv"
using namespace llvm;
using namespace SPIRV;
using namespace OCLUtil;
namespace SPIRV {
cl::opt<bool> EraseOCLMD("spirv-erase-cl-md", cl::init(true),
cl::desc("Erase OpenCL metadata"));
char PreprocessMetadataLegacy::ID = 0;
bool PreprocessMetadataLegacy::runOnModule(Module &Module) {
return runPreprocessMetadata(Module);
}
llvm::PreservedAnalyses
PreprocessMetadataPass::run(llvm::Module &M, llvm::ModuleAnalysisManager &MAM) {
return runPreprocessMetadata(M) ? llvm::PreservedAnalyses::none()
: llvm::PreservedAnalyses::all();
}
bool PreprocessMetadataBase::runPreprocessMetadata(Module &Module) {
M = &Module;
Ctx = &M->getContext();
LLVM_DEBUG(dbgs() << "Enter PreprocessMetadata:\n");
visit(M);
LLVM_DEBUG(dbgs() << "After PreprocessMetadata:\n" << *M);
verifyRegularizationPass(*M, "PreprocessMetadata");
return true;
}
void PreprocessMetadataBase::preprocessCXXStructorList(
SPIRVMDBuilder::NamedMDWrapper &EM, GlobalVariable *V,
ExecutionMode EMode) {
auto *List = dyn_cast_or_null<ConstantArray>(V->getInitializer());
if (!List)
return;
for (Value *V : List->operands()) {
auto *Structor = cast<ConstantStruct>(V);
// Each entry in the list is a struct containing 3 members:
// (priority, function, data), with function being the entry point.
auto *Kernel = cast<Function>(Structor->getOperand(1));
EM.addOp().add(Kernel).add(EMode).done();
}
}
void PreprocessMetadataBase::visit(Module *M) {
SPIRVMDBuilder B(*M);
SPIRVMDWalker W(*M);
preprocessOCLMetadata(M, &B, &W);
preprocessVectorComputeMetadata(M, &B, &W);
// Create metadata representing (empty so far) list
// of OpExecutionMode instructions
auto EM = B.addNamedMD(kSPIRVMD::ExecutionMode); // !spirv.ExecutionMode = {}
// Process special variables in LLVM IR module.
if (auto *GV = M->getGlobalVariable("llvm.global_ctors"))
preprocessCXXStructorList(EM, GV, spv::ExecutionModeInitializer);
// Add execution modes for kernels. We take it from metadata attached to
// the kernel functions.
for (Function &Kernel : *M) {
if (Kernel.getCallingConv() != CallingConv::SPIR_KERNEL)
continue;
// Specifing execution modes for the Kernel and adding it to the list
// of ExecutionMode instructions.
// !{void (i32 addrspace(1)*)* @kernel, i32 17, i32 X, i32 Y, i32 Z}
// !{void (i32 addrspace(1)*)* @kernel, i32 18, i32 X, i32 Y, i32 Z}
// !{void (i32 addrspace(1)*)* @kernel, i32 max_work_group_size, i32 X,
// i32 Y, i32 Z}
std::pair<unsigned, const char *> WGSizeMDs[3] = {
{spv::ExecutionModeLocalSize, kSPIR2MD::WGSize},
{spv::ExecutionModeLocalSizeHint, kSPIR2MD::WGSizeHint},
{spv::ExecutionModeMaxWorkgroupSizeINTEL, kSPIR2MD::MaxWGSize},
};
for (auto &[ExMode, MDName] : WGSizeMDs) {
if (MDNode *WGMD = Kernel.getMetadata(MDName)) {
assert(WGMD->getNumOperands() >= 1 && WGMD->getNumOperands() <= 3 &&
"work-group metadata does not have between 1 and 3 operands.");
SmallVector<unsigned, 3> DecodedVals = decodeMDNode(WGMD);
EM.addOp()
.add(&Kernel)
.add(ExMode)
.add(DecodedVals[0])
.add(DecodedVals.size() >= 2 ? DecodedVals[1] : 1)
.add(DecodedVals.size() == 3 ? DecodedVals[2] : 1)
.done();
}
}
// !{void (i32 addrspace(1)*)* @kernel, i32 30, i32 hint}
if (MDNode *VecTypeHint = Kernel.getMetadata(kSPIR2MD::VecTyHint)) {
EM.addOp()
.add(&Kernel)
.add(spv::ExecutionModeVecTypeHint)
.add(transVecTypeHint(VecTypeHint))
.done();
}
// !{void (i32 addrspace(1)*)* @kernel, i32 35, i32 size}
if (MDNode *ReqdSubgroupSize = Kernel.getMetadata(kSPIR2MD::SubgroupSize)) {
// A primary named subgroup size is encoded as
// the metadata intel_reqd_sub_group_size with value -1.
auto Val = getMDOperandAsInt(ReqdSubgroupSize, 0);
if (Val == -1U)
EM.addOp()
.add(&Kernel)
.add(spv::internal::ExecutionModeNamedSubgroupSizeINTEL)
.add(/* PrimarySubgroupSizeINTEL = */ 0U)
.done();
EM.addOp()
.add(&Kernel)
.add(spv::ExecutionModeSubgroupSize)
.add(Val)
.done();
}
// !{void (i32 addrspace(1)*)* @kernel, i32 no_global_work_offset}
if (Kernel.getMetadata(kSPIR2MD::NoGlobalOffset)) {
EM.addOp().add(&Kernel).add(spv::ExecutionModeNoGlobalOffsetINTEL).done();
}
// !{void (i32 addrspace(1)*)* @kernel, i32 max_global_work_dim, i32 dim}
if (MDNode *MaxWorkDimINTEL = Kernel.getMetadata(kSPIR2MD::MaxWGDim)) {
EM.addOp()
.add(&Kernel)
.add(spv::ExecutionModeMaxWorkDimINTEL)
.add(getMDOperandAsInt(MaxWorkDimINTEL, 0))
.done();
}
// !{void (i32 addrspace(1)*)* @kernel, i32 num_simd_work_items, i32 num}
if (MDNode *NumSIMDWorkitemsINTEL = Kernel.getMetadata(kSPIR2MD::NumSIMD)) {
EM.addOp()
.add(&Kernel)
.add(spv::ExecutionModeNumSIMDWorkitemsINTEL)
.add(getMDOperandAsInt(NumSIMDWorkitemsINTEL, 0))
.done();
}
// !{void (i32 addrspace(1)*)* @kernel, i32 scheduler_target_fmax_mhz,
// i32 num}
if (MDNode *SchedulerTargetFmaxMhzINTEL =
Kernel.getMetadata(kSPIR2MD::FmaxMhz)) {
EM.addOp()
.add(&Kernel)
.add(spv::ExecutionModeSchedulerTargetFmaxMhzINTEL)
.add(getMDOperandAsInt(SchedulerTargetFmaxMhzINTEL, 0))
.done();
}
// !{void (i32 addrspace(1)*)* @kernel, i32 ip_interface, i32 interface}
if (MDNode *Interface =
Kernel.getMetadata(kSPIR2MD::IntelFPGAIPInterface)) {
std::set<std::string> InterfaceStrSet;
for (size_t I = 0; I != Interface->getNumOperands(); ++I)
InterfaceStrSet.insert(getMDOperandAsString(Interface, I).str());
// ip_interface metadata will either have Register Map metadata or
// Streaming metadata.
//
// Register Map mode metadata:
// Not 'WaitForDoneWrite' mode (to be mapped on '0' literal)
// !ip_interface !N
// !N = !{!"csr"}
// 'WaitForDoneWrite' mode (to be mapped on '1' literal)
// !ip_interface !N
// !N = !{!"csr", !"wait_for_done_write"}
if (InterfaceStrSet.find("csr") != InterfaceStrSet.end()) {
int32_t InterfaceMode = 0;
if (InterfaceStrSet.find("wait_for_done_write") !=
InterfaceStrSet.end())
InterfaceMode = 1;
EM.addOp()
.add(&Kernel)
.add(spv::ExecutionModeRegisterMapInterfaceINTEL)
.add(InterfaceMode)
.done();
}
// Streaming mode metadata be like:
// Not 'stall free' mode (to be mapped on '0' literal)
// !ip_interface !N
// !N = !{!"streaming"}
// 'stall free' mode (to be mapped on '1' literal)
// !ip_interface !N
// !N = !{!"streaming", !"stall_free_return"}
if (InterfaceStrSet.find("streaming") != InterfaceStrSet.end()) {
int32_t InterfaceMode = 0;
if (InterfaceStrSet.find("stall_free_return") != InterfaceStrSet.end())
InterfaceMode = 1;
EM.addOp()
.add(&Kernel)
.add(spv::ExecutionModeStreamingInterfaceINTEL)
.add(InterfaceMode)
.done();
}
}
}
}
void PreprocessMetadataBase::preprocessOCLMetadata(Module *M, SPIRVMDBuilder *B,
SPIRVMDWalker *W) {
unsigned CLVer = getOCLVersion(M, true);
if (CLVer == 0)
return;
// Preprocess OpenCL-specific metadata
// !spirv.Source = !{!x}
// !{x} = !{i32 3, i32 102000}
B->addNamedMD(kSPIRVMD::Source)
.addOp()
.add(M->getNamedMetadata(kSPIR2MD::OCLCXXVer) &&
(CLVer == kOCLVer::CLCXX10 || CLVer == kOCLVer::CLCXX2021)
? spv::SourceLanguageCPP_for_OpenCL
: spv::SourceLanguageOpenCL_C)
.add(CLVer)
.done();
if (EraseOCLMD)
B->eraseNamedMD(kSPIR2MD::OCLVer)
.eraseNamedMD(kSPIR2MD::SPIRVer)
.eraseNamedMD(kSPIR2MD::OCLCXXVer);
// !spirv.MemoryModel = !{!x}
// !{x} = !{i32 1, i32 2}
Triple TT(M->getTargetTriple());
assert(isSupportedTriple(TT) && "Invalid triple");
B->addNamedMD(kSPIRVMD::MemoryModel)
.addOp()
.add(TT.isArch32Bit() ? spv::AddressingModelPhysical32
: spv::AddressingModelPhysical64)
.add(spv::MemoryModelOpenCL)
.done();
// Add source extensions
// !spirv.SourceExtension = !{!x, !y, ...}
// !x = {!"cl_khr_..."}
// !y = {!"cl_khr_..."}
auto Exts = getNamedMDAsStringSet(M, kSPIR2MD::Extensions);
if (!Exts.empty()) {
auto N = B->addNamedMD(kSPIRVMD::SourceExtension);
for (auto &I : Exts)
N.addOp().add(I).done();
}
if (EraseOCLMD)
B->eraseNamedMD(kSPIR2MD::Extensions).eraseNamedMD(kSPIR2MD::OptFeatures);
if (EraseOCLMD)
B->eraseNamedMD(kSPIR2MD::FPContract);
}
void PreprocessMetadataBase::preprocessVectorComputeMetadata(Module *M,
SPIRVMDBuilder *B,
SPIRVMDWalker *W) {
using namespace VectorComputeUtil;
auto EM = B->addNamedMD(kSPIRVMD::ExecutionMode);
for (auto &F : *M) {
if (F.getCallingConv() != CallingConv::SPIR_KERNEL)
continue;
// Add VC float control execution modes
// RoundMode and FloatMode are always same for all types in VC
// While Denorm could be different for double, float and half
auto Attrs = F.getAttributes();
if (Attrs.hasFnAttr(kVCMetadata::VCFloatControl)) {
SPIRVWord Mode = 0;
Attrs.getFnAttr(kVCMetadata::VCFloatControl)
.getValueAsString()
.getAsInteger(0, Mode);
spv::ExecutionMode ExecRoundMode =
FPRoundingModeExecModeMap::map(getFPRoundingMode(Mode));
spv::ExecutionMode ExecFloatMode =
FPOperationModeExecModeMap::map(getFPOperationMode(Mode));
VCFloatTypeSizeMap::foreach ([&](VCFloatType FloatType,
unsigned TargetWidth) {
EM.addOp().add(&F).add(ExecRoundMode).add(TargetWidth).done();
EM.addOp().add(&F).add(ExecFloatMode).add(TargetWidth).done();
EM.addOp()
.add(&F)
.add(FPDenormModeExecModeMap::map(getFPDenormMode(Mode, FloatType)))
.add(TargetWidth)
.done();
});
}
if (Attrs.hasFnAttr(kVCMetadata::VCSLMSize)) {
SPIRVWord SLMSize = 0;
Attrs.getFnAttr(kVCMetadata::VCSLMSize)
.getValueAsString()
.getAsInteger(0, SLMSize);
EM.addOp()
.add(&F)
.add(spv::ExecutionModeSharedLocalMemorySizeINTEL)
.add(SLMSize)
.done();
}
if (Attrs.hasFnAttr(kVCMetadata::VCNamedBarrierCount)) {
SPIRVWord NBarrierCnt = 0;
Attrs.getFnAttr(kVCMetadata::VCNamedBarrierCount)
.getValueAsString()
.getAsInteger(0, NBarrierCnt);
EM.addOp()
.add(&F)
.add(spv::ExecutionModeNamedBarrierCountINTEL)
.add(NBarrierCnt)
.done();
}
}
}
} // namespace SPIRV
INITIALIZE_PASS(PreprocessMetadataLegacy, "preprocess-metadata",
"Transform LLVM IR metadata to SPIR-V metadata format", false,
false)
ModulePass *llvm::createPreprocessMetadataLegacy() {
return new PreprocessMetadataLegacy();
}
@@ -0,0 +1,92 @@
//=- PreprocessMetadata.h - Metadata preprocessing pass -*- C++ -*-=//
//
// The LLVM/SPIR-V Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2022 The Khronos Group Inc.
//
// 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 Khronos Group, 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.
//
//===----------------------------------------------------------------------===//
//
// This file implements preprocessing of LLVM IR metadata in order to perform
// further translation to SPIR-V.
//
//===----------------------------------------------------------------------===//
#ifndef SPIRV_PREPROCESSMETADATA_H
#define SPIRV_PREPROCESSMETADATA_H
#include "llvm/IR/PassManager.h"
#include "llvm/Pass.h"
#include "SPIRVMDBuilder.h"
namespace SPIRV {
class SPIRVMDWalker;
class PreprocessMetadataBase {
public:
PreprocessMetadataBase() : M(nullptr), Ctx(nullptr) {}
bool runPreprocessMetadata(Module &M);
void visit(Module *M);
void preprocessCXXStructorList(SPIRVMDBuilder::NamedMDWrapper &EM,
GlobalVariable *V, ExecutionMode EMode);
void preprocessOCLMetadata(Module *M, SPIRVMDBuilder *B, SPIRVMDWalker *W);
void preprocessVectorComputeMetadata(Module *M, SPIRVMDBuilder *B,
SPIRVMDWalker *W);
private:
Module *M;
LLVMContext *Ctx;
};
class PreprocessMetadataLegacy : public ModulePass,
public PreprocessMetadataBase {
public:
PreprocessMetadataLegacy() : ModulePass(ID) {
initializePreprocessMetadataLegacyPass(*PassRegistry::getPassRegistry());
}
bool runOnModule(Module &M) override;
static char ID;
};
class PreprocessMetadataPass
: public llvm::PassInfoMixin<PreprocessMetadataPass>,
public PreprocessMetadataBase {
public:
llvm::PreservedAnalyses run(llvm::Module &M,
llvm::ModuleAnalysisManager &MAM);
static bool isRequired() { return true; }
};
} // namespace SPIRV
#endif // SPIRV_PREPROCESSMETADATA_H
@@ -0,0 +1,390 @@
//===- SPIRVBuiltinHelper.cpp - Helpers for managing calls to builtins ----===//
//
// The LLVM/SPIR-V Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2022 The Khronos Group Inc.
//
// 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 Khronos Group, 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.
//
//===----------------------------------------------------------------------===//
//
// This file implements helper functions for adding calls to OpenCL or SPIR-V
// builtin functions, or for rewriting calls to one into calls to the other.
//
//===----------------------------------------------------------------------===//
#include "SPIRVBuiltinHelper.h"
#include "OCLUtil.h"
#include "SPIRVInternal.h"
using namespace llvm;
using namespace SPIRV;
static std::unique_ptr<BuiltinFuncMangleInfo> makeMangler(CallBase *CB,
ManglingRules Rules) {
switch (Rules) {
case ManglingRules::None:
return nullptr;
case ManglingRules::SPIRV:
return std::make_unique<BuiltinFuncMangleInfo>();
case ManglingRules::OpenCL:
return OCLUtil::makeMangler(*CB->getCalledFunction());
}
llvm_unreachable("Unknown mangling rules to make a name mangler");
}
BuiltinCallMutator::BuiltinCallMutator(
CallInst *CI, std::string FuncName, ManglingRules Rules,
std::function<std::string(StringRef)> NameMapFn)
: CI(CI), FuncName(FuncName),
Attrs(CI->getCalledFunction()->getAttributes()),
CallAttrs(CI->getAttributes()), ReturnTy(CI->getType()), Args(CI->args()),
Rules(Rules), Builder(CI) {
bool DidDemangle = getParameterTypes(CI->getCalledFunction(), PointerTypes,
std::move(NameMapFn));
if (!DidDemangle) {
// TODO: PipeBlocking.ll causes demangling failures.
// assert(isNonMangledOCLBuiltin(CI->getCalledFunction()->getName()) &&
// "SPIR-V builtin functions should be mangled");
for (Value *Arg : Args)
PointerTypes.push_back(Arg->getType());
}
}
BuiltinCallMutator::BuiltinCallMutator(BuiltinCallMutator &&Other)
: CI(Other.CI), FuncName(std::move(Other.FuncName)),
MutateRet(std::move(Other.MutateRet)), Attrs(Other.Attrs),
CallAttrs(Other.CallAttrs), ReturnTy(Other.ReturnTy),
Args(std::move(Other.Args)), PointerTypes(std::move(Other.PointerTypes)),
Rules(std::move(Other.Rules)), Builder(CI) {
// Clear the other's CI instance so that it knows not to construct the actual
// call.
Other.CI = nullptr;
}
Value *BuiltinCallMutator::doConversion() {
assert(CI && "Need to have a call instruction to do the conversion");
auto Mangler = makeMangler(CI, Rules);
for (unsigned I = 0, E = std::min(Args.size(), PointerTypes.size()); I < E;
I++) {
Mangler->getTypeMangleInfo(I).PointerTy =
dyn_cast<TypedPointerType>(PointerTypes[I]);
}
assert(Attrs.getNumAttrSets() <= Args.size() + 2 && "Too many attributes?");
// Sanitize the return type, in case it's a TypedPointerType.
if (auto *TPT = dyn_cast<TypedPointerType>(ReturnTy))
ReturnTy = PointerType::get(CI->getContext(), TPT->getAddressSpace());
CallInst *NewCall =
Builder.Insert(addCallInst(CI->getModule(), FuncName, ReturnTy, Args,
&Attrs, nullptr, Mangler.get()));
NewCall->copyMetadata(*CI);
NewCall->setAttributes(CallAttrs);
NewCall->setTailCall(CI->isTailCall());
if (isa<FPMathOperator>(CI))
NewCall->setFastMathFlags(CI->getFastMathFlags());
if (CI->hasFnAttr("fpbuiltin-max-error")) {
auto Attr = CI->getFnAttr("fpbuiltin-max-error");
NewCall->addFnAttr(Attr);
}
Value *Result = MutateRet ? MutateRet(Builder, NewCall) : NewCall;
Result->takeName(CI);
if (!CI->getType()->isVoidTy())
CI->replaceAllUsesWith(Result);
CI->dropAllReferences();
CI->eraseFromParent();
CI = nullptr;
return Result;
}
BuiltinCallMutator &BuiltinCallMutator::setArgs(ArrayRef<Value *> NewArgs) {
// Retain only the function attributes, not any parameter attributes.
Attrs = AttributeList::get(CI->getContext(), Attrs.getFnAttrs(),
Attrs.getRetAttrs(), {});
CallAttrs = AttributeList::get(CI->getContext(), CallAttrs.getFnAttrs(),
CallAttrs.getRetAttrs(), {});
Args.clear();
PointerTypes.clear();
for (Value *Arg : NewArgs) {
assert(!Arg->getType()->isPointerTy() &&
"Cannot use this signature with pointer types");
Args.push_back(Arg);
PointerTypes.push_back(Arg->getType());
}
return *this;
}
// This is a helper method to handle splicing of the attribute lists, as
// llvm::AttributeList doesn't have any helper methods for this sort of design.
// (It's designed to be manually built-up, not adjusted to add/remove
// arguments on the fly).
static void moveAttributes(LLVMContext &Ctx, AttributeList &Attrs,
unsigned Start, unsigned Len, unsigned Dest) {
SmallVector<std::pair<unsigned, AttributeSet>, 6> NewAttrs;
for (unsigned Index : Attrs.indexes()) {
AttributeSet AttrSet = Attrs.getAttributes(Index);
if (!AttrSet.hasAttributes())
continue;
// If the attribute is a parameter index, check to see how its index should
// be adjusted.
if (Index > AttributeList::FirstArgIndex) {
unsigned ParamIndex = Index - AttributeList::FirstArgIndex;
if (ParamIndex >= Start && ParamIndex < Start + Len)
// A parameter in this range needs to have its index adjusted to its
// destination location.
Index += Dest - Start;
else if (ParamIndex >= Dest && ParamIndex < Dest + Len)
// This parameter will be overwritten by one of the moved parameters, so
// omit it entirely.
continue;
}
// The array is usually going to be sorted, but because of the above
// adjustment, we might end up out of order. This logic ensures that the
// array always remains in sorted order.
std::pair<unsigned, AttributeSet> ToInsert(Index, AttrSet);
NewAttrs.insert(llvm::lower_bound(NewAttrs, ToInsert, llvm::less_first()),
ToInsert);
}
Attrs = AttributeList::get(Ctx, NewAttrs);
}
BuiltinCallMutator &BuiltinCallMutator::insertArg(unsigned Index,
ValueTypePair Arg) {
Args.insert(Args.begin() + Index, Arg.first);
PointerTypes.insert(PointerTypes.begin() + Index, Arg.second);
moveAttributes(CI->getContext(), Attrs, Index, Args.size() - Index,
Index + 1);
moveAttributes(CI->getContext(), CallAttrs, Index, Args.size() - Index,
Index + 1);
return *this;
}
BuiltinCallMutator &BuiltinCallMutator::replaceArg(unsigned Index,
ValueTypePair Arg) {
Args[Index] = Arg.first;
PointerTypes[Index] = Arg.second;
Attrs = Attrs.removeParamAttributes(CI->getContext(), Index);
CallAttrs = CallAttrs.removeParamAttributes(CI->getContext(), Index);
return *this;
}
BuiltinCallMutator &BuiltinCallMutator::removeArg(unsigned Index) {
// If the argument being dropped is the last one, there is nothing to move, so
// just remove the attributes.
auto &Ctx = CI->getContext();
if (Index == Args.size() - 1) {
Attrs = Attrs.removeParamAttributes(Ctx, Index);
CallAttrs = CallAttrs.removeParamAttributes(Ctx, Index);
} else {
moveAttributes(Ctx, Attrs, Index + 1, Args.size() - Index - 1, Index);
moveAttributes(Ctx, CallAttrs, Index + 1, Args.size() - Index - 1, Index);
}
Args.erase(Args.begin() + Index);
PointerTypes.erase(PointerTypes.begin() + Index);
return *this;
}
BuiltinCallMutator &
BuiltinCallMutator::changeReturnType(Type *NewReturnTy,
MutateRetFuncTy MutateFunc) {
ReturnTy = NewReturnTy;
MutateRet = std::move(MutateFunc);
return *this;
}
BuiltinCallMutator BuiltinCallHelper::mutateCallInst(CallInst *CI,
spv::Op Opcode) {
return mutateCallInst(CI, getSPIRVFuncName(Opcode));
}
BuiltinCallMutator BuiltinCallHelper::mutateCallInst(CallInst *CI,
std::string FuncName) {
assert(CI->getCalledFunction() && "Can only mutate direct function calls.");
return BuiltinCallMutator(CI, std::move(FuncName), Rules, NameMapFn);
}
Value *BuiltinCallHelper::addSPIRVCall(IRBuilder<> &Builder, spv::Op Opcode,
Type *ReturnTy, ArrayRef<Value *> Args,
ArrayRef<Type *> ArgTys,
const Twine &Name) {
// Sanitize the return type, in case it's a TypedPointerType.
if (auto *TPT = dyn_cast<TypedPointerType>(ReturnTy))
ReturnTy = PointerType::get(Builder.getContext(), TPT->getAddressSpace());
// Copy the types into the mangling info.
BuiltinFuncMangleInfo BtnInfo;
for (unsigned I = 0; I < ArgTys.size(); I++) {
if (Args[I]->getType()->isPointerTy())
BtnInfo.getTypeMangleInfo(I).PointerTy = ArgTys[I];
}
// Create the function and the call.
auto *F = getOrCreateFunction(M, ReturnTy, getTypes(Args),
getSPIRVFuncName(Opcode), &BtnInfo);
return Builder.CreateCall(F, Args, ReturnTy->isVoidTy() ? "" : Name);
}
Type *BuiltinCallHelper::adjustImageType(Type *T, StringRef OldImageKind,
StringRef NewImageKind) {
if (auto *TypedPtrTy = dyn_cast<TypedPointerType>(T)) {
Type *StructTy = TypedPtrTy->getElementType();
// Adapt opencl.* struct type names to spirv.* struct type names.
if (isOCLImageType(T)) {
if (OldImageKind != kSPIRVTypeName::Image)
report_fatal_error("Type was not an image type");
auto ImageTypeName = StructTy->getStructName();
auto Desc =
map<SPIRVTypeImageDescriptor>(getImageBaseTypeName(ImageTypeName));
spv::AccessQualifier Acc = AccessQualifierReadOnly;
if (hasAccessQualifiedName(ImageTypeName))
Acc = getAccessQualifier(ImageTypeName);
auto NewImageType = SPIRVOpaqueTypeOpCodeMap::map(NewImageKind.str());
return getSPIRVType(NewImageType, Type::getVoidTy(M->getContext()), Desc,
Acc);
}
// Change type name (e.g., spirv.Image -> spirv.SampledImg) if necessary.
StringRef Postfixes;
if (isSPIRVStructType(StructTy, OldImageKind, &Postfixes))
StructTy = getOrCreateOpaqueStructType(
M, getSPIRVTypeName(NewImageKind, Postfixes));
else {
report_fatal_error("Type did not have expected image kind");
}
return TypedPointerType::get(StructTy, TypedPtrTy->getAddressSpace());
}
if (auto *TargetTy = dyn_cast<TargetExtType>(T)) {
StringRef Name = TargetTy->getName();
if (!Name.consume_front(kSPIRVTypeName::PrefixAndDelim) ||
Name != OldImageKind)
report_fatal_error("Type did not have expected image kind");
return TargetExtType::get(
TargetTy->getContext(),
(Twine(kSPIRVTypeName::PrefixAndDelim) + NewImageKind).str(),
TargetTy->type_params(), TargetTy->int_params());
}
report_fatal_error("Expected type to be a SPIRV image type");
}
Type *BuiltinCallHelper::getSPIRVType(spv::Op TypeOpcode, bool UseRealType) {
return getSPIRVType(TypeOpcode, "", {}, UseRealType);
}
Type *BuiltinCallHelper::getSPIRVType(spv::Op TypeOpcode,
spv::AccessQualifier Access,
bool UseRealType) {
return getSPIRVType(TypeOpcode, "", {(unsigned)Access}, UseRealType);
}
Type *BuiltinCallHelper::getSPIRVType(
spv::Op TypeOpcode, Type *InnerType, SPIRVTypeImageDescriptor Desc,
std::optional<spv::AccessQualifier> Access, bool UseRealType) {
return getSPIRVType(TypeOpcode, convertTypeToPostfix(InnerType),
{(unsigned)Desc.Dim, (unsigned)Desc.Depth,
(unsigned)Desc.Arrayed, (unsigned)Desc.MS,
(unsigned)Desc.Sampled, (unsigned)Desc.Format,
(unsigned)Access.value_or(AccessQualifierReadOnly)},
UseRealType);
}
Type *BuiltinCallHelper::getSPIRVType(spv::Op TypeOpcode,
StringRef InnerTypeName,
ArrayRef<unsigned> Parameters,
bool UseRealType) {
if (UseTargetTypes) {
std::string BaseName = (Twine(kSPIRVTypeName::PrefixAndDelim) +
SPIRVOpaqueTypeOpCodeMap::rmap(TypeOpcode))
.str();
SmallVector<Type *, 1> TypeParams;
if (!InnerTypeName.empty()) {
TypeParams.push_back(getLLVMTypeForSPIRVImageSampledTypePostfix(
InnerTypeName, M->getContext()));
}
return TargetExtType::get(M->getContext(), BaseName, TypeParams,
Parameters);
}
std::string FullName;
{
raw_string_ostream OS(FullName);
OS << kSPIRVTypeName::PrefixAndDelim
<< SPIRVOpaqueTypeOpCodeMap::rmap(TypeOpcode);
if (!InnerTypeName.empty() || !Parameters.empty())
OS << kSPIRVTypeName::Delimiter;
if (!InnerTypeName.empty())
OS << kSPIRVTypeName::PostfixDelim << InnerTypeName;
for (unsigned IntParam : Parameters)
OS << kSPIRVTypeName::PostfixDelim << IntParam;
}
auto *STy = StructType::getTypeByName(M->getContext(), FullName);
if (!STy)
STy = StructType::create(M->getContext(), FullName);
unsigned AddrSpace = getOCLOpaqueTypeAddrSpace(TypeOpcode);
return UseRealType ? (Type *)PointerType::get(M->getContext(), AddrSpace)
: TypedPointerType::get(STy, AddrSpace);
}
void BuiltinCallHelper::initialize(llvm::Module &M) {
this->M = &M;
// We want to use pointers-to-opaque-structs for the special types if:
// * We are translating from SPIR-V to LLVM IR (which means we are using
// OpenCL mangling rules)
// * There are %opencl.* or %spirv.* struct type names already present.
UseTargetTypes = Rules != ManglingRules::OpenCL;
for (StructType *Ty : M.getIdentifiedStructTypes()) {
if (!Ty->isOpaque() || !Ty->hasName())
continue;
StringRef Name = Ty->getName();
if (Name.starts_with("opencl.") || Name.starts_with("spirv.")) {
UseTargetTypes = false;
}
}
}
BuiltinCallMutator::ValueTypePair
BuiltinCallHelper::getCallValue(CallInst *CI, unsigned ArgNo) {
Function *CalledFunc = CI->getCalledFunction();
assert(CalledFunc && "Unexpected indirect call");
if (CalledFunc != CachedFunc) {
CachedFunc = CalledFunc;
[[maybe_unused]] bool DidDemangle =
getParameterTypes(CalledFunc, CachedParameterTypes, NameMapFn);
assert(DidDemangle && "Expected SPIR-V builtins to be properly mangled");
}
Value *ParamValue = CI->getArgOperand(ArgNo);
Type *ParamType = CachedParameterTypes[ArgNo];
return {ParamValue, ParamType};
}
@@ -0,0 +1,365 @@
//===- SPIRVBuiltinHelper.h - Helpers for managing calls to builtins ------===//
//
// The LLVM/SPIR-V Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2022 The Khronos Group Inc.
//
// 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 Khronos Group, 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.
//
//===----------------------------------------------------------------------===//
//
// This file implements helper functions for adding calls to OpenCL or SPIR-V
// builtin functions, or for rewriting calls to one into calls to the other.
//
//===----------------------------------------------------------------------===//
#ifndef SPIRVBUILTINHELPER_H
#define SPIRVBUILTINHELPER_H
#include "LLVMSPIRVLib.h"
#include "libSPIRV/SPIRVOpCode.h"
#include "libSPIRV/SPIRVType.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/TypedPointerType.h"
namespace SPIRV {
enum class ManglingRules { None, OpenCL, SPIRV };
namespace detail {
/// This is a helper for triggering the static_assert in mapArg.
template <typename> constexpr bool LegalFnType = false;
} // namespace detail
/// A helper class for changing OpenCL builtin function calls to SPIR-V function
/// calls, or vice versa. Most of the functions will return a reference to the
/// current instance, allowing calls to be chained together, for example:
/// mutateCallInst(CI, NewFuncName)
/// .removeArg(3)
/// .appendArg(translateScope());
///
/// Only when the destuctor of this object is called will the original CallInst
/// be destroyed and replaced with the new CallInst be created.
class BuiltinCallMutator {
// Original call instruction
llvm::CallInst *CI;
// New unmangled function name
std::string FuncName;
// Return type mutator. This needs to be saved, because we can't call it until
// the new instruction is created.
std::function<llvm::Value *(llvm::IRBuilder<> &, llvm::CallInst *)> MutateRet;
typedef decltype(MutateRet) MutateRetFuncTy;
// The attribute list for the new called function.
llvm::AttributeList Attrs;
// The attribute list for the new call instruction.
llvm::AttributeList CallAttrs;
// The return type for the new call instruction.
llvm::Type *ReturnTy;
// The arguments for the new call instruction.
llvm::SmallVector<llvm::Value *, 8> Args;
// The pointer element types for the new call instruction.
llvm::SmallVector<llvm::Type *, 8> PointerTypes;
// The mangler rules to use for the new call instruction.
ManglingRules Rules;
friend class BuiltinCallHelper;
BuiltinCallMutator(
llvm::CallInst *CI, std::string FuncName, ManglingRules Rules,
std::function<std::string(llvm::StringRef)> NameMapFn = nullptr);
// This does the actual work of creating of the new call, and will return the
// new instruction.
llvm::Value *doConversion();
public:
~BuiltinCallMutator() {
if (CI)
doConversion();
}
BuiltinCallMutator(const BuiltinCallMutator &) = delete;
BuiltinCallMutator &operator=(const BuiltinCallMutator &) = delete;
BuiltinCallMutator &operator=(BuiltinCallMutator &&) = delete;
BuiltinCallMutator(BuiltinCallMutator &&);
/// The builder used to generate IR for this call.
llvm::IRBuilder<> Builder;
/// Return the resulting new instruction. It is not possible to use any
/// method on this object after calling this function.
llvm::Value *getMutated() { return doConversion(); }
/// Return the number of arguments currently specified for the new call.
unsigned arg_size() const { return Args.size(); }
/// Get the corresponding argument for the new call.
llvm::Value *getArg(unsigned Index) const { return Args[Index]; }
llvm::Type *getType(unsigned Index) const { return PointerTypes[Index]; }
/// Return the pointer element type of the corresponding index, or nullptr if
/// it is not a pointer.
llvm::Type *getPointerElementType(unsigned Index) const {
if (auto *TPT = llvm::dyn_cast<llvm::TypedPointerType>(PointerTypes[Index]))
return TPT->getElementType();
return nullptr;
}
/// A pair representing both the LLVM value of an argument and its
/// corresponding pointer element type. This type can be constructed from
/// implicit conversion from an LLVM value object (but only if it is not of
/// pointer type), or by the appropriate std::pair type.
struct ValueTypePair : public std::pair<llvm::Value *, llvm::Type *> {
ValueTypePair(llvm::Value *V) : pair(V, V->getType()) {
assert(!V->getType()->isPointerTy() &&
"Must specify a pointer element type if value is a pointer.");
}
ValueTypePair(std::pair<llvm::Value *, llvm::Type *> P) : pair(P) {}
ValueTypePair() = delete;
using pair::pair;
};
/// Use the following arguments as the arguments of the new call, replacing
/// any previous arguments. This version may not be used if any argument is of
/// pointer type.
BuiltinCallMutator &setArgs(llvm::ArrayRef<llvm::Value *> Args);
/// This will replace the return type of the call with a different return
/// type. The second argument is a function that will be called with an
/// IRBuilder parameter and the newly generated function, and will return the
/// value to replace all uses of the original call instruction with. Example
/// usage:
///
/// BuiltinCallMutator Mutator = /* ... */;
/// Mutator.changeReturnType(Int16Ty, [](IRBuilder<> &IRB, CallInst *CI) {
/// return IRB.CreateZExt(CI, Int16Ty);
/// });
BuiltinCallMutator &changeReturnType(llvm::Type *ReturnTy,
MutateRetFuncTy MutateFunc);
/// Insert an argument before the given index.
BuiltinCallMutator &insertArg(unsigned Index, ValueTypePair Arg);
/// Add an argument to the end of the argument list.
BuiltinCallMutator &appendArg(ValueTypePair Arg) {
return insertArg(Args.size(), Arg);
}
/// Replace the argument at the given index with a new value.
BuiltinCallMutator &replaceArg(unsigned Index, ValueTypePair Arg);
/// Remove the argument at the given index.
BuiltinCallMutator &removeArg(unsigned Index);
/// Remove all arguments in a range.
BuiltinCallMutator &removeArgs(unsigned Start, unsigned Len) {
for (unsigned I = 0; I < Len; I++)
removeArg(Start);
return *this;
}
/// Move the argument from the given index to the new index.
BuiltinCallMutator &moveArg(unsigned FromIndex, unsigned ToIndex) {
if (FromIndex == ToIndex)
return *this;
ValueTypePair Pair(Args[FromIndex], getType(FromIndex));
removeArg(FromIndex);
insertArg(ToIndex, Pair);
return *this;
}
/// Use a callback function or lambda to convert an argument to a new value.
/// The expected return type of the lambda is anything that is convertible
/// to ValueTypePair, which could be a single Value* (but only if it is not
/// pointer-typed), or a std::pair<Value *, Type *>. The possible signatures
/// of the function parameter are as follows:
/// ValueTypePair func(IRBuilder<> &Builder, Value *, Type *);
/// ValueTypePair func(IRBuilder<> &Builder, Value *);
/// ValueTypePair func(Value *, Type *);
/// ValueTypePair func(Value *);
///
/// When present, the IRBuilder parameter corresponds to a builder that is set
/// to insert immediately before the new call instruction. The Value parameter
/// corresponds to the argument to be mutated. The Type parameter, when
/// present, will be either a TypedPointerType representing the "true" type of
/// the value, or the argument's type otherwise.
template <typename FnType>
BuiltinCallMutator &mapArg(unsigned Index, FnType Func) {
using namespace llvm;
using std::is_invocable;
IRBuilder<> Builder(CI);
Value *V = Args[Index];
[[maybe_unused]] Type *T = getType(Index);
// Dispatch the function call as appropriate, based on the types that the
// function may be called with.
if constexpr (is_invocable<FnType, IRBuilder<> &, Value *, Type *>::value)
replaceArg(Index, Func(Builder, V, T));
else if constexpr (is_invocable<FnType, IRBuilder<> &, Value *>::value)
replaceArg(Index, Func(Builder, V));
else if constexpr (is_invocable<FnType, Value *, Type *>::value)
replaceArg(Index, Func(V, T));
else if constexpr (is_invocable<FnType, Value *>::value)
replaceArg(Index, Func(V));
else {
// We need a helper value that is always false, but is dependent on the
// template parameter to prevent this static_assert from firing when one
// of the if constexprs above fires.
static_assert(detail::LegalFnType<FnType>,
"mapArg lambda signature is not satisfied");
}
return *this;
}
/// Map all arguments according to the given function, as if mapArg(i, Func)
/// had been called for every argument i.
template <typename FnType> BuiltinCallMutator &mapArgs(FnType Func) {
for (unsigned I = 0, E = Args.size(); I < E; I++)
mapArg(I, Func);
return *this;
}
};
/// A helper class for generating calls to SPIR-V builtins with appropriate name
/// mangling rules. It is expected that transformation passes inherit from this
/// class.
class BuiltinCallHelper {
ManglingRules Rules;
std::function<std::string(llvm::StringRef)> NameMapFn;
protected:
llvm::Module *M = nullptr;
bool UseTargetTypes = false;
public:
/// Initialize details about how to mangle and demangle builtins correctly.
/// The Rules argument selects which name mangler to use for mangling.
/// The NameMapFn function will map type names during demangling; it defaults
/// to the identity function.
explicit BuiltinCallHelper(
ManglingRules Rules,
std::function<std::string(llvm::StringRef)> NameMapFn = nullptr)
: Rules(Rules), NameMapFn(std::move(NameMapFn)) {}
/// Initialize the module that will be operated on. This method must be called
/// before future methods.
void initialize(llvm::Module &M);
/// Return a mutator that will replace the given call instruction with a call
/// to the given function name. The function name will have its name mangled
/// in accordance with the argument types provided to the mutator.
BuiltinCallMutator mutateCallInst(llvm::CallInst *CI, std::string FuncName);
/// Return a mutator that will replace the given call instruction with a call
/// to the given SPIR-V opcode (whose name is used in the lookup map of
/// getSPIRVFuncName).
BuiltinCallMutator mutateCallInst(llvm::CallInst *CI, spv::Op Opcode);
/// Create a call to a SPIR-V builtin function (specified via opcode).
/// The return type and argument types may be TypedPointerType, if the actual
/// LLVM type is a pointer type.
llvm::Value *addSPIRVCall(llvm::IRBuilder<> &Builder, spv::Op Opcode,
llvm::Type *ReturnTy,
llvm::ArrayRef<llvm::Value *> Args,
llvm::ArrayRef<llvm::Type *> ArgTys,
const llvm::Twine &Name = "");
/// Create a call to a SPIR-V builtin function, returning a value and type
/// pair suitable for use in BuiltinCallMutator::replaceArg and similar
/// functions.
BuiltinCallMutator::ValueTypePair
addSPIRVCallPair(llvm::IRBuilder<> &Builder, spv::Op Opcode,
llvm::Type *ReturnTy, llvm::ArrayRef<llvm::Value *> Args,
llvm::ArrayRef<llvm::Type *> ArgTys,
const llvm::Twine &Name = "") {
llvm::Value *V =
addSPIRVCall(Builder, Opcode, ReturnTy, Args, ArgTys, Name);
return BuiltinCallMutator::ValueTypePair(V, ReturnTy);
}
/// Adapt the various SPIR-V image types, for example changing a "spirv.Image"
/// type into a "spirv.SampledImage" type with identical parameters.
///
/// The input type is expected to be a TypedPointerType to either a
/// "spirv.*" or "opencl.*" struct type. In the case of "opencl.*" struct
/// types, it will first convert it into the corresponding "spirv.Image"
/// struct type.
///
/// If the image type does not match OldImageKind, this method will abort.
llvm::Type *adjustImageType(llvm::Type *T, llvm::StringRef OldImageKind,
llvm::StringRef NewImageKind);
/// Create a new type representing a SPIR-V opaque type that takes no
/// parameters (such as sampler types).
///
/// If UseRealType is false, a typed pointer type may be returned; if it is
/// true, a pointer type will be used instead.
llvm::Type *getSPIRVType(spv::Op TypeOpcode, bool UseRealType = false);
/// Create a new type representing a SPIR-V opaque type that takes only an
/// access qualifier (such as pipe types).
///
/// If UseRealType is false, a typed pointer type may be returned; if it is
/// true, a pointer type will be used instead.
llvm::Type *getSPIRVType(spv::Op TypeOpcode, spv::AccessQualifier Access,
bool UseRealType = false);
/// Create a new type representing a SPIR-V opaque type that is an image type
/// of some kind.
///
/// If UseRealType is false, a typed pointer type may be returned; if it is
/// true, a pointer type will be used instead.
llvm::Type *getSPIRVType(spv::Op TypeOpcode, llvm::Type *InnerType,
SPIRVTypeImageDescriptor Desc,
std::optional<spv::AccessQualifier> Access,
bool UseRealType = false);
/// Create a new type representing a SPIR-V opaque type that takes arbitrary
/// parameters.
///
/// If UseRealType is false, a typed pointer type may be returned; if it is
/// true, a pointer type will be used instead.
llvm::Type *getSPIRVType(spv::Op TypeOpcode, llvm::StringRef InnerTypeName,
llvm::ArrayRef<unsigned> Parameters,
bool UseRealType = false);
private:
llvm::SmallVector<llvm::Type *, 4> CachedParameterTypes;
llvm::Function *CachedFunc = nullptr;
public:
BuiltinCallMutator::ValueTypePair getCallValue(llvm::CallInst *CI,
unsigned ArgNo);
llvm::Type *getCallValueType(llvm::CallInst *CI, unsigned ArgNo) {
return getCallValue(CI, ArgNo).second;
}
};
} // namespace SPIRV
#endif // SPIRVBUILTINHELPER_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,240 @@
//===============- SPIRVLowerBitCastToNonStandardType.cpp -================//
//
// The LLVM/SPIRV Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2021 Intel Corporation. All rights reserved.
//
// 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 Intel Corporation, 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.
//
//===----------------------------------------------------------------------===//
//
// This file implements lowering of BitCast to nonstandard types. LLVM
// transformations bitcast some vector types to scalar types, which are not
// universally supported across all targets. We need ensure that "optimized"
// LLVM IR doesn't have primitive types other than supported by the
// SPIR target (i.e. "scalar 8/16/32/64-bit integer and 16/32/64-bit floating
// point types, 2/3/4/8/16-element vector of scalar types").
//
//===----------------------------------------------------------------------===//
#include "SPIRVLowerBitCastToNonStandardType.h"
#include "SPIRVInternal.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/NoFolder.h"
#include "llvm/Transforms/Utils/Local.h"
#include <utility>
#define DEBUG_TYPE "spv-lower-bitcast-to-nonstandard-type"
using namespace llvm;
namespace SPIRV {
using NFIRBuilder = IRBuilder<NoFolder>;
static Value *removeBitCasts(Value *OldValue, Type *NewTy, NFIRBuilder &Builder,
std::vector<Instruction *> &InstsToErase) {
IRBuilderBase::InsertPointGuard Guard(Builder);
auto RauwBitcasts = [&](Instruction *OldValue, Value *NewValue) {
// If there's only one use, don't create a bitcast for any uses, since it
// will be immediately replaced anyways.
if (OldValue->hasOneUse()) {
OldValue->replaceAllUsesWith(PoisonValue::get(OldValue->getType()));
} else {
OldValue->replaceAllUsesWith(
Builder.CreateBitCast(NewValue, OldValue->getType()));
}
InstsToErase.push_back(OldValue);
return NewValue;
};
if (auto *LI = dyn_cast<LoadInst>(OldValue)) {
Builder.SetInsertPoint(LI);
Value *Pointer = LI->getPointerOperand();
LoadInst *NewLI = Builder.CreateAlignedLoad(NewTy, Pointer, LI->getAlign(),
LI->isVolatile());
NewLI->setOrdering(LI->getOrdering());
NewLI->setSyncScopeID(LI->getSyncScopeID());
return RauwBitcasts(LI, NewLI);
}
if (auto *ASCI = dyn_cast<AddrSpaceCastInst>(OldValue)) {
Builder.SetInsertPoint(ASCI);
Type *NewSrcTy =
PointerType::get(Builder.getContext(), ASCI->getSrcAddressSpace());
Value *Pointer = removeBitCasts(ASCI->getPointerOperand(), NewSrcTy,
Builder, InstsToErase);
return RauwBitcasts(ASCI, Builder.CreateAddrSpaceCast(Pointer, NewTy));
}
if (auto *BC = dyn_cast<BitCastInst>(OldValue)) {
if (BC->getSrcTy() == NewTy) {
if (BC->hasOneUse()) {
BC->replaceAllUsesWith(PoisonValue::get(BC->getType()));
InstsToErase.push_back(BC);
}
return BC->getOperand(0);
}
Builder.SetInsertPoint(BC);
return RauwBitcasts(BC, Builder.CreateBitCast(BC->getOperand(0), NewTy));
}
report_fatal_error("Cannot translate source of bitcast instruction.");
return nullptr;
}
static bool isNonStdVecType(VectorType *VecTy) {
uint64_t NumElems = VecTy->getElementCount().getFixedValue();
return !isValidVectorSize(NumElems);
}
PreservedAnalyses
SPIRVLowerBitCastToNonStandardTypePass::run(Function &F,
FunctionAnalysisManager &FAM) {
// This pass doesn't cover all possible uses of non-standard types, only
// known. We assume that bad type won't be passed to a function as
// parameter, since it added by an optimization.
bool Changed = false;
// SPV_EXT_long_vector and SPV_INTEL_vector_compute allow to use vectors with
// any number of components. Since this method only lowers vectors with
// non-standard in pure SPIR-V number of components, there is no need to do
// anything in case any of them is enabled.
if (Opts.isAllowedToUseExtension(ExtensionID::SPV_EXT_long_vector) ||
Opts.isAllowedToUseExtension(ExtensionID::SPV_INTEL_vector_compute))
return PreservedAnalyses::all();
// The basic pattern we're trying to fix is this InstCombine pattern:
// trunc (extractelement) -> extractelement (bitcast)
// (note that the bitcast itself can get propagated back to change the type
// of load instructions, and even through those to pointer casts, if typed
// pointers are enabled.
std::vector<ExtractElementInst *> NonStdVecInsts;
SmallVector<WeakTrackingVH, 4> MaybeDeletedInsts;
for (auto &BB : F)
for (auto &I : BB) {
if (auto *EI = dyn_cast<ExtractElementInst>(&I)) {
if (isNonStdVecType(EI->getVectorOperandType()))
NonStdVecInsts.push_back(EI);
} else if (auto *VT = dyn_cast<VectorType>(I.getType())) {
if (isNonStdVecType(VT)) {
MaybeDeletedInsts.push_back(&I);
}
}
}
std::vector<Instruction *> InstsToErase;
NFIRBuilder Builder(F.getContext());
for (auto &I : NonStdVecInsts) {
VectorType *OldVecTy = I->getVectorOperandType();
unsigned OldVecSize = OldVecTy->getElementCount().getFixedValue();
// Compute the adjustment factor for the new vector size.
unsigned VecFactor = 2;
while (OldVecSize % VecFactor == 0 &&
!isValidVectorSize(OldVecSize / VecFactor))
VecFactor *= 2;
if (OldVecSize % VecFactor != 0) {
report_fatal_error(Twine("Invalid vector size for fixup: ") +
Twine(OldVecSize));
return PreservedAnalyses::none();
}
unsigned NewElemSize = OldVecTy->getScalarSizeInBits() * VecFactor;
VectorType *NewVecTy =
VectorType::get(Type::getIntNTy(F.getContext(), NewElemSize),
OldVecSize / VecFactor, false);
// Adjust the element index as appropriate.
uint64_t OldElemIdx =
cast<ConstantInt>(I->getIndexOperand())->getZExtValue();
uint64_t NewElemIdx = OldElemIdx / VecFactor;
uint64_t ShiftCount = OldElemIdx % VecFactor;
Builder.SetInsertPoint(I);
Value *NewVecOp =
removeBitCasts(I->getVectorOperand(), NewVecTy, Builder, InstsToErase);
Value *NewExtracted = Builder.CreateExtractElement(NewVecOp, NewElemIdx);
// If the extract does higher-order bits of the value, shift as necessary.
if (ShiftCount > 0)
NewExtracted = Builder.CreateLShr(
NewExtracted, ShiftCount * OldVecTy->getScalarSizeInBits());
Value *NewValue = Builder.CreateTrunc(NewExtracted, I->getType());
I->replaceAllUsesWith(NewValue);
I->eraseFromParent();
Changed = true;
}
for (auto *I : InstsToErase)
RecursivelyDeleteTriviallyDeadInstructions(I);
// Check if there are any residual unsupported vector types.
for (auto &VH : MaybeDeletedInsts) {
// Some vector-valued instructions were replaced with undef values, so if
// that's what we got, it's still a dead instruction.
if (VH.pointsToAliveValue() && !isa<UndefValue>(VH)) {
auto *VT = cast<VectorType>(VH->getType());
report_fatal_error(Twine("Unsupported vector type with ") +
Twine(VT->getElementCount().getFixedValue()) +
Twine(" elements"),
false);
}
}
return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all();
}
bool SPIRVLowerBitCastToNonStandardTypeLegacy::runOnFunction(Function &F) {
SPIRVLowerBitCastToNonStandardTypePass Impl(Opts);
FunctionAnalysisManager FAM;
auto PA = Impl.run(F, FAM);
return !PA.areAllPreserved();
}
bool SPIRVLowerBitCastToNonStandardTypeLegacy::doFinalization(Module &M) {
verifyRegularizationPass(M, "SPIRVLowerBitCastToNonStandardType");
return false;
}
StringRef SPIRVLowerBitCastToNonStandardTypeLegacy::getPassName() const {
return "Lower nonstandard type";
}
char SPIRVLowerBitCastToNonStandardTypeLegacy::ID = 0;
} // namespace SPIRV
INITIALIZE_PASS(SPIRVLowerBitCastToNonStandardTypeLegacy,
"spv-lower-bitcast-to-nonstandard-type",
"Remove bitcast to nonstandard types", false, false)
llvm::FunctionPass *llvm::createSPIRVLowerBitCastToNonStandardTypeLegacy(
const SPIRV::TranslatorOpts &Opts) {
return new SPIRVLowerBitCastToNonStandardTypeLegacy(Opts);
}
@@ -0,0 +1,80 @@
//===- SPIRVLowerBitCastToNonStandardType.h - Bitcast lowering --*- C++ -*-===//
//
// The LLVM/SPIR-V Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2022 The Khronos Group Inc.
//
// 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 Khronos Group, 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.
//
//===----------------------------------------------------------------------===//
#ifndef SPIRV_SPIRVLOWERBITCASTTONONSTANDARDTYPE_H
#define SPIRV_SPIRVLOWERBITCASTTONONSTANDARDTYPE_H
#include "LLVMSPIRVOpts.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Pass.h"
namespace SPIRV {
class SPIRVLowerBitCastToNonStandardTypePass
: public llvm::PassInfoMixin<SPIRVLowerBitCastToNonStandardTypePass> {
public:
SPIRVLowerBitCastToNonStandardTypePass(const SPIRV::TranslatorOpts &Opts)
: Opts(Opts) {}
llvm::PreservedAnalyses run(llvm::Function &F,
llvm::FunctionAnalysisManager &FAM);
static bool isRequired() { return true; }
private:
SPIRV::TranslatorOpts Opts;
};
class SPIRVLowerBitCastToNonStandardTypeLegacy : public llvm::FunctionPass {
public:
static char ID;
SPIRVLowerBitCastToNonStandardTypeLegacy(const SPIRV::TranslatorOpts &Opts)
: FunctionPass(ID), Opts(Opts) {}
SPIRVLowerBitCastToNonStandardTypeLegacy() : FunctionPass(ID) {}
bool runOnFunction(llvm::Function &F) override;
bool doFinalization(llvm::Module &M) override;
llvm::StringRef getPassName() const override;
private:
SPIRV::TranslatorOpts Opts;
};
} // namespace SPIRV
#endif // SPIRV_SPIRVLOWERBITCASTTONONSTANDARDTYPE_H
@@ -0,0 +1,155 @@
//===- SPIRVLowerBool.cpp - Lower instructions with bool operands ---------===//
//
// The LLVM/SPIRV Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
//
// 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 Advanced Micro Devices, Inc., 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.
//
//===----------------------------------------------------------------------===//
//
// This file implements lowering instructions with bool operands.
//
//===----------------------------------------------------------------------===//
#include "SPIRVLowerBool.h"
#include "SPIRVInternal.h"
#include "libSPIRV/SPIRVDebug.h"
#include "llvm/IR/IRBuilder.h"
#define DEBUG_TYPE "spvbool"
using namespace llvm;
using namespace SPIRV;
namespace SPIRV {
void SPIRVLowerBoolBase::replace(Instruction *I, Instruction *NewI) {
NewI->takeName(I);
NewI->setDebugLoc(I->getDebugLoc());
I->replaceAllUsesWith(NewI);
I->dropAllReferences();
I->eraseFromParent();
}
bool SPIRVLowerBoolBase::isBoolType(Type *Ty) {
if (Ty->isIntegerTy(1))
return true;
if (auto *VT = dyn_cast<VectorType>(Ty))
return isBoolType(VT->getElementType());
return false;
}
void SPIRVLowerBoolBase::visitTruncInst(TruncInst &I) {
if (isBoolType(I.getType())) {
auto *Op = I.getOperand(0);
auto *And = BinaryOperator::CreateAnd(
Op, getScalarOrVectorConstantInt(Op->getType(), 1, false), "",
I.getIterator());
And->setDebugLoc(I.getDebugLoc());
auto *Zero = getScalarOrVectorConstantInt(Op->getType(), 0, false);
auto *Cmp = new ICmpInst(I.getIterator(), CmpInst::ICMP_NE, And, Zero);
replace(&I, Cmp);
}
}
void SPIRVLowerBoolBase::handleExtInstructions(Instruction &I) {
auto *Op = I.getOperand(0);
if (isBoolType(Op->getType())) {
auto Opcode = I.getOpcode();
auto *Ty = I.getType();
auto *Zero = getScalarOrVectorConstantInt(Ty, 0, false);
auto *One = getScalarOrVectorConstantInt(
Ty, (Opcode == Instruction::SExt) ? ~0 : 1, false);
assert(Zero && One && "Couldn't create constant int");
auto *Sel = SelectInst::Create(Op, One, Zero, "", I.getIterator());
replace(&I, Sel);
}
}
void SPIRVLowerBoolBase::handleCastInstructions(Instruction &I) {
auto *Op = I.getOperand(0);
auto *OpTy = Op->getType();
if (isBoolType(OpTy)) {
Type *Ty = Type::getInt32Ty(*Context);
if (auto *VT = dyn_cast<FixedVectorType>(OpTy))
Ty = llvm::FixedVectorType::get(Ty, VT->getNumElements());
auto *Zero = getScalarOrVectorConstantInt(Ty, 0, false);
auto *One = getScalarOrVectorConstantInt(Ty, 1, false);
assert(Zero && One && "Couldn't create constant int");
auto *Sel = SelectInst::Create(Op, One, Zero, "", I.getIterator());
Sel->setDebugLoc(I.getDebugLoc());
I.setOperand(0, Sel);
}
}
void SPIRVLowerBoolBase::visitZExtInst(ZExtInst &I) {
handleExtInstructions(I);
}
void SPIRVLowerBoolBase::visitSExtInst(SExtInst &I) {
handleExtInstructions(I);
}
void SPIRVLowerBoolBase::visitUIToFPInst(UIToFPInst &I) {
handleCastInstructions(I);
}
void SPIRVLowerBoolBase::visitSIToFPInst(SIToFPInst &I) {
handleCastInstructions(I);
}
bool SPIRVLowerBoolBase::runLowerBool(Module &M) {
Context = &M.getContext();
visit(M);
verifyRegularizationPass(M, "SPIRVLowerBool");
return true;
}
llvm::PreservedAnalyses
SPIRVLowerBoolPass::run(llvm::Module &M, llvm::ModuleAnalysisManager &MAM) {
return runLowerBool(M) ? llvm::PreservedAnalyses::none()
: llvm::PreservedAnalyses::all();
}
SPIRVLowerBoolLegacy::SPIRVLowerBoolLegacy() : ModulePass(ID) {
initializeSPIRVLowerBoolLegacyPass(*PassRegistry::getPassRegistry());
}
bool SPIRVLowerBoolLegacy::runOnModule(Module &M) { return runLowerBool(M); }
char SPIRVLowerBoolLegacy::ID = 0;
} // namespace SPIRV
INITIALIZE_PASS(SPIRVLowerBoolLegacy, "spvbool",
"Lower instructions with bool operands", false, false)
ModulePass *llvm::createSPIRVLowerBoolLegacy() {
return new SPIRVLowerBoolLegacy();
}
@@ -0,0 +1,88 @@
//===- SPIRVLowerBool.h - Bool operand lowering --------*- C++ -*-===//
//
// The LLVM/SPIR-V Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2022 The Khronos Group Inc.
//
// 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 Khronos Group, 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.
//
//===----------------------------------------------------------------------===//
//
// This file implements lowering instructions with bool operands.
//
//===----------------------------------------------------------------------===//
#ifndef SPIRV_SPIRVLOWERBOOL_H
#define SPIRV_SPIRVLOWERBOOL_H
#include "llvm/IR/InstVisitor.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Pass.h"
namespace SPIRV {
class SPIRVLowerBoolBase : public llvm::InstVisitor<SPIRVLowerBoolBase> {
public:
SPIRVLowerBoolBase() : Context(nullptr) {}
virtual ~SPIRVLowerBoolBase() {}
void replace(llvm::Instruction *I, llvm::Instruction *NewI);
bool isBoolType(llvm::Type *Ty);
virtual void visitTruncInst(llvm::TruncInst &I);
void handleExtInstructions(llvm::Instruction &I);
void handleCastInstructions(llvm::Instruction &I);
virtual void visitZExtInst(llvm::ZExtInst &I);
virtual void visitSExtInst(llvm::SExtInst &I);
virtual void visitUIToFPInst(llvm::UIToFPInst &I);
virtual void visitSIToFPInst(llvm::SIToFPInst &I);
bool runLowerBool(llvm::Module &M);
private:
llvm::LLVMContext *Context;
};
class SPIRVLowerBoolPass : public llvm::PassInfoMixin<SPIRVLowerBoolPass>,
public SPIRVLowerBoolBase {
public:
llvm::PreservedAnalyses run(llvm::Module &M,
llvm::ModuleAnalysisManager &MAM);
static bool isRequired() { return true; }
};
class SPIRVLowerBoolLegacy : public llvm::ModulePass,
public SPIRVLowerBoolBase {
public:
SPIRVLowerBoolLegacy();
bool runOnModule(llvm::Module &M) override;
static char ID;
};
} // namespace SPIRV
#endif // SPIRV_SPIRVLOWERBOOL_H
@@ -0,0 +1,186 @@
//===- SPIRVLowerConstExpr.cpp - Regularize LLVM for SPIR-V ------- C++ -*-===//
//
// The LLVM/SPIRV Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
//
// 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 Advanced Micro Devices, Inc., 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.
//
//===----------------------------------------------------------------------===//
//
// This file implements regularization of LLVM module for SPIR-V.
//
//===----------------------------------------------------------------------===//
#include "SPIRVLowerConstExpr.h"
#include "OCLUtil.h"
#include "SPIRVInternal.h"
#include "SPIRVMDBuilder.h"
#include "SPIRVMDWalker.h"
#include "libSPIRV/SPIRVDebug.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/InstVisitor.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Pass.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/TargetParser/Triple.h"
#include <list>
#include <set>
#define DEBUG_TYPE "spv-lower-const-expr"
using namespace llvm;
using namespace SPIRV;
using namespace OCLUtil;
namespace SPIRV {
cl::opt<bool> SPIRVLowerConst(
"spirv-lower-const-expr", cl::init(true),
cl::desc("LLVM/SPIR-V translation enable lowering constant expression"));
class SPIRVLowerConstExprLegacy : public ModulePass,
public SPIRVLowerConstExprBase {
public:
SPIRVLowerConstExprLegacy() : ModulePass(ID) {
initializeSPIRVLowerConstExprLegacyPass(*PassRegistry::getPassRegistry());
}
bool runOnModule(Module &M) override { return runLowerConstExpr(M); }
static char ID;
};
char SPIRVLowerConstExprLegacy::ID = 0;
bool SPIRVLowerConstExprBase::runLowerConstExpr(Module &Module) {
if (!SPIRVLowerConst)
return false;
M = &Module;
Ctx = &M->getContext();
LLVM_DEBUG(dbgs() << "Enter SPIRVLowerConstExpr:\n");
bool Changed = visit(M);
verifyRegularizationPass(*M, "SPIRVLowerConstExpr");
return Changed;
}
/// Since SPIR-V cannot represent constant expression, constant expressions
/// in LLVM needs to be lowered to instructions.
/// For each function, the constant expressions used by instructions of the
/// function are replaced by instructions placed in the entry block since it
/// dominates all other BB's. Each constant expression only needs to be lowered
/// once in each function and all uses of it by instructions in that function
/// is replaced by one instruction.
/// ToDo: remove redundant instructions for common subexpression
bool SPIRVLowerConstExprBase::visit(Module *M) {
bool Changed = false;
for (auto &I : M->functions()) {
std::list<Instruction *> WorkList;
for (auto &BI : I) {
for (auto &II : BI) {
WorkList.push_back(&II);
}
}
auto FBegin = I.begin();
while (!WorkList.empty()) {
auto *II = WorkList.front();
auto LowerOp = [&II, &FBegin, &I, &Changed](Value *V) -> Value * {
if (isa<Function>(V))
return V;
auto *CE = cast<ConstantExpr>(V);
SPIRVDBG(dbgs() << "[lowerConstantExpressions] " << *CE;)
auto *ReplInst = CE->getAsInstruction();
auto InsPoint = II->getParent() == &*FBegin
? II->getIterator()
: FBegin->back().getIterator();
ReplInst->insertBefore(InsPoint);
SPIRVDBG(dbgs() << " -> " << *ReplInst << '\n';)
std::vector<Instruction *> Users;
// Do not replace use during iteration of use. Do it in another loop
for (auto *U : CE->users()) {
SPIRVDBG(dbgs() << "[lowerConstantExpressions] Use: " << *U << '\n';)
if (auto *InstUser = dyn_cast<Instruction>(U)) {
// Only replace users in scope of current function
if (InstUser->getParent()->getParent() == &I)
Users.push_back(InstUser);
}
}
for (auto &User : Users) {
if (ReplInst->getParent() == User->getParent())
if (User->comesBefore(ReplInst))
ReplInst->moveBefore(User->getIterator());
User->replaceUsesOfWith(CE, ReplInst);
}
Changed = true;
return ReplInst;
};
WorkList.pop_front();
for (unsigned OI = 0, OE = II->getNumOperands(); OI != OE; ++OI) {
auto *Op = II->getOperand(OI);
if (auto *CE = dyn_cast<ConstantExpr>(Op)) {
WorkList.push_front(cast<Instruction>(LowerOp(CE)));
} else if (auto *MDAsVal = dyn_cast<MetadataAsValue>(Op)) {
Metadata *MD = MDAsVal->getMetadata();
if (auto *ConstMD = dyn_cast<ConstantAsMetadata>(MD)) {
Constant *C = ConstMD->getValue();
Value *ReplInst = nullptr;
if (auto *CE = dyn_cast<ConstantExpr>(C))
ReplInst = LowerOp(CE);
if (ReplInst) {
Metadata *RepMD = ValueAsMetadata::get(ReplInst);
Value *RepMDVal = MetadataAsValue::get(M->getContext(), RepMD);
II->setOperand(OI, RepMDVal);
WorkList.push_front(cast<Instruction>(ReplInst));
}
}
}
}
}
}
return Changed;
}
} // namespace SPIRV
INITIALIZE_PASS(SPIRVLowerConstExprLegacy, "spv-lower-const-expr",
"Regularize LLVM for SPIR-V", false, false)
ModulePass *llvm::createSPIRVLowerConstExprLegacy() {
return new SPIRVLowerConstExprLegacy();
}
@@ -0,0 +1,53 @@
//===- SPIRVLowerConstExpr.h - Lower constant expression --------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
/// \file SPIRVLowerConstExpr.h
///
/// This file declares SPIRVLowerConstExprPass that lowers constant expression.
///
//===----------------------------------------------------------------------===//
#ifndef SPIRV_LOWERCONSTEXPR_H
#define SPIRV_LOWERCONSTEXPR_H
#include "llvm/IR/PassManager.h"
namespace llvm {
class LLVMContext;
} // namespace llvm
namespace SPIRV {
class SPIRVLowerConstExprBase {
public:
SPIRVLowerConstExprBase() : M(nullptr), Ctx(nullptr) {}
bool runLowerConstExpr(llvm::Module &M);
bool visit(llvm::Module *M);
private:
llvm::Module *M;
llvm::LLVMContext *Ctx;
};
class SPIRVLowerConstExprPass
: public llvm::PassInfoMixin<SPIRVLowerConstExprPass>,
public SPIRVLowerConstExprBase {
public:
llvm::PreservedAnalyses run(llvm::Module &M,
llvm::ModuleAnalysisManager &MAM) {
return runLowerConstExpr(M) ? llvm::PreservedAnalyses::none()
: llvm::PreservedAnalyses::all();
}
static bool isRequired() { return true; }
};
} // namespace SPIRV
#endif // SPIRV_LOWERCONSTEXPR_H
@@ -0,0 +1,226 @@
//===- SPIRVLowerLLVMIntrinsic.cpp - Lower llvm-intrinsics -----===//
//
// The LLVM/SPIRV Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2024 The Khronos Group Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
//
// This file implements lowering of:
// llvm.sadd.with.overflow.*
// llvm.bitreverse.*
// into basic LLVM operations.
//
//===----------------------------------------------------------------------===//
#include "SPIRVLowerLLVMIntrinsic.h"
#include "LLVMBitreverse.h"
#include "LLVMSaddWithOverflow.h"
#include "LLVMSPIRVLib.h"
#include "SPIRVError.h"
#include "libSPIRV/SPIRVDebug.h"
#include "llvm/IR/InstVisitor.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Module.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Linker/Linker.h"
#include "llvm/Support/SourceMgr.h"
#define DEBUG_TYPE "spv-lower-llvm_intrinsic"
using namespace llvm;
using namespace SPIRV;
namespace SPIRV {
namespace {
typedef struct {
// Extension that is required for an emulation
const ExtensionID RequiredExtension;
// Extension that supports the LLVM Intrinsic.
// Thus, emulation is not needed if extension is enabled.
const ExtensionID ForbiddenExtension;
// A mapping is only applied if the RequiredExtension and the
// ForbiddenExtension tests are both met. Thus, llvm.bitreverse.i2
// will not be lowered even if its RequiredExtension,
// SPV_INTEL_arbitrary_precision_integers, is enabled if its
// ForbiddenExtension, SPV_KHR_bit_instructions, is also enabled.
const char *ModuleText;
} LLVMIntrinsicMapValueType;
// clang-format off
#define NO_REQUIRED_EXTENSION ExtensionID::Last
#define NO_FORBIDDEN_EXTENSION ExtensionID::Last
const std::map<const StringRef, const LLVMIntrinsicMapValueType> LLVMIntrinsicMapEntries = {
// LLVM Intrinsic Name Required Extension Forbidden Extension Module with
// emulation function
{ "llvm.bitreverse.i2", {ExtensionID::SPV_INTEL_arbitrary_precision_integers, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversei2}},
{ "llvm.bitreverse.i4", {ExtensionID::SPV_INTEL_arbitrary_precision_integers, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversei4}},
{ "llvm.bitreverse.i8", {NO_REQUIRED_EXTENSION, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversei8}},
{ "llvm.bitreverse.i16", {NO_REQUIRED_EXTENSION, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversei16}},
{ "llvm.bitreverse.i32", {NO_REQUIRED_EXTENSION, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversei32}},
{ "llvm.bitreverse.i64", {NO_REQUIRED_EXTENSION, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversei64}},
{ "llvm.bitreverse.v2i2", {ExtensionID::SPV_INTEL_arbitrary_precision_integers, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversev2i2}},
{ "llvm.bitreverse.v2i4", {ExtensionID::SPV_INTEL_arbitrary_precision_integers, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversev2i4}},
{ "llvm.bitreverse.v2i8", {NO_REQUIRED_EXTENSION, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversev2i8}},
{ "llvm.bitreverse.v2i16", {NO_REQUIRED_EXTENSION, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversev2i16}},
{ "llvm.bitreverse.v2i32", {NO_REQUIRED_EXTENSION, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversev2i32}},
{ "llvm.bitreverse.v2i64", {NO_REQUIRED_EXTENSION, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversev2i64}},
{ "llvm.bitreverse.v3i2", {ExtensionID::SPV_INTEL_arbitrary_precision_integers, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversev3i2}},
{ "llvm.bitreverse.v3i4", {ExtensionID::SPV_INTEL_arbitrary_precision_integers, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversev3i4}},
{ "llvm.bitreverse.v3i8", {NO_REQUIRED_EXTENSION, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversev3i8}},
{ "llvm.bitreverse.v3i16", {NO_REQUIRED_EXTENSION, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversev3i16}},
{ "llvm.bitreverse.v3i32", {NO_REQUIRED_EXTENSION, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversev3i32}},
{ "llvm.bitreverse.v3i64", {NO_REQUIRED_EXTENSION, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversev3i64}},
{ "llvm.bitreverse.v4i2", {ExtensionID::SPV_INTEL_arbitrary_precision_integers, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversev4i2}},
{ "llvm.bitreverse.v4i4", {ExtensionID::SPV_INTEL_arbitrary_precision_integers, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversev4i4}},
{ "llvm.bitreverse.v4i8", {NO_REQUIRED_EXTENSION, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversev4i8}},
{ "llvm.bitreverse.v4i16", {NO_REQUIRED_EXTENSION, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversev4i16}},
{ "llvm.bitreverse.v4i32", {NO_REQUIRED_EXTENSION, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversev4i32}},
{ "llvm.bitreverse.v4i64", {NO_REQUIRED_EXTENSION, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversev4i64}},
{ "llvm.bitreverse.v8i2", {ExtensionID::SPV_INTEL_arbitrary_precision_integers, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversev8i2}},
{ "llvm.bitreverse.v8i4", {ExtensionID::SPV_INTEL_arbitrary_precision_integers, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversev8i4}},
{ "llvm.bitreverse.v8i8", {NO_REQUIRED_EXTENSION, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversev8i8}},
{ "llvm.bitreverse.v8i16", {NO_REQUIRED_EXTENSION, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversev8i16}},
{ "llvm.bitreverse.v8i32", {NO_REQUIRED_EXTENSION, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversev8i32}},
{ "llvm.bitreverse.v8i64", {NO_REQUIRED_EXTENSION, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversev8i64}},
{ "llvm.bitreverse.v16i2", {ExtensionID::SPV_INTEL_arbitrary_precision_integers, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversev16i2}},
{ "llvm.bitreverse.v16i4", {ExtensionID::SPV_INTEL_arbitrary_precision_integers, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversev16i4}},
{ "llvm.bitreverse.v16i8", {NO_REQUIRED_EXTENSION, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversev16i8}},
{ "llvm.bitreverse.v16i16", {NO_REQUIRED_EXTENSION, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversev16i16}},
{ "llvm.bitreverse.v16i32", {NO_REQUIRED_EXTENSION, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversev16i32}},
{ "llvm.bitreverse.v16i64", {NO_REQUIRED_EXTENSION, ExtensionID::SPV_KHR_bit_instructions, LLVMBitreversev16i64}},
{ "llvm.sadd.with.overflow.i16", {NO_REQUIRED_EXTENSION, NO_FORBIDDEN_EXTENSION, LLVMSaddWithOverflow}},
{ "llvm.sadd.with.overflow.i32", {NO_REQUIRED_EXTENSION, NO_FORBIDDEN_EXTENSION, LLVMSaddWithOverflow}},
{ "llvm.sadd.with.overflow.i64", {NO_REQUIRED_EXTENSION, NO_FORBIDDEN_EXTENSION, LLVMSaddWithOverflow}},
};
// clang-format on
} // namespace
void SPIRVLowerLLVMIntrinsicBase::visitIntrinsicInst(CallInst &I) {
IntrinsicInst *II = dyn_cast<IntrinsicInst>(&I);
if (!II)
return;
Function *IntrinsicFunc = I.getCalledFunction();
assert(IntrinsicFunc && "Missing function");
StringRef IntrinsicName = IntrinsicFunc->getName();
const LLVMIntrinsicMapValueType *MapEntry{nullptr};
auto It = LLVMIntrinsicMapEntries.find(IntrinsicName);
if (It != LLVMIntrinsicMapEntries.end())
MapEntry = &It->second;
if (!MapEntry ||
!(MapEntry->RequiredExtension == NO_REQUIRED_EXTENSION ||
Opts.isAllowedToUseExtension(MapEntry->RequiredExtension)) ||
Opts.isAllowedToUseExtension(MapEntry->ForbiddenExtension))
return;
// Redirect @llvm.* call to the function we have in
// the loaded module in ModuleText
std::string SPIRVFuncName = IntrinsicName.str();
std::replace(SPIRVFuncName.begin(), SPIRVFuncName.end(), '.', '_');
Function *F = Mod->getFunction(SPIRVFuncName);
if (F) { // This function is already linked in.
I.setCalledFunction(F);
return;
}
FunctionCallee FC =
Mod->getOrInsertFunction(SPIRVFuncName, I.getFunctionType());
I.setCalledFunction(FC);
// Read LLVM IR with the intrinsic's implementation
SMDiagnostic Err;
auto MB = MemoryBuffer::getMemBuffer(MapEntry->ModuleText);
auto EmulationModule = parseIR(MB->getMemBufferRef(), Err, *Context,
ParserCallbacks([&](StringRef, StringRef) {
return Mod->getDataLayoutStr();
}));
if (!EmulationModule) {
std::string ErrMsg;
raw_string_ostream ErrStream(ErrMsg);
Err.print("", ErrStream);
SPIRVErrorLog EL;
EL.checkError(false, SPIRVEC_InvalidLlvmModule, ErrMsg);
return;
}
// Link in the intrinsic's implementation.
if (!Linker::linkModules(*Mod, std::move(EmulationModule),
Linker::LinkOnlyNeeded))
TheModuleIsModified = true;
}
bool SPIRVLowerLLVMIntrinsicBase::runLowerLLVMIntrinsic(Module &M) {
Context = &M.getContext();
Mod = &M;
visit(M);
verifyRegularizationPass(M, "SPIRVLowerLLVMIntrinsic");
return TheModuleIsModified;
}
SPIRVLowerLLVMIntrinsicPass::SPIRVLowerLLVMIntrinsicPass(
const SPIRV::TranslatorOpts &Opts)
: SPIRVLowerLLVMIntrinsicBase(Opts) {}
llvm::PreservedAnalyses
SPIRVLowerLLVMIntrinsicPass::run(llvm::Module &M,
llvm::ModuleAnalysisManager &MAM) {
return runLowerLLVMIntrinsic(M) ? llvm::PreservedAnalyses::none()
: llvm::PreservedAnalyses::all();
}
SPIRVLowerLLVMIntrinsicLegacy::SPIRVLowerLLVMIntrinsicLegacy(
const SPIRV::TranslatorOpts &Opts)
: ModulePass(ID), SPIRVLowerLLVMIntrinsicBase(Opts) {
initializeSPIRVLowerLLVMIntrinsicLegacyPass(*PassRegistry::getPassRegistry());
}
bool SPIRVLowerLLVMIntrinsicLegacy::runOnModule(Module &M) {
return runLowerLLVMIntrinsic(M);
}
char SPIRVLowerLLVMIntrinsicLegacy::ID = 0;
} // namespace SPIRV
// INITIALIZE_PASS defines static functions but clang-tidy enforces
// anonymous namespace
// NOLINTNEXTLINE
INITIALIZE_PASS(SPIRVLowerLLVMIntrinsicLegacy, "spv-lower-llvm-intrinsic",
"Lower llvm intrinsics", false, false)
ModulePass *
llvm::createSPIRVLowerLLVMIntrinsicLegacy(const SPIRV::TranslatorOpts &Opts) {
return new SPIRVLowerLLVMIntrinsicLegacy(Opts);
}
@@ -0,0 +1,87 @@
//===- SPIRVLowerLLVMIntrinsic.h - llvm-intrinsic lowering --------*- C++
//-*-===//
//
// The LLVM/SPIR-V Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2022 The Khronos Group Inc.
//
// 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 Khronos Group, 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.
//
//===----------------------------------------------------------------------===//
#ifndef SPIRV_SPIRVLOWERLLVMINTRINSIC_H
#define SPIRV_SPIRVLOWERLLVMINTRINSIC_H
#include "LLVMSPIRVOpts.h"
#include "llvm/IR/InstVisitor.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Pass.h"
namespace SPIRV {
class SPIRVLowerLLVMIntrinsicBase
: public llvm::InstVisitor<SPIRVLowerLLVMIntrinsicBase> {
public:
SPIRVLowerLLVMIntrinsicBase(const SPIRV::TranslatorOpts &Opts)
: Context(nullptr), Mod(nullptr), Opts(Opts) {}
virtual ~SPIRVLowerLLVMIntrinsicBase() {}
virtual void visitIntrinsicInst(llvm::CallInst &I);
bool runLowerLLVMIntrinsic(llvm::Module &M);
private:
llvm::LLVMContext *Context;
llvm::Module *Mod;
const SPIRV::TranslatorOpts Opts;
bool TheModuleIsModified = false;
};
class SPIRVLowerLLVMIntrinsicPass
: public llvm::PassInfoMixin<SPIRVLowerLLVMIntrinsicPass>,
public SPIRVLowerLLVMIntrinsicBase {
public:
SPIRVLowerLLVMIntrinsicPass(const SPIRV::TranslatorOpts &Opts);
llvm::PreservedAnalyses run(llvm::Module &M,
llvm::ModuleAnalysisManager &MAM);
static bool isRequired() { return true; }
};
class SPIRVLowerLLVMIntrinsicLegacy : public llvm::ModulePass,
public SPIRVLowerLLVMIntrinsicBase {
public:
SPIRVLowerLLVMIntrinsicLegacy(const SPIRV::TranslatorOpts &Opts);
bool runOnModule(llvm::Module &M) override;
static char ID;
};
} // namespace SPIRV
#endif // SPIRV_SPIRVLOWERLLVMINTRINSIC_H
@@ -0,0 +1,161 @@
//===- SPIRVLowerMemmove.cpp - Lower llvm.memmove to llvm.memcpys ---------===//
//
// The LLVM/SPIRV Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
//
// 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 Advanced Micro Devices, Inc., 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.
//
//===----------------------------------------------------------------------===//
//
// This file implements lowering llvm.memmove into several llvm.memcpys.
//
//===----------------------------------------------------------------------===//
#include "SPIRVLowerMemmove.h"
#include "SPIRVInternal.h"
#include "libSPIRV/SPIRVDebug.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/Transforms/Utils/LowerMemIntrinsics.h"
#define DEBUG_TYPE "spvmemmove"
using namespace llvm;
using namespace SPIRV;
namespace SPIRV {
void SPIRVLowerMemmoveBase::LowerMemMoveInst(MemMoveInst &I) {
// There is no direct equivalent of @llvm.memmove in SPIR-V and the closest
// instructions are 'OpCopyMemory' and 'OpCopyMemorySized'.
//
// 'OpCopyMemory' does not accept amount of bytes to copy and infers that
// from type which is being copied; also it only allows to copy value of a
// particular type to pointer pointing to the same type.
//
// 'OpCopyMemorySized' is closer to @llvm.memmove, because it actually
// copies bytes, but unlike memove it is not explicitly specified whether it
// supports overlapping source and destination. Therefore, we replace
// memmove with two 'OpCopyMemorySized' instructions: the first one copies
// bytes from source to a temporary location, the second one copies bytes
// from that temporary location to the destination.
IRBuilder<> Builder(I.getParent());
Builder.SetInsertPoint(&I);
auto *Length = cast<ConstantInt>(I.getLength());
auto *AllocaTy =
ArrayType::get(IntegerType::getInt8Ty(*Context), Length->getZExtValue());
MaybeAlign SrcAlign = I.getSourceAlign();
AllocaInst *Alloca;
{
IRBuilderBase::InsertPointGuard IG(Builder);
Builder.SetInsertPointPastAllocas(I.getParent()->getParent());
Alloca = Builder.CreateAlloca(AllocaTy);
if (SrcAlign.has_value())
Alloca->setAlignment(SrcAlign.value());
}
// FIXME: Do we need to pass the size of alloca here? From LangRef:
// > The first argument is a constant integer representing the size of the
// > object, or -1 if it is variable sized.
//
// https://llvm.org/docs/LangRef.html#llvm-lifetime-start-intrinsic
Builder.CreateLifetimeStart(Alloca);
Builder.CreateMemCpy(Alloca, SrcAlign, I.getRawSource(), SrcAlign, Length,
I.isVolatile());
auto *SecondCpy =
Builder.CreateMemCpy(I.getRawDest(), I.getDestAlign(), Alloca, SrcAlign,
Length, I.isVolatile());
Builder.CreateLifetimeEnd(Alloca);
SecondCpy->takeName(&I);
I.replaceAllUsesWith(SecondCpy);
I.dropAllReferences();
I.eraseFromParent();
}
bool SPIRVLowerMemmoveBase::expandMemMoveIntrinsicUses(Function &F) {
bool Changed = false;
for (User *U : make_early_inc_range(F.users())) {
MemMoveInst *Inst = cast<MemMoveInst>(U);
if (!isa<ConstantInt>(Inst->getLength())) {
expandMemMoveAsLoop(Inst,
TargetTransformInfo(F.getParent()->getDataLayout()));
Inst->eraseFromParent();
} else {
LowerMemMoveInst(*Inst);
}
Changed = true;
}
return Changed;
}
bool SPIRVLowerMemmoveBase::runLowerMemmove(Module &M) {
Context = &M.getContext();
bool Changed = false;
for (Function &F : M) {
if (!F.isDeclaration())
continue;
if (F.getIntrinsicID() == Intrinsic::memmove)
Changed |= expandMemMoveIntrinsicUses(F);
}
verifyRegularizationPass(M, "SPIRVLowerMemmove");
return Changed;
}
llvm::PreservedAnalyses
SPIRVLowerMemmovePass::run(llvm::Module &M, llvm::ModuleAnalysisManager &MAM) {
return runLowerMemmove(M) ? llvm::PreservedAnalyses::none()
: llvm::PreservedAnalyses::all();
}
SPIRVLowerMemmoveLegacy::SPIRVLowerMemmoveLegacy() : ModulePass(ID) {
initializeSPIRVLowerMemmoveLegacyPass(*PassRegistry::getPassRegistry());
}
bool SPIRVLowerMemmoveLegacy::runOnModule(Module &M) {
return runLowerMemmove(M);
}
char SPIRVLowerMemmoveLegacy::ID = 0;
} // namespace SPIRV
INITIALIZE_PASS(SPIRVLowerMemmoveLegacy, "spvmemmove",
"Lower llvm.memmove into llvm.memcpy", false, false)
ModulePass *llvm::createSPIRVLowerMemmoveLegacy() {
return new SPIRVLowerMemmoveLegacy();
}
@@ -0,0 +1,81 @@
//===- SPIRVLowerMemmove.h - memmove lowering --------*- C++ -*-===//
//
// The LLVM/SPIR-V Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2022 The Khronos Group Inc.
//
// 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 Khronos Group, 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.
//
//===----------------------------------------------------------------------===//
//
// This file implements lowering llvm.memmove into several llvm.memcpys.
//
//===----------------------------------------------------------------------===//
#ifndef SPIRV_SPIRVLOWERMEMMOVE_H
#define SPIRV_SPIRVLOWERMEMMOVE_H
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Pass.h"
namespace SPIRV {
class SPIRVLowerMemmoveBase {
public:
SPIRVLowerMemmoveBase() : Context(nullptr) {}
void LowerMemMoveInst(llvm::MemMoveInst &I);
bool expandMemMoveIntrinsicUses(llvm::Function &F);
bool runLowerMemmove(llvm::Module &M);
private:
llvm::LLVMContext *Context;
};
class SPIRVLowerMemmovePass : public llvm::PassInfoMixin<SPIRVLowerMemmovePass>,
public SPIRVLowerMemmoveBase {
public:
llvm::PreservedAnalyses run(llvm::Module &M,
llvm::ModuleAnalysisManager &MAM);
static bool isRequired() { return true; }
};
class SPIRVLowerMemmoveLegacy : public llvm::ModulePass,
public SPIRVLowerMemmoveBase {
public:
SPIRVLowerMemmoveLegacy();
bool runOnModule(llvm::Module &M) override;
static char ID;
};
} // namespace SPIRV
#endif // SPIRV_SPIRVLOWERMEMMOVE_H
@@ -0,0 +1,115 @@
//===- SPIRVLowerOCLBlocks.cpp - OCL Utilities ----------------------------===//
//
// The LLVM/SPIRV Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2018 Intel Corporation. All rights reserved.
//
// 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 Intel Corporation, 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.
//
//===----------------------------------------------------------------------===//
//
// SPIR-V specification doesn't allow function pointers, so SPIR-V translator
// is designed to fail if a value with function type (except calls) occurs.
// Currently there is only two cases, when function pointers are generating in
// LLVM IR in OpenCL - block calls and device side enqueue built-in calls.
//
// In both cases values with function type used as intermediate representation
// for block literal structure.
//
// In LLVM IR produced by clang, blocks are represented with the following
// structure:
// %struct.__opencl_block_literal_generic = type { i32, i32, i8 addrspace(4)* }
// Pointers to block invoke functions are stored in the third field. Clang
// replaces indirect function calls in all cases except if block is passed as a
// function argument. Note that it is somewhat unclear if the OpenCL C spec
// should allow passing blocks as function arguments. This pass is not supposed
// to work correctly with such functions.
// Clang though has to store function pointers to this structure. Purpose of
// this pass is to replace store of function pointers(not allowed in SPIR-V)
// with null pointers.
//
//===----------------------------------------------------------------------===//
#include "SPIRVLowerOCLBlocks.h"
#include "SPIRVInternal.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Pass.h"
#include "llvm/Support/Regex.h"
#define DEBUG_TYPE "spv-lower-ocl-blocks"
using namespace llvm;
namespace {
static bool isBlockInvoke(Function &F) {
static Regex BlockInvokeRegex("_block_invoke_?[0-9]*$");
return BlockInvokeRegex.match(F.getName());
}
} // namespace
namespace SPIRV {
bool SPIRVLowerOCLBlocksBase::runLowerOCLBlocks(Module &M) {
bool Changed = false;
for (Function &F : M) {
if (!isBlockInvoke(F))
continue;
for (User *U : F.users()) {
if (!isa<Constant>(U))
continue;
Constant *Null = Constant::getNullValue(U->getType());
if (U != Null) {
U->replaceAllUsesWith(Null);
Changed = true;
}
}
}
return Changed;
}
llvm::PreservedAnalyses
SPIRVLowerOCLBlocksPass::run(llvm::Module &M,
llvm::ModuleAnalysisManager &MAM) {
return runLowerOCLBlocks(M) ? llvm::PreservedAnalyses::none()
: llvm::PreservedAnalyses::all();
}
char SPIRVLowerOCLBlocksLegacy::ID = 0;
} // namespace SPIRV
INITIALIZE_PASS(SPIRVLowerOCLBlocksLegacy, "spv-lower-ocl-blocks",
"Remove function pointers originating from OpenCL blocks",
false, false)
llvm::ModulePass *llvm::createSPIRVLowerOCLBlocksLegacy() {
return new SPIRVLowerOCLBlocksLegacy();
}
@@ -0,0 +1,76 @@
//===- SPIRVLowerOCLBlocks.h - OpenCL block lowering --------*- C++ -*-===//
//
// The LLVM/SPIR-V Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2022 The Khronos Group Inc.
//
// 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 Khronos Group, 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.
//
//===----------------------------------------------------------------------===//
#ifndef SPIRV_SPIRVLOWEROCLBLOCKS_H
#define SPIRV_SPIRVLOWEROCLBLOCKS_H
#include "llvm/IR/PassManager.h"
#include "llvm/Pass.h"
namespace SPIRV {
class SPIRVLowerOCLBlocksBase {
public:
SPIRVLowerOCLBlocksBase() {}
bool runLowerOCLBlocks(llvm::Module &M);
};
class SPIRVLowerOCLBlocksPass
: public llvm::PassInfoMixin<SPIRVLowerOCLBlocksPass>,
public SPIRVLowerOCLBlocksBase {
public:
llvm::PreservedAnalyses run(llvm::Module &M,
llvm::ModuleAnalysisManager &MAM);
static bool isRequired() { return true; }
};
class SPIRVLowerOCLBlocksLegacy : public llvm::ModulePass,
public SPIRVLowerOCLBlocksBase {
public:
SPIRVLowerOCLBlocksLegacy() : ModulePass(ID) {}
bool runOnModule(llvm::Module &M) override { return runLowerOCLBlocks(M); }
llvm::StringRef getPassName() const override {
return "Lower OpenCL Blocks For SPIR-V";
}
static char ID;
};
} // namespace SPIRV
#endif // SPIRV_SPIRVLOWEROCLBLOCKS_H
@@ -0,0 +1,131 @@
//===- SPIRVMDBuilder.h - SPIR-V metadata builder header file --*- C++ -*-===//
//
// The LLVM/SPIRV Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
//
// 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 Advanced Micro Devices, Inc., 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.
//
//===----------------------------------------------------------------------===//
/// \file
///
/// This file declares classes for creating SPIR-V metadata.
///
//===----------------------------------------------------------------------===//
#ifndef SPIRV_SPIRVMDBUILDER_H
#define SPIRV_SPIRVMDBUILDER_H
#include "SPIRVInternal.h"
#include "llvm/IR/Metadata.h"
#include <functional>
using namespace llvm;
namespace SPIRV {
class SPIRVMDBuilder {
public:
template <typename ParentT> struct MDWrapper;
struct NamedMDWrapper {
NamedMDWrapper(NamedMDNode &Named, SPIRVMDBuilder &BB)
: NMD(Named), B(BB) {}
MDWrapper<NamedMDWrapper> addOp() {
return MDWrapper<NamedMDWrapper>(*this, B);
}
NamedMDWrapper &addOp(MDWrapper<NamedMDWrapper> &MD) {
NMD.addOperand(MD.M);
return *this;
}
NamedMDNode &NMD;
SPIRVMDBuilder &B;
};
template <typename ParentT> struct MDWrapper {
MDWrapper(ParentT &Parent, SPIRVMDBuilder &Builder)
: M(nullptr), P(Parent), B(Builder) {}
MDWrapper &add(unsigned I) {
V.push_back(ConstantAsMetadata::get(getUInt32(&B.M, I)));
return *this;
}
MDWrapper &addU16(unsigned short I) {
V.push_back(ConstantAsMetadata::get(getUInt16(&B.M, I)));
return *this;
}
MDWrapper &add(StringRef S) {
V.push_back(MDString::get(B.C, S));
return *this;
}
MDWrapper &add(Function *F) {
V.push_back(ConstantAsMetadata::get(F));
return *this;
}
MDWrapper &add(SmallVectorImpl<StringRef> &S) {
for (auto &I : S)
add(I);
return *this;
}
MDWrapper &addOp(MDNode *Node) {
V.push_back(Node);
return *this;
}
MDWrapper<MDWrapper> addOp() { return MDWrapper<MDWrapper>(*this, B); }
MDWrapper &addOp(MDWrapper<MDWrapper> &MD) {
V.push_back(MD.M);
return *this;
}
/// Generate the scheduled MDNode and return the parent.
/// If \param Ptr is not nullptr, save the generated MDNode.
ParentT &done(MDNode **Ptr = nullptr) {
M = MDNode::get(B.C, V);
if (Ptr)
*Ptr = M;
return P.addOp(*this);
}
MDNode *M;
ParentT &P;
SPIRVMDBuilder &B;
SmallVector<Metadata *, 10> V;
};
explicit SPIRVMDBuilder(Module &Mod) : M(Mod), C(Mod.getContext()) {}
NamedMDWrapper addNamedMD(StringRef Name) {
return NamedMDWrapper(*M.getOrInsertNamedMetadata(Name), *this);
}
SPIRVMDBuilder &eraseNamedMD(StringRef Name) {
if (auto N = M.getNamedMetadata(Name))
M.eraseNamedMetadata(N);
return *this;
}
friend struct NamedMDWrapper;
private:
Module &M;
LLVMContext &C;
};
} /* namespace SPIRV */
#endif // SPIRV_SPIRVMDBUILDER_H
@@ -0,0 +1,177 @@
//===- SPIRVMDWalker.h - SPIR-V metadata walker header file ----*- C++ -*-===//
//
// The LLVM/SPIRV Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
//
// 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 Advanced Micro Devices, Inc., 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.
//
//===----------------------------------------------------------------------===//
/// \file
///
/// This file declares classes for walking SPIR-V metadata.
///
//===----------------------------------------------------------------------===//
#ifndef SPIRV_SPIRVMDWALKER_H
#define SPIRV_SPIRVMDWALKER_H
#include "SPIRVInternal.h"
#include "llvm/IR/Metadata.h"
#include <functional>
using namespace llvm;
namespace SPIRV {
class SPIRVMDWalker {
public:
template <typename ParentT> struct MDWrapper;
struct NamedMDWrapper {
NamedMDWrapper(NamedMDNode *Named, SPIRVMDWalker &WW)
: NMD(Named), W(WW), I(0), Q(true) {
E = Named ? Named->getNumOperands() : 0;
}
operator bool() const { return NMD; }
bool atEnd() const { return !(NMD && I < E); }
MDWrapper<NamedMDWrapper> nextOp() {
if (!Q)
assert(I < E && "out of bound");
return MDWrapper<NamedMDWrapper>(
(NMD && I < E) ? NMD->getOperand(I++) : nullptr, *this, W);
}
NamedMDWrapper &setQuiet(bool Quiet) {
Q = Quiet;
return *this;
}
NamedMDNode *NMD;
SPIRVMDWalker &W;
unsigned I;
unsigned E;
bool Q; // Quiet
};
template <typename ParentT> struct MDWrapper {
MDWrapper(MDNode *Node, ParentT &Parent, SPIRVMDWalker &Walker)
: M(Node), P(Parent), W(Walker), I(0), Q(false) {
E = Node ? Node->getNumOperands() : 0;
}
operator bool() const { return M; }
bool atEnd() const { return !(M && I < E); }
template <typename T> MDWrapper &get(T &V) {
if (!Q)
assert(I < E && "out of bound");
if (atEnd())
return *this;
V = mdconst::dyn_extract<ConstantInt>(M->getOperand(I++))->getZExtValue();
return *this;
}
MDWrapper &get(std::string &S) {
if (!Q)
assert(I < E && "out of bound");
if (atEnd())
return *this;
Metadata *Op = M->getOperand(I++);
if (!Op)
S = "";
else if (auto Str = dyn_cast<MDString>(Op))
S = Str->getString().str();
else
S = "";
return *this;
}
MDWrapper &get(Function *&F) {
if (!Q)
assert(I < E && "out of bound");
if (atEnd())
return *this;
F = mdconst::dyn_extract<Function>(M->getOperand(I++));
return *this;
}
MDWrapper &get(SmallVectorImpl<std::string> &SV) {
if (atEnd())
return *this;
while (I < E) {
std::string S;
get(S);
SV.push_back(S);
}
return *this;
}
MDWrapper<MDWrapper> nextOp() {
if (!Q)
assert(I < E && "out of bound");
return MDWrapper<MDWrapper>(
(M && I < E) ? dyn_cast<MDNode>(M->getOperand(I++)) : nullptr, *this,
W);
}
ParentT &done() { return P; }
MDWrapper &setQuiet(bool Quiet) {
Q = Quiet;
return *this;
}
MDNode *M;
ParentT &P;
SPIRVMDWalker &W;
SmallVector<Metadata *, 10> V;
unsigned I;
unsigned E;
bool Q; // Quiet
};
explicit SPIRVMDWalker(Module &Mod) : M(Mod), C(Mod.getContext()) {}
NamedMDWrapper getNamedMD(StringRef Name) {
return NamedMDWrapper(M.getNamedMetadata(Name), *this);
}
friend struct NamedMDWrapper;
private:
Module &M;
LLVMContext &C;
};
} /* namespace SPIRV */
#endif // SPIRV_SPIRVMDWALKER_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,276 @@
//===- SPIRVReader.h - Converts SPIR-V to LLVM ------------------*- C++ -*-===//
//
// The LLVM/SPIR-V Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
//
// 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 Advanced Micro Devices, Inc., 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.
//
//===----------------------------------------------------------------------===//
/// \file
///
/// This file contains declaration of SPIRVToLLVM class which implements
/// conversion of SPIR-V binary to LLVM IR.
///
//===----------------------------------------------------------------------===//
#ifndef SPIRVREADER_H
#define SPIRVREADER_H
#include "SPIRVBuiltinHelper.h"
#include "SPIRVInternal.h"
#include "SPIRVModule.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/IR/GlobalValue.h" // llvm::GlobalValue::LinkageTypes
namespace llvm {
class Metadata;
class Module;
class Type;
class Instruction;
class CallInst;
class BasicBlock;
class Loop;
class Function;
class GlobalVariable;
class LLVMContext;
class MDString;
class IntrinsicInst;
class LoadInst;
class BranchInst;
class BinaryOperator;
class Value;
} // namespace llvm
using namespace llvm;
namespace SPIRV {
class SPIRVFunctionParameter;
class SPIRVConstantSampler;
class SPIRVConstantPipeStorage;
class SPIRVLoopMerge;
class SPIRVToLLVMDbgTran;
class SPIRVToLLVM : private BuiltinCallHelper {
public:
SPIRVToLLVM(Module *LLVMModule, SPIRVModule *TheSPIRVModule);
static const StringSet<> BuiltInConstFunc;
/// Translate the SPIR-V type into an LLVM type. If UseTypedPointerTypes is
/// true, then generate a TypedPointerType instead of a PointerType. The
/// intended use of TypedPointerTypes is for name mangling, so pointer types
/// that occur as array members or struct members will not be represented with
/// TypedPointerType, even when UseTypedPointerTypes is true.
Type *transType(SPIRVType *BT, bool UseTypedPointerTypes = false);
std::string transTypeToOCLTypeName(SPIRVType *BT, bool IsSigned = true);
std::vector<Type *> transTypeVector(const std::vector<SPIRVType *> &,
bool UseTypedPointerTypes = false);
bool translate();
bool transAddressingModel();
Value *transValue(SPIRVValue *, Function *F, BasicBlock *,
bool CreatePlaceHolder = true);
Value *transValueWithoutDecoration(SPIRVValue *, Function *F, BasicBlock *,
bool CreatePlaceHolder = true);
bool transDecoration(SPIRVValue *, Value *);
bool transAlign(SPIRVValue *, Value *);
Instruction *transOCLBuiltinFromExtInst(SPIRVExtInst *BC, BasicBlock *BB);
void transAuxDataInst(SPIRVExtInst *BC);
std::vector<Value *> transValue(const std::vector<SPIRVValue *> &,
Function *F, BasicBlock *);
Function *transFunction(SPIRVFunction *F, unsigned AS = SPIRAS_Private);
void transFunctionAttrs(SPIRVFunction *BF, Function *F);
Value *transBlockInvoke(SPIRVValue *Invoke, BasicBlock *BB);
Instruction *transWGSizeQueryBI(SPIRVInstruction *BI, BasicBlock *BB);
Instruction *transSGSizeQueryBI(SPIRVInstruction *BI, BasicBlock *BB);
bool transFPContractMetadata();
bool transMetadata();
bool transOCLMetadata(SPIRVFunction *BF);
bool transVectorComputeMetadata(SPIRVFunction *BF);
bool transFPGAFunctionMetadata(SPIRVFunction *BF, Function *F);
Value *transAsmINTEL(SPIRVAsmINTEL *BA);
CallInst *transAsmCallINTEL(SPIRVAsmCallINTEL *BI, Function *F,
BasicBlock *BB);
Value *transFixedPointInst(SPIRVInstruction *BI, BasicBlock *BB);
Value *transArbFloatInst(SPIRVInstruction *BI, BasicBlock *BB,
bool IsBinaryInst = false);
bool transNonTemporalMetadata(Instruction *I);
template <typename SPIRVInstType>
void transAliasingMemAccess(SPIRVInstType *BI, Instruction *I);
void addMemAliasMetadata(Instruction *I, SPIRVId AliasListId,
uint32_t AliasMDKind);
void transSourceLanguage();
bool transSourceExtension();
void transGeneratorMD();
Value *transConvertInst(SPIRVValue *BV, Function *F, BasicBlock *BB);
Instruction *transBuiltinFromInst(const std::string &FuncName,
SPIRVInstruction *BI, BasicBlock *BB);
Instruction *transSPIRVBuiltinFromInst(SPIRVInstruction *BI, BasicBlock *BB);
/// \brief Expand OCL builtin functions with scalar argument, e.g.
/// step, smoothstep.
/// gentype func (fp edge, gentype x)
/// =>
/// gentype func (gentype edge, gentype x)
/// \return transformed call instruction.
CallInst *expandOCLBuiltinWithScalarArg(CallInst *CI,
const std::string &FuncName);
typedef DenseMap<SPIRVType *, Type *> SPIRVToLLVMTypeMap;
typedef DenseMap<SPIRVValue *, Value *> SPIRVToLLVMValueMap;
typedef DenseMap<SPIRVValue *, Value *> SPIRVBlockToLLVMStructMap;
typedef DenseMap<SPIRVFunction *, Function *> SPIRVToLLVMFunctionMap;
typedef DenseMap<GlobalVariable *, SPIRVBuiltinVariableKind> BuiltinVarMap;
typedef std::unordered_map<SPIRVId, MDNode *> SPIRVToLLVMMDAliasInstMap;
// A SPIRV value may be translated to a load instruction of a placeholder
// global variable. This map records load instruction of these placeholders
// which are supposed to be replaced by the real values later.
typedef std::unordered_map<SPIRVValue *, LoadInst *>
SPIRVToLLVMPlaceholderMap;
typedef std::unordered_map<const BasicBlock *, const SPIRVValue *>
SPIRVToLLVMLoopMetadataMap;
// Store all the allocations to Struct Types that are further
// accessed inside GetElementPtr instruction or in ptr.annotation intrinsics.
// For every structure we save the accessed structure field index and the
// last corresponding translated LLVM instruction.
typedef std::unordered_map<Value *,
std::unordered_map<SPIRVWord, Instruction *>>
TypeToGEPOrUseMap;
private:
Module *M;
LLVMContext *Context;
SPIRVModule *BM;
SPIRVToLLVMTypeMap TypeMap;
SPIRVToLLVMValueMap ValueMap;
SPIRVToLLVMFunctionMap FuncMap;
SPIRVBlockToLLVMStructMap BlockMap;
SPIRVToLLVMPlaceholderMap PlaceholderMap;
std::unique_ptr<SPIRVToLLVMDbgTran> DbgTran;
// GlobalAnnotations collects array of annotation entries for global variables
// and functions. They are used in translation of llvm.global.annotations
// instruction.
std::vector<Constant *> GlobalAnnotations;
// AnnotationsMap helps to translate annotation strings for local variables.
// Map values are pointers on global strings in LLVM-IR. It is used to avoid
// duplication of these annotation strings in LLVM-IR, which can be caused by
// multiple translation of UserSemantic decorations with the same literal.
std::unordered_map<std::string, Constant *> AnnotationsMap;
// Loops metadata is translated in the end of a function translation.
// This storage contains pairs of translated loop header basic block and loop
// metadata SPIR-V instruction in SPIR-V representation of this basic block.
SPIRVToLLVMLoopMetadataMap FuncLoopMetadataMap;
// These storages are used to prevent duplication of alias.scope/noalias
// metadata
SPIRVToLLVMMDAliasInstMap MDAliasDomainMap;
SPIRVToLLVMMDAliasInstMap MDAliasScopeMap;
SPIRVToLLVMMDAliasInstMap MDAliasListMap;
TypeToGEPOrUseMap GEPOrUseMap;
Type *mapType(SPIRVType *BT, Type *T);
// If a value is mapped twice, the existing mapped value is a placeholder,
// which must be a load instruction of a global variable whose name starts
// with kPlaceholderPrefix.
Value *mapValue(SPIRVValue *BV, Value *V);
// OpenCL function always has NoUnwind attribute.
// Change this if it is no longer true.
bool isFuncNoUnwind() const { return true; }
bool isFuncReadNone(const std::string &Name) const {
return BuiltInConstFunc.count(Name);
}
bool isDirectlyTranslatedToOCL(Op OpCode) const;
MDString *transOCLKernelArgTypeName(SPIRVFunctionParameter *);
// Attempt to translate Id as a (specialization) constant.
std::optional<uint64_t> transIdAsConstant(SPIRVId Id);
// Return the value of an Alignment or AlignmentId decoration for V.
std::optional<uint64_t> getAlignment(SPIRVValue *V);
Value *mapFunction(SPIRVFunction *BF, Function *F);
Value *getTranslatedValue(SPIRVValue *BV);
IntrinsicInst *getLifetimeStartIntrinsic(Instruction *I);
SPIRVErrorLog &getErrorLog();
void setCallingConv(CallInst *Call);
Type *transFPType(SPIRVType *T);
Value *transShiftLogicalBitwiseInst(SPIRVValue *BV, BasicBlock *BB,
Function *F);
Value *transCmpInst(SPIRVValue *BV, BasicBlock *BB, Function *F);
void transOCLBuiltinFromInstPreproc(SPIRVInstruction *BI, Type *&RetTy,
std::vector<SPIRVValue *> &Args);
Instruction *transOCLBuiltinPostproc(SPIRVInstruction *BI, CallInst *CI,
BasicBlock *BB,
const std::string &DemangledName);
std::string transOCLImageTypeAccessQualifier(SPIRV::SPIRVTypeImage *ST);
std::string transOCLPipeTypeAccessQualifier(SPIRV::SPIRVTypePipe *ST);
std::string transVCTypeName(SPIRVTypeBufferSurfaceINTEL *PST);
Value *oclTransConstantSampler(SPIRV::SPIRVConstantSampler *BCS,
BasicBlock *BB);
Value *oclTransConstantPipeStorage(SPIRV::SPIRVConstantPipeStorage *BCPS);
void setName(llvm::Value *V, SPIRVValue *BV);
template <typename LoopInstType>
void setLLVMLoopMetadata(const LoopInstType *LM, const Loop *LoopObj);
void transLLVMLoopMetadata(const Function *F);
inline llvm::Metadata *getMetadataFromName(std::string Name);
inline std::vector<llvm::Metadata *>
getMetadataFromNameAndParameter(std::string Name, SPIRVWord Parameter);
inline MDNode *getMetadataFromNameAndParameter(std::string Name,
int64_t Parameter);
template <class Source, class Func> bool foreachFuncCtlMask(Source, Func);
llvm::GlobalValue::LinkageTypes transLinkageType(const SPIRVValue *V);
Instruction *transAllAny(SPIRVInstruction *BI, BasicBlock *BB);
Instruction *transRelational(SPIRVInstruction *BI, BasicBlock *BB);
void transUserSemantic(SPIRV::SPIRVFunction *Fun);
void transGlobalAnnotations();
void transGlobalCtorDtors(SPIRVVariableBase *BV);
void createCXXStructor(const char *ListName,
SmallVectorImpl<Function *> &Funcs);
void transIntelFPGADecorations(SPIRVValue *BV, Value *V);
void transMemAliasingINTELDecorations(SPIRVValue *BV, Value *V);
void transDecorationsToMetadata(SPIRVValue *BV, Value *V);
void transFunctionDecorationsToMetadata(SPIRVFunction *BF, Function *F);
void
transFunctionPointerCallArgumentAttributes(SPIRVValue *BV, CallInst *CI,
SPIRVTypeFunction *CalledFnTy);
}; // class SPIRVToLLVM
} // namespace SPIRV
#endif // SPIRVREADER_H
@@ -0,0 +1,840 @@
//===- SPIRVRegularizeLLVM.cpp - Regularize LLVM for SPIR-V ------- C++ -*-===//
//
// The LLVM/SPIRV Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
//
// 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 Advanced Micro Devices, Inc., 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.
//
//===----------------------------------------------------------------------===//
//
// This file implements regularization of LLVM module for SPIR-V.
//
//===----------------------------------------------------------------------===//
#include "SPIRVRegularizeLLVM.h"
#include "OCLUtil.h"
#include "SPIRVInternal.h"
#include "SPIRVMDWalker.h"
#include "libSPIRV/SPIRVDebug.h"
#include "llvm/ADT/StringExtras.h" // llvm::isDigit
#include "llvm/CodeGen/IntrinsicLowering.h"
#include "llvm/Demangle/Demangle.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Operator.h"
#include "llvm/Support/Debug.h"
#include "llvm/Transforms/Utils/LowerMemIntrinsics.h" // expandMemSetAsLoop()
#include <set>
#include <vector>
#define DEBUG_TYPE "spvregular"
using namespace llvm;
using namespace SPIRV;
using namespace OCLUtil;
namespace SPIRV {
static bool SPIRVDbgSaveRegularizedModule = false;
static std::string RegularizedModuleTmpFile = "regularized.bc";
char SPIRVRegularizeLLVMLegacy::ID = 0;
bool SPIRVRegularizeLLVMLegacy::runOnModule(Module &Module) {
return runRegularizeLLVM(Module);
}
std::string SPIRVRegularizeLLVMBase::lowerLLVMIntrinsicName(IntrinsicInst *II) {
Function *IntrinsicFunc = II->getCalledFunction();
assert(IntrinsicFunc && "Missing function");
std::string FuncName = IntrinsicFunc->getName().str();
std::replace(FuncName.begin(), FuncName.end(), '.', '_');
FuncName = "spirv." + FuncName;
return FuncName;
}
void SPIRVRegularizeLLVMBase::lowerIntrinsicToFunction(
IntrinsicInst *Intrinsic) {
// For @llvm.memset.* intrinsic cases with constant value and length arguments
// are emulated via "storing" a constant array to the destination. For other
// cases we wrap the intrinsic in @spirv.llvm_memset_* function and expand the
// intrinsic to a loop via expandMemSetAsLoop() from
// llvm/Transforms/Utils/LowerMemIntrinsics.h
if (auto *MSI = dyn_cast<MemSetInst>(Intrinsic))
if (isa<Constant>(MSI->getValue()) && isa<ConstantInt>(MSI->getLength()))
return; // To be handled in LLVMToSPIRV::transIntrinsicInst
std::string FuncName = lowerLLVMIntrinsicName(Intrinsic);
if (Intrinsic->isVolatile())
FuncName += ".volatile";
// Redirect @llvm.intrinsic.* call to @spirv.llvm_intrinsic_*
Function *F = M->getFunction(FuncName);
if (F) {
// This function is already linked in.
Intrinsic->setCalledFunction(F);
return;
}
// TODO copy arguments attributes: captures(none) writeonly.
FunctionCallee FC =
M->getOrInsertFunction(FuncName, Intrinsic->getFunctionType());
auto IntrinsicID = Intrinsic->getIntrinsicID();
Intrinsic->setCalledFunction(FC);
F = dyn_cast<Function>(FC.getCallee());
assert(F && "must be a function!");
switch (IntrinsicID) {
case Intrinsic::memset: {
auto *MSI = static_cast<MemSetInst *>(Intrinsic);
Argument *Dest = F->getArg(0);
Argument *Val = F->getArg(1);
Argument *Len = F->getArg(2);
Argument *IsVolatile = F->getArg(3);
Dest->setName("dest");
Val->setName("val");
Len->setName("len");
IsVolatile->setName("isvolatile");
IsVolatile->addAttr(Attribute::ImmArg);
BasicBlock *EntryBB = BasicBlock::Create(M->getContext(), "entry", F);
IRBuilder<> IRB(EntryBB);
auto *MemSet = IRB.CreateMemSet(Dest, Val, Len, MSI->getDestAlign(),
MSI->isVolatile());
IRB.CreateRetVoid();
expandMemSetAsLoop(cast<MemSetInst>(MemSet));
MemSet->eraseFromParent();
break;
}
case Intrinsic::bswap: {
BasicBlock *EntryBB = BasicBlock::Create(M->getContext(), "entry", F);
IRBuilder<> IRB(EntryBB);
auto *BSwap = IRB.CreateIntrinsic(Intrinsic::bswap, Intrinsic->getType(),
F->getArg(0));
IRB.CreateRet(BSwap);
IntrinsicLowering IL(M->getDataLayout());
IL.LowerIntrinsicCall(BSwap);
break;
}
default:
break; // do nothing
}
return;
}
void SPIRVRegularizeLLVMBase::lowerFunnelShift(IntrinsicInst *FSHIntrinsic) {
// Get a separate function - otherwise, we'd have to rework the CFG of the
// current one. Then simply replace the intrinsic uses with a call to the new
// function.
// Expected LLVM IR for the function: i* @spirv.llvm_fsh?_i* (i* %a, i* %b, i*
// %c)
FunctionType *FSHFuncTy = FSHIntrinsic->getFunctionType();
Type *FSHRetTy = FSHFuncTy->getReturnType();
const std::string FuncName = lowerLLVMIntrinsicName(FSHIntrinsic);
Function *FSHFunc =
getOrCreateFunction(M, FSHRetTy, FSHFuncTy->params(), FuncName);
if (!FSHFunc->empty()) {
FSHIntrinsic->setCalledFunction(FSHFunc);
return;
}
auto *RotateBB = BasicBlock::Create(M->getContext(), "rotate", FSHFunc);
IRBuilder<> Builder(RotateBB);
Type *Ty = FSHFunc->getReturnType();
// Build the actual funnel shift rotate logic.
// In the comments, "int" is used interchangeably with "vector of int
// elements".
FixedVectorType *VectorTy = dyn_cast<FixedVectorType>(Ty);
Type *IntTy = VectorTy ? VectorTy->getElementType() : Ty;
unsigned BitWidth = IntTy->getIntegerBitWidth();
ConstantInt *BitWidthConstant = Builder.getInt({BitWidth, BitWidth});
Value *BitWidthForInsts =
VectorTy ? Builder.CreateVectorSplat(VectorTy->getNumElements(),
BitWidthConstant)
: BitWidthConstant;
auto *RotateModVal =
Builder.CreateURem(/*Rotate*/ FSHFunc->getArg(2), BitWidthForInsts);
Value *FirstShift = nullptr, *SecShift = nullptr;
if (FSHIntrinsic->getIntrinsicID() == Intrinsic::fshr)
// Shift the less significant number right, the "rotate" number of bits
// will be 0-filled on the left as a result of this regular shift.
FirstShift = Builder.CreateLShr(FSHFunc->getArg(1), RotateModVal);
else
// Shift the more significant number left, the "rotate" number of bits
// will be 0-filled on the right as a result of this regular shift.
FirstShift = Builder.CreateShl(FSHFunc->getArg(0), RotateModVal);
// We want the "rotate" number of the more significant int's LSBs (MSBs) to
// occupy the leftmost (rightmost) "0 space" left by the previous operation.
// Therefore, subtract the "rotate" number from the integer bitsize...
auto *SubRotateVal = Builder.CreateSub(BitWidthForInsts, RotateModVal);
if (FSHIntrinsic->getIntrinsicID() == Intrinsic::fshr)
// ...and left-shift the more significant int by this number, zero-filling
// the LSBs.
SecShift = Builder.CreateShl(FSHFunc->getArg(0), SubRotateVal);
else
// ...and right-shift the less significant int by this number, zero-filling
// the MSBs.
SecShift = Builder.CreateLShr(FSHFunc->getArg(1), SubRotateVal);
// A simple binary addition of the shifted ints yields the final result.
auto *FunnelShiftRes = Builder.CreateOr(FirstShift, SecShift);
Builder.CreateRet(FunnelShiftRes);
FSHIntrinsic->setCalledFunction(FSHFunc);
}
void SPIRVRegularizeLLVMBase::buildUMulWithOverflowFunc(Function *UMulFunc) {
if (!UMulFunc->empty())
return;
BasicBlock *EntryBB = BasicBlock::Create(M->getContext(), "entry", UMulFunc);
IRBuilder<> Builder(EntryBB);
// Build the actual unsigned multiplication logic with the overflow
// indication.
auto *FirstArg = UMulFunc->getArg(0);
auto *SecondArg = UMulFunc->getArg(1);
// Do unsigned multiplication Mul = A * B.
// Then check if unsigned division Div = Mul / A is not equal to B.
// If so, then overflow has happened.
auto *Mul = Builder.CreateNUWMul(FirstArg, SecondArg);
auto *Div = Builder.CreateUDiv(Mul, FirstArg);
auto *Overflow = Builder.CreateICmpNE(FirstArg, Div);
// umul.with.overflow intrinsic return a structure, where the first element
// is the multiplication result, and the second is an overflow bit.
auto *StructTy = UMulFunc->getReturnType();
auto *Agg = Builder.CreateInsertValue(PoisonValue::get(StructTy), Mul, {0});
auto *Res = Builder.CreateInsertValue(Agg, Overflow, {1});
Builder.CreateRet(Res);
}
void SPIRVRegularizeLLVMBase::lowerUMulWithOverflow(
IntrinsicInst *UMulIntrinsic) {
// Get a separate function - otherwise, we'd have to rework the CFG of the
// current one. Then simply replace the intrinsic uses with a call to the new
// function.
FunctionType *UMulFuncTy = UMulIntrinsic->getFunctionType();
Type *FSHLRetTy = UMulFuncTy->getReturnType();
const std::string FuncName = lowerLLVMIntrinsicName(UMulIntrinsic);
Function *UMulFunc =
getOrCreateFunction(M, FSHLRetTy, UMulFuncTy->params(), FuncName);
buildUMulWithOverflowFunc(UMulFunc);
UMulIntrinsic->setCalledFunction(UMulFunc);
}
void SPIRVRegularizeLLVMBase::expandVEDWithSYCLTypeSRetArg(Function *F) {
auto Attrs = F->getAttributes();
StructType *SRetTy = cast<StructType>(Attrs.getParamStructRetType(0));
Attrs = Attrs.removeParamAttribute(F->getContext(), 0, Attribute::StructRet);
std::string Name = F->getName().str();
CallInst *OldCall = nullptr;
mutateFunction(
F,
[=, &OldCall](CallInst *CI, std::vector<Value *> &Args, Type *&RetTy) {
Args.erase(Args.begin());
RetTy = SRetTy->getElementType(0);
OldCall = CI;
return Name;
},
[=, &OldCall](CallInst *NewCI) {
IRBuilder<> Builder(OldCall);
Value *Target =
Builder.CreateStructGEP(SRetTy, OldCall->getOperand(0), 0);
return Builder.CreateStore(NewCI, Target);
},
nullptr, &Attrs, true);
}
void SPIRVRegularizeLLVMBase::expandVIDWithSYCLTypeByValComp(Function *F) {
auto Attrs = F->getAttributes();
auto *CompPtrTy = cast<StructType>(Attrs.getParamByValType(1));
Attrs = Attrs.removeParamAttribute(F->getContext(), 1, Attribute::ByVal);
std::string Name = F->getName().str();
mutateFunction(
F,
[=](CallInst *CI, std::vector<Value *> &Args) {
Type *HalfTy = CompPtrTy->getElementType(0);
IRBuilder<> Builder(CI);
auto *Target = Builder.CreateStructGEP(CompPtrTy, CI->getOperand(1), 0);
Args[1] = Builder.CreateLoad(HalfTy, Target);
return Name;
},
nullptr, &Attrs, true);
}
void SPIRVRegularizeLLVMBase::expandSYCLTypeUsing(Module *M) {
std::vector<Function *> ToExpandVEDWithSYCLTypeSRetArg;
std::vector<Function *> ToExpandVIDWithSYCLTypeByValComp;
for (auto &F : *M) {
if (F.getName().starts_with("_Z28__spirv_VectorExtractDynamic") &&
F.hasStructRetAttr()) {
auto *SRetTy = F.getParamStructRetType(0);
if (isSYCLHalfType(SRetTy) || isSYCLBfloat16Type(SRetTy))
ToExpandVEDWithSYCLTypeSRetArg.push_back(&F);
else
llvm_unreachable("The return type of the VectorExtractDynamic "
"instruction cannot be a structure other than SYCL "
"half.");
}
if (F.getName().starts_with("_Z27__spirv_VectorInsertDynamic") &&
F.getArg(1)->getType()->isPointerTy()) {
auto *ET = F.getParamByValType(1);
if (isSYCLHalfType(ET) || isSYCLBfloat16Type(ET))
ToExpandVIDWithSYCLTypeByValComp.push_back(&F);
else
llvm_unreachable("The component argument type of an "
"VectorInsertDynamic instruction can't be a "
"structure other than SYCL half.");
}
}
for (auto *F : ToExpandVEDWithSYCLTypeSRetArg)
expandVEDWithSYCLTypeSRetArg(F);
for (auto *F : ToExpandVIDWithSYCLTypeByValComp)
expandVIDWithSYCLTypeByValComp(F);
}
// In this function, we handle two conversion operations
// 1. fptoui.sat.iX.fY (X is not 8,16,32,64; Y is 32 or 64)
// 2. fptosi.sat.iX.fY (X is not 8,16,32,64; Y is 32 or 64)
// Such non-standard integer types cannot be handled in SPIR-V. Hence, they
// will be promoted to
// 1. fptoui.sat.i64.fY (Y is 32 or 64)
// 2. fptosi.sat.i64.fY (Y is 32 or 64)
// However, LLVM documentation requires the following rules to be obeyed.
// Rule 1: If the argument is any NaN, zero is returned.
// Rule 2: If the argument is smaller than the smallest representable
// (un)signed integer of the result type, the smallest representable
// (un)signed integer is returned.
// Rule 3: If the argument is larger than the largest representable (un)signed
// integer of the result type, the largest representable (un)signed integer is
// returned.
// Rule 4: Otherwise, the result of rounding the argument towards zero is
// returned.
// Rules 1 & 4 are preserved when promoting iX to i64. For preserving Rule 2
// and Rule 3, we saturate the result of the promoted instruction based on
// original integer type (iX)
// Example:
// Input:
// %0 = call i2 @llvm.fptosi.sat.i2.f32(float %input)
// %1 = sext i32 %0
// Output:
// %0 = call i32 @_Z17convert_long_satf(float %input)
// %1 = icmp sge i32 %0, 1 <Largest 2-bit signed integer>
// %2 = icmp sle i32 %0, -2 <Smallest 2-bit signed integer>
// %3 = select i1 %1, i32 1, i32 %0
// %4 = select i1 %2, i32 -2, i32 %3
// Replace uses of %1 in Input with %4 in Output
void SPIRVRegularizeLLVMBase::cleanupConversionToNonStdIntegers(Module *M) {
for (auto FI = M->begin(), FE = M->end(); FI != FE;) {
Function *F = &(*FI++);
std::vector<Instruction *> ToErase;
auto IID = F->getIntrinsicID();
if (IID != Intrinsic::fptosi_sat && IID != Intrinsic::fptoui_sat)
continue;
for (auto *I : F->users()) {
if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(I)) {
// TODO: Vector type not supported yet.
if (isa<VectorType>(II->getType()))
continue;
auto IID = II->getIntrinsicID();
auto IntBitWidth = II->getType()->getScalarSizeInBits();
if (IntBitWidth == 8 || IntBitWidth == 16 || IntBitWidth == 32 ||
IntBitWidth == 64)
continue;
if (IID == Intrinsic::fptosi_sat) {
// Identify sext (user of II). Make sure that's the only use of II.
auto *User = II->getUniqueUndroppableUser();
if (!User || !isa<SExtInst>(User))
continue;
auto *SExtI = dyn_cast<SExtInst>(User);
auto *NewIType = SExtI->getType();
IRBuilder<> IRB(II);
auto *NewII = IRB.CreateIntrinsic(
IID, {NewIType, II->getOperand(0)->getType()}, II->getOperand(0));
Constant *MaxVal = ConstantInt::get(
NewIType, APInt::getSignedMaxValue(IntBitWidth).getSExtValue());
Constant *MinVal = ConstantInt::get(
NewIType, APInt::getSignedMinValue(IntBitWidth).getSExtValue());
auto *GTMax = IRB.CreateICmp(CmpInst::ICMP_SGE, NewII, MaxVal);
auto *LTMin = IRB.CreateICmp(CmpInst::ICMP_SLE, NewII, MinVal);
auto *SatMax = IRB.CreateSelect(GTMax, MaxVal, NewII);
auto *SatMin = IRB.CreateSelect(LTMin, MinVal, SatMax);
SExtI->replaceAllUsesWith(SatMin);
ToErase.push_back(SExtI);
ToErase.push_back(II);
}
if (IID == Intrinsic::fptoui_sat) {
// Identify zext (user of II). Make sure that's the only use of II.
auto *User = II->getUniqueUndroppableUser();
if (!User || !isa<ZExtInst>(User))
continue;
auto *ZExtI = dyn_cast<ZExtInst>(User);
auto *NewIType = ZExtI->getType();
IRBuilder<> IRB(II);
auto *NewII = IRB.CreateIntrinsic(
IID, {NewIType, II->getOperand(0)->getType()}, II->getOperand(0));
Constant *MaxVal = ConstantInt::get(
NewIType, APInt::getMaxValue(IntBitWidth).getZExtValue());
auto *GTMax = IRB.CreateICmp(CmpInst::ICMP_UGE, NewII, MaxVal);
auto *SatMax = IRB.CreateSelect(GTMax, MaxVal, NewII);
ZExtI->replaceAllUsesWith(SatMax);
ToErase.push_back(ZExtI);
ToErase.push_back(II);
}
}
}
for (Instruction *V : ToErase) {
assert(V->user_empty());
V->dropAllReferences();
V->eraseFromParent();
}
}
}
bool SPIRVRegularizeLLVMBase::runRegularizeLLVM(Module &Module) {
M = &Module;
Ctx = &M->getContext();
LLVM_DEBUG(dbgs() << "Enter SPIRVRegularizeLLVM:\n");
regularize();
LLVM_DEBUG(dbgs() << "After SPIRVRegularizeLLVM:\n" << *M);
verifyRegularizationPass(*M, "SPIRVRegularizeLLVM");
return true;
}
namespace {
void regularizeWithOverflowInstrinsics(StringRef MangledName, CallInst *Call,
Module *M,
std::vector<Instruction *> &ToErase) {
IRBuilder Builder(Call);
Function *Builtin = Call->getModule()->getFunction(MangledName);
AllocaInst *A;
StructType *StructBuiltinTy;
if (Builtin) {
StructBuiltinTy = cast<StructType>(Builtin->getParamStructRetType(0));
{
IRBuilderBase::InsertPointGuard Guard(Builder);
Builder.SetInsertPointPastAllocas(Call->getParent()->getParent());
A = Builder.CreateAlloca(StructBuiltinTy);
}
CallInst *C = Builder.CreateCall(
Builtin, {A, Call->getArgOperand(0), Call->getArgOperand(1)});
auto SretAttr = Attribute::get(
Builder.getContext(), Attribute::AttrKind::StructRet, StructBuiltinTy);
C->addParamAttr(0, SretAttr);
} else {
StructBuiltinTy = StructType::create(
Call->getContext(),
{Call->getArgOperand(0)->getType(), Call->getArgOperand(1)->getType()});
{
IRBuilderBase::InsertPointGuard Guard(Builder);
Builder.SetInsertPointPastAllocas(Call->getParent()->getParent());
A = Builder.CreateAlloca(StructBuiltinTy);
}
FunctionType *FT =
FunctionType::get(Builder.getVoidTy(),
{A->getType(), Call->getArgOperand(0)->getType(),
Call->getArgOperand(1)->getType()},
false);
Builtin =
Function::Create(FT, GlobalValue::ExternalLinkage, MangledName, M);
Builtin->setCallingConv(CallingConv::SPIR_FUNC);
Builtin->addFnAttr(Attribute::NoUnwind);
auto SretAttr = Attribute::get(
Builder.getContext(), Attribute::AttrKind::StructRet, StructBuiltinTy);
Builtin->addParamAttr(0, SretAttr);
CallInst *C = Builder.CreateCall(
Builtin, {A, Call->getArgOperand(0), Call->getArgOperand(1)});
C->addParamAttr(0, SretAttr);
}
Type *RetTy = Call->getArgOperand(0)->getType();
Constant *ConstZero = ConstantInt::get(RetTy, 0);
Value *L = Builder.CreateLoad(StructBuiltinTy, A);
Value *V0 = Builder.CreateExtractValue(L, {0});
Value *V1 = Builder.CreateExtractValue(L, {1});
Value *V2 = Builder.CreateICmpNE(V1, ConstZero);
Type *StructI32I1Ty =
StructType::create(Call->getContext(), {RetTy, V2->getType()});
Value *Undef = PoisonValue::get(StructI32I1Ty);
Value *V3 = Builder.CreateInsertValue(Undef, V0, {0});
Value *V4 = Builder.CreateInsertValue(V3, V2, {1});
SmallVector<User *> Users(Call->users());
for (User *U : Users) {
U->replaceUsesOfWith(Call, V4);
}
ToErase.push_back(Call);
}
// CacheControls(Load/Store)INTEL decorations can be represented as metadata
// placed on memory accessing instruction with the following form:
// !spirv.DecorationCacheControlINTEL !X
// !X = !{i32 %decoration_kind%, i32 %level%, i32 %control%,
// i32 %operand of the instruction to decorate%}
// This function creates a dummy GEP accessing pointer operand of the
// instruction and creates !spirv.Decorations metadata attached to it.
void prepareCacheControlsTranslation(Metadata *MD, Instruction *Inst) {
if (!Inst->mayReadOrWriteMemory())
return;
auto *ArgDecoMD = dyn_cast<MDNode>(MD);
assert(ArgDecoMD && "Decoration list must be a metadata node");
std::vector<Instruction *> CreatedGeps;
for (unsigned I = 0, E = ArgDecoMD->getNumOperands(); I != E; ++I) {
auto *DecoMD = dyn_cast<MDNode>(ArgDecoMD->getOperand(I));
if (!DecoMD) {
assert(false && "Decoration does not name metadata");
return;
}
constexpr size_t CacheControlsNumOps = 4;
if (DecoMD->getNumOperands() != CacheControlsNumOps) {
assert(false &&
"Cache controls metadata on instruction must have 4 operands");
return;
}
auto *const KindMD = cast<ConstantAsMetadata>(DecoMD->getOperand(0));
auto *const LevelMD = cast<ConstantAsMetadata>(DecoMD->getOperand(1));
auto *const ControlMD = cast<ConstantAsMetadata>(DecoMD->getOperand(2));
const size_t TargetArgNo =
mdconst::dyn_extract<ConstantInt>(DecoMD->getOperand(3))
->getZExtValue();
Value *PtrInstOp = Inst->getOperand(TargetArgNo);
if (!PtrInstOp->getType()->isPointerTy()) {
assert(false && "Cache controls must decorate a pointer");
return;
}
// Create dummy GEP for SSA copy of the pointer operand. Lets do our best
// to guess pointee type here, but if we won't - just pointer is also fine,
// if necessary TypeScavenger will adjust types and create bitcasts. If
// memory instruction operand is already created zero GEP - create nothing
// and use the old GEP.
SmallVector<Metadata *, 4> MDs;
std::vector<Metadata *> OPs = {KindMD, LevelMD, ControlMD};
if (auto *const GEP = dyn_cast<GetElementPtrInst>(PtrInstOp)) {
if (GEP->hasAllZeroIndices() &&
(std::find(CreatedGeps.begin(), CreatedGeps.end(), GEP) !=
std::end(CreatedGeps))) {
MDs.push_back(MDNode::get(Inst->getContext(), OPs));
// If the existing GEP has SPIRV_MD_DECORATIONS metadata - copy it
if (auto *OldMD = GEP->getMetadata(SPIRV_MD_DECORATIONS))
for (unsigned I = 0, E = OldMD->getNumOperands(); I != E; ++I)
if (auto *DecoMD = dyn_cast<MDNode>(OldMD->getOperand(I)))
MDs.push_back(DecoMD);
MDNode *MDList = MDNode::get(Inst->getContext(), MDs);
GEP->setMetadata(SPIRV_MD_DECORATIONS, MDList);
return;
}
}
IRBuilder Builder(Inst);
Type *GEPTy = Builder.getInt8Ty();
if (auto *LI = dyn_cast<LoadInst>(Inst))
GEPTy = LI->getType();
else if (auto *SI = dyn_cast<StoreInst>(Inst))
GEPTy = SI->getValueOperand()->getType();
auto *GEP =
cast<Instruction>(Builder.CreateConstGEP1_32(GEPTy, PtrInstOp, 0));
CreatedGeps.push_back(GEP);
Inst->setOperand(TargetArgNo, GEP);
MDs.push_back(MDNode::get(Inst->getContext(), OPs));
MDNode *MDList = MDNode::get(Inst->getContext(), MDs);
GEP->setMetadata(SPIRV_MD_DECORATIONS, MDList);
}
}
} // namespace
/// Remove entities not representable by SPIR-V
bool SPIRVRegularizeLLVMBase::regularize() {
eraseUselessFunctions(M);
addKernelEntryPoint(M);
expandSYCLTypeUsing(M);
cleanupConversionToNonStdIntegers(M);
for (auto I = M->begin(), E = M->end(); I != E;) {
Function *F = &(*I++);
if (F->isDeclaration() && F->use_empty()) {
F->eraseFromParent();
continue;
}
// TODO: query intrinsic calls from their declarations
std::vector<Instruction *> ToErase;
for (BasicBlock &BB : *F) {
for (Instruction &II : BB) {
if (auto *MD = II.getMetadata(SPIRV_MD_INTEL_CACHE_DECORATIONS))
prepareCacheControlsTranslation(MD, &II);
if (auto *Call = dyn_cast<CallInst>(&II)) {
Call->setTailCall(false);
Function *CF = Call->getCalledFunction();
if (CF && CF->isIntrinsic()) {
removeFnAttr(Call, Attribute::NoUnwind);
auto *II = cast<IntrinsicInst>(Call);
if (II->getIntrinsicID() == Intrinsic::memset ||
II->getIntrinsicID() == Intrinsic::bswap)
lowerIntrinsicToFunction(II);
else if (II->getIntrinsicID() == Intrinsic::fshl ||
II->getIntrinsicID() == Intrinsic::fshr)
lowerFunnelShift(II);
else if (II->getIntrinsicID() == Intrinsic::umul_with_overflow)
lowerUMulWithOverflow(II);
else if (II->getIntrinsicID() == Intrinsic::uadd_with_overflow) {
BuiltinFuncMangleInfo Info;
std::string MangledName =
mangleBuiltin("__spirv_IAddCarry",
{Call->getArgOperand(0)->getType(),
Call->getArgOperand(1)->getType()},
&Info);
regularizeWithOverflowInstrinsics(MangledName, Call, M, ToErase);
} else if (II->getIntrinsicID() == Intrinsic::usub_with_overflow) {
BuiltinFuncMangleInfo Info;
std::string MangledName =
mangleBuiltin("__spirv_ISubBorrow",
{Call->getArgOperand(0)->getType(),
Call->getArgOperand(1)->getType()},
&Info);
regularizeWithOverflowInstrinsics(MangledName, Call, M, ToErase);
}
}
}
if (II.isLogicalShift()) {
// Translator treats i1 as boolean, but bit instructions take
// a scalar/vector integers, so we have to extend such arguments.
// shl i1 %a %b and lshr i1 %a %b are now converted on:
// %0 = select i1 %a, i32 1, i32 0
// %1 = select i1 %b, i32 1, i32 0
// %2 = lshr i32 %0, %1
// if any other instruction other than zext was dependant:
// %3 = icmp ne i32 %2, 0
// which converts it back to i1 and replace original result with %3
// to dependant instructions.
if (II.getOperand(0)->getType()->isIntOrIntVectorTy(1)) {
IRBuilder<> Builder(&II);
Value *CmpNEInst = nullptr;
Constant *ConstZero = ConstantInt::get(Builder.getInt32Ty(), 0);
Constant *ConstOne = ConstantInt::get(Builder.getInt32Ty(), 1);
if (auto *VecTy =
dyn_cast<FixedVectorType>(II.getOperand(0)->getType())) {
const unsigned NumElements = VecTy->getNumElements();
ConstZero = ConstantVector::getSplat(
ElementCount::getFixed(NumElements), ConstZero);
ConstOne = ConstantVector::getSplat(
ElementCount::getFixed(NumElements), ConstOne);
}
Value *ExtendedBase =
Builder.CreateSelect(II.getOperand(0), ConstOne, ConstZero);
Value *ExtendedShift =
Builder.CreateSelect(II.getOperand(1), ConstOne, ConstZero);
Value *ExtendedShiftedVal =
Builder.CreateLShr(ExtendedBase, ExtendedShift);
SmallVector<User *, 8> Users(II.users());
for (User *U : Users) {
if (auto *UI = dyn_cast<Instruction>(U)) {
if (UI->getOpcode() == Instruction::ZExt) {
UI->dropAllReferences();
UI->replaceAllUsesWith(ExtendedShiftedVal);
ToErase.push_back(UI);
continue;
}
}
if (!CmpNEInst) {
CmpNEInst = Builder.CreateICmpNE(ExtendedShiftedVal, ConstZero);
}
U->replaceUsesOfWith(&II, CmpNEInst);
}
ToErase.push_back(&II);
}
}
// Remove optimization info not supported by SPIRV
if (auto *BO = dyn_cast<BinaryOperator>(&II)) {
if (isa<PossiblyExactOperator>(BO) && BO->isExact())
BO->setIsExact(false);
}
// FIXME: This is not valid handling for freeze instruction
if (auto *FI = dyn_cast<FreezeInst>(&II)) {
auto *V = FI->getOperand(0);
if (isa<UndefValue>(V))
V = Constant::getNullValue(V->getType());
FI->replaceAllUsesWith(V);
FI->dropAllReferences();
ToErase.push_back(FI);
}
// Remove metadata not supported by SPIRV
static const char *MDs[] = {
"tbaa",
"range",
};
for (auto &MDName : MDs) {
if (II.getMetadata(MDName)) {
II.setMetadata(MDName, nullptr);
}
}
if (auto *Cmpxchg = dyn_cast<AtomicCmpXchgInst>(&II)) {
// Transform:
// %1 = cmpxchg i32* %ptr, i32 %comparator, i32 %0 seq_cst acquire
// To:
// %cmpxchg.res = call spir_func
// i32 @_Z29__spirv_AtomicCompareExchangePiiiiii(
// i32* %ptr, i32 1, i32 16, i32 2, i32 %0, i32 %comparator)
// %cmpxchg.success = icmp eq i32 %cmpxchg.res, %comparator
// %1 = insertvalue { i32, i1 } undef, i32 %cmpxchg.res, 0
// %2 = insertvalue { i32, i1 } %1, i1 %cmpxchg.success, 1
// cmpxchg LLVM instruction returns a pair {i32, i1}: the original
// value and a flag indicating success (true) or failure (false).
// OpAtomicCompareExchange SPIR-V instruction returns only the
// original value. To keep the return type({i32, i1}) we construct
// a composite. The first element of the composite holds result of
// OpAtomicCompareExchange, i.e. the original value. The second
// element holds result of comparison of the returned value and the
// comparator, which matches with semantics of the flag returned by
// cmpxchg.
Value *Ptr = Cmpxchg->getPointerOperand();
spv::Scope S =
toSPIRVScope(Cmpxchg->getContext(), Cmpxchg->getSyncScopeID());
Value *MemoryScope = getInt32(M, S);
auto SuccessOrder = static_cast<OCLMemOrderKind>(
llvm::toCABI(Cmpxchg->getSuccessOrdering()));
auto FailureOrder = static_cast<OCLMemOrderKind>(
llvm::toCABI(Cmpxchg->getFailureOrdering()));
Value *EqualSem = getInt32(M, OCLMemOrderMap::map(SuccessOrder));
Value *UnequalSem = getInt32(M, OCLMemOrderMap::map(FailureOrder));
Value *Val = Cmpxchg->getNewValOperand();
Value *Comparator = Cmpxchg->getCompareOperand();
Type *MemType = Cmpxchg->getCompareOperand()->getType();
llvm::Value *Args[] = {Ptr, MemoryScope, EqualSem,
UnequalSem, Val, Comparator};
auto *Res =
addCallInstSPIRV(M, "__spirv_AtomicCompareExchange", MemType,
Args, nullptr, {MemType}, &II, "cmpxchg.res");
IRBuilder<> Builder(Cmpxchg);
auto *Cmp = Builder.CreateICmpEQ(Res, Comparator, "cmpxchg.success");
auto *V1 = Builder.CreateInsertValue(
PoisonValue::get(Cmpxchg->getType()), Res, 0);
auto *V2 = Builder.CreateInsertValue(V1, Cmp, 1, Cmpxchg->getName());
Cmpxchg->replaceAllUsesWith(V2);
ToErase.push_back(Cmpxchg);
}
}
}
for (Instruction *V : ToErase) {
assert(V->user_empty());
V->eraseFromParent();
}
}
if (SPIRVDbgSaveRegularizedModule)
saveLLVMModule(M, RegularizedModuleTmpFile);
return true;
}
void SPIRVRegularizeLLVMBase::addKernelEntryPoint(Module *M) {
std::vector<Function *> Work;
// Get a list of all functions that have SPIR kernel calling conv
for (auto &F : *M) {
if (F.getCallingConv() == CallingConv::SPIR_KERNEL)
Work.push_back(&F);
}
for (auto &F : Work) {
// for declarations just make them into SPIR functions.
F->setCallingConv(CallingConv::SPIR_FUNC);
if (F->isDeclaration())
continue;
// Otherwise add a wrapper around the function to act as an entry point.
FunctionType *FType = F->getFunctionType();
std::string WrapName =
kSPIRVName::EntrypointPrefix + static_cast<std::string>(F->getName());
Function *WrapFn =
getOrCreateFunction(M, F->getReturnType(), FType->params(), WrapName);
auto *CallBB = BasicBlock::Create(M->getContext(), "", WrapFn);
IRBuilder<> Builder(CallBB);
Function::arg_iterator DestI = WrapFn->arg_begin();
for (const Argument &I : F->args()) {
DestI->setName(I.getName());
DestI++;
}
SmallVector<Value *, 1> Args;
for (Argument &I : WrapFn->args()) {
Args.emplace_back(&I);
}
auto *CI = CallInst::Create(F, ArrayRef<Value *>(Args), "", CallBB);
CI->setCallingConv(F->getCallingConv());
CI->setAttributes(F->getAttributes());
// copy over all the metadata (should it be removed from F?)
SmallVector<std::pair<unsigned, MDNode *>> MDs;
F->getAllMetadata(MDs);
WrapFn->setAttributes(F->getAttributes());
for (auto MD = MDs.begin(), End = MDs.end(); MD != End; ++MD) {
WrapFn->addMetadata(MD->first, *MD->second);
}
WrapFn->setCallingConv(CallingConv::SPIR_KERNEL);
WrapFn->setLinkage(llvm::GlobalValue::InternalLinkage);
Builder.CreateRet(F->getReturnType()->isVoidTy() ? nullptr : CI);
// Have to find the spir-v metadata for execution mode and transfer it to
// the wrapper.
if (auto NMD = SPIRVMDWalker(*M).getNamedMD(kSPIRVMD::ExecutionMode)) {
while (!NMD.atEnd()) {
Function *MDF = nullptr;
auto N = NMD.nextOp(); /* execution mode MDNode */
N.get(MDF);
if (MDF == F)
N.M->replaceOperandWith(0, ValueAsMetadata::get(WrapFn));
}
}
}
}
} // namespace SPIRV
INITIALIZE_PASS(SPIRVRegularizeLLVMLegacy, "spvregular",
"Regularize LLVM for SPIR-V", false, false)
ModulePass *llvm::createSPIRVRegularizeLLVMLegacy() {
return new SPIRVRegularizeLLVMLegacy();
}
@@ -0,0 +1,148 @@
//=- SPIRVRegularizeLLVM.h - LLVM Module regularization pass -*- C++ -*-=//
//
// The LLVM/SPIR-V Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2022 The Khronos Group Inc.
//
// 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 Khronos Group, 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.
//
//===----------------------------------------------------------------------===//
#ifndef SPIRV_SPIRVREGULARIZELLVM_H
#define SPIRV_SPIRVREGULARIZELLVM_H
#include "SPIRVInternal.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Pass.h"
namespace SPIRV {
class SPIRVRegularizeLLVMBase {
public:
SPIRVRegularizeLLVMBase() : M(nullptr), Ctx(nullptr) {}
bool runRegularizeLLVM(llvm::Module &M);
// Lower functions
bool regularize();
// SPIR-V disallows functions being entrypoints and called
// LLVM doesn't. This adds a wrapper around the entry point
// that later SPIR-V writer renames.
void addKernelEntryPoint(llvm::Module *M);
/// Some LLVM intrinsics that have no SPIR-V counterpart may be wrapped in
/// @spirv.llvm_intrinsic_* function. During reverse translation from SPIR-V
/// to LLVM IR we can detect this @spirv.llvm_intrinsic_* function and
/// replace it with @llvm.intrinsic.* back.
void lowerIntrinsicToFunction(llvm::IntrinsicInst *Intrinsic);
/// No SPIR-V counterpart for @llvm.fshl.*(@llvm.fshr.*) intrinsic. It will be
/// lowered to a newly generated @spirv.llvm_fshl_*(@spirv.llvm_fshr_*)
/// function.
///
/// Conceptually, FSHL (FSHR):
/// 1. concatenates the ints, the first one being the more significant;
/// 2. performs a left (right) shift-rotate on the resulting doubled-sized
/// int;
/// 3. returns the most (least) significant bits of the shift-rotate result,
/// the number of bits being equal to the size of the original integers.
/// If FSHL (FSHR) operates on a vector type instead, the same operations are
/// performed for each set of corresponding vector elements.
///
/// The actual implementation algorithm will be slightly different for
/// simplification purposes.
void lowerFunnelShift(llvm::IntrinsicInst *FSHIntrinsic);
void lowerUMulWithOverflow(llvm::IntrinsicInst *UMulIntrinsic);
void buildUMulWithOverflowFunc(llvm::Function *UMulFunc);
// For some cases Clang emits VectorExtractDynamic as:
// void @_Z28__spirv_VectorExtractDynamic(<Ty>* sret(<Ty>), jointMatrix, idx);
// Instead of:
// <Ty> @_Z28__spirv_VectorExtractDynamic(JointMatrix, Idx);
// And VectorInsertDynamic as:
// @_Z27__spirv_VectorInsertDynamic(jointMatrix, <Ty>* byval(<Ty>), idx);
// Instead of:
// @_Z27__spirv_VectorInsertDynamic(jointMatrix, <Ty>, idx)
// Need to add additional GEP, store and load instructions and mutate called
// function to avoid translation failures
void expandSYCLTypeUsing(llvm::Module *M);
void expandVEDWithSYCLTypeSRetArg(llvm::Function *F);
void expandVIDWithSYCLTypeByValComp(llvm::Function *F);
// It is possible that incoming LLVM IR conversion instructions convert
// floating point to non-standard integer types. Such types are not supported
// in SPIR-V. This function cleans up such code and removes occurence of
// non-standard integer types.
void cleanupConversionToNonStdIntegers(llvm::Module *M);
// According to the specification, the operands of a shift instruction must be
// a scalar/vector of integer. When LLVM-IR contains a shift instruction with
// i1 operands, they are treated as a bool. We need to extend them to i32 to
// comply with the specification. For example: "%shift = lshr i1 0, 1";
// The bit instruction should be changed to the extended version
// "%shift = lshr i32 0, 1" so the args are treated as int operands.
Value *extendBitInstBoolArg(llvm::Instruction *OldInst);
static std::string lowerLLVMIntrinsicName(llvm::IntrinsicInst *II);
static char ID;
private:
llvm::Module *M;
llvm::LLVMContext *Ctx;
};
class SPIRVRegularizeLLVMPass
: public llvm::PassInfoMixin<SPIRVRegularizeLLVMPass>,
public SPIRVRegularizeLLVMBase {
public:
llvm::PreservedAnalyses run(llvm::Module &M,
llvm::ModuleAnalysisManager &MAM) {
return runRegularizeLLVM(M) ? llvm::PreservedAnalyses::none()
: llvm::PreservedAnalyses::all();
}
static bool isRequired() { return true; }
};
class SPIRVRegularizeLLVMLegacy : public llvm::ModulePass,
public SPIRVRegularizeLLVMBase {
public:
SPIRVRegularizeLLVMLegacy() : ModulePass(ID) {
initializeSPIRVRegularizeLLVMLegacyPass(*PassRegistry::getPassRegistry());
}
bool runOnModule(llvm::Module &M) override;
static char ID;
};
} // namespace SPIRV
#endif // SPIRV_SPIRVREGULARIZELLVM_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,228 @@
//===- SPIRVToLLVMDbgTran.h - Converts SPIR-V DebugInfo to LLVM -*- C++ -*-===//
//
// The LLVM/SPIR-V Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2018 Intel Corporation. All rights reserved.
//
// 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 Intel Corporation, 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.
//
//===----------------------------------------------------------------------===//
//
// This file implements translation of debug info from SPIR-V to LLVM metadata
//
//===----------------------------------------------------------------------===//
#ifndef SPIRVTOLLVMDBGTRAN_H
#define SPIRVTOLLVMDBGTRAN_H
#include "SPIRVInstruction.h"
#include "llvm/IR/DIBuilder.h"
#include "llvm/IR/DebugLoc.h"
#include <unordered_map>
namespace llvm {
class Module;
class Value;
class Instruction;
class Type;
} // namespace llvm
using namespace llvm;
namespace SPIRV {
class SPIRVToLLVM;
class SPIRVEntry;
class SPIRVFunction;
class SPIRVValue;
class SPIRVToLLVMDbgTran {
public:
typedef std::vector<SPIRVWord> SPIRVWordVec;
SPIRVToLLVMDbgTran(SPIRVModule *TBM, Module *TM, SPIRVToLLVM *Reader);
void addDbgInfoVersion();
void transDbgInfo(const SPIRVValue *SV, Value *V);
template <typename T = MDNode>
T *transDebugInst(const SPIRVExtInst *DebugInst) {
assert((DebugInst->getExtSetKind() == SPIRVEIS_Debug ||
DebugInst->getExtSetKind() == SPIRVEIS_OpenCL_DebugInfo_100 ||
DebugInst->getExtSetKind() ==
SPIRVEIS_NonSemantic_Shader_DebugInfo_100 ||
DebugInst->getExtSetKind() ==
SPIRVEIS_NonSemantic_Shader_DebugInfo_200) &&
"Unexpected extended instruction set");
auto It = DebugInstCache.find(DebugInst);
if (It != DebugInstCache.end())
return static_cast<T *>(It->second);
MDNode *Res = transDebugInstImpl(DebugInst);
DebugInstCache[DebugInst] = Res;
return static_cast<T *>(Res);
}
DbgInstPtr transDebugIntrinsic(const SPIRVExtInst *DebugInst, BasicBlock *BB);
void finalize();
llvm::DebugLoc transDebugScope(const SPIRVInstruction *Inst);
private:
DIFile *getFile(const SPIRVId SourceId);
DIFile *
getDIFile(const std::string &FileName,
std::optional<DIFile::ChecksumInfo<StringRef>> CS = std::nullopt,
std::optional<StringRef> Source = std::nullopt);
DIFile *getDIFile(const SPIRVEntry *E);
unsigned getLineNo(const SPIRVEntry *E);
MDNode *transDebugInstImpl(const SPIRVExtInst *DebugInst);
DIType *transNonNullDebugType(const SPIRVExtInst *DebugInst);
llvm::DebugLoc transDebugLocation(const SPIRVExtInst *DebugInst);
MDNode *transDebugInlined(const SPIRVExtInst *Inst);
MDNode *transDebugInlinedNonSemanticShader200(const SPIRVExtInst *Inst);
void appendToSourceLangLiteral(DICompileUnit *CompileUnit,
SPIRVWord SourceLang);
DICompileUnit *transCompilationUnit(const SPIRVExtInst *DebugInst,
const std::string CompilerVersion = "",
const std::string Flags = "");
DIBasicType *transTypeBasic(const SPIRVExtInst *DebugInst);
DIDerivedType *transTypeQualifier(const SPIRVExtInst *DebugInst);
DIType *transTypePointer(const SPIRVExtInst *DebugInst);
DICompositeType *transTypeArray(const SPIRVExtInst *DebugInst);
DICompositeType *transTypeArrayOpenCL(const SPIRVExtInst *DebugInst);
DICompositeType *transTypeArrayNonSemantic(const SPIRVExtInst *DebugInst);
DICompositeType *transTypeArrayDynamic(const SPIRVExtInst *DebugInst);
DICompositeType *transTypeVector(const SPIRVExtInst *DebugInst);
DICompositeType *transTypeComposite(const SPIRVExtInst *DebugInst);
DISubrange *transTypeSubrange(const SPIRVExtInst *DebugInst);
DIStringType *transTypeString(const SPIRVExtInst *DebugInst);
DINode *transTypeMember(const SPIRVExtInst *DebugInst,
const SPIRVExtInst *ParentInst = nullptr,
DIScope *Scope = nullptr);
DINode *transTypeMemberOpenCL(const SPIRVExtInst *DebugInst);
DINode *transTypeMemberNonSemantic(const SPIRVExtInst *DebugInst,
const SPIRVExtInst *ParentInst,
DIScope *Scope);
DINode *transTypeEnum(const SPIRVExtInst *DebugInst);
DINode *transTypeTemplateParameter(const SPIRVExtInst *DebugInst);
DINode *transTypeTemplateTemplateParameter(const SPIRVExtInst *DebugInst);
DINode *transTypeTemplateParameterPack(const SPIRVExtInst *DebugInst);
MDNode *transTypeTemplate(const SPIRVExtInst *DebugInst);
DINode *transTypeFunction(const SPIRVExtInst *DebugInst);
DINode *transTypePtrToMember(const SPIRVExtInst *DebugInst);
DINode *transLexicalBlock(const SPIRVExtInst *DebugInst);
DINode *transLexicalBlockDiscriminator(const SPIRVExtInst *DebugInst);
DINode *transFunction(const SPIRVExtInst *DebugInst,
bool IsMainSubprogram = false);
DINode *transFunctionDefinition(const SPIRVExtInst *DebugInst);
void transFunctionBody(DISubprogram *DIS, SPIRVId FuncId);
DINode *transFunctionDecl(const SPIRVExtInst *DebugInst);
MDNode *transEntryPoint(const SPIRVExtInst *DebugInst);
MDNode *transGlobalVariable(const SPIRVExtInst *DebugInst);
DINode *transLocalVariable(const SPIRVExtInst *DebugInst);
DINode *transTypedef(const SPIRVExtInst *DebugInst);
DINode *transTypeInheritance(const SPIRVExtInst *DebugInst,
DIType *ChildClass = nullptr);
DINode *transImportedEntry(const SPIRVExtInst *DebugInst);
DINode *transModule(const SPIRVExtInst *DebugInst);
MDNode *transExpression(const SPIRVExtInst *DebugInst);
SPIRVModule *BM;
Module *M;
std::unordered_map<SPIRVId, std::unique_ptr<DIBuilder>> BuilderMap;
SPIRVToLLVM *SPIRVReader;
bool Enable;
std::unordered_map<std::string, DIFile *> FileMap;
std::unordered_map<SPIRVId, DISubprogram *> FuncMap;
std::unordered_map<const SPIRVExtInst *, MDNode *> DebugInstCache;
struct SplitFileName {
SplitFileName(const std::string &FileName);
std::string BaseName;
std::string Path;
};
DIScope *getScope(const SPIRVEntry *ScopeInst);
SPIRVExtInst *getDbgInst(const SPIRVId Id);
DIBuilder &getDIBuilder(const SPIRVExtInst *DebugInst);
template <SPIRVWord OpCode> SPIRVExtInst *getDbgInst(const SPIRVId Id) {
if (SPIRVExtInst *DI = getDbgInst(Id)) {
if (DI->getExtOp() == OpCode) {
return DI;
}
}
return nullptr;
}
const std::string &getString(const SPIRVId Id);
const std::string getStringSourceContinued(const SPIRVId Id,
SPIRVExtInst *DebugInst);
SPIRVWord getConstantValueOrLiteral(const std::vector<SPIRVWord> &,
const SPIRVWord,
const SPIRVExtInstSetKind);
std::string findModuleProducer();
std::optional<DIFile::ChecksumInfo<StringRef>> ParseChecksum(StringRef Text);
// BuildIdentifier and StoragePath must both be set or both unset.
// If StoragePath is empty both variables are unset and not valid.
uint64_t BuildIdentifier{0};
std::string StoragePath{};
void setBuildIdentifierAndStoragePath();
};
} // namespace SPIRV
#endif // SPIRVTOLLVMDBGTRAN_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,487 @@
//===- SPIRVToOCL.h - Converts SPIR-V to LLVM ------------------*- C++ -*-===//
//
// The LLVM/SPIR-V Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
//
// 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 Advanced Micro Devices, Inc., 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.
//
//===----------------------------------------------------------------------===//
/// \file
///
/// This file contains declaration of SPIRVToOCL class which implements
/// common transform of SPIR-V builtins to OCL builtins.
///
//===----------------------------------------------------------------------===//
#ifndef SPIRVTOOCL_H
#define SPIRVTOOCL_H
#include "OCLUtil.h"
#include "SPIRVBuiltinHelper.h"
#include "SPIRVInternal.h"
#include "llvm/IR/InstVisitor.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Pass.h"
#include <string>
namespace SPIRV {
class SPIRVToOCLBase : public InstVisitor<SPIRVToOCLBase>,
protected BuiltinCallHelper {
public:
SPIRVToOCLBase()
: BuiltinCallHelper(ManglingRules::OpenCL, translateOpaqueType),
M(nullptr), Ctx(nullptr) {}
virtual ~SPIRVToOCLBase() {}
virtual bool runSPIRVToOCL(Module &M) = 0;
void visitCallInst(CallInst &CI);
// SPIR-V reader should translate vector casts into OCL built-ins because
// such conversions are not defined neither by OpenCL C/C++ nor
// by SPIR 1.2/2.0 standards. So, it is safer to convert such casts into
// appropriate calls to conversion built-ins defined by the standards.
void visitCastInst(CastInst &CI);
/// Transform __spirv_ImageQuerySize[Lod] into vector of the same length
/// containing {[get_image_width | get_image_dim], get_image_array_size}
/// for all images except image1d_t which is always converted into
/// get_image_width returning scalar result.
void visitCallSPIRVImageQuerySize(CallInst *CI);
/// Transform __spirv_(NonUniform)Group* to {work_group|sub_group}_*.
///
/// Special handling of work_group_broadcast.
/// __spirv_GroupBroadcast(a, vec3(x, y, z))
/// =>
/// work_group_broadcast(a, x, y, z)
///
/// Special handling of sub_group_all, sub_group_any,
/// sub_group_non_uniform_all, sub_group_non_uniform_any, sub_group_ballot,
/// sub_group_clustered_logical_[and/or/xor].
/// retTy func(i1 arg)
/// =>
/// retTy func(i32 arg)
///
/// Special handling of sub_group_all, sub_group_any,
/// sub_group_non_uniform_all,
/// sub_group_non_uniform_any, sub_group_non_uniform_all_equal.
/// i1 func
/// =>
/// i32 func
void visitCallSPIRVGroupBuiltin(CallInst *CI, Op OC);
/// Transform __spirv_{PipeOpName} to OCL pipe builtin functions.
void visitCallSPIRVPipeBuiltin(CallInst *CI, Op OC);
/// Transform __spirv_OpOpSubgroupImageMediaBlockReadINTEL =>
/// intel_sub_group_media_block_read
/// __spirv_OpSubgroupImageMediaBlockWriteINTEL =>
/// intel_sub_group_media_block_write
void visitCallSPIRVImageMediaBlockBuiltin(CallInst *CI, Op OC);
/// Transform __spirv_OpGenericCastToPtr_To{Global|Local|Private} to llvm
/// addrspacecast instruction.
void visitCallGenericCastToPtrBuiltIn(CallInst *CI, Op OC);
/// Transform __spirv_OpGenericCastToPtrExplicit_To{Global|Local|Private} to
/// to_{global|local|private} OCL builtin.
void visitCallGenericCastToPtrExplicitBuiltIn(CallInst *CI, Op OC);
/// Transform __spirv_OpBuildINDRange_{1|2|3}D to
/// ndrange_{1|2|3}D OCL builtin.
void visitCallBuildNDRangeBuiltIn(CallInst *CI, Op OC,
StringRef DemangledName);
/// Transform __spirv_*Convert_R{ReturnType}{_sat}{_rtp|_rtn|_rtz|_rte} to
/// convert_{ReturnType}_{sat}{_rtp|_rtn|_rtz|_rte}
/// example: <2 x i8> __spirv_SatConvertUToS(<2 x i32>) =>
/// convert_uchar2_sat(int2)
void visitCallSPIRVCvtBuiltin(CallInst *CI, Op OC, StringRef DemangledName);
/// Transform
/// __spirv_AsyncGroupCopy(ScopeWorkGroup, dst, src, n, stride, event)
/// => async_work_group_strided_copy(dst, src, n, stride, event)
void visitCallAsyncWorkGroupCopy(CallInst *CI, Op OC);
/// Transform __spirv_GroupWaitEvents(Scope, NumEvents, EventsList)
/// => wait_group_events(NumEvents, EventsList)
void visitCallGroupWaitEvents(CallInst *CI, Op OC);
/// Transform __spirv_ImageSampleExplicitLod__{ReturnType} to read_imade
void visitCallSPIRVImageSampleExplicitLodBuiltIn(CallInst *CI, Op OC);
/// Transform __spirv_ImageWrite to write_image
void visitCallSPIRVImageWriteBuiltIn(CallInst *CI, Op OC);
/// Transform __spirv_ImageRead to read_image
void visitCallSPIRVImageReadBuiltIn(CallInst *CI, Op OC);
/// Transform __spirv_ImageQueryOrder to get_image_channel_order
// __spirv_ImageQueryFormat to get_image_channel_data_type
void visitCallSPIRVImageQueryBuiltIn(CallInst *CI, Op OC);
/// Transform subgroup Intel opcodes
/// example: __spirv_SubgroupBlockWriteINTEL
/// => intel_sub_group_block_write_ul
void visitCallSPIRVSubgroupINTELBuiltIn(CallInst *CI, Op OC);
/// Transform AVC INTEL Evaluate opcodes
/// example: __spirv_SubgroupAvcImeEvaluateWithSingleReference
/// => intel_sub_group_avc_ime_evaluate_with_single_reference
void visitCallSPIRVAvcINTELEvaluateBuiltIn(CallInst *CI, Op OC);
/// Transform AVC INTEL general opcodes
/// example: __spirv_SubgroupAvcMceGetDefaultInterBaseMultiReferencePenalty
/// =>
/// intel_sub_group_avc_mce_get_default_inter_base_multi_reference_penalty
void visitCallSPIRVAvcINTELInstructionBuiltin(CallInst *CI, Op OC);
/// Transform __spirv_GenericPtrMemSemantics to:
/// %0 = call spirv_func i32 @_Z9get_fence
/// %1 = shl i31 %0, 8
void visitCallSPIRVGenericPtrMemSemantics(CallInst *CI);
/// Transform __spirv_ConvertFToBF16INTELDv(N)_f to:
/// intel_convert_bfloat16(N)_as_ushort(N)Dv(N)_f;
/// and transform __spirv_ConvertBF16ToFINTELDv(N)_s to:
/// intel_convert_as_bfloat16(N)_float(N)Dv(N)_t;
/// where N is vector size
void visitCallSPIRVBFloat16Conversions(CallInst *CI, Op OC);
/// Transform __spirv_* builtins to OCL 2.0 builtins.
/// No change with arguments.
void visitCallSPIRVBuiltin(CallInst *CI, Op OC);
/// Transform __spirv_* builtins (originates from builtin variables) to OCL
/// builtins.
/// No change with arguments.
/// e.g.
/// _Z33__spirv_BuiltInGlobalInvocationIdi(x) -> get_global_id(x)
void visitCallSPIRVBuiltin(CallInst *CI, SPIRVBuiltinVariableKind Kind);
/// Transform __spirv_ocl* instructions (OpenCL Extended Instruction Set)
/// to OpenCL builtins.
void visitCallSPIRVOCLExt(CallInst *CI, OCLExtOpKind Kind);
/// Transform __spirv_ocl_vstore* to corresponding vstore OpenCL instruction
void visitCallSPIRVVStore(CallInst *CI, OCLExtOpKind Kind);
/// Transform __spirv_ocl_vloadn to OpenCL vload[2|4|8|16]
void visitCallSPIRVVLoadn(CallInst *CI, OCLExtOpKind Kind);
/// Transform __spirv_ocl_printf to (i8 addrspace(2)*, ...) @printf
void visitCallSPIRVPrintf(CallInst *CI, OCLExtOpKind Kind);
/// Get prefix work_/sub_ for OCL group builtin functions.
/// Assuming the first argument of \param CI is a constant integer for
/// workgroup/subgroup scope enums.
std::string getGroupBuiltinPrefix(CallInst *CI);
/// Transform __spirv_OpAtomicCompareExchange and
/// __spirv_OpAtomicCompareExchangeWeak
virtual void visitCallSPIRVAtomicCmpExchg(CallInst *CI) = 0;
/// Transform __spirv_OpAtomicIIncrement/OpAtomicIDecrement to:
/// - OCL2.0: atomic_fetch_add_explicit/atomic_fetch_sub_explicit
/// - OCL1.2: atomic_inc/atomic_dec
virtual void visitCallSPIRVAtomicIncDec(CallInst *CI, Op OC) = 0;
/// Transform __spirv_Atomic* to atomic_*.
/// __spirv_Atomic*(atomic_op, scope, sema, ops, ...) =>
/// atomic_*(atomic_op, ops, ..., order(sema), map(scope))
virtual void visitCallSPIRVAtomicBuiltin(CallInst *CI, Op OC) = 0;
/// Transform __spirv_MemoryBarrier to:
/// - OCL2.0: atomic_work_item_fence.__spirv_MemoryBarrier(scope, sema) =>
/// atomic_work_item_fence(flag(sema), order(sema), map(scope))
/// - OCL1.2: mem_fence
virtual void visitCallSPIRVMemoryBarrier(CallInst *CI) = 0;
/// Transform __spirv_ControlBarrier to:
/// - OCL2.0: work_group_barrier or sub_group barrier
/// - OCL1.2: barrier
virtual void visitCallSPIRVControlBarrier(CallInst *CI) = 0;
/// Transform split __spirv_ControlBarrierArriveINTEL and
/// __spirv_ControlBarrierWaitINTEL barrier to:
/// - OCL2.0: overload with a memory_scope argument
/// - OCL1.2: overload with no memory_scope argument
virtual void visitCallSPIRVSplitBarrierINTEL(CallInst *CI, Op OC) = 0;
/// Transform __spirv_EnqueueKernel to __enqueue_kernel
virtual void visitCallSPIRVEnqueueKernel(CallInst *CI, Op OC) = 0;
/// Transform __spirv_Any and __spirv_All to OpenCL builtin.
void visitCallSPIRVAnyAll(CallInst *CI, Op OC);
/// Transform relational builtin, e.g. __spirv_IsNan, to OpenCL builtin.
void visitCallSPIRVRelational(CallInst *CI, Op OC);
/// Transform __spirv_ReadClockKHR to OpenCL builtin.
void visitCallSPIRVReadClockKHR(CallInst *CI);
/// Conduct generic mutations for all atomic builtins
virtual CallInst *mutateCommonAtomicArguments(CallInst *CI, Op OC) = 0;
/// Transform __spirv_Opcode to ocl-version specific builtin name
/// using separate maps for OpenCL 1.2 and OpenCL 2.0
virtual void mutateAtomicName(CallInst *CI, Op OC) = 0;
// Transform FP atomic opcode to corresponding OpenCL function name
virtual std::string mapFPAtomicName(Op OC) = 0;
/// Transform integer dot product builtins to corresponding OpenCL builtins
/// examples: __spirv_SDotKHR => dot, __spirv_SDotAccSatKHR => dot_acc_sat
void visitCallSPIRVDot(CallInst *CI, Op OC, StringRef DemangledName);
void translateOpaqueTypes();
private:
/// Transform uniform group opcode to corresponding OpenCL function name,
/// example: GroupIAdd(Reduce) => group_iadd => work_group_reduce_add |
/// sub_group_reduce_add
std::string getUniformArithmeticBuiltinName(CallInst *CI, Op OC);
/// Transform non-uniform group opcode to corresponding OpenCL function name,
/// example: GroupNonUniformIAdd(Reduce) => group_non_uniform_iadd =>
/// sub_group_non_uniform_reduce_add
std::string getNonUniformArithmeticBuiltinName(CallInst *CI, Op OC);
/// Transform ballot bit count opcode to corresponding OpenCL function name,
/// example: GroupNonUniformBallotBitCount(Reduce) =>
/// group_ballot_bit_count_iadd => sub_group_ballot_bit_count
std::string getBallotBuiltinName(CallInst *CI, Op OC);
/// Transform OpGroupNonUniformRotateKHR to corresponding OpenCL function
/// name.
std::string getRotateBuiltinName(CallInst *CI, Op OC);
/// Transform group opcode to corresponding OpenCL function name
std::string groupOCToOCLBuiltinName(CallInst *CI, Op OC);
/// Transform SPV-IR image opaque type into OpenCL representation,
/// example: spirv.Image._void_1_0_0_0_0_0_1 => opencl.image2d_wo_t
static std::string
getOCLImageOpaqueType(SmallVector<std::string, 8> &Postfixes);
/// Transform SPV-IR pipe opaque type into OpenCL representation,
/// example: spirv.Pipe._0 => opencl.pipe_ro_t
static std::string
getOCLPipeOpaqueType(SmallVector<std::string, 8> &Postfixes);
static std::string translateOpaqueType(StringRef STName);
/// Mutate the call instruction based on (optional) image operands at position
/// ImOpArgIndex. The new function name will be based on NewFuncName, and the
/// type suffix based on Ty and whether the image operand was signed.
BuiltinCallMutator mutateCallImageOperands(CallInst *CI,
StringRef NewFuncName, Type *Ty,
unsigned ImOpArgIndex);
protected:
Module *M;
LLVMContext *Ctx;
};
class SPIRVToOCLLegacy : public ModulePass {
protected:
SPIRVToOCLLegacy(char &ID) : ModulePass(ID) {}
public:
bool runOnModule(Module &M) override = 0;
};
class SPIRVToOCL12Base : public SPIRVToOCLBase {
public:
bool runSPIRVToOCL(Module &M) override;
/// Transform __spirv_MemoryBarrier to atomic_work_item_fence.
/// __spirv_MemoryBarrier(scope, sema) =>
/// atomic_work_item_fence(flag(sema), order(sema), map(scope))
void visitCallSPIRVMemoryBarrier(CallInst *CI) override;
/// Transform __spirv_ControlBarrier to barrier.
/// __spirv_ControlBarrier(execScope, memScope, sema) =>
/// barrier(flag(sema))
void visitCallSPIRVControlBarrier(CallInst *CI) override;
/// Transform split __spirv_ControlBarrierArriveINTEL and
/// __spirv_ControlBarrierWaitINTEL barrier to overloads without a
/// memory_scope argument.
void visitCallSPIRVSplitBarrierINTEL(CallInst *CI, Op OC) override;
/// Transform __spirv_OpAtomic functions. It firstly conduct generic
/// mutations for all builtins and then mutate some of them seperately
void visitCallSPIRVAtomicBuiltin(CallInst *CI, Op OC) override;
/// Transform __spirv_OpAtomicIIncrement / OpAtomicIDecrement to
/// atomic_inc / atomic_dec
void visitCallSPIRVAtomicIncDec(CallInst *CI, Op OC) override;
/// Transform __spirv_OpAtomicUMin/SMin/UMax/SMax into
/// atomic_min/atomic_max, as there is no distinction in OpenCL 1.2
/// between signed and unsigned version of those functions
void visitCallSPIRVAtomicUMinUMax(CallInst *CI, Op OC);
/// Transform __spirv_OpAtomicLoad to atomic_add(*ptr, 0)
void visitCallSPIRVAtomicLoad(CallInst *CI);
/// Transform __spirv_OpAtomicStore to atomic_xchg(*ptr, value)
void visitCallSPIRVAtomicStore(CallInst *CI);
/// Transform __spirv_OpAtomicFlagClear to atomic_xchg(*ptr, 0)
/// with ignoring the result
void visitCallSPIRVAtomicFlagClear(CallInst *CI);
/// Transform __spirv_OpAtomicFlagTestAndTest to
/// (bool)atomic_xchg(*ptr, 1)
void visitCallSPIRVAtomicFlagTestAndSet(CallInst *CI);
/// Transform __spirv_OpAtomicCompareExchange/Weak into atomic_cmpxchg
/// OpAtomicCompareExchangeWeak is not "weak" at all, but instead has
/// the same semantics as OpAtomicCompareExchange.
void visitCallSPIRVAtomicCmpExchg(CallInst *CI) override;
/// Trigger assert, since OpenCL 1.2 doesn't support enqueue_kernel
void visitCallSPIRVEnqueueKernel(CallInst *CI, Op OC) override;
/// Conduct generic mutations for all atomic builtins
CallInst *mutateCommonAtomicArguments(CallInst *CI, Op OC) override;
/// Transform atomic builtin name into correct ocl-dependent name
void mutateAtomicName(CallInst *CI, Op OC) override;
// Transform FP atomic opcode to corresponding OpenCL function name
std::string mapFPAtomicName(Op OC) override;
/// Transform SPIR-V atomic instruction opcode into OpenCL 1.2 builtin name.
/// Depending on the type, the return name starts with "atomic_" for 32-bit
/// types or with "atom_" for 64-bit types, as specified by
/// cl_khr_int64_base_atomics and cl_khr_int64_extended_atomics extensions.
std::string mapAtomicName(Op OC, Type *Ty);
};
class SPIRVToOCL12Pass : public llvm::PassInfoMixin<SPIRVToOCL12Pass>,
public SPIRVToOCL12Base {
public:
llvm::PreservedAnalyses run(llvm::Module &M,
llvm::ModuleAnalysisManager &MAM) {
return runSPIRVToOCL(M) ? llvm::PreservedAnalyses::none()
: llvm::PreservedAnalyses::all();
}
static bool isRequired() { return true; }
};
class SPIRVToOCL12Legacy : public SPIRVToOCL12Base, public SPIRVToOCLLegacy {
public:
SPIRVToOCL12Legacy() : SPIRVToOCLLegacy(ID) {
initializeSPIRVToOCL12LegacyPass(*PassRegistry::getPassRegistry());
}
bool runOnModule(Module &M) override;
static char ID;
};
class SPIRVToOCL20Base : public SPIRVToOCLBase {
public:
bool runSPIRVToOCL(Module &M) override;
/// Transform __spirv_MemoryBarrier to atomic_work_item_fence.
/// __spirv_MemoryBarrier(scope, sema) =>
/// atomic_work_item_fence(flag(sema), order(sema), map(scope))
void visitCallSPIRVMemoryBarrier(CallInst *CI) override;
/// Transform __spirv_ControlBarrier to work_group_barrier/sub_group_barrier.
/// If execution scope is ScopeWorkgroup:
/// __spirv_ControlBarrier(execScope, memScope, sema) =>
/// work_group_barrier(flag(sema), map(memScope))
/// Otherwise:
/// __spirv_ControlBarrier(execScope, memScope, sema) =>
/// sub_group_barrier(flag(sema), map(memScope))
void visitCallSPIRVControlBarrier(CallInst *CI) override;
/// Transform split __spirv_ControlBarrierArriveINTEL and
/// __spirv_ControlBarrierWaitINTEL barrier to overloads with a
/// memory_scope argument.
void visitCallSPIRVSplitBarrierINTEL(CallInst *CI, Op OC) override;
/// Transform __spirv_Atomic* to atomic_*.
/// __spirv_Atomic*(atomic_op, scope, sema, ops, ...) =>
/// atomic_*(generic atomic_op, ops, ..., order(sema), map(scope))
void visitCallSPIRVAtomicBuiltin(CallInst *CI, Op OC) override;
/// Transform __spirv_OpAtomicIIncrement / OpAtomicIDecrement to
/// atomic_fetch_add_explicit / atomic_fetch_sub_explicit
void visitCallSPIRVAtomicIncDec(CallInst *CI, Op OC) override;
/// Transform __spirv_EnqueueKernel to __enqueue_kernel
void visitCallSPIRVEnqueueKernel(CallInst *CI, Op OC) override;
/// Conduct generic mutations for all atomic builtins
CallInst *mutateCommonAtomicArguments(CallInst *CI, Op OC) override;
/// Transform atomic builtin name into correct ocl-dependent name
void mutateAtomicName(CallInst *CI, Op OC) override;
// Transform FP atomic opcode to corresponding OpenCL function name
std::string mapFPAtomicName(Op OC) override;
/// Transform __spirv_OpAtomicCompareExchange/Weak into
/// atomic_compare_exchange_strong_explicit
/// OpAtomicCompareExchangeWeak is not "weak" at all, but instead has
/// the same semantics as OpAtomicCompareExchange.
void visitCallSPIRVAtomicCmpExchg(CallInst *CI) override;
};
class SPIRVToOCL20Pass : public llvm::PassInfoMixin<SPIRVToOCL20Pass>,
public SPIRVToOCL20Base {
public:
llvm::PreservedAnalyses run(llvm::Module &M,
llvm::ModuleAnalysisManager &MAM) {
return runSPIRVToOCL(M) ? llvm::PreservedAnalyses::none()
: llvm::PreservedAnalyses::all();
}
static bool isRequired() { return true; }
};
class SPIRVToOCL20Legacy : public SPIRVToOCLLegacy, public SPIRVToOCL20Base {
public:
SPIRVToOCL20Legacy() : SPIRVToOCLLegacy(ID) {
initializeSPIRVToOCL20LegacyPass(*PassRegistry::getPassRegistry());
}
bool runOnModule(Module &M) override;
static char ID;
};
/// Add passes for translating SPIR-V Instructions to the desired
/// representation in LLVM IR (such as OpenCL builtins or SPIR-V Friendly IR).
void addSPIRVBIsLoweringPass(ModulePassManager &PassMgr,
SPIRV::BIsRepresentation BIsRep);
} // namespace SPIRV
#endif // SPIRVTOOCL_H
@@ -0,0 +1,245 @@
//===- SPIRVToOCL12.cpp - Transform SPIR-V builtins to OCL 1.2
// builtins------===//
//
// The LLVM/SPIRV Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
//
// 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 Advanced Micro Devices, Inc., 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.
//
//===----------------------------------------------------------------------===//
//
// This file implements transform of SPIR-V builtins to OCL 1.2 builtins.
//
//===----------------------------------------------------------------------===//
#include "SPIRVToOCL.h"
#include "llvm/IR/Verifier.h"
#define DEBUG_TYPE "spvtocl12"
namespace SPIRV {
char SPIRVToOCL12Legacy::ID = 0;
bool SPIRVToOCL12Legacy::runOnModule(Module &Module) {
return SPIRVToOCL12Base::runSPIRVToOCL(Module);
}
bool SPIRVToOCL12Base::runSPIRVToOCL(Module &Module) {
M = &Module;
Ctx = &M->getContext();
// Lower builtin variables to builtin calls first.
lowerBuiltinVariablesToCalls(M);
translateOpaqueTypes();
visit(*M);
postProcessBuiltinsReturningStruct(M);
postProcessBuiltinsWithArrayArguments(M);
eraseUselessFunctions(&Module);
LLVM_DEBUG(dbgs() << "After SPIRVToOCL12:\n" << *M);
std::string Err;
raw_string_ostream ErrorOS(Err);
if (verifyModule(*M, &ErrorOS)) {
LLVM_DEBUG(errs() << "Fails to verify module: " << ErrorOS.str());
}
return true;
}
void SPIRVToOCL12Base::visitCallSPIRVMemoryBarrier(CallInst *CI) {
mutateCallInst(CI, kOCLBuiltinName::MemFence)
.mapArg(1,
[=](Value *V) {
return transSPIRVMemorySemanticsIntoOCLMemFenceFlags(V, CI);
})
.removeArg(0);
}
void SPIRVToOCL12Base::visitCallSPIRVControlBarrier(CallInst *CI) {
mutateCallInst(CI, kOCLBuiltinName::Barrier)
.mapArg(2,
[=](Value *V) {
return transSPIRVMemorySemanticsIntoOCLMemFenceFlags(V, CI);
})
.removeArg(1)
.removeArg(0);
}
void SPIRVToOCL12Base::visitCallSPIRVSplitBarrierINTEL(CallInst *CI, Op OC) {
mutateCallInst(CI, OCLSPIRVBuiltinMap::rmap(OC))
.mapArg(2,
[=](Value *V) {
return transSPIRVMemorySemanticsIntoOCLMemFenceFlags(V, CI);
})
.removeArg(1)
.removeArg(0);
}
void SPIRVToOCL12Base::visitCallSPIRVAtomicIncDec(CallInst *CI, Op OC) {
mutateCallInst(CI, mapAtomicName(OC, CI->getType()))
.removeArg(2)
.removeArg(1);
}
CallInst *SPIRVToOCL12Base::mutateCommonAtomicArguments(CallInst *CI, Op OC) {
auto Ptr = findFirstPtr(CI->args());
auto NumOrder = getSPIRVAtomicBuiltinNumMemoryOrderArgs(OC);
auto ArgsToRemove = NumOrder + 1; // OpenCL1.2 builtins does not use
// scope and memory order arguments
auto Mutator = mutateCallInst(CI, mapAtomicName(OC, CI->getType()));
Mutator.removeArgs(Ptr + 1, ArgsToRemove);
return cast<CallInst>(Mutator.getMutated());
}
void SPIRVToOCL12Base::visitCallSPIRVAtomicUMinUMax(CallInst *CI, Op OC) {
mutateCallInst(CI, mapAtomicName(OC, CI->getType()))
.moveArg(3, 1)
.removeArg(3)
.removeArg(2);
}
void SPIRVToOCL12Base::visitCallSPIRVAtomicLoad(CallInst *CI) {
// There is no atomic_load in OpenCL 1.2 spec.
// Emit this builtin via call of atomic_add(*p, 0).
Type *PtrElemTy = CI->getType();
mutateCallInst(CI, mapAtomicName(OpAtomicIAdd, PtrElemTy))
.removeArg(2)
.removeArg(1)
.appendArg(Constant::getNullValue(PtrElemTy));
}
void SPIRVToOCL12Base::visitCallSPIRVAtomicStore(CallInst *CI) {
Type *RetTy = CI->getArgOperand(3)->getType();
mutateCallInst(CI, mapAtomicName(OpAtomicExchange, RetTy))
.removeArg(2)
.removeArg(1)
.changeReturnType(RetTy, nullptr);
}
void SPIRVToOCL12Base::visitCallSPIRVAtomicFlagClear(CallInst *CI) {
Type *RetTy = Type::getInt32Ty(M->getContext());
mutateCallInst(CI, mapAtomicName(OpAtomicExchange, RetTy))
.removeArg(2)
.removeArg(1)
.appendArg(getInt32(M, 0))
.changeReturnType(RetTy, nullptr);
}
void SPIRVToOCL12Base::visitCallSPIRVAtomicFlagTestAndSet(CallInst *CI) {
Type *RetTy = Type::getInt32Ty(M->getContext());
mutateCallInst(CI, mapAtomicName(OpAtomicExchange, RetTy))
.removeArg(2)
.removeArg(1)
.appendArg(getInt32(M, 1))
.changeReturnType(RetTy, [](IRBuilder<> &Builder, CallInst *NewCI) {
return Builder.CreateTrunc(NewCI, Builder.getInt1Ty());
});
}
void SPIRVToOCL12Base::visitCallSPIRVAtomicCmpExchg(CallInst *CI) {
mutateCallInst(CI, mapAtomicName(OpAtomicCompareExchange, CI->getType()))
.removeArg(3)
.removeArg(2)
.removeArg(1)
// SPIRV OpAtomicCompareExchange and OpAtomicCompareExchangeWeak has Value
// and Comparator in different order than ocl functions both of them are
// translated into atomic_cmpxchg
.moveArg(2, 1);
}
void SPIRVToOCL12Base::visitCallSPIRVAtomicBuiltin(CallInst *CI, Op OC) {
switch (OC) {
case OpAtomicLoad:
visitCallSPIRVAtomicLoad(CI);
break;
case OpAtomicStore:
visitCallSPIRVAtomicStore(CI);
break;
case OpAtomicFlagClear:
visitCallSPIRVAtomicFlagClear(CI);
break;
case OpAtomicFlagTestAndSet:
visitCallSPIRVAtomicFlagTestAndSet(CI);
break;
case OpAtomicUMin:
case OpAtomicUMax:
visitCallSPIRVAtomicUMinUMax(CI, OC);
break;
case OpAtomicCompareExchange:
case OpAtomicCompareExchangeWeak:
visitCallSPIRVAtomicCmpExchg(CI);
break;
default:
mutateCommonAtomicArguments(CI, OC);
}
}
void SPIRVToOCL12Base::visitCallSPIRVEnqueueKernel(CallInst *CI, Op OC) {
assert(0 && "OpenCL 1.2 doesn't support enqueue_kernel!");
}
std::string SPIRVToOCL12Base::mapFPAtomicName(Op OC) {
assert(isFPAtomicOpCode(OC) && "Not intended to handle other opcodes than "
"AtomicF{Add/Min/Max}EXT!");
switch (OC) {
case OpAtomicFAddEXT:
return "atomic_add";
case OpAtomicFMinEXT:
return "atomic_min";
case OpAtomicFMaxEXT:
return "atomic_max";
default:
llvm_unreachable("Unsupported opcode!");
}
}
void SPIRVToOCL12Base::mutateAtomicName(CallInst *CI, Op OC) {
mutateCallInst(CI, OCL12SPIRVBuiltinMap::rmap(OC));
}
std::string SPIRVToOCL12Base::mapAtomicName(Op OC, Type *Ty) {
std::string Prefix = Ty->isIntegerTy(64) ? kOCLBuiltinName::AtomPrefix
: kOCLBuiltinName::AtomicPrefix;
// Map fp atomic instructions to regular OpenCL built-ins.
if (isFPAtomicOpCode(OC))
return mapFPAtomicName(OC);
return Prefix += OCL12SPIRVBuiltinMap::rmap(OC);
}
} // namespace SPIRV
INITIALIZE_PASS(SPIRVToOCL12Legacy, "spvtoocl12",
"Translate SPIR-V builtins to OCL 1.2 builtins", false, false)
ModulePass *llvm::createSPIRVToOCL12Legacy() {
return new SPIRVToOCL12Legacy();
}
@@ -0,0 +1,301 @@
//===- SPIRVToOCL20.cpp - Transform SPIR-V builtins to OCL20 builtins------===//
//
// The LLVM/SPIRV Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
//
// 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 Advanced Micro Devices, Inc., 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.
//
//===----------------------------------------------------------------------===//
//
// This file implements transform SPIR-V builtins to OCL 2.0 builtins.
//
//===----------------------------------------------------------------------===//
#include "OCLUtil.h"
#include "SPIRVToOCL.h"
#include "llvm/IR/Verifier.h"
#define DEBUG_TYPE "spvtocl20"
namespace SPIRV {
char SPIRVToOCL20Legacy::ID = 0;
bool SPIRVToOCL20Legacy::runOnModule(Module &Module) {
return SPIRVToOCL20Base::runSPIRVToOCL(Module);
}
bool SPIRVToOCL20Base::runSPIRVToOCL(Module &Module) {
M = &Module;
Ctx = &M->getContext();
// Lower builtin variables to builtin calls first.
lowerBuiltinVariablesToCalls(M);
translateOpaqueTypes();
visit(*M);
postProcessBuiltinsReturningStruct(M);
postProcessBuiltinsWithArrayArguments(M);
eraseUselessFunctions(&Module);
LLVM_DEBUG(dbgs() << "After SPIRVToOCL20:\n" << *M);
std::string Err;
raw_string_ostream ErrorOS(Err);
if (verifyModule(*M, &ErrorOS)) {
LLVM_DEBUG(errs() << "Fails to verify module: " << ErrorOS.str());
}
return true;
}
void SPIRVToOCL20Base::visitCallSPIRVMemoryBarrier(CallInst *CI) {
Value *MemScope =
SPIRV::transSPIRVMemoryScopeIntoOCLMemoryScope(CI->getArgOperand(0), CI);
Value *MemFenceFlags = SPIRV::transSPIRVMemorySemanticsIntoOCLMemFenceFlags(
CI->getArgOperand(1), CI);
Value *MemOrder = SPIRV::transSPIRVMemorySemanticsIntoOCLMemoryOrder(
CI->getArgOperand(1), CI);
mutateCallInst(CI, kOCLBuiltinName::AtomicWorkItemFence)
.setArgs({MemFenceFlags, MemOrder, MemScope});
}
void SPIRVToOCL20Base::visitCallSPIRVControlBarrier(CallInst *CI) {
auto GetArg = [=](unsigned I) {
return cast<ConstantInt>(CI->getArgOperand(I))->getZExtValue();
};
auto ExecScope = static_cast<Scope>(GetArg(0));
Value *MemScope =
SPIRV::transSPIRVMemoryScopeIntoOCLMemoryScope(CI->getArgOperand(1), CI);
Value *MemFenceFlags = SPIRV::transSPIRVMemorySemanticsIntoOCLMemFenceFlags(
CI->getArgOperand(2), CI);
mutateCallInst(CI, ExecScope == ScopeWorkgroup
? kOCLBuiltinName::WorkGroupBarrier
: kOCLBuiltinName::SubGroupBarrier)
.setArgs({MemFenceFlags, MemScope});
}
void SPIRVToOCL20Base::visitCallSPIRVSplitBarrierINTEL(CallInst *CI, Op OC) {
Value *MemScope =
SPIRV::transSPIRVMemoryScopeIntoOCLMemoryScope(CI->getArgOperand(1), CI);
Value *MemFenceFlags = SPIRV::transSPIRVMemorySemanticsIntoOCLMemFenceFlags(
CI->getArgOperand(2), CI);
mutateCallInst(CI, OCLSPIRVBuiltinMap::rmap(OC))
.setArgs({MemFenceFlags, MemScope});
}
std::string SPIRVToOCL20Base::mapFPAtomicName(Op OC) {
assert(isFPAtomicOpCode(OC) && "Not intended to handle other opcodes than "
"AtomicF{Add/Min/Max}EXT!");
switch (OC) {
case OpAtomicFAddEXT:
return "atomic_fetch_add_explicit";
case OpAtomicFMinEXT:
return "atomic_fetch_min_explicit";
case OpAtomicFMaxEXT:
return "atomic_fetch_max_explicit";
default:
llvm_unreachable("Unsupported opcode!");
}
}
void SPIRVToOCL20Base::mutateAtomicName(CallInst *CI, Op OC) {
// Map fp atomic instructions to regular OpenCL built-ins.
mutateCallInst(CI, isFPAtomicOpCode(OC) ? mapFPAtomicName(OC)
: OCLSPIRVBuiltinMap::rmap(OC));
}
void SPIRVToOCL20Base::visitCallSPIRVAtomicBuiltin(CallInst *CI, Op OC) {
CallInst *CIG = mutateCommonAtomicArguments(CI, OC);
switch (OC) {
case OpAtomicIIncrement:
case OpAtomicIDecrement:
visitCallSPIRVAtomicIncDec(CIG, OC);
break;
case OpAtomicCompareExchange:
case OpAtomicCompareExchangeWeak:
visitCallSPIRVAtomicCmpExchg(CIG);
break;
default:
mutateAtomicName(CIG, OC);
}
}
void SPIRVToOCL20Base::visitCallSPIRVAtomicIncDec(CallInst *CI, Op OC) {
// Since OpenCL 2.0 doesn't have atomic_inc and atomic_dec builtins, we
// translate these instructions to atomic_fetch_add_explicit and
// atomic_fetch_sub_explicit OpenCL 2.0 builtins with "operand" argument = 1.
auto Name = OCLSPIRVBuiltinMap::rmap(OC == OpAtomicIIncrement ? OpAtomicIAdd
: OpAtomicISub);
Type *ValueTy = CI->getType();
assert(ValueTy->isIntegerTy());
mutateCallInst(CI, Name).insertArg(1, ConstantInt::get(ValueTy, 1));
}
CallInst *SPIRVToOCL20Base::mutateCommonAtomicArguments(CallInst *CI, Op OC) {
std::string Name;
// Map fp atomic instructions to regular OpenCL built-ins.
if (isFPAtomicOpCode(OC))
Name = mapFPAtomicName(OC);
else
Name = OCLSPIRVBuiltinMap::rmap(OC);
auto Ptr = findFirstPtr(CI->args());
auto NumOrder = getSPIRVAtomicBuiltinNumMemoryOrderArgs(OC);
auto ScopeIdx = Ptr + 1;
auto OrderIdx = Ptr + 2;
auto Mutator = mutateCallInst(CI, Name);
Mutator.mapArgs([=](IRBuilder<> &Builder, Value *PtrArg, Type *PtrArgTy) {
if (auto *TypedPtrTy = dyn_cast<TypedPointerType>(PtrArgTy)) {
if (TypedPtrTy->getAddressSpace() != SPIRAS_Generic) {
Type *ElementTy = TypedPtrTy->getElementType();
Type *FixedPtr = PointerType::get(CI->getContext(), SPIRAS_Generic);
PtrArg = Builder.CreateAddrSpaceCast(PtrArg, FixedPtr,
PtrArg->getName() + ".as");
PtrArgTy = TypedPointerType::get(ElementTy, SPIRAS_Generic);
}
}
return std::make_pair(PtrArg, PtrArgTy);
});
Mutator.mapArg(ScopeIdx, [=](Value *Arg) {
return SPIRV::transSPIRVMemoryScopeIntoOCLMemoryScope(Arg, CI);
});
for (size_t I = 0; I < NumOrder; ++I) {
Mutator.mapArg(OrderIdx + I, [=](Value *Arg) {
return SPIRV::transSPIRVMemorySemanticsIntoOCLMemoryOrder(Arg, CI);
});
}
Mutator.moveArg(Mutator.arg_size() - 1, ScopeIdx + 1);
Mutator.moveArg(ScopeIdx, Mutator.arg_size() - 1);
return cast<CallInst>(Mutator.getMutated());
}
void SPIRVToOCL20Base::visitCallSPIRVAtomicCmpExchg(CallInst *CI) {
Type *MemTy = CI->getType();
// OpAtomicCompareExchange[Weak] semantics is different from
// atomic_compare_exchange_strong semantics as well as arguments order.
// OCL built-ins returns boolean value and stores a new/original
// value by pointer passed as 2nd argument (aka expected) while SPIR-V
// instructions returns this new/original value as a resulting value.
AllocaInst *PExpected = new AllocaInst(
MemTy, M->getDataLayout().getAllocaAddrSpace(), "expected",
CI->getParent()->getParent()->getEntryBlock().getFirstInsertionPt());
PExpected->setAlignment(Align(MemTy->getScalarSizeInBits() / 8));
// Tail call implies that the callee doesn't access alloca from the caller.
// The newly created alloca invalidates the tail call semantics.
CI->setTailCall(false);
// OpAtomicCompareExchangeWeak is not "weak" at all, but instead has the same
// semantics as OpAtomicCompareExchange.
mutateCallInst(CI, "atomic_compare_exchange_strong_explicit")
.mapArg(1,
[=](IRBuilder<> &Builder, Value *Expected) {
Builder.CreateStore(Expected, PExpected);
unsigned AddrSpc = SPIRAS_Generic;
Type *PtrTyAS =
PointerType::get(Expected->getContext(), AddrSpc);
Value *V = Builder.CreateAddrSpaceCast(
PExpected, PtrTyAS, PExpected->getName() + ".as");
return std::make_pair(V, TypedPointerType::get(MemTy, AddrSpc));
})
.moveArg(4, 2)
.changeReturnType(Type::getInt1Ty(*Ctx), [=](IRBuilder<> &Builder,
CallInst *NewCI) {
// OCL built-ins atomic_compare_exchange_[strong|weak] return boolean
// value. So, to obtain the same value as SPIR-V instruction is
// returning it has to be loaded from the memory where 'expected'
// value is stored. This memory must contain the needed value after a
// call to OCL built-in is completed.
return Builder.CreateLoad(MemTy, NewCI->getArgOperand(1), "original");
});
}
void SPIRVToOCL20Base::visitCallSPIRVEnqueueKernel(CallInst *CI, Op OC) {
bool HasVaargs = CI->arg_size() > 10;
bool HasEvents = true;
Value *EventRet = CI->getArgOperand(5);
if (isa<ConstantPointerNull>(EventRet)) {
Value *NumEvents = CI->getArgOperand(3);
if (isa<ConstantInt>(NumEvents)) {
ConstantInt *NE = cast<ConstantInt>(NumEvents);
HasEvents = NE->getZExtValue() != 0;
}
}
StringRef FName = "";
if (!HasVaargs && !HasEvents)
FName = "__enqueue_kernel_basic";
else if (!HasVaargs && HasEvents)
FName = "__enqueue_kernel_basic_events";
else if (HasVaargs && !HasEvents)
FName = "__enqueue_kernel_varargs";
else
FName = "__enqueue_kernel_events_varargs";
auto Mutator = mutateCallInst(CI, FName.str());
Mutator.mapArg(6, [=](IRBuilder<> &Builder, Value *Invoke) {
Value *Replace = CastInst::CreatePointerBitCastOrAddrSpaceCast(
Invoke, Builder.getPtrTy(SPIRAS_Generic), "", CI->getIterator());
return std::make_pair(
Replace, TypedPointerType::get(Builder.getInt8Ty(), SPIRAS_Generic));
});
if (!HasVaargs) {
// Remove arguments at indices 8 (Param Size), 9 (Param Align)
Mutator.removeArgs(8, 2);
} else {
// GEP to array of sizes of local arguments
Mutator.moveArg(10, 8);
Type *Int32Ty = Type::getInt32Ty(*Ctx);
size_t NumLocalArgs = Mutator.arg_size() - 10;
Mutator.insertArg(8, ConstantInt::get(Int32Ty, NumLocalArgs));
// Mark all SPIRV-specific arguments as removed
Mutator.removeArgs(10, Mutator.arg_size() - 10);
}
if (!HasEvents) {
// Remove arguments at indices 3 (Num Events), 4 (Wait Events), 5 (Ret
// Event).
Mutator.removeArgs(3, 3);
}
}
} // namespace SPIRV
INITIALIZE_PASS(SPIRVToOCL20Legacy, "spvtoocl20",
"Translate SPIR-V builtins to OCL 2.0 builtins", false, false)
ModulePass *llvm::createSPIRVToOCL20Legacy() {
return new SPIRVToOCL20Legacy();
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,225 @@
//===- SPIRVTypeScavenger.h - Recover pointer types in opaque pointer IR --===//
//
// The LLVM/SPIR-V Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2022 The Khronos Group Inc.
//
// 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 Khronos Group, 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.
//
//===----------------------------------------------------------------------===//
//
// This file implements the necessary logic to recover pointer types from LLVM
// IR for the output SPIR-V file after LLVM IR completes its transition to
// opaque pointers.
//
//===----------------------------------------------------------------------===//
#ifndef SPIRVTYPESCAVENGER_H
#define SPIRVTYPESCAVENGER_H
#include "llvm/ADT/IntEqClasses.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/ValueMap.h"
using namespace llvm;
/// This class allows for the recovery of typed pointer types from LLVM opaque
/// pointer types. A detailed description of how this algorithm works may be
/// found in the file comment of SPIRVTypeScavenger.cpp.
class SPIRVTypeScavenger {
/// The mapping from type variables to concrete types.
std::vector<Type *> TypeVariables;
/// The structure storing which type variables have been unified.
IntEqClasses UnifiedTypeVars;
/// Replace all ptr types found within T with new type variables.
Type *allocateTypeVariable(Type *T);
/// Replace all type variables found within T with their concrete types. If
/// the type variable doesn't have a concrete type yet, the type variable will
/// be retained.
Type *substituteTypeVariables(Type *T);
/// Try to resolve all type variables into concrete types using knowledge that
/// T1 and T2 have to be the same type. If T1 and T2 cannot be made the same
/// type, return false (and callers will know they need to insert synthetic
/// bitcasts to guarantee equality).
bool unifyType(Type *T1, Type *T2);
/// This stores the Value -> corrected type mapping for the module. It is
/// expected that all instructions, arguments, and global values will appear
/// in this mapping, while constants are not expected to be listed here.
ValueMap<Value *, Type *> DeducedTypes;
/// Store associated type variables for certain instructions. In the case
/// where a return value has an association with an operand, it's necessary
/// that the type variable used to generate type rules be the same for all
/// invocations of getTypeRules. This variable allows storage of such
/// variables.
ValueMap<Value *, Type *> AssociatedTypeVariables;
/// A type rule, which expresses that the given operand of a User must have
/// the given type (which may contain type variables).
struct TypeRule {
unsigned OpNo;
bool LhsIndirect;
bool RhsIndirect;
PointerUnion<Type *, Use *> Target;
TypeRule(unsigned A, bool AIndirect, Type *B, bool BIndirect)
: OpNo(A), LhsIndirect(AIndirect), RhsIndirect(BIndirect), Target(B) {}
TypeRule(unsigned A, bool AIndirect, Use *B, bool BIndirect)
: OpNo(A), LhsIndirect(AIndirect), RhsIndirect(BIndirect), Target(B) {}
/// Establishes typeof(operand) == concrete type
static TypeRule is(unsigned OpIndex, Type *Ty) {
return TypeRule(OpIndex, false, Ty, false);
}
/// Establishes typeof(operand) == concrete type
static TypeRule is(Use &U, Type *Ty) {
return TypeRule::is(U.getOperandNo(), Ty);
}
/// Establishes typeof(operand) == typeof(operand)
static TypeRule is(User &U, unsigned Op1, unsigned Op2) {
return TypeRule(Op1, false, &U.getOperandUse(Op2), false);
}
/// Establishes typedptr(typeof(operand)) == typedptr(typeof(operand))
/// (this is useful when the address spaces do not need to match).
static TypeRule isIndirect(User &U, unsigned Op1, unsigned Op2) {
return TypeRule(Op1, true, &U.getOperandUse(Op2), true);
}
/// Establishes typeof(operand) == typedptr(concrete type)
static TypeRule pointsTo(Use &U, Type *Ty) {
return TypeRule(U.getOperandNo(), false, Ty, true);
}
/// Establishes typeof(operand) == typedptr(concrete type)
static TypeRule pointsTo(User &U, unsigned OpIndex, Type *Ty) {
return TypeRule::pointsTo(U.getOperandUse(OpIndex), Ty);
}
/// Establishes typeof(mem operand) == typedptr(typeof(val operand))
static TypeRule pointsTo(User &U, unsigned MemIndex, unsigned ValIndex) {
return TypeRule(MemIndex, false, &U.getOperandUse(ValIndex), true);
}
/// Establishes typeof(operand) == typedptr(typeof(return))
static TypeRule pointsToReturn(User &U, unsigned OpIndex) {
return TypeRule(RETURN_OPERAND, true, &U.getOperandUse(OpIndex), false);
}
/// Establishes typeof(return) == concrete type
static TypeRule returns(Type *Ty) {
return TypeRule(RETURN_OPERAND, false, Ty, false);
}
/// Establishes typeof(return) == typedptr(concrete type)
static TypeRule returnsPointerTo(Type *Ty) {
return TypeRule(RETURN_OPERAND, false, Ty, true);
}
/// Establishes typeof(return) == typeof(operand)
static TypeRule propagates(Use &U) {
return TypeRule(RETURN_OPERAND, false, &U, false);
}
/// Establishes typeof(return) == typeof(operand)
static TypeRule propagates(User &U, unsigned OpIndex) {
return TypeRule::propagates(U.getOperandUse(OpIndex));
}
/// Establishes typedptr(typeof(return)) == typedptr(typeof(operand))
static TypeRule propagatesIndirect(Use &U) {
return TypeRule(RETURN_OPERAND, true, &U, true);
}
/// Establishes typedptr(typeof(return)) == typedptr(typeof(operand))
static TypeRule propagatesIndirect(User &U, unsigned OpIndex) {
return TypeRule::propagatesIndirect(U.getOperandUse(OpIndex));
}
};
/// This is a value that allows the ability to express the type of a value as
/// a whole in a typing rule.
static constexpr unsigned RETURN_OPERAND = ~0U;
/// Turn a type rule into an operand and a type to check for. If the type of
/// the operand and the type to check against cannot be unified, then a
/// bitcast will need to be inserted for the use.
std::pair<Use &, Type *> getTypeCheck(Instruction &I, const TypeRule &Rule);
/// Retrieve the list of typing rules for an instruction.
void getTypeRules(Instruction &I, SmallVectorImpl<TypeRule> &Rules);
/// Get the best guess for the type of the value, applying any type rules to
/// the return value of an instruction that exist. The return type may refer
/// to type variables that have yet to be resolved, if the type rules are
/// insufficient to establish a typed pointer type for the instruction.
Type *getTypeAfterRules(Value *V);
/// Enforce that the pointer element types of all operands of the instruction
/// matches the type that the instruction itself requires. If a pointer
/// element type of one of the operands is deferred, this will type the use
/// correctly.
void correctUseTypes(Instruction &I);
/// This assigns known pointer element types for the parameters of a function.
/// This method should be called for all functions before doing any type
/// analysis on the module.
void deduceFunctionType(Function &F);
/// This computes known type rules of a call to an LLVM intrinsic or specific
/// well-known function name. Returns true if the call was known to this
/// function.
bool typeIntrinsicCall(CallBase &CB, SmallVectorImpl<TypeRule> &TypeRules);
/// Get the type rules for checking argument and return value compatibility
/// for the function type being called. This is meant to help unify cases
/// for indirect function calls.
void typeFunctionParams(CallBase &CB, FunctionType *FT, unsigned ArgStart,
bool IncludeRet,
SmallVectorImpl<TypeRule> &TypeRules);
/// Compute the type of a global variable or global alias, based on the type
/// of the initializer (which may be null for global variables).
void typeGlobalValue(GlobalValue &GV, Constant *Init);
/// Compute pointer element types for all pertinent values in the module.
void typeModule(Module &M);
/// This stores a list of instructions whose pointer element types are
/// currently being investigated, to avoid the possibility of infinite cycles.
std::vector<Value *> VisitStack;
public:
explicit SPIRVTypeScavenger(Module &M) : UnifiedTypeVars(1024) {
typeModule(M);
}
/// Get the type of the value, with pointer types replaced with
/// TypedPointerType types instead.
Type *getScavengedType(Value *V);
/// Get the deduced function type for a function, with pointer types replaced
/// with TypedPointerTypes (maybe including type variables).
FunctionType *getFunctionType(Function *F);
};
#endif // SPIRVTYPESCAVENGER_H
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,308 @@
//===- SPIRVWriter.h - Converts LLVM to SPIR-V ------------------*- C++ -*-===//
//
// The LLVM/SPIR-V Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
//
// 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 Advanced Micro Devices, Inc., 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.
//
//===----------------------------------------------------------------------===//
/// \file
///
/// This file contains declaration of LLVMToSPIRV class which implements
/// conversion of LLVM intermediate language to SPIR-V
/// binary.
///
//===----------------------------------------------------------------------===//
#ifndef SPIRVWRITER_H
#define SPIRVWRITER_H
#include "LLVMToSPIRVDbgTran.h"
#include "OCLTypeToSPIRV.h"
#include "OCLUtil.h"
#include "SPIRVBasicBlock.h"
#include "SPIRVBuiltinHelper.h"
#include "SPIRVEntry.h"
#include "SPIRVEnum.h"
#include "SPIRVFunction.h"
#include "SPIRVInstruction.h"
#include "SPIRVModule.h"
#include "SPIRVType.h"
#include "SPIRVTypeScavenger.h"
#include "SPIRVValue.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Analysis/CallGraph.h"
#include "llvm/IR/IntrinsicInst.h"
#include <memory>
using namespace llvm;
using namespace SPIRV;
using namespace OCLUtil;
namespace SPIRV {
class LLVMToSPIRVBase : protected BuiltinCallHelper {
public:
LLVMToSPIRVBase(SPIRVModule *SMod);
LLVMToSPIRVBase(const LLVMToSPIRVBase &Other) = delete;
LLVMToSPIRVBase &operator=(const LLVMToSPIRVBase &Other) = delete;
LLVMToSPIRVBase(LLVMToSPIRVBase &&Other) = delete;
LLVMToSPIRVBase &operator=(LLVMToSPIRVBase &&Other) = delete;
bool runLLVMToSPIRV(Module &Mod);
// This enum sets the mode used to translate the value which is
// a function, that is necessary for a convenient function pointers handling.
// By default transValue uses 'Decl' mode, which means every function
// we meet during the translation should result in its declaration generated.
// In 'Pointer' mode we generate OpConstantFunctionPointerINTEL constant
// instead.
enum class FuncTransMode { Decl, Pointer };
SPIRVType *transType(Type *T);
SPIRVType *transPointerType(Type *PointeeTy, unsigned AddrSpace);
SPIRVType *transPointerType(SPIRVType *PointeeTy, unsigned AddrSpace);
SPIRVType *transSPIRVOpaqueType(StringRef STName, unsigned AddrSpace);
SPIRVType *
transSPIRVJointMatrixINTELType(SmallVector<std::string, 8> Postfixes);
/// Use the type scavenger to get the correct type for V. This is equivalent
/// to transType(V->getType()) if V is not a pointer type; otherwise, it tries
/// to pick an appropriate pointee type for V.
SPIRVType *transScavengedType(Value *V);
SPIRVValue *getTranslatedValue(const Value *) const;
spv::LoopControlMask getLoopControl(const BranchInst *Branch,
std::vector<SPIRVWord> &Parameters);
// Translation functions
bool transAddressingMode();
bool transAlign(Value *V, SPIRVValue *BV);
std::vector<SPIRVWord> transArguments(CallInst *, SPIRVBasicBlock *,
SPIRVEntry *);
bool transSourceLanguage();
bool transExtension();
bool transBuiltinSet();
bool isKnownIntrinsic(Intrinsic::ID Id);
SPIRVValue *transIntrinsicInst(IntrinsicInst *Intrinsic, SPIRVBasicBlock *BB);
enum class FPBuiltinType {
REGULAR_MATH,
EXT_1OPS,
EXT_2OPS,
EXT_3OPS,
UNKNOWN
};
FPBuiltinType getFPBuiltinType(IntrinsicInst *II, StringRef &);
SPIRVValue *transFPBuiltinIntrinsicInst(IntrinsicInst *II,
SPIRVBasicBlock *BB);
SPIRVValue *transFenceInst(FenceInst *FI, SPIRVBasicBlock *BB);
SPIRVValue *transCallInst(CallInst *Call, SPIRVBasicBlock *BB);
SPIRVValue *transDirectCallInst(CallInst *Call, SPIRVBasicBlock *BB);
SPIRVValue *transIndirectCallInst(CallInst *Call, SPIRVBasicBlock *BB);
SPIRVValue *transAsmINTEL(InlineAsm *Asm);
SPIRVValue *transAsmCallINTEL(CallInst *Call, SPIRVBasicBlock *BB);
bool transDecoration(Value *V, SPIRVValue *BV);
bool shouldTryToAddMemAliasingDecoration(Instruction *V);
void transMemAliasingINTELDecorations(Instruction *V, SPIRVValue *BV);
SPIRVWord transFunctionControlMask(Function *);
SPIRVFunction *transFunctionDecl(Function *F);
void transVectorComputeMetadata(Function *F);
void transFPGAFunctionMetadata(SPIRVFunction *BF, Function *F);
void transFunctionMetadataAsExecutionMode(SPIRVFunction *BF, Function *F);
void transFunctionMetadataAsUserSemanticDecoration(SPIRVFunction *BF,
Function *F);
void transAuxDataInst(SPIRVValue *BV, Value *V);
bool transGlobalVariables();
Op transBoolOpCode(SPIRVValue *Opn, Op OC);
// Translate LLVM module to SPIR-V module.
// Returns true if succeeds.
bool translate();
bool transExecutionMode();
void transFPContract();
SPIRVValue *transConstant(Value *V);
/// Translate a reference to a constant in a constant expression. This may
/// involve inserting extra bitcasts to correct type issues.
SPIRVValue *transConstantUse(Constant *V, SPIRVType *ExpectedType);
SPIRVValue *transValue(Value *V, SPIRVBasicBlock *BB,
bool CreateForward = true,
FuncTransMode FuncTrans = FuncTransMode::Decl);
void transGlobalAnnotation(GlobalVariable *V);
SPIRVValue *
transValueWithoutDecoration(Value *V, SPIRVBasicBlock *BB,
bool CreateForward = true,
FuncTransMode FuncTrans = FuncTransMode::Decl);
void transGlobalIOPipeStorage(GlobalVariable *V, MDNode *IO);
static SPIRVInstruction *applyRoundingModeConstraint(Value *V,
SPIRVInstruction *I);
typedef DenseMap<Type *, SPIRVType *> LLVMToSPIRVTypeMap;
typedef DenseMap<Value *, SPIRVValue *> LLVMToSPIRVValueMap;
typedef DenseMap<MDNode *, SmallSet<SPIRVId, 2>> LLVMToSPIRVMetadataMap;
void setOCLTypeToSPIRV(OCLTypeToSPIRVBase *OCLTypeToSPIRV) {
OCLTypeToSPIRVPtr = OCLTypeToSPIRV;
}
OCLTypeToSPIRVBase *getOCLTypeToSPIRV() { return OCLTypeToSPIRVPtr; }
~LLVMToSPIRVBase();
private:
Module *M;
LLVMContext *Ctx;
SPIRVModule *BM;
// This maps LLVM types (except for pointers) to SPIRVType.
LLVMToSPIRVTypeMap TypeMap;
// This maps {struct name, addrspace} to SPIRVType, for those structs that
// represent special SPIRV types.
DenseMap<std::pair<StringRef, unsigned>, SPIRVType *> OpaqueStructMap;
// This maps <type-unique keys> to SPIRVType, for use in function types.
StringMap<SPIRVType *> PointeeTypeMap;
/// Get the SPIRVFunctionType with appropriate return and argument types,
/// returning an existing instance if one has already been created. This is
/// necessary to unique locally, as SPIRVModule does not do such uniquing.
SPIRVType *getSPIRVFunctionType(SPIRVType *RT,
const std::vector<SPIRVType *> &Args);
LLVMToSPIRVValueMap ValueMap;
LLVMToSPIRVMetadataMap IndexGroupArrayMap;
SPIRVWord SrcLang;
SPIRVWord SrcLangVer;
std::unique_ptr<LLVMToSPIRVDbgTran> DbgTran;
std::unique_ptr<CallGraph> CG;
OCLTypeToSPIRVBase *OCLTypeToSPIRVPtr = nullptr;
std::vector<llvm::Instruction *> UnboundInst;
std::unique_ptr<SPIRVTypeScavenger> Scavenger;
enum class FPContract { UNDEF, DISABLED, ENABLED };
DenseMap<Function *, FPContract> FPContractMap;
FPContract getFPContract(Function *F);
bool joinFPContract(Function *F, FPContract C);
void fpContractUpdateRecursive(Function *F, FPContract FPC);
SPIRVType *mapType(Type *T, SPIRVType *BT);
SPIRVValue *mapValue(Value *V, SPIRVValue *BV);
SPIRVErrorLog &getErrorLog() { return BM->getErrorLog(); }
llvm::IntegerType *getSizetType(unsigned AS = 0);
std::vector<SPIRVValue *> transValue(const std::vector<Value *> &Values,
SPIRVBasicBlock *BB);
std::vector<SPIRVWord> transValue(const std::vector<Value *> &Values,
SPIRVBasicBlock *BB, SPIRVEntry *Entry);
SPIRVInstruction *transBinaryInst(BinaryOperator *B, SPIRVBasicBlock *BB);
SPIRVInstruction *transCmpInst(CmpInst *Cmp, SPIRVBasicBlock *BB);
SPIRVInstruction *transLifetimeIntrinsicInst(Op OC, IntrinsicInst *Intrinsic,
SPIRVBasicBlock *BB);
SPIRVValue *transAtomicStore(StoreInst *ST, SPIRVBasicBlock *BB);
SPIRVValue *transAtomicLoad(LoadInst *LD, SPIRVBasicBlock *BB);
void dumpUsers(Value *V);
template <class ExtInstKind>
bool oclGetExtInstIndex(const std::string &MangledName,
const std::string &DemangledName,
SPIRVWord *EntryPoint);
void oclGetMutatedArgumentTypesByBuiltin(
llvm::FunctionType *FT, std::unordered_map<unsigned, Type *> &ChangedType,
Function *F);
bool isBuiltinTransToInst(Function *F);
bool isBuiltinTransToExtInst(Function *F,
SPIRVExtInstSetKind *BuiltinSet = nullptr,
SPIRVWord *EntryPoint = nullptr,
SmallVectorImpl<std::string> *Dec = nullptr);
bool isKernel(Function *F);
bool transMetadata();
bool transOCLMetadata();
SPIRVInstruction *transBuiltinToInst(StringRef DemangledName, CallInst *CI,
SPIRVBasicBlock *BB);
SPIRVValue *transBuiltinToConstant(StringRef DemangledName, CallInst *CI);
SPIRVInstruction *transBuiltinToInstWithoutDecoration(Op OC, CallInst *CI,
SPIRVBasicBlock *BB);
void
mutateFuncArgType(const std::unordered_map<unsigned, Type *> &ChangedType,
Function *F);
SPIRVValue *transSpcvCast(CallInst *CI, SPIRVBasicBlock *BB);
SPIRVValue *oclTransSpvcCastSampler(CallInst *CI, SPIRVBasicBlock *BB);
SPIRVValue *transUnaryInst(UnaryInstruction *U, SPIRVBasicBlock *BB);
void transFunction(Function *I);
SPIRV::SPIRVLinkageTypeKind transLinkageType(const GlobalValue *GV);
bool isAnyFunctionReachableFromFunction(
const Function *FS,
const std::unordered_set<const Function *> Funcs) const;
std::vector<SPIRVId> collectEntryPointInterfaces(SPIRVFunction *BF,
Function *F);
};
class LLVMToSPIRVPass : public PassInfoMixin<LLVMToSPIRVPass> {
public:
LLVMToSPIRVPass(SPIRVModule *SMod) : SMod(SMod) {}
llvm::PreservedAnalyses run(llvm::Module &M,
llvm::ModuleAnalysisManager &MAM) {
LLVMToSPIRVBase PassInstance(SMod);
PassInstance.setOCLTypeToSPIRV(&MAM.getResult<OCLTypeToSPIRVPass>(M));
return PassInstance.runLLVMToSPIRV(M) ? llvm::PreservedAnalyses::none()
: llvm::PreservedAnalyses::all();
}
static bool isRequired() { return true; }
private:
SPIRVModule *SMod;
};
class LLVMToSPIRVLegacy : public ModulePass, public LLVMToSPIRVBase {
public:
LLVMToSPIRVLegacy(SPIRVModule *SMod = nullptr)
: ModulePass(ID), LLVMToSPIRVBase(SMod) {}
virtual StringRef getPassName() const override { return "LLVMToSPIRV"; }
bool runOnModule(Module &Mod) override {
setOCLTypeToSPIRV(&getAnalysis<OCLTypeToSPIRVLegacy>());
return runLLVMToSPIRV(Mod);
}
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.addRequired<OCLTypeToSPIRVLegacy>();
}
static char ID;
};
} // namespace SPIRV
#endif // SPIRVWRITER_H
@@ -0,0 +1,62 @@
//===- SPIRVWriterPass.cpp - SPIRV writing pass -----------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// SPIRVWriterPass implementation.
//
//===----------------------------------------------------------------------===//
#include "SPIRVWriterPass.h"
#include "LLVMSPIRVLib.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Pass.h"
using namespace llvm;
PreservedAnalyses SPIRVWriterPass::run(Module &M) {
// FIXME: at the moment LLVM/SPIR-V translation errors are ignored.
std::string Err;
writeSpirv(&M, Opts, OS, Err);
return PreservedAnalyses::all();
}
namespace {
class WriteSPIRVPass : public ModulePass {
std::ostream &OS; // std::ostream to print on
SPIRV::TranslatorOpts Opts;
public:
static char ID; // Pass identification, replacement for typeid
WriteSPIRVPass(std::ostream &OS, const SPIRV::TranslatorOpts &Opts)
: ModulePass(ID), OS(OS), Opts(Opts) {}
StringRef getPassName() const override { return "SPIRV Writer"; }
bool runOnModule(Module &M) override {
// FIXME: at the moment LLVM/SPIR-V translation errors are ignored.
std::string Err;
writeSpirv(&M, Opts, OS, Err);
return false;
}
};
} // namespace
char WriteSPIRVPass::ID = 0;
ModulePass *llvm::createSPIRVWriterPass(std::ostream &Str) {
SPIRV::TranslatorOpts DefaultOpts;
// To preserve old behavior of the translator, let's enable all extensions
// by default in this API
DefaultOpts.enableAllExtensions();
return createSPIRVWriterPass(Str, DefaultOpts);
}
ModulePass *llvm::createSPIRVWriterPass(std::ostream &Str,
const SPIRV::TranslatorOpts &Opts) {
return new WriteSPIRVPass(Str, Opts);
}
@@ -0,0 +1,62 @@
//===------ SPIRVWriterPass.h - SPIRV writing pass --------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
/// \file
///
/// This file provides a SPIRV writing pass.
///
//===----------------------------------------------------------------------===//
#ifndef SPIRV_SPIRVWRITERPASS_H
#define SPIRV_SPIRVWRITERPASS_H
#include "LLVMSPIRVOpts.h"
#include "llvm/ADT/StringRef.h"
namespace llvm {
class Module;
class ModulePass;
class PreservedAnalyses;
/// \brief Create and return a pass that writes the module to the specified
/// ostream. Note that this pass is designed for use with the legacy pass
/// manager.
ModulePass *createSPIRVWriterPass(std::ostream &Str);
/// \brief Create and return a pass that writes the module to the specified
/// ostream. Note that this pass is designed for use with the legacy pass
/// manager.
ModulePass *createSPIRVWriterPass(std::ostream &Str,
const SPIRV::TranslatorOpts &Opts);
/// \brief Pass for writing a module of IR out to a SPIRV file.
///
/// Note that this is intended for use with the new pass manager. To construct
/// a pass for the legacy pass manager, use the function above.
class SPIRVWriterPass {
std::ostream &OS;
SPIRV::TranslatorOpts Opts;
public:
/// \brief Construct a SPIRV writer pass around a particular output stream.
explicit SPIRVWriterPass(std::ostream &OS) : OS(OS) {
Opts.enableAllExtensions();
}
SPIRVWriterPass(std::ostream &OS, const SPIRV::TranslatorOpts &Opts)
: OS(OS), Opts(Opts) {}
/// \brief Run the SPIRV writer pass, and output the module to the selected
/// output stream.
PreservedAnalyses run(Module &M);
static StringRef name() { return "SPIRVWriterPass"; }
};
} // namespace llvm
#endif // SPIRV_SPIRVWRITERPASS_H
@@ -0,0 +1,161 @@
//=- VectorComputeUtil.cpp - vector compute utilities implemetation * C++ -*-=//
//
// The LLVM/SPIR-V Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2020 Intel Corporation. All rights reserved.
//
// 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 Intel Corporation, 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.
//
//===----------------------------------------------------------------------===//
//
// This file implements translation of VC float control bits
//
//===----------------------------------------------------------------------===//
#include "VectorComputeUtil.h"
#include "SPIRVInternal.h"
#include "llvm/IR/Metadata.h"
using namespace VectorComputeUtil;
using namespace SPIRV;
enum VCFloatControl {
VC_RTE = 0, // Round to nearest or even
VC_RTP = 1 << 4, // Round towards +ve inf
VC_RTN = 2 << 4, // Round towards -ve inf
VC_RTZ = 3 << 4, // Round towards zero
VC_DENORM_FTZ = 0, // Denorm mode flush to zero
VC_DENORM_D_ALLOW = 1 << 6, // Denorm mode double allow
VC_DENORM_F_ALLOW = 1 << 7, // Denorm mode float allow
VC_DENORM_HF_ALLOW = 1 << 10, // Denorm mode half allow
VC_FLOAT_MODE_IEEE = 0, // Single precision float IEEE mode
VC_FLOAT_MODE_ALT = 1 // Single precision float ALT mode
};
enum VCFloatControlMask {
VC_ROUND_MASK = (VC_RTE | VC_RTP | VC_RTN | VC_RTZ),
VC_FLOAT_MASK = (VC_FLOAT_MODE_IEEE | VC_FLOAT_MODE_ALT)
};
typedef SPIRVMap<FPRoundingMode, VCFloatControl> FPRoundingModeControlBitMap;
typedef SPIRVMap<FPOperationMode, VCFloatControl> FPOperationModeControlBitMap;
typedef SPIRVMap<VCFloatType, VCFloatControl> VCFloatTypeDenormMaskMap;
template <> inline void SPIRVMap<FPRoundingMode, VCFloatControl>::init() {
add(spv::FPRoundingModeRTE, VC_RTE);
add(spv::FPRoundingModeRTP, VC_RTP);
add(spv::FPRoundingModeRTN, VC_RTN);
add(spv::FPRoundingModeRTZ, VC_RTZ);
}
template <> inline void SPIRVMap<FPOperationMode, VCFloatControl>::init() {
add(spv::FPOperationModeIEEE, VC_FLOAT_MODE_IEEE);
add(spv::FPOperationModeALT, VC_FLOAT_MODE_ALT);
}
template <> inline void SPIRVMap<VCFloatType, VCFloatControl>::init() {
add(Double, VC_DENORM_D_ALLOW);
add(Float, VC_DENORM_F_ALLOW);
add(Half, VC_DENORM_HF_ALLOW);
}
namespace VectorComputeUtil {
FPRoundingMode getFPRoundingMode(unsigned FloatControl) noexcept {
return FPRoundingModeControlBitMap::rmap(
VCFloatControl(VC_ROUND_MASK & FloatControl));
}
FPDenormMode getFPDenormMode(unsigned FloatControl,
VCFloatType FloatType) noexcept {
VCFloatControl DenormMask =
VCFloatTypeDenormMaskMap::map(FloatType); // 1 Bit mask
return (DenormMask == (DenormMask & FloatControl))
? spv::FPDenormModePreserve
: spv::FPDenormModeFlushToZero;
}
FPOperationMode getFPOperationMode(unsigned FloatControl) noexcept {
return FPOperationModeControlBitMap::rmap(
VCFloatControl(VC_FLOAT_MASK & FloatControl));
}
unsigned getVCFloatControl(FPRoundingMode RoundMode) noexcept {
return FPRoundingModeControlBitMap::map(RoundMode);
}
unsigned getVCFloatControl(FPOperationMode FloatMode) noexcept {
return FPOperationModeControlBitMap::map(FloatMode);
}
unsigned getVCFloatControl(FPDenormMode DenormMode,
VCFloatType FloatType) noexcept {
if (DenormMode == spv::FPDenormModePreserve)
return VCFloatTypeDenormMaskMap::map(FloatType);
return VC_DENORM_FTZ;
}
SPIRVStorageClassKind
getVCGlobalVarStorageClass(SPIRAddressSpace AddressSpace) noexcept {
switch (AddressSpace) {
case SPIRAS_Private:
return StorageClassPrivate;
case SPIRAS_Local:
return StorageClassWorkgroup;
case SPIRAS_Global:
return StorageClassCrossWorkgroup;
case SPIRAS_Constant:
return StorageClassUniformConstant;
default:
assert(false && "Unexpected address space");
return StorageClassPrivate;
}
}
SPIRAddressSpace
getVCGlobalVarAddressSpace(SPIRVStorageClassKind StorageClass) noexcept {
switch (StorageClass) {
case StorageClassPrivate:
return SPIRAS_Private;
case StorageClassWorkgroup:
return SPIRAS_Local;
case StorageClassCrossWorkgroup:
return SPIRAS_Global;
case StorageClassUniformConstant:
return SPIRAS_Constant;
default:
assert(false && "Unexpected storage class");
return SPIRAS_Private;
}
}
std::string getVCBufferSurfaceName() {
return std::string(kVCType::VCBufferSurface) + kAccessQualPostfix::Type;
}
std::string getVCBufferSurfaceName(SPIRVAccessQualifierKind Access) {
return std::string(kVCType::VCBufferSurface) +
getAccessQualifierPostfix(Access).str() + kAccessQualPostfix::Type;
}
} // namespace VectorComputeUtil
@@ -0,0 +1,154 @@
//=- VectorComputeUtil.h - vector compute utilities declarations -*- C++ -*-=//
//
// The LLVM/SPIR-V Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2020 Intel Corporation. All rights reserved.
//
// 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 Intel Corporation, 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.
//
//===----------------------------------------------------------------------===//
//
// This file declares translation of VectorComputeUtil float control bits,
// and VC kernel metadata
//
//===----------------------------------------------------------------------===//
#ifndef SPIRV_VCUTIL_H
#define SPIRV_VCUTIL_H
#include "SPIRVInternal.h"
#include "SPIRVUtil.h"
#include "spirv/unified1/spirv.hpp"
namespace VectorComputeUtil {
///////////////////////////////////////////////////////////////////////////////
//
// Types
//
///////////////////////////////////////////////////////////////////////////////
enum VCFloatType {
Double,
Float,
Half,
};
FPRoundingMode getFPRoundingMode(unsigned FloatControl) noexcept;
FPDenormMode getFPDenormMode(unsigned FloatControl,
VCFloatType FloatType) noexcept;
FPOperationMode getFPOperationMode(unsigned FloatControl) noexcept;
unsigned getVCFloatControl(FPRoundingMode RoundMode) noexcept;
unsigned getVCFloatControl(FPOperationMode FloatMode) noexcept;
unsigned getVCFloatControl(FPDenormMode DenormMode,
VCFloatType FloatType) noexcept;
typedef SPIRV::SPIRVMap<FPRoundingMode, spv::ExecutionMode>
FPRoundingModeExecModeMap;
typedef SPIRV::SPIRVMap<FPOperationMode, spv::ExecutionMode>
FPOperationModeExecModeMap;
typedef SPIRV::SPIRVMap<FPDenormMode, spv::ExecutionMode>
FPDenormModeExecModeMap;
typedef SPIRV::SPIRVMap<VCFloatType, unsigned> VCFloatTypeSizeMap;
///////////////////////////////////////////////////////////////////////////////
//
// Functions
//
///////////////////////////////////////////////////////////////////////////////
SPIRVStorageClassKind
getVCGlobalVarStorageClass(SPIRAddressSpace AddressSpace) noexcept;
SPIRAddressSpace
getVCGlobalVarAddressSpace(SPIRVStorageClassKind StorageClass) noexcept;
std::string getVCBufferSurfaceName();
std::string getVCBufferSurfaceName(SPIRVAccessQualifierKind Access);
} // namespace VectorComputeUtil
///////////////////////////////////////////////////////////////////////////////
//
// Constants
//
///////////////////////////////////////////////////////////////////////////////
namespace kVCMetadata {
const static char VCFunction[] = "VCFunction";
const static char VCStackCall[] = "VCStackCall";
const static char VCArgumentIOKind[] = "VCArgumentIOKind";
const static char VCFloatControl[] = "VCFloatControl";
const static char VCSLMSize[] = "VCSLMSize";
const static char VCGlobalVariable[] = "VCGlobalVariable";
const static char VCVolatile[] = "VCVolatile";
const static char VCByteOffset[] = "VCByteOffset";
const static char VCSIMTCall[] = "VCSIMTCall";
const static char VCCallable[] = "VCCallable";
const static char VCSingleElementVector[] = "VCSingleElementVector";
const static char VCFCEntry[] = "VCFCEntry";
const static char VCMediaBlockIO[] = "VCMediaBlockIO";
const static char VCNamedBarrierCount[] = "VCNamedBarrierCount";
} // namespace kVCMetadata
namespace kVCType {
const static char VCBufferSurface[] = "intel.buffer";
}
///////////////////////////////////////////////////////////////////////////////
//
// Map definitions
//
///////////////////////////////////////////////////////////////////////////////
namespace SPIRV {
template <>
inline void SPIRVMap<spv::FPRoundingMode, spv::ExecutionMode>::init() {
add(spv::FPRoundingModeRTE, spv::ExecutionModeRoundingModeRTE);
add(spv::FPRoundingModeRTZ, spv::ExecutionModeRoundingModeRTZ);
add(spv::FPRoundingModeRTP, spv::ExecutionModeRoundingModeRTPINTEL);
add(spv::FPRoundingModeRTN, spv::ExecutionModeRoundingModeRTNINTEL);
}
template <>
inline void SPIRVMap<spv::FPDenormMode, spv::ExecutionMode>::init() {
add(spv::FPDenormModeFlushToZero, spv::ExecutionModeDenormFlushToZero);
add(spv::FPDenormModePreserve, spv::ExecutionModeDenormPreserve);
}
template <>
inline void SPIRVMap<spv::FPOperationMode, spv::ExecutionMode>::init() {
add(spv::FPOperationModeIEEE, spv::ExecutionModeFloatingPointModeIEEEINTEL);
add(spv::FPOperationModeALT, spv::ExecutionModeFloatingPointModeALTINTEL);
}
template <>
inline void SPIRVMap<VectorComputeUtil::VCFloatType, unsigned>::init() {
add(VectorComputeUtil::Double, 64);
add(VectorComputeUtil::Float, 32);
add(VectorComputeUtil::Half, 16);
}
} // namespace SPIRV
#endif // SPIRV_VCUTIL_H
@@ -0,0 +1,34 @@
/*
** Copyright (c) 2023 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a copy
** of this software and/or associated documentation files (the "Materials"),
** to deal in the Materials without restriction, including without limitation
** the rights to use, copy, modify, merge, publish, distribute, sublicense,
** and/or sell copies of the Materials, and to permit persons to whom the
** Materials are furnished to do so, subject to the following conditions:
**
** The above copyright notice and this permission notice shall be included in
** all copies or substantial portions of the Materials.
**
** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/
**
** THE MATERIALS ARE 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 AUTHORS 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 MATERIALS OR THE USE OR OTHER DEALINGS
** IN THE MATERIALS.
*/
namespace NonSemanticAuxData {
enum Instruction {
FunctionMetadata = 0,
FunctionAttribute = 1,
GlobalVariableMetadata = 2,
GlobalVariableAttribute = 3
};
} // namespace NonSemanticAuxData
@@ -0,0 +1,233 @@
// clang-format off
/*
** Copyright (c) 2015 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a copy
** of this software and/or associated documentation files (the "Materials"),
** to deal in the Materials without restriction, including without limitation
** the rights to use, copy, modify, merge, publish, distribute, sublicense,
** and/or sell copies of the Materials, and to permit persons to whom the
** Materials are furnished to do so, subject to the following conditions:
**
** The above copyright notice and this permission notice shall be included in
** all copies or substantial portions of the Materials.
**
** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/
**
** THE MATERIALS ARE 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 AUTHORS 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 MATERIALS OR THE USE OR OTHER DEALINGS
** IN THE MATERIALS.
*/
//
// Author: Boaz Ouriel, Intel
//
namespace OpenCLLIB {
enum Entrypoints {
// math functions
Acos = 0,
Acosh = 1,
Acospi = 2,
Asin = 3,
Asinh = 4,
Asinpi = 5,
Atan = 6,
Atan2 = 7,
Atanh = 8,
Atanpi = 9,
Atan2pi = 10,
Cbrt = 11,
Ceil = 12,
Copysign = 13,
Cos = 14,
Cosh = 15,
Cospi = 16,
Erfc = 17,
Erf = 18,
Exp = 19,
Exp2 = 20,
Exp10 = 21,
Expm1 = 22,
Fabs = 23,
Fdim = 24,
Floor = 25,
Fma = 26,
Fmax = 27,
Fmin = 28,
Fmod = 29,
Fract = 30,
Frexp = 31,
Hypot = 32,
Ilogb = 33,
Ldexp = 34,
Lgamma = 35,
Lgamma_r = 36,
Log = 37,
Log2 = 38,
Log10 = 39,
Log1p = 40,
Logb = 41,
Mad = 42,
Maxmag = 43,
Minmag = 44,
Modf = 45,
Nan = 46,
Nextafter = 47,
Pow = 48,
Pown = 49,
Powr = 50,
Remainder = 51,
Remquo = 52,
Rint = 53,
Rootn = 54,
Round = 55,
Rsqrt = 56,
Sin = 57,
Sincos = 58,
Sinh = 59,
Sinpi = 60,
Sqrt = 61,
Tan = 62,
Tanh = 63,
Tanpi = 64,
Tgamma = 65,
Trunc = 66,
Half_cos = 67,
Half_divide = 68,
Half_exp = 69,
Half_exp2 = 70,
Half_exp10 = 71,
Half_log = 72,
Half_log2 = 73,
Half_log10 = 74,
Half_powr = 75,
Half_recip = 76,
Half_rsqrt = 77,
Half_sin = 78,
Half_sqrt = 79,
Half_tan = 80,
Native_cos = 81,
Native_divide = 82,
Native_exp = 83,
Native_exp2 = 84,
Native_exp10 = 85,
Native_log = 86,
Native_log2 = 87,
Native_log10 = 88,
Native_powr = 89,
Native_recip = 90,
Native_rsqrt = 91,
Native_sin = 92,
Native_sqrt = 93,
Native_tan = 94,
// Common
FClamp = 95,
Degrees = 96,
FMax_common = 97,
FMin_common = 98,
Mix = 99,
Radians = 100,
Step = 101,
Smoothstep = 102,
Sign = 103,
// Geometrics
Cross = 104,
Distance = 105,
Length = 106,
Normalize = 107,
Fast_distance = 108,
Fast_length = 109,
Fast_normalize = 110,
// Integers
SAbs = 141,
SAbs_diff = 142,
SAdd_sat = 143,
UAdd_sat = 144,
SHadd = 145,
UHadd = 146,
SRhadd = 147,
URhadd = 148,
SClamp = 149,
UClamp = 150,
Clz = 151,
Ctz = 152,
SMad_hi = 153,
UMad_sat = 154,
SMad_sat = 155,
SMax = 156,
UMax = 157,
SMin = 158,
UMin = 159,
SMul_hi = 160,
Rotate = 161,
SSub_sat = 162,
USub_sat = 163,
U_Upsample = 164,
S_Upsample = 165,
Popcount = 166,
SMad24 = 167,
UMad24 = 168,
SMul24 = 169,
UMul24 = 170,
// Vector Loads/Stores
Vloadn = 171,
Vstoren = 172,
Vload_half = 173,
Vload_halfn = 174,
Vstore_half = 175,
Vstore_half_r = 176,
Vstore_halfn = 177,
Vstore_halfn_r = 178,
Vloada_halfn = 179,
Vstorea_halfn = 180,
Vstorea_halfn_r = 181,
// Vector Misc
Shuffle = 182,
Shuffle2 = 183,
//
Printf = 184,
Prefetch = 185,
// Relationals
Bitselect = 186,
Select = 187,
// pipes
Read_pipe = 188,
Write_pipe = 189,
Reserve_read_pipe = 190,
Reserve_write_pipe = 191,
Commit_read_pipe = 192,
Commit_write_pipe = 193,
Is_valid_reserve_id = 194,
Work_group_reserve_read_pipe = 195,
Work_group_reserve_write_pipe = 196,
Work_group_commit_read_pipe = 197,
Work_group_commit_write_pipe = 198,
Get_pipe_num_packets = 199,
Get_pipe_max_packets = 200,
// more integers
UAbs = 201,
UAbs_diff = 202,
UMul_hi = 203,
UMad_hi = 204,
};
} // end namespace OpenCL20
// clang-format on
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,142 @@
//===- SPIRVAsm.h - --*- C++ -*-===//
//
// The LLVM/SPIRV Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
/// \file
///
/// This file defines the inline assembler entries defined in SPIRV spec with op
/// codes.
///
//===----------------------------------------------------------------------===//
#ifndef SPIRV_LIBSPIRV_SPIRVASM_H
#define SPIRV_LIBSPIRV_SPIRVASM_H
#include "SPIRVEntry.h"
#include "SPIRVInstruction.h"
#include "SPIRVValue.h"
namespace SPIRV {
class SPIRVAsmTargetINTEL : public SPIRVEntry {
public:
static const SPIRVWord FixedWC = 2;
static const Op OC = OpAsmTargetINTEL;
// Complete constructor
SPIRVAsmTargetINTEL(SPIRVModule *M, SPIRVId TheId,
const std::string &TheTarget)
: SPIRVEntry(M, FixedWC + getSizeInWords(TheTarget), OC, TheId),
Target(TheTarget) {
validate();
}
// Incomplete constructor
SPIRVAsmTargetINTEL() : SPIRVEntry(OC) {}
SPIRVCapVec getRequiredCapability() const override {
return getVec(CapabilityAsmINTEL);
}
std::optional<ExtensionID> getRequiredExtension() const override {
return ExtensionID::SPV_INTEL_inline_assembly;
}
const std::string &getTarget() const { return Target; }
protected:
void validate() const override {
SPIRVEntry::validate();
assert(WordCount > FixedWC);
assert(OpCode == OC);
}
_SPIRV_DEF_ENCDEC2(Id, Target)
std::string Target;
};
class SPIRVAsmINTEL : public SPIRVValue {
public:
static const SPIRVWord FixedWC = 5;
static const Op OC = OpAsmINTEL;
// Complete constructor
SPIRVAsmINTEL(SPIRVModule *M, SPIRVTypeFunction *TheFunctionType,
SPIRVId TheId, SPIRVAsmTargetINTEL *TheTarget,
const std::string &TheInstructions,
const std::string &TheConstraints)
: SPIRVValue(M,
FixedWC + getSizeInWords(TheInstructions) +
getSizeInWords(TheConstraints),
OC, TheFunctionType->getReturnType(), TheId),
Target(TheTarget), FunctionType(TheFunctionType),
Instructions(TheInstructions), Constraints(TheConstraints) {
validate();
}
// Incomplete constructor
SPIRVAsmINTEL() : SPIRVValue(OC) {}
SPIRVCapVec getRequiredCapability() const override {
return getVec(CapabilityAsmINTEL);
}
std::optional<ExtensionID> getRequiredExtension() const override {
return ExtensionID::SPV_INTEL_inline_assembly;
}
const std::string &getInstructions() const { return Instructions; }
const std::string &getConstraints() const { return Constraints; }
SPIRVTypeFunction *getFunctionType() const { return FunctionType; }
protected:
_SPIRV_DEF_ENCDEC6(Type, Id, FunctionType, Target, Instructions, Constraints)
void validate() const override {
SPIRVValue::validate();
assert(WordCount > FixedWC);
assert(OpCode == OC);
}
SPIRVAsmTargetINTEL *Target = nullptr;
SPIRVTypeFunction *FunctionType = nullptr;
std::string Instructions;
std::string Constraints;
};
class SPIRVAsmCallINTEL : public SPIRVInstruction {
public:
static const SPIRVWord FixedWC = 4;
static const Op OC = OpAsmCallINTEL;
// Complete constructor
SPIRVAsmCallINTEL(SPIRVId TheId, SPIRVAsmINTEL *TheAsm,
const std::vector<SPIRVWord> &TheArgs,
SPIRVBasicBlock *TheBB)
: SPIRVInstruction(FixedWC + TheArgs.size(), OC, TheAsm->getType(), TheId,
TheBB),
Asm(TheAsm), Args(TheArgs) {
validate();
}
// Incomplete constructor
SPIRVAsmCallINTEL() : SPIRVInstruction(OC) {}
SPIRVCapVec getRequiredCapability() const override {
return getVec(CapabilityAsmINTEL);
}
std::optional<ExtensionID> getRequiredExtension() const override {
return ExtensionID::SPV_INTEL_inline_assembly;
}
bool isOperandLiteral(unsigned int Index) const override { return false; }
void setWordCount(SPIRVWord TheWordCount) override {
SPIRVEntry::setWordCount(TheWordCount);
Args.resize(TheWordCount - FixedWC);
}
const std::vector<SPIRVWord> &getArguments() const { return Args; }
SPIRVAsmINTEL *getAsm() const { return Asm; }
protected:
_SPIRV_DEF_ENCDEC4(Type, Id, Asm, Args)
void validate() const override {
SPIRVInstruction::validate();
assert(WordCount >= FixedWC);
assert(OpCode == OC);
assert(getBasicBlock() && "Invalid BB");
assert(getBasicBlock()->getModule() == Asm->getModule());
}
SPIRVAsmINTEL *Asm = nullptr;
std::vector<SPIRVWord> Args;
};
} // namespace SPIRV
#endif // SPIRV_LIBSPIRV_SPIRVASM_H
@@ -0,0 +1,107 @@
//===- SPIRVBasicBlock.cpp - SPIR-V Basic Block -----------------*- C++ -*-===//
//
// The LLVM/SPIRV Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
//
// 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 Advanced Micro Devices, Inc., 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.
//
//===----------------------------------------------------------------------===//
/// \file
///
/// This file implements SPIRV basic block.
///
//===----------------------------------------------------------------------===//
#include "SPIRVBasicBlock.h"
#include "SPIRVEntry.h"
#include "SPIRVFunction.h"
#include "SPIRVInstruction.h"
#include "SPIRVStream.h"
#include "SPIRVValue.h"
#include <iostream>
using namespace SPIRV;
SPIRVBasicBlock::SPIRVBasicBlock(SPIRVId TheId, SPIRVFunction *Func)
: SPIRVValue(Func->getModule(), 2, OpLabel, TheId), ParentF(Func) {
setAttr();
validate();
}
SPIRVDecoder SPIRVBasicBlock::getDecoder(std::istream &IS) {
return SPIRVDecoder(IS, *this);
}
/// Assume I contains valid Id.
SPIRVInstruction *
SPIRVBasicBlock::addInstruction(SPIRVInstruction *I,
const SPIRVInstruction *InsertBefore) {
assert(I && "Invalid instruction");
Module->add(I);
I->setParent(this);
if (InsertBefore) {
auto Pos = find(InsertBefore);
// If insertion of a new instruction before the one passed to the function
// is illegal, insertion before the returned instruction is guaranteed
// to retain correct instruction order in a block
if (Pos != InstVec.begin() && (isa<OpLoopMerge>(*std::prev(Pos)) ||
isa<OpLoopControlINTEL>(*std::prev(Pos))))
--Pos;
InstVec.insert(Pos, I);
} else
InstVec.push_back(I);
return I;
}
void SPIRVBasicBlock::encodeChildren(spv_ostream &O) const {
O << SPIRVNL();
for (size_t I = 0, E = InstVec.size(); I != E; ++I)
O << *InstVec[I];
}
_SPIRV_IMP_ENCDEC1(SPIRVBasicBlock, Id)
SPIRVInstruction *SPIRVBasicBlock::getVariableInsertionPoint() const {
auto IP =
std::find_if(InstVec.begin(), InstVec.end(), [](SPIRVInstruction *Inst) {
return !(isa<OpVariable>(Inst) || isa<OpLine>(Inst) ||
isa<OpNoLine>(Inst) ||
// Note: OpVariable and OpPhi instructions do not belong to the
// same block in a valid SPIR-V module.
isa<OpPhi>(Inst) || isa<OpUntypedVariableKHR>(Inst));
});
if (IP == InstVec.end())
return nullptr;
return *IP;
}
void SPIRVBasicBlock::setScope(SPIRVEntry *Scope) {
assert(Scope && Scope->getOpCode() == OpFunction && "Invalid scope");
setParent(static_cast<SPIRVFunction *>(Scope));
}
@@ -0,0 +1,122 @@
//===- SPIRVBasicBlock.h - SPIR-V Basic Block -------------------*- C++ -*-===//
//
// The LLVM/SPIRV Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
//
// 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 Advanced Micro Devices, Inc., 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.
//
//===----------------------------------------------------------------------===//
/// \file
///
/// This file defines Basic Block class for SPIR-V.
///
//===----------------------------------------------------------------------===//
#ifndef SPIRV_LIBSPIRV_SPIRVBASICBLOCK_H
#define SPIRV_LIBSPIRV_SPIRVBASICBLOCK_H
#include "SPIRVValue.h"
#include <algorithm>
namespace SPIRV {
class SPIRVFunction;
class SPIRVInstruction;
class SPIRVDecoder;
class SPIRVBasicBlock : public SPIRVValue {
public:
SPIRVBasicBlock(SPIRVId TheId, SPIRVFunction *Func);
SPIRVBasicBlock() : SPIRVValue(OpLabel), ParentF(NULL) { setAttr(); }
SPIRVDecoder getDecoder(std::istream &IS) override;
SPIRVFunction *getParent() const { return ParentF; }
size_t getNumInst() const { return InstVec.size(); }
SPIRVInstruction *getInst(size_t I) const { return InstVec[I]; }
SPIRVInstruction *getPrevious(const SPIRVInstruction *I) const {
auto Loc = find(I);
if (Loc == InstVec.end() || Loc == InstVec.begin())
return nullptr;
return *(--Loc);
}
SPIRVInstruction *getNext(const SPIRVInstruction *I) const {
auto Loc = find(I);
if (Loc == InstVec.end())
return nullptr;
++Loc;
if (Loc == InstVec.end())
return nullptr;
return *Loc;
}
// Return the last instruction in the BB or nullptr if the BB is empty.
const SPIRVInstruction *getTerminateInstr() const {
return InstVec.empty() ? nullptr : InstVec.back();
}
// Variables must be the first instructions in the block,
// intermixed with OpLine and OpNoLine instructions. Return first instruction
// not being an OpVariable, OpUntypedVariableKHR, OpLine or OpNoLine.
SPIRVInstruction *getVariableInsertionPoint() const;
void setScope(SPIRVEntry *Scope) override;
void setParent(SPIRVFunction *F) { ParentF = F; }
SPIRVInstruction *
addInstruction(SPIRVInstruction *I,
const SPIRVInstruction *InsertBefore = nullptr);
void eraseInstruction(const SPIRVInstruction *I) {
auto Loc = find(I);
assert(Loc != InstVec.end());
InstVec.erase(Loc);
}
void setAttr() { setHasNoType(); }
_SPIRV_DCL_ENCDEC
void encodeChildren(spv_ostream &) const override;
void validate() const override {
SPIRVValue::validate();
assert(ParentF && "Invalid parent function");
}
private:
SPIRVFunction *ParentF;
typedef std::vector<SPIRVInstruction *> SPIRVInstructionVector;
SPIRVInstructionVector InstVec;
SPIRVInstructionVector::const_iterator
find(const SPIRVInstruction *Inst) const {
return std::find(InstVec.begin(), InstVec.end(), Inst);
}
SPIRVInstructionVector::iterator find(const SPIRVInstruction *Inst) {
return std::find(InstVec.begin(), InstVec.end(), Inst);
}
};
typedef SPIRVBasicBlock SPIRVLabel;
} // namespace SPIRV
#endif // SPIRV_LIBSPIRV_SPIRVBASICBLOCK_H
@@ -0,0 +1,73 @@
//===- SPIRVDebug.cpp - SPIR-V Debug Utility --------------------*- C++ -*-===//
//
// The LLVM/SPIRV Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
//
// 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 Advanced Micro Devices, Inc., 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.
//
//===----------------------------------------------------------------------===//
/// \file
///
/// This file defines variables for enabling/disabling SPIR-V debug macro.
///
//===----------------------------------------------------------------------===//
#include "SPIRVDebug.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#define DEBUG_TYPE "spirv-regularization"
using namespace SPIRV;
bool SPIRV::SPIRVDbgEnable = false;
SPIRV::SPIRVDbgErrorHandlingKinds SPIRV::SPIRVDbgError =
SPIRVDbgErrorHandlingKinds::Exit;
bool SPIRV::SPIRVDbgErrorMsgIncludesSourceInfo = true;
namespace SPIRV {
llvm::cl::opt<bool> VerifyRegularizationPasses(
"spirv-verify-regularize-passes", llvm::cl::init(_SPIRVDBG),
llvm::cl::desc(
"Verify module after each pass in LLVM regularization phase"));
void verifyRegularizationPass(llvm::Module &M, const std::string &PassName) {
if (VerifyRegularizationPasses) {
std::string Err;
llvm::raw_string_ostream ErrorOS(Err);
if (llvm::verifyModule(M, &ErrorOS)) {
LLVM_DEBUG(llvm::errs()
<< "Failed to verify module after pass: " << PassName << "\n"
<< ErrorOS.str());
}
}
}
} // namespace SPIRV
@@ -0,0 +1,114 @@
//===- SPIRVDebug.h - SPIR-V Debug Utility ----------------------*- C++ -*-===//
//
// The LLVM/SPIRV Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
//
// 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 Advanced Micro Devices, Inc., 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.
//
//===----------------------------------------------------------------------===//
/// \file
///
/// This file defines Macros and variables for debugging SPIRV.
///
//===----------------------------------------------------------------------===//
#ifndef SPIRV_LIBSPIRV_SPIRVDEBUG_H
#define SPIRV_LIBSPIRV_SPIRVDEBUG_H
#include "SPIRVUtil.h"
#include <iostream>
#include <string>
namespace llvm {
class Module;
}
namespace SPIRV {
// Include source file and line number in error message.
extern bool SPIRVDbgErrorMsgIncludesSourceInfo;
// Enable assert or exit on error
enum class SPIRVDbgErrorHandlingKinds { Abort, Exit, Ignore };
extern SPIRVDbgErrorHandlingKinds SPIRVDbgError;
// Enable debug output.
extern bool SPIRVDbgEnable;
void verifyRegularizationPass(llvm::Module &, const std::string &);
#ifndef _SPIRVDBG
#if !defined(NDEBUG) || defined(_DEBUG)
#define _SPIRVDBG true
#else
#define _SPIRVDBG false
#endif
#endif
#if _SPIRVDBG
#define SPIRVDBG(x) \
if (SPIRVDbgEnable) { \
x; \
}
// Output stream for SPIRV debug information.
inline spv_ostream &spvdbgs() { return std::cerr; }
#else
#define SPIRVDBG(x)
// Minimal std::basic_ostream mock that ignores everything being printed via
// operator<<
class dev_null_stream {
public:
void flush() {}
};
template <typename T>
const dev_null_stream &operator<<(const dev_null_stream &Out, const T &) {
return Out;
}
template <typename T>
const dev_null_stream &&operator<<(const dev_null_stream &&Out, const T &) {
return std::move(Out);
}
// Output stream for SPIRV debug information.
inline dev_null_stream &spvdbgs() {
static dev_null_stream Out;
return Out;
}
#endif
} // namespace SPIRV
#endif // SPIRV_LIBSPIRV_SPIRVDEBUG_H
@@ -0,0 +1,272 @@
//===- SPIRVDecorate.cpp -SPIR-V Decorations --------------------*- C++ -*-===//
//
// The LLVM/SPIRV Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
//
// 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 Advanced Micro Devices, Inc., 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.
//
//===----------------------------------------------------------------------===//
/// \file
///
/// This file implements SPIR-V decorations.
///
//===----------------------------------------------------------------------===//
#include "SPIRVDecorate.h"
#include "SPIRVModule.h"
#include "SPIRVStream.h"
#include "SPIRVValue.h"
namespace SPIRV {
template <class T>
spv_ostream &operator<<(spv_ostream &O, const std::vector<T *> &V) {
for (auto &I : V)
O << *I;
return O;
}
SPIRVDecorateGeneric::SPIRVDecorateGeneric(Op OC, SPIRVWord WC,
Decoration TheDec,
SPIRVEntry *TheTarget)
: SPIRVAnnotationGeneric(TheTarget->getModule(), WC, OC,
TheTarget->getId()),
Dec(TheDec), Owner(nullptr) {
validate();
updateModuleVersion();
}
SPIRVDecorateGeneric::SPIRVDecorateGeneric(Op OC, SPIRVWord WC,
Decoration TheDec,
SPIRVEntry *TheTarget, SPIRVWord V)
: SPIRVAnnotationGeneric(TheTarget->getModule(), WC, OC,
TheTarget->getId()),
Dec(TheDec), Owner(nullptr) {
Literals.push_back(V);
validate();
updateModuleVersion();
}
SPIRVDecorateGeneric::SPIRVDecorateGeneric(Op OC, SPIRVWord WC,
Decoration TheDec,
SPIRVEntry *TheTarget, SPIRVWord V1,
SPIRVWord V2)
: SPIRVDecorateGeneric(OC, WC, TheDec, TheTarget, V1) {
Literals.push_back(V2);
validate();
updateModuleVersion();
}
SPIRVDecorateGeneric::SPIRVDecorateGeneric(Op OC, SPIRVWord WC,
Decoration TheDec,
SPIRVEntry *TheTarget, SPIRVWord V1,
SPIRVWord V2, SPIRVWord V3)
: SPIRVDecorateGeneric(OC, WC, TheDec, TheTarget, V1, V2) {
Literals.push_back(V3);
validate();
updateModuleVersion();
}
SPIRVDecorateGeneric::SPIRVDecorateGeneric(Op OC)
: SPIRVAnnotationGeneric(OC), Dec(DecorationRelaxedPrecision),
Owner(nullptr) {}
Decoration SPIRVDecorateGeneric::getDecorateKind() const { return Dec; }
SPIRVWord SPIRVDecorateGeneric::getLiteral(size_t I) const {
assert(I <= Literals.size() && "Out of bounds");
return Literals[I];
}
std::vector<SPIRVWord> SPIRVDecorateGeneric::getVecLiteral() const {
return Literals;
}
size_t SPIRVDecorateGeneric::getLiteralCount() const { return Literals.size(); }
void SPIRVDecorate::encode(spv_ostream &O) const {
SPIRVEncoder Encoder = getEncoder(O);
Encoder << Target << Dec;
switch (static_cast<size_t>(Dec)) {
case DecorationLinkageAttributes:
SPIRVDecorateLinkageAttr::encodeLiterals(Encoder, Literals);
break;
case DecorationMemoryINTEL:
SPIRVDecorateMemoryINTELAttr::encodeLiterals(Encoder, Literals);
break;
case DecorationMergeINTEL:
SPIRVDecorateMergeINTELAttr::encodeLiterals(Encoder, Literals);
break;
case DecorationUserSemantic:
SPIRVDecorateUserSemanticAttr::encodeLiterals(Encoder, Literals);
break;
case internal::DecorationHostAccessINTEL:
SPIRVDecorateHostAccessINTELLegacy::encodeLiterals(Encoder, Literals);
break;
case DecorationHostAccessINTEL:
SPIRVDecorateHostAccessINTEL::encodeLiterals(Encoder, Literals);
break;
case DecorationInitModeINTEL:
SPIRVDecorateInitModeINTEL::encodeLiterals(Encoder, Literals);
break;
default:
Encoder << Literals;
}
}
void SPIRVDecorate::setWordCount(SPIRVWord Count) {
WordCount = Count;
Literals.resize(WordCount - FixedWC);
}
void SPIRVDecorate::decode(std::istream &I) {
SPIRVDecoder Decoder = getDecoder(I);
Decoder >> Target >> Dec;
switch (static_cast<size_t>(Dec)) {
case DecorationLinkageAttributes:
SPIRVDecorateLinkageAttr::decodeLiterals(Decoder, Literals);
break;
case DecorationMemoryINTEL:
SPIRVDecorateMemoryINTELAttr::decodeLiterals(Decoder, Literals);
break;
case DecorationMergeINTEL:
SPIRVDecorateMergeINTELAttr::decodeLiterals(Decoder, Literals);
break;
case DecorationUserSemantic:
SPIRVDecorateUserSemanticAttr::decodeLiterals(Decoder, Literals);
break;
case internal::DecorationHostAccessINTEL:
SPIRVDecorateHostAccessINTELLegacy::decodeLiterals(Decoder, Literals);
break;
case DecorationHostAccessINTEL:
SPIRVDecorateHostAccessINTEL::decodeLiterals(Decoder, Literals);
break;
default:
Decoder >> Literals;
}
getOrCreateTarget()->addDecorate(this);
}
void SPIRVDecorateId::encode(spv_ostream &O) const {
SPIRVEncoder Encoder = getEncoder(O);
Encoder << Target << Dec << Literals;
}
void SPIRVDecorateId::setWordCount(SPIRVWord Count) {
WordCount = Count;
Literals.resize(WordCount - FixedWC);
}
void SPIRVDecorateId::decode(std::istream &I) {
SPIRVDecoder Decoder = getDecoder(I);
Decoder >> Target >> Dec >> Literals;
getOrCreateTarget()->addDecorate(this);
}
void SPIRVMemberDecorate::encode(spv_ostream &O) const {
SPIRVEncoder Encoder = getEncoder(O);
Encoder << Target << MemberNumber << Dec;
switch (Dec) {
case DecorationMemoryINTEL:
SPIRVDecorateMemoryINTELAttr::encodeLiterals(Encoder, Literals);
break;
case DecorationMergeINTEL:
SPIRVDecorateMergeINTELAttr::encodeLiterals(Encoder, Literals);
break;
case DecorationUserSemantic:
SPIRVDecorateUserSemanticAttr::encodeLiterals(Encoder, Literals);
break;
default:
Encoder << Literals;
}
}
void SPIRVMemberDecorate::setWordCount(SPIRVWord Count) {
WordCount = Count;
Literals.resize(WordCount - FixedWC);
}
void SPIRVMemberDecorate::decode(std::istream &I) {
SPIRVDecoder Decoder = getDecoder(I);
Decoder >> Target >> MemberNumber >> Dec;
switch (Dec) {
case DecorationMemoryINTEL:
SPIRVDecorateMemoryINTELAttr::decodeLiterals(Decoder, Literals);
break;
case DecorationMergeINTEL:
SPIRVDecorateMergeINTELAttr::decodeLiterals(Decoder, Literals);
break;
case DecorationUserSemantic:
SPIRVDecorateUserSemanticAttr::decodeLiterals(Decoder, Literals);
break;
default:
Decoder >> Literals;
}
getOrCreateTarget()->addMemberDecorate(this);
}
void SPIRVDecorationGroup::encode(spv_ostream &O) const { getEncoder(O) << Id; }
void SPIRVDecorationGroup::decode(std::istream &I) {
getDecoder(I) >> Id;
Module->addDecorationGroup(this);
}
void SPIRVDecorationGroup::encodeAll(spv_ostream &O) const {
O << Decorations;
SPIRVEntry::encodeAll(O);
}
void SPIRVGroupDecorateGeneric::encode(spv_ostream &O) const {
getEncoder(O) << DecorationGroup << Targets;
}
void SPIRVGroupDecorateGeneric::decode(std::istream &I) {
getDecoder(I) >> DecorationGroup >> Targets;
Module->addGroupDecorateGeneric(this);
}
void SPIRVGroupDecorate::decorateTargets() {
for (auto &I : Targets) {
auto *Target = getOrCreate(I);
for (auto &Dec : DecorationGroup->getDecorations()) {
assert(Dec->isDecorate());
Target->addDecorate(static_cast<SPIRVDecorate *>(Dec));
}
}
}
void SPIRVGroupMemberDecorate::decorateTargets() {
for (auto &I : Targets) {
auto *Target = getOrCreate(I);
for (auto &Dec : DecorationGroup->getDecorations()) {
assert(Dec->isMemberDecorate());
Target->addMemberDecorate(static_cast<SPIRVMemberDecorate *>(Dec));
}
}
}
} // namespace SPIRV
@@ -0,0 +1,972 @@
//===- SPIRVDecorate.h - SPIR-V Decorations ---------------------*- C++ -*-===//
//
// The LLVM/SPIRV Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
//
// 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 Advanced Micro Devices, Inc., 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.
//
//===----------------------------------------------------------------------===//
/// \file
///
/// This file defines SPIR-V decorations.
///
//===----------------------------------------------------------------------===//
#ifndef SPIRV_LIBSPIRV_SPIRVDECORATE_H
#define SPIRV_LIBSPIRV_SPIRVDECORATE_H
#include "SPIRVEntry.h"
#include "SPIRVStream.h"
#include "SPIRVUtil.h"
#include <string>
#include <utility>
#include <vector>
namespace SPIRV {
class SPIRVDecorationGroup;
class SPIRVDecorateGeneric : public SPIRVAnnotationGeneric {
public:
// Complete constructor for decorations without literals
SPIRVDecorateGeneric(Op OC, SPIRVWord WC, Decoration TheDec,
SPIRVEntry *TheTarget);
// Complete constructor for decorations with one word literal
SPIRVDecorateGeneric(Op OC, SPIRVWord WC, Decoration TheDec,
SPIRVEntry *TheTarget, SPIRVWord V);
// Complete constructor for decorations with two word literals
SPIRVDecorateGeneric(Op OC, SPIRVWord WC, Decoration TheDec,
SPIRVEntry *TheTarget, SPIRVWord V1, SPIRVWord V2);
// Complete constructor for decorations with three word literals
SPIRVDecorateGeneric(Op OC, SPIRVWord WC, Decoration TheDec,
SPIRVEntry *TheTarget, SPIRVWord V1, SPIRVWord V2,
SPIRVWord V3);
// Incomplete constructor
SPIRVDecorateGeneric(Op OC);
SPIRVWord getLiteral(size_t) const;
std::vector<SPIRVWord> getVecLiteral() const;
Decoration getDecorateKind() const;
size_t getLiteralCount() const;
SPIRVDecorationGroup *getOwner() const { return Owner; }
void setOwner(SPIRVDecorationGroup *Owner) { this->Owner = Owner; }
SPIRVCapVec getRequiredCapability() const override {
switch (Dec) {
case DecorationBuiltIn: {
// Return the BuiltIn's capabilities.
BuiltIn BI = static_cast<BuiltIn>(Literals.back());
return getCapability(BI);
}
case DecorationUniform:
case DecorationUniformId:
if (Module->isAllowedToUseVersion(VersionNumber::SPIRV_1_6))
return getVec(CapabilityUniformDecoration);
return getVec(CapabilityShader);
default:
return getCapability(Dec);
}
}
VersionNumber getRequiredSPIRVVersion() const override {
switch (Dec) {
case DecorationSpecId:
if (getModule()->hasCapability(CapabilityKernel))
return VersionNumber::SPIRV_1_1;
else
return VersionNumber::SPIRV_1_0;
case DecorationMaxByteOffset:
return VersionNumber::SPIRV_1_1;
case DecorationUserSemantic:
case DecorationCounterBuffer:
return VersionNumber::SPIRV_1_4;
default:
return VersionNumber::SPIRV_1_0;
}
}
protected:
Decoration Dec;
std::vector<SPIRVWord> Literals;
SPIRVDecorationGroup *Owner; // Owning decorate group
};
typedef std::vector<SPIRVDecorateGeneric *> SPIRVDecorateVec;
class SPIRVDecorate : public SPIRVDecorateGeneric {
public:
static const Op OC = OpDecorate;
static const SPIRVWord FixedWC = 3;
// Complete constructor for decorations without literals
SPIRVDecorate(Decoration TheDec, SPIRVEntry *TheTarget)
: SPIRVDecorateGeneric(OC, 3, TheDec, TheTarget) {}
// Complete constructor for decorations with one word literal
SPIRVDecorate(Decoration TheDec, SPIRVEntry *TheTarget, SPIRVWord V)
: SPIRVDecorateGeneric(OC, 4, TheDec, TheTarget, V) {}
// Complete constructor for decorations with two word literals
SPIRVDecorate(Decoration TheDec, SPIRVEntry *TheTarget, SPIRVWord V1,
SPIRVWord V2)
: SPIRVDecorateGeneric(OC, 5, TheDec, TheTarget, V1, V2) {}
// Complete constructor for decorations with three word literals
SPIRVDecorate(Decoration TheDec, SPIRVEntry *TheTarget, SPIRVWord V1,
SPIRVWord V2, SPIRVWord V3)
: SPIRVDecorateGeneric(OC, 6, TheDec, TheTarget, V1, V2, V3) {}
// Incomplete constructor
SPIRVDecorate() : SPIRVDecorateGeneric(OC) {}
std::optional<ExtensionID> getRequiredExtension() const override {
switch (static_cast<size_t>(Dec)) {
case DecorationRegisterINTEL:
case DecorationMemoryINTEL:
case DecorationNumbanksINTEL:
case DecorationBankwidthINTEL:
case DecorationMaxPrivateCopiesINTEL:
case DecorationSinglepumpINTEL:
case DecorationDoublepumpINTEL:
case DecorationMaxReplicatesINTEL:
case DecorationSimpleDualPortINTEL:
case DecorationMergeINTEL:
case DecorationBankBitsINTEL:
case DecorationForcePow2DepthINTEL:
case DecorationStridesizeINTEL:
case DecorationWordsizeINTEL:
case DecorationTrueDualPortINTEL:
return ExtensionID::SPV_INTEL_fpga_memory_attributes;
case DecorationBurstCoalesceINTEL:
case DecorationCacheSizeINTEL:
case DecorationDontStaticallyCoalesceINTEL:
case DecorationPrefetchINTEL:
return ExtensionID::SPV_INTEL_fpga_memory_accesses;
case DecorationReferencedIndirectlyINTEL:
case internal::DecorationArgumentAttributeINTEL:
return ExtensionID::SPV_INTEL_function_pointers;
case DecorationIOPipeStorageINTEL:
return ExtensionID::SPV_INTEL_io_pipes;
case DecorationBufferLocationINTEL:
return ExtensionID::SPV_INTEL_fpga_buffer_location;
case DecorationFunctionFloatingPointModeINTEL:
case DecorationFunctionRoundingModeINTEL:
case DecorationFunctionDenormModeINTEL:
return ExtensionID::SPV_INTEL_float_controls2;
case DecorationStallEnableINTEL:
return ExtensionID::SPV_INTEL_fpga_cluster_attributes;
case DecorationStallFreeINTEL:
return ExtensionID::SPV_INTEL_fpga_cluster_attributes;
case DecorationFuseLoopsInFunctionINTEL:
return ExtensionID::SPV_INTEL_loop_fuse;
case DecorationMathOpDSPModeINTEL:
return ExtensionID::SPV_INTEL_fpga_dsp_control;
case DecorationInitiationIntervalINTEL:
return ExtensionID::SPV_INTEL_fpga_invocation_pipelining_attributes;
case DecorationMaxConcurrencyINTEL:
return ExtensionID::SPV_INTEL_fpga_invocation_pipelining_attributes;
case DecorationPipelineEnableINTEL:
return ExtensionID::SPV_INTEL_fpga_invocation_pipelining_attributes;
case internal::DecorationRuntimeAlignedINTEL:
return ExtensionID::SPV_INTEL_runtime_aligned;
case internal::DecorationHostAccessINTEL:
case internal::DecorationInitModeINTEL:
case internal::DecorationImplementInCSRINTEL:
return ExtensionID::SPV_INTEL_global_variable_decorations;
case DecorationInitModeINTEL:
case DecorationImplementInRegisterMapINTEL:
return ExtensionID::SPV_INTEL_global_variable_fpga_decorations;
case DecorationHostAccessINTEL:
return ExtensionID::SPV_INTEL_global_variable_host_access;
case DecorationConduitKernelArgumentINTEL:
case DecorationRegisterMapKernelArgumentINTEL:
case DecorationStableKernelArgumentINTEL:
case DecorationMMHostInterfaceReadWriteModeINTEL:
case DecorationMMHostInterfaceAddressWidthINTEL:
case DecorationMMHostInterfaceDataWidthINTEL:
case DecorationMMHostInterfaceLatencyINTEL:
case DecorationMMHostInterfaceMaxBurstINTEL:
case DecorationMMHostInterfaceWaitRequestINTEL:
return ExtensionID::SPV_INTEL_fpga_argument_interfaces;
case DecorationLatencyControlLabelINTEL:
case DecorationLatencyControlConstraintINTEL:
return ExtensionID::SPV_INTEL_fpga_latency_control;
case DecorationFPMaxErrorDecorationINTEL:
return ExtensionID::SPV_INTEL_fp_max_error;
case DecorationCacheControlLoadINTEL:
case DecorationCacheControlStoreINTEL:
return ExtensionID::SPV_INTEL_cache_controls;
default:
return {};
}
}
_SPIRV_DCL_ENCDEC
void setWordCount(SPIRVWord) override;
void validate() const override {
SPIRVDecorateGeneric::validate();
assert(WordCount == Literals.size() + FixedWC);
}
};
class SPIRVDecorateString : public SPIRVDecorate {};
class SPIRVDecorateId : public SPIRVDecorateGeneric {
public:
static const Op OC = OpDecorateId;
static const SPIRVWord FixedWC = 3;
// Complete constructor for decorations with one id operand
SPIRVDecorateId(Decoration TheDec, SPIRVEntry *TheTarget, SPIRVId V)
: SPIRVDecorateGeneric(OC, 4, TheDec, TheTarget, V) {}
// Incomplete constructor
SPIRVDecorateId() : SPIRVDecorateGeneric(OC) {}
std::optional<ExtensionID> getRequiredExtension() const override {
switch (static_cast<int>(Dec)) {
case DecorationAliasScopeINTEL:
case DecorationNoAliasINTEL:
return ExtensionID::SPV_INTEL_memory_access_aliasing;
default:
return {};
}
}
_SPIRV_DCL_ENCDEC
void setWordCount(SPIRVWord) override;
void validate() const override {
SPIRVDecorateGeneric::validate();
assert(WordCount == Literals.size() + FixedWC);
}
};
class SPIRVDecorateLinkageAttr : public SPIRVDecorate {
public:
// Complete constructor for LinkageAttributes decorations
SPIRVDecorateLinkageAttr(SPIRVEntry *TheTarget, const std::string &Name,
SPIRVLinkageTypeKind Kind)
: SPIRVDecorate(DecorationLinkageAttributes, TheTarget) {
for (auto &I : getVec(Name))
Literals.push_back(I);
Literals.push_back(Kind);
WordCount += Literals.size();
}
// Incomplete constructor
SPIRVDecorateLinkageAttr() : SPIRVDecorate() {}
std::string getLinkageName() const {
return getString(Literals.cbegin(), Literals.cend() - 1);
}
SPIRVLinkageTypeKind getLinkageType() const {
return (SPIRVLinkageTypeKind)Literals.back();
}
static void encodeLiterals(SPIRVEncoder &Encoder,
const std::vector<SPIRVWord> &Literals) {
#ifdef _SPIRV_SUPPORT_TEXT_FMT
if (SPIRVUseTextFormat) {
Encoder << getString(Literals.cbegin(), Literals.cend() - 1);
Encoder << (SPIRVLinkageTypeKind)Literals.back();
} else
#endif
Encoder << Literals;
}
static void decodeLiterals(SPIRVDecoder &Decoder,
std::vector<SPIRVWord> &Literals) {
#ifdef _SPIRV_SUPPORT_TEXT_FMT
if (SPIRVUseTextFormat) {
std::string Name;
Decoder >> Name;
SPIRVLinkageTypeKind Kind;
Decoder >> Kind;
std::copy_n(getVec(Name).begin(), Literals.size() - 1, Literals.begin());
Literals.back() = Kind;
} else
#endif
Decoder >> Literals;
}
std::optional<ExtensionID> getRequiredExtension() const override {
if (getLinkageType() == SPIRVLinkageTypeKind::LinkageTypeLinkOnceODR)
return ExtensionID::SPV_KHR_linkonce_odr;
return {};
}
};
class SPIRVMemberDecorate : public SPIRVDecorateGeneric {
public:
static const Op OC = OpMemberDecorate;
static const SPIRVWord FixedWC = 4;
// Complete constructor for decorations without literals
SPIRVMemberDecorate(Decoration TheDec, SPIRVWord Member,
SPIRVEntry *TheTarget)
: SPIRVDecorateGeneric(OC, 4, TheDec, TheTarget), MemberNumber(Member) {}
// Complete constructor for decorations with one word literal
SPIRVMemberDecorate(Decoration TheDec, SPIRVWord Member,
SPIRVEntry *TheTarget, SPIRVWord V)
: SPIRVDecorateGeneric(OC, 5, TheDec, TheTarget, V),
MemberNumber(Member) {}
// Incomplete constructor
SPIRVMemberDecorate()
: SPIRVDecorateGeneric(OC), MemberNumber(SPIRVWORD_MAX) {}
std::optional<ExtensionID> getRequiredExtension() const override {
switch (static_cast<size_t>(Dec)) {
case DecorationRegisterINTEL:
case DecorationMemoryINTEL:
case DecorationNumbanksINTEL:
case DecorationBankwidthINTEL:
case DecorationMaxPrivateCopiesINTEL:
case DecorationSinglepumpINTEL:
case DecorationDoublepumpINTEL:
case DecorationMaxReplicatesINTEL:
case DecorationSimpleDualPortINTEL:
case DecorationMergeINTEL:
case DecorationBankBitsINTEL:
case DecorationForcePow2DepthINTEL:
case DecorationStridesizeINTEL:
case DecorationWordsizeINTEL:
case DecorationTrueDualPortINTEL:
return ExtensionID::SPV_INTEL_fpga_memory_attributes;
case DecorationBurstCoalesceINTEL:
case DecorationCacheSizeINTEL:
case DecorationDontStaticallyCoalesceINTEL:
case DecorationPrefetchINTEL:
return ExtensionID::SPV_INTEL_fpga_memory_accesses;
case DecorationIOPipeStorageINTEL:
return ExtensionID::SPV_INTEL_io_pipes;
case DecorationBufferLocationINTEL:
return ExtensionID::SPV_INTEL_fpga_buffer_location;
case internal::DecorationRuntimeAlignedINTEL:
return ExtensionID::SPV_INTEL_runtime_aligned;
default:
return {};
}
}
SPIRVWord getMemberNumber() const { return MemberNumber; }
std::pair<SPIRVWord, Decoration> getPair() const {
return std::make_pair(MemberNumber, Dec);
}
_SPIRV_DCL_ENCDEC
void setWordCount(SPIRVWord) override;
void validate() const override {
SPIRVDecorateGeneric::validate();
assert(WordCount == Literals.size() + FixedWC);
}
protected:
SPIRVWord MemberNumber;
};
class SPIRVMemberDecorateString : public SPIRVMemberDecorate {};
class SPIRVDecorationGroup : public SPIRVEntry {
public:
static const Op OC = OpDecorationGroup;
static const SPIRVWord WC = 2;
// Complete constructor. Does not populate Decorations.
SPIRVDecorationGroup(SPIRVModule *TheModule, SPIRVId TheId)
: SPIRVEntry(TheModule, WC, OC, TheId) {
validate();
}
// Incomplete constructor
SPIRVDecorationGroup() : SPIRVEntry(OC) {}
void encodeAll(spv_ostream &O) const override;
_SPIRV_DCL_ENCDEC
// Move the given decorates to the decoration group
void takeDecorates(SPIRVDecorateVec &Decs) {
Decorations = std::move(Decs);
for (auto &I : Decorations)
const_cast<SPIRVDecorateGeneric *>(I)->setOwner(this);
Decs.clear();
}
SPIRVDecorateVec &getDecorations() { return Decorations; }
protected:
SPIRVDecorateVec Decorations;
void validate() const override {
assert(OpCode == OC);
assert(WordCount == WC);
}
};
class SPIRVGroupDecorateGeneric : public SPIRVEntryNoIdGeneric {
public:
static const SPIRVWord FixedWC = 2;
// Complete constructor
SPIRVGroupDecorateGeneric(Op OC, SPIRVDecorationGroup *TheGroup,
const std::vector<SPIRVId> &TheTargets)
: SPIRVEntryNoIdGeneric(TheGroup->getModule(),
FixedWC + TheTargets.size(), OC),
DecorationGroup(TheGroup), Targets(TheTargets) {}
// Incomplete constructor
SPIRVGroupDecorateGeneric(Op OC)
: SPIRVEntryNoIdGeneric(OC), DecorationGroup(nullptr) {}
void setWordCount(SPIRVWord WC) override {
SPIRVEntryNoIdGeneric::setWordCount(WC);
Targets.resize(WC - FixedWC);
}
virtual void decorateTargets() = 0;
_SPIRV_DCL_ENCDEC
protected:
SPIRVDecorationGroup *DecorationGroup;
std::vector<SPIRVId> Targets;
};
class SPIRVGroupDecorate : public SPIRVGroupDecorateGeneric {
public:
static const Op OC = OpGroupDecorate;
// Complete constructor
SPIRVGroupDecorate(SPIRVDecorationGroup *TheGroup,
const std::vector<SPIRVId> &TheTargets)
: SPIRVGroupDecorateGeneric(OC, TheGroup, TheTargets) {}
// Incomplete constructor
SPIRVGroupDecorate() : SPIRVGroupDecorateGeneric(OC) {}
void decorateTargets() override;
};
class SPIRVGroupMemberDecorate : public SPIRVGroupDecorateGeneric {
public:
static const Op OC = OpGroupMemberDecorate;
// Complete constructor
SPIRVGroupMemberDecorate(SPIRVDecorationGroup *TheGroup,
const std::vector<SPIRVId> &TheTargets)
: SPIRVGroupDecorateGeneric(OC, TheGroup, TheTargets) {}
// Incomplete constructor
SPIRVGroupMemberDecorate() : SPIRVGroupDecorateGeneric(OC) {}
void decorateTargets() override;
};
template <Decoration D> class SPIRVDecorateStrAttrBase : public SPIRVDecorate {
public:
// Complete constructor for decoration with string literal
SPIRVDecorateStrAttrBase(SPIRVEntry *TheTarget, const std::string &Str)
: SPIRVDecorate(D, TheTarget) {
for (auto &I : getVec(Str))
Literals.push_back(I);
WordCount += Literals.size();
}
// Incomplete constructor
SPIRVDecorateStrAttrBase() : SPIRVDecorate() {}
static void encodeLiterals(SPIRVEncoder &Encoder,
const std::vector<SPIRVWord> &Literals) {
#ifdef _SPIRV_SUPPORT_TEXT_FMT
if (SPIRVUseTextFormat) {
Encoder << getString(Literals.cbegin(), Literals.cend());
} else
#endif
Encoder << Literals;
}
static void decodeLiterals(SPIRVDecoder &Decoder,
std::vector<SPIRVWord> &Literals) {
#ifdef _SPIRV_SUPPORT_TEXT_FMT
if (SPIRVUseTextFormat) {
std::string Str;
Decoder >> Str;
std::copy_n(getVec(Str).begin(), Literals.size(), Literals.begin());
} else
#endif
Decoder >> Literals;
}
};
class SPIRVDecorateMemoryINTELAttr
: public SPIRVDecorateStrAttrBase<DecorationMemoryINTEL> {
public:
// Complete constructor for MemoryINTEL decoration
SPIRVDecorateMemoryINTELAttr(SPIRVEntry *TheTarget,
const std::string &MemoryType)
: SPIRVDecorateStrAttrBase(TheTarget, MemoryType) {}
};
class SPIRVDecorateUserSemanticAttr
: public SPIRVDecorateStrAttrBase<DecorationUserSemantic> {
public:
// Complete constructor for UserSemantic decoration
SPIRVDecorateUserSemanticAttr(SPIRVEntry *TheTarget,
const std::string &AnnotateString)
: SPIRVDecorateStrAttrBase(TheTarget, AnnotateString) {}
};
class SPIRVDecorateMergeINTELAttr : public SPIRVDecorate {
public:
// Complete constructor for MergeINTEL decoration
SPIRVDecorateMergeINTELAttr(SPIRVEntry *TheTarget, const std::string &Name,
const std::string &Direction)
: SPIRVDecorate(DecorationMergeINTEL, TheTarget) {
for (auto &I : getVec(Name))
Literals.push_back(I);
for (auto &I : getVec(Direction))
Literals.push_back(I);
WordCount += Literals.size();
}
static void encodeLiterals(SPIRVEncoder &Encoder,
const std::vector<SPIRVWord> &Literals) {
#ifdef _SPIRV_SUPPORT_TEXT_FMT
if (SPIRVUseTextFormat) {
std::string FirstString = getString(Literals.cbegin(), Literals.cend());
Encoder << FirstString;
Encoder.OS << " ";
Encoder << getString(Literals.cbegin() + getVec(FirstString).size(),
Literals.cend());
} else
#endif
Encoder << Literals;
}
static void decodeLiterals(SPIRVDecoder &Decoder,
std::vector<SPIRVWord> &Literals) {
#ifdef _SPIRV_SUPPORT_TEXT_FMT
if (SPIRVUseTextFormat) {
std::string Name;
Decoder >> Name;
std::string Direction;
Decoder >> Direction;
std::string Buf = Name + ':' + Direction;
std::copy_n(getVec(Buf).begin(), Literals.size(), Literals.begin());
} else
#endif
Decoder >> Literals;
}
};
class SPIRVDecorateBankBitsINTELAttr : public SPIRVDecorate {
public:
// Complete constructor for BankBitsINTEL decoration
SPIRVDecorateBankBitsINTELAttr(SPIRVEntry *TheTarget,
const std::vector<SPIRVWord> &TheBits)
: SPIRVDecorate(DecorationBankBitsINTEL, TheTarget) {
Literals = TheBits;
WordCount += Literals.size();
}
};
template <Decoration D>
class SPIRVMemberDecorateStrAttrBase : public SPIRVMemberDecorate {
public:
// Complete constructor for decoration with string literal
SPIRVMemberDecorateStrAttrBase(SPIRVEntry *TheTarget, SPIRVWord MemberNumber,
const std::string &Str)
: SPIRVMemberDecorate(D, MemberNumber, TheTarget) {
for (auto &I : getVec(Str))
Literals.push_back(I);
WordCount += Literals.size();
}
// Incomplete constructor
SPIRVMemberDecorateStrAttrBase() : SPIRVMemberDecorate() {}
};
class SPIRVMemberDecorateMemoryINTELAttr
: public SPIRVMemberDecorateStrAttrBase<DecorationMemoryINTEL> {
public:
// Complete constructor for MemoryINTEL decoration
SPIRVMemberDecorateMemoryINTELAttr(SPIRVEntry *TheTarget,
SPIRVWord MemberNumber,
const std::string &MemoryType)
: SPIRVMemberDecorateStrAttrBase(TheTarget, MemberNumber, MemoryType) {}
};
class SPIRVMemberDecorateUserSemanticAttr
: public SPIRVMemberDecorateStrAttrBase<DecorationUserSemantic> {
public:
// Complete constructor for UserSemantic decoration
SPIRVMemberDecorateUserSemanticAttr(SPIRVEntry *TheTarget,
SPIRVWord MemberNumber,
const std::string &AnnotateString)
: SPIRVMemberDecorateStrAttrBase(TheTarget, MemberNumber,
AnnotateString) {}
};
class SPIRVMemberDecorateMergeINTELAttr : public SPIRVMemberDecorate {
public:
// Complete constructor for MergeINTEL decoration
SPIRVMemberDecorateMergeINTELAttr(SPIRVEntry *TheTarget,
SPIRVWord MemberNumber,
const std::string &Name,
const std::string &Direction)
: SPIRVMemberDecorate(DecorationMergeINTEL, MemberNumber, TheTarget) {
for (auto &I : getVec(Name))
Literals.push_back(I);
for (auto &I : getVec(Direction))
Literals.push_back(I);
WordCount += Literals.size();
}
};
class SPIRVMemberDecorateBankBitsINTELAttr : public SPIRVMemberDecorate {
public:
// Complete constructor for BankBitsINTEL decoration
SPIRVMemberDecorateBankBitsINTELAttr(SPIRVEntry *TheTarget,
SPIRVWord MemberNumber,
const std::vector<SPIRVWord> &TheBits)
: SPIRVMemberDecorate(DecorationBankBitsINTEL, MemberNumber, TheTarget) {
Literals = TheBits;
WordCount += Literals.size();
}
};
class SPIRVDecorateFunctionRoundingModeINTEL : public SPIRVDecorate {
public:
// Complete constructor for SPIRVDecorateFunctionRoundingModeINTEL
SPIRVDecorateFunctionRoundingModeINTEL(SPIRVEntry *TheTarget,
SPIRVWord TargetWidth,
spv::FPRoundingMode FloatControl)
: SPIRVDecorate(spv::DecorationFunctionRoundingModeINTEL, TheTarget,
TargetWidth, static_cast<SPIRVWord>(FloatControl)) {}
SPIRVWord getTargetWidth() const { return Literals.at(0); }
spv::FPRoundingMode getRoundingMode() const {
return static_cast<spv::FPRoundingMode>(Literals.at(1));
}
};
class SPIRVDecorateFunctionDenormModeINTEL : public SPIRVDecorate {
public:
// Complete constructor for SPIRVDecorateFunctionDenormModeINTEL
SPIRVDecorateFunctionDenormModeINTEL(SPIRVEntry *TheTarget,
SPIRVWord TargetWidth,
spv::FPDenormMode FloatControl)
: SPIRVDecorate(spv::DecorationFunctionDenormModeINTEL, TheTarget,
TargetWidth, static_cast<SPIRVWord>(FloatControl)) {}
SPIRVWord getTargetWidth() const { return Literals.at(0); }
spv::FPDenormMode getDenormMode() const {
return static_cast<spv::FPDenormMode>(Literals.at(1));
}
};
class SPIRVDecorateFunctionFloatingPointModeINTEL : public SPIRVDecorate {
public:
// Complete constructor for SPIRVDecorateFunctionOperationModeINTEL
SPIRVDecorateFunctionFloatingPointModeINTEL(SPIRVEntry *TheTarget,
SPIRVWord TargetWidth,
spv::FPOperationMode FloatControl)
: SPIRVDecorate(spv::DecorationFunctionFloatingPointModeINTEL, TheTarget,
TargetWidth, static_cast<SPIRVWord>(FloatControl)) {}
SPIRVWord getTargetWidth() const { return Literals.at(0); }
spv::FPOperationMode getOperationMode() const {
return static_cast<spv::FPOperationMode>(Literals.at(1));
}
};
class SPIRVDecorateStallEnableINTEL : public SPIRVDecorate {
public:
// Complete constructor for SPIRVDecorateStallEnableINTEL
SPIRVDecorateStallEnableINTEL(SPIRVEntry *TheTarget)
: SPIRVDecorate(spv::DecorationStallEnableINTEL, TheTarget) {}
};
class SPIRVDecorateStallFreeINTEL : public SPIRVDecorate {
public:
SPIRVDecorateStallFreeINTEL(SPIRVEntry *TheTarget)
: SPIRVDecorate(spv::DecorationStallFreeINTEL, TheTarget) {}
};
class SPIRVDecorateFuseLoopsInFunctionINTEL : public SPIRVDecorate {
public:
// Complete constructor for SPIRVDecorateFuseLoopsInFunctionINTEL
SPIRVDecorateFuseLoopsInFunctionINTEL(SPIRVEntry *TheTarget, SPIRVWord Depth,
SPIRVWord Independent)
: SPIRVDecorate(spv::DecorationFuseLoopsInFunctionINTEL, TheTarget, Depth,
Independent) {}
};
class SPIRVDecorateMathOpDSPModeINTEL : public SPIRVDecorate {
public:
// Complete constructor for SPIRVDecorateMathOpDSPModeINTEL
SPIRVDecorateMathOpDSPModeINTEL(SPIRVEntry *TheTarget, SPIRVWord Mode,
SPIRVWord Propagate)
: SPIRVDecorate(spv::DecorationMathOpDSPModeINTEL, TheTarget, Mode,
Propagate) {}
};
class SPIRVDecorateAliasScopeINTEL : public SPIRVDecorateId {
public:
// Complete constructor for SPIRVDecorateAliasScopeINTEL
SPIRVDecorateAliasScopeINTEL(SPIRVEntry *TheTarget, SPIRVId AliasList)
: SPIRVDecorateId(spv::DecorationAliasScopeINTEL, TheTarget, AliasList) {}
};
class SPIRVDecorateNoAliasINTEL : public SPIRVDecorateId {
public:
// Complete constructor for SPIRVDecorateNoAliasINTEL
SPIRVDecorateNoAliasINTEL(SPIRVEntry *TheTarget, SPIRVId AliasList)
: SPIRVDecorateId(spv::DecorationNoAliasINTEL, TheTarget, AliasList) {}
};
class SPIRVDecorateInitiationIntervalINTEL : public SPIRVDecorate {
public:
// Complete constructor for SPIRVDecorateInitiationIntervalINTEL
SPIRVDecorateInitiationIntervalINTEL(SPIRVEntry *TheTarget, SPIRVWord Cycles)
: SPIRVDecorate(spv::DecorationInitiationIntervalINTEL, TheTarget,
Cycles) {}
};
class SPIRVDecorateMaxConcurrencyINTEL : public SPIRVDecorate {
public:
// Complete constructor for SPIRVDecorateMaxConcurrencyINTEL
SPIRVDecorateMaxConcurrencyINTEL(SPIRVEntry *TheTarget, SPIRVWord Invocations)
: SPIRVDecorate(spv::DecorationMaxConcurrencyINTEL, TheTarget,
Invocations) {}
};
class SPIRVDecoratePipelineEnableINTEL : public SPIRVDecorate {
public:
// Complete constructor for SPIRVDecoratePipelineEnableINTEL
SPIRVDecoratePipelineEnableINTEL(SPIRVEntry *TheTarget, SPIRVWord Enable)
: SPIRVDecorate(spv::DecorationPipelineEnableINTEL, TheTarget, Enable) {}
};
class SPIRVDecorateHostAccessINTELBase : public SPIRVDecorate {
public:
// Complete constructor for SPIRVHostAccessINTEL
SPIRVDecorateHostAccessINTELBase(Decoration D, SPIRVEntry *TheTarget,
HostAccessQualifier AccessMode,
const std::string &VarName)
: SPIRVDecorate(D, TheTarget) {
Literals.push_back(AccessMode);
for (auto &I : getVec(VarName))
Literals.push_back(I);
WordCount += Literals.size();
}
SPIRVWord getAccessMode() const { return Literals.front(); }
std::string getVarName() const {
return getString(Literals.cbegin() + 1, Literals.cend());
}
};
class SPIRVDecorateHostAccessINTEL : public SPIRVDecorateHostAccessINTELBase {
public:
SPIRVDecorateHostAccessINTEL(SPIRVEntry *TheTarget,
HostAccessQualifier AccessMode,
const std::string &VarName)
: SPIRVDecorateHostAccessINTELBase(DecorationHostAccessINTEL, TheTarget,
AccessMode, VarName) {}
static void encodeLiterals(SPIRVEncoder &Encoder,
const std::vector<SPIRVWord> &Literals) {
#ifdef _SPIRV_SUPPORT_TEXT_FMT
if (SPIRVUseTextFormat) {
Encoder << (HostAccessQualifier)Literals.front();
std::string Name = getString(Literals.cbegin() + 1, Literals.cend());
Encoder << Name;
} else
#endif
Encoder << Literals;
}
static void decodeLiterals(SPIRVDecoder &Decoder,
std::vector<SPIRVWord> &Literals) {
#ifdef _SPIRV_SUPPORT_TEXT_FMT
if (SPIRVUseTextFormat) {
HostAccessQualifier Mode;
Decoder >> Mode;
std::string Name;
Decoder >> Name;
Literals.front() = Mode;
std::copy_n(getVec(Name).begin(), Literals.size() - 1,
Literals.begin() + 1);
} else
#endif
Decoder >> Literals;
}
};
class SPIRVDecorateHostAccessINTELLegacy
: public SPIRVDecorateHostAccessINTELBase {
public:
SPIRVDecorateHostAccessINTELLegacy(SPIRVEntry *TheTarget,
HostAccessQualifier AccessMode,
const std::string &VarName)
: SPIRVDecorateHostAccessINTELBase(internal::DecorationHostAccessINTEL,
TheTarget, AccessMode, VarName) {}
static void encodeLiterals(SPIRVEncoder &Encoder,
const std::vector<SPIRVWord> &Literals) {
#ifdef _SPIRV_SUPPORT_TEXT_FMT
if (SPIRVUseTextFormat) {
Encoder << Literals.front();
std::string Name = getString(Literals.cbegin() + 1, Literals.cend());
Encoder << Name;
} else
#endif
Encoder << Literals;
}
static void decodeLiterals(SPIRVDecoder &Decoder,
std::vector<SPIRVWord> &Literals) {
#ifdef _SPIRV_SUPPORT_TEXT_FMT
if (SPIRVUseTextFormat) {
SPIRVWord Mode;
Decoder >> Mode;
std::string Name;
Decoder >> Name;
Literals.front() = Mode;
std::copy_n(getVec(Name).begin(), Literals.size() - 1,
Literals.begin() + 1);
} else
#endif
Decoder >> Literals;
}
};
class SPIRVDecorateInitModeINTELBase : public SPIRVDecorate {
public:
// Complete constructor for SPIRVInitModeINTEL
SPIRVDecorateInitModeINTELBase(Decoration D, SPIRVEntry *TheTarget,
InitializationModeQualifier Trigger)
: SPIRVDecorate(D, TheTarget) {
Literals.push_back(Trigger);
WordCount += Literals.size();
}
};
class SPIRVDecorateInitModeINTEL : public SPIRVDecorateInitModeINTELBase {
public:
SPIRVDecorateInitModeINTEL(SPIRVEntry *TheTarget,
InitializationModeQualifier Trigger)
: SPIRVDecorateInitModeINTELBase(DecorationInitModeINTEL, TheTarget,
Trigger) {}
static void encodeLiterals(SPIRVEncoder &Encoder,
const std::vector<SPIRVWord> &Literals) {
#ifdef _SPIRV_SUPPORT_TEXT_FMT
if (SPIRVUseTextFormat) {
Encoder << (InitializationModeQualifier)Literals.back();
} else
#endif
Encoder << Literals;
}
static void decodeLiterals(SPIRVDecoder &Decoder,
std::vector<SPIRVWord> &Literals) {
#ifdef _SPIRV_SUPPORT_TEXT_FMT
if (SPIRVUseTextFormat) {
InitializationModeQualifier Q;
Decoder >> Q;
Literals.back() = Q;
} else
#endif
Decoder >> Literals;
}
};
class SPIRVDecorateInitModeINTELLegacy : public SPIRVDecorateInitModeINTELBase {
public:
SPIRVDecorateInitModeINTELLegacy(SPIRVEntry *TheTarget,
InitializationModeQualifier Trigger)
: SPIRVDecorateInitModeINTELBase(internal::DecorationInitModeINTEL,
TheTarget, Trigger) {}
static void encodeLiterals(SPIRVEncoder &Encoder,
const std::vector<SPIRVWord> &Literals) {
#ifdef _SPIRV_SUPPORT_TEXT_FMT
if (SPIRVUseTextFormat) {
Encoder << Literals.back();
} else
#endif
Encoder << Literals;
}
static void decodeLiterals(SPIRVDecoder &Decoder,
std::vector<SPIRVWord> &Literals) {
#ifdef _SPIRV_SUPPORT_TEXT_FMT
if (SPIRVUseTextFormat) {
SPIRVWord Q;
Decoder >> Q;
Literals.back() = Q;
} else
#endif
Decoder >> Literals;
}
};
class SPIRVDecorateImplementInCSRINTEL : public SPIRVDecorate {
public:
// Complete constructor for SPIRVImplementInCSRINTEL
SPIRVDecorateImplementInCSRINTEL(SPIRVEntry *TheTarget, SPIRVWord Value)
: SPIRVDecorate(spv::internal::DecorationImplementInCSRINTEL, TheTarget,
Value) {}
};
class SPIRVDecorateImplementInRegisterMapINTEL : public SPIRVDecorate {
public:
// Complete constructor for SPIRVImplementInCSRINTEL
SPIRVDecorateImplementInRegisterMapINTEL(SPIRVEntry *TheTarget,
SPIRVWord Value)
: SPIRVDecorate(DecorationImplementInRegisterMapINTEL, TheTarget, Value) {
}
};
class SPIRVDecorateCacheControlLoadINTEL : public SPIRVDecorate {
public:
// Complete constructor for SPIRVDecorateCacheControlLoadINTEL
SPIRVDecorateCacheControlLoadINTEL(SPIRVEntry *TheTarget,
SPIRVWord CacheLevel,
LoadCacheControl CacheControl)
: SPIRVDecorate(DecorationCacheControlLoadINTEL, TheTarget, CacheLevel,
static_cast<SPIRVWord>(CacheControl)) {}
SPIRVWord getCacheLevel() const { return Literals.at(0); }
LoadCacheControl getCacheControl() const {
return static_cast<LoadCacheControl>(Literals.at(1));
}
};
class SPIRVDecorateCacheControlStoreINTEL : public SPIRVDecorate {
public:
// Complete constructor for SPIRVDecorateCacheControlStoreINTEL
SPIRVDecorateCacheControlStoreINTEL(SPIRVEntry *TheTarget,
SPIRVWord CacheLevel,
StoreCacheControl CacheControl)
: SPIRVDecorate(DecorationCacheControlStoreINTEL, TheTarget, CacheLevel,
static_cast<SPIRVWord>(CacheControl)) {}
SPIRVWord getCacheLevel() const { return Literals.at(0); }
StoreCacheControl getCacheControl() const {
return static_cast<StoreCacheControl>(Literals.at(1));
}
};
} // namespace SPIRV
#endif // SPIRV_LIBSPIRV_SPIRVDECORATE_H
@@ -0,0 +1,874 @@
//===- SPIRVEntry.cpp - Base Class for SPIR-V Entities ----------*- C++ -*-===//
//
// The LLVM/SPIRV Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
//
// 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 Advanced Micro Devices, Inc., 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.
//
//===----------------------------------------------------------------------===//
/// \file
///
/// This file implements base class for SPIR-V entities.
///
//===----------------------------------------------------------------------===//
#include "SPIRVEntry.h"
#include "SPIRVAsm.h"
#include "SPIRVBasicBlock.h"
#include "SPIRVDebug.h"
#include "SPIRVDecorate.h"
#include "SPIRVFnVar.h"
#include "SPIRVFunction.h"
#include "SPIRVInstruction.h"
#include "SPIRVMemAliasingINTEL.h"
#include "SPIRVNameMapEnum.h"
#include "SPIRVStream.h"
#include "SPIRVType.h"
#include <algorithm>
#include <map>
#include <set>
#include <sstream>
#include <string>
#include <utility>
using namespace SPIRV;
namespace SPIRV {
template <typename T> SPIRVEntry *create() { return new T(); }
SPIRVEntry *SPIRVEntry::create(Op OpCode) {
typedef SPIRVEntry *(*SPIRVFactoryTy)();
struct TableEntry {
Op Opn;
SPIRVFactoryTy Factory;
operator std::pair<const Op, SPIRVFactoryTy>() {
return std::make_pair(Opn, Factory);
}
};
static TableEntry Table[] = {
#define _SPIRV_OP(x, ...) {Op##x, &SPIRV::create<SPIRV##x>},
#define _SPIRV_OP_INTERNAL(x, ...) {internal::Op##x, &SPIRV::create<SPIRV##x>},
#include "SPIRVOpCodeEnum.h"
#include "SPIRVOpCodeEnumInternal.h"
#undef _SPIRV_OP_INTERNAL
#undef _SPIRV_OP
};
typedef std::unordered_map<Op, SPIRVFactoryTy> OpToFactoryMapTy;
static const OpToFactoryMapTy OpToFactoryMap(std::begin(Table),
std::end(Table));
// TODO: To remove this when we make a switch to new version
if (OpCode == internal::OpTypeJointMatrixINTELv2)
OpCode = internal::OpTypeJointMatrixINTEL;
// OpAtomicCompareExchangeWeak is removed starting from SPIR-V 1.4
if (OpCode == OpAtomicCompareExchangeWeak)
OpCode = OpAtomicCompareExchange;
OpToFactoryMapTy::const_iterator Loc = OpToFactoryMap.find(OpCode);
if (Loc != OpToFactoryMap.end())
return Loc->second();
SPIRVDBG(spvdbgs() << "No factory for OpCode " << (unsigned)OpCode << '\n';)
assert(0 && "Not implemented");
return 0;
}
std::unique_ptr<SPIRV::SPIRVEntry> SPIRVEntry::createUnique(Op OC) {
return std::unique_ptr<SPIRVEntry>(create(OC));
}
std::unique_ptr<SPIRV::SPIRVExtInst>
SPIRVEntry::createUnique(SPIRVExtInstSetKind Set, unsigned ExtOp) {
return std::unique_ptr<SPIRVExtInst>(new SPIRVExtInst(Set, ExtOp));
}
SPIRVErrorLog &SPIRVEntry::getErrorLog() const { return Module->getErrorLog(); }
bool SPIRVEntry::exist(SPIRVId TheId) const { return Module->exist(TheId); }
SPIRVEntry *SPIRVEntry::getOrCreate(SPIRVId TheId) const {
SPIRVEntry *Entry = nullptr;
bool Found = Module->exist(TheId, &Entry);
if (!Found)
return Module->addForward(TheId, nullptr);
return Entry;
}
SPIRVValue *SPIRVEntry::getValue(SPIRVId TheId) const {
return get<SPIRVValue>(TheId);
}
SPIRVType *SPIRVEntry::getValueType(SPIRVId TheId) const {
return get<SPIRVValue>(TheId)->getType();
}
SPIRVEncoder SPIRVEntry::getEncoder(spv_ostream &O) const {
return SPIRVEncoder(O);
}
SPIRVDecoder SPIRVEntry::getDecoder(std::istream &I) {
return SPIRVDecoder(I, *Module);
}
void SPIRVEntry::setWordCount(SPIRVWord TheWordCount) {
WordCount = TheWordCount;
}
void SPIRVEntry::setName(const std::string &TheName) {
Name = TheName;
SPIRVDBG(spvdbgs() << "Set name for obj " << Id << " " << Name << '\n');
}
void SPIRVEntry::setModule(SPIRVModule *TheModule) {
assert(TheModule && "Invalid module");
if (TheModule == Module)
return;
assert(Module == NULL && "Cannot change owner of entry");
Module = TheModule;
}
void SPIRVEntry::encode(spv_ostream &O) const {
assert(0 && "Not implemented");
}
void SPIRVEntry::encodeName(spv_ostream &O) const {
if (!Name.empty())
O << SPIRVName(this, Name);
}
bool SPIRVEntry::isEndOfBlock() const {
switch (OpCode) {
case OpBranch:
case OpBranchConditional:
case OpSwitch:
case OpKill:
case OpReturn:
case OpReturnValue:
case OpUnreachable:
return true;
default:
return false;
}
}
void SPIRVEntry::encodeLine(spv_ostream &O) const {
if (!Module)
return;
const std::shared_ptr<const SPIRVLine> &CurrLine = Module->getCurrentLine();
if (Line && (!CurrLine || *Line != *CurrLine)) {
O << *Line;
Module->setCurrentLine(Line);
}
if (isEndOfBlock() || OpCode == OpNoLine)
Module->setCurrentLine(nullptr);
}
namespace {
bool isDebugLineEqual(const SPIRVExtInst &DL1, const SPIRVExtInst &DL2) {
std::vector<SPIRVWord> DL1Args = DL1.getArguments();
std::vector<SPIRVWord> DL2Args = DL2.getArguments();
using namespace SPIRVDebug::Operand::DebugLine;
assert(DL1Args.size() == OperandCount && DL2Args.size() == OperandCount &&
"Invalid number of operands");
return DL1Args[SourceIdx] == DL2Args[SourceIdx] &&
DL1Args[StartIdx] == DL2Args[StartIdx] &&
DL1Args[EndIdx] == DL2Args[EndIdx] &&
DL1Args[ColumnStartIdx] == DL2Args[ColumnStartIdx] &&
DL1Args[ColumnEndIdx] == DL2Args[ColumnEndIdx];
}
} // namespace
void SPIRVEntry::encodeDebugLine(spv_ostream &O) const {
if (!Module)
return;
const std::shared_ptr<const SPIRVExtInst> &CurrDebugLine =
Module->getCurrentDebugLine();
if (DebugLine &&
(!CurrDebugLine || !isDebugLineEqual(*DebugLine, *CurrDebugLine))) {
O << *DebugLine;
Module->setCurrentDebugLine(DebugLine);
}
if (isEndOfBlock() ||
isExtInst(SPIRVEIS_NonSemantic_Shader_DebugInfo_100,
SPIRVDebug::DebugNoLine) ||
isExtInst(SPIRVEIS_NonSemantic_Shader_DebugInfo_200,
SPIRVDebug::DebugNoLine))
Module->setCurrentDebugLine(nullptr);
}
void SPIRVEntry::encodeAll(spv_ostream &O) const {
encodeLine(O);
encodeDebugLine(O);
encodeWordCountOpCode(O);
encode(O);
encodeChildren(O);
}
void SPIRVEntry::encodeChildren(spv_ostream &O) const {}
void SPIRVEntry::encodeWordCountOpCode(spv_ostream &O) const {
#ifdef _SPIRV_SUPPORT_TEXT_FMT
if (SPIRVUseTextFormat) {
getEncoder(O) << WordCount << OpCode;
return;
}
#endif
assert(WordCount < 65536 && "WordCount must fit into 16-bit value");
SPIRVWord WordCountOpCode = (WordCount << WordCountShift) | OpCode;
getEncoder(O) << WordCountOpCode;
}
// Read words from SPIRV binary and create members for SPIRVEntry.
// The word count and op code has already been read before calling this
// function for creating the SPIRVEntry. Therefore the input stream only
// contains the remaining part of the words for the SPIRVEntry.
void SPIRVEntry::decode(std::istream &I) { assert(0 && "Not implemented"); }
std::vector<SPIRVValue *>
SPIRVEntry::getValues(const std::vector<SPIRVId> &IdVec) const {
std::vector<SPIRVValue *> ValueVec;
for (auto I : IdVec)
ValueVec.push_back(getValue(I));
return ValueVec;
}
std::vector<SPIRVType *>
SPIRVEntry::getValueTypes(const std::vector<SPIRVId> &IdVec) const {
std::vector<SPIRVType *> TypeVec;
for (auto I : IdVec)
TypeVec.push_back(getValue(I)->getType());
return TypeVec;
}
std::vector<SPIRVId>
SPIRVEntry::getIds(const std::vector<SPIRVValue *> ValueVec) const {
std::vector<SPIRVId> IdVec;
for (auto *I : ValueVec)
IdVec.push_back(I->getId());
return IdVec;
}
SPIRVEntry *SPIRVEntry::getEntry(SPIRVId TheId) const {
return Module->getEntry(TheId);
}
void SPIRVEntry::validateFunctionControlMask(SPIRVWord TheFCtlMask) const {
SPIRVCK(isValidFunctionControlMask(TheFCtlMask), InvalidFunctionControlMask,
"");
}
void SPIRVEntry::validateValues(const std::vector<SPIRVId> &Ids) const {
for (auto I : Ids)
getValue(I)->validate();
}
void SPIRVEntry::validateBuiltin(SPIRVWord TheSet, SPIRVWord Index) const {
assert(TheSet != SPIRVWORD_MAX && Index != SPIRVWORD_MAX &&
"Invalid builtin");
}
void SPIRVEntry::addDecorate(SPIRVDecorate *Dec) {
auto Kind = Dec->getDecorateKind();
Decorates.insert(std::make_pair(Kind, Dec));
Module->addDecorate(Dec);
if (Kind == spv::DecorationLinkageAttributes) {
auto *LinkageAttr = static_cast<const SPIRVDecorateLinkageAttr *>(Dec);
setName(LinkageAttr->getLinkageName());
}
SPIRVDBG(spvdbgs() << "[addDecorate] Add "
<< SPIRVDecorationNameMap::map(Kind) << " to Id " << Id
<< '\n';)
}
void SPIRVEntry::addDecorate(SPIRVDecorateId *Dec) {
auto Kind = Dec->getDecorateKind();
DecorateIds.insert(std::make_pair(Kind, Dec));
Module->addDecorate(Dec);
SPIRVDBG(spvdbgs() << "[addDecorateId] Add"
<< SPIRVDecorationNameMap::map(Kind) << " to Id " << Id
<< '\n';)
}
void SPIRVEntry::addDecorate(Decoration Kind) {
addDecorate(new SPIRVDecorate(Kind, this));
}
void SPIRVEntry::addDecorate(Decoration Kind, SPIRVWord Literal) {
switch (static_cast<int>(Kind)) {
case DecorationAliasScopeINTEL:
case DecorationNoAliasINTEL:
addDecorate(new SPIRVDecorateId(Kind, this, Literal));
return;
default:
addDecorate(new SPIRVDecorate(Kind, this, Literal));
}
}
void SPIRVEntry::eraseDecorate(Decoration Dec) { Decorates.erase(Dec); }
void SPIRVEntry::takeDecorates(SPIRVEntry *E) {
Decorates = std::move(E->Decorates);
SPIRVDBG(spvdbgs() << "[takeDecorates] " << Id << '\n';)
}
void SPIRVEntry::takeDecorateIds(SPIRVEntry *E) {
DecorateIds = std::move(E->DecorateIds);
SPIRVDBG(spvdbgs() << "[takeDecorateIds] " << Id << '\n';)
}
void SPIRVEntry::setLine(const std::shared_ptr<const SPIRVLine> &L) {
Line = L;
SPIRVDBG(if (L) spvdbgs() << "[setLine] " << *L << '\n';)
}
void SPIRVEntry::setDebugLine(const std::shared_ptr<const SPIRVExtInst> &DL) {
DebugLine = DL;
SPIRVDBG(if (DL) spvdbgs() << "[setDebugLine] " << *DL << '\n';)
}
void SPIRVEntry::addMemberDecorate(SPIRVMemberDecorate *Dec) {
assert(canHaveMemberDecorates());
MemberDecorates.insert(std::make_pair(
std::make_pair(Dec->getMemberNumber(), Dec->getDecorateKind()), Dec));
Module->addDecorate(Dec);
SPIRVDBG(spvdbgs() << "[addMemberDecorate] " << *Dec << '\n';)
}
void SPIRVEntry::addMemberDecorate(SPIRVWord MemberNumber, Decoration Kind) {
addMemberDecorate(new SPIRVMemberDecorate(Kind, MemberNumber, this));
}
void SPIRVEntry::addMemberDecorate(SPIRVWord MemberNumber, Decoration Kind,
SPIRVWord Literal) {
addMemberDecorate(new SPIRVMemberDecorate(Kind, MemberNumber, this, Literal));
}
void SPIRVEntry::eraseMemberDecorate(SPIRVWord MemberNumber, Decoration Dec) {
MemberDecorates.erase(std::make_pair(MemberNumber, Dec));
}
void SPIRVEntry::takeMemberDecorates(SPIRVEntry *E) {
MemberDecorates = std::move(E->MemberDecorates);
SPIRVDBG(spvdbgs() << "[takeMemberDecorates] " << Id << '\n';)
}
void SPIRVEntry::takeAnnotations(SPIRVForward *E) {
Module->setName(this, E->getName());
takeDecorates(E);
takeDecorateIds(E);
takeMemberDecorates(E);
if (OpCode == OpFunction)
static_cast<SPIRVFunction *>(this)->takeExecutionModes(E);
}
void SPIRVEntry::replaceTargetIdInDecorates(SPIRVId Id) {
for (auto It = Decorates.begin(), E = Decorates.end(); It != E; ++It)
const_cast<SPIRVDecorate *>(It->second)->setTargetId(Id);
for (auto It = DecorateIds.begin(), E = DecorateIds.end(); It != E; ++It)
const_cast<SPIRVDecorateId *>(It->second)->setTargetId(Id);
for (auto It = MemberDecorates.begin(), E = MemberDecorates.end(); It != E;
++It)
const_cast<SPIRVMemberDecorate *>(It->second)->setTargetId(Id);
}
// Check if an entry has Kind of decoration and get the literal of the
// first decoration of such kind at Index.
bool SPIRVEntry::hasDecorate(Decoration Kind, size_t Index,
SPIRVWord *Result) const {
auto Loc = Decorates.find(Kind);
if (Loc == Decorates.end())
return false;
if (Result)
*Result = Loc->second->getLiteral(Index);
return true;
}
bool SPIRVEntry::hasDecorateId(Decoration Kind, size_t Index,
SPIRVId *Result) const {
auto Loc = DecorateIds.find(Kind);
if (Loc == DecorateIds.end())
return false;
if (Result)
*Result = Loc->second->getLiteral(Index);
return true;
}
// Check if an entry member has Kind of decoration and get the literal of the
// first decoration of such kind at Index.
bool SPIRVEntry::hasMemberDecorate(Decoration Kind, size_t Index,
SPIRVWord MemberNumber,
SPIRVWord *Result) const {
auto Loc = MemberDecorates.find({MemberNumber, Kind});
if (Loc == MemberDecorates.end())
return false;
if (Result)
*Result = Loc->second->getLiteral(Index);
return true;
}
std::vector<std::string>
SPIRVEntry::getDecorationStringLiteral(Decoration Kind) const {
auto Loc = Decorates.find(Kind);
if (Loc == Decorates.end())
return {};
return getVecString(Loc->second->getVecLiteral());
}
std::vector<std::string>
SPIRVEntry::getMemberDecorationStringLiteral(Decoration Kind,
SPIRVWord MemberNumber) const {
auto Loc = MemberDecorates.find({MemberNumber, Kind});
if (Loc == MemberDecorates.end())
return {};
return getVecString(Loc->second->getVecLiteral());
}
std::vector<std::vector<std::string>>
SPIRVEntry::getAllDecorationStringLiterals(Decoration Kind) const {
auto Loc = Decorates.find(Kind);
if (Loc == Decorates.end())
return {};
std::vector<std::vector<std::string>> Literals;
auto It = Decorates.equal_range(Kind);
for (auto Itr = It.first; Itr != It.second; ++Itr)
Literals.push_back(getVecString(Itr->second->getVecLiteral()));
return Literals;
}
std::vector<std::vector<std::string>>
SPIRVEntry::getAllMemberDecorationStringLiterals(Decoration Kind,
SPIRVWord MemberNumber) const {
auto Loc = MemberDecorates.find({MemberNumber, Kind});
if (Loc == MemberDecorates.end())
return {};
std::vector<std::vector<std::string>> Literals;
auto It = MemberDecorates.equal_range({MemberNumber, Kind});
for (auto Itr = It.first; Itr != It.second; ++Itr)
Literals.push_back(getVecString(Itr->second->getVecLiteral()));
return Literals;
}
std::vector<SPIRVWord>
SPIRVEntry::getDecorationLiterals(Decoration Kind) const {
auto Loc = Decorates.find(Kind);
if (Loc == Decorates.end())
return {};
return (Loc->second->getVecLiteral());
}
std::vector<SPIRVId>
SPIRVEntry::getDecorationIdLiterals(Decoration Kind) const {
auto Loc = DecorateIds.find(Kind);
if (Loc == DecorateIds.end())
return {};
return (Loc->second->getVecLiteral());
}
std::vector<SPIRVWord>
SPIRVEntry::getMemberDecorationLiterals(Decoration Kind,
SPIRVWord MemberNumber) const {
auto Loc = MemberDecorates.find({MemberNumber, Kind});
if (Loc == MemberDecorates.end())
return {};
return (Loc->second->getVecLiteral());
}
// Get literals of all decorations of Kind at Index.
std::set<SPIRVWord> SPIRVEntry::getDecorate(Decoration Kind,
size_t Index) const {
auto Range = Decorates.equal_range(Kind);
std::set<SPIRVWord> Value;
for (auto I = Range.first, E = Range.second; I != E; ++I) {
assert(Index < I->second->getLiteralCount() && "Invalid index");
Value.insert(I->second->getLiteral(Index));
}
return Value;
}
std::vector<SPIRVDecorate const *>
SPIRVEntry::getDecorations(Decoration Kind) const {
auto Range = Decorates.equal_range(Kind);
std::vector<SPIRVDecorate const *> Decors;
Decors.reserve(Decorates.count(Kind));
for (auto I = Range.first, E = Range.second; I != E; ++I) {
Decors.push_back(I->second);
}
return Decors;
}
std::vector<SPIRVDecorate const *> SPIRVEntry::getDecorations() const {
std::vector<SPIRVDecorate const *> Decors;
Decors.reserve(Decorates.size());
for (auto &DecoPair : Decorates)
Decors.push_back(DecoPair.second);
return Decors;
}
std::set<SPIRVId> SPIRVEntry::getDecorateId(Decoration Kind,
size_t Index) const {
auto Range = DecorateIds.equal_range(Kind);
std::set<SPIRVId> Value;
for (auto I = Range.first, E = Range.second; I != E; ++I) {
assert(Index < I->second->getLiteralCount() && "Invalid index");
Value.insert(I->second->getLiteral(Index));
}
return Value;
}
std::vector<SPIRVDecorateId const *>
SPIRVEntry::getDecorationIds(Decoration Kind) const {
auto Range = DecorateIds.equal_range(Kind);
std::vector<SPIRVDecorateId const *> Decors;
Decors.reserve(DecorateIds.count(Kind));
for (auto I = Range.first, E = Range.second; I != E; ++I) {
Decors.push_back(I->second);
}
return Decors;
}
bool SPIRVEntry::hasLinkageType() const {
return OpCode == OpFunction || OpCode == OpVariable ||
OpCode == OpUntypedVariableKHR;
}
bool SPIRVEntry::isExtInst(const SPIRVExtInstSetKind InstSet) const {
if (isExtInst()) {
const SPIRVExtInst *EI = static_cast<const SPIRVExtInst *>(this);
return EI->getExtSetKind() == InstSet;
}
return false;
}
bool SPIRVEntry::isExtInst(const SPIRVExtInstSetKind InstSet,
const SPIRVWord ExtOp) const {
if (isExtInst()) {
const SPIRVExtInst *EI = static_cast<const SPIRVExtInst *>(this);
if (EI->getExtSetKind() == InstSet) {
return EI->getExtOp() == ExtOp;
}
}
return false;
}
void SPIRVEntry::encodeDecorate(spv_ostream &O) const {
for (auto &I : Decorates)
O << *I.second;
for (auto &I : DecorateIds)
O << *I.second;
}
SPIRVLinkageTypeKind SPIRVEntry::getLinkageType() const {
assert(hasLinkageType());
DecorateMapType::const_iterator Loc =
Decorates.find(DecorationLinkageAttributes);
if (Loc == Decorates.end())
return internal::LinkageTypeInternal;
return static_cast<const SPIRVDecorateLinkageAttr *>(Loc->second)
->getLinkageType();
}
void SPIRVEntry::setLinkageType(SPIRVLinkageTypeKind LT) {
assert(isValid(LT));
assert(hasLinkageType());
addDecorate(new SPIRVDecorateLinkageAttr(this, Name, LT));
}
void SPIRVEntry::updateModuleVersion() const {
if (!Module)
return;
Module->setMinSPIRVVersion(getRequiredSPIRVVersion());
}
spv_ostream &operator<<(spv_ostream &O, const SPIRVEntry &E) {
E.validate();
E.encodeAll(O);
O << SPIRVNL();
return O;
}
std::istream &operator>>(std::istream &I, SPIRVEntry &E) {
E.decode(I);
return I;
}
SPIRVEntryPoint::SPIRVEntryPoint(SPIRVModule *TheModule,
SPIRVExecutionModelKind TheExecModel,
SPIRVId TheId, const std::string &TheName,
std::vector<SPIRVId> Variables)
: SPIRVAnnotation(OpEntryPoint, TheModule->get<SPIRVFunction>(TheId),
getSizeInWords(TheName) + Variables.size() + 3),
ExecModel(TheExecModel), Name(TheName), Variables(Variables) {}
void SPIRVEntryPoint::encode(spv_ostream &O) const {
getEncoder(O) << ExecModel << Target << Name << Variables;
}
void SPIRVEntryPoint::decode(std::istream &I) {
getDecoder(I) >> ExecModel >> Target >> Name;
Variables.resize(WordCount - FixedWC - getSizeInWords(Name) + 1);
getDecoder(I) >> Variables;
Module->setName(getOrCreateTarget(), Name);
Module->addEntryPoint(ExecModel, Target, Name, Variables);
}
void SPIRVExecutionMode::encode(spv_ostream &O) const {
getEncoder(O) << Target << ExecMode << WordLiterals;
}
void SPIRVExecutionMode::decode(std::istream &I) {
getDecoder(I) >> Target >> ExecMode;
switch (static_cast<uint32_t>(ExecMode)) {
case ExecutionModeLocalSize:
case ExecutionModeLocalSizeId:
case ExecutionModeLocalSizeHint:
case ExecutionModeLocalSizeHintId:
case ExecutionModeMaxWorkgroupSizeINTEL:
WordLiterals.resize(3);
break;
case ExecutionModeInvocations:
case ExecutionModeOutputVertices:
case ExecutionModeVecTypeHint:
case ExecutionModeDenormPreserve:
case ExecutionModeDenormFlushToZero:
case ExecutionModeSignedZeroInfNanPreserve:
case ExecutionModeRoundingModeRTE:
case ExecutionModeRoundingModeRTZ:
case ExecutionModeRoundingModeRTPINTEL:
case ExecutionModeRoundingModeRTNINTEL:
case ExecutionModeFloatingPointModeALTINTEL:
case ExecutionModeFloatingPointModeIEEEINTEL:
case ExecutionModeSharedLocalMemorySizeINTEL:
case ExecutionModeNamedBarrierCountINTEL:
case ExecutionModeSubgroupSize:
case ExecutionModeSubgroupsPerWorkgroup:
case ExecutionModeSubgroupsPerWorkgroupId:
case ExecutionModeMaxWorkDimINTEL:
case ExecutionModeNumSIMDWorkitemsINTEL:
case ExecutionModeSchedulerTargetFmaxMhzINTEL:
case ExecutionModeRegisterMapInterfaceINTEL:
case ExecutionModeStreamingInterfaceINTEL:
case spv::internal::ExecutionModeNamedSubgroupSizeINTEL:
case ExecutionModeMaximumRegistersINTEL:
case ExecutionModeMaximumRegistersIdINTEL:
case ExecutionModeNamedMaximumRegistersINTEL:
WordLiterals.resize(1);
break;
default:
// Do nothing. Keep this to avoid VS2013 warning.
break;
}
getDecoder(I) >> WordLiterals;
getOrCreateTarget()->addExecutionMode(Module->add(this));
}
SPIRVForward *SPIRVAnnotationGeneric::getOrCreateTarget() const {
SPIRVEntry *Entry = nullptr;
bool Found = Module->exist(Target, &Entry);
assert((!Found || Entry->getOpCode() == internal::OpForward) &&
"Annotations only allowed on forward");
if (!Found)
Entry = Module->addForward(Target, nullptr);
return static_cast<SPIRVForward *>(Entry);
}
SPIRVName::SPIRVName(const SPIRVEntry *TheTarget, const std::string &TheStr)
: SPIRVAnnotation(OpName, TheTarget, getSizeInWords(TheStr) + 2),
Str(TheStr) {}
void SPIRVName::encode(spv_ostream &O) const { getEncoder(O) << Target << Str; }
void SPIRVName::decode(std::istream &I) {
getDecoder(I) >> Target >> Str;
Module->setName(getOrCreateTarget(), Str);
}
void SPIRVName::validate() const {
assert(WordCount == getSizeInWords(Str) + 2 && "Incorrect word count");
}
_SPIRV_IMP_ENCDEC2(SPIRVString, Id, Str)
_SPIRV_IMP_ENCDEC3(SPIRVMemberName, Target, MemberNumber, Str)
void SPIRVLine::encode(spv_ostream &O) const {
getEncoder(O) << FileName << Line << Column;
}
void SPIRVLine::decode(std::istream &I) {
getDecoder(I) >> FileName >> Line >> Column;
}
void SPIRVLine::validate() const {
assert(OpCode == OpLine);
assert(WordCount == 4);
assert(get<SPIRVEntry>(FileName)->getOpCode() == OpString);
assert(Line != SPIRVWORD_MAX);
assert(Column != SPIRVWORD_MAX);
assert(!hasId());
}
void SPIRVMemberName::validate() const {
assert(OpCode == OpMemberName);
assert(WordCount == getSizeInWords(Str) + FixedWC);
assert(get<SPIRVEntry>(Target)->getOpCode() == OpTypeStruct);
assert(MemberNumber < get<SPIRVTypeStruct>(Target)->getStructMemberCount());
}
SPIRVExtInstImport::SPIRVExtInstImport(SPIRVModule *TheModule, SPIRVId TheId,
const std::string &TheStr)
: SPIRVEntry(TheModule, 2 + getSizeInWords(TheStr), OC, TheId),
Str(TheStr) {
validate();
}
void SPIRVExtInstImport::encode(spv_ostream &O) const {
getEncoder(O) << Id << Str;
}
void SPIRVExtInstImport::decode(std::istream &I) {
getDecoder(I) >> Id >> Str;
Module->importBuiltinSetWithId(Str, Id);
}
void SPIRVExtInstImport::validate() const {
SPIRVEntry::validate();
assert(!Str.empty() && "Invalid builtin set");
}
void SPIRVMemoryModel::encode(spv_ostream &O) const {
getEncoder(O) << Module->getAddressingModel() << Module->getMemoryModel();
}
void SPIRVMemoryModel::decode(std::istream &I) {
SPIRVAddressingModelKind AddrModel;
SPIRVMemoryModelKind MemModel;
getDecoder(I) >> AddrModel >> MemModel;
Module->setAddressingModel(AddrModel);
Module->setMemoryModel(MemModel);
}
void SPIRVMemoryModel::validate() const {
auto AM = Module->getAddressingModel();
auto MM = Module->getMemoryModel();
SPIRVCK(isValid(AM), InvalidAddressingModel,
"Actual is " + std::to_string(AM));
SPIRVCK(isValid(MM), InvalidMemoryModel, "Actual is " + std::to_string(MM));
}
void SPIRVSource::encode(spv_ostream &O) const {
SPIRVWord Ver = SPIRVWORD_MAX;
auto Language = Module->getSourceLanguage(&Ver);
getEncoder(O) << Language << Ver;
}
void SPIRVSource::decode(std::istream &I) {
SourceLanguage Lang = SourceLanguageUnknown;
SPIRVWord Ver = SPIRVWORD_MAX;
getDecoder(I) >> Lang >> Ver;
Module->setSourceLanguage(Lang, Ver);
}
SPIRVSourceExtension::SPIRVSourceExtension(SPIRVModule *M,
const std::string &SS)
: SPIRVEntryNoId(M, 1 + getSizeInWords(SS)), S(SS) {}
void SPIRVSourceExtension::encode(spv_ostream &O) const { getEncoder(O) << S; }
void SPIRVSourceExtension::decode(std::istream &I) {
getDecoder(I) >> S;
Module->getSourceExtension().insert(S);
}
SPIRVExtension::SPIRVExtension(SPIRVModule *M, const std::string &SS)
: SPIRVEntryNoId(M, 1 + getSizeInWords(SS)), S(SS) {}
void SPIRVExtension::encode(spv_ostream &O) const { getEncoder(O) << S; }
void SPIRVExtension::decode(std::istream &I) {
getDecoder(I) >> S;
Module->getExtension().insert(S);
}
SPIRVCapability::SPIRVCapability(SPIRVModule *M, SPIRVCapabilityKind K)
: SPIRVEntryNoId(M, 2), Kind(K) {
updateModuleVersion();
}
void SPIRVCapability::encode(spv_ostream &O) const { getEncoder(O) << Kind; }
void SPIRVCapability::decode(std::istream &I) {
getDecoder(I) >> Kind;
Module->addCapability(Kind);
}
template <spv::Op OC> void SPIRVContinuedInstINTELBase<OC>::validate() const {
SPIRVEntry::validate();
}
template <spv::Op OC>
void SPIRVContinuedInstINTELBase<OC>::encode(spv_ostream &O) const {
SPIRVEntry::getEncoder(O) << (Elements);
}
template <spv::Op OC>
void SPIRVContinuedInstINTELBase<OC>::decode(std::istream &I) {
SPIRVEntry::getDecoder(I) >> (Elements);
}
SPIRVType *SPIRVTypeStructContinuedINTEL::getMemberType(size_t I) const {
return static_cast<SPIRVType *>(SPIRVEntry::getEntry(Elements[I]));
}
void SPIRVModuleProcessed::validate() const {
assert(WordCount == FixedWC + getSizeInWords(ProcessStr) &&
"Incorrect word count in OpModuleProcessed");
}
void SPIRVModuleProcessed::encode(spv_ostream &O) const {
getEncoder(O) << ProcessStr;
}
void SPIRVModuleProcessed::decode(std::istream &I) {
getDecoder(I) >> ProcessStr;
Module->addModuleProcessed(ProcessStr);
}
std::string SPIRVModuleProcessed::getProcessStr() { return ProcessStr; }
} // namespace SPIRV
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,631 @@
//===- SPIRVEnum.h - SPIR-V enums -------------------------------*- C++ -*-===//
//
// The LLVM/SPIRV Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
//
// 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 Advanced Micro Devices, Inc., 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.
//
//===----------------------------------------------------------------------===//
/// \file
///
/// This file defines SPIR-V enums.
///
//===----------------------------------------------------------------------===//
#ifndef SPIRV_LIBSPIRV_SPIRVENUM_H
#define SPIRV_LIBSPIRV_SPIRVENUM_H
#include "LLVMSPIRVOpts.h"
#include "SPIRVOpCode.h"
#include "spirv/unified1/spirv.hpp"
#include "spirv_internal.hpp"
#include <cstdint>
using namespace spv;
namespace SPIRV {
// SPIR-V specification p2.2.1. "Instructions":
// - SPIR-V Word size is 32 bits.
// - an <id> always consumes one word.
typedef uint32_t SPIRVWord;
typedef uint32_t SPIRVId;
#define SPIRVID_MAX ~0U
#define SPIRVID_INVALID ~0U
#define SPIRVWORD_MAX ~0U
static constexpr unsigned SpirvWordSize =
static_cast<unsigned>(sizeof(SPIRVWord));
static constexpr unsigned SpirvWordBitWidth = 32;
inline bool isValidId(SPIRVId Id) { return Id != SPIRVID_INVALID && Id != 0; }
const static unsigned KSpirvMemOrderSemanticMask = 0x1F;
enum SPIRVGeneratorKind {
SPIRVGEN_KhronosLLVMSPIRVTranslator = 6,
SPIRVGEN_KhronosSPIRVAssembler = 7,
};
enum SPIRVInstructionSchemaKind {
SPIRVISCH_Default,
};
enum SPIRVExtInstSetKind {
SPIRVEIS_OpenCL,
SPIRVEIS_Debug,
SPIRVEIS_OpenCL_DebugInfo_100,
SPIRVEIS_NonSemantic_Shader_DebugInfo_100,
SPIRVEIS_NonSemantic_Shader_DebugInfo_200,
SPIRVEIS_NonSemantic_AuxData,
SPIRVEIS_Count,
};
enum SPIRVSamplerAddressingModeKind {
SPIRVSAM_None = 0,
SPIRVSAM_ClampEdge = 2,
SPIRVSAM_Clamp = 4,
SPIRVSAM_Repeat = 6,
SPIRVSAM_RepeatMirrored = 8,
SPIRVSAM_Invalid = 255,
};
enum SPIRVSamplerFilterModeKind {
SPIRVSFM_Nearest = 16,
SPIRVSFM_Linear = 32,
SPIRVSFM_Invalid = 255,
};
typedef spv::Capability SPIRVCapabilityKind;
typedef spv::ExecutionModel SPIRVExecutionModelKind;
typedef spv::ExecutionMode SPIRVExecutionModeKind;
typedef spv::AccessQualifier SPIRVAccessQualifierKind;
typedef spv::AddressingModel SPIRVAddressingModelKind;
typedef spv::LinkageType SPIRVLinkageTypeKind;
typedef spv::MemoryModel SPIRVMemoryModelKind;
typedef spv::StorageClass SPIRVStorageClassKind;
typedef spv::FunctionControlMask SPIRVFunctionControlMaskKind;
typedef spv::FPRoundingMode SPIRVFPRoundingModeKind;
typedef spv::FunctionParameterAttribute SPIRVFuncParamAttrKind;
typedef spv::BuiltIn SPIRVBuiltinVariableKind;
typedef spv::MemoryAccessMask SPIRVMemoryAccessKind;
typedef spv::GroupOperation SPIRVGroupOperationKind;
typedef spv::Dim SPIRVImageDimKind;
typedef std::vector<SPIRVCapabilityKind> SPIRVCapVec;
typedef std::set<ExtensionID> SPIRVExtSet;
template <> inline void SPIRVMap<ExtensionID, std::string>::init() {
#define _STRINGIFY(X) #X
#define STRINGIFY(X) _STRINGIFY(X)
#define EXT(X) add(ExtensionID::X, STRINGIFY(X));
#include "LLVMSPIRVExtensions.inc"
#undef EXT
#undef STRINGIFY
#undef _STRINGIFY
}
template <> inline void SPIRVMap<SPIRVExtInstSetKind, std::string>::init() {
add(SPIRVEIS_OpenCL, "OpenCL.std");
add(SPIRVEIS_Debug, "SPIRV.debug");
add(SPIRVEIS_OpenCL_DebugInfo_100, "OpenCL.DebugInfo.100");
add(SPIRVEIS_NonSemantic_Shader_DebugInfo_100,
"NonSemantic.Shader.DebugInfo.100");
add(SPIRVEIS_NonSemantic_Shader_DebugInfo_200,
"NonSemantic.Shader.DebugInfo.200");
add(SPIRVEIS_NonSemantic_AuxData, "NonSemantic.AuxData");
}
typedef SPIRVMap<SPIRVExtInstSetKind, std::string> SPIRVBuiltinSetNameMap;
template <typename K> SPIRVCapVec getCapability(K Key) {
SPIRVCapVec V;
SPIRVMap<K, SPIRVCapVec>::find(Key, &V);
return V;
}
#define ADD_VEC_INIT(Cap, ...) \
{ \
SPIRVCapabilityKind C[] = __VA_ARGS__; \
SPIRVCapVec V(C, C + sizeof(C) / sizeof(C[0])); \
add(Cap, V); \
}
template <> inline void SPIRVMap<SPIRVCapabilityKind, SPIRVCapVec>::init() {
ADD_VEC_INIT(CapabilityShader, {CapabilityMatrix});
ADD_VEC_INIT(CapabilityGeometry, {CapabilityShader});
ADD_VEC_INIT(CapabilityTessellation, {CapabilityShader});
ADD_VEC_INIT(CapabilityVector16, {CapabilityKernel});
ADD_VEC_INIT(CapabilityFloat16Buffer, {CapabilityKernel});
ADD_VEC_INIT(CapabilityInt64Atomics, {CapabilityInt64});
ADD_VEC_INIT(CapabilityImageBasic, {CapabilityKernel});
ADD_VEC_INIT(CapabilityImageReadWrite, {CapabilityImageBasic});
ADD_VEC_INIT(CapabilityImageMipmap, {CapabilityImageBasic});
ADD_VEC_INIT(CapabilityPipes, {CapabilityKernel});
ADD_VEC_INIT(CapabilityBlockingPipesINTEL, {CapabilityKernel});
ADD_VEC_INIT(CapabilityDeviceEnqueue, {CapabilityKernel});
ADD_VEC_INIT(CapabilityLiteralSampler, {CapabilityKernel});
ADD_VEC_INIT(CapabilityAtomicStorage, {CapabilityShader});
ADD_VEC_INIT(CapabilityTessellationPointSize, {CapabilityTessellation});
ADD_VEC_INIT(CapabilityGeometryPointSize, {CapabilityGeometry});
ADD_VEC_INIT(CapabilityImageGatherExtended, {CapabilityShader});
ADD_VEC_INIT(CapabilityStorageImageMultisample, {CapabilityShader});
ADD_VEC_INIT(CapabilityUniformBufferArrayDynamicIndexing, {CapabilityShader});
ADD_VEC_INIT(CapabilitySampledImageArrayDynamicIndexing, {CapabilityShader});
ADD_VEC_INIT(CapabilityStorageBufferArrayDynamicIndexing, {CapabilityShader});
ADD_VEC_INIT(CapabilityStorageImageArrayDynamicIndexing, {CapabilityShader});
ADD_VEC_INIT(CapabilityClipDistance, {CapabilityShader});
ADD_VEC_INIT(CapabilityCullDistance, {CapabilityShader});
ADD_VEC_INIT(CapabilityImageCubeArray, {CapabilitySampledCubeArray});
ADD_VEC_INIT(CapabilitySampleRateShading, {CapabilityShader});
ADD_VEC_INIT(CapabilityImageRect, {CapabilitySampledRect});
ADD_VEC_INIT(CapabilitySampledRect, {CapabilityShader});
ADD_VEC_INIT(CapabilityGenericPointer, {CapabilityAddresses});
ADD_VEC_INIT(CapabilityInt8, {CapabilityKernel});
ADD_VEC_INIT(CapabilityInputAttachment, {CapabilityShader});
ADD_VEC_INIT(CapabilitySparseResidency, {CapabilityShader});
ADD_VEC_INIT(CapabilityMinLod, {CapabilityShader});
ADD_VEC_INIT(CapabilityImage1D, {CapabilitySampled1D});
ADD_VEC_INIT(CapabilitySampledCubeArray, {CapabilityShader});
ADD_VEC_INIT(CapabilityImageBuffer, {CapabilitySampledBuffer});
ADD_VEC_INIT(CapabilityImageMSArray, {CapabilityShader});
ADD_VEC_INIT(CapabilityStorageImageExtendedFormats, {CapabilityShader});
ADD_VEC_INIT(CapabilityImageQuery, {CapabilityShader});
ADD_VEC_INIT(CapabilityDerivativeControl, {CapabilityShader});
ADD_VEC_INIT(CapabilityInterpolationFunction, {CapabilityShader});
ADD_VEC_INIT(CapabilityTransformFeedback, {CapabilityShader});
ADD_VEC_INIT(CapabilityGeometryStreams, {CapabilityGeometry});
ADD_VEC_INIT(CapabilityStorageImageReadWithoutFormat, {CapabilityShader});
ADD_VEC_INIT(CapabilityStorageImageWriteWithoutFormat, {CapabilityShader});
ADD_VEC_INIT(CapabilityMultiViewport, {CapabilityGeometry});
ADD_VEC_INIT(CapabilitySubgroupAvcMotionEstimationINTEL, {CapabilityGroups});
ADD_VEC_INIT(CapabilitySubgroupAvcMotionEstimationIntraINTEL,
{CapabilitySubgroupAvcMotionEstimationINTEL});
ADD_VEC_INIT(CapabilitySubgroupAvcMotionEstimationChromaINTEL,
{CapabilitySubgroupAvcMotionEstimationIntraINTEL});
ADD_VEC_INIT(internal::CapabilityJointMatrixWIInstructionsINTEL,
{internal::CapabilityJointMatrixINTEL});
ADD_VEC_INIT(internal::CapabilityJointMatrixTF32ComponentTypeINTEL,
{internal::CapabilityJointMatrixINTEL});
ADD_VEC_INIT(internal::CapabilityJointMatrixBF16ComponentTypeINTEL,
{internal::CapabilityJointMatrixINTEL});
ADD_VEC_INIT(internal::CapabilityJointMatrixPackedInt2ComponentTypeINTEL,
{internal::CapabilityJointMatrixINTEL});
ADD_VEC_INIT(internal::CapabilityJointMatrixPackedInt4ComponentTypeINTEL,
{internal::CapabilityJointMatrixINTEL});
ADD_VEC_INIT(internal::CapabilityCooperativeMatrixPrefetchINTEL,
{CapabilityCooperativeMatrixKHR});
ADD_VEC_INIT(internal::CapabilityCooperativeMatrixInvocationInstructionsINTEL,
{CapabilityCooperativeMatrixKHR});
ADD_VEC_INIT(internal::CapabilityCooperativeMatrixCheckedInstructionsINTEL,
{CapabilityCooperativeMatrixKHR});
ADD_VEC_INIT(internal::CapabilityCooperativeMatrixOffsetInstructionsINTEL,
{CapabilityCooperativeMatrixKHR});
ADD_VEC_INIT(CapabilityBFloat16DotProductKHR, {CapabilityBFloat16TypeKHR});
ADD_VEC_INIT(CapabilityBFloat16CooperativeMatrixKHR,
{CapabilityBFloat16TypeKHR, CapabilityCooperativeMatrixKHR});
ADD_VEC_INIT(CapabilityInt4CooperativeMatrixINTEL,
{CapabilityInt4TypeINTEL, CapabilityCooperativeMatrixKHR});
ADD_VEC_INIT(internal::CapabilityBFloat16ArithmeticINTEL,
{CapabilityBFloat16TypeKHR});
ADD_VEC_INIT(internal::CapabilityAtomicInt16CompareExchangeINTEL,
{CapabilityInt16});
ADD_VEC_INIT(internal::CapabilityInt16AtomicsINTEL,
{internal::CapabilityAtomicInt16CompareExchangeINTEL});
ADD_VEC_INIT(internal::CapabilityAtomicBFloat16LoadStoreINTEL,
{CapabilityBFloat16TypeKHR});
ADD_VEC_INIT(internal::CapabilityAtomicBFloat16AddINTEL,
{CapabilityBFloat16TypeKHR});
ADD_VEC_INIT(internal::CapabilityAtomicBFloat16MinMaxINTEL,
{CapabilityBFloat16TypeKHR});
}
template <> inline void SPIRVMap<SPIRVExecutionModelKind, SPIRVCapVec>::init() {
ADD_VEC_INIT(ExecutionModelVertex, {CapabilityShader});
ADD_VEC_INIT(ExecutionModelTessellationControl, {CapabilityTessellation});
ADD_VEC_INIT(ExecutionModelTessellationEvaluation, {CapabilityTessellation});
ADD_VEC_INIT(ExecutionModelGeometry, {CapabilityGeometry});
ADD_VEC_INIT(ExecutionModelFragment, {CapabilityShader});
ADD_VEC_INIT(ExecutionModelGLCompute, {CapabilityShader});
ADD_VEC_INIT(ExecutionModelKernel, {CapabilityKernel});
}
template <> inline void SPIRVMap<SPIRVExecutionModeKind, SPIRVCapVec>::init() {
ADD_VEC_INIT(ExecutionModeInvocations, {CapabilityGeometry});
ADD_VEC_INIT(ExecutionModeSpacingEqual, {CapabilityTessellation});
ADD_VEC_INIT(ExecutionModeSpacingFractionalEven, {CapabilityTessellation});
ADD_VEC_INIT(ExecutionModeSpacingFractionalOdd, {CapabilityTessellation});
ADD_VEC_INIT(ExecutionModeVertexOrderCw, {CapabilityTessellation});
ADD_VEC_INIT(ExecutionModeVertexOrderCcw, {CapabilityTessellation});
ADD_VEC_INIT(ExecutionModePixelCenterInteger, {CapabilityShader});
ADD_VEC_INIT(ExecutionModeOriginUpperLeft, {CapabilityShader});
ADD_VEC_INIT(ExecutionModeOriginLowerLeft, {CapabilityShader});
ADD_VEC_INIT(ExecutionModeEarlyFragmentTests, {CapabilityShader});
ADD_VEC_INIT(ExecutionModePointMode, {CapabilityTessellation});
ADD_VEC_INIT(ExecutionModeXfb, {CapabilityTransformFeedback});
ADD_VEC_INIT(ExecutionModeDepthReplacing, {CapabilityShader});
ADD_VEC_INIT(ExecutionModeDepthGreater, {CapabilityShader});
ADD_VEC_INIT(ExecutionModeDepthLess, {CapabilityShader});
ADD_VEC_INIT(ExecutionModeDepthUnchanged, {CapabilityShader});
ADD_VEC_INIT(ExecutionModeLocalSizeHint, {CapabilityKernel});
ADD_VEC_INIT(ExecutionModeInputPoints, {CapabilityGeometry});
ADD_VEC_INIT(ExecutionModeInputLines, {CapabilityGeometry});
ADD_VEC_INIT(ExecutionModeInputLinesAdjacency, {CapabilityGeometry});
ADD_VEC_INIT(ExecutionModeTriangles,
{CapabilityGeometry, CapabilityTessellation});
ADD_VEC_INIT(ExecutionModeInputTrianglesAdjacency, {CapabilityGeometry});
ADD_VEC_INIT(ExecutionModeQuads, {CapabilityTessellation});
ADD_VEC_INIT(ExecutionModeIsolines, {CapabilityTessellation});
ADD_VEC_INIT(ExecutionModeOutputVertices,
{CapabilityGeometry, CapabilityTessellation});
ADD_VEC_INIT(ExecutionModeOutputPoints, {CapabilityGeometry});
ADD_VEC_INIT(ExecutionModeOutputLineStrip, {CapabilityGeometry});
ADD_VEC_INIT(ExecutionModeOutputTriangleStrip, {CapabilityGeometry});
ADD_VEC_INIT(ExecutionModeVecTypeHint, {CapabilityKernel});
ADD_VEC_INIT(ExecutionModeContractionOff, {CapabilityKernel});
ADD_VEC_INIT(ExecutionModeSubgroupSize, {CapabilitySubgroupDispatch});
ADD_VEC_INIT(ExecutionModeDenormPreserve, {CapabilityDenormPreserve});
ADD_VEC_INIT(ExecutionModeDenormFlushToZero, {CapabilityDenormFlushToZero});
ADD_VEC_INIT(ExecutionModeSignedZeroInfNanPreserve,
{CapabilitySignedZeroInfNanPreserve});
ADD_VEC_INIT(ExecutionModeRoundingModeRTE, {CapabilityRoundingModeRTE});
ADD_VEC_INIT(ExecutionModeRoundingModeRTZ, {CapabilityRoundingModeRTZ});
ADD_VEC_INIT(ExecutionModeRoundingModeRTPINTEL,
{CapabilityRoundToInfinityINTEL});
ADD_VEC_INIT(ExecutionModeRoundingModeRTNINTEL,
{CapabilityRoundToInfinityINTEL});
ADD_VEC_INIT(ExecutionModeFloatingPointModeALTINTEL,
{CapabilityFloatingPointModeINTEL});
ADD_VEC_INIT(ExecutionModeFloatingPointModeIEEEINTEL,
{CapabilityFloatingPointModeINTEL});
ADD_VEC_INIT(ExecutionModeSharedLocalMemorySizeINTEL,
{CapabilityVectorComputeINTEL});
ADD_VEC_INIT(ExecutionModeRegisterMapInterfaceINTEL,
{CapabilityFPGAKernelAttributesv2INTEL});
ADD_VEC_INIT(ExecutionModeStreamingInterfaceINTEL,
{CapabilityFPGAKernelAttributesINTEL});
ADD_VEC_INIT(ExecutionModeNamedBarrierCountINTEL,
{CapabilityVectorComputeINTEL});
ADD_VEC_INIT(internal::ExecutionModeNamedSubgroupSizeINTEL,
{internal::CapabilitySubgroupRequirementsINTEL});
ADD_VEC_INIT(ExecutionModeMaximumRegistersINTEL,
{CapabilityRegisterLimitsINTEL});
ADD_VEC_INIT(ExecutionModeMaximumRegistersIdINTEL,
{CapabilityRegisterLimitsINTEL});
ADD_VEC_INIT(ExecutionModeNamedMaximumRegistersINTEL,
{CapabilityRegisterLimitsINTEL});
}
template <> inline void SPIRVMap<SPIRVMemoryModelKind, SPIRVCapVec>::init() {
ADD_VEC_INIT(MemoryModelSimple, {CapabilityShader});
ADD_VEC_INIT(MemoryModelGLSL450, {CapabilityShader});
ADD_VEC_INIT(MemoryModelOpenCL, {CapabilityKernel});
}
template <> inline void SPIRVMap<SPIRVStorageClassKind, SPIRVCapVec>::init() {
ADD_VEC_INIT(StorageClassUniform, {CapabilityShader});
ADD_VEC_INIT(StorageClassOutput, {CapabilityShader});
ADD_VEC_INIT(StorageClassPrivate,
{CapabilityShader, CapabilityVectorComputeINTEL});
ADD_VEC_INIT(StorageClassGeneric, {CapabilityGenericPointer});
ADD_VEC_INIT(StorageClassPushConstant, {CapabilityShader});
ADD_VEC_INIT(StorageClassAtomicCounter, {CapabilityAtomicStorage});
ADD_VEC_INIT(StorageClassDeviceOnlyINTEL, {CapabilityUSMStorageClassesINTEL});
ADD_VEC_INIT(StorageClassHostOnlyINTEL, {CapabilityUSMStorageClassesINTEL});
}
template <> inline void SPIRVMap<SPIRVImageDimKind, SPIRVCapVec>::init() {
ADD_VEC_INIT(Dim1D, {CapabilitySampled1D});
ADD_VEC_INIT(DimCube, {CapabilityShader});
ADD_VEC_INIT(DimRect, {CapabilitySampledRect});
ADD_VEC_INIT(DimBuffer, {CapabilitySampledBuffer});
ADD_VEC_INIT(DimSubpassData, {CapabilityInputAttachment});
}
template <> inline void SPIRVMap<ImageFormat, SPIRVCapVec>::init() {
ADD_VEC_INIT(ImageFormatRgba32f, {CapabilityShader});
ADD_VEC_INIT(ImageFormatRgba16f, {CapabilityShader});
ADD_VEC_INIT(ImageFormatR32f, {CapabilityShader});
ADD_VEC_INIT(ImageFormatRgba8, {CapabilityShader});
ADD_VEC_INIT(ImageFormatRgba8Snorm, {CapabilityShader});
ADD_VEC_INIT(ImageFormatRg32f, {CapabilityStorageImageExtendedFormats});
ADD_VEC_INIT(ImageFormatRg16f, {CapabilityStorageImageExtendedFormats});
ADD_VEC_INIT(ImageFormatR11fG11fB10f,
{CapabilityStorageImageExtendedFormats});
ADD_VEC_INIT(ImageFormatR16f, {CapabilityStorageImageExtendedFormats});
ADD_VEC_INIT(ImageFormatRgba16, {CapabilityStorageImageExtendedFormats});
ADD_VEC_INIT(ImageFormatRgb10A2, {CapabilityStorageImageExtendedFormats});
ADD_VEC_INIT(ImageFormatRg16, {CapabilityStorageImageExtendedFormats});
ADD_VEC_INIT(ImageFormatRg8, {CapabilityStorageImageExtendedFormats});
ADD_VEC_INIT(ImageFormatR16, {CapabilityStorageImageExtendedFormats});
ADD_VEC_INIT(ImageFormatR8, {CapabilityStorageImageExtendedFormats});
ADD_VEC_INIT(ImageFormatRgba16Snorm, {CapabilityStorageImageExtendedFormats});
ADD_VEC_INIT(ImageFormatRg16Snorm, {CapabilityStorageImageExtendedFormats});
ADD_VEC_INIT(ImageFormatRg8Snorm, {CapabilityStorageImageExtendedFormats});
ADD_VEC_INIT(ImageFormatR16Snorm, {CapabilityStorageImageExtendedFormats});
ADD_VEC_INIT(ImageFormatR8Snorm, {CapabilityStorageImageExtendedFormats});
ADD_VEC_INIT(ImageFormatRgba32i, {CapabilityShader});
ADD_VEC_INIT(ImageFormatRgba16i, {CapabilityShader});
ADD_VEC_INIT(ImageFormatRgba8i, {CapabilityShader});
ADD_VEC_INIT(ImageFormatR32i, {CapabilityShader});
ADD_VEC_INIT(ImageFormatRg32i, {CapabilityStorageImageExtendedFormats});
ADD_VEC_INIT(ImageFormatRg16i, {CapabilityStorageImageExtendedFormats});
ADD_VEC_INIT(ImageFormatRg8i, {CapabilityStorageImageExtendedFormats});
ADD_VEC_INIT(ImageFormatR16i, {CapabilityStorageImageExtendedFormats});
ADD_VEC_INIT(ImageFormatR8i, {CapabilityStorageImageExtendedFormats});
ADD_VEC_INIT(ImageFormatRgba32ui, {CapabilityShader});
ADD_VEC_INIT(ImageFormatRgba16ui, {CapabilityShader});
ADD_VEC_INIT(ImageFormatRgba8ui, {CapabilityShader});
ADD_VEC_INIT(ImageFormatR32ui, {CapabilityShader});
ADD_VEC_INIT(ImageFormatRgb10a2ui, {CapabilityStorageImageExtendedFormats});
ADD_VEC_INIT(ImageFormatRg32ui, {CapabilityStorageImageExtendedFormats});
ADD_VEC_INIT(ImageFormatRg16ui, {CapabilityStorageImageExtendedFormats});
ADD_VEC_INIT(ImageFormatR16ui, {CapabilityStorageImageExtendedFormats});
ADD_VEC_INIT(ImageFormatR8ui, {CapabilityStorageImageExtendedFormats});
}
template <> inline void SPIRVMap<ImageOperandsMask, SPIRVCapVec>::init() {
ADD_VEC_INIT(ImageOperandsBiasMask, {CapabilityShader});
ADD_VEC_INIT(ImageOperandsOffsetMask, {CapabilityImageGatherExtended});
ADD_VEC_INIT(ImageOperandsMinLodMask, {CapabilityMinLod});
}
template <> inline void SPIRVMap<Decoration, SPIRVCapVec>::init() {
ADD_VEC_INIT(DecorationRelaxedPrecision, {CapabilityShader});
ADD_VEC_INIT(DecorationSpecId, {CapabilityKernel});
ADD_VEC_INIT(DecorationBlock, {CapabilityShader});
ADD_VEC_INIT(DecorationBufferBlock, {CapabilityShader});
ADD_VEC_INIT(DecorationRowMajor, {CapabilityMatrix});
ADD_VEC_INIT(DecorationColMajor, {CapabilityMatrix});
ADD_VEC_INIT(DecorationArrayStride, {CapabilityShader});
ADD_VEC_INIT(DecorationMatrixStride, {CapabilityMatrix});
ADD_VEC_INIT(DecorationGLSLShared, {CapabilityShader});
ADD_VEC_INIT(DecorationGLSLPacked, {CapabilityShader});
ADD_VEC_INIT(DecorationCPacked, {CapabilityKernel});
ADD_VEC_INIT(DecorationNoPerspective, {CapabilityShader});
ADD_VEC_INIT(DecorationFlat, {CapabilityShader});
ADD_VEC_INIT(DecorationPatch, {CapabilityTessellation});
ADD_VEC_INIT(DecorationCentroid, {CapabilityShader});
ADD_VEC_INIT(DecorationSample, {CapabilitySampleRateShading});
ADD_VEC_INIT(DecorationInvariant, {CapabilityShader});
ADD_VEC_INIT(DecorationConstant, {CapabilityKernel});
ADD_VEC_INIT(DecorationSaturatedConversion, {CapabilityKernel});
ADD_VEC_INIT(DecorationSaturatedToLargestFloat8NormalConversionEXT,
{CapabilityFloat8EXT});
ADD_VEC_INIT(DecorationStream, {CapabilityGeometryStreams});
ADD_VEC_INIT(DecorationLocation, {CapabilityShader});
ADD_VEC_INIT(DecorationComponent, {CapabilityShader});
ADD_VEC_INIT(DecorationIndex, {CapabilityShader});
ADD_VEC_INIT(DecorationBinding, {CapabilityShader});
ADD_VEC_INIT(DecorationDescriptorSet, {CapabilityShader});
ADD_VEC_INIT(DecorationOffset, {CapabilityShader});
ADD_VEC_INIT(DecorationXfbBuffer, {CapabilityTransformFeedback});
ADD_VEC_INIT(DecorationXfbStride, {CapabilityTransformFeedback});
ADD_VEC_INIT(DecorationFuncParamAttr, {CapabilityKernel});
ADD_VEC_INIT(DecorationFPRoundingMode, {CapabilityKernel});
ADD_VEC_INIT(DecorationFPFastMathMode, {CapabilityKernel});
ADD_VEC_INIT(DecorationLinkageAttributes, {CapabilityLinkage});
ADD_VEC_INIT(DecorationNoContraction, {CapabilityShader});
ADD_VEC_INIT(DecorationInputAttachmentIndex, {CapabilityInputAttachment});
ADD_VEC_INIT(DecorationAlignment, {CapabilityKernel});
ADD_VEC_INIT(DecorationRegisterINTEL, {CapabilityFPGAMemoryAttributesINTEL});
ADD_VEC_INIT(DecorationMemoryINTEL, {CapabilityFPGAMemoryAttributesINTEL});
ADD_VEC_INIT(DecorationNumbanksINTEL, {CapabilityFPGAMemoryAttributesINTEL});
ADD_VEC_INIT(DecorationBankwidthINTEL, {CapabilityFPGAMemoryAttributesINTEL});
ADD_VEC_INIT(DecorationMaxPrivateCopiesINTEL,
{CapabilityFPGAMemoryAttributesINTEL});
ADD_VEC_INIT(DecorationSinglepumpINTEL,
{CapabilityFPGAMemoryAttributesINTEL});
ADD_VEC_INIT(DecorationDoublepumpINTEL,
{CapabilityFPGAMemoryAttributesINTEL});
ADD_VEC_INIT(DecorationMaxReplicatesINTEL,
{CapabilityFPGAMemoryAttributesINTEL});
ADD_VEC_INIT(DecorationSimpleDualPortINTEL,
{CapabilityFPGAMemoryAttributesINTEL});
ADD_VEC_INIT(DecorationMergeINTEL, {CapabilityFPGAMemoryAttributesINTEL});
ADD_VEC_INIT(DecorationBankBitsINTEL, {CapabilityFPGAMemoryAttributesINTEL});
ADD_VEC_INIT(DecorationForcePow2DepthINTEL,
{CapabilityFPGAMemoryAttributesINTEL});
ADD_VEC_INIT(DecorationStridesizeINTEL,
{CapabilityFPGAMemoryAttributesINTEL});
ADD_VEC_INIT(DecorationWordsizeINTEL, {CapabilityFPGAMemoryAttributesINTEL});
ADD_VEC_INIT(DecorationTrueDualPortINTEL,
{CapabilityFPGAMemoryAttributesINTEL});
ADD_VEC_INIT(DecorationReferencedIndirectlyINTEL,
{CapabilityIndirectReferencesINTEL});
ADD_VEC_INIT(DecorationIOPipeStorageINTEL, {CapabilityIOPipesINTEL});
ADD_VEC_INIT(DecorationSideEffectsINTEL, {CapabilityAsmINTEL});
ADD_VEC_INIT(DecorationVectorComputeFunctionINTEL,
{CapabilityVectorComputeINTEL});
ADD_VEC_INIT(DecorationVectorComputeVariableINTEL,
{CapabilityVectorComputeINTEL});
ADD_VEC_INIT(DecorationGlobalVariableOffsetINTEL,
{CapabilityVectorComputeINTEL});
ADD_VEC_INIT(DecorationFuncParamIOKindINTEL, {CapabilityVectorComputeINTEL});
ADD_VEC_INIT(DecorationStackCallINTEL, {CapabilityVectorComputeINTEL});
ADD_VEC_INIT(DecorationSIMTCallINTEL, {CapabilityVectorComputeINTEL});
ADD_VEC_INIT(DecorationBurstCoalesceINTEL,
{CapabilityFPGAMemoryAccessesINTEL});
ADD_VEC_INIT(DecorationCacheSizeINTEL, {CapabilityFPGAMemoryAccessesINTEL});
ADD_VEC_INIT(DecorationDontStaticallyCoalesceINTEL,
{CapabilityFPGAMemoryAccessesINTEL});
ADD_VEC_INIT(DecorationPrefetchINTEL, {CapabilityFPGAMemoryAccessesINTEL});
ADD_VEC_INIT(DecorationBufferLocationINTEL,
{CapabilityFPGABufferLocationINTEL});
ADD_VEC_INIT(DecorationFunctionRoundingModeINTEL,
{CapabilityFunctionFloatControlINTEL});
ADD_VEC_INIT(DecorationFunctionDenormModeINTEL,
{CapabilityFunctionFloatControlINTEL});
ADD_VEC_INIT(DecorationFunctionFloatingPointModeINTEL,
{CapabilityFunctionFloatControlINTEL});
ADD_VEC_INIT(DecorationSingleElementVectorINTEL,
{CapabilityVectorComputeINTEL});
ADD_VEC_INIT(DecorationAliasScopeINTEL,
{CapabilityMemoryAccessAliasingINTEL});
ADD_VEC_INIT(DecorationNoAliasINTEL, {CapabilityMemoryAccessAliasingINTEL});
ADD_VEC_INIT(DecorationMediaBlockIOINTEL, {CapabilityVectorComputeINTEL});
ADD_VEC_INIT(DecorationStallEnableINTEL,
{CapabilityFPGAClusterAttributesINTEL});
ADD_VEC_INIT(DecorationStallFreeINTEL,
{CapabilityFPGAClusterAttributesV2INTEL});
ADD_VEC_INIT(DecorationFuseLoopsInFunctionINTEL, {CapabilityLoopFuseINTEL});
ADD_VEC_INIT(DecorationMathOpDSPModeINTEL, {CapabilityFPGADSPControlINTEL});
ADD_VEC_INIT(DecorationInitiationIntervalINTEL,
{CapabilityFPGAInvocationPipeliningAttributesINTEL});
ADD_VEC_INIT(DecorationMaxConcurrencyINTEL,
{CapabilityFPGAInvocationPipeliningAttributesINTEL});
ADD_VEC_INIT(DecorationPipelineEnableINTEL,
{CapabilityFPGAInvocationPipeliningAttributesINTEL});
ADD_VEC_INIT(internal::DecorationRuntimeAlignedINTEL,
{CapabilityRuntimeAlignedAttributeINTEL});
ADD_VEC_INIT(internal::DecorationHostAccessINTEL,
{internal::CapabilityGlobalVariableDecorationsINTEL});
ADD_VEC_INIT(internal::DecorationInitModeINTEL,
{internal::CapabilityGlobalVariableDecorationsINTEL});
ADD_VEC_INIT(internal::DecorationImplementInCSRINTEL,
{internal::CapabilityGlobalVariableDecorationsINTEL});
ADD_VEC_INIT(DecorationHostAccessINTEL,
{CapabilityGlobalVariableHostAccessINTEL});
ADD_VEC_INIT(DecorationInitModeINTEL,
{CapabilityGlobalVariableFPGADecorationsINTEL});
ADD_VEC_INIT(DecorationImplementInRegisterMapINTEL,
{CapabilityGlobalVariableFPGADecorationsINTEL});
ADD_VEC_INIT(internal::DecorationArgumentAttributeINTEL,
{CapabilityFunctionPointersINTEL});
ADD_VEC_INIT(DecorationCacheControlLoadINTEL, {CapabilityCacheControlsINTEL});
ADD_VEC_INIT(DecorationCacheControlStoreINTEL,
{CapabilityCacheControlsINTEL});
ADD_VEC_INIT(DecorationConduitKernelArgumentINTEL,
{CapabilityFPGAArgumentInterfacesINTEL});
ADD_VEC_INIT(DecorationRegisterMapKernelArgumentINTEL,
{CapabilityFPGAArgumentInterfacesINTEL});
ADD_VEC_INIT(DecorationMMHostInterfaceAddressWidthINTEL,
{CapabilityFPGAArgumentInterfacesINTEL});
ADD_VEC_INIT(DecorationMMHostInterfaceDataWidthINTEL,
{CapabilityFPGAArgumentInterfacesINTEL});
ADD_VEC_INIT(DecorationMMHostInterfaceLatencyINTEL,
{CapabilityFPGAArgumentInterfacesINTEL});
ADD_VEC_INIT(DecorationMMHostInterfaceReadWriteModeINTEL,
{CapabilityFPGAArgumentInterfacesINTEL});
ADD_VEC_INIT(DecorationMMHostInterfaceMaxBurstINTEL,
{CapabilityFPGAArgumentInterfacesINTEL});
ADD_VEC_INIT(DecorationMMHostInterfaceWaitRequestINTEL,
{CapabilityFPGAArgumentInterfacesINTEL});
ADD_VEC_INIT(DecorationStableKernelArgumentINTEL,
{CapabilityFPGAArgumentInterfacesINTEL});
ADD_VEC_INIT(DecorationLatencyControlLabelINTEL,
{CapabilityFPGALatencyControlINTEL});
ADD_VEC_INIT(DecorationLatencyControlConstraintINTEL,
{CapabilityFPGALatencyControlINTEL});
ADD_VEC_INIT(DecorationFPMaxErrorDecorationINTEL,
{CapabilityFPMaxErrorINTEL});
}
template <> inline void SPIRVMap<BuiltIn, SPIRVCapVec>::init() {
ADD_VEC_INIT(BuiltInPosition, {CapabilityShader});
ADD_VEC_INIT(BuiltInPointSize, {CapabilityShader});
ADD_VEC_INIT(BuiltInClipDistance, {CapabilityClipDistance});
ADD_VEC_INIT(BuiltInCullDistance, {CapabilityCullDistance});
ADD_VEC_INIT(BuiltInVertexId, {CapabilityShader});
ADD_VEC_INIT(BuiltInInstanceId, {CapabilityShader});
ADD_VEC_INIT(BuiltInPrimitiveId,
{CapabilityGeometry, CapabilityTessellation});
ADD_VEC_INIT(BuiltInInvocationId,
{CapabilityGeometry, CapabilityTessellation});
ADD_VEC_INIT(BuiltInLayer, {CapabilityGeometry});
ADD_VEC_INIT(BuiltInViewportIndex, {CapabilityMultiViewport});
ADD_VEC_INIT(BuiltInTessLevelOuter, {CapabilityTessellation});
ADD_VEC_INIT(BuiltInTessLevelInner, {CapabilityTessellation});
ADD_VEC_INIT(BuiltInTessCoord, {CapabilityTessellation});
ADD_VEC_INIT(BuiltInPatchVertices, {CapabilityTessellation});
ADD_VEC_INIT(BuiltInFragCoord, {CapabilityShader});
ADD_VEC_INIT(BuiltInPointCoord, {CapabilityShader});
ADD_VEC_INIT(BuiltInFrontFacing, {CapabilityShader});
ADD_VEC_INIT(BuiltInSampleId, {CapabilitySampleRateShading});
ADD_VEC_INIT(BuiltInSamplePosition, {CapabilitySampleRateShading});
ADD_VEC_INIT(BuiltInSampleMask, {CapabilitySampleRateShading});
ADD_VEC_INIT(BuiltInFragDepth, {CapabilityShader});
ADD_VEC_INIT(BuiltInHelperInvocation, {CapabilityShader});
ADD_VEC_INIT(BuiltInWorkDim, {CapabilityKernel});
ADD_VEC_INIT(BuiltInGlobalSize, {CapabilityKernel});
ADD_VEC_INIT(BuiltInEnqueuedWorkgroupSize, {CapabilityKernel});
ADD_VEC_INIT(BuiltInGlobalOffset, {CapabilityKernel});
ADD_VEC_INIT(BuiltInGlobalLinearId, {CapabilityKernel});
ADD_VEC_INIT(BuiltInSubgroupSize, {CapabilityKernel});
ADD_VEC_INIT(BuiltInSubgroupMaxSize, {CapabilityKernel});
ADD_VEC_INIT(BuiltInNumSubgroups, {CapabilityKernel});
ADD_VEC_INIT(BuiltInNumEnqueuedSubgroups, {CapabilityKernel});
ADD_VEC_INIT(BuiltInSubgroupId, {CapabilityKernel});
ADD_VEC_INIT(BuiltInSubgroupLocalInvocationId, {CapabilityKernel});
ADD_VEC_INIT(BuiltInSubgroupEqMask, {CapabilityGroupNonUniformBallot});
ADD_VEC_INIT(BuiltInSubgroupGeMask, {CapabilityGroupNonUniformBallot});
ADD_VEC_INIT(BuiltInSubgroupGtMask, {CapabilityGroupNonUniformBallot});
ADD_VEC_INIT(BuiltInSubgroupLeMask, {CapabilityGroupNonUniformBallot});
ADD_VEC_INIT(BuiltInSubgroupLtMask, {CapabilityGroupNonUniformBallot});
ADD_VEC_INIT(BuiltInVertexIndex, {CapabilityShader});
ADD_VEC_INIT(BuiltInInstanceIndex, {CapabilityShader});
ADD_VEC_INIT(internal::BuiltInSubDeviceIDINTEL,
{internal::CapabilityHWThreadQueryINTEL});
ADD_VEC_INIT(internal::BuiltInGlobalHWThreadIDINTEL,
{internal::CapabilityHWThreadQueryINTEL});
ADD_VEC_INIT(internal::BuiltInDeviceBarrierValidINTEL,
{internal::CapabilityDeviceBarrierINTEL});
}
template <> inline void SPIRVMap<MemorySemanticsMask, SPIRVCapVec>::init() {
ADD_VEC_INIT(MemorySemanticsUniformMemoryMask, {CapabilityShader});
ADD_VEC_INIT(MemorySemanticsAtomicCounterMemoryMask,
{CapabilityAtomicStorage});
}
#undef ADD_VEC_INIT
inline unsigned getImageDimension(SPIRVImageDimKind K) {
switch (K) {
case Dim1D:
return 1;
case Dim2D:
return 2;
case Dim3D:
return 3;
case DimCube:
return 2;
case DimRect:
return 2;
case DimBuffer:
return 1;
default:
return 0;
}
}
/// Extract memory order part of SPIR-V memory semantics.
inline unsigned extractSPIRVMemOrderSemantic(unsigned Sema) {
return Sema & KSpirvMemOrderSemanticMask;
}
} // namespace SPIRV
#endif // SPIRV_LIBSPIRV_SPIRVENUM_H
@@ -0,0 +1,58 @@
//===- SPIRVError.cpp - SPIR-V error code and checking ----------*- C++ -*-===//
//
// The LLVM/SPIR-V Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2024 The Khronos Group Inc.
//
// 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 Khronos Group, 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.
//
//===----------------------------------------------------------------------===//
//
// This file implements SPIRV error code and checking utility.
//
//===----------------------------------------------------------------------===//
#include "LLVMSPIRVLib.h"
#include "SPIRVError.h"
#include <string>
namespace SPIRV {
// Return the message associated with the error code. If error code is invalid,
// return the message associated with InternalMaxErrorCode.
std::string getErrorMessage(int ErrCode) {
std::string ErrorMessage;
bool Found =
(ErrCode >= SPIRVEC_Success && ErrCode < SPIRVEC_InternalMaxErrorCode)
? SPIRVErrorMap::find(static_cast<SPIRVErrorCode>(ErrCode),
&ErrorMessage)
: false;
return Found ? ErrorMessage : std::string("Unknown error code");
}
} // namespace SPIRV
@@ -0,0 +1,179 @@
//===- SPIRVError.h - SPIR-V error code and checking ------------*- C++ -*-===//
//
// The LLVM/SPIRV Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
//
// 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 Advanced Micro Devices, Inc., 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.
//
//===----------------------------------------------------------------------===//
//
// This file defines SPIRV error code and checking utility.
//
//===----------------------------------------------------------------------===//
#ifndef SPIRV_LIBSPIRV_SPIRVERROR_H
#define SPIRV_LIBSPIRV_SPIRVERROR_H
#include "SPIRVDebug.h"
#include "SPIRVUtil.h"
#include "llvm/IR/Instruction.h"
#include <iostream>
#include <sstream>
#include <string>
namespace SPIRV {
// Check condition and set error code and error msg.
// To use this macro, function checkError must be defined in the scope.
// Emit absolute path only in debug mode.
#ifdef NDEBUG
#define SPIRVCK(Condition, ErrCode, ErrMsg) \
getErrorLog().checkError(Condition, SPIRVEC_##ErrCode, \
std::string() + (ErrMsg), #Condition)
#else
#define SPIRVCK(Condition, ErrCode, ErrMsg) \
getErrorLog().checkError(Condition, SPIRVEC_##ErrCode, \
std::string() + (ErrMsg), #Condition, __FILE__, \
__LINE__)
#endif // NDEBUG
// Check condition and set error code and error msg. If fail returns false.
// Emit absolute path only in debug mode.
#ifdef NDEBUG
#define SPIRVCKRT(Condition, ErrCode, ErrMsg) \
if (!getErrorLog().checkError(Condition, SPIRVEC_##ErrCode, \
std::string() + (ErrMsg), #Condition)) \
return false;
#else
#define SPIRVCKRT(Condition, ErrCode, ErrMsg) \
if (!getErrorLog().checkError(Condition, SPIRVEC_##ErrCode, \
std::string() + (ErrMsg), #Condition, \
__FILE__, __LINE__)) \
return false;
#endif // NDEBUG
// Defines error code enum type SPIRVErrorCode.
enum SPIRVErrorCode {
#define _SPIRV_OP(x, y) SPIRVEC_##x,
#include "SPIRVErrorEnum.h"
#undef _SPIRV_OP
};
// Defines SPIRVErrorMap which maps error code to a string describing the error.
template <> inline void SPIRVMap<SPIRVErrorCode, std::string>::init() {
#define _SPIRV_OP(x, y) add(SPIRVEC_##x, std::string(#x) + ": " + (y));
#include "SPIRVErrorEnum.h"
#undef _SPIRV_OP
}
typedef SPIRVMap<SPIRVErrorCode, std::string> SPIRVErrorMap;
class SPIRVErrorLog {
public:
SPIRVErrorLog() : ErrorCode(SPIRVEC_Success) {}
SPIRVErrorCode getError(std::string &ErrMsg) {
ErrMsg = ErrorMsg;
return ErrorCode;
}
void setError(SPIRVErrorCode ErrCode, const std::string &ErrMsg) {
ErrorCode = ErrCode;
ErrorMsg = ErrMsg;
}
// Check if Condition is satisfied and set ErrCode and DetailedMsg
// if not. Returns true if no error.
bool checkError(bool Condition, SPIRVErrorCode ErrCode,
const std::string &DetailedMsg = "",
const char *CondString = nullptr,
const char *FileName = nullptr, unsigned LineNumber = 0);
// Check if Condition is satisfied and set ErrCode and DetailedMsg with Value
// text representation if not. Returns true if no error.
bool checkError(bool Condition, SPIRVErrorCode ErrCode, llvm::Value *Value,
const std::string &DetailedMsg = "",
const char *CondString = nullptr,
const char *FileName = nullptr, unsigned LineNumber = 0);
protected:
SPIRVErrorCode ErrorCode;
std::string ErrorMsg;
};
inline bool SPIRVErrorLog::checkError(bool Cond, SPIRVErrorCode ErrCode,
llvm::Value *Value,
const std::string &Msg,
const char *CondString,
const char *FileName, unsigned LineNo) {
// Do early exit to avoid expensive toString() function call unless it is
// actually needed. That speeds up translator's execution.
if (Cond)
return Cond;
// Do not overwrite previous failure.
if (ErrorCode != SPIRVEC_Success)
return Cond;
std::string ValueIR = toString(Value);
return checkError(Cond, ErrCode, Msg + "\n" + ValueIR, CondString, FileName,
LineNo);
}
inline bool SPIRVErrorLog::checkError(bool Cond, SPIRVErrorCode ErrCode,
const std::string &Msg,
const char *CondString,
const char *FileName, unsigned LineNo) {
std::stringstream SS;
if (Cond)
return Cond;
// Do not overwrite previous failure.
if (ErrorCode != SPIRVEC_Success)
return Cond;
SS << SPIRVErrorMap::map(ErrCode) << " " << Msg;
if (SPIRVDbgErrorMsgIncludesSourceInfo && FileName)
SS << " [Src: " << FileName << ":" << LineNo << " " << CondString << " ]";
setError(ErrCode, SS.str());
switch (SPIRVDbgError) {
case SPIRVDbgErrorHandlingKinds::Abort:
std::cerr << SS.str() << std::endl;
abort();
break;
case SPIRVDbgErrorHandlingKinds::Exit:
std::cerr << SS.str() << std::endl;
std::exit(ErrCode);
break;
case SPIRVDbgErrorHandlingKinds::Ignore:
// Still print info about the error into debug output stream
// TODO: The value Ignore is not currently used but if it would be used
// then places where this routine is called must be checked as just
// ignoring the error may lead to NULL pointer dereferences
spvdbgs() << SS.str() << '\n';
spvdbgs().flush();
break;
}
return Cond;
}
} // namespace SPIRV
#endif // SPIRV_LIBSPIRV_SPIRVERROR_H

Some files were not shown because too many files have changed in this diff Show More