spirv-llvm-translator: vendor as full-fork recipe (path=source, patches baked)
This commit is contained in:
@@ -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'
|
||||
+186
@@ -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
|
||||
+131
@@ -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
|
||||
+226
@@ -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
|
||||
Vendored
+111
@@ -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)
|
||||
+88
@@ -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
|
||||
|
||||
[](https://github.com/KhronosGroup/SPIRV-LLVM-Translator/actions?query=workflow%3A%22Out-of-tree+build+%26+tests%22+event%3Aschedule)
|
||||
[](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**
|
||||
+244
@@ -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
|
||||
}
|
||||
)"};
|
||||
+109
@@ -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
|
||||
+29
@@ -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_
|
||||
+95
@@ -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
+240
@@ -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);
|
||||
}
|
||||
+80
@@ -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
|
||||
+34
@@ -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
|
||||
+107
@@ -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
Reference in New Issue
Block a user