chore: import upstream snapshot with attribution
Lint / lint (push) Waiting to run
CI / MacOS (push) Waiting to run
CI / Windows (push) Waiting to run

This commit is contained in:
wehub-resource-sync
2026-07-13 13:36:25 +08:00
commit 26446540fa
3151 changed files with 974126 additions and 0 deletions
+124
View File
@@ -0,0 +1,124 @@
#!/usr/bin/env bash
# Watch a single GPU for foreign processes (anyone other than the current
# user) appearing during a long-running test. Intended companion to
# `/tir-test`: leave this running in a side terminal while pytest runs, and
# it will alert if someone else lands on the same GPU.
#
# Usage:
# monitor_gpu.sh # uses $CUDA_VISIBLE_DEVICES, defaults to 0
# monitor_gpu.sh --gpu 3 # watch GPU 3
# monitor_gpu.sh --gpu 3 --interval 2 # poll every 2 seconds
# monitor_gpu.sh --log /tmp/gpu.log # also tee to a log file
# Note: deliberately not `set -u` — bash <5.2 errors on `${#assoc[@]}` when
# the associative array is empty.
GPU=""
INTERVAL=5
LOG=""
while [[ $# -gt 0 ]]; do
case "$1" in
--gpu) GPU="$2"; shift 2 ;;
--interval) INTERVAL="$2"; shift 2 ;;
--log) LOG="$2"; shift 2 ;;
-h|--help)
sed -n '2,12p' "$0" | sed 's/^# \{0,1\}//'
exit 0 ;;
*) echo "unknown arg: $1" >&2; exit 2 ;;
esac
done
if [[ -z "$GPU" ]]; then
GPU="${CUDA_VISIBLE_DEVICES:-0}"
fi
# Only the first index if CUDA_VISIBLE_DEVICES is a list.
GPU="${GPU%%,*}"
if ! [[ "$GPU" =~ ^[0-9]+$ ]]; then
echo "monitor_gpu: GPU must be an integer index (got '$GPU'); pass --gpu <n>" >&2
exit 2
fi
ME="$(id -un)"
emit() {
local line="[$(date +'%H:%M:%S')] $*"
if [[ -n "$LOG" ]]; then
printf '%s\n' "$line" | tee -a "$LOG" >&2
else
printf '%s\n' "$line" >&2
fi
}
# Returns "pid|user|mem_mib|process_name" lines for compute apps on $GPU.
snapshot() {
nvidia-smi --id="$GPU" \
--query-compute-apps=pid,process_name,used_memory \
--format=csv,noheader,nounits 2>/dev/null \
| while IFS=, read -r pid pname mem; do
pid="${pid// /}"
[[ -z "$pid" ]] && continue
local user
user="$(ps -o user= -p "$pid" 2>/dev/null | tr -d ' ')"
[[ -z "$user" ]] && user="?"
pname="${pname# }"
mem="${mem# }"
printf '%s|%s|%s|%s\n' "$pid" "$user" "$mem" "$pname"
done
}
emit "monitor_gpu started: GPU=$GPU interval=${INTERVAL}s user=$ME"
declare -A KNOWN # pid -> "user|mem|pname"
# Initial snapshot — record everyone we already see as the baseline.
while IFS='|' read -r pid user mem pname; do
[[ -z "${pid:-}" ]] && continue
KNOWN[$pid]="$user|$mem|$pname"
flag=""
[[ "$user" != "$ME" ]] && flag=" [FOREIGN]"
emit "baseline pid=$pid user=$user mem=${mem}MiB cmd=$pname$flag"
done < <(snapshot)
if [[ ${#KNOWN[@]} -eq 0 ]]; then
emit "baseline: GPU $GPU is idle"
fi
trap 'emit "monitor_gpu stopped"; exit 0' INT TERM
heartbeat_due=$(( $(date +%s) + 60 ))
while :; do
sleep "$INTERVAL"
declare -A SEEN=()
while IFS='|' read -r pid user mem pname; do
[[ -z "${pid:-}" ]] && continue
SEEN[$pid]=1
if [[ -z "${KNOWN[$pid]:-}" ]]; then
flag=""
[[ "$user" != "$ME" ]] && flag=" *** FOREIGN USER ***"
emit "NEW pid=$pid user=$user mem=${mem}MiB cmd=$pname$flag"
KNOWN[$pid]="$user|$mem|$pname"
fi
done < <(snapshot)
for pid in "${!KNOWN[@]}"; do
if [[ -z "${SEEN[$pid]:-}" ]]; then
emit "GONE pid=$pid (was: ${KNOWN[$pid]})"
unset 'KNOWN[$pid]'
fi
done
unset SEEN
now=$(date +%s)
if (( now >= heartbeat_due )); then
foreign=0
for v in "${KNOWN[@]}"; do
u="${v%%|*}"
[[ "$u" != "$ME" ]] && foreign=$((foreign+1))
done
emit "heartbeat: ${#KNOWN[@]} process(es) on GPU $GPU (${foreign} foreign)"
heartbeat_due=$(( now + 60 ))
fi
done
+17
View File
@@ -0,0 +1,17 @@
Build TVM from the current worktree.
## Steps
1. Check that `build/` directory exists. If not, run initial setup:
```bash
mkdir -p build
cmake -S . -B build
cmake --build build --parallel
```
2. If `build/` already exists, run incremental build:
```bash
cmake --build build --parallel
```
3. Report success/failure and build time.
+59
View File
@@ -0,0 +1,59 @@
Run the full TIRX test suite.
## Steps
1. Install the kernel package and select the least busy GPU:
```bash
pip install -e /path/to/tirx-kernels-staging # or sibling tirx-kernels checkout
export CUDA_VISIBLE_DEVICES=$(nvidia-smi --query-gpu=index,memory.used --format=csv,noheader,nounits | sort -t',' -k2 -n | head -1 | cut -d',' -f1 | tr -d ' ')
export TVM_PATH=/path/to/tvm
export PYTHONPATH="${TVM_PATH}/python"
export TVM_LIBRARY_PATH="${TVM_PATH}/build/lib"
```
2. Start the GPU monitor in the background so we can detect if anyone else lands on the same GPU mid-run:
```bash
GPU_LOG="/tmp/tir_test_gpu_${CUDA_VISIBLE_DEVICES}.log"
bash .agents/scripts/monitor_gpu.sh --gpu "$CUDA_VISIBLE_DEVICES" --interval 5 --log "$GPU_LOG" &
MON_PID=$!
trap 'kill $MON_PID 2>/dev/null' EXIT
```
3. Import gate — bench workloads: fail fast if any kernel listed in `workloads.yaml` fails to import:
```bash
python -m tirx_kernels.bench_suite --check-imports
```
A non-zero exit means a pinned workload kernel failed to import — fix it before proceeding.
4. Full kernel import gate (correctness test suite coverage):
```bash
python -m tirx_kernels.registry --cc 10 --strict
```
5. Run the full test suite with xdist parallelism:
```bash
pytest tests/python/tirx/ -n 16
```
6. Stop the monitor and check for foreign GPU usage during the run:
```bash
kill $MON_PID 2>/dev/null; wait $MON_PID 2>/dev/null
grep -E 'FOREIGN USER|\[FOREIGN\]' "$GPU_LOG" || echo "no foreign GPU usage observed"
```
7. Report results: total passed, failed, skipped, errors — and the import-gate results from steps 34. If any foreign-user events are present in step 6, mention them — flaky failures should be re-evaluated on a clean GPU before being attributed to code changes.
## Failure triage rules
**CRITICAL: Never pipe test output to `tail` or `grep` when diagnosing failures. Always capture and read full logs.**
Classify every failure into one of these categories:
- **A — Environment/import error**: Module not found, missing dependency, collection error. These are not caused by code changes.
- **B — Real kernel correctness regression**: Assertion failures (cosine_sim, numerical diff), `CUDA: unspecified launch failure`, or wrong results. **These MUST be investigated and fixed if caused by current changes.**
- **C — Secondary xdist crash**: `KeyError: <WorkerController gwXX>` after a worker abort. The KeyError itself is noise — find the underlying cause (usually category B in another worker).
**Never dismiss a failure as "pre-existing" without evidence.** If a test fails:
1. Check whether the test touches code you changed.
2. If unclear, verify on the parent commit before claiming pre-existing.
3. All failures caused by current changes MUST be fixed — not deferred.
+89
View File
@@ -0,0 +1,89 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
github:
description: "Open Machine Learning Compiler Framework"
homepage: https://tvm.apache.org/
labels:
- tvm
- compiler
- tensor
- deep-learning
- gpu
- opencl
- metal
- performance
- javascript
- rocm
- vulkan
- spirv
- machine-learning
features:
# Enable issue management
issues: true
# Enable projects for project management boards
projects: true
# Triage perm for collaborators(test run)
#
# The perm is given based on needs and not based on
# evaluation of past contributions. The rationale
# is that people may need the permission to start
# contributing in this way. It serves to diversify
# the ways to contribute.
#
# There is a limited number of slots. To enable broad
# participation, permission is given on a three month
# cycle. PMC may review and recycle slots when necessary.
collaborators:
- tvm-bot # For automated feedback in PR review.
# See https://cwiki.apache.org/confluence/display/INFRA/Git+-+.asf.yaml+features#Git.asf.yamlfeatures-Branchprotection
protected_branches:
main:
required_status_checks:
contexts:
- arm/pr-head
- cpu/pr-head
- docker/pr-head
- gpu/pr-head
- wasm/pr-head
required_pull_request_reviews:
required_approving_review_count: 1
enabled_merge_buttons:
# enable squash button:
squash: true
# default commit message when merging with a squash commit
# can either be: DEFAULT | PR_TITLE | PR_TITLE_AND_COMMIT_DETAILS | PR_TITLE_AND_DESC
squash_commit_message: PR_TITLE_AND_DESC
# enable merge button:
merge: false
# default commit message when merging with a merge commit
# can either be: DEFAULT | PR_TITLE | PR_TITLE_AND_DESC
merge_commit_message: DEFAULT
# enable rebase button for rare use.
rebase: true
notifications:
commits: commits@tvm.apache.org
issues: discuss-archive@tvm.apache.org
pullrequests: discuss-archive@tvm.apache.org
jobs: discuss-archive@tvm.apache.org
discussions: discuss-archive@tvm.apache.org
+8
View File
@@ -0,0 +1,8 @@
# Run the following command to reformat a file:
# clang-format -i -style=Google <file>
# Or use clang-format-diff to only reformat the changed lines:
# https://clang.llvm.org/docs/ClangFormat.html
BasedOnStyle: Google
DerivePointerAlignment: false
ColumnLimit: 100
PointerAlignment: Left
+2
View File
@@ -0,0 +1,2 @@
Jenkinsfile linguist-generated=true
ci/jenkins/generated/* linguist-generated=true
+162
View File
@@ -0,0 +1,162 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
# GitHub code owners file
# This file is used as a convenient tool to map
# committers' areas of expertise and faciliate the review process.
#
# This may not be the non-comprehensive list and is meant to be
# updated over time.
# Per ASF policy, committer have global write permission.
# We normally recommend committers to shepherd code in their area of expertise.
* @apache/tvm-committers
# Order is important; the last matching pattern takes the most precedence.
# The sub modules should be ordered first by depth.
# Making sure we append new sub-module rules after exisiting modules rules.
###############################################################################
# IMPORTANT NOTE
# This file is intentionally not named CODEOWNERS to avoid getting picked up
# by GitHub's code owners -> review mechanism. For details see
# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
# and https://github.com/apache/tvm-rfcs/pull/58
#
# This file is kept to allow manual inspection of who is responsible for
# different segments of the codebase.
###############################################################################
##############################
# Top-level Fallbacks
##############################
include/** @tqchen @jroesch @yzhliu @icemelon @junrushao1994 @comaniac @zhiics
src/** @tqchen @jroesch @yzhliu @icemelon @junrushao1994 @comaniac @zhiics
apps/** @tqchen @jroesch @yzhliu @icemelon @junrushao1994 @comaniac @zhiics
python/** @tqchen @jroesch @yzhliu @icemelon @junrushao1994 @comaniac @zhiics
# Thirdparty license audit
3rdparty/** @tqchen @jroesch
licenses/** @tqchen @jroesch
# JVM language
jvm/** @yzhliu
# Golang
golang/** @srkreddy1238
# WASM
web/** @tqchen @jroesch
# Docker
docker/** @areusch @leandron @jroesch
# Conda
tests/conda/** @tqchen @junrushao1994 @comaniac
# CMake
cmake/** @jroesch @tqchen @areusch @junrushao1994 @comaniac
# rust bindings
rust/** @jroesch @nhynes @nhynes
# vta
vta/** @tmoreau89 @vegaluisjose
# docs
docs/** @comaniac @junrushao1994 @tqchen @jroesch @areusch @yzhliu @merrymercy @icemelon
tutorials/** @comaniac @junrushao1994 @tqchen @jroesch @areusch @yzhliu @merrymercy @icemelon
# tests
tests/** @comaniac @junrushao1994 @tqchen @jroesch @areusch @yzhliu @merrymercy @icemelon
##############################
# Specific modules
##############################
# automation related
src/auto_scheduler/** @merrymercy @jcf94 @comaniac @junrushao1994 @vinx13 @Hzfengsy
include/tvm/auto_scheduler/** @merrymercy @jcf94 @comaniac @junrushao1994 @vinx13 @Hzfengsy
python/tvm/auto_scheduler/** @merrymercy @jcf94 @comaniac @junrushao1994 @vinx13 @Hzfengsy
python/tvm/autotvm/** @merrymercy @jcf94 @comaniac @junrushao1994 @vinx13
# node system and reflection
src/node/** @junrushao1994 @vinx13 @tqchen @jroesch @comaniac
include/tvm/node/** @junrushao1994 @vinx13 @tqchen @jroesch @comaniac
# ir: Common IR
src/ir/** @junrushao1994 @vinx13 @tqchen @jroesch @comaniac
include/tvm/ir/** @junrushao1994 @vinx13 @tqchen @jroesch @comaniac
python/tvm/ir/** @junrushao1994 @vinx13 @tqchen @jroesch @comaniac
# tir
src/tir/** @junrushao1994 @vinx13 @tqchen @kparzysz-quic @ZihengJiang @masahi @were @Hzfengsy
include/tvm/tir/** @junrushao1994 @vinx13 @tqchen @kparzysz-quic @ZihengJiang @masahi @were @Hzfengsy
python/tvm/tir/** @junrushao1994 @vinx13 @tqchen @kparzysz-quic @ZihengJiang @masahi @were @Hzfengsy
# te
src/te/** @junrushao1994 @vinx13 @tqchen @kparzysz-quic @ZihengJiang @masahi @were
include/tvm/te/** @junrushao1994 @vinx13 @tqchen @kparzysz-quic @ZihengJiang @masahi @were
python/tvm/te/** @junrushao1994 @vinx13 @tqchen @kparzysz-quic @ZihengJiang @masahi @were
# target
src/target/** @junrushao1994 @vinx13 @tqchen @kparzysz-quic @ZihengJiang @masahi
include/tvm/target/** @junrushao1994 @vinx13 @tqchen @kparzysz-quic @ZihengJiang @masahi
python/tvm/target/** @junrushao1994 @vinx13 @tqchen @kparzysz-quic @ZihengJiang @masahi
# arith: Arithmetic module and simplifiers
src/arith/** @tqchen @junrushao1994 @vinx13
include/tvm/arith/** @tqchen @junrushao1994 @vinx13
python/tvm/arith/** @tqchen @junrushao1994 @vinx13
# parser
src/parser/** @jroesch @slyubomirsky
# runtime
src/runtime/** @vinx13 @tqchen @FronzenGene @liangfu @areusch @tmoreau89 @ajtulloch @masahi @kazum @ZihengJiang @junrushao1994
include/tvm/runtime/** @vinx13 @tqchen @FronzenGene @liangfu @areusch @tmoreau89 @ajtulloch @masahi @kazum @ZihengJiang @junrushao1994
python/tvm/runtime/** @vinx13 @tqchen @FronzenGene @liangfu @areusch @tmoreau89 @ajtulloch @masahi @kazum @ZihengJiang @junrushao1994
# relay
src/relay/** @jroesch @slyubomirsky @icemelon @MarisaKirisame @ZihengJiang @yzhliu @vinx13 @mbrookhart @jwfromm @zhiics @anijain2305 @wweic @eqy @junrushao1994
include/tvm/relay/** @jroesch @slyubomirsky @icemelon @MarisaKirisame @ZihengJiang @yzhliu @vinx13 @mbrookhart @jwfromm @zhiics @anijain2305 @wweic @eqy @junrushao1994
python/tvm/relay/** @jroesch @slyubomirsky @icemelon @MarisaKirisame @ZihengJiang @yzhliu @vinx13 @mbrookhart @jwfromm @zhiics @anijain2305 @wweic @eqy @junrushao1994
# relay/qnn
src/relay/qnn/** @jwfromm @anijain2305 @ZihengJiang
inlcude/tvm/relay/qnn/** @jwfromm @anijain2305 @ZihengJiang
python/tvm/relay/qnn/** @jwfromm @anijain2305 @ZihengJiang
# relay/backend/contrib: BYOC
src/relay/backend/contrib/** @zhiics @trevor-m @comaniac @mbaret @manupa-arm
# relay/frontends
python/tvm/relay/frontend/** @jwfromm @mbrookhart @srkreddy1238 @siju-samuel @Huyuwei @hlu1 @kazum @PariksheetPinjari909
# topi: Operator definitions
src/topi/** @Laurawly @Huyuwei @kevinthesun @jwfromm @vinx13 @masahi @FronzenGene @yzhliu @mbrookhart @ZihengJiang @jcf94
include/tvm/topi/** @Laurawly @Huyuwei @kevinthesun @jwfromm @vinx13 @masahi @FronzenGene @yzhliu @mbrookhart @ZihengJiang @jcf94
python/tvm/topi/** @Laurawly @Huyuwei @kevinthesun @jwfromm @vinx13 @masahi @FronzenGene @yzhliu @mbrookhart @ZihengJiang @jcf94
# tvm/driver/
python/tvm/driver/** @leandron @jwfromm @tqchen @jroesch
# tvm/driver/tvmc
python/tvm/driver/tvmc/** @leandron @jwfromm
+32
View File
@@ -0,0 +1,32 @@
---
name: "\U0001F41B Bug report"
about: Please include a description of your environment, preferably a minimum script to reproduce the problem. Find the list of label tags at https://github.com/apache/tvm/labels.
title: "[Bug] "
labels: "needs-triage, type: bug"
---
Thanks for participating in the TVM community! We use https://discuss.tvm.apache.org/ for any general usage questions and discussions. The issue tracker is used for actionable items such as feature proposals discussion, roadmaps, and bug tracking. You are always welcomed to post on the forum first :smile_cat:
Issues that are inactive for a period of time may get closed. We adopt this policy so that we won't lose track of actionable issues that may fall at the bottom of the pile. Feel free to reopen a new one if you feel there is an additional problem that needs attention when an old one gets closed.
### Expected behavior
What you were expecting
### Actual behavior
What actually happened
### Environment
Any environment details, such as: Operating System, TVM version, etc
### Steps to reproduce
Preferably a minimal script to cause the issue to occur.
### Triage
Please refer to the list of label tags [here](https://github.com/apache/tvm/labels) to find the relevant tags and add them below in a bullet format (example below).
* needs-triage
+28
View File
@@ -0,0 +1,28 @@
---
name: "\U0000274C CI Problem"
about: To help the developers act on these problems, please give us as many details of the CI failure as possible. Find the list of label tags at https://github.com/apache/tvm/labels.
title: "[CI Problem] "
labels: "needs-triage, type:ci"
---
Thanks for participating in the TVM community! We use https://discuss.tvm.apache.org/ for any general usage questions and discussions. The issue tracker is used for actionable items such as feature proposals discussion, roadmaps, and bug tracking. You are always welcomed to post on the forum first :smile_cat:
Issues that are inactive for a period of time may get closed. We adopt this policy so that we won't lose track of actionable issues that may fall at the bottom of the pile. Feel free to reopen a new one if you feel there is an additional problem that needs attention when an old one gets closed.
### Branch/PR Failing
Please provide a link to the PR that has failed to run CI.
### Jenkins Link
Provide a link to the specific run that has failed.
### Flakiness
Have you seen this multiple times in this branch or in other branches?
### Triage
Please refer to the list of label tags [here](https://github.com/apache/tvm/labels) to find the relevant tags and add them below in a bullet format (example below).
* needs-triage
+5
View File
@@ -0,0 +1,5 @@
blank_issues_enabled: false # default: true
contact_links:
- name: 💬 Discourse
url: https://discuss.tvm.apache.org/
about: Thanks for participating in the TVM community! We use https://discuss.tvm.apache.org/ for any general usage questions and discussions. The issue tracker is used for actionable items such as feature proposals discussion, roadmaps, and bug tracking. You are always welcomed to post on the forum first 😺
+26
View File
@@ -0,0 +1,26 @@
---
name: "\U0001F4C4 Documentation"
about: Use this template to suggest additions and changes to the documentation. Find the list of label tags at https://github.com/apache/tvm/labels.
title: "[Docs] "
labels: "needs-triage, type: doc"
---
Thanks for participating in the TVM community! We use https://discuss.tvm.apache.org/ for any general usage questions and discussions. The issue tracker is used for actionable items such as feature proposals discussion, roadmaps, and bug tracking. You are always welcomed to post on the forum first :smile_cat:
Issues that are inactive for a period of time may get closed. We adopt this policy so that we won't lose track of actionable issues that may fall at the bottom of the pile. Feel free to reopen a new one if you feel there is an additional problem that needs attention when an old one gets closed.
### Documentation Title & Type
Include the title of the document (e.g. "Getting Started with TVM"), and the type of documentation (e.g. user docs, developer docs, tutorials)
### Additions/Changes Requested
If an RFC/discuss post exists, link it here.
Otherwise, specify what actions should be taken to provide additional clarity/readability/reproducibility to the document. Include code snippets from the previous documentation if applicable.
### Triage
Please refer to the list of label tags [here](https://github.com/apache/tvm/labels) to find the relevant tags and add them below in a bullet format (example below).
* needs-triage
@@ -0,0 +1,19 @@
---
name: "\U0001F527 Feature Tracking"
about: List clear, small actionable items so we can track the progress of the change. Find the list of label tags at https://github.com/apache/tvm/labels.
title: "[Tracking Issue] "
labels: "needs-triage, type:rfc-tracking"
---
Thanks for participating in the TVM community! We use https://discuss.tvm.apache.org/ for any general usage questions and discussions. The issue tracker is used for actionable items such as feature proposals discussion, roadmaps, and bug tracking. You are always welcomed to post on the forum first :smile_cat:
Issues that are inactive for a period of time may get closed. We adopt this policy so that we won't lose track of actionable issues that may fall at the bottom of the pile. Feel free to reopen a new one if you feel there is an additional problem that needs attention when an old one gets closed.
### This issue is to track progress for FEATURE NAME
- [ ] P1. Title of this piece of the feature (PR link if available)
### Triage
Please refer to the list of label tags [here](https://github.com/apache/tvm/labels) to find the relevant tags and add them below in a bullet format (example below).
* needs-triage
+24
View File
@@ -0,0 +1,24 @@
---
name: "\U00002744 Flaky Test"
about: Report flaky tests, make sure to include link to CI runs, a sample failure log, and the name of the test(s). Find the list of label tags at https://github.com/apache/tvm/labels.
title: "[Flaky Test] "
labels: "needs-triage, test: flaky"
---
Thanks for participating in the TVM community! We use https://discuss.tvm.apache.org/ for any general usage questions and discussions. The issue tracker is used for actionable items such as feature proposals discussion, roadmaps, and bug tracking. You are always welcomed to post on the forum first :smile_cat:
These tests were found to be flaky (intermittently failing on `main` or failed in a PR with unrelated changes). As per [the docs](https://github.com/apache/tvm/blob/main/docs/contribute/ci.rst#handling-flaky-failures), these failures will be disabled in a PR that references this issue until the test owners can fix the source of the flakiness.
### Test(s)
- `tests/python/some_file.py::the_test_name`
### Jenkins Links
- Please provide link(s) to failed CI runs. If runs are for a PR, explain why your PR did not break the test (e.g. did not touch that part of the codebase)
### Triage
Please refer to the list of label tags [here](https://github.com/apache/tvm/labels) to find the relevant tags and add them below in a bullet format (example below).
* needs-triage
@@ -0,0 +1,148 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
name: Build TVM Wheel
description: >
Build and test the LLVM-enabled TVM wheel for a given OS/architecture
combination using cibuildwheel.
inputs:
arch:
description: "Target architecture for cibuildwheel (e.g., x86_64, aarch64, arm64, AMD64)"
required: true
build:
description: "cibuildwheel build selector (e.g., cp310-manylinux_x86_64)"
required: true
cmake_defines:
description: "Feature CMake defines that vary by wheel (e.g. -DUSE_METAL=ON on macOS)"
required: false
default: ""
include_cuda_runtime:
description: "Set to 1 to build and inject the CUDA runtime library (Linux only)"
required: false
default: "0"
runs:
using: "composite"
steps:
# Single source of truth for the LLVM toolchain version, shared by the cache
# key and the conda install steps below.
- name: Set LLVM version
shell: bash
run: echo "LLVM_VERSION=22.1.0" >> "$GITHUB_ENV"
- name: Prepare LLVM cache path (Unix)
if: runner.os != 'Windows'
shell: bash
run: |
set -eux
sudo mkdir -p /opt/llvm
sudo chown -R "$(whoami)" /opt/llvm
# ---- Cache LLVM prefix ----
- name: Cache LLVM
uses: actions/cache@v5.0.5
id: llvm-cache
with:
path: ${{ runner.os == 'Windows' && 'C:/opt/llvm' || '/opt/llvm' }}
key: tvm-wheel-llvm-${{ env.LLVM_VERSION }}-${{ runner.os }}-${{ inputs.arch }}-v6
# ---- Install LLVM via conda (cache miss only) ----
- name: Setup conda
if: steps.llvm-cache.outputs.cache-hit != 'true'
uses: conda-incubator/setup-miniconda@v4.0.1
continue-on-error: true
id: conda1
with:
miniforge-version: latest
conda-remove-defaults: true
- name: Setup conda (retry with tar.bz2)
if: steps.llvm-cache.outputs.cache-hit != 'true' && steps.conda1.outcome == 'failure'
uses: conda-incubator/setup-miniconda@v4.0.1
with:
miniforge-version: latest
use-only-tar-bz2: true
conda-remove-defaults: true
- name: Install LLVM (Unix)
if: steps.llvm-cache.outputs.cache-hit != 'true' && runner.os != 'Windows'
shell: bash -l {0}
run: |
set -eux
if [[ "${RUNNER_OS}" == "Linux" ]]; then
sudo mkdir -p /opt/llvm
sudo chown -R "$(whoami)" /opt/llvm
fi
conda create -q -p /opt/llvm -c conda-forge \
"llvmdev=${LLVM_VERSION}" "clangdev=${LLVM_VERSION}" "compiler-rt=${LLVM_VERSION}" zlib zstd-static libxml2-devel \
-y
- name: Install LLVM (Windows)
if: steps.llvm-cache.outputs.cache-hit != 'true' && runner.os == 'Windows'
shell: cmd /C call {0}
run: |
call conda create -q -p C:\opt\llvm -c conda-forge llvmdev=%LLVM_VERSION% zlib zstd-static libxml2-devel -y
# The {project} placeholder is not expanded inside CIBW_ENVIRONMENT values, so
# compute the forward-slash host path here (the Windows analog of the Linux
# /project hardcode).
- name: Compute CUDA sidecar path (Windows)
if: runner.os == 'Windows' && inputs.include_cuda_runtime == '1'
shell: bash
run: echo "TVM_CUDA_EXTRA_LIB=$(cygpath -m "$(pwd)")/build-wheel-cuda/lib/tvm_runtime_cuda.dll" >> "$GITHUB_ENV"
# ---- Build and test wheels ----
- name: Build and test wheels
uses: pypa/cibuildwheel@294735312765b09d24a2fbec22660ce817587d55 # v4.1.0
with:
package-dir: .
output-dir: wheelhouse
env:
CIBW_BUILD: ${{ inputs.build }}
CIBW_ARCHS_LINUX: ${{ inputs.arch }}
CIBW_ARCHS_MACOS: ${{ inputs.arch }}
CIBW_ARCHS_WINDOWS: ${{ inputs.arch }}
# Linux builds run in cibuildwheel's default manylinux_2_28 container;
# bind-mount the cached LLVM prefix into it. Ignored on macOS/Windows,
# which build without a container.
CIBW_CONTAINER_ENGINE: "docker; create_args: --volume /opt/llvm:/opt/llvm:ro"
# Each per-OS block carries the toolchain location (conda LLVM prefix +
# llvm-config path), the universal static-link policy (USE_LLVM --link-static
# and ZLIB_USE_STATIC_LIBS), and the CUDA sidecar. Feature defines that vary by
# wheel (e.g. USE_METAL on macOS) come from the matrix via inputs.cmake_defines.
# One explicit block per build platform (macOS / Linux / Windows); cibuildwheel
# env overrides replace rather than merge, so there is no shared base block.
CIBW_ENVIRONMENT_MACOS: >-
CMAKE_PREFIX_PATH="/opt/llvm"
CMAKE_ARGS="-DUSE_LLVM='/opt/llvm/bin/llvm-config --link-static' -DZLIB_USE_STATIC_LIBS=ON -DCMAKE_PREFIX_PATH=/opt/llvm ${{ inputs.cmake_defines }}"
CIBW_ENVIRONMENT_LINUX: >-
CMAKE_PREFIX_PATH="/opt/llvm"
LIBRARY_PATH="/opt/llvm/lib"
CMAKE_ARGS="-DUSE_LLVM='/opt/llvm/bin/llvm-config --link-static' -DZLIB_USE_STATIC_LIBS=ON -DCMAKE_PREFIX_PATH=/opt/llvm ${{ inputs.cmake_defines }} -DTVM_PACKAGE_EXTRA_LIBS=/project/build-wheel-cuda/lib/libtvm_runtime_cuda.so"
CIBW_ENVIRONMENT_WINDOWS: >-
CMAKE_PREFIX_PATH="C:/opt/llvm/Library"
PATH="C:/opt/llvm/Library/bin;$PATH"
CMAKE_ARGS="-DUSE_LLVM='C:/opt/llvm/Library/bin/llvm-config.exe --link-static' -DZLIB_USE_STATIC_LIBS=ON -DCMAKE_PREFIX_PATH=C:/opt/llvm/Library ${{ inputs.cmake_defines }} -DTVM_PACKAGE_EXTRA_LIBS=${{ env.TVM_CUDA_EXTRA_LIB }}"
# Turns the wheel-specific assertions in
# tests/python/all-platform-minimal-test/test_validate_runtime_library.py
# ON (they skip unless these are set). Every published wheel is LLVM-enabled,
# so TVM_WHEEL_EXPECT_LLVM is always "1"; the CUDA runtime is only bundled on
# the CUDA wheels, so TVM_WHEEL_EXPECT_CUDA_RUNTIME is "0" for CPU wheels.
CIBW_TEST_ENVIRONMENT: >-
TVM_WHEEL_EXPECT_LLVM="1"
TVM_WHEEL_EXPECT_CUDA_RUNTIME="${{ inputs.include_cuda_runtime }}"
@@ -0,0 +1,40 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
name: Detect Environment Variables
description: Detects environment variables such as CPU count and sets them as outputs.
runs:
using: "composite"
steps:
- name: Run Python to detect environment variables
shell: python
id: detect
run: |
import multiprocessing, os
output_file = open(os.environ.get("GITHUB_OUTPUT"), "a")
def write_env_var(name, value):
output_file.write(f"{name}={value}\n")
print(f"Detected environment variable: {name}={value}")
write_env_var("cpu_count", multiprocessing.cpu_count())
outputs:
cpu_count:
description: "The number of CPU cores"
value: ${{ steps.detect.outputs.cpu_count }}
+47
View File
@@ -0,0 +1,47 @@
name: Setup TVM CI environment
description: Set up the cached conda build environment and install tvm-ffi.
runs:
using: "composite"
steps:
- uses: actions/cache@v5.0.5
env:
CACHE_NUMBER: 2
with:
path: ~/conda_pkgs_dir
key: ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}-${{ hashFiles('ci/scripts/package/build-environment.yaml') }}
- uses: conda-incubator/setup-miniconda@v4.0.1
continue-on-error: true
id: conda1
with:
activate-environment: tvm-build
channel-priority: strict
environment-file: ci/scripts/package/build-environment.yaml
auto-activate-base: false
miniforge-version: latest
python-version: "3.10"
condarc-file: tests/conda/condarc
conda-remove-defaults: true
- uses: conda-incubator/setup-miniconda@v4.0.1
if: steps.conda1.outcome == 'failure'
with:
activate-environment: tvm-build
channel-priority: strict
environment-file: ci/scripts/package/build-environment.yaml
auto-activate-base: false
miniforge-version: latest
use-only-tar-bz2: true
python-version: "3.10"
condarc-file: tests/conda/condarc
conda-remove-defaults: true
- name: Conda info
shell: pwsh
run: |
mamba info
mamba list
mamba info --envs
mamba list --name base
- name: Install tvm-ffi pip package
shell: bash -l {0}
run: |
pip install -v ./3rdparty/tvm-ffi
+15
View File
@@ -0,0 +1,15 @@
# See https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#about-the-dependabotyml-file
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "monthly"
open-pull-requests-limit: 0
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "monthly"
open-pull-requests-limit: 0
+41
View File
@@ -0,0 +1,41 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
name: Lint
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
concurrency:
group: Lint-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
with:
fetch-depth: 0
fetch-tags: true
- uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1
+119
View File
@@ -0,0 +1,119 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
# GH actions.
# We use it to cover windows and mac builds
# Jenkins is still the primary CI
name: CI
on:
push:
branches:
- main
pull_request:
branches:
- main
workflow_dispatch:
permissions:
contents: read
concurrency:
group: CI-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
jobs:
MacOS:
if: ${{ github.repository == 'apache/tvm' }}
runs-on: macOS-latest
steps:
- uses: actions/checkout@v6.0.2
with:
submodules: 'recursive'
- name: Set up environment
uses: ./.github/actions/setup
- name: Detect environment variables
uses: ./.github/actions/detect-env-vars
id: env_vars
- name: Build TVM wheel
shell: bash -l {0}
env:
CMAKE_BUILD_PARALLEL_LEVEL: ${{ steps.env_vars.outputs.cpu_count }}
run: |
pip install scikit-build-core
export CMAKE_ARGS="-DUSE_LLVM=ON -DBUILD_TESTING=OFF"
pip wheel --no-deps -w dist . -v
- name: Install TVM from wheel
shell: bash -l {0}
run: |
export TVM_FFI_VERSION="$(python -c 'from importlib.metadata import version; print(version("apache-tvm-ffi"))')"
pip install ml_dtypes
# Keep the source-matched tvm-ffi installed by the setup action.
pip install --no-deps dist/*.whl
python - <<'PY'
import os
from importlib.metadata import requires, version
assert version("apache-tvm-ffi") == os.environ["TVM_FFI_VERSION"]
assert "apache-tvm-ffi>=0.1.13" in requires("apache-tvm")
PY
- name: Test
shell: bash -l {0}
run: >-
python -m pytest -v tests/python/all-platform-minimal-test
- name: Minimal Metal Compile-Only
shell: bash -l {0}
run: |
python -m pytest -v -s 'tests/python/codegen/test_gpu_codegen_allreduce.py::test_allreduce_sum_compile'
python -m pytest -v -s 'tests/python/codegen/test_target_codegen_metal.py::test_func_with_trailing_pod_params'
- name: Minimal Metal Compile-and-Run
shell: bash -l {0}
run: |
python -m pytest -v -s 'tests/python/codegen/test_target_codegen_metal.py'
python -m pytest -v -s 'tests/python/codegen/test_target_codegen_gpu_common.py'
python -m pytest -v -s 'tests/python/codegen/test_gpu_codegen_allreduce.py::test_allreduce_sum[dims0-metal]'
Windows:
if: ${{ github.repository == 'apache/tvm' }}
runs-on: windows-latest
steps:
- uses: actions/checkout@v6.0.2
with:
submodules: 'recursive'
- name: Set up environment
uses: ./.github/actions/setup
- name: Detect environment variables
uses: ./.github/actions/detect-env-vars
id: env_vars
- name: Install TVM
shell: cmd /C call {0}
env:
CMAKE_BUILD_PARALLEL_LEVEL: ${{ steps.env_vars.outputs.cpu_count }}
run: |
pip install scikit-build-core
set CMAKE_ARGS=-DUSE_LLVM=ON -DBUILD_TESTING=OFF
pip install --no-deps . -v
- name: Install test dependencies
shell: cmd /C call {0}
run: |
pip install psutil cloudpickle ml_dtypes numpy packaging scipy tornado typing_extensions
- name: Test
shell: cmd /C call {0}
run: >-
python -m pytest -v tests/python/all-platform-minimal-test
@@ -0,0 +1,24 @@
name: Nightly Docker Update
on:
workflow_dispatch:
concurrency:
group: nightly-docker-update
cancel-in-progress: true
jobs:
open_update_pr:
permissions:
contents: write
pull-requests: write
if: github.repository == 'apache/tvm'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
- name: Open PR to update Docker images
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -eux
python ci/scripts/jenkins/open_docker_update_pr.py
+219
View File
@@ -0,0 +1,219 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
name: Publish TVM wheels
on:
workflow_dispatch:
inputs:
tag:
description: "Tag, branch, or SHA to build"
required: true
type: string
publish_repository:
description: "Where to publish after the wheel build succeeds"
required: true
default: "none"
type: choice
options:
- none
- pypi
permissions:
contents: read
# CI runners are ephemeral, so pip's HTTP cache buys nothing and a stale
# preinstalled cache (e.g. on the macOS image) only produces noisy
# "Cache entry deserialization failed" warnings. Disable it everywhere.
env:
PIP_NO_CACHE_DIR: "1"
jobs:
# Build the CUDA runtime sidecar once per arch and upload it as an artifact that
# build_wheels bundles. The Linux legs build inside the official manylinux_2_28
# CUDA image (CUDA toolkit preinstalled, see pypa/manylinux) -- the same glibc
# baseline cibuildwheel targets for the wheel -- so the sidecar stays
# ABI-compatible with the wheel's libtvm_runtime.so regardless of the exact tag.
build_cuda_runtime:
name: ${{ matrix.name }}
runs-on: ${{ matrix.os }}
container: ${{ matrix.container }}
strategy:
fail-fast: false
matrix:
include:
- name: "CUDA runtime sidecar (Linux x86_64, manylinux_2_28)"
os: ubuntu-latest
container: quay.io/manylinux_cuda/manylinux_2_28_x86_64_cuda13_1:latest
arch: x86_64
script: ci/scripts/package/manylinux_build_libtvm_runtime_cuda.sh
lib: build-wheel-cuda/lib/libtvm_runtime_cuda.so
- name: "CUDA runtime sidecar (Linux aarch64, manylinux_2_28)"
os: ubuntu-24.04-arm
container: quay.io/manylinux_cuda/manylinux_2_28_aarch64_cuda13_1:latest
arch: aarch64
script: ci/scripts/package/manylinux_build_libtvm_runtime_cuda.sh
lib: build-wheel-cuda/lib/libtvm_runtime_cuda.so
- name: "CUDA runtime sidecar (Windows AMD64)"
os: windows-2022
container: ""
arch: AMD64
script: ci/scripts/package/windows_build_libtvm_runtime_cuda.bat
lib: build-wheel-cuda/lib/tvm_runtime_cuda.dll
steps:
# The containerized Linux legs check out as root; mark the tree safe so git
# (submodule init in checkout) does not bail on "dubious ownership".
- name: Mark workspace safe for git
if: runner.os == 'Linux'
shell: bash
run: git config --global --add safe.directory '*'
- name: Checkout source
uses: actions/checkout@v6.0.2
with:
ref: ${{ inputs.tag }}
submodules: recursive
fetch-depth: 1
fetch-tags: true
# Windows has no manylinux interpreter; the script's pip install needs one.
- name: Set up Python (Windows host)
if: runner.os == 'Windows'
uses: actions/setup-python@v6.2.0
with:
python-version: "3.10"
- name: Build CUDA runtime sidecar (Unix)
if: runner.os != 'Windows'
shell: bash
run: bash ${{ matrix.script }}
- name: Build CUDA runtime sidecar (Windows)
if: runner.os == 'Windows'
shell: cmd
run: call ${{ matrix.script }}
- name: Upload CUDA runtime sidecar
uses: actions/upload-artifact@v7.0.1
with:
name: tvm-cuda-runtime-${{ matrix.arch }}
path: ${{ matrix.lib }}
if-no-files-found: error
build_wheels:
name: ${{ matrix.name }}
# All-or-nothing: a failed sidecar leg blocks the whole wheel matrix (a publish
# needs the complete 4-wheel set anyway).
needs: [build_cuda_runtime]
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- name: "Linux x86_64 wheel with CUDA runtime (manylinux_2_28)"
os: ubuntu-latest
arch: x86_64
build: cp310-manylinux_x86_64
include_cuda_runtime: "1"
artifact_suffix: linux-x86_64-manylinux_2_28
- name: "Linux aarch64 wheel with CUDA runtime (manylinux_2_28)"
os: ubuntu-24.04-arm
arch: aarch64
build: cp310-manylinux_aarch64
include_cuda_runtime: "1"
artifact_suffix: linux-aarch64-manylinux_2_28
- name: "macOS arm64 wheel with Metal"
os: macos-14
arch: arm64
build: cp310-macosx_arm64
include_cuda_runtime: "0"
artifact_suffix: macos-arm64
cmake_defines: "-DUSE_METAL=ON"
- name: "Windows AMD64 wheel with CUDA runtime"
os: windows-2022
arch: AMD64
build: cp310-win_amd64
include_cuda_runtime: "1"
artifact_suffix: windows-amd64
steps:
- name: Checkout source
uses: actions/checkout@v6.0.2
with:
ref: ${{ inputs.tag }}
submodules: recursive
# Full history + tags so setuptools_scm can derive the wheel version from
# the most recent Git tag during the build (shallow clones break it).
fetch-depth: 0
fetch-tags: true
# Land the sidecar where -DTVM_PACKAGE_EXTRA_LIBS / cibuildwheel's /project
# mount expects it. Skipped on CPU-only rows (macOS).
- name: Download CUDA runtime sidecar
if: ${{ matrix.include_cuda_runtime == '1' }}
uses: actions/download-artifact@v8.0.1
with:
name: tvm-cuda-runtime-${{ matrix.arch }}
path: build-wheel-cuda/lib
- name: Build TVM wheel
uses: ./.github/actions/build-wheel-for-publish
with:
arch: ${{ matrix.arch }}
build: ${{ matrix.build }}
cmake_defines: ${{ matrix.cmake_defines }}
include_cuda_runtime: ${{ matrix.include_cuda_runtime }}
- name: Upload wheel artifact
uses: actions/upload-artifact@v7.0.1
with:
name: tvm-wheel-${{ matrix.artifact_suffix }}
path: wheelhouse/*.whl
if-no-files-found: error
upload_pypi:
name: Upload package distributions
needs: [build_wheels]
if: ${{ inputs.publish_repository != 'none' }}
runs-on: ubuntu-latest
environment: ${{ inputs.publish_repository }}
permissions:
id-token: write
attestations: write
steps:
- uses: actions/download-artifact@v8.0.1
with:
pattern: tvm-wheel-*
path: dist
merge-multiple: true
# Print wheel sizes for visibility only. We do not fail on the PyPI 100 MB
# limit here -- the publish step's upload would surface that error directly.
- name: Print wheel sizes
shell: bash
run: ls -alh dist/*.whl
- name: Generate artifact attestation for wheels
uses: actions/attest-build-provenance@v4.1.0
with:
subject-path: dist/*
- name: Publish package distributions to PyPI
if: ${{ inputs.publish_repository == 'pypi' }}
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
with:
attestations: true
verbose: true
+31
View File
@@ -0,0 +1,31 @@
name: tvm-bot
on:
issue_comment:
types: [created]
concurrency:
group: merge-${{ github.event.issue.number }}
cancel-in-progress: true
jobs:
run-tvm-bot:
permissions:
contents: write
issues: write
pull-requests: write
if: ${{ github.event.issue.pull_request && startsWith(github.event.comment.body, '@tvm-bot ') && github.repository == 'apache/tvm' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
- name: Run tvm-bot
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_ACTIONS_TOKEN: ${{ secrets.GH_ACTIONS_TOKEN }}
TVM_BOT_JENKINS_TOKEN: ${{ secrets.TVM_BOT_JENKINS_TOKEN }}
PR_NUMBER: ${{ github.event.issue.number }}
ISSUE_COMMENT: ${{ toJson(github.event.comment) }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
set -eux
python ci/scripts/github/github_tvmbot.py --pr "$PR_NUMBER" --run-url "$RUN_URL" --trigger-comment-json "$ISSUE_COMMENT"
+296
View File
@@ -0,0 +1,296 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
*.S
# C extensions
*.so
*.ll
.npm
# Distribution / packaging
.Python
env/
build/
build-*/
!.github/actions/build-*/
!.github/actions/build-*/action.yml
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
.conda/
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Generated by python/gen_requirements.py
python/requirements/*.txt
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
docs/_staging/
# PyBuilder
/target/
# IPython Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# dotenv
.env
# virtualenv
venv/
ENV/
# Spyder project settings
.spyderproject
# Rope project settings
.ropeproject
*~
*.pyc
*~
config.mk
config.cmake
Win32
*.dir
perf
*.wasm
.emscripten
## IOS
DerivedData/
## Java
*.class
jvm/*/target/
jvm/*/*/target/
jvm/native/*/generated
jvm/native/src/main/native/org_apache_tvm_native_c_api.h
*.worksheet
*.idea
*.iml
*.classpath
*.project
*.settings
*/node_modules/
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/
.pkl_memoize_*
.emscripten*
.m2
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
## Other
*.moved-aside
*.xccheckout
*.xcscmblueprint
.DS_Store
tags
cscope*
*.lock
# vim temporary files
*.swp
*.swo
# TVM generated code
perf
.bash_history
*.json
*.params
*.ro
*.onnx
*.h5
synset.txt
cat.jpg
cat.png
docs.tgz
cat.png
*.mlmodel
tvm_u.*
tvm_t.*
# Mac OS X
.DS_Store
# Jetbrain
.idea
.ipython
.jupyter
.nv
.pylint.d
.python_history
.pytest_cache
.local
cmake-build-debug
# Visual Studio
.vs
# Visual Studio Code
.vscode
# tmp file
.nfs*
# keys
*.pem
*.p12
*.pfx
*.cer
*.crt
*.der
# patch sentinel
patched.txt
# Python type checking
.mypy_cache/
.pyre/
# pipenv files
Pipfile
Pipfile.lock
# conda package artifacts
conda/Dockerfile.cuda*
conda/pkg
.node_repl_history
# nix files
.envrc
*.nix
# Docker files
.sudo_as_admin_successful
# Downloaded models/datasets
.tvm_test_data
.dgl
.caffe2
# Local docs build
_docs/
jvm/target
.config/configstore/
.ci-py-scripts/
# Generated Hexagon files
src/runtime/hexagon/rpc/hexagon_rpc.h
src/runtime/hexagon/rpc/hexagon_rpc_skel.c
src/runtime/hexagon/rpc/hexagon_rpc_stub.c
# Local tvm-site checkout
tvm-site/
# Test sample data files
!tests/python/ci/sample_prs/*.json
# Used in CI to communicate between Python and Jenkins
.docker-image-names/
# Printed TIR code on disk
*.tir
# GDB history file
.gdb_history
# Less command history file
.lesshst
# Agent tracking files
LOG.md
STATUS.md
# Local editable-install artifacts (pip install -e .)
python/tvm_ffi/
# Generated by setuptools_scm at build time
python/tvm/_version.py
python/bin/
python/typing_extensions.py
python/*.dist-info/
pytest-of-bohanhou/
+15
View File
@@ -0,0 +1,15 @@
[submodule "3rdparty/cutlass"]
path = 3rdparty/cutlass
url = https://github.com/NVIDIA/cutlass.git
[submodule "3rdparty/OpenCL-Headers"]
path = 3rdparty/OpenCL-Headers
url = https://github.com/KhronosGroup/OpenCL-Headers.git
[submodule "3rdparty/cutlass_fpA_intB_gemm"]
path = 3rdparty/cutlass_fpA_intB_gemm
url = https://github.com/tlc-pack/cutlass_fpA_intB_gemm
[submodule "3rdparty/libflash_attn"]
path = 3rdparty/libflash_attn
url = https://github.com/tlc-pack/libflash_attn
[submodule "3rdparty/tvm-ffi"]
path = 3rdparty/tvm-ffi
url = https://github.com/apache/tvm-ffi
+19
View File
@@ -0,0 +1,19 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
config:
MD013: false
+99
View File
@@ -0,0 +1,99 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
exclude: ^(\.txdev/|\.claude/)
default_install_hook_types:
- pre-commit
repos:
# ---------- Local hooks ----------
- repo: local
hooks:
- id: check-asf-header
name: check ASF Header
entry: python tests/lint/check_asf_header.py --check
language: python
language_version: python3
pass_filenames: false
verbose: false
- id: check-file-type
name: check file types
entry: python tests/lint/check_file_type.py
language: python
language_version: python3
pass_filenames: false
verbose: false
# ---------- General file checks ----------
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: check-added-large-files
- id: check-case-conflict
- id: check-merge-conflict
- id: check-symlinks
- id: end-of-file-fixer
exclude: ^3rdparty/
- id: mixed-line-ending
exclude: ^3rdparty/
- id: requirements-txt-fixer
exclude: ^docker/python/bootstrap
- id: trailing-whitespace
exclude: ^3rdparty/
- id: check-yaml
- id: check-toml
# ---------- YAML lint ----------
- repo: https://github.com/adrienverge/yamllint
rev: v1.35.1
hooks:
- id: yamllint
args:
- --config-file
- .yamllint.yaml
exclude: ^\.github/
# ---------- TOML format ----------
- repo: https://github.com/ComPWA/taplo-pre-commit
rev: v0.9.3
hooks:
- id: taplo-format
# ---------- Python: ruff ----------
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.12.3
hooks:
- id: ruff-check
types_or: [python, pyi, jupyter]
args: [--fix]
- id: ruff-format
types_or: [python, pyi, jupyter]
# ---------- C/C++: clang-format ----------
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: "v20.1.8"
hooks:
- id: clang-format
types_or: [c, c++, cuda, objective-c++]
exclude: ^3rdparty/
# ---------- Cython ----------
- repo: https://github.com/MarcoGorelli/cython-lint
rev: v0.16.7
hooks:
- id: cython-lint
args: [--max-line-length=120]
- id: double-quote-cython-strings
+32
View File
@@ -0,0 +1,32 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
extends: default
rules:
document-start: disable
line-length:
max: 120
level: warning
truthy:
allowed-values:
- "on"
- "off"
- "yes"
- "no"
- "true"
- "false"
+246
View File
@@ -0,0 +1,246 @@
/*
* Copyright (c) 2009-2015 by llvm/compiler-rt contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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
* 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 SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* \file builtin_fp16.h
* \brief Functions for conversion between fp32 and fp16, adopted from compiler-rt.
*/
#ifndef COMPILER_RT_BUILTIN_FP16_H_
#define COMPILER_RT_BUILTIN_FP16_H_
#ifdef _MSC_VER
#pragma warning(disable : 4305 4805)
#endif
#include <cstdint>
static inline uint32_t __clz(uint32_t x) {
// count leading zeros
int n = 32;
uint32_t y;
y = x >> 16;
if (y) {
n = n - 16;
x = y;
}
y = x >> 8;
if (y) {
n = n - 8;
x = y;
}
y = x >> 4;
if (y) {
n = n - 4;
x = y;
}
y = x >> 2;
if (y) {
n = n - 2;
x = y;
}
y = x >> 1;
if (y) return n - 2;
return n - x;
}
template <typename SRC_T, typename SRC_REP_T, int SRC_SIG_BITS, typename DST_T, typename DST_REP_T,
int DST_SIG_BITS>
static inline DST_T __truncXfYf2__(SRC_T a) {
// Various constants whose values follow from the type parameters.
// Any reasonable optimizer will fold and propagate all of these.
const int srcBits = sizeof(SRC_T) * 8;
const int srcExpBits = srcBits - SRC_SIG_BITS - 1;
const int srcInfExp = (1 << srcExpBits) - 1;
const int srcExpBias = srcInfExp >> 1;
const SRC_REP_T srcMinNormal = SRC_REP_T(1) << SRC_SIG_BITS;
const SRC_REP_T srcSignificandMask = srcMinNormal - 1;
const SRC_REP_T srcInfinity = (SRC_REP_T)srcInfExp << SRC_SIG_BITS;
const SRC_REP_T srcSignMask = SRC_REP_T(1) << (SRC_SIG_BITS + srcExpBits);
const SRC_REP_T srcAbsMask = srcSignMask - 1;
const SRC_REP_T roundMask = (SRC_REP_T(1) << (SRC_SIG_BITS - DST_SIG_BITS)) - 1;
const SRC_REP_T halfway = SRC_REP_T(1) << (SRC_SIG_BITS - DST_SIG_BITS - 1);
const SRC_REP_T srcQNaN = SRC_REP_T(1) << (SRC_SIG_BITS - 1);
const SRC_REP_T srcNaNCode = srcQNaN - 1;
const int dstBits = sizeof(DST_T) * 8;
const int dstExpBits = dstBits - DST_SIG_BITS - 1;
const int dstInfExp = (1 << dstExpBits) - 1;
const int dstExpBias = dstInfExp >> 1;
const int underflowExponent = srcExpBias + 1 - dstExpBias;
const int overflowExponent = srcExpBias + dstInfExp - dstExpBias;
const SRC_REP_T underflow = (SRC_REP_T)underflowExponent << SRC_SIG_BITS;
const SRC_REP_T overflow = (SRC_REP_T)overflowExponent << SRC_SIG_BITS;
const DST_REP_T dstQNaN = DST_REP_T(1) << (DST_SIG_BITS - 1);
const DST_REP_T dstNaNCode = dstQNaN - 1;
// Break a into a sign and representation of the absolute value
union SrcExchangeType {
SRC_T f;
SRC_REP_T i;
};
SrcExchangeType src_rep;
src_rep.f = a;
const SRC_REP_T aRep = src_rep.i;
const SRC_REP_T aAbs = aRep & srcAbsMask;
const SRC_REP_T sign = aRep & srcSignMask;
DST_REP_T absResult;
if (aAbs - underflow < aAbs - overflow) {
// The exponent of a is within the range of normal numbers in the
// destination format. We can convert by simply right-shifting with
// rounding and adjusting the exponent.
absResult = aAbs >> (SRC_SIG_BITS - DST_SIG_BITS);
absResult -= (DST_REP_T)(srcExpBias - dstExpBias) << DST_SIG_BITS;
const SRC_REP_T roundBits = aAbs & roundMask;
// Round to nearest
if (roundBits > halfway) absResult++;
// Ties to even
else if (roundBits == halfway)
absResult += absResult & 1;
} else if (aAbs > srcInfinity) {
// a is NaN.
// Conjure the result by beginning with infinity, setting the qNaN
// bit and inserting the (truncated) trailing NaN field.
absResult = (DST_REP_T)dstInfExp << DST_SIG_BITS;
absResult |= dstQNaN;
absResult |= ((aAbs & srcNaNCode) >> (SRC_SIG_BITS - DST_SIG_BITS)) & dstNaNCode;
} else if (aAbs >= overflow) {
// a overflows to infinity.
absResult = (DST_REP_T)dstInfExp << DST_SIG_BITS;
} else {
// a underflows on conversion to the destination type or is an exact
// zero. The result may be a denormal or zero. Extract the exponent
// to get the shift amount for the denormalization.
const int aExp = aAbs >> SRC_SIG_BITS;
const int shift = srcExpBias - dstExpBias - aExp + 1;
const SRC_REP_T significand = (aRep & srcSignificandMask) | srcMinNormal;
// Right shift by the denormalization amount with sticky.
if (shift > SRC_SIG_BITS) {
absResult = 0;
} else {
const bool sticky = significand << (srcBits - shift);
SRC_REP_T denormalizedSignificand = significand >> shift | sticky;
absResult = denormalizedSignificand >> (SRC_SIG_BITS - DST_SIG_BITS);
const SRC_REP_T roundBits = denormalizedSignificand & roundMask;
// Round to nearest
if (roundBits > halfway) absResult++;
// Ties to even
else if (roundBits == halfway)
absResult += absResult & 1;
}
}
// Apply the signbit to (DST_T)abs(a).
const DST_REP_T result = absResult | sign >> (srcBits - dstBits);
union DstExchangeType {
DST_T f;
DST_REP_T i;
};
DstExchangeType dst_rep;
dst_rep.i = result;
return dst_rep.f;
}
template <typename SRC_T, typename SRC_REP_T, int SRC_SIG_BITS, typename DST_T, typename DST_REP_T,
int DST_SIG_BITS>
static inline DST_T __extendXfYf2__(SRC_T a) {
// Various constants whose values follow from the type parameters.
// Any reasonable optimizer will fold and propagate all of these.
const int srcBits = sizeof(SRC_T) * 8;
const int srcExpBits = srcBits - SRC_SIG_BITS - 1;
const int srcInfExp = (1 << srcExpBits) - 1;
const int srcExpBias = srcInfExp >> 1;
const SRC_REP_T srcMinNormal = SRC_REP_T(1) << SRC_SIG_BITS;
const SRC_REP_T srcInfinity = (SRC_REP_T)srcInfExp << SRC_SIG_BITS;
const SRC_REP_T srcSignMask = SRC_REP_T(1) << (SRC_SIG_BITS + srcExpBits);
const SRC_REP_T srcAbsMask = srcSignMask - 1;
const SRC_REP_T srcQNaN = SRC_REP_T(1) << (SRC_SIG_BITS - 1);
const SRC_REP_T srcNaNCode = srcQNaN - 1;
const int dstBits = sizeof(DST_T) * 8;
const int dstExpBits = dstBits - DST_SIG_BITS - 1;
const int dstInfExp = (1 << dstExpBits) - 1;
const int dstExpBias = dstInfExp >> 1;
const DST_REP_T dstMinNormal = DST_REP_T(1) << DST_SIG_BITS;
// Break a into a sign and representation of the absolute value
union SrcExchangeType {
SRC_T f;
SRC_REP_T i;
};
SrcExchangeType src_rep;
src_rep.f = a;
const SRC_REP_T aRep = src_rep.i;
const SRC_REP_T aAbs = aRep & srcAbsMask;
const SRC_REP_T sign = aRep & srcSignMask;
DST_REP_T absResult;
// If sizeof(SRC_REP_T) < sizeof(int), the subtraction result is promoted
// to (signed) int. To avoid that, explicitly cast to SRC_REP_T.
if ((SRC_REP_T)(aAbs - srcMinNormal) < srcInfinity - srcMinNormal) {
// a is a normal number.
// Extend to the destination type by shifting the significand and
// exponent into the proper position and rebiasing the exponent.
absResult = (DST_REP_T)aAbs << (DST_SIG_BITS - SRC_SIG_BITS);
absResult += (DST_REP_T)(dstExpBias - srcExpBias) << DST_SIG_BITS;
}
else if (aAbs >= srcInfinity) {
// a is NaN or infinity.
// Conjure the result by beginning with infinity, then setting the qNaN
// bit (if needed) and right-aligning the rest of the trailing NaN
// payload field.
absResult = (DST_REP_T)dstInfExp << DST_SIG_BITS;
absResult |= (DST_REP_T)(aAbs & srcQNaN) << (DST_SIG_BITS - SRC_SIG_BITS);
absResult |= (DST_REP_T)(aAbs & srcNaNCode) << (DST_SIG_BITS - SRC_SIG_BITS);
} else if (aAbs) {
// a is denormal.
// renormalize the significand and clear the leading bit, then insert
// the correct adjusted exponent in the destination type.
const int scale = __clz(aAbs) - __clz(srcMinNormal);
absResult = (DST_REP_T)aAbs << (DST_SIG_BITS - SRC_SIG_BITS + scale);
absResult ^= dstMinNormal;
const int resultExponent = dstExpBias - srcExpBias - scale + 1;
absResult |= (DST_REP_T)resultExponent << DST_SIG_BITS;
} else {
// a is zero.
absResult = 0;
}
// Apply the signbit to (DST_T)abs(a).
const DST_REP_T result = absResult | (DST_REP_T)sign << (dstBits - srcBits);
union DstExchangeType {
DST_T f;
DST_REP_T i;
};
DstExchangeType dst_rep;
dst_rep.i = result;
return dst_rep.f;
}
#endif // COMPILER_RT_BUILTIN_FP16_H_
+396
View File
@@ -0,0 +1,396 @@
/*
* Copyright (c) 2022-2024, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <cuda_bf16.h>
#include <cuda_fp16.h>
#include <dlpack/dlpack.h>
#include <stdint.h>
#include <tvm/runtime/logging.h>
#include "custom_allreduce_kernels.h"
namespace tensorrt_llm {
static inline __device__ void st_flag_release(uint32_t& flag, uint32_t* flag_addr) {
#if __CUDA_ARCH__ >= 700
asm volatile("st.global.release.sys.b32 [%1], %0;" ::"r"(flag), "l"(flag_addr));
#else
__threadfence_system();
asm volatile("st.global.volatile.b32 [%1], %0;" ::"r"(flag), "l"(flag_addr));
#endif
}
////////////////////////////////////////////////////////////////////////////////////////////////////
static inline __device__ void ld_flag_acquire(uint32_t& flag, uint32_t* flag_addr) {
#if __CUDA_ARCH__ >= 700
asm volatile("ld.global.acquire.sys.b32 %0, [%1];" : "=r"(flag) : "l"(flag_addr));
#else
asm volatile("ld.global.volatile.b32 %0, [%1];" : "=r"(flag) : "l"(flag_addr));
#endif
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Type Converter that packs data format to 128 bits data type
//
using PackedFloat = union {
int4 packed;
float unpacked[4];
};
using PackedHalf = union {
int4 packed;
half2 unpacked[4];
};
template <typename T>
struct PackedOn16Bytes {};
template <>
struct PackedOn16Bytes<float> {
using Type = PackedFloat;
};
template <>
struct PackedOn16Bytes<half> {
using Type = PackedHalf;
};
using PackedBFloat16 = union {
int4 packed;
__nv_bfloat162 unpacked[4];
};
template <>
struct PackedOn16Bytes<__nv_bfloat16> {
using Type = PackedBFloat16;
};
// add two 128b data
template <typename T>
inline __device__ int4 add128b(T& a, T& b) {
T c;
c.unpacked[0] = a.unpacked[0] + b.unpacked[0];
c.unpacked[1] = a.unpacked[1] + b.unpacked[1];
c.unpacked[2] = a.unpacked[2] + b.unpacked[2];
c.unpacked[3] = a.unpacked[3] + b.unpacked[3];
return c.packed;
}
__inline__ __device__ void multi_gpu_barrier(uint32_t** signals, const uint32_t flag,
const size_t rank, const size_t world_size,
int const tidx, int const bidx) {
// At the end of the function, we now that has least block 0 from all others GPUs have reached
// that point.
uint32_t volatile* my_signals = signals[rank];
if (tidx < world_size) {
// The 1st block notifies the other ranks.
if (bidx == 0) {
signals[tidx][rank] = flag;
}
// Busy-wait until all ranks are ready.
while (my_signals[tidx] != flag) {
}
}
// Make sure we can move on...
__syncthreads();
}
__global__ void multiGpuBarrierKernel(AllReduceParams params) {
multi_gpu_barrier(params.peer_barrier_ptrs_out, params.barrier_flag, params.local_rank,
params.ranks_per_node, threadIdx.x, blockIdx.x);
}
template <typename T, int RANKS_PER_NODE>
static __global__ void oneShotAllReduceKernel(AllReduceParams params) {
int const bidx = blockIdx.x;
int const tidx = threadIdx.x;
// The number of elements packed into one for comms
static constexpr int NUM_ELTS = 16 / sizeof(T);
// Packed data type for comms
using PackedStruct = typename PackedOn16Bytes<T>::Type;
multi_gpu_barrier(params.peer_barrier_ptrs_in, params.barrier_flag, params.local_rank,
RANKS_PER_NODE, tidx, bidx);
// The source pointers. Distributed round-robin for the different warps.
T const* src_d[RANKS_PER_NODE];
#pragma unroll
for (int ii = 0; ii < RANKS_PER_NODE; ++ii) {
int rank = (params.local_rank + ii) % RANKS_PER_NODE;
src_d[ii] = reinterpret_cast<T*>(params.peer_comm_buffer_ptrs[rank]);
}
// The location in the destination array (load 8 fp16 or load 4 fp32 using LDG.128).
size_t offset = bidx * params.elts_per_block + tidx * NUM_ELTS;
// The end of the segment computed by that block.
size_t max_offset = min((bidx + 1) * params.elts_per_block, params.elts_per_rank);
// Each block accumulates the values from the different GPUs on the same node.
for (size_t iter_offset = offset; iter_offset < max_offset;
iter_offset += blockDim.x * NUM_ELTS) {
// Iterate over the different ranks/devices on the node to load the values.
PackedStruct vals[RANKS_PER_NODE];
#pragma unroll
for (int ii = 0; ii < RANKS_PER_NODE; ++ii) {
vals[ii].packed = *reinterpret_cast<int4 const*>(&src_d[ii][iter_offset]);
}
// Sum the values from the different ranks.
PackedStruct sums;
sums.packed = {0, 0, 0, 0};
#pragma unroll
for (int ii = 0; ii < RANKS_PER_NODE; ++ii) {
sums.packed = add128b(sums, vals[ii]);
}
// Store to the destination buffer.
*reinterpret_cast<int4*>(&reinterpret_cast<T*>(params.local_output_buffer_ptr)[iter_offset]) =
sums.packed;
}
}
template <typename T, int RANKS_PER_NODE>
static __global__ void twoShotAllReduceKernel(AllReduceParams params) {
// The block index.
int const bidx = blockIdx.x;
// The thread index with the block.
int const tidx = threadIdx.x;
// The number of elements packed into one for comms
static constexpr int NUM_ELTS = 16 / sizeof(T);
// Packed data type for comms
using PackedType = typename PackedOn16Bytes<T>::Type;
// The location in the destination array (load 8 fp16 or load 4 fp32 using LDG.128).
const size_t block_offset = bidx * params.elts_per_block + tidx * NUM_ELTS;
const size_t block_start = params.rank_offset + block_offset;
// The end of the segment computed by that block.
size_t max_offset =
min(block_start + params.elts_per_block, params.rank_offset + params.elts_per_rank);
multi_gpu_barrier(params.peer_barrier_ptrs_in, params.barrier_flag, params.local_rank,
RANKS_PER_NODE, tidx, bidx);
// The source pointers. Distributed round-robin for the different warps.
T* src_d[RANKS_PER_NODE];
// The destination ranks for round-robin gathering
size_t dst_rank[RANKS_PER_NODE];
#pragma unroll
for (int ii = 0; ii < RANKS_PER_NODE; ++ii) {
int rank = (params.local_rank + ii) % RANKS_PER_NODE;
src_d[ii] = reinterpret_cast<T*>(params.peer_comm_buffer_ptrs[rank]);
dst_rank[ii] = rank;
}
// Each block accumulates the values from the different GPUs on the same node.
for (size_t local_offset = block_start; local_offset < max_offset;
local_offset += blockDim.x * NUM_ELTS) {
// Iterate over the different ranks/devices on the node to load the values.
PackedType vals[RANKS_PER_NODE];
#pragma unroll
for (int ii = 0; ii < RANKS_PER_NODE; ++ii) {
vals[ii].packed = *reinterpret_cast<int4 const*>(&src_d[ii][local_offset]);
}
// Sum the values from the different ranks.
PackedType sums;
sums.packed = {0, 0, 0, 0};
#pragma unroll
for (int ii = 0; ii < RANKS_PER_NODE; ++ii) {
sums.packed = add128b(sums, vals[ii]);
}
// Store to the local buffer.
*reinterpret_cast<int4*>(&src_d[0][local_offset]) = sums.packed;
}
// sync threads to make sure all block threads have the sums
__syncthreads();
// barriers among the blocks with the same idx (release-acquire semantics)
if (tidx < RANKS_PER_NODE) {
// The all blocks notifies the other ranks.
uint32_t flag_block_offset = RANKS_PER_NODE + bidx * RANKS_PER_NODE;
st_flag_release(params.barrier_flag,
params.peer_barrier_ptrs_in[tidx] + flag_block_offset + params.local_rank);
// Busy-wait until all ranks are ready.
uint32_t rank_barrier = 0;
uint32_t* peer_barrier_d =
params.peer_barrier_ptrs_in[params.local_rank] + flag_block_offset + tidx;
do {
ld_flag_acquire(rank_barrier, peer_barrier_d);
} while (rank_barrier != params.barrier_flag);
}
// sync threads to make sure all other ranks has the final partial results
__syncthreads();
size_t max_block_offset = min(block_offset + params.elts_per_block, params.elts_per_rank);
// Gather all needed elts from other intra-node ranks
for (size_t local_offset = block_offset; local_offset < max_block_offset;
local_offset += blockDim.x * NUM_ELTS) {
#pragma unroll
for (int ii = 0; ii < RANKS_PER_NODE; ++ii) {
// use round-robin gathering from other ranks
size_t offset_rank = dst_rank[ii] * params.elts_per_rank + local_offset;
if (offset_rank >= params.elts_total) {
continue;
}
*reinterpret_cast<int4*>(&reinterpret_cast<T*>(params.local_output_buffer_ptr)[offset_rank]) =
*reinterpret_cast<int4*>(&src_d[ii][offset_rank]);
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
inline int divUp(int a, int b) { return (a + b - 1) / b; }
std::tuple<int, int> kernelLaunchConfig(AllReduceStrategyType algo, AllReduceParams& param,
size_t elts_per_thread) {
TVM_FFI_ICHECK(param.elts_total % elts_per_thread == 0);
int blocks_per_grid = 1, threads_per_block = DEFAULT_BLOCK_SIZE;
const size_t total_threads = param.elts_total / elts_per_thread;
switch (algo) {
case AllReduceStrategyType::ONESHOT: { // one stage all reduce algo
if (total_threads <= DEFAULT_BLOCK_SIZE) { // local reduce
threads_per_block = WARP_SIZE * divUp(total_threads, WARP_SIZE);
blocks_per_grid = 1;
} else { // local reduce
threads_per_block = DEFAULT_BLOCK_SIZE;
blocks_per_grid = divUp(total_threads, DEFAULT_BLOCK_SIZE);
blocks_per_grid = std::min(static_cast<int>(MAX_ALL_REDUCE_BLOCKS), blocks_per_grid);
}
param.elts_per_rank = param.elts_total;
param.elts_per_block =
elts_per_thread * divUp(param.elts_per_rank, elts_per_thread * blocks_per_grid);
break;
}
case AllReduceStrategyType::TWOSHOT: { // two stage all reduce algo
const size_t elts_per_rank = param.elts_total / param.ranks_per_node;
TVM_FFI_ICHECK(elts_per_rank % elts_per_thread == 0);
size_t total_threads = elts_per_rank / elts_per_thread;
total_threads = WARP_SIZE * ((total_threads + WARP_SIZE - 1) / WARP_SIZE);
TVM_FFI_ICHECK(total_threads % WARP_SIZE == 0);
while (total_threads % blocks_per_grid != 0 ||
total_threads / blocks_per_grid > DEFAULT_BLOCK_SIZE) {
blocks_per_grid += 1;
}
threads_per_block = total_threads / blocks_per_grid;
// NOTE: need to adjust here
if (static_cast<size_t>(blocks_per_grid) > MAX_ALL_REDUCE_BLOCKS) {
size_t iter_factor = 1;
while (blocks_per_grid / iter_factor > MAX_ALL_REDUCE_BLOCKS ||
blocks_per_grid % iter_factor) {
iter_factor += 1;
}
blocks_per_grid /= iter_factor;
}
param.elts_per_rank = param.elts_total / param.ranks_per_node;
param.elts_per_block = param.elts_per_rank / blocks_per_grid;
param.elts_per_block = elts_per_thread * divUp(param.elts_per_block, elts_per_thread);
param.rank_offset = param.rank * param.elts_per_rank;
break;
}
default:
LOG(FATAL) << ("Algorithm not supported here.");
}
return std::make_tuple(blocks_per_grid, threads_per_block);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename T, int RANKS_PER_NODE>
void dispatchARKernels(AllReduceStrategyType algo, AllReduceParams& param, int blocks_per_grid,
int threads_per_block, cudaStream_t stream) {
if (algo == AllReduceStrategyType::ONESHOT) {
oneShotAllReduceKernel<T, RANKS_PER_NODE>
<<<blocks_per_grid, threads_per_block, 0, stream>>>(param);
} else {
twoShotAllReduceKernel<T, RANKS_PER_NODE>
<<<blocks_per_grid, threads_per_block, 0, stream>>>(param);
}
}
template <typename T>
void invokeOneOrTwoShotAllReduceKernel(AllReduceParams& param, AllReduceStrategyType strat,
cudaStream_t stream) {
TVM_FFI_ICHECK(strat == AllReduceStrategyType::ONESHOT || strat == AllReduceStrategyType::TWOSHOT);
auto last_error = cudaGetLastError();
if (last_error != cudaSuccess) {
LOG(INFO) << "cuda error:" << cudaGetErrorString(last_error);
}
size_t elts_per_thread = 16 / sizeof(T);
auto [blocks_per_grid, threads_per_block] = kernelLaunchConfig(strat, param, elts_per_thread);
switch (param.ranks_per_node) {
case 2:
dispatchARKernels<T, 2>(strat, param, blocks_per_grid, threads_per_block, stream);
break;
case 4:
dispatchARKernels<T, 4>(strat, param, blocks_per_grid, threads_per_block, stream);
break;
case 6:
dispatchARKernels<T, 6>(strat, param, blocks_per_grid, threads_per_block, stream);
break;
case 8:
dispatchARKernels<T, 8>(strat, param, blocks_per_grid, threads_per_block, stream);
break;
default:
break;
}
last_error = cudaGetLastError();
if (last_error != cudaSuccess) {
LOG(INFO) << "cuda error:" << cudaGetErrorString(last_error);
}
}
void invokeMultiGpuBarrier(AllReduceParams& param, cudaStream_t stream) {
multiGpuBarrierKernel<<<1, param.ranks_per_node, 0, stream>>>(param);
}
void customAllReduce(AllReduceParams& params, void* data, size_t elts, DLDataType dataType,
AllReduceStrategyType strat, cudaStream_t stream) {
params.local_output_buffer_ptr = data;
params.elts_total = elts;
if (dataType.code == kDLFloat && dataType.bits == 32) {
invokeOneOrTwoShotAllReduceKernel<float>(params, strat, stream);
} else if (dataType.code == kDLFloat && dataType.bits == 16) {
invokeOneOrTwoShotAllReduceKernel<half>(params, strat, stream);
} else if (dataType.code == kDLBfloat && dataType.bits == 16) {
invokeOneOrTwoShotAllReduceKernel<__nv_bfloat16>(params, strat, stream);
} else {
LOG(FATAL) << ("Unsupported dataType for customAllReduce");
}
}
} // namespace tensorrt_llm
+81
View File
@@ -0,0 +1,81 @@
/*
* Copyright (c) 2022-2024, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <cuda_fp16.h>
#include <stdint.h>
namespace tensorrt_llm {
constexpr size_t WARP_SIZE = 32;
constexpr size_t MAX_ALL_REDUCE_BLOCKS = 24;
constexpr size_t MAX_RANKS_PER_NODE = 8;
constexpr size_t DEFAULT_BLOCK_SIZE = 1024;
enum class AllReduceStrategyType : int8_t {
RING = 0,
ONESHOT = 1,
TWOSHOT = 2,
AUTO = 3,
};
struct AllReduceParams {
size_t elts_total;
size_t elts_per_rank;
size_t elts_per_block;
size_t rank_offset;
size_t ranks_per_node, rank, local_rank;
uint32_t barrier_flag;
uint32_t* peer_barrier_ptrs_in[MAX_RANKS_PER_NODE];
uint32_t* peer_barrier_ptrs_out[MAX_RANKS_PER_NODE];
void* peer_comm_buffer_ptrs[MAX_RANKS_PER_NODE];
void* local_output_buffer_ptr;
};
inline size_t GetMaxRequiredWorkspaceSize(int world_size) {
if (world_size <= 2) {
return 16 * 1000 * 1000;
}
return 8 * 1000 * 1000;
}
inline AllReduceStrategyType SelectImplementation(size_t message_size, int world_size) {
const size_t maxWorkspaceSize = GetMaxRequiredWorkspaceSize(world_size);
if (message_size > maxWorkspaceSize) {
return AllReduceStrategyType::RING;
}
if (world_size <= 2) {
return AllReduceStrategyType::ONESHOT;
}
if (world_size <= 4) {
if (message_size < 1 * 1000 * 1000) {
return AllReduceStrategyType::ONESHOT;
}
return AllReduceStrategyType::TWOSHOT;
}
if (message_size < 500 * 1000) {
return AllReduceStrategyType::ONESHOT;
}
return AllReduceStrategyType::TWOSHOT;
}
void customAllReduce(AllReduceParams& params, void* data, size_t elts, DLDataType dataType,
AllReduceStrategyType strat, cudaStream_t stream);
} // namespace tensorrt_llm
+99
View File
@@ -0,0 +1,99 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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.
-->
# AGENTS.md
This file provides vendor-neutral guidance for agentic coding tools working
with Apache TVM.
## Repository Overview
Apache TVM is an open-source machine learning compiler stack. The repository
contains the C++ compiler/runtime, Python bindings, TIR/Relax IRs, scheduling
and lowering passes, target code generators, runtime integrations, tests,
documentation, and application examples.
## Repository Structure
- `include/tvm/` - public C++ headers
- `src/` - C++ implementation
- `python/tvm/` - Python package
- `tests/` - C++, Python, integration, and lint tests
- `cmake/` - CMake modules and default configuration
- `3rdparty/` - vendored dependencies and submodules
- `docs/` - documentation source
- `apps/` - application examples
- `.agents/skills/` - reusable agent workflows for this repository
## Build
Use an existing `build/` directory when present:
```bash
cmake --build build --parallel
```
For a fresh checkout, initialize submodules and configure CMake first:
```bash
git submodule update --init --recursive
mkdir -p build
cp cmake/config.cmake build/config.cmake
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo
cmake --build build --parallel
```
Development should use `PYTHONPATH`, not editable installs:
```bash
export PYTHONPATH="$(pwd)/python:$(pwd)/.local/python"
```
Do not use `pip install -e` for TVM or `tvm-ffi`; editable installs can make
one worktree silently import another worktree's code.
## Test And Lint
Run the smallest relevant test first, then broaden as needed. Common examples:
```bash
python -m pytest tests/python/all-platform-minimal-test/ -xvs
python -m pytest tests/python/tir-base/test_tir_base.py -xvs
./build/cpptest
```
For lint validation on a pull request, run pre-commit on the files changed by
the branch instead of the whole repository:
```bash
pre-commit run --files <changed-file>...
```
Use `python -m tirx_kernels.bench_suite` in the **tirx-kernels** repo
(`tirx_kernels/bench_suite/`) when that workflow applies.
## Coding Conventions
- Follow the surrounding style before introducing new abstractions.
- Keep changes scoped to the task and avoid unrelated cleanups.
- Prefer explicit tests that show the IR or behavior being changed.
- Use Apache TVM commit tags such as `[REFACTOR][IR]`, `[FIX][TIR]`, or
`[DOCS]` as appropriate.
- Preserve Apache license headers in new source, script, and documentation
files when the surrounding tree uses them.
+958
View File
@@ -0,0 +1,958 @@
cmake_minimum_required(VERSION 3.18)
project(tvm C CXX)
# --- TVM version (no dependency on version.py) ---
# When built via scikit-build-core the version is resolved by setuptools_scm and
# passed in as SKBUILD_PROJECT_VERSION_FULL; bake it into the C++ TVM_VERSION macro.
# A bare `cmake` build (no scikit-build-core) leaves the checked-in default in
# include/tvm/runtime/base.h untouched. An explicit -DTVM_VERSION always wins.
if(NOT DEFINED TVM_VERSION AND DEFINED SKBUILD_PROJECT_VERSION_FULL)
set(TVM_VERSION "${SKBUILD_PROJECT_VERSION_FULL}")
endif()
if(DEFINED TVM_VERSION)
message(STATUS "TVM_VERSION=${TVM_VERSION}")
add_compile_definitions(TVM_VERSION="${TVM_VERSION}")
endif()
# Utility functions
include(cmake/utils/Utils.cmake)
include(cmake/utils/Summary.cmake)
include(cmake/utils/Linker.cmake)
include(cmake/utils/Library.cmake)
include(cmake/utils/FindCUDA.cmake)
include(cmake/utils/FindNCCL.cmake)
include(cmake/utils/FindOpenCL.cmake)
include(cmake/utils/FindVulkan.cmake)
include(cmake/utils/FindLLVM.cmake)
include(cmake/utils/FindROCM.cmake)
include(cmake/utils/FindRCCL.cmake)
include(cmake/utils/FindNVSHMEM.cmake)
if(EXISTS ${CMAKE_BINARY_DIR}/config.cmake)
include(${CMAKE_BINARY_DIR}/config.cmake)
else()
if(EXISTS ${CMAKE_SOURCE_DIR}/config.cmake)
include(${CMAKE_SOURCE_DIR}/config.cmake)
endif()
endif()
# NOTE: do not modify this file to change option values.
# You can create a config.cmake at build folder
# and add set(OPTION VALUE) to override these build options.
# Alernatively, use cmake -DOPTION=VALUE through command-line.
tvm_option(USE_CUDA "Build with CUDA" OFF)
tvm_option(USE_NCCL "Build with NCCL" OFF)
tvm_option(USE_OPENCL "Build with OpenCL" OFF)
tvm_option(USE_OPENCL_ENABLE_HOST_PTR "Enable OpenCL memory object access to host" OFF)
tvm_option(USE_OPENCL_GTEST "Path to OpenCL specific gtest version for runtime cpp tests." /path/to/opencl/gtest)
tvm_option(USE_VULKAN "Build with Vulkan" OFF)
# Whether to use spirv-tools and SPIRV-Headers from Khronos GitHub or GitLab.
#
# Possible values:
# - OFF: not to use
# - /path/to/install: path to your khronis spirv-tools and SPIRV-Headers installation directory
#
tvm_option(USE_KHRONOS_SPIRV "Whether to use spirv-tools and SPIRV-Headers from Khronos GitHub or GitLab" OFF)
tvm_option(USE_SPIRV_KHR_INTEGER_DOT_PRODUCT "whether enable SPIRV_KHR_DOT_PRODUCT" OFF)
tvm_option(USE_METAL "Build with Metal" OFF)
tvm_option(USE_ROCM "Build with ROCM" OFF)
tvm_option(USE_RCCL "Build with RCCL" OFF)
tvm_option(ROCM_PATH "The path to rocm" /opt/rocm)
tvm_option(USE_HEXAGON "Build with Hexagon support" OFF)
tvm_option(USE_HEXAGON_SDK "Path to the Hexagon SDK root (required for Hexagon support)" /path/to/sdk)
tvm_option(USE_HEXAGON_RPC "Enable Hexagon RPC using minRPC implementation over Android." OFF)
tvm_option(USE_HEXAGON_GTEST "Path to Hexagon specific gtest version for runtime cpp tests." /path/to/hexagon/gtest)
tvm_option(USE_HEXAGON_EXTERNAL_LIBS "Path to git repo containing external Hexagon runtime sources or libraries" OFF)
tvm_option(USE_RPC "Build with RPC" ON)
tvm_option(USE_THREADS "Build with thread support" ON)
tvm_option(USE_LLVM "Build with LLVM, can be set to specific llvm-config path" OFF)
tvm_option(USE_MLIR "Build with MLIR support" OFF)
tvm_option(USE_OPENMP "Build with OpenMP thread pool implementation" OFF)
tvm_option(TVM_DEBUG_WITH_ABI_CHANGE "Enable debug code that may cause ABI changes" OFF)
tvm_option(TVM_LOG_BEFORE_THROW "Whether log before throw, for debugging purposes" OFF)
tvm_option(USE_RTTI "Build with RTTI" ON)
tvm_option(USE_MSVC_MT "Build with MT" OFF)
tvm_option(INSTALL_DEV "Install compiler infrastructure" OFF)
tvm_option(HIDE_PRIVATE_SYMBOLS "Compile with -fvisibility=hidden." ON)
tvm_option(INDEX_DEFAULT_I64 "Defaults the index datatype to int64" ON)
tvm_option(BUILD_STATIC_RUNTIME "Build static version of libtvm_runtime" OFF)
tvm_option(USE_GTEST "Use GoogleTest for C++ sanity tests" AUTO)
tvm_option(USE_CUSTOM_LOGGING "Use user-defined custom logging, tvm::runtime::detail::LogFatalImpl and tvm::runtime::detail::LogMessageImpl must be implemented" OFF)
tvm_option(USE_ALTERNATIVE_LINKER "Use 'mold' or 'lld' if found when invoking compiler to link artifact" AUTO)
tvm_option(USE_CCACHE "Use ccache if found when invoking compiler" AUTO)
# 3rdparty libraries
tvm_option(COMPILER_RT_PATH "Path to COMPILER-RT" "3rdparty/compiler-rt")
# Contrib library options
tvm_option(USE_BLAS "The blas library to be linked" none)
tvm_option(USE_AMX "Enable Intel AMX" OFF)
tvm_option(USE_Z3 "Build with Z3 SMT solver support" OFF)
tvm_option(USE_MKL "MKL root path when use MKL blas" OFF)
tvm_option(USE_DNNL "Enable DNNL codegen" OFF)
tvm_option(USE_CUDNN "Build with cuDNN" OFF)
tvm_option(USE_CUBLAS "Build with cuBLAS" OFF)
tvm_option(USE_NVTX "Build with NVTX" OFF)
tvm_option(USE_CUTLASS "Build with CUTLASS" OFF)
tvm_option(USE_THRUST "Build with Thrust" OFF)
tvm_option(USE_CURAND "Build with cuRAND" OFF)
tvm_option(USE_HIPBLAS "Build with ROCM:HIPBLAS" OFF)
tvm_option(USE_SORT "Build with sort support" ON)
tvm_option(USE_RANDOM "Build with random support" ON)
tvm_option(USE_CPP_RPC "Build CPP RPC" OFF)
tvm_option(USE_IOS_RPC "Build iOS RPC" OFF)
tvm_option(USE_COREML "Build with coreml support" OFF)
tvm_option(USE_TENSORRT_CODEGEN "Build with TensorRT Codegen support" OFF)
tvm_option(USE_TENSORRT_RUNTIME "Build with TensorRT runtime" OFF)
tvm_option(USE_NNAPI_CODEGEN "Build with NNAPI Codegen support" OFF)
tvm_option(USE_NNAPI_RUNTIME "Build with NNAPI runtime" OFF)
tvm_option(USE_EXAMPLE_NPU_CODEGEN "Build with Example NPU Codegen support" OFF)
tvm_option(USE_EXAMPLE_NPU_RUNTIME "Build with Example NPU runtime" OFF)
tvm_option(SUMMARIZE "Print CMake option summary after configuring" OFF)
tvm_option(USE_CLML "Build with CLML Codegen support" OFF)
tvm_option(USE_CLML_GRAPH_EXECUTOR "Build with CLML graph runtime" OFF)
tvm_option(USE_NVSHMEM "Build with NVSHMEM support" OFF)
# Python package options
tvm_option(TVM_BUILD_PYTHON_MODULE "Build Python module with scikit-build-core" OFF)
# include directories
include_directories(${CMAKE_INCLUDE_PATH})
include_directories("include")
include_directories(SYSTEM ${COMPILER_RT_PATH})
# initial variables
set(TVM_LINKER_LIBS "")
set(TVM_RUNTIME_LINKER_LIBS "")
set(TVM_RUNTIME_BACKEND_LIBS "")
# Early target creation so contrib cmake files can call
# target_link_libraries(tvm_runtime_extra PRIVATE <object_lib>) directly.
add_library(tvm_runtime_extra SHARED)
list(APPEND TVM_RUNTIME_BACKEND_LIBS tvm_runtime_extra)
set_target_properties(tvm_runtime_extra PROPERTIES LINKER_LANGUAGE CXX)
# INTERFACE target carrying compile definitions for OBJECT libs that build
# into tvm_runtime_extra. On MSVC, TVM_RUNTIME_EXPORTS makes TVM_RUNTIME_DLL
# expand to __declspec(dllexport) so that functions defined in extra modules
# are properly exported from tvm_runtime_extra.dll.
add_library(tvm_runtime_extra_defs INTERFACE)
target_link_libraries(tvm_runtime_extra_defs INTERFACE tvm_ffi_header)
target_compile_definitions(tvm_runtime_extra_defs
INTERFACE TVM_RUNTIME_EXPORTS TVM_FFI_EXPORTS)
# Check if this is being run on its own or as a subdirectory for another project
# If we update to CMake 2.21+, we can use PROJECT_IS_TOP_LEVEL instead
get_directory_property(IS_SUBPROJECT PARENT_DIRECTORY)
if(NOT IS_SUBPROJECT AND NOT DEFINED "${CMAKE_EXPORT_COMPILE_COMMANDS}")
# If not set manually, change the default to ON
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
endif()
if(TVM_LOG_BEFORE_THROW)
# log error before throw as
# when system have issues with stack trace
add_definitions(-DTVM_LOG_BEFORE_THROW=1)
endif()
# Generic compilation options
if(MSVC)
add_definitions(-DWIN32_LEAN_AND_MEAN)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
add_definitions(-D_SCL_SECURE_NO_WARNINGS)
add_definitions(-D_ENABLE_EXTENDED_ALIGNED_STORAGE)
add_definitions(-DNOMINMAX)
# regeneration does not work well with msbuild custom rules.
set(CMAKE_SUPPRESS_REGENERATION ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
add_compile_options(/bigobj)
# Use standard-conforming two-phase name resolution for templates.
# This minimizes the differences between g++/clang builds on Linux,
# and MSVC builds on Windows.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /permissive-")
# MSVC already errors on undefined symbols, no additional flag needed.
set(TVM_NO_UNDEFINED_SYMBOLS "")
if(USE_MSVC_MT)
foreach(flag_var
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
if(${flag_var} MATCHES "/MD")
string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
endif(${flag_var} MATCHES "/MD")
endforeach(flag_var)
# Static linking. CMake behavior changed in 3.15 making this necessary.
add_compile_options(/MT)
endif()
# Disable common MSVC warnings
# Integer conversion warnings(e.g. int64 to int)
add_compile_options(/wd4244)
add_compile_options(/wd4267)
# Signed unsigned constant comparison
add_compile_options(/wd4018)
# Aligned alloc may not met(need c++17)
add_compile_options(/wd4316)
# unreferenced local variables(usually in exception catch)
add_compile_options(/wd4101)
# always inline keyword not necessary
add_compile_options(/wd4180)
# DLL interface warning in c++
add_compile_options(/wd4251)
# destructor was implicitly defined as deleted
add_compile_options(/wd4624)
# unary minus operator applied to unsigned type, result still unsigned
add_compile_options(/wd4146)
# 'inline': used more than once
add_compile_options(/wd4141)
# unknown pragma
add_compile_options(/wd4068)
else(MSVC)
set(WARNING_FLAG -Wall)
if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
message(STATUS "Build in Debug mode")
set(CMAKE_C_FLAGS "-O0 -g ${WARNING_FLAG} -fPIC ${CMAKE_C_FLAGS}")
set(CMAKE_CXX_FLAGS "-O0 -g ${WARNING_FLAG} -fPIC ${CMAKE_CXX_FLAGS}")
set(CMAKE_CUDA_FLAGS "-O0 -g -Xcompiler=-Wall -Xcompiler=-fPIC ${CMAKE_CUDA_FLAGS}")
else()
set(CMAKE_C_FLAGS "-O2 ${WARNING_FLAG} -fPIC ${CMAKE_C_FLAGS}")
set(CMAKE_CXX_FLAGS "-O2 ${WARNING_FLAG} -fPIC ${CMAKE_CXX_FLAGS}")
set(CMAKE_CUDA_FLAGS "-O2 -Xcompiler=-Wall -Xcompiler=-fPIC ${CMAKE_CUDA_FLAGS}")
set(TVM_VISIBILITY_FLAG "")
if (HIDE_PRIVATE_SYMBOLS)
message(STATUS "Hide private symbols...")
set(TVM_VISIBILITY_FLAG "-fvisibility=hidden")
endif(HIDE_PRIVATE_SYMBOLS)
endif ()
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang" AND
CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0)
set(CMAKE_CXX_FLAGS "-faligned-new ${CMAKE_CXX_FLAGS}")
endif()
# ld option to warn if symbols are undefined (e.g. libtvm_runtime.so
# using symbols only present in libtvm.so). Not needed for MSVC,
# since this is already the default there.
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin" OR ${CMAKE_SYSTEM_NAME} MATCHES "iOS")
set(TVM_NO_UNDEFINED_SYMBOLS "-Wl,-undefined,error")
else()
set(TVM_NO_UNDEFINED_SYMBOLS "-Wl,--no-undefined")
endif()
message(STATUS "Forbidding undefined symbols in shared library, using ${TVM_NO_UNDEFINED_SYMBOLS} on platform ${CMAKE_SYSTEM_NAME}")
# Detect if we're compiling for Hexagon.
set(TEST_FOR_HEXAGON_CXX
"#ifndef __hexagon__"
"#error"
"#endif"
"int main() {}"
# Define _start_main to avoid linking errors with -fPIC.
"extern \"C\" void _start_main() {}")
set(TEST_FOR_HEXAGON_DIR
"${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp")
set(TEST_FOR_HEXAGON_FILE "${TEST_FOR_HEXAGON_DIR}/test_for_hexagon.cc")
string(REPLACE ";" "\n" TEST_FOR_HEXAGON_CXX_TEXT "${TEST_FOR_HEXAGON_CXX}")
file(WRITE "${TEST_FOR_HEXAGON_FILE}" "${TEST_FOR_HEXAGON_CXX_TEXT}")
try_compile(BUILD_FOR_HEXAGON "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}"
"${TEST_FOR_HEXAGON_FILE}")
file(REMOVE "${TEST_FOR_HEXAGON_FILE}")
if(BUILD_FOR_HEXAGON)
message(STATUS "Building for Hexagon")
endif()
# Detect if we're compiling for Android.
set(TEST_FOR_ANDROID_CXX
"#ifndef __ANDROID__"
"#error"
"#endif"
"int main() {}")
set(TEST_FOR_ANDROID_DIR
"${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp")
set(TEST_FOR_ANDROID_FILE "${TEST_FOR_ANDROID_DIR}/test_for_android.cc")
string(REPLACE ";" "\n" TEST_FOR_ANDROID_CXX_TEXT "${TEST_FOR_ANDROID_CXX}")
file(WRITE "${TEST_FOR_ANDROID_FILE}" "${TEST_FOR_ANDROID_CXX_TEXT}")
try_compile(BUILD_FOR_ANDROID "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}"
"${TEST_FOR_ANDROID_FILE}")
file(REMOVE "${TEST_FOR_ANDROID_FILE}")
if(BUILD_FOR_ANDROID)
message(STATUS "Building for Android")
endif()
endif(MSVC)
# Hexagon has dlopen built into QuRT (no need for static library).
if(NOT BUILD_FOR_HEXAGON)
list(APPEND TVM_RUNTIME_LINKER_LIBS ${CMAKE_DL_LIBS})
endif()
# add source group
tvm_file_glob(GLOB_RECURSE GROUP_SOURCE "src/*.cc")
tvm_file_glob(GLOB_RECURSE GROUP_INCLUDE "src/*.h" "include/*.h")
assign_source_group("Source" ${GROUP_SOURCE})
assign_source_group("Include" ${GROUP_INCLUDE})
# Source file lists
tvm_file_glob(GLOB_RECURSE COMPILER_SRCS
src/ir/*.cc
src/arith/*.cc
src/te/*.cc
src/tirx/*.cc
src/s_tir/*.cc
src/topi/*.cc
src/driver/*.cc
src/support/*.cc
# TVMScript shared core (Doc IR + dispatch infrastructure + IR-layer
# printer/builder). Per-dialect (relax, tirx, ...) printer/builder pieces
# live under src/<dialect>/script/. The list below is intentionally explicit
# (not src/script/*.cc) so that any new file accidentally added under
# src/script/{printer,ir_builder}/<dialect>/ for a non-IR dialect is
# rejected at link time rather than silently included.
src/script/ir_builder/base.cc
src/script/ir_builder/ir/*.cc
src/script/printer/config.cc
src/script/printer/script_printer.cc
src/script/printer/doc.cc
src/script/printer/doc_printer/*.cc
src/script/printer/ir_docsifier.cc
src/script/printer/ir/*.cc
src/relax/ir/*.cc
src/relax/op/*.cc
src/relax/analysis/*.cc
src/relax/transform/*.cc
src/relax/backend/vm/*.cc
src/relax/backend/adreno/*.cc
src/relax/backend/task_extraction.cc
src/relax/backend/pattern_registry.cc
src/relax/utils.cc
src/relax/distributed/*.cc
src/relax/distributed/transform/*.cc
src/relax/op/distributed/*.cc
src/relax/script/*.cc
src/relax/testing/*.cc
)
tvm_file_glob(GLOB CODEGEN_SRCS
src/target/*.cc
src/target/source/*.cc
src/target/canonicalizer/*.cc
src/target/canonicalizer/llvm/*.cc
src/backend/cuda/codegen/*.cc
src/backend/cuda/op/*.cc
src/backend/hexagon/codegen/*.cc
src/backend/metal/codegen/*.cc
src/backend/metal/op/*.cc
src/backend/opencl/codegen/*.cc
src/backend/rocm/codegen/*.cc
src/backend/trn/codegen/*.cc
src/backend/trn/op/*.cc
src/backend/trn/transform/*.cc
src/backend/vulkan/codegen/target_kind.cc
src/backend/vulkan/codegen/vulkan_fallback_module.cc
src/backend/webgpu/codegen/*.cc
)
list(APPEND COMPILER_SRCS ${CODEGEN_SRCS})
tvm_file_glob(GLOB RUNTIME_SRCS
src/runtime/*.cc
src/runtime/vm/*.cc
src/runtime/memory/*.cc
src/runtime/rpc/minrpc/*.cc
)
# Note: src/runtime/extra/disco/** moves to libtvm_runtime_extra.
# Note: src/backend/*/runtime sources move to per-backend DSOs.
set(TVM_RUNTIME_EXT_OBJS "")
if(BUILD_FOR_HEXAGON)
if(NOT BUILD_STATIC_RUNTIME)
# Allow undefined symbols (there will be some from libc).
set(TVM_NO_UNDEFINED_SYMBOLS "")
endif()
add_definitions(-D_MACH_I32=int)
endif()
# Package runtime rules
if(NOT USE_RTTI)
endif()
if(INDEX_DEFAULT_I64)
add_definitions(-DTVM_INDEX_DEFAULT_I64=1)
endif()
if(USE_RPC)
message(STATUS "Build with RPC support...")
tvm_file_glob(GLOB RUNTIME_RPC_SRCS src/runtime/rpc/*.cc)
list(APPEND RUNTIME_SRCS ${RUNTIME_RPC_SRCS})
endif(USE_RPC)
# Note: disco/**, NCCL, NVSHMEM, RCCL all move to libtvm_runtime_extra
# (assembled inline below after all contrib cmake files).
# Enable ctest if gtest is available
if(USE_GTEST)
# Check env var for backward compatibility. A better way to specify package
# locations is to use CMAKE_PREFIX_PATH or other standard CMake mechanism
# (see CMake documentation for `find_package`).
set(GTEST_ROOT "$ENV{GTEST_LIB}")
if("${USE_GTEST}" STREQUAL "AUTO")
# If USE_GTEST is AUTO, treat GTest as optional: enable if found.
find_package(GTest)
elseif("${USE_GTEST}" MATCHES ${IS_TRUE_PATTERN})
# USE_GTEST is set to ON, TRUE, etc. Treat GTest as a required package.
find_package(GTest REQUIRED)
endif()
if(GTEST_FOUND)
if(NOT TARGET GTest::gmock)
# GMock is formally supported in CMake 3.20; for now, expect libgmock.a in the same directory,
# and require that folks compiling against GTest::gmock also link against GTest::GTest
# (for the includes dir).
add_library(GTest::gmock STATIC IMPORTED GLOBAL)
get_target_property(GTEST_LIB_PATH GTest::GTest IMPORTED_LOCATION)
if("${GTEST_LIB_PATH}" STREQUAL "GTEST_LIB_PATH-NOTFOUND")
# CMake >= 3.20 makes GTest::GTest into a compatibility target. The real import location is in
# GTest::gtest.
get_target_property(GTEST_LIB_PATH GTest::gtest IMPORTED_LOCATION)
if("${GTEST_LIB_PATH}" STREQUAL "GTEST_LIB_PATH-NOTFOUND")
message(FATAL_ERROR "Neither GTest::GTest nor GTest::gtest targets defined IMPORTED_LOCATION")
endif()
endif()
get_filename_component(GTEST_LIB_DIR "${GTEST_LIB_PATH}" DIRECTORY)
set_target_properties(GTest::gmock PROPERTIES
IMPORTED_LOCATION "${GTEST_LIB_DIR}/libgmock.a")
endif()
enable_testing()
include(CTest)
endif()
endif()
if(USE_KALLOC_ALIGNMENT)
message(STATUS "Build Alloc alignment set to ${USE_KALLOC_ALIGNMENT}")
add_definitions(-DTVM_KALLOC_ALIGNMENT=${USE_KALLOC_ALIGNMENT})
endif(USE_KALLOC_ALIGNMENT)
# Caches the build.
# Note that ccache-3.x doesn't support nvcc well, so CUDA kernels may never hit the cache and still
# need to be re-compiled every time. Using ccache 4.0+ can resolve this issue.
include(cmake/utils/CCache.cmake)
include(CheckCXXCompilerFlag)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CUDA_STANDARD_REQUIRED ON)
set(CMAKE_CUDA_STANDARD 20)
# Module rules
include(cmake/modules/CUDA.cmake)
include(cmake/modules/Hexagon.cmake) # This must come before logging.cmake
include(cmake/modules/contrib/CLML.cmake) # Must be before OpenCL.cmake
include(cmake/modules/OpenCL.cmake)
include(cmake/modules/OpenMP.cmake)
include(cmake/modules/Vulkan.cmake)
include(cmake/modules/Metal.cmake)
include(cmake/modules/ROCM.cmake)
include(cmake/modules/LLVM.cmake)
include(cmake/modules/contrib/BLAS.cmake)
include(cmake/modules/contrib/DNNL.cmake)
include(cmake/modules/contrib/AMX.cmake)
include(cmake/modules/contrib/CUTLASS.cmake)
include(cmake/modules/contrib/Random.cmake)
include(cmake/modules/contrib/Sort.cmake)
include(cmake/modules/contrib/Z3.cmake)
include(cmake/modules/contrib/CoreML.cmake)
include(cmake/modules/contrib/TensorRT.cmake)
include(cmake/modules/contrib/NNAPI.cmake)
include(cmake/modules/contrib/ExampleNPU.cmake)
include(cmake/modules/contrib/vllm.cmake)
include(cmake/modules/Git.cmake)
# ---- libtvm_runtime_extra assembly ----
# Disco core sources.
tvm_file_glob(GLOB _disco_core_srcs src/runtime/extra/disco/*.cc)
add_library(tvm_disco_objs OBJECT ${_disco_core_srcs})
target_link_libraries(tvm_disco_objs PRIVATE tvm_runtime_extra_defs)
target_link_libraries(tvm_runtime_extra PRIVATE tvm_disco_objs)
# Distributed disco (disabled for Hexagon cross-compile).
if(NOT BUILD_FOR_HEXAGON)
tvm_file_glob(GLOB _disco_dist_srcs src/runtime/extra/disco/distributed/*.cc)
add_library(tvm_disco_distributed_objs OBJECT ${_disco_dist_srcs})
target_link_libraries(tvm_disco_distributed_objs PRIVATE tvm_runtime_extra_defs)
target_link_libraries(tvm_runtime_extra PRIVATE tvm_disco_distributed_objs)
endif()
# NCCL / cuda_ipc — requires CUDA + NCCL.
if(USE_CUDA AND USE_NCCL)
find_nccl(${USE_NCCL})
include_directories(SYSTEM ${NCCL_INCLUDE_DIR})
tvm_file_glob(GLOB _nccl_srcs src/runtime/extra/disco/nccl/*.cc src/runtime/extra/disco/cuda_ipc/*.cc 3rdparty/tensorrt_llm/*.cu)
set_source_files_properties(src/runtime/extra/disco/nccl/nccl.cc PROPERTIES COMPILE_DEFINITIONS "TVM_NCCL_RCCL_SWITCH=0")
add_library(tvm_nccl_objs OBJECT ${_nccl_srcs})
target_link_libraries(tvm_nccl_objs PRIVATE tvm_runtime_extra_defs)
find_library(LIBRT rt)
target_link_libraries(tvm_runtime_extra PRIVATE tvm_nccl_objs nccl ${LIBRT})
endif()
# NVSHMEM.
if(USE_CUDA AND USE_NVSHMEM)
find_nvshmem(${USE_NVSHMEM})
if(NOT NVSHMEM_FOUND)
message(FATAL_ERROR "Cannot find NVSHMEM, USE_NVSHMEM=" ${USE_NVSHMEM})
endif()
set(CMAKE_CUDA_SEPARABLE_COMPILATION ON)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
tvm_file_glob(GLOB _nvshmem_srcs src/runtime/extra/contrib/nvshmem/*.cc src/runtime/extra/contrib/nvshmem/*.cu)
add_library(tvm_nvshmem_objs OBJECT ${_nvshmem_srcs})
target_link_libraries(tvm_nvshmem_objs PRIVATE tvm_runtime_extra_defs)
target_include_directories(tvm_nvshmem_objs PUBLIC ${NVSHMEM_INCLUDE_DIR})
find_library(NVSHMEM_HOST nvshmem_host ${NVSHMEM_LIB_DIR})
find_library(NVSHMEM_DEVICE nvshmem_device ${NVSHMEM_LIB_DIR})
target_link_libraries(tvm_runtime_extra PRIVATE tvm_nvshmem_objs ${NVSHMEM_HOST} ${NVSHMEM_DEVICE})
set_target_properties(tvm_runtime_extra PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
endif()
# RCCL.
if(USE_ROCM AND USE_RCCL)
find_rccl(${USE_RCCL})
include_directories(SYSTEM ${RCCL_INCLUDE_DIR})
tvm_file_glob(GLOB _rccl_srcs src/runtime/extra/disco/nccl/*.cc)
set_source_files_properties(src/runtime/extra/disco/nccl/nccl.cc PROPERTIES COMPILE_DEFINITIONS "TVM_NCCL_RCCL_SWITCH=1")
add_library(tvm_rccl_objs OBJECT ${_rccl_srcs})
target_link_libraries(tvm_rccl_objs PRIVATE tvm_runtime_extra_defs)
target_link_libraries(tvm_runtime_extra PRIVATE tvm_rccl_objs rccl)
endif()
target_link_libraries(tvm_runtime_extra PUBLIC tvm_runtime)
# If disco/cuda_ipc is included, link the CUDA DSO.
if(USE_CUDA)
target_link_libraries(tvm_runtime_extra PUBLIC tvm_runtime_cuda)
endif()
# CUTLASS fpA_intB_gemm and flash_attn are separate shared libs.
if(USE_CUDA AND USE_CUTLASS)
target_link_libraries(tvm_runtime_extra PRIVATE fpA_intB_gemm fpA_intB_gemm_tvm)
target_link_libraries(tvm_runtime_extra PRIVATE -Wl,--no-as-needed flash_attn)
endif()
if(TVM_VISIBILITY_FLAG)
set_property(TARGET tvm_runtime_extra APPEND PROPERTY LINK_OPTIONS "${TVM_VISIBILITY_FLAG}")
endif()
tvm_configure_target_library(tvm_runtime_extra RUNTIME_MODULE)
add_library(tvm_objs OBJECT ${COMPILER_SRCS})
add_library(tvm_runtime_objs OBJECT ${RUNTIME_SRCS})
target_link_libraries(tvm_objs PUBLIC tvm_ffi_header)
target_link_libraries(tvm_runtime_objs PUBLIC tvm_ffi_header)
if(TARGET tvm_llvm_header)
target_link_libraries(tvm_objs PUBLIC tvm_llvm_header)
endif()
include(GNUInstallDirs)
# Runtime library: built from runtime sources only. Loaded with RTLD_GLOBAL on
# Linux/macOS so its symbols are exposed to downstream loads (e.g. NVRTC kernels
# and other dynamically loaded modules that resolve runtime symbols at link
# time).
if(BUILD_STATIC_RUNTIME)
add_library(tvm_runtime STATIC
$<TARGET_OBJECTS:tvm_runtime_objs>
${TVM_RUNTIME_EXT_OBJS}
)
set(NOTICE_MULTILINE
"You have build static version of the TVM runtime library. Make "
"sure to use --whole-archive when linking it into your project.")
string(CONCAT NOTICE ${NOTICE_MULTILINE})
add_custom_command(TARGET tvm_runtime POST_BUILD
COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --yellow --bold ${NOTICE})
target_link_libraries(tvm_runtime PUBLIC tvm_ffi_static)
else()
add_library(tvm_runtime SHARED
$<TARGET_OBJECTS:tvm_runtime_objs>
${TVM_RUNTIME_EXT_OBJS}
)
set_property(TARGET tvm_runtime APPEND PROPERTY LINK_OPTIONS "${TVM_NO_UNDEFINED_SYMBOLS}")
target_link_libraries(tvm_runtime PUBLIC tvm_ffi_shared)
endif()
target_include_directories(tvm_runtime PUBLIC "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
set_property(TARGET tvm_runtime APPEND PROPERTY LINK_OPTIONS "${TVM_VISIBILITY_FLAG}")
# ``-fvisibility=hidden`` only affects symbol export when applied at COMPILE
# time. Setting it as a link option (above) is preserved for back-compat but
# is effectively a no-op for visibility — the per-TU compile flag is what
# actually hides symbols. Apply it to the OBJECT libs that feed both shared
# libraries.
if(TVM_VISIBILITY_FLAG)
target_compile_options(tvm_runtime_objs PRIVATE "${TVM_VISIBILITY_FLAG}")
target_compile_options(tvm_objs PRIVATE "${TVM_VISIBILITY_FLAG}")
endif()
# Compiler library: built from compiler-only sources.
# Links against tvm_runtime (and tvm_ffi_shared transitively). Loaded with
# RTLD_LOCAL so compiler-internal symbols don't leak into the global namespace.
add_library(tvm_compiler SHARED
$<TARGET_OBJECTS:tvm_objs>
)
target_link_libraries(tvm_compiler PUBLIC tvm_runtime tvm_ffi_shared)
target_include_directories(tvm_compiler PUBLIC "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
set_property(TARGET tvm_compiler APPEND PROPERTY LINK_OPTIONS "${TVM_NO_UNDEFINED_SYMBOLS}")
set_property(TARGET tvm_compiler APPEND PROPERTY LINK_OPTIONS "${TVM_VISIBILITY_FLAG}")
# Work around a GNU ld (binutils) relaxation bug that miscompiles
# R_X86_64_GOTPCRELX relocations inside very large statically-linked archives.
# When the full LLVM static libraries are linked into libtvm_compiler.so, the
# library is large enough that ld can relax an indirect GOT call (LLVM built
# with -fno-plt emits these) into a direct call with an incorrect displacement.
# The call then targets read-only data instead of the intended function and
# crashes at runtime with a SIGSEGV inside llvm::X86Subtarget during code
# generation. Disabling linker relaxation keeps the GOT-indirect sequences and
# avoids the miscompilation; it is harmless when LLVM is linked dynamically.
# See binutils bug ld/25754.
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND NOT "${USE_LLVM}" MATCHES ${IS_FALSE_PATTERN})
set_property(TARGET tvm_compiler APPEND PROPERTY LINK_OPTIONS "-Wl,--no-relax")
# LLVM's --system-libs reports -lxml2, but TVM calls no libxml2 symbols. The
# manylinux toolchain does not default to --as-needed, so set it explicitly:
# the unused libxml2 is then not recorded as a NEEDED dependency, so auditwheel
# does not vendor it (and its deps) into the wheel.
set_property(TARGET tvm_compiler APPEND PROPERTY LINK_OPTIONS "-Wl,--as-needed")
endif()
# Place runtime/compiler/allvisible artifacts under build/lib/ to mirror the
# tvm-ffi layout and make tvm_ffi.libinfo.load_lib_ctypes(package="tvm") able
# to discover them in dev / editable builds.
set_target_properties(tvm_runtime PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
)
set_target_properties(tvm_compiler PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
)
if(MSVC)
foreach(config_type Release RELEASE Debug DEBUG RelWithDebInfo RELWITHDEBINFO MinSizeRel MINSIZEREL)
set_target_properties(tvm_runtime PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY_${config_type} "${CMAKE_BINARY_DIR}/lib"
LIBRARY_OUTPUT_DIRECTORY_${config_type} "${CMAKE_BINARY_DIR}/lib"
RUNTIME_OUTPUT_DIRECTORY_${config_type} "${CMAKE_BINARY_DIR}/lib"
)
set_target_properties(tvm_compiler PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY_${config_type} "${CMAKE_BINARY_DIR}/lib"
LIBRARY_OUTPUT_DIRECTORY_${config_type} "${CMAKE_BINARY_DIR}/lib"
RUNTIME_OUTPUT_DIRECTORY_${config_type} "${CMAKE_BINARY_DIR}/lib"
)
endforeach()
endif()
# logging option for libbacktrace
include(cmake/modules/Logging.cmake)
if(USE_CPP_RPC)
add_subdirectory("apps/cpp_rpc")
endif()
if(USE_CPP_RTVM)
add_subdirectory("apps/cpp_rtvm")
endif()
if(USE_IOS_RPC)
add_subdirectory("apps/ios_rpc")
endif()
add_subdirectory(3rdparty/tvm-ffi)
if(TVM_DEBUG_WITH_ABI_CHANGE)
message(STATUS "Building with debug code that may cause ABI changes...")
target_compile_definitions(tvm_objs PRIVATE "TVM_DEBUG_WITH_ABI_CHANGE")
target_compile_definitions(tvm_runtime_objs PRIVATE "TVM_DEBUG_WITH_ABI_CHANGE")
endif(TVM_DEBUG_WITH_ABI_CHANGE)
if(USE_THREADS AND NOT BUILD_FOR_HEXAGON)
message(STATUS "Build with thread support...")
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
find_package(Threads REQUIRED)
target_link_libraries(tvm_compiler PUBLIC Threads::Threads)
target_link_libraries(tvm_runtime PUBLIC Threads::Threads)
endif()
target_link_libraries(tvm_compiler PRIVATE ${TVM_LINKER_LIBS})
target_link_libraries(tvm_compiler PRIVATE ${TVM_RUNTIME_LINKER_LIBS})
target_link_libraries(tvm_runtime PRIVATE ${TVM_RUNTIME_LINKER_LIBS})
# Set flags for clang
include(cmake/modules/ClangFlags.cmake)
set(TVM_TEST_LIBRARY_NAME tvm_compiler)
# Create the `cpptest` target if we can find GTest. If not, we create dummy
# targets that give the user an informative error message.
if(GTEST_FOUND)
tvm_file_glob(GLOB_RECURSE TEST_SRCS tests/cpp/*.cc)
add_executable(cpptest ${TEST_SRCS})
# include runtime files for unit testing
target_link_libraries(cpptest PRIVATE ${TVM_TEST_LIBRARY_NAME} GTest::GTest GTest::Main GTest::gmock pthread dl)
if(DEFINED LLVM_LIBS)
# The TVM library is linked with LLVM libraries. If the LLVM libraries are
# static and the symbols are not hidden, then don't link them again into
# cpptest since cpptest is itself linked against the TVM library. If static
# LLVM libraries are linked in twice, it can cause issues with global
# variable initialization (cl::opt).
# If the LLVM libraries are dynamic, we have to link them again, since the
# TVM library will not contain any LLVM definitions.
unset(LLVM_SO)
foreach(L IN LISTS LLVM_LIBS)
if(L MATCHES "libLLVM.*\.so")
set(LLVM_SO TRUE)
break()
endif()
endforeach()
if(DEFINED LLVM_SO OR HIDE_PRIVATE_SYMBOLS)
target_link_libraries(cpptest PRIVATE ${LLVM_LIBS})
endif()
endif()
set_target_properties(cpptest PROPERTIES EXCLUDE_FROM_ALL 1)
set_target_properties(cpptest PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD 1)
# cpptest is built into ${CMAKE_BINARY_DIR} but the TVM shared libs it
# links against (libtvm_compiler.so / libtvm_runtime.so) now live in
# ${CMAKE_BINARY_DIR}/lib. CMake's default behaviour bakes the
# absolute path of build/lib/ into the binary's RUNPATH, which works in tree
# but is not relocatable. Set $ORIGIN/lib (or @loader_path/lib on macOS)
# explicitly so the test binary finds its TVM libs relative to its own
# location, mirroring the install-time RPATH setup.
if(APPLE)
set_target_properties(cpptest PROPERTIES
BUILD_RPATH "@loader_path/lib"
INSTALL_RPATH "@loader_path/lib"
)
elseif(UNIX)
set_target_properties(cpptest PROPERTIES
BUILD_RPATH "\$ORIGIN/lib"
INSTALL_RPATH "\$ORIGIN/lib"
)
endif()
target_compile_definitions(cpptest PRIVATE "NDEBUG")
if(TVM_DEBUG_WITH_ABI_CHANGE)
target_compile_definitions(cpptest PRIVATE "TVM_DEBUG_WITH_ABI_CHANGE")
endif(TVM_DEBUG_WITH_ABI_CHANGE)
# For some reason, compile definitions are not propagated correctly, so we manually add them here
target_compile_definitions(cpptest PUBLIC $<TARGET_PROPERTY:tvm_compiler,INTERFACE_COMPILE_DEFINITIONS>)
gtest_discover_tests(cpptest)
endif()
# Custom targets
add_custom_target(runtime DEPENDS tvm_runtime)
# Installation rules
install(TARGETS tvm_compiler DESTINATION lib${LIB_SUFFIX})
install(TARGETS tvm_runtime DESTINATION lib${LIB_SUFFIX})
if (INSTALL_DEV)
install(
DIRECTORY "include/" DESTINATION "include"
FILES_MATCHING
PATTERN "*.h"
)
else(INSTALL_DEV)
install(
DIRECTORY "include/tvm/runtime/" DESTINATION "include/tvm/runtime"
FILES_MATCHING
PATTERN "*.h"
)
endif(INSTALL_DEV)
include(CMakePackageConfigHelpers)
set(PROJECT_CONFIG_CONTENT "@PACKAGE_INIT@\n")
string(APPEND PROJECT_CONFIG_CONTENT "include(CMakeFindDependencyMacro)\n")
string(APPEND PROJECT_CONFIG_CONTENT "find_dependency(Threads REQUIRED)\n")
string(APPEND PROJECT_CONFIG_CONTENT
"include(\"\${CMAKE_CURRENT_LIST_DIR}/${PROJECT_NAME}Targets.cmake\")")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/temp_config_file.cmake" ${PROJECT_CONFIG_CONTENT})
# install(EXPORT ${PROJECT_NAME}Targets
# NAMESPACE ${PROJECT_NAME}::
# DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
# Create config for find_package()
configure_package_config_file(
"${CMAKE_CURRENT_BINARY_DIR}/temp_config_file.cmake" ${PROJECT_NAME}Config.cmake
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
install(
FILES
"${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
# More target definitions
if(MSVC)
# tvm_objs builds libtvm_compiler.dll → TVM_DLL=dllexport,
# but TVM_RUNTIME_DLL=dllimport (runtime symbols come from libtvm_runtime.dll).
target_compile_definitions(tvm_objs PRIVATE -DTVM_EXPORTS -DTVM_FFI_EXPORTS)
# tvm_runtime_objs builds libtvm_runtime.dll → TVM_RUNTIME_DLL=dllexport.
# No TVM_EXPORTS here: the runtime side never defines compiler-exported
# symbols, so leaving TVM_DLL as dllimport correctly fails any accidental
# use of compiler-side ``TVM_DLL`` symbols from a runtime TU.
target_compile_definitions(tvm_runtime_objs PRIVATE -DTVM_RUNTIME_EXPORTS -DTVM_FFI_EXPORTS)
endif()
set(TVM_IS_DEBUG_BUILD OFF)
if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo" OR CMAKE_CXX_FLAGS MATCHES "-g")
set(TVM_IS_DEBUG_BUILD ON)
endif()
# Change relative paths in backtrace to absolute ones
if(TVM_IS_DEBUG_BUILD)
set(FILE_PREFIX_MAP_FLAG "-ffile-prefix-map=..=${CMAKE_CURRENT_SOURCE_DIR}")
target_compile_options(tvm_compiler PRIVATE "${FILE_PREFIX_MAP_FLAG}")
CHECK_CXX_COMPILER_FLAG("${FILE_PREFIX_MAP_FLAG}" FILE_PREFIX_MAP_SUPPORTED)
if(FILE_PREFIX_MAP_SUPPORTED)
target_compile_options(tvm_compiler PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${FILE_PREFIX_MAP_FLAG}>)
target_compile_options(tvm_objs PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${FILE_PREFIX_MAP_FLAG}>)
target_compile_options(tvm_runtime PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${FILE_PREFIX_MAP_FLAG}>)
target_compile_options(tvm_runtime_objs PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${FILE_PREFIX_MAP_FLAG}>)
endif()
endif()
tvm_ffi_add_apple_dsymutil(tvm_compiler)
# Only run dsymutil on shared libraries, not static libraries
if(NOT BUILD_STATIC_RUNTIME)
tvm_ffi_add_apple_dsymutil(tvm_runtime)
endif()
if(BUILD_FOR_HEXAGON)
# Wrap pthread_create to allow setting custom stack size.
set_property(TARGET tvm_runtime APPEND PROPERTY LINK_FLAGS
"-Wl,--wrap=pthread_create")
# Link tvm_runtime into the RPC skel library. Make sure it's built
# as a part of the "runtime" target.
if(USE_HEXAGON_RPC)
target_link_libraries(
hexagon_rpc_skel -Wl,--whole-archive tvm_runtime tvm_ffi_static -Wl,--no-whole-archive)
add_dependencies(runtime hexagon_rpc_skel)
endif()
endif()
find_and_set_linker(${USE_ALTERNATIVE_LINKER})
if(${SUMMARIZE})
print_summary()
endif()
dump_options_to_file("${TVM_ALL_OPTIONS}")
if(USE_CUDA AND USE_CUTLASS)
install(TARGETS fpA_intB_gemm EXPORT ${PROJECT_NAME}Targets DESTINATION lib${LIB_SUFFIX})
install(TARGETS flash_attn EXPORT ${PROJECT_NAME}Targets DESTINATION lib${LIB_SUFFIX})
# fpA_intB_gemm, fpA_intB_gemm_tvm, and flash_attn are linked by
# tvm_runtime_extra (see the inline assembly block above); no link needed here.
endif()
if(USE_CUDA AND USE_NVTX)
add_compile_definitions(TVM_NVTX_ENABLED=1)
endif()
# Note: NCCL, NVSHMEM, RCCL target_link_libraries are handled in the inline
# libtvm_runtime_extra assembly block above.
# Keep the core shared libraries relocatable. A relative rpath
# ($ORIGIN / @loader_path) is correct in any build because the sibling runtime
# DSOs are installed next to each other, so apply it unconditionally rather than
# only when building the Python wheel. (tvm_runtime_extra already gets its rpath
# where it is defined above.)
tvm_configure_target_library(tvm_compiler)
tvm_configure_target_library(tvm_runtime)
# Python package installation configuration
# This section ensures that all necessary files are installed for the Python wheel
if(TVM_BUILD_PYTHON_MODULE)
message(STATUS "Configuring Python package installation")
# Install compiled shared libraries into <project>/lib so that
# tvm_ffi.libinfo.load_lib_ctypes(package="tvm", target_name=...) can find
# them via the package RECORD or the project's lib/ fallback dir.
install(TARGETS tvm_compiler DESTINATION "lib")
install(TARGETS tvm_runtime DESTINATION "lib")
# Install third-party compiled dependencies into the same lib/ dir.
if(TARGET fpA_intB_gemm)
tvm_configure_target_library(fpA_intB_gemm)
install(TARGETS fpA_intB_gemm DESTINATION "lib")
endif()
if(TARGET flash_attn)
tvm_configure_target_library(flash_attn)
install(TARGETS flash_attn DESTINATION "lib")
endif()
# Install prebuilt extra runtime libraries into the same lib/ dir. This is how
# the separately-built CUDA runtime (libtvm_runtime_cuda.so) is bundled: the
# publishing flow builds it in a CUDA-enabled environment and passes its path
# via TVM_PACKAGE_EXTRA_LIBS, so it ships through the normal CMake install
# rather than a post-build wheel rewrite.
foreach(_extra_lib IN LISTS TVM_PACKAGE_EXTRA_LIBS)
if(NOT EXISTS "${_extra_lib}")
message(FATAL_ERROR "TVM_PACKAGE_EXTRA_LIBS entry does not exist: ${_extra_lib}")
endif()
install(FILES "${_extra_lib}" DESTINATION "lib")
endforeach()
# Install minimal header files needed by Python extensions
install(
DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/tvm/runtime/"
DESTINATION "include/tvm/runtime/"
FILES_MATCHING
PATTERN "*.h"
)
# Install minimal CMake configuration
install(
DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/cmake/utils/"
DESTINATION "cmake/utils/"
FILES_MATCHING
PATTERN "*.cmake"
)
# Install CUTLASS headers only if available
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/cutlass/include")
install(
DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/cutlass/include/"
DESTINATION "3rdparty/cutlass/include/"
FILES_MATCHING
PATTERN "*.h"
PATTERN "*.hpp"
)
endif()
# Install minimal source files
install(
DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/src/runtime/"
DESTINATION "src/runtime/"
FILES_MATCHING
PATTERN "*.cc"
PATTERN "*.h"
)
# Install web package
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/web/" DESTINATION "web/")
# Install licenses (required for distribution)
install(
DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/licenses/"
DESTINATION "licenses/"
)
# Install essential metadata files
install(FILES
"${CMAKE_CURRENT_SOURCE_DIR}/README.md"
"${CMAKE_CURRENT_SOURCE_DIR}/LICENSE"
"${CMAKE_CURRENT_SOURCE_DIR}/NOTICE"
DESTINATION "."
)
message(STATUS "Python package installation configured")
endif()
+243
View File
@@ -0,0 +1,243 @@
<!--- Licensed to the Apache Software Foundation (ASF) under one -->
<!--- or more contributor license agreements. See the NOTICE file -->
<!--- distributed with this work for additional information -->
<!--- regarding copyright ownership. The ASF licenses this file -->
<!--- to you 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. -->
TVM Contributors
================
TVM adopts the Apache way and governs by merit. We believe that it is important to create an inclusive community where everyone can use,
contribute to, and influence the direction of the project. We actively invite contributors who have earned the merit to be part of the development community.
See the [community structure document](https://tvm.apache.org/docs/contribute/community.html) for the explanation of community structure and contribution guidelines.
## Committers
We add tag along with committer name to show areas that they are familiar with.
We do encourage everyone to work anything they are interested in.
- [Aditya Atluri](https://github.com/adityaatluri): @adityaatluri - rocm
- [Matthew Barrett](https://github.com/mbaret): @mbaret - byoc, arm
- [Matthew Brookhart](https://github.com/mbrookhart): @mbrookhart - relay, frontends
- [Yaxing Cai](https://github.com/cyx-6): @cyx-6 - tvm-script, runtime
- [Liangfu Chen](https://github.com/liangfu): @liangfu - vta, chisel, intel FPGA, c runtime
- [Tianqi Chen](https://github.com/tqchen) (PMC): @tqchen - topi, compiler, relay, docs
- [Wei Chen](https://github.com/wweic): @wweic - runtime, relay, vm
- [Zhi Chen](https://github.com/zhiics) (PMC): @zhiics - relay, quantization, pass manager
- [Egor Churaev](https://github.com/echuraev): @echuraev - metal, opencl, adreno
- [Balint Cristian](https://github.com/cbalint13): @cbalint13
- [Siyuan Feng](https://github.com/Hzfengsy) (PMC): @Hzfengsy - tir
- [Josh Fromm](https://github.com/jwfromm) (PMC): @jwfromm - frontends, quantization, topi
- [Mehrdad Hessar](https://github.com/mehrdadh): @mehrdadh - microTVM, hexagon
- [Masahiro Hiramori](https://github.com/mshr-h): @mshr-h - relax, frontend
- [Bohan Hou](https://github.com/spectrometerHBH) (PMC): @spectrometerHBH - tir, arith, tvm-script
- [Yuwei Hu](https://github.com/Huyuwei): @Huyuwei - topi, frontends
- [Luke Hutton](https://github.com/lhutton1): @lhutton1 - ethos-u, arm
- [Nick Hynes](https://github.com/nhynes): @nhynes: - sgx, rust
- [Animesh Jain](https://github.com/anijain2305): @anijain2305 - quantization, relay
- [Chenfan Jia](https://github.com/jcf94): @jcf94 - auto_scheduler
- [Ziheng Jiang](https://github.com/ZihengJiang) (PMC): @ZihengJiang - relay, compiler
- [Hongyi Jin](https://github.com/jinhongyii): @jinhongyii - tir, tvm-script, arith, relay, topi
- [Manupa Karunaratne](https://github.com/manupak): @manupak - ethos-u, memory planner
- [Elen Kalda](https://github.com/ekalda): @ekalda - ethos-u, arm
- [Marisa Kirisame](https://github.com/MarisaKirisame): @MarisaKirisame - relay
- [Tristan Konolige](https://github.com/tkonolige): @tkonolige - profiling, relay, tir, runtime
- [Ruihang Lai](https://github.com/MasterJH5574) (PMC): @MasterJH5574 - tir, tvm-script
- [Wuwei Lin](https://github.com/vinx13) (PMC): @vinx13 - relay, topi, tir, meta_schedule
- [Yizhi Liu](https://github.com/yzhliu) (PMC): @yzhliu - jvm, topi, relay
- [Hao Lu](https://github.com/hlu1): @hlu1 - nnpack, frontends
- [Eric Lunderberg](https://github.com/Lunderberg): @Lunderberg - CI, Vulkan backend
- [Andrew Z. Luo](https://github.com/AndrewZhaoLuo): @AndrewZhaoLuo - amp, relay, frontends
- [Steven Lyubomirsky](https://github.com/slyubomirsky): @slyubomirsky - relay
- [Masahiro Masuda](https://github.com/masahi) (PMC): @masahi - topi, relay
- [Thierry Moreau](https://github.com/tmoreau89) (PMC): @tmoreau89 - vta
- [Kazutaka Morita](https://github.com/kazum): @kazum - frontends, opencl
- [Trevor Morris](https://github.com/trevor-m): @trevor-m - byoc, compiler
- [Leandro Nunes](https://github.com/leandron) (PMC): @leandron - tvmc
- [Lily Orth-Smith](https://github.com/electriclilies): @electriclilies - relay
- [Ashutosh Parkhi](https://github.com/ashutosh-arm): @ashutosh-arm - cmsis-nn
- [Krzysztof Parzyszek](https://github.com/kparzysz-quic) (PMC): @kparzysz-quic - hexagon, llvm
- [Andrew Reusch](https://github.com/areusch): (PMC) @areusch - runtime, microTVM
- [David Riazati](https://github.com/driazati): @driazati - ci, community
- [Jared Roesch](https://github.com/jroesch) (PMC): @jroesch - relay
- [Gustavo Romero](https://github.com/gromero): @gromero - microtvm, tvmc
- [Giuseppe Rossini](https://github.com/giuseros): @giuseros - aot, arm
- [Siju Samuel](https://github.com/siju-samuel): @siju-samuel - frontends
- [Christopher Sidebottom](https://github.com/Mousius): @Mousius - arm, ethos-u, relay
- [Junru Shao](https://github.com/junrushao) (PMC): @junrushao - relay, compiler
- [Haichen Shen](https://github.com/icemelon) (PMC): @icemelon - relay, topi
- [Chris Sullivan](https://github.com/csullivan): @csullivan - amd backend
- [Siva Rama Krishna Reddy](https://github.com/srkreddy1238): @srkreddy1238 - frontends, golang
- [Zhixun Tan](https://github.com/phisiart): @phisiart - opengl, web
- [Tong Meng](https://github.com/Archermmt): @Archermmt - msc
- [Andrew Tulloch](https://github.com/ajtulloch): @ajtulloch - topi, compiler, runtime
- [Gavin Uberti](https://github.com/guberti): @guberti - microtvm, arm
- [Luis Vega](https://github.com/vegaluisjose): @vegaluisjose - vta, chisel
- [Leyuan Wang](https://github.com/Laurawly) (PMC): @Laurawly: - topi
- [Yao Wang](https://github.com/kevinthesun): @kevinthesun (PMC): - topi, vision
- [Jian Weng](https://github.com/were): @were: - hybrid script
- [Zhao Wu](https://github.com/FrozenGene): @FrozenGene - runtime, topi, frontends
- [Eddie Yan](https://github.com/eqy) (PMC): @eqy - runtime, autotvm, rpc, topi
- [Zihao Ye](https://github.com/yzh119): @yzh119 - tir
- [Hao Yu](https://github.com/comaniac): @comaniac (PMC) - relay, byoc, auto_scheduler
- [Shuai Yuan](https://github.com/ysh329): @ysh329 (PMC) - ci
- [Qiang Zhang](https://github.com/Johnson9009): @Johnson9009 - relay, tvm-script
- [Lianmin Zheng](https://github.com/merrymercy) (PMC): @merrymercy - autotvm, auto_scheduler, topi, relay
- [Xiyou Zhou](https://github.com/zxybazh): @zxybazh - relay
- [wrongtest](https://github.com/wrongtest-intellif) (PMC): @wrongtest-intellif - tir, tvm-script, arith
- [Anirudh Sundar Subramaniam](https://github.com/quic-sanirudh): @quic-sanirudh
## Reviewers
- [Aditya Atluri](https://github.com/adityaatluri): @adityaatluri
- [Matthew Barrett](https://github.com/mbaret): @mbaret
- [Arnaud Bergeron](https://github.com/abergeron): @abergeron
- [Florin Blanaru](https://github.com/gigiblender): @gigiblender
- [Matthew Brookhart](https://github.com/mbrookhart): @mbrookhart
- [Yaxing Cai](https://github.com/cyx-6): @cyx-6
- [Liangfu Chen](https://github.com/liangfu): @liangfu
- [Tianqi Chen](https://github.com/tqchen): @tqchen
- [Zhi Chen](https://github.com/zhiics): @zhiics
- [Valery Chernov](https://github.com/vvchernov): @vvchernov
- [Neo Chien](https://github.com/cchung100m): @cchung100m
- [Christian Convey](https://github.com/cconvey/): @cconvey
- [Meghan Cowan](https://github.com/cowanmeg): @cowanmeg
- [Balint Cristian](https://github.com/cbalint13): @cbalint13
- [Egor Churaev](https://github.com/echuraev): @echuraev
- [Xiaoqiang Dan](https://github.com/xqdan): @xqdan
- [Yixin Dong](https://github.com/Ubospica) @Ubospica
- [Haozheng Fan](https://github.com/hzfan): @hzfan
- [Siyuan Feng](https://github.com/Hzfengsy): @Hzfengsy
- [Josh Fromm](https://github.com/jwfromm): @jwfromm
- [Alexey Gladyshev](https://github.com/KJlaccHoeUM9l): @KJlaccHoeUM9l
- [Sergei Grechanik](https://github.com/sgrechanik-h): @sgrechanik-h
- [Altan Haan](https://github.com/altanh): @altanh
- [Mehrdad Hessar](https://github.com/mehrdadh): @mehrdadh
- [Masahiro Hiramori](https://github.com/mshr-h): @mshr-h
- [Bohan Hou](https://github.com/spectrometerHBH): @spectrometerHBH
- [Yuwei Hu](https://github.com/Huyuwei): @Huyuwei
- [Luke Hutton](https://github.com/lhutton1): @lhutton1
- [Nick Hynes](https://github.com/nhynes): @nhynes
- [Animesh Jain](https://github.com/anijain2305): @anijain2305
- [Chenfan Jia](https://github.com/jcf94): @jcf94
- [Hua Jiang](https://github.com/huajsj): @huajsj
- [Ziheng Jiang](https://github.com/ZihengJiang): @ZihengJiang
- [Hongyi Jin](https://github.com/jinhongyii): @jinhongyii
- [Manupa Karunaratne](https://github.com/manupak): @manupak
- [Elen Kalda](https://github.com/ekalda): @ekalda
- [Marisa Kirisame](https://github.com/MarisaKirisame): @MarisaKirisame
- [Michael J. Klaiber](https://github.com/MichaelJKlaiber/) @MichaelJKlaiber
- [Noah Kontur](https://github.com/konturn/) @konturn
- [Tristan Konolige](https://github.com/tkonolige): @tkonolige
- [Mohamad Katanbaf](https://github.com/mkatanbaf): @mkatanbaf
- [Denise Kutnick](https://github.com/denise-k): @denise-k
- [Ruihang Lai](https://github.com/MasterJH5574): @MasterJH5574
- [Nicola Lancellotti](https://github.com/nicolalancellotti): @NicolaLancellotti
- [Wuwei Lin](https://github.com/vinx13): @vinx13
- [Andrew Liu](https://github.com/hypercubestart): @hypercubestart
- [Henry Liu](https://github.com/optima2005): @optima2005
- [Xin Liu](https://github.com/Meteorix): @Meteorix
- [Yizhi Liu](https://github.com/yzhliu) : @yzhliu
- [Hao Lu](https://github.com/hlu1): @hlu1
- [Eric Lunderberg](https://github.com/Lunderberg): @Lunderberg
- [Andrew Z. Luo](https://github.com/AndrewZhaoLuo): @AndrewZhaoLuo
- [Steven Lyubomirsky](https://github.com/slyubomirsky): @slyubomirsky
- [Alan MacDonald](https://github.com/alanmacd): @alanmacd
- [Masahiro Masuda](https://github.com/masahi): @masahi
- [Andrey Malyshev](https://github.com/elvin-n): @elvin-n
- [Sergey Mironov](https://github.com/grwlf): @grwlf
- [Thierry Moreau](https://github.com/tmoreau89): @tmoreau89
- [Kazutaka Morita](https://github.com/kazum): @kazum
- [Trevor Morris](https://github.com/trevor-m): @trevor-m
- [Tatsuya Nishiyama](https://github.com/nishi-t): @nishi-t
- [Leandro Nunes](https://github.com/leandron): @leandron
- [Jiawei Liu](https://github.com/ganler): @ganler
- [Lily Orth-Smith](https://github.com/electriclilies): @electriclilies
- [Wei Pan](https://github.com/wpan11nv): @wpan11nv
- [Michalis Papadimitriou](https://github.com/mikepapadim): @mikepapadim
- [Krzysztof Parzyszek](https://github.com/kparzysz-quic): @kparzysz-quic
- [Sunghyun Park](https://github.com/sunggg): @sunggg
- [Ashutosh Parkhi](https://github.com/ashutosh-arm): @ashutosh-arm
- [Alexander Peskov](https://github.com/apeskov): @apeskov
- [Pariksheet Pinjari](https://github.com/PariksheetPinjari909): @PariksheetPinjari909
- [Josh Pollock](https://github.com/joshpoll): @joshpoll
- [Ramana Radhakrishnan](https://github.com/u99127): @u99127
- [Andrew Reusch](https://github.com/areusch): @areusch
- [David Riazati](https://github.com/driazati): @driazati
- [Jared Roesch](https://github.com/jroesch): @jroesch
- [Gustavo Romero](https://github.com/gromero): @gromero
- [Giuseppe Rossini](https://github.com/giuseros): @giuseros
- [Siju Samuel](https://github.com/siju-samuel): @siju-samuel
- [Janet Schneider](https://github.com/janetsc): @janetsc
- [Junru Shao](https://github.com/junrushao): @junrushao
- [Haichen Shen](https://github.com/icemelon): @icemelon
- [Qingchao Shen](https://github.com/jikechao): @jikechao
- [Xingjian Shi](https://github.com/sxjscience): @sxjscience
- [Yuanjing Shi](https://github.com/shingjan): @shingjan
- [Mark Shields](https://github.com/mbs-octoml): @mbs-octoml
- [Christopher Sidebottom](https://github.com/mousius): @mousius
- [Siva Rama Krishna Reddy](https://github.com/srkreddy1238): @srkreddy1238
- [Dmitriy Smirnov](https://github.com/d-smirnov): @d-smirnov
- [Jon Soifer](https://github.com/soiferj): @soiferj
- [Adam Straw](https://github.com/adstraw): @adstraw
- [Chris Sullivan](https://github.com/csullivan): @csullivan
- [Anirudh Sundar Subramaniam](https://github.com/quic-sanirudh): @quic-sanirudh
- [Zhixun Tan](https://github.com/phisiart): @phisiart
- [Tong Meng](https://github.com/Archermmt): @Archermmt
- [Andrew Tulloch](https://github.com/ajtulloch): @ajtulloch
- [Jorn Tuyls](https://github.com/jtuyls): @jtuyls
- [Gavin Uberti](https://github.com/guberti): @guberti
- [Luis Vega](https://github.com/vegaluisjose): @vegaluisjose
- [Jyotsna Verma](https://github.com/jverma-quic): @jverma-quic
- [Thomas Viehmann](https://github.com/t-vi): @t-vi
- [An Wang](https://github.com/anwang2009): @anwang2009
- [Yao Wang](https://github.com/kevinthesun): @kevinthesun
- [Yuchen Wang](https://github.com/wyc-ruiker): @wyc-ruiker
- [Leyuan Wang](https://github.com/Laurawly): @Laurawly
- [Alex Weaver](https://github.com/alex-weaver): @alex-weaver
- [Logan Weber](https://github.com/weberlo): @weberlo
- [Matt Welsh](https://github.com/mdw-octoml): @mdw-octoml
- [Cheng Wen](https://github.com/chengven027-intellif): @chengven027-intellif
- [Jian Weng](https://github.com/were): @were
- [wrongtest](https://github.com/wrongtest-intellif): @wrongtest-intellif
- [Yong Wu](https://github.com/yongwww): @yongwww
- [Zhao Wu](https://github.com/FrozenGene): @FrozenGene
- [Bing Xu](https://github.com/antinucleon): @antinucleon
- [Eddie Yan](https://github.com/eqy): @eqy
- [Aleksei Yazev](https://github.com/Aleksei-grovety): @Aleksei-grovety
- [Zihao Ye](https://github.com/yzh119): @yzh119
- [Hao Yu](https://github.com/comaniac): @comaniac
- [Shuai Yuan](https://github.com/ysh329): @ysh329
- [Joshua Z. Zhang](https://github.com/zhreshold): @zhreshold
- [Lianmin Zheng](https://github.com/merrymercy): @merrymercy
- [Min Chen](https://github.com/multiverstack-intellif): @multiverstack-intellif
- [Xiyou Zhou](https://github.com/zxybazh): @zxybazh
- [@blackkker](https://github.com/blackkker): @blackkker
- [Jiajun Jiang](https://github.com/jiangjiajun): @jiangjiajun
- [Qiang Zhang](https://github.com/Johnson9009): @Johnson9009
## List of Contributors
- [Full List of Contributors](https://github.com/apache/tvm/graphs/contributors)
## Mentors
TVM is now a top-level Apache project. During our Incubator phase, we were fortunate to have the following mentors.
- Markus Weimer @markusweimer
- Sebastian Schelter @sscdotopen
- Byung-Gon Chun @bgchun
- Henry Saputra @hsaputra
- Timothy Chen @tnachen
- Furkan KAMACI @kamaci
+762
View File
@@ -0,0 +1,762 @@
This file contains the PGP keys of various developers.
Please don't use them for email unless you have to. Their main
purpose is code signing.
Examples of importing this file in your keystore:
gpg --import KEYS.txt
(need pgp and other examples here)
Examples of adding your key to this file:
pgp -kxa <your name> and append it to this file.
(pgpk -ll <your name> && pgpk -xa <your name>) >> this file.
(gpg --list-sigs <your name>
&& gpg --armor --export <your name>) >> this file.
-----------------------------------------------------------------------------------
pub rsa4096 2019-11-15 [SC]
EF52D68AD5276994249816836754EA97C55E3DEB
uid [ultimate] Tianqi Chen (CODE SIGNING KEY) <tqchen@apache.org>
sig 3 6754EA97C55E3DEB 2019-11-15 Tianqi Chen (CODE SIGNING KEY) <tqchen@apache.org>
sub rsa4096 2019-11-15 [E]
sig 6754EA97C55E3DEB 2019-11-15 Tianqi Chen (CODE SIGNING KEY) <tqchen@apache.org>
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
ha8fS9ysWDy/Ye65MP4=
=MSiP
-----END PGP PUBLIC KEY BLOCK-----
pub rsa4096 2018-02-07 [SC]
F42C1A6E634C105E8D985105CA751254E97B9FE4
uid [ultimate] Yizhi Liu <liuyizhi@apache.org>
sig 3 CA751254E97B9FE4 2018-02-07 Yizhi Liu <liuyizhi@apache.org>
sub rsa4096 2018-02-07 [E]
sig CA751254E97B9FE4 2018-02-07 Yizhi Liu <liuyizhi@apache.org>
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
=1xfH
-----END PGP PUBLIC KEY BLOCK-----
pub rsa4096 2020-09-24 [SC]
6A0D4938D8C052C759AE2460ED03B26E4FC3509F
uid [ultimate] Ziheng Jiang <ziheng@apache.org>
sig 3 ED03B26E4FC3509F 2020-09-24 Ziheng Jiang <ziheng@apache.org>
sub rsa4096 2020-09-24 [E]
sig ED03B26E4FC3509F 2020-09-24 Ziheng Jiang <ziheng@apache.org>
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
=nhJ/
-----END PGP PUBLIC KEY BLOCK-----
pub 4096R/D75EFD4B 2020-09-24
uid Zhi Chen <zhic@apache.org>
sig 3 D75EFD4B 2020-09-24 Zhi Chen <zhic@apache.org>
sub 4096R/285DD7CB 2020-09-24
sig D75EFD4B 2020-09-24 Zhi Chen <zhic@apache.org>
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
=GEwA
-----END PGP PUBLIC KEY BLOCK-----
pub rsa4096 2021-11-10 [SC]
C5E5C09030E7BD32DF9A67CE35ABC9676004ADAE
uid [ultimate] Junru Shao <junrushao@apache.org>
sig 3 35ABC9676004ADAE 2021-11-10 Junru Shao <junrushao@apache.org>
sub rsa4096 2021-11-10 [E]
sig 35ABC9676004ADAE 2021-11-10 Junru Shao <junrushao@apache.org>
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
=jNCy
-----END PGP PUBLIC KEY BLOCK-----
pub rsa4096 2021-11-10 [SC]
43C2306EBED3D533F2CFBA8A2C75E5A496C80880
uid [ultimate] Wuwei Lin <wuwei@apache.org>
sig 3 2C75E5A496C80880 2021-11-10 Wuwei Lin <wuwei@apache.org>
sub rsa4096 2021-11-10 [E]
sig 2C75E5A496C80880 2021-11-10 Wuwei Lin <wuwei@apache.org>
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
=+gLd
-----END PGP PUBLIC KEY BLOCK-----
pub rsa3072 2022-07-12 [SC] [expires: 2024-07-11]
B3C6A14C13B8C6727BC2FD2F07FA463F1C926F48
uid [ultimate] David Riazati <driazati@apache.org>
sig 3 07FA463F1C926F48 2022-07-12 David Riazati <driazati@apache.org>
sub rsa3072 2022-07-12 [E] [expires: 2024-07-11]
sig 07FA463F1C926F48 2022-07-12 David Riazati <driazati@apache.org>
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
rSSBN90DWxIgPhlAqgO0ofkbPSVwF/9i7nd3
=XBuV
-----END PGP PUBLIC KEY BLOCK-----
pub rsa4096 2022-10-10 [SC]
1B63BD2FFF5E515DA1BEF393C9A56ABD5CCA3EB8
uid [ultimate] Andrew Zhao Luo <andrewzhaoluo@apache.org>
sig 3 C9A56ABD5CCA3EB8 2022-10-10 Andrew Zhao Luo <andrewzhaoluo@apache.org>
sub rsa4096 2022-10-10 [E]
sig C9A56ABD5CCA3EB8 2022-10-10 Andrew Zhao Luo <andrewzhaoluo@apache.org>
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
/lFRrVmO
=rAtV
-----END PGP PUBLIC KEY BLOCK-----
pub rsa4096 2023-02-02 [SC]
466390C69A21ABAE77DC63F128D4862222B8EC31
uid [ultimate] Leandro Nunes <leandron@apache.org>
sig 3 28D4862222B8EC31 2023-02-02 Leandro Nunes <leandron@apache.org>
sub rsa4096 2023-02-02 [E]
sig 28D4862222B8EC31 2023-02-02 Leandro Nunes <leandron@apache.org>
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
tO0=
=PI53
-----END PGP PUBLIC KEY BLOCK-----
pub rsa4096 2023-05-05 [SC]
664EF29634C05669C3DCF83106D051CA84EF3749
uid [ultimate] Siyuan Feng (CODE SIGNING KEY) <syfeng@apache.org>
sig 3 06D051CA84EF3749 2023-05-05 Siyuan Feng (CODE SIGNING KEY) <syfeng@apache.org>
sub rsa4096 2023-05-05 [E]
sig 06D051CA84EF3749 2023-05-05 Siyuan Feng (CODE SIGNING KEY) <syfeng@apache.org>
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBGRUb34BEACyedBJjD2tYY86mIr1OR42eR+w0dvh/cECgp4UIm5QG6z9YXrU
KjHtv426uKCloYiWU5+b8ASPMbtP5q1rvrRKQuapbDBN2qlS7E/PScFHDK50ydOA
v8melfp8pWLW48kE5EeSdvhF8U2QzEqT6EYmNIExLYjSV1+Jck40DbVL+ak/4clB
Qv/l0DW2Fw1u6GAKaNgnWgZDhc3os176rS0ERzflZeF+rqskYSb8Uy37vBB9By64
edIsaxxOEqvpJfa2Ar/6vDnBplYSfMHq9kqs0vuAVu0r7Dadl199oVMmUwBPD4uq
IVaWoetCbR+DpVIZZ74i4kOH47xfh3kZ5zvWo5E+PL4XH/6us5tp+nAnCjhthxY2
tpBBZo7M4qgwNOxk/3zsysZPYPhQaZUx7/LBaApOhyWrmQYrtZpvNjHsoEToHtDj
wCSwDxf2c6mrncnmKg6UfsWSEiIiPcsYqXkp2Bh4xKzdI54qCt8LTPGKjTPj4FO/
EccT5Ad8+lOKSOIugbNmECEbpVUhrlDa/yYtkosHbnQZFgBBaa+RCsRdbGpuuc+E
hqEpvwbPq0J8zPJpKmyeu/S5gWww29ix39J+F5ZxjQZBSUUPRsnCwHDdkgFBlDdK
ZeQqlKjr7Mfp+LlI+7HIIxO9HOPN3WIjFyWPm10d6cWHN7MDxMySP8l1+wARAQAB
tDJTaXl1YW4gRmVuZyAoQ09ERSBTSUdOSU5HIEtFWSkgPHN5ZmVuZ0BhcGFjaGUu
b3JnPokCTgQTAQoAOBYhBGZO8pY0wFZpw9z4MQbQUcqE7zdJBQJkVG9+AhsDBQsJ
CAcDBRUKCQgLBRYCAwEAAh4FAheAAAoJEAbQUcqE7zdJsZQP/0EX1XKFDF37c8cI
jQEeQ44Z3F5C6cxM7X9efqLQzDgvVRR525qjpM9uk/usUdKupwwX0GkJQu+JbRus
jFCi9RaOO88+w7ihS4qOFcXV0CXHxxSKkKfKU7DZhEprJtyQ1QE38gWlHKB/E6Y+
oJy9EKMZMXP28gj/tUT56IABI6X+b1BSTT5PV8QURkkTPDVQDpWo/AmtPdei2bvd
KnTZGAjxv98rMYvjJUGMPx8oA3cqDRlIltgSyXlwht8Ig/wbUzW/oRmzLk3TdeoE
42/QHTLjfDlSm72V2B67QToYo/URIxJimUvg4/+VT8ByLxPzkL3jYkDh6K87JaB3
9XO8HjNCH/xfZtMSQjmApInpk7VjDVhDC4Dlf5t41pUK5KvGsU7eLAE0jL/R/aA8
z5pvf3afLK3Bpj2zKvy2rFkRmCKIS7mycogBUdOk4GT8ZoLDuaTmUcbfx9H4/9Zw
UYWCw6cJzD3qICqCcszlfW+99b92JddCU5ITMfwuWuY/OX/LfpwibAjzor2TFWya
CVI7kkQj9C0vcbpxgCMd4HRMV9p2CQUkvPEKfaPg+kzJC1Yz87DC6aSrLIzVvcIj
LZ2yOzR4QIeTS6hRsMmQRGPZO0KFres4760BiUCH0gid6LWDNq2YTXqdNu1ffQXe
PV8Risr23rrxOTJqlYX3GF+Xd7C1uQINBGRUb34BEAC2q4MdKGYgsl9BpvOA7TnN
kBtc8Gmg+DOdjBhG5BCo6h6U15RxfIvSikRi0Sz3F3YZymGKIeJp8ug6brY4KWjA
7dtwqlvnthyWa0mPrgHZvkIM86URO5wSvRMXx1x/qWJ8BrOoCDji+fmC3uI9IbY5
RvkzHACYz4duZM54ZlhM6lOL3TtgF2OyXod2MFwuC5WAAPuqAG5MF+gNdf5JA+p2
RfDIGeZNOWQVWi9CrHWt8fC80WG/7r6Ta84yV6KTfqhsXToFZICVXt2BEg7K/UzI
Ip7Zf5rKsDT0iDxtiJIbwFBbTS0hE3ICrPWVHPNRVsqp3wHDkjjnt0aPL+G938i/
dzOwHZIf9nPjIrX94DvPpOXGrHsW6JyMHZ/3diROpWy7DzplW2i/wVftzkhT4GSN
xARgLJM/iZriOMYvHafpOm5OxkfFzeJpnjZrRTJKNCAQFbdxI9pzR/2ingvZekUs
FYcauQVuL1MbamEhf7pRXHOS0bOPAONpeXl7aNAH4jf8x/3iRHRQYcpGRT1ESddO
/swv+Cj2qj80vzF/oT/QMWrWDGHIiriRDSf1WpFHVqzki1jY6znrw6EvGsowbzha
kGC9dRzLm8P1aCwjjsU1J82yEKA4oHwAX6rlkRkWVUkTMk3G1coandbgcbnI6ngG
7V932RapIOZB06rh/6mGgQARAQABiQI2BBgBCgAgFiEEZk7yljTAVmnD3PgxBtBR
yoTvN0kFAmRUb34CGwwACgkQBtBRyoTvN0kLfg/+JOmX8SZLksdEo4H5KmeHVOQk
EpWlFk/SmSoxV1k+kz68B5gxBPWQwRj61cGoBFKdvP2s3BSnKy7+iow0uwh6KIy0
zMooEOqCr/kEeFLxq79kFzxwwDSkzUO1UwGWCzVGj4V3UCq72xt2r3mJxRLNijTr
JJFe6+pLFXRbgrZ5ulWGxkiZRK007fPqtkretLiTyUXcJzU6HBUi0/pnyA2B0mWL
E0HO0TdOPIDTH/t2vtLZNhWl2T0lbjtdL7IcPAKQoNd07GyK1pPGpLQqP5gRrpFR
zrbslqRvKtpVjL5iPQCtv8Tc9ovOIQVRXzuXm9W1OtgFVYH2GQe+vbpYhLL7dOfF
Cwo6YLQnc2DRLAffy6G5weLJYYE52gMzb8z++Ys0A+XBkuVvHX1EBeyWy+OSuFSl
Aujm/jMMgK7dtNIHXgtVGEAKKJb4amc1wsZ9dmUyf1UyFxWlDUEaLq8+5Ut8a+tW
pTwwQLAXPVElpi32gLDP2rvHzIw1Hs0MpoxwOOjH/QCeRQ/V3acAUVv1JX96On0t
NqlR/Q5S24ktyC1uy9oLdIZmgllKUb8i6s6+XSkWRata3HTsfySDXMdntZV1Zrjx
WTgrESErlqNLN5ZTTW/1jBELJCfJKxgHUip+Yo6qNZoWwNLP1BaIcoA3miSG3DXf
wS/UuN04NxDy7V6mPXE=
=MTba
-----END PGP PUBLIC KEY BLOCK-----
pub rsa4096 2024-01-15 [SC]
A4D9228E55761E665BF01CBB5CE869CB7DEC048C
uid [ultimate] Star Yuan (CODE SIGNING KEY) <ysh329@apache.org>
sig 3 5CE869CB7DEC048C 2024-01-15 Star Yuan (CODE SIGNING KEY) <ysh329@apache.org>
sub rsa4096 2024-01-15 [E]
sig 5CE869CB7DEC048C 2024-01-15 Star Yuan (CODE SIGNING KEY) <ysh329@apache.org>
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
HeZaRpYvkV8UNmnE
=BtHq
-----END PGP PUBLIC KEY BLOCK-----
pub rsa4096 2024-03-10 [SC]
298A8AA3D25AFD95D5C89C63C8815953907B66AD
uid [ultimate] Ruihang Lai (CODE SIGNING KEY) <ruihangl@apache.org>
sig 3 C8815953907B66AD 2024-03-10 [self-signature]
sub rsa4096 2024-03-10 [E]
sig C8815953907B66AD 2024-03-10 [self-signature]
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
2KO2FSofA649SDc2HheDeQ==
=yNdl
-----END PGP PUBLIC KEY BLOCK-----
+229
View File
@@ -0,0 +1,229 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
------------------------------------------------------------------------------------
This product bundles various third-party components under other open source licenses.
This section summarizes those components and their licenses. See licenses/
and 3rdparty/tvm-ffi/licenses/ for text of these licenses.
Apache Software Foundation License 2.0
--------------------------------------
3rdparty/OpenCL-Headers
3rdparty/cutlass_fpA_intB_gemm
3rdparty/tensorrt_llm
3rdparty/tvm-ffi/3rdparty/dlpack
MIT License
-----------
3rdparty/compiler-rt/builtin_fp16.h
BSD 3-Clause "New" or "Revised" License
---------------------------------------
3rdparty/cutlass
3rdparty/libflash_attn
3rdparty/tvm-ffi/3rdparty/libbacktrace
+5
View File
@@ -0,0 +1,5 @@
Apache TVM
Copyright 2019-2023 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
+65
View File
@@ -0,0 +1,65 @@
<!--- Licensed to the Apache Software Foundation (ASF) under one -->
<!--- or more contributor license agreements. See the NOTICE file -->
<!--- distributed with this work for additional information -->
<!--- regarding copyright ownership. The ASF licenses this file -->
<!--- to you 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. -->
<img src=https://raw.githubusercontent.com/apache/tvm-site/main/images/logo/tvm-logo-small.png width=128/> Open Machine Learning Compiler Framework
==============================================
[Documentation](https://tvm.apache.org/docs) |
[Contributors](CONTRIBUTORS.md) |
[Community](https://tvm.apache.org/community) |
[Release Notes](NEWS.md)
Apache TVM is an open machine learning compilation framework,
following the following principles:
- Python-first development that enables quick customization of machine learning compiler pipelines.
- Universal deployment to bring models into minimum deployable modules.
License
-------
TVM is licensed under the [Apache-2.0](LICENSE) license.
Getting Started
---------------
Check out the [TVM Documentation](https://tvm.apache.org/docs/) site for installation instructions, tutorials, examples, and more.
The [Getting Started with TVM](https://tvm.apache.org/docs/get_started/overview.html) tutorial is a great
place to start.
Contribute to TVM
-----------------
TVM adopts the Apache committer model. We aim to create an open-source project maintained and owned by the community.
Check out the [Contributor Guide](https://tvm.apache.org/docs/contribute/).
History and Acknowledgement
---------------------------
TVM started as a research project for deep learning compilation.
The first version of the project benefited a lot from the following projects:
- [Halide](https://github.com/halide/Halide): Part of TVM's TIR and arithmetic simplification module
originates from Halide. We also learned and adapted some parts of the lowering pipeline from Halide.
- [Loopy](https://github.com/inducer/loopy): use of integer set analysis and its loop transformation primitives.
- [Theano](https://github.com/Theano/Theano): the design inspiration of symbolic scan operator for recurrence.
Since then, the project has gone through several rounds of redesigns.
The current design is also drastically different from the initial design, following the
development trend of the ML compiler community.
The most recent version focuses on a cross-level design with TensorIR as the tensor-level representation
and Relax as the graph-level representation and Python-first transformations.
The project's current design goal is to make the ML compiler accessible by enabling most
transformations to be customizable in Python and bringing a cross-level representation that can jointly
optimize computational graphs, tensor programs, and libraries. The project is also a foundation
infra for building Python-first vertical compilers for domains, such as LLMs.
+7
View File
@@ -0,0 +1,7 @@
# WeHub 来源说明
- 原始项目:`apache/tvm`
- 原始仓库:https://github.com/apache/tvm
- 导入方式:上游默认分支的最新快照
- 原作者、版权和许可证信息以原始仓库及本仓库 LICENSE 为准
- 本文件仅用于记录来源,不代表 WeHub 是原项目作者
+15
View File
@@ -0,0 +1,15 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.externalNativeBuild
/app/src/main/jni/jni_helper_func.h
/app/src/main/jni/org_apache_tvm_native_c_api.cc
/app/src/main/jni/org_apache_tvm_native_c_api.h
/app/src/main/obj/
dev_tools/tvmrpc.keystore
+170
View File
@@ -0,0 +1,170 @@
<!--- Licensed to the Apache Software Foundation (ASF) under one -->
<!--- or more contributor license agreements. See the NOTICE file -->
<!--- distributed with this work for additional information -->
<!--- regarding copyright ownership. The ASF licenses this file -->
<!--- to you 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. -->
# Android TVM RPC
This folder contains Android RPC app that allows us to launch an RPC server on a Android device and connect to it through python script and do testing on the python side as normal TVM RPC.
You will need JDK, [Android NDK](https://developer.android.com/ndk) and an Android device to use this.
## Build and Installation
### <a name="buildapk">Build APK</a>
We use [Gradle](https://gradle.org) to build. Please follow [the installation instruction](https://gradle.org/install) for your operating system.
Before you build the Android application, please refer to [TVM4J Installation Guide](https://github.com/apache/tvm/blob/main/jvm/README.md) and install tvm4j-core to your local maven repository. You can find tvm4j dependency declare in `app/build.gradle`. Modify it if it is necessary.
```
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', {
exclude group: 'com.android.support', module: 'support-annotations'
})
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'com.google.android.material:material:1.8.0'
implementation files('../../../jvm/core/target/tvm4j-core-0.0.1-SNAPSHOT.jar')
testImplementation 'junit:junit:4.13.2'
}
```
Now use Gradle to compile JNI, resolve Java dependencies and build the Android application together with tvm4j. Run following script to generate the apk file.
```bash
export ANDROID_HOME=[Path to your Android SDK, e.g., ~/Android/sdk]
cd apps/android_rpc
gradle clean build
```
In `app/build/outputs/apk` you'll find `app-release-unsigned.apk`, use `dev_tools/gen_keystore.sh` to generate a signature and use `dev_tools/sign_apk.sh` to get the signed apk file `app/build/outputs/apk/release/tvmrpc-release.apk`.
Upload `tvmrpc-release.apk` to your Android device and install it:
```bash
$ANDROID_HOME/platform-tools/adb install app/build/outputs/apk/release/tvmrpc-release.apk
```
If you see error:
adb: failed to install app/build/outputs/apk/release/tvmrpc-release.apk:
Failure [INSTALL_FAILED_UPDATE_INCOMPATIBLE:
Package org.apache.tvm.tvmrpc signatures do not match the previously installed version; ignoring!]
Run uninstall first:
```bash
$ANDROID_HOME/platform-tools/adb uninstall org.apache.tvm.tvmrpc
```
### Build with OpenCL
Application is building with OpenCL support by default.
[OpenCL-wrapper](../../src/runtime/opencl/opencl_wrapper) is used and will dynamically load OpenCL library on the device.
If the device doesn't have OpenCL library on it, then you'll see in the runtime that OpenCL library cannot be opened.
If you want to build this application without OpenCL then set `USE_OPENCL = 0`
in [config.mk](./app/src/main/jni/make/config.mk)
## Cross Compile and Run on Android Devices
### Architecture and Android Standalone Toolchain
In order to cross compile a shared library (.so) for your android device, you have to know the target triple for the device. (Refer to [Cross-compilation using Clang](https://clang.llvm.org/docs/CrossCompilation.html) for more information). Run `adb shell cat /proc/cpuinfo` to list the device's CPU information.
Now use NDK to generate standalone toolchain for your device. For my test device, I use following command.
```bash
cd /opt/android-ndk/build/tools/
./make-standalone-toolchain.sh --platform=android-24 --use-llvm --arch=arm64 --install-dir=/opt/android-toolchain-arm64
```
If everything goes well, you will find compile tools in `/opt/android-toolchain-arm64/bin`. For example, `bin/aarch64-linux-android-g++` can be used to compile C++ source codes and create shared libraries for arm64 Android devices.
### Cross Compile and Upload to the Android Device
First start an RPC tracker using
```python -m tvm.exec.rpc_tracker --port [PORT]```
and connect your Android device to this RPC tracker via the TVM RPC application. Open the app,
set the `Address` and `Port` fields to the address and port of the RPC tracker respectively.
The key should be set to "android" if you wish to avoid modifying the default test script.
After pushing "START RPC" button on the app, you can check the connect by run
```python -m tvm.exec.query_rpc_tracker --port [PORT]```
on your host machine.
You are supposed to find a free "android" in the queue status.
```
...
Queue Status
-------------------------------
key total free pending
-------------------------------
android 1 1 0
-------------------------------
```
Then checkout [android\_rpc/tests/android\_rpc\_test.py](https://github.com/apache/tvm/blob/main/apps/android_rpc/tests/android_rpc_test.py) and run,
```bash
# Specify the RPC tracker
export TVM_TRACKER_HOST=0.0.0.0
export TVM_TRACKER_PORT=[PORT]
# Specify the standalone Android C++ compiler
export TVM_NDK_CC=/opt/android-toolchain-arm64/bin/aarch64-linux-android-g++
python android_rpc_test.py
```
This will compile TVM IR to shared libraries (CPU, OpenCL and Vulkan) and run vector addition on your Android device. To verify compiled TVM IR shared libraries on OpenCL target set `'test_opencl = True'` and on Vulkan target set `'test_vulkan = True'` in [tests/android_rpc_test.py](https://github.com/apache/tvm/blob/main/apps/android_rpc/tests/android_rpc_test.py), by default on CPU target will execute.
On my test device, it gives following results.
```bash
Run CPU test ...
0.000962932 secs/op
Run GPU(OpenCL Flavor) test ...
0.000155807 secs/op
[23:29:34] /home/tvm/src/runtime/vulkan/vulkan_device_api.cc:674: Cannot initialize vulkan: [23:29:34] /home/tvm/src/runtime/vulkan/vulkan_device_api.cc:512: Check failed: __e == VK_SUCCESS Vulan Error, code=-9: VK_ERROR_INCOMPATIBLE_DRIVER
Stack trace returned 10 entries:
[bt] (0) /home/user/.local/lib/python3.6/site-packages/tvm-0.4.0-py3.6-linux-x86_64.egg/tvm/libtvm.so(dmlc::StackTrace[abi:cxx11]()+0x53) [0x7f477f5399f3]
.........
You can still compile vulkan module but cannot run locally
Run GPU(Vulkan Flavor) test ...
0.000225198 secs/op
```
You can define your own TVM operators and test via this RPC app on your Android device to find the most optimized TVM schedule.
### Troubleshooting
If you build the application in Android Studio and see error similar to this one:
```
A problem occurred evaluating project ':app'.
> Failed to apply plugin 'com.android.internal.version-check'.
> Minimum supported Gradle version is 7.5. Current version is 7.4. If using the gradle wrapper, try editing the distributionUrl in /Users/echuraev/Workspace/OctoML/tvm_android_test/apps/android_deploy/gradle/wrapper/gradle-wrapper.properties to gradle-7.5-all.zip
```
Run project syncing `File -> Sync Project with Gradle Files`. It should sync the
project and create gradle-wrapper files.
+1
View File
@@ -0,0 +1 @@
/build
+103
View File
@@ -0,0 +1,103 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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.
apply plugin: 'com.android.application'
task generateJniHeaders(type: Exec, description: 'Generate JNI Headers') {
def headerPath = "${project.projectDir}/src/main/jni"
def classPath = "${project.projectDir}/../../../jvm/core/target/*"
def filePath = "${project.projectDir}/../../../jvm/core/src/main/java/org/apache/tvm/LibInfo.java"
commandLine "javac", "-h", headerPath, "-classpath", classPath, filePath
doLast {
file("${headerPath}/org_apache_tvm_LibInfo.h").renameTo(file("${headerPath}/org_apache_tvm_native_c_api.h"))
}
}
task copyFiles(type: Copy, description: 'Copy Sources for ndk-build') {
dependsOn "generateJniHeaders"
def ndkFilesPath = "${project.projectDir}/../../../jvm/native/src/main/native"
def srcPath = "${project.projectDir}/src/main/jni/"
from "${ndkFilesPath}/org_apache_tvm_native_c_api.cc", "${ndkFilesPath}/jni_helper_func.h"
into srcPath
}
task deleteLibs(type: Delete, description: "Delete Compiled Libraries") {
dependsOn "copyFiles"
def libsPath = "${project.projectDir}/src/main/libs"
delete libsPath
}
task buildJni(type: Exec, description: 'Build JNI libs') {
dependsOn "deleteLibs"
def buildPath = "${project.projectDir}/src/main/jni"
commandLine "ndk-build", "--directory", buildPath
}
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn buildJni
}
// gradle.projectsEvaluated {
// tasks.withType(JavaCompile) {
// options.compilerArgs << "-Xlint:deprecation"
// }
// }
android {
compileSdkVersion 33
defaultConfig {
applicationId "org.apache.tvm.tvmrpc"
minSdkVersion 24
targetSdkVersion 33
versionCode 1
versionName "1.0"
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
sourceSets {
main {
jni.srcDirs = []
jniLibs.srcDirs = ['src/main/libs']
}
}
lint {
disable 'Instantiatable' // MainActivity and RPCActivity must extend android.app.Activity
disable 'MissingClass' // .RPCWatchdogService was not found in the project or the libraries
disable 'IconDipSize' // The image ic_launcher.png varies significantly in its density-independent size
}
namespace 'org.apache.tvm.tvmrpc'
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', {
exclude group: 'com.android.support', module: 'support-annotations'
})
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'com.google.android.material:material:1.8.0'
implementation files('../../../jvm/core/target/tvm4j-core-0.0.1-SNAPSHOT.jar')
testImplementation 'junit:junit:4.13.2'
}
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:icon="@mipmap/ic_launcher">
<uses-native-library
android:name="libOpenCL.so"
android:required="false"/>
<activity
android:name=".MainActivity"
android:theme="@style/AppTheme.NoActionBar"
android:screenOrientation="unspecified"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".RPCWatchdogService"
android:process=":RPCWatchdogServiceProcess"
android:permission="android.permission.BIND_JOB_SERVICE" />
<activity
android:name=".RPCActivity"
android:process=":RPCProcess"
android:label="@string/rpc_name"
android:theme="@style/AppTheme.NoActionBar"
android:exported="false"
android:screenOrientation="unspecified">
</activity>
</application>
</manifest>
@@ -0,0 +1,160 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package org.apache.tvm.tvmrpc;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.widget.CompoundButton;
import android.widget.EditText;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.SwitchCompat;
import androidx.appcompat.widget.Toolbar;
public class MainActivity extends AppCompatActivity {
// wait time before automatic restart of RPC Activity
public static final int HANDLER_RESTART_DELAY = 5000;
private void showDialog(String title, String msg) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(title);
builder.setMessage(msg);
builder.setCancelable(true);
builder.setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
builder.create().show();
}
public Intent updateRPCPrefs() {
System.err.println("updating preferences...");
EditText edProxyAddress = findViewById(R.id.input_address);
EditText edProxyPort = findViewById(R.id.input_port);
EditText edAppKey = findViewById(R.id.input_key);
SwitchCompat inputSwitch = findViewById(R.id.switch_persistent);
final String proxyHost = edProxyAddress.getText().toString();
final int proxyPort = Integer.parseInt(edProxyPort.getText().toString());
final String key = edAppKey.getText().toString();
final boolean isChecked = inputSwitch.isChecked();
SharedPreferences pref =
getApplicationContext().getSharedPreferences("RPCProxyPreference", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = pref.edit();
editor.putString("input_address", proxyHost);
editor.putString("input_port", edProxyPort.getText().toString());
editor.putString("input_key", key);
editor.putBoolean("input_switch", isChecked);
editor.commit();
Intent intent = new Intent(this, RPCActivity.class);
intent.putExtra("host", proxyHost);
intent.putExtra("port", proxyPort);
intent.putExtra("key", key);
return intent;
}
private void setupRelaunch() {
final Context context = this;
final SwitchCompat switchPersistent = findViewById(R.id.switch_persistent);
final Runnable rPCStarter = new Runnable() {
public void run() {
if (switchPersistent.isChecked()) {
System.err.println("relaunching RPC activity...");
Intent intent = ((MainActivity) context).updateRPCPrefs();
startActivity(intent);
}
}
};
Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(rPCStarter, HANDLER_RESTART_DELAY);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
final Context context = this;
SwitchCompat switchPersistent = findViewById(R.id.switch_persistent);
switchPersistent.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
System.err.println("automatic RPC restart enabled...");
updateRPCPrefs();
setupRelaunch();
} else {
System.err.println("automatic RPC restart disabled...");
updateRPCPrefs();
}
}
});
enableInputView(true);
}
@Override
protected void onResume() {
System.err.println("MainActivity onResume...");
enableInputView(true);
setupRelaunch();
super.onResume();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
private void enableInputView(boolean enable) {
EditText edProxyAddress = findViewById(R.id.input_address);
EditText edProxyPort = findViewById(R.id.input_port);
EditText edAppKey = findViewById(R.id.input_key);
SwitchCompat input_switch = findViewById(R.id.switch_persistent);
edProxyAddress.setEnabled(enable);
edProxyPort.setEnabled(enable);
edAppKey.setEnabled(enable);
if (enable) {
SharedPreferences pref =
getApplicationContext().getSharedPreferences("RPCProxyPreference", Context.MODE_PRIVATE);
String inputAddress = pref.getString("input_address", null);
if (null != inputAddress)
edProxyAddress.setText(inputAddress);
String inputPort = pref.getString("input_port", null);
if (null != inputPort)
edProxyPort.setText(inputPort);
String inputKey = pref.getString("input_key", null);
if (null != inputKey)
edAppKey.setText(inputKey);
boolean isChecked = pref.getBoolean("input_switch", false);
input_switch.setChecked(isChecked);
}
}
}
@@ -0,0 +1,67 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package org.apache.tvm.tvmrpc;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
public class RPCActivity extends AppCompatActivity {
private RPCProcessor tvmServerWorker;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_rpc);
Button stopRPC = findViewById(R.id.button_stop_rpc);
stopRPC.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
System.err.println(tvmServerWorker == null);
if (tvmServerWorker != null) {
// currently will raise a socket closed exception
tvmServerWorker.disconnect();
}
finish();
// prevent Android from recycling the process
System.exit(0);
}
});
System.err.println("rpc activity onCreate...");
Intent intent = getIntent();
String host = intent.getStringExtra("host");
int port = intent.getIntExtra("port", 9090);
String key = intent.getStringExtra("key");
tvmServerWorker = new RPCProcessor(this);
tvmServerWorker.setDaemon(true);
tvmServerWorker.start();
tvmServerWorker.connect(host, port, key);
}
@Override
protected void onDestroy() {
System.err.println("rpc activity onDestroy");
tvmServerWorker.disconnect();
super.onDestroy();
android.os.Process.killProcess(android.os.Process.myPid());
}
}
@@ -0,0 +1,40 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package org.apache.tvm.tvmrpc;
import android.app.Activity;
import org.apache.tvm.rpc.RPCWatchdog;
/**
* Watchdog for Android RPC.
*/
public class RPCAndroidWatchdog extends RPCWatchdog {
public Activity rpc_activity = null;
public RPCAndroidWatchdog(Activity activity) {
super();
rpc_activity = activity;
}
/**
* Method to non-destructively terminate the running thread on Android
*/
@Override
protected void terminate() {
rpc_activity.finish();
}
}
@@ -0,0 +1,95 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package org.apache.tvm.tvmrpc;
import android.app.Activity;
import android.os.ParcelFileDescriptor;
import java.net.Socket;
import org.apache.tvm.rpc.ConnectTrackerServerProcessor;
/**
* Connect to RPC proxy and deal with requests.
*/
class RPCProcessor extends Thread {
private String host;
private int port;
private String key;
private boolean running = false;
private long startTime;
private ConnectTrackerServerProcessor currProcessor;
private boolean first = true;
private Activity rpc_activity = null;
public RPCProcessor(Activity activity) {
super();
rpc_activity = activity;
}
@Override
public void run() {
RPCAndroidWatchdog watchdog = new RPCAndroidWatchdog(rpc_activity);
watchdog.start();
while (true) {
synchronized (this) {
currProcessor = null;
while (!running) {
try {
this.wait();
} catch (InterruptedException e) {
}
}
try {
currProcessor = new ConnectTrackerServerProcessor(host, port, key, watchdog);
} catch (Throwable e) {
e.printStackTrace();
// kill if creating a new processor failed
System.exit(0);
}
}
if (currProcessor != null)
currProcessor.run();
watchdog.finishTimeout();
}
}
/**
* Disconnect from the proxy server.
*/
synchronized void disconnect() {
if (running) {
running = false;
if (currProcessor != null) {
currProcessor.terminate();
}
}
}
/**
* Start rpc processor and connect to the proxy server.
* @param host proxy server host.
* @param port proxy server port.
* @param key proxy server key.
*/
synchronized void connect(String host, int port, String key) {
this.host = host;
this.port = port;
this.key = key;
running = true;
this.notify();
}
}
@@ -0,0 +1,58 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
LOCAL_PATH := $(call my-dir)
MY_PATH := $(LOCAL_PATH)
include $(CLEAR_VARS)
LOCAL_PATH := $(MY_PATH)
ROOT_PATH := $(MY_PATH)/../../../../../..
ifndef config
ifneq ("$(wildcard ./config.mk)","")
config ?= config.mk
else
config ?= make/config.mk
endif
endif
include $(config)
LOCAL_SRC_FILES := org_apache_tvm_native_c_api.cc
LOCAL_LDFLAGS := -L$(SYSROOT)/usr/lib/ -llog
LOCAL_C_INCLUDES := $(ROOT_PATH)/include \
$(ROOT_PATH)/3rdparty/tvm-ffi/include \
$(ROOT_PATH)/3rdparty/tvm-ffi/3rdparty/dlpack/include \
$(ROOT_PATH)/3rdparty/OpenCL-Headers
LOCAL_MODULE = tvm4j_runtime_packed
LOCAL_CPP_FEATURES += exceptions
LOCAL_LDLIBS += -latomic
LOCAL_ARM_MODE := arm
ifdef ADD_C_INCLUDES
LOCAL_C_INCLUDES += $(ADD_C_INCLUDES)
endif
ifdef ADD_LDLIBS
LOCAL_LDLIBS += $(ADD_LDLIBS)
endif
include $(BUILD_SHARED_LIBRARY)
@@ -0,0 +1,50 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
ifndef config
ifneq ("$(wildcard ./config.mk)","")
config ?= config.mk
else
config ?= make/config.mk
endif
endif
include $(config)
# We target every architecture except armeabi here, for two reasons:
# 1) armeabi is deprecated in NDK r16 and removed in r17
# 2) vulkan is not supported in armeabi
APP_ABI ?= armeabi-v7a arm64-v8a x86 x86_64 mips
APP_STL := c++_shared
APP_CPPFLAGS += -DTVM4J_ANDROID=1 -std=c++17 -Oz -frtti
ifeq ($(USE_OPENCL), 1)
APP_CPPFLAGS += -DTVM_OPENCL_RUNTIME=1
endif
ifeq ($(USE_VULKAN), 1)
APP_CPPFLAGS += -DTVM_VULKAN_RUNTIME=1
APP_LDFLAGS += -lvulkan
endif
ifeq ($(USE_SORT), 1)
APP_CPPFLAGS += -DUSE_SORT=1
endif
ifeq ($(USE_RANDOM), 1)
APP_CPPFLAGS += -DUSE_RANDOM=1
endif
@@ -0,0 +1,111 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/*!
* \file tvm_runtime.h
* \brief Pack all tvm runtime source files
*/
#include <sys/stat.h>
#include <fstream>
/* Enable custom logging - this will cause TVM to use a custom implementation
* of tvm::runtime::detail::LogMessage. We use this to pass TVM log messages to
* Android logcat.
*/
#define TVM_LOG_CUSTOMIZE 1
#define TVM_FFI_USE_LIBBACKTRACE 0
#include "../3rdparty/tvm-ffi/src/ffi/backtrace.cc"
#include "../3rdparty/tvm-ffi/src/ffi/container.cc"
#include "../3rdparty/tvm-ffi/src/ffi/dtype.cc"
#include "../3rdparty/tvm-ffi/src/ffi/error.cc"
#include "../3rdparty/tvm-ffi/src/ffi/extra/library_module.cc"
#include "../3rdparty/tvm-ffi/src/ffi/extra/library_module_dynamic_lib.cc"
#include "../3rdparty/tvm-ffi/src/ffi/extra/library_module_system_lib.cc"
#include "../3rdparty/tvm-ffi/src/ffi/extra/module.cc"
#include "../3rdparty/tvm-ffi/src/ffi/function.cc"
#include "../3rdparty/tvm-ffi/src/ffi/object.cc"
#include "../3rdparty/tvm-ffi/src/ffi/tensor.cc"
#include "../src/runtime/cpu_device_api.cc"
#include "../src/runtime/device_api.cc"
#include "../src/runtime/file_utils.cc"
#include "../src/runtime/logging.cc"
#include "../src/runtime/memory/memory_manager.cc"
#include "../src/runtime/registry.cc"
#include "../src/runtime/rpc/rpc_channel.cc"
#include "../src/runtime/rpc/rpc_endpoint.cc"
#include "../src/runtime/rpc/rpc_event_impl.cc"
#include "../src/runtime/rpc/rpc_local_session.cc"
#include "../src/runtime/rpc/rpc_module.cc"
#include "../src/runtime/rpc/rpc_server_env.cc"
#include "../src/runtime/rpc/rpc_session.cc"
#include "../src/runtime/rpc/rpc_socket_impl.cc"
#include "../src/runtime/tensor.cc"
#include "../src/runtime/thread_pool.cc"
#include "../src/runtime/threading_backend.cc"
#include "../src/runtime/timer.cc"
#include "../src/runtime/workspace_pool.cc"
#ifdef TVM_OPENCL_RUNTIME
#include "../src/runtime/opencl/opencl_device_api.cc"
#include "../src/runtime/opencl/opencl_module.cc"
#include "../src/runtime/opencl/opencl_wrapper/opencl_wrapper.cc"
#include "../src/runtime/source_utils.cc"
#endif
#ifdef TVM_VULKAN_RUNTIME
#include "../src/runtime/vulkan/vulkan_amdrgp.cc"
#include "../src/runtime/vulkan/vulkan_buffer.cc"
#include "../src/runtime/vulkan/vulkan_common.cc"
#include "../src/runtime/vulkan/vulkan_device.cc"
#include "../src/runtime/vulkan/vulkan_device_api.cc"
#include "../src/runtime/vulkan/vulkan_instance.cc"
#include "../src/runtime/vulkan/vulkan_module.cc"
#include "../src/runtime/vulkan/vulkan_stream.cc"
#include "../src/runtime/vulkan/vulkan_wrapped_func.cc"
#endif
#ifdef USE_SORT
#include "../src/runtime/extra/contrib/sort/sort.cc"
#endif
#ifdef USE_RANDOM
#include "../src/runtime/extra/contrib/random/random.cc"
#endif
#include <android/log.h>
namespace tvm {
namespace runtime {
namespace detail {
// Override logging mechanism
[[noreturn]] void LogFatalImpl(const std::string& file, int lineno, const std::string& message) {
std::string m = file + ":" + std::to_string(lineno) + ": " + message;
__android_log_write(ANDROID_LOG_FATAL, "TVM_RUNTIME", m.c_str());
throw tvm::ffi::Error("InternalError", message, TVMFFIBacktrace(file.c_str(), lineno, "", 0));
}
void LogMessageImpl(const std::string& file, int lineno, int level, const std::string& message) {
std::string m = file + ":" + std::to_string(lineno) + ": " + message;
__android_log_write(ANDROID_LOG_DEBUG + level, "TVM_RUNTIME", m.c_str());
}
} // namespace detail
} // namespace runtime
} // namespace tvm
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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.
-->
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="org.apache.tvm.tvmrpc.MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:theme="@style/AppTheme.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>
<include layout="@layout/content_main"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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.
-->
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="org.apache.tvm.tvmrpc.RPCActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:theme="@style/AppTheme.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>
<include layout="@layout/content_rpc"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
@@ -0,0 +1,106 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:showIn="@layout/activity_main">
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_address"/>
<EditText
android:id="@+id/input_address"
android:hint="@string/input_address"
android:autofillHints="@string/label_address_hint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="phone"
android:background="@android:drawable/editbox_background"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_port"/>
<EditText
android:id="@+id/input_port"
android:hint="@string/input_port"
android:autofillHints="@string/label_port_hint"
android:minWidth="100dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="phone"
android:background="@android:drawable/editbox_background"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_key"/>
<EditText
android:id="@+id/input_key"
android:hint="@string/input_key"
android:autofillHints="@string/label_key_hint"
android:inputType="text"
android:minWidth="100dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:drawable/editbox_background"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_persistent"/>
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/switch_persistent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:switchMinWidth="55dp"
android:paddingLeft="10dip"
android:paddingRight="10dip"
android:checked="false"
android:textOff="@string/switch_off"
android:textOn="@string/switch_on" />
</LinearLayout>
</LinearLayout>
@@ -0,0 +1,34 @@
<?xml version='1.0'?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:showIn="@layout/activity_rpc">
<Button
android:id="@+id/button_stop_rpc"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="@string/stop_rpc" />
</LinearLayout>
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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.
-->
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#06d467</color>
</resources>
@@ -0,0 +1,43 @@
<?xml version='1.0'?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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.
-->
<resources>
<string name="app_name">TVM RPC</string>
<string name="rpc_name">RPC</string>
<string name="input_address">Enter the tracker server address</string>
<string name="input_port">Enter the tracker server port</string>
<string name="input_key">Enter the app connection key</string>
<string name="label_address">Address</string>
<string name="label_port">Port</string>
<string name="label_key">Key</string>
<string name="label_persistent">Enable RPC</string>
<string name="label_address_hint">192.168.1.1</string>
<string name="label_port_hint">9190</string>
<string name="label_key_hint">android</string>
<string name="switch_on">Enabled</string>
<string name="switch_off">Disabled</string>
<string name="stop_rpc">Stop RPC</string>
</resources>
@@ -0,0 +1,37 @@
<?xml version='1.0'?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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.
-->
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
</resources>
+50
View File
@@ -0,0 +1,50 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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.
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
gradlePluginPortal()
maven {
url 'https://maven.google.com'
}
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.4.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
gradlePluginPortal()
maven {
url 'https://maven.google.com'
}
mavenLocal()
mavenCentral()
google()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
+19
View File
@@ -0,0 +1,19 @@
#!/bin/bash
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
CURR_DIR=$(cd `dirname $0`; pwd)
keytool -genkey -keystore $CURR_DIR/tvmrpc.keystore -alias tvmrpc -keyalg RSA -validity 10000
+23
View File
@@ -0,0 +1,23 @@
#!/bin/bash
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
CURR_DIR=$(cd `dirname $0`; pwd)
APK_DIR=$CURR_DIR/../app/build/outputs/apk/release
UNSIGNED_APK=$APK_DIR/app-release-unsigned.apk
SIGNED_APK=$APK_DIR/tvmrpc-release.apk
jarsigner -verbose -keystore $CURR_DIR/tvmrpc.keystore -signedjar $SIGNED_APK $UNSIGNED_APK 'tvmrpc'
echo $SIGNED_APK
+19
View File
@@ -0,0 +1,19 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
android.useAndroidX=true
android.enableJetifier=true
+18
View File
@@ -0,0 +1,18 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
include ':app'
@@ -0,0 +1,86 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
# ruff: noqa: F821
"""Testcode for Android RPC.
To use it, start an RPC tracker with "python -m tvm.exec.rpc_tracker".
Use the tracker's address and port when configuring the RPC app.
Use "android" as the key if you wish to avoid modifying this script.
"""
import os
import numpy as np
import tvm
from tvm import rpc, te
from tvm.support import ndk, utils
# Set to be address of tvm proxy.
tracker_host = os.environ["TVM_TRACKER_HOST"]
tracker_port = int(os.environ["TVM_TRACKER_PORT"])
key = "android"
# Change target configuration.
# Run `adb shell cat /proc/cpuinfo` to find the arch.
arch = "arm64"
target = {"kind": "llvm", "mtriple": f"{arch}-linux-android"}
# whether enable to execute test on OpenCL target
test_opencl = False
# whether enable to execute test on Vulkan target
test_vulkan = False
def test_rpc_module():
# graph
n = tvm.runtime.convert(1024)
A = te.placeholder((n,), name="A")
B = te.compute(A.shape, lambda *i: A(*i) + 1.0, name="B")
a_np = np.random.uniform(size=1024).astype(A.dtype)
temp = utils.tempdir()
# Establish remote connection with target hardware
tracker = rpc.connect_tracker(tracker_host, tracker_port)
remote = tracker.request(key, priority=0, session_timeout=60)
mod = tvm.IRModule.from_expr(te.create_prim_func([A, B]).with_attr("global_symbol", "myadd"))
sch = tvm.s_tir.Schedule(mod)
(x,) = sch.get_loops(block=sch.get_sblock("B"))
xo, xi = sch.split(i, [None, 32])
sch.bind(xo, "blockIdx.x")
sch.bind(xi, "threadIdx.x")
if test_opencl:
f = tvm.compile(sch.mod, target=tvm.target.Target("opencl", host=target))
path_dso_cl = temp.relpath("dev_lib_cl.so")
f.export_library(path_dso_cl, fcompile=ndk.create_shared)
print("Run GPU(OpenCL Flavor) test ...")
dev = remote.cl(0)
remote.upload(path_dso_cl)
f1 = remote.load_module("dev_lib_cl.so")
a = tvm.runtime.tensor(a_np, dev)
b = tvm.runtime.tensor(np.zeros(1024, dtype=A.dtype), dev)
time_f = f1.time_evaluator(f1.entry_name, dev, number=10)
cost = time_f(a, b).mean
print(f"{cost:g} secs/op\n")
np.testing.assert_equal(b.numpy(), a.numpy() + 1)
if __name__ == "__main__":
test_rpc_module()
+82
View File
@@ -0,0 +1,82 @@
cmake_policy(SET CMP0069 NEW) # suppress CMake warning about IPO
set(TVM_RPC_SOURCES
main.cc
rpc_env.cc
rpc_server.cc
)
set(TVM_RPC_LINKER_LIBS "")
if(WIN32)
list(APPEND TVM_RPC_SOURCES win32_process.cc)
endif()
# Set output to same directory as the other TVM libs
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
add_executable(tvm_rpc ${TVM_RPC_SOURCES})
include(CheckIPOSupported)
check_ipo_supported(RESULT result OUTPUT output)
if(result)
set_property(TARGET tvm_rpc PROPERTY INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE)
endif()
if(WIN32)
target_compile_definitions(tvm_rpc PUBLIC -DNOMINMAX)
endif()
if (OS)
if (OS STREQUAL "Linux")
set_property(TARGET tvm_rpc PROPERTY LINK_FLAGS -lpthread)
endif()
endif()
if(USE_OPENCL)
if (ANDROID_ABI)
if(DEFINED ENV{ANDROID_NDK_MAJOR})
if($ENV{ANDROID_NDK_MAJOR} VERSION_LESS "23")
set_property(TARGET tvm_rpc PROPERTY LINK_FLAGS -fuse-ld=gold)
endif()
endif()
endif()
endif()
target_include_directories(
tvm_rpc
PUBLIC "../../include"
)
target_link_libraries(tvm_rpc PUBLIC tvm_ffi_header)
if (BUILD_FOR_ANDROID AND USE_HEXAGON)
get_hexagon_sdk_property("${USE_HEXAGON_SDK}" "${USE_HEXAGON_ARCH}"
DSPRPC_LIB DSPRPC_LIB_DIRS
)
if(DSPRPC_LIB_DIRS)
link_directories(${DSPRPC_LIB_DIRS})
else()
message(WARNING "Could not locate some Hexagon SDK components")
endif()
list(APPEND TVM_RPC_LINKER_LIBS cdsprpc log)
endif()
if(BUILD_STATIC_RUNTIME)
foreach(lib ${TVM_RUNTIME_BACKEND_LIBS})
if(MSVC)
list(APPEND TVM_RPC_LINKER_LIBS "/WHOLEARCHIVE:$<TARGET_FILE:${lib}>")
elseif(APPLE)
list(APPEND TVM_RPC_LINKER_LIBS "-Wl,-force_load,$<TARGET_FILE:${lib}>")
else()
list(APPEND TVM_RPC_LINKER_LIBS "-Wl,--whole-archive" "${lib}" "-Wl,--no-whole-archive")
endif()
endforeach()
else()
if(NOT MSVC AND NOT APPLE)
list(APPEND TVM_RPC_LINKER_LIBS tvm_runtime "-Wl,--no-as-needed" ${TVM_RUNTIME_BACKEND_LIBS} "-Wl,--as-needed")
else()
list(APPEND TVM_RPC_LINKER_LIBS tvm_runtime ${TVM_RUNTIME_BACKEND_LIBS})
endif()
endif()
target_link_libraries(tvm_rpc PRIVATE ${TVM_RPC_LINKER_LIBS})
+83
View File
@@ -0,0 +1,83 @@
<!--- Licensed to the Apache Software Foundation (ASF) under one -->
<!--- or more contributor license agreements. See the NOTICE file -->
<!--- distributed with this work for additional information -->
<!--- regarding copyright ownership. The ASF licenses this file -->
<!--- to you 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. -->
# TVM RPC Server
This folder contains a simple recipe to make RPC server in c++.
## Usage (Non-Windows)
- Configure the tvm CMake build with `config.cmake` ensuring that `USE_CPP_RPC` is set to `ON` in the config.
- If cross compiling for Android, add the following options to the CMake config or specify them when invoking CMake:
```
# Whether to build the C++ RPC server binary
set(USE_CPP_RPC ON)
# Path to the Android NDK CMake toolchain
set(CMAKE_TOOLCHAIN_FILE $ENV{ANDROID_NDK}/build/cmake/android.toolchain.cmake)
# The Android ABI and platform to target
set(ANDROID_ABI "arm64-v8a")
set(ANDROID_PLATFORM android-28)
```
- Similarly, if cross compiling for embedded Linux add the following options to CMake config:
```
# Needed to ensure pthread is linked
set(OS Linux)
# Path to the desired C++ cross compiler
set(CMAKE_CXX_COMPILER /path/to/cross/compiler/executable)
```
- If you need to build cpp_rpc with OpenCL support, specify variable `USE_OPENCL` in the config:
```
set(USE_OPENCL ON)
```
In this case [OpenCL-wrapper](../../src/runtime/opencl/opencl_wrapper) or OpenCL installed to your system will be used.
When OpenCL-wrapper is used, it will dynamically load OpenCL library on the device.
If the device doesn't have OpenCL library on it, then you'll see in the runtime that OpenCL library cannot be opened.
If linking against a custom device OpenCL library is needed, in the config specify the path to the OpenCL SDK containing the include/CL headers and lib/ or lib64/libOpenCL.so:
```
set(USE_OPENCL /path/to/opencl-sdk)
```
- From within the configured tvm build directory, compile `tvm_runtime` and the `tvm_rpc` server:
```
cmake --build $TVM_ROOT/build --target tvm_runtime tvm_rpc -j$(nproc)
```
- Use `./tvm_rpc server` to start the RPC server
## Usage (Windows)
- Configure the tvm CMake build with `config.cmake` ensuring that `USE_CPP_RPC` is set to `ON` in the config.
- Install [LLVM pre-build binaries](https://releases.llvm.org/download.html), making sure to select the option to add it to the PATH.
- Verify Python 3.6 or newer is installed and in the PATH.
- Use `<tvm_output_dir>\tvm_rpc.exe` to start the RPC server
## How it works
- The tvm runtime dll is linked along with this executable and when the RPC server starts it will load the tvm runtime library.
```
Command line usage
server - Start the server
--host - The listen address of the server, Default=0.0.0.0 (any)
--port - The port of the RPC server, Default=9090
--port-end - The end search port of the RPC server, Default=9099
--tracker - The RPC tracker address in host:port format e.g. 10.1.1.2:9190 Default=""
--key - The key used to identify the device type in tracker. Default=""
--custom-addr - Custom IP Address to Report to RPC Tracker. Default=""
--silent - Whether to run in silent mode. Default=False
Example
./tvm_rpc server --host=0.0.0.0 --port=9090 --port-end=9099 --tracker=127.0.0.1:9190 --key=rasp
```
## Note
Currently support is only there for Linux / Android / Windows environment and proxy mode isn't supported currently.
+311
View File
@@ -0,0 +1,311 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/*!
* \file rpc_server.cc
* \brief RPC Server for TVM.
*/
#include <csignal>
#include <cstdio>
#include <cstdlib>
#if defined(__linux__) || defined(__ANDROID__)
#include <unistd.h>
#endif
#include <tvm/runtime/logging.h>
#include <cstring>
#include <iostream>
#include <sstream>
#include <vector>
#include "../../src/support/socket.h"
#include "../../src/support/utils.h"
#include "rpc_server.h"
#if defined(_WIN32)
#include "win32_process.h"
#endif
using namespace std;
using namespace tvm::runtime;
using namespace tvm::support;
static const string kUsage =
"Command line usage\n"
" server - Start the server\n"
"--host - The listen address of the server, Default=0.0.0.0 (any)\n"
"--port - The port of the RPC server, Default=9090\n"
"--port-end - The end search port of the RPC server, Default=9099\n"
"--tracker - The RPC tracker address in host:port format e.g. 10.1.1.2:9190 Default=\"\"\n"
"--key - The key used to identify the device type in tracker. Default=\"\"\n"
"--custom-addr - Custom IP Address to Report to RPC Tracker. Default=\"\"\n"
"--work-dir - Custom work directory. Default=\"\"\n"
"--silent - Whether to run in silent mode. Default=False\n"
"\n"
" Example\n"
" ./tvm_rpc server --host=0.0.0.0 --port=9090 --port-end=9099 "
" --tracker=127.0.0.1:9190 --key=rasp"
"\n";
/*!
* \brief RpcServerArgs.
* \arg host The listen address of the server, Default=0.0.0.0 (any)
* \arg port The port of the RPC server, Default=9090
* \arg port_end The end search port of the RPC server, Default=9099
* \arg tracker The address of RPC tracker in host:port format e.g. 10.77.1.234:9190 Default=""
* \arg key The key used to identify the device type in tracker. Default=""
* \arg custom_addr Custom IP Address to Report to RPC Tracker. Default=""
* \arg work_dir Custom work directory. Default=""
* \arg silent Whether run in silent mode. Default=False
*/
struct RpcServerArgs {
string host = "0.0.0.0";
int port = 9090;
int port_end = 9099;
string tracker;
string key;
string custom_addr;
string work_dir;
bool silent = false;
#if defined(WIN32)
std::string mmap_path;
#endif
};
/*!
* \brief PrintArgs print the contents of RpcServerArgs
* \param args RpcServerArgs structure
*/
void PrintArgs(const RpcServerArgs& args) {
LOG(INFO) << "host = " << args.host;
LOG(INFO) << "port = " << args.port;
LOG(INFO) << "port_end = " << args.port_end;
LOG(INFO) << "tracker = " << args.tracker;
LOG(INFO) << "key = " << args.key;
LOG(INFO) << "custom_addr = " << args.custom_addr;
LOG(INFO) << "work_dir = " << args.work_dir;
LOG(INFO) << "silent = " << ((args.silent) ? ("True") : ("False"));
}
#if defined(__linux__) || defined(__ANDROID__)
/*!
* \brief CtrlCHandler, exits if Ctrl+C is pressed
* \param s signal
*/
void CtrlCHandler(int s) {
LOG(INFO) << "\nUser pressed Ctrl+C, Exiting";
exit(1);
}
/*!
* \brief HandleCtrlC Register for handling Ctrl+C event.
*/
void HandleCtrlC() {
// Ctrl+C handler
struct sigaction sigIntHandler;
sigIntHandler.sa_handler = CtrlCHandler;
sigemptyset(&sigIntHandler.sa_mask);
sigIntHandler.sa_flags = 0;
sigaction(SIGINT, &sigIntHandler, nullptr);
}
#endif
/*!
* \brief GetCmdOption Parse and find the command option.
* \param argc arg counter
* \param argv arg values
* \param option command line option to search for.
* \param key whether the option itself is key
* \return value corresponding to option.
*/
string GetCmdOption(int argc, char* argv[], string option, bool key = false) {
string cmd;
for (int i = 1; i < argc; ++i) {
string arg = argv[i];
if (arg.find(option) == 0) {
if (key) {
cmd = argv[i];
return cmd;
}
// We assume "=" is the end of option.
TVM_FFI_ICHECK_EQ(*option.rbegin(), '=');
cmd = arg.substr(arg.find('=') + 1);
return cmd;
}
}
return cmd;
}
/*!
* \brief ValidateTracker Check the tracker address format is correct and changes the format.
* \param tracker The tracker input.
* \return result of operation.
*/
bool ValidateTracker(string& tracker) {
vector<string> list = Split(tracker, ':');
if ((list.size() != 2) || (!ValidateIP(list[0])) || (!IsNumber(list[1]))) {
return false;
}
ostringstream ss;
ss << "('" << list[0] << "', " << list[1] << ")";
tracker = ss.str();
return true;
}
/*!
* \brief ParseCmdArgs parses the command line arguments.
* \param argc arg counter
* \param argv arg values
* \param args the output structure which holds the parsed values
*/
void ParseCmdArgs(int argc, char* argv[], struct RpcServerArgs& args) {
const string silent = GetCmdOption(argc, argv, "--silent", true);
if (!silent.empty()) {
args.silent = true;
}
const string host = GetCmdOption(argc, argv, "--host=");
if (!host.empty()) {
if (!ValidateIP(host)) {
LOG(WARNING) << "Wrong host address format.";
LOG(INFO) << kUsage;
exit(1);
}
args.host = host;
}
const string port = GetCmdOption(argc, argv, "--port=");
if (!port.empty()) {
if (!IsNumber(port) || stoi(port) > 65535) {
LOG(WARNING) << "Wrong port number.";
LOG(INFO) << kUsage;
exit(1);
}
args.port = stoi(port);
}
const string port_end = GetCmdOption(argc, argv, "--port-end=");
if (!port_end.empty()) {
if (!IsNumber(port_end) || stoi(port_end) > 65535) {
LOG(WARNING) << "Wrong port-end number.";
LOG(INFO) << kUsage;
exit(1);
}
args.port_end = stoi(port_end);
}
string tracker = GetCmdOption(argc, argv, "--tracker=");
if (!tracker.empty()) {
if (!ValidateTracker(tracker)) {
LOG(WARNING) << "Wrong tracker address format.";
LOG(INFO) << kUsage;
exit(1);
}
args.tracker = tracker;
}
const string key = GetCmdOption(argc, argv, "--key=");
if (!key.empty()) {
args.key = key;
}
const string custom_addr = GetCmdOption(argc, argv, "--custom-addr=");
if (!custom_addr.empty()) {
if (!ValidateIP(custom_addr)) {
LOG(WARNING) << "Wrong custom address format.";
LOG(INFO) << kUsage;
exit(1);
}
args.custom_addr = custom_addr;
}
#if defined(WIN32)
const string mmap_path = GetCmdOption(argc, argv, "--child_proc=");
if (!mmap_path.empty()) {
args.mmap_path = mmap_path;
}
#endif
const string work_dir = GetCmdOption(argc, argv, "--work-dir=");
if (!work_dir.empty()) {
args.work_dir = work_dir;
}
}
/*!
* \brief RpcServer Starts the RPC server.
* \param argc arg counter
* \param argv arg values
* \return result of operation.
*/
int RpcServer(int argc, char* argv[]) {
RpcServerArgs args;
/* parse the command line args */
ParseCmdArgs(argc, argv, args);
PrintArgs(args);
LOG(INFO) << "Starting CPP Server, Press Ctrl+C to stop.";
#if defined(__linux__) || defined(__ANDROID__)
// Ctrl+C handler
HandleCtrlC();
#endif
#if defined(WIN32)
if (!args.mmap_path.empty()) {
int ret = 0;
try {
ChildProcSocketHandler(args.mmap_path);
} catch (const std::exception&) {
ret = -1;
}
return ret;
}
#endif
RPCServerCreate(args.host, args.port, args.port_end, args.tracker, args.key, args.custom_addr,
args.work_dir, args.silent);
return 0;
}
/*!
* \brief main The main function.
* \param argc arg counter
* \param argv arg values
* \return result of operation.
*/
int main(int argc, char* argv[]) {
if (argc <= 1) {
LOG(INFO) << kUsage;
return 0;
}
// Runs WSAStartup on Win32, no-op on POSIX
Socket::Startup();
#if defined(_WIN32)
SetEnvironmentVariableA("CUDA_CACHE_DISABLE", "1");
#endif
if (0 == strcmp(argv[1], "server")) {
return RpcServer(argc, argv);
}
LOG(INFO) << kUsage;
return 0;
}
+288
View File
@@ -0,0 +1,288 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/*!
* \file rpc_env.cc
* \brief Server environment of the RPC.
*/
#include "rpc_env.h"
#include <tvm/ffi/extra/module.h>
#include <tvm/ffi/function.h>
#include <tvm/runtime/logging.h>
#include <filesystem>
#include <fstream>
#include <string>
#include <system_error>
#include <vector>
#include "../../src/support/utils.h"
namespace {
std::string GenerateUntarCommand(const std::string& tar_file, const std::string& output_dir) {
std::string untar_cmd;
untar_cmd.reserve(512);
#if defined(__linux__) || defined(__ANDROID__) || defined(__APPLE__)
untar_cmd += "tar -C ";
untar_cmd += output_dir;
untar_cmd += " -zxf ";
untar_cmd += tar_file;
#elif defined(_WIN32)
untar_cmd += "python -m tarfile -e ";
untar_cmd += tar_file;
untar_cmd += " ";
untar_cmd += output_dir;
#endif
return untar_cmd;
}
} // Anonymous namespace
namespace tvm {
namespace runtime {
RPCEnv::RPCEnv(const std::string& wd) {
std::error_code ec;
if (!wd.empty()) {
base_ = wd + "/.cache";
std::filesystem::create_directories(base_, ec);
if (ec) {
LOG(WARNING) << "Failed to create directory " << base_ << " : " << ec.message();
}
} else {
#if defined(ANDROID) || defined(__ANDROID__)
std::string pkg_name;
if (std::ifstream cmdline("/proc/self/cmdline"); cmdline) {
std::getline(cmdline, pkg_name, '\0');
}
std::string android_base_ = "/data/data/" + pkg_name + "/cache";
// Check if application data directory exist. If not exist, usually means we run tvm_rpc from
// adb shell terminal.
if (!std::filesystem::is_directory(android_base_)) {
// Tmp directory is always writable for 'shell' user.
android_base_ = "/data/local/tmp";
}
base_ = android_base_ + "/rpc";
#elif !defined(_WIN32)
base_ = std::filesystem::current_path(ec).string() + "/rpc";
if (ec) {
base_ = "./rpc";
}
#else
base_ = "./rpc";
#endif
std::filesystem::create_directories(base_, ec);
if (ec) {
LOG(WARNING) << "Failed to create directory " << base_ << " : " << ec.message();
}
}
std::filesystem::permissions(base_, std::filesystem::perms::all,
std::filesystem::perm_options::replace, ec);
if (ec) {
LOG(WARNING) << "Failed to grant permissions to " << base_ << " : " << ec.message();
}
ffi::Function::SetGlobal(
"tvm.rpc.server.workpath",
ffi::Function::FromTyped([this](const std::string& path) { return this->GetPath(path); }));
ffi::Function::SetGlobal("tvm.rpc.server.listdir",
ffi::Function::FromTyped([this](const std::string& path) {
std::string dir = this->GetPath(path);
std::ostringstream os;
for (auto d : ListDir(dir)) {
os << d << ",";
}
return os.str();
}));
ffi::Function::SetGlobal("tvm.rpc.server.load_module",
ffi::Function::FromTyped([this](const std::string& path) {
std::string file_name = this->GetPath(path);
file_name = BuildSharedLibrary(file_name);
LOG(INFO) << "Load module from " << file_name << " ...";
return ffi::Module::LoadFromFile(file_name);
}));
ffi::Function::SetGlobal("tvm.rpc.server.download_linked_module",
ffi::Function::FromTyped([this](const std::string& path) {
std::string file_name = this->GetPath(path);
file_name = BuildSharedLibrary(file_name);
std::string bin;
std::ifstream fs(file_name, std::ios::in | std::ios::binary);
TVM_FFI_ICHECK(!fs.fail()) << "Cannot open " << file_name;
fs.seekg(0, std::ios::end);
size_t size = static_cast<size_t>(fs.tellg());
fs.seekg(0, std::ios::beg);
bin.resize(size);
fs.read(bin.data(), size);
LOG(INFO) << "Send linked module " << file_name << " to client";
return ffi::Bytes(bin);
}));
}
/*!
* \brief GetPath To get the work path from packed function
* \param file_name The file name
* \return The full path of file.
*/
std::string RPCEnv::GetPath(const std::string& file_name) const {
// we assume file_name starts with "/" means file_name is the exact path
// and does not create /.rpc/
return !file_name.empty() && file_name[0] == '/' ? file_name : base_ + "/" + file_name;
}
/*!
* \brief Remove The RPC Environment cleanup function
*/
void RPCEnv::CleanUp() const {
std::error_code ec;
std::filesystem::remove_all(base_, ec);
if (ec) {
LOG(WARNING) << "Cleanup " << base_ << " failed: " << ec.message();
}
}
/*!
* \brief ListDir get the list of files in a directory
* \param dirname The root directory name
* \return vector Files in directory.
*/
std::vector<std::string> ListDir(const std::string& dirname) {
std::error_code ec;
std::vector<std::string> vec;
auto iter = std::filesystem::directory_iterator(dirname, ec);
if (ec) {
if (ec == std::errc::no_such_file_or_directory) return vec;
TVM_FFI_THROW(InternalError) << "ListDir " << dirname << " error: " << ec.message();
}
for (const auto& entry : iter) {
vec.push_back(entry.path().generic_string());
}
return vec;
}
#if defined(__linux__) || defined(__ANDROID__) || defined(__APPLE__)
/*!
* \brief LinuxShared Creates a linux shared library
* \param output The output file name
* \param files The files for building
* \param options The compiler options
* \param cc The compiler
*/
void LinuxShared(const std::string output, const std::vector<std::string>& files,
std::string options = "", std::string cc = "g++") {
std::string cmd = cc;
cmd += " -shared -fPIC ";
cmd += " -o " + output;
for (auto f = files.begin(); f != files.end(); ++f) {
cmd += " " + *f;
}
cmd += " " + options;
std::string err_msg;
auto executed_status = support::Execute(cmd, &err_msg);
if (executed_status) {
TVM_FFI_THROW(InternalError) << err_msg;
}
}
#endif
#ifdef _WIN32
/*!
* \brief WindowsShared Creates a Windows shared library
* \param output The output file name
* \param files The files for building
* \param options The compiler options
* \param cc The compiler
*/
void WindowsShared(const std::string& output, const std::vector<std::string>& files,
const std::string& options = "", const std::string& cc = "clang") {
std::string cmd = cc;
cmd += " -O2 -flto=full -fuse-ld=lld-link -shared ";
cmd += " -o " + output;
for (const auto& file : files) {
cmd += " " + file;
}
cmd += " " + options;
std::string err_msg;
const auto executed_status = support::Execute(cmd, &err_msg);
if (executed_status) {
TVM_FFI_THROW(InternalError) << err_msg;
}
}
#endif
/*!
* \brief CreateShared Creates a shared library
* \param output The output file name
* \param files The files for building
*/
void CreateShared(const std::string& output, const std::vector<std::string>& files) {
#if defined(__linux__) || defined(__ANDROID__) || defined(__APPLE__)
LinuxShared(output, files);
#elif defined(_WIN32)
WindowsShared(output, files);
#else
TVM_FFI_THROW(InternalError) << "Operating system not supported";
#endif
}
std::string BuildSharedLibrary(std::string file) {
if (support::EndsWith(file, ".so") || support::EndsWith(file, ".dll") ||
support::EndsWith(file, ".dylib")) {
return file;
}
std::string file_name = file + ".so";
if (support::EndsWith(file, ".o")) {
CreateShared(file_name, {file});
} else if (support::EndsWith(file, ".tar")) {
const std::string tmp_dir = "./rpc/tmp/";
std::error_code ec;
std::filesystem::create_directories(tmp_dir, ec);
if (ec) {
LOG(WARNING) << "Failed to create directory " << tmp_dir << " : " << ec.message();
}
std::filesystem::permissions(tmp_dir, std::filesystem::perms::all,
std::filesystem::perm_options::replace, ec);
if (ec) {
LOG(WARNING) << "Failed to grant permissions to " << tmp_dir << " : " << ec.message();
}
const std::string cmd = GenerateUntarCommand(file, tmp_dir);
std::string err_msg;
const int executed_status = support::Execute(cmd, &err_msg);
if (executed_status) {
TVM_FFI_THROW(InternalError) << err_msg;
}
CreateShared(file_name, ListDir(tmp_dir));
std::filesystem::remove_all(tmp_dir, ec);
if (ec) {
LOG(WARNING) << "Remove " << tmp_dir << " failed: " << ec.message();
}
} else {
file_name = file;
}
return file_name;
}
} // namespace runtime
} // namespace tvm
+83
View File
@@ -0,0 +1,83 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/*!
* \file rpc_env.h
* \brief Server environment of the RPC.
*/
#ifndef TVM_APPS_CPP_RPC_ENV_H_
#define TVM_APPS_CPP_RPC_ENV_H_
#include <tvm/ffi/function.h>
#include <string>
namespace tvm {
namespace runtime {
/*!
* \brief ListDir Get the list of files in a directory
* \param dirname The root directory name
* \return vector Files in directory.
*/
std::vector<std::string> ListDir(const std::string& dirname);
/*!
* \brief build a shared library if necessary
*
* This function will automatically call
* cc.create_shared if the path is in format .o or .tar
* High level handling for .o and .tar file.
* We support this to be consistent with RPC module load.
* \param file_in The input file path.
*
* \return The name of the shared library.
*/
std::string BuildSharedLibrary(std::string file_in);
/*!
* \brief RPCEnv The RPC Environment parameters for c++ rpc server
*/
struct RPCEnv {
public:
/*!
* \brief Constructor Init The RPC Environment initialize function
*/
RPCEnv(const std::string& word_dir = "");
/*!
* \brief GetPath To get the workpath from packed function
* \param name The file name
* \return The full path of file.
*/
std::string GetPath(const std::string& file_name) const;
/*!
* \brief The RPC Environment cleanup function
*/
void CleanUp() const;
private:
/*!
* \brief Holds the environment path.
*/
std::string base_;
}; // RPCEnv
} // namespace runtime
} // namespace tvm
#endif // TVM_APPS_CPP_RPC_ENV_H_
+394
View File
@@ -0,0 +1,394 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/*!
* \file rpc_server.cc
* \brief RPC Server implementation.
*/
#include <tvm/ffi/function.h>
#include <tvm/ffi/reflection/registry.h>
#if defined(__linux__) || defined(__ANDROID__) || defined(__APPLE__)
#include <signal.h>
#include <sys/select.h>
#include <sys/wait.h>
#endif
#include <chrono>
#include <future>
#include <iostream>
#include <set>
#include <string>
#include <thread>
#include <utility>
#include "../../src/runtime/rpc/rpc_endpoint.h"
#include "../../src/runtime/rpc/rpc_socket_impl.h"
#include "../../src/support/socket.h"
#include "rpc_env.h"
#include "rpc_server.h"
#include "rpc_tracker_client.h"
#if defined(_WIN32)
#include "win32_process.h"
#endif
using namespace std::chrono;
namespace tvm {
namespace runtime {
#ifdef __ANDROID__
static std::string getNextString(std::stringstream* iss) {
std::string str = iss->str();
size_t start = iss->tellg();
size_t len = str.size();
// Skip leading spaces.
while (start < len && isspace(str[start])) start++;
size_t end = start;
while (end < len && !isspace(str[end])) end++;
iss->seekg(end);
return str.substr(start, end - start);
}
#endif
/*!
* \brief RPCServer RPC Server class.
*
* \param host The listen address of the server, Default=0.0.0.0 (any)
*
* \param port_search_start The low end of the search range for an
* available port for the RPC, Default=9090
*
* \param port_search_end The high search the search range for an
* available port for the RPC, Default=9099
*
* \param tracker The address of RPC tracker in host:port format
* (e.g. "10.77.1.234:9190")
*
* \param key The key used to identify the device type in tracker.
*
* \param custom_addr Custom IP Address to Report to RPC Tracker.
*/
class RPCServer {
public:
/*!
* \brief Constructor.
*/
RPCServer(std::string host, int port_search_start, int port_search_end, std::string tracker_addr,
std::string key, std::string custom_addr, std::string work_dir)
: host_(std::move(host)),
port_search_start_(port_search_start),
my_port_(0),
port_search_end_(port_search_end),
tracker_addr_(std::move(tracker_addr)),
key_(std::move(key)),
custom_addr_(std::move(custom_addr)),
work_dir_(std::move(work_dir)) {}
/*!
* \brief Destructor.
*/
~RPCServer() {
try {
// Free the resources
tracker_sock_.Close();
listen_sock_.Close();
} catch (...) {
}
}
/*!
* \brief Start Creates the RPC listen process and execution.
*/
void Start() {
listen_sock_.Create();
my_port_ = listen_sock_.TryBindHost(host_, port_search_start_, port_search_end_);
LOG(INFO) << "Bind to " << host_ << ":" << my_port_;
listen_sock_.Listen(1);
std::future<void> proc(std::async(std::launch::async, &RPCServer::ListenLoopProc, this));
proc.get();
// Close the listen socket
listen_sock_.Close();
}
private:
/*!
* \brief ListenLoopProc The listen process.
*/
void ListenLoopProc() {
TrackerClient tracker(tracker_addr_, key_, custom_addr_, my_port_);
while (true) {
support::TCPSocket conn;
support::SockAddr addr("0.0.0.0", 0);
std::string opts;
try {
// step 1: setup tracker and report to tracker
tracker.TryConnect();
// step 2: wait for in-coming connections
AcceptConnection(&tracker, &conn, &addr, &opts);
} catch (const char* msg) {
LOG(WARNING) << "Socket exception: " << msg;
// close tracker resource
tracker.Close();
continue;
} catch (const std::exception& e) {
// close tracker resource
tracker.Close();
LOG(WARNING) << "Exception standard: " << e.what();
continue;
}
int timeout = GetTimeOutFromOpts(opts);
#if defined(__linux__) || defined(__ANDROID__) || defined(__APPLE__)
// step 3: serving
if (timeout != 0) {
const pid_t worker_pid = fork();
if (worker_pid == 0) {
// Worker process
ServerLoopProc(conn, addr, work_dir_);
_exit(0);
}
int status = 0;
bool timed_out = false;
auto start_timer = std::chrono::steady_clock::now();
while (true) {
// Check worker pid (non-blocking)
int ret = waitpid(worker_pid, &status, WNOHANG);
if (ret == worker_pid) {
break;
} else if (ret == -1) {
if (errno == EINTR) continue;
break;
}
// Check worker timeout
if (std::chrono::steady_clock::now() - start_timer >= std::chrono::seconds(timeout)) {
timed_out = true;
kill(worker_pid, SIGTERM);
waitpid(worker_pid, &status, 0);
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
// Logging.
if (timed_out) {
LOG(INFO) << "Child pid=" << worker_pid << " killed"
<< " (timeout = " << timeout << " sec)"
<< ", status = " << status;
} else {
LOG(INFO) << "Child pid=" << worker_pid << " finished"
<< ", status = " << status;
}
} else {
auto pid = fork();
if (pid == 0) {
ServerLoopProc(conn, addr, work_dir_);
_exit(0);
}
// Wait for the result
int status = 0;
wait(&status);
LOG(INFO) << "Child pid=" << pid << " exited, status =" << status;
}
#elif defined(WIN32)
auto start_time = high_resolution_clock::now();
try {
SpawnRPCChild(conn.sockfd, seconds(timeout));
} catch (const std::exception&) {
}
auto dur = high_resolution_clock::now() - start_time;
LOG(INFO) << "Serve Time " << duration_cast<milliseconds>(dur).count() << "ms";
#else
LOG(WARNING) << "Unknown platform. It is not known how to bring up the subprocess."
<< " RPC will be launched in the main thread.";
ServerLoopProc(conn, addr, work_dir_);
#endif
// close from our side.
LOG(INFO) << "End session with " << addr.AsString();
conn.Close();
}
}
/*!
* \brief AcceptConnection Accepts the RPC Server connection.
* \param tracker Tracker details.
* \param conn_sock New connection information.
* \param addr New connection address information.
* \param opts Parsed options for socket
* \param ping_period Timeout for select call waiting
*/
void AcceptConnection(TrackerClient* tracker, support::TCPSocket* conn_sock,
support::SockAddr* addr, std::string* opts, int ping_period = 2) {
std::set<std::string> old_keyset;
std::string matchkey;
// Report resource to tracker and get key
tracker->ReportResourceAndGetKey(my_port_, &matchkey);
while (true) {
tracker->WaitConnectionAndUpdateKey(listen_sock_, my_port_, ping_period, &matchkey);
support::TCPSocket conn = listen_sock_.Accept(addr);
int code = kRPCMagic;
TVM_FFI_ICHECK_EQ(conn.RecvAll(&code, sizeof(code)), sizeof(code));
if (code != kRPCMagic) {
conn.Close();
TVM_FFI_THROW(InternalError) << "Client connected is not TVM RPC server";
continue;
}
int keylen = 0;
TVM_FFI_ICHECK_EQ(conn.RecvAll(&keylen, sizeof(keylen)), sizeof(keylen));
const char* CLIENT_HEADER = "client:";
const char* SERVER_HEADER = "server:";
std::string expect_header = CLIENT_HEADER + matchkey;
std::string server_key = SERVER_HEADER + key_;
if (size_t(keylen) < expect_header.length()) {
conn.Close();
LOG(INFO) << "Wrong client header length";
continue;
}
TVM_FFI_ICHECK_NE(keylen, 0);
std::string remote_key;
remote_key.resize(keylen);
TVM_FFI_ICHECK_EQ(conn.RecvAll(&remote_key[0], keylen), keylen);
std::stringstream ssin(remote_key);
std::string arg0;
#ifndef __ANDROID__
ssin >> arg0;
#else
arg0 = getNextString(&ssin);
#endif
if (arg0 != expect_header) {
code = kRPCMismatch;
TVM_FFI_ICHECK_EQ(conn.SendAll(&code, sizeof(code)), sizeof(code));
conn.Close();
LOG(WARNING) << "Mismatch key from" << addr->AsString();
continue;
} else {
code = kRPCSuccess;
TVM_FFI_ICHECK_EQ(conn.SendAll(&code, sizeof(code)), sizeof(code));
keylen = int(server_key.length());
TVM_FFI_ICHECK_EQ(conn.SendAll(&keylen, sizeof(keylen)), sizeof(keylen));
TVM_FFI_ICHECK_EQ(conn.SendAll(server_key.c_str(), keylen), keylen);
LOG(INFO) << "New session from " << addr->AsString();
#ifndef __ANDROID__
ssin >> *opts;
#else
*opts = getNextString(&ssin);
#endif
*conn_sock = conn;
return;
}
}
}
/*!
* \brief ServerLoopProc The Server loop process.
* \param sock The socket information
* \param addr The socket address information
*/
static void ServerLoopProc(support::TCPSocket sock, support::SockAddr addr,
std::string work_dir) {
// Server loop
const auto s_time = std::chrono::high_resolution_clock::now();
const auto env = RPCEnv(work_dir);
RPCServerLoop(int(sock.sockfd));
const auto e_time = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = e_time - s_time;
LOG(INFO) << "Finished serving " << addr.AsString() << " after " << elapsed.count() << " sec";
env.CleanUp();
}
/*!
* \brief GetTimeOutFromOpts Parse and get the timeout option.
* \param opts The option string
*/
int GetTimeOutFromOpts(const std::string& opts) const {
const std::string option = "-timeout=";
size_t pos = opts.rfind(option);
if (pos != std::string::npos) {
const std::string cmd = opts.substr(pos + option.size());
TVM_FFI_ICHECK(support::IsNumber(cmd)) << "Timeout is not valid";
return std::stoi(cmd);
}
return 0;
}
std::string host_;
int port_search_start_;
int my_port_;
int port_search_end_;
std::string tracker_addr_;
std::string key_;
std::string custom_addr_;
std::string work_dir_;
support::TCPSocket listen_sock_;
support::TCPSocket tracker_sock_;
};
#if defined(WIN32)
/*!
* \brief ServerLoopFromChild The Server loop process.
* \param socket The socket information
*/
void ServerLoopFromChild(SOCKET socket) {
// Server loop
tvm::support::TCPSocket sock(socket);
const auto env = RPCEnv();
RPCServerLoop(int(sock.sockfd));
sock.Close();
env.CleanUp();
}
#endif
/*!
* \brief RPCServerCreate Creates the RPC Server.
* \param host The listen address of the server, Default=0.0.0.0 (any)
* \param port The port of the RPC server, Default=9090
* \param port_end The end search port of the RPC server, Default=9099
* \param tracker_addr The address of RPC tracker in host:port format e.g. 10.77.1.234:9190
* Default="" \param key The key used to identify the device type in tracker. Default="" \param
* custom_addr Custom IP Address to Report to RPC Tracker. Default="" \param silent Whether run in
* silent mode. Default=True
*/
void RPCServerCreate(std::string host, int port, int port_end, std::string tracker_addr,
std::string key, std::string custom_addr, std::string work_dir, bool silent) {
if (silent) {
}
// Start the rpc server
RPCServer rpc(std::move(host), port, port_end, std::move(tracker_addr), std::move(key),
std::move(custom_addr), std::move(work_dir));
rpc.Start();
}
TVM_FFI_STATIC_INIT_BLOCK() {
namespace refl = tvm::ffi::reflection;
refl::GlobalDef().def("rpc.ServerCreate", RPCServerCreate);
}
} // namespace runtime
} // namespace tvm
+59
View File
@@ -0,0 +1,59 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/*!
* \file rpc_server.h
* \brief RPC Server implementation.
*/
#ifndef TVM_APPS_CPP_RPC_SERVER_H_
#define TVM_APPS_CPP_RPC_SERVER_H_
#include <string>
#include "tvm/runtime/base.h"
namespace tvm {
namespace runtime {
#if defined(WIN32)
/*!
* \brief ServerLoopFromChild The Server loop process.
* \param sock The socket information
* \param addr The socket address information
*/
void ServerLoopFromChild(SOCKET socket);
#endif
/*!
* \brief RPCServerCreate Creates the RPC Server.
* \param host The listen address of the server, Default=0.0.0.0 (any)
* \param port The port of the RPC server, Default=9090
* \param port_end The end search port of the RPC server, Default=9099
* \param tracker The address of RPC tracker in host:port format e.g. 10.77.1.234:9190 Default=""
* \param key The key used to identify the device type in tracker. Default=""
* \param custom_addr Custom IP Address to Report to RPC Tracker. Default=""
* \param work_dir Custom work directory. Default=""
* \param silent Whether run in silent mode. Default=True
*/
void RPCServerCreate(std::string host = "", int port = 9090, int port_end = 9099,
std::string tracker_addr = "", std::string key = "",
std::string custom_addr = "", std::string work_dir = "", bool silent = true);
} // namespace runtime
} // namespace tvm
#endif // TVM_APPS_CPP_RPC_SERVER_H_
+248
View File
@@ -0,0 +1,248 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/*!
* \file rpc_tracker_client.h
* \brief RPC Tracker client to report resources.
*/
#ifndef TVM_APPS_CPP_RPC_TRACKER_CLIENT_H_
#define TVM_APPS_CPP_RPC_TRACKER_CLIENT_H_
#include <chrono>
#include <iostream>
#include <random>
#include <set>
#include <string>
#include <vector>
#include "../../src/runtime/rpc/rpc_endpoint.h"
#include "../../src/support/socket.h"
namespace tvm {
namespace runtime {
/*!
* \brief TrackerClient Tracker client class.
* \param tracker The address of RPC tracker in host:port format e.g. 10.77.1.234:9190 Default=""
* \param key The key used to identify the device type in tracker. Default=""
* \param custom_addr Custom IP Address to Report to RPC Tracker. Default=""
*/
class TrackerClient {
public:
/*!
* \brief Constructor.
*/
TrackerClient(const std::string& tracker_addr, const std::string& key,
const std::string& custom_addr, int port)
: tracker_addr_(tracker_addr),
key_(key),
custom_addr_(custom_addr),
port_(port),
gen_(std::random_device{}()),
dis_(0.0, 1.0) {
if (custom_addr_.empty()) {
custom_addr_ = "null";
} else {
// Since custom_addr_ can be either the json value null which is not surrounded by quotes
// or a string containing the custom value which json does required to be quoted then we
// need to set custom_addr_ to be a string containing the quotes here.
custom_addr_ = "\"" + custom_addr_ + "\"";
}
}
/*!
* \brief Destructor.
*/
~TrackerClient() {
// Free the resources
Close();
}
/*!
* \brief IsValid Check tracker is valid.
*/
bool IsValid() { return (!tracker_addr_.empty() && !tracker_sock_.IsClosed()); }
/*!
* \brief TryConnect Connect to tracker if the tracker address is valid.
*/
void TryConnect() {
if (!tracker_addr_.empty() && (tracker_sock_.IsClosed())) {
tracker_sock_ = ConnectWithRetry();
int code = kRPCTrackerMagic;
TVM_FFI_ICHECK_EQ(tracker_sock_.SendAll(&code, sizeof(code)), sizeof(code));
TVM_FFI_ICHECK_EQ(tracker_sock_.RecvAll(&code, sizeof(code)), sizeof(code));
TVM_FFI_ICHECK_EQ(code, kRPCTrackerMagic) << tracker_addr_.c_str() << " is not RPC Tracker";
std::ostringstream ss;
ss << "[" << static_cast<int>(TrackerCode::kUpdateInfo) << ", {\"key\": \"server:" << key_
<< "\", \"addr\": [" << custom_addr_ << ", \"" << port_ << "\"]}]";
tracker_sock_.SendBytes(ss.str());
// Receive status and validate
std::string remote_status = tracker_sock_.RecvBytes();
TVM_FFI_ICHECK_EQ(std::stoi(remote_status), static_cast<int>(TrackerCode::kSuccess));
}
}
/*!
* \brief Close Clean up tracker resources.
*/
void Close() {
// close tracker resource
if (!tracker_sock_.IsClosed()) {
tracker_sock_.Close();
}
}
/*!
* \brief ReportResourceAndGetKey Report resource to tracker.
* \param port listening port.
* \param matchkey Random match key output.
*/
void ReportResourceAndGetKey(int port, std::string* matchkey) {
if (!tracker_sock_.IsClosed()) {
*matchkey = RandomKey(key_ + ":", old_keyset_);
std::ostringstream ss;
ss << "[" << static_cast<int>(TrackerCode::kPut) << ", \"" << key_ << "\", [" << port
<< ", \"" << *matchkey << "\"], " << custom_addr_ << "]";
tracker_sock_.SendBytes(ss.str());
// Receive status and validate
std::string remote_status = tracker_sock_.RecvBytes();
TVM_FFI_ICHECK_EQ(std::stoi(remote_status), static_cast<int>(TrackerCode::kSuccess));
} else {
*matchkey = key_;
}
}
/*!
* \brief ReportResourceAndGetKey Report resource to tracker.
* \param listen_sock Listen socket details for select.
* \param port listening port.
* \param ping_period Select wait time.
* \param matchkey Random match key output.
*/
void WaitConnectionAndUpdateKey(support::TCPSocket listen_sock, int port, int ping_period,
std::string* matchkey) {
int unmatch_period_count = 0;
int unmatch_timeout = 4;
while (true) {
if (!tracker_sock_.IsClosed()) {
support::PollHelper poller;
poller.WatchRead(listen_sock.sockfd);
poller.Poll(ping_period * 1000);
if (!poller.CheckRead(listen_sock.sockfd)) {
std::ostringstream ss;
ss << "[" << int(TrackerCode::kGetPendingMatchKeys) << "]";
tracker_sock_.SendBytes(ss.str());
// Receive status and validate
std::string pending_keys = tracker_sock_.RecvBytes();
old_keyset_.insert(*matchkey);
// if match key not in pending key set
// it means the key is acquired by a client but not used.
if (pending_keys.find(*matchkey) == std::string::npos) {
unmatch_period_count += 1;
} else {
unmatch_period_count = 0;
}
// regenerate match key if key is acquired but not used for a while
if (unmatch_period_count * ping_period > unmatch_timeout + ping_period) {
LOG(INFO) << "no incoming connections, regenerate key ...";
*matchkey = RandomKey(key_ + ":", old_keyset_);
std::ostringstream ss;
ss << "[" << static_cast<int>(TrackerCode::kPut) << ", \"" << key_ << "\", [" << port
<< ", \"" << *matchkey << "\"], " << custom_addr_ << "]";
tracker_sock_.SendBytes(ss.str());
std::string remote_status = tracker_sock_.RecvBytes();
TVM_FFI_ICHECK_EQ(std::stoi(remote_status), static_cast<int>(TrackerCode::kSuccess));
unmatch_period_count = 0;
}
continue;
}
}
break;
}
}
private:
/*!
* \brief Connect to a RPC address with retry.
This function is only reliable to short period of server restart.
* \param timeout Timeout during retry
* \param retry_period Number of seconds before we retry again.
* \return TCPSocket The socket information if connect is success.
*/
support::TCPSocket ConnectWithRetry(int timeout = 60, int retry_period = 5) {
auto tbegin = std::chrono::system_clock::now();
while (true) {
support::SockAddr addr(tracker_addr_);
support::TCPSocket sock;
sock.Create();
if (sock.Connect(addr)) {
LOG(INFO) << "Connected to tracker " << addr.AsString();
return sock;
}
auto period = (std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now() - tbegin))
.count();
TVM_FFI_ICHECK(period < timeout) << "Failed to connect to tracker " << addr.AsString();
LOG(WARNING) << "Cannot connect to tracker " << addr.AsString() << " retry in "
<< retry_period << " seconds.";
std::this_thread::sleep_for(std::chrono::seconds(retry_period));
}
}
/*!
* \brief Random Generate a random number between 0 and 1.
* \return random float value.
*/
float Random() { return dis_(gen_); }
/*!
* \brief Generate a random key.
* \param prefix The string prefix.
* \return cmap The conflict map set.
*/
std::string RandomKey(const std::string& prefix, const std::set<std::string>& cmap) {
if (!cmap.empty()) {
while (true) {
std::string key = prefix + std::to_string(Random());
if (cmap.find(key) == cmap.end()) {
return key;
}
}
}
return prefix + std::to_string(Random());
}
std::string tracker_addr_;
std::string key_;
std::string custom_addr_;
int port_;
support::TCPSocket tracker_sock_;
std::set<std::string> old_keyset_;
std::mt19937 gen_;
std::uniform_real_distribution<float> dis_;
};
} // namespace runtime
} // namespace tvm
#endif // TVM_APPS_CPP_RPC_TRACKER_CLIENT_H_
+262
View File
@@ -0,0 +1,262 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include "win32_process.h"
#include <conio.h>
#include <tvm/runtime/logging.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <cstdio>
#include <memory>
#include <stdexcept>
#include <string>
#include "rpc_server.h"
using namespace std::chrono;
using namespace tvm::runtime;
namespace {
// The prefix path for the memory mapped file used to store IPC information
const std::string kMemoryMapPrefix = "/MAPPED_FILE/TVM_RPC";
// Used to construct unique names for named resources in the parent process
const std::string kParent = "parent";
// Used to construct unique names for named resources in the child process
const std::string kChild = "child";
// The timeout of the WIN32 events, in the parent and the child
const milliseconds kEventTimeout(2000);
// Used to create unique WIN32 mmap paths and event names
int child_counter_ = 0;
/*!
* \brief HandleDeleter Deleter for UniqueHandle smart pointer
* \param handle The WIN32 HANDLE to manage
*/
struct HandleDeleter {
void operator()(HANDLE handle) const {
if (handle != INVALID_HANDLE_VALUE && handle != nullptr) {
CloseHandle(handle);
}
}
};
/*!
* \brief UniqueHandle Smart pointer to manage a WIN32 HANDLE
*/
using UniqueHandle = std::unique_ptr<void, HandleDeleter>;
/*!
* \brief MakeUniqueHandle Helper method to construct a UniqueHandle
* \param handle The WIN32 HANDLE to manage
*/
UniqueHandle MakeUniqueHandle(HANDLE handle) {
if (handle == INVALID_HANDLE_VALUE || handle == nullptr) {
return nullptr;
}
return UniqueHandle(handle);
}
/*!
* \brief GetSocket Gets the socket info from the parent process and duplicates the socket
* \param mmap_path The path to the memory mapped info set by the parent
*/
SOCKET GetSocket(const std::string& mmap_path) {
WSAPROTOCOL_INFO protocol_info;
const std::string parent_event_name = mmap_path + kParent;
const std::string child_event_name = mmap_path + kChild;
// Open the events
UniqueHandle parent_file_mapping_event;
if ((parent_file_mapping_event = MakeUniqueHandle(
OpenEventA(SYNCHRONIZE, false, parent_event_name.c_str()))) == nullptr) {
TVM_FFI_THROW(InternalError) << "OpenEvent() failed: " << GetLastError();
}
UniqueHandle child_file_mapping_event;
if ((child_file_mapping_event = MakeUniqueHandle(
OpenEventA(EVENT_MODIFY_STATE, false, child_event_name.c_str()))) == nullptr) {
TVM_FFI_THROW(InternalError) << "OpenEvent() failed: " << GetLastError();
}
// Wait for the parent to set the event, notifying WSAPROTOCOL_INFO is ready to be read
if (WaitForSingleObject(parent_file_mapping_event.get(), uint32_t(kEventTimeout.count())) !=
WAIT_OBJECT_0) {
TVM_FFI_THROW(InternalError) << "WaitForSingleObject() failed: " << GetLastError();
}
const UniqueHandle file_map =
MakeUniqueHandle(OpenFileMappingA(FILE_MAP_READ | FILE_MAP_WRITE, false, mmap_path.c_str()));
if (!file_map) {
LOG(INFO) << "CreateFileMapping() failed: " << GetLastError();
}
void* map_view = MapViewOfFile(file_map.get(), FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
SOCKET sock_duplicated = INVALID_SOCKET;
if (map_view != nullptr) {
memcpy(&protocol_info, map_view, sizeof(WSAPROTOCOL_INFO));
UnmapViewOfFile(map_view);
// Creates the duplicate socket, that was created in the parent
sock_duplicated =
WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, &protocol_info, 0, 0);
// Let the parent know we are finished duplicating the socket
SetEvent(child_file_mapping_event.get());
} else {
TVM_FFI_THROW(InternalError) << "MapViewOfFile() failed: " << GetLastError();
}
return sock_duplicated;
}
} // Anonymous namespace
namespace tvm {
namespace runtime {
/*!
* \brief SpawnRPCChild Spawns a child process with a given timeout to run
* \param fd The client socket to duplicate in the child
* \param timeout The time in seconds to wait for the child to complete before termination
*/
void SpawnRPCChild(SOCKET fd, seconds timeout) {
STARTUPINFOA startup_info;
memset(&startup_info, 0, sizeof(startup_info));
startup_info.cb = sizeof(startup_info);
std::string file_map_path = kMemoryMapPrefix + std::to_string(child_counter_++);
const std::string parent_event_name = file_map_path + kParent;
const std::string child_event_name = file_map_path + kChild;
// Create an event to let the child know the socket info was set to the mmap file
UniqueHandle parent_file_mapping_event;
if ((parent_file_mapping_event = MakeUniqueHandle(
CreateEventA(nullptr, true, false, parent_event_name.c_str()))) == nullptr) {
TVM_FFI_THROW(InternalError) << "CreateEvent for parent file mapping failed";
}
UniqueHandle child_file_mapping_event;
// An event to let the parent know the socket info was read from the mmap file
if ((child_file_mapping_event = MakeUniqueHandle(
CreateEventA(nullptr, true, false, child_event_name.c_str()))) == nullptr) {
TVM_FFI_THROW(InternalError) << "CreateEvent for child file mapping failed";
}
char current_executable[MAX_PATH];
// Get the full path of the current executable
GetModuleFileNameA(nullptr, current_executable, MAX_PATH);
std::string child_command_line = current_executable;
child_command_line += " server --child_proc=";
child_command_line += file_map_path;
// CreateProcessA requires a non const char*, so we copy our std::string
auto command_line_ptr = std::make_unique<char[]>(child_command_line.size() + 1);
strcpy(command_line_ptr.get(), child_command_line.c_str());
PROCESS_INFORMATION child_process_info;
if (CreateProcessA(nullptr, command_line_ptr.get(), nullptr, nullptr, false, CREATE_NO_WINDOW,
nullptr, nullptr, &startup_info, &child_process_info)) {
// Child process and thread handles must be closed, so wrapped in RAII
auto child_process_handle = MakeUniqueHandle(child_process_info.hProcess);
auto child_process_thread_handle = MakeUniqueHandle(child_process_info.hThread);
WSAPROTOCOL_INFO protocol_info;
// Get info needed to duplicate the socket
if (WSADuplicateSocket(fd, child_process_info.dwProcessId, &protocol_info) == SOCKET_ERROR) {
TVM_FFI_THROW(InternalError) << "WSADuplicateSocket(): failed. Error =" << WSAGetLastError();
}
// Create a mmap file to store the info needed for duplicating the SOCKET in the child proc
UniqueHandle file_map =
MakeUniqueHandle(CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0,
sizeof(WSAPROTOCOL_INFO), file_map_path.c_str()));
if (!file_map) {
LOG(INFO) << "CreateFileMapping() failed: " << GetLastError();
}
if (GetLastError() == ERROR_ALREADY_EXISTS) {
TVM_FFI_THROW(InternalError) << "CreateFileMapping(): mapping file already exists";
} else {
void* map_view = MapViewOfFile(file_map.get(), FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
if (map_view != nullptr) {
memcpy(map_view, &protocol_info, sizeof(WSAPROTOCOL_INFO));
UnmapViewOfFile(map_view);
// Let child proc know the mmap file is ready to be read
SetEvent(parent_file_mapping_event.get());
// Wait for the child to finish reading mmap file
if (WaitForSingleObject(child_file_mapping_event.get(), uint32_t(kEventTimeout.count())) !=
WAIT_OBJECT_0) {
TerminateProcess(child_process_handle.get(), 0);
TVM_FFI_THROW(InternalError)
<< "WaitForSingleObject for child file mapping timed out. Terminating child "
"process.";
}
} else {
TerminateProcess(child_process_handle.get(), 0);
TVM_FFI_THROW(InternalError) << "MapViewOfFile() failed: " << GetLastError();
}
}
const DWORD process_timeout =
timeout.count() ? uint32_t(duration_cast<milliseconds>(timeout).count()) : INFINITE;
// Wait for child process to exit, or hit configured timeout
if (WaitForSingleObject(child_process_handle.get(), process_timeout) != WAIT_OBJECT_0) {
LOG(INFO) << "Child process timeout. Terminating.";
TerminateProcess(child_process_handle.get(), 0);
}
} else {
LOG(INFO) << "Create child process failed: " << GetLastError();
}
}
/*!
* \brief ChildProcSocketHandler Ran from the child process and runs server to handle the client
* socket \param mmap_path The memory mapped file path that will contain the information to
* duplicate the client socket from the parent
*/
void ChildProcSocketHandler(const std::string& mmap_path) {
SOCKET socket;
// Set high thread priority to avoid the thread scheduler from
// interfering with any measurements in the RPC server.
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
if ((socket = GetSocket(mmap_path)) != INVALID_SOCKET) {
tvm::runtime::ServerLoopFromChild(socket);
} else {
TVM_FFI_THROW(InternalError) << "GetSocket() failed";
}
}
} // namespace runtime
} // namespace tvm
+48
View File
@@ -0,0 +1,48 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/*!
* \file win32_process.h
* \brief Win32 process code to mimic a POSIX fork()
*/
#ifndef TVM_APPS_CPP_RPC_WIN32_PROCESS_H_
#define TVM_APPS_CPP_RPC_WIN32_PROCESS_H_
#include <chrono>
#include <string>
#include "../../src/support/socket.h"
namespace tvm {
namespace runtime {
/*!
* \brief SpawnRPCChild Spawns a child process with a given timeout to run
* \param fd The client socket to duplicate in the child
* \param timeout The time in seconds to wait for the child to complete before termination
*/
void SpawnRPCChild(SOCKET fd, std::chrono::seconds timeout);
/*!
* \brief ChildProcSocketHandler Ran from the child process and runs server to handle the client
* socket \param mmap_path The memory mapped file path that will contain the information to
* duplicate the client socket from the parent
*/
void ChildProcSocketHandler(const std::string& mmap_path);
} // namespace runtime
} // namespace tvm
#endif // TVM_APPS_CPP_RPC_WIN32_PROCESS_H_
+1
View File
@@ -0,0 +1 @@
rpc_config.txt
+82
View File
@@ -0,0 +1,82 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
include(ExternalProject)
# Check if xcodebuild tool is available and configured.
# Otherwise will skip all iOS specific targets.
execute_process(COMMAND xcodebuild -version
RESULT_VARIABLE XCBUILD_AVAILABLE
OUTPUT_QUIET
ERROR_QUIET
)
if (NOT XCBUILD_AVAILABLE EQUAL 0)
message(WARNING
"The build tool xcodebuild is not properly configured. Please install Xcode app and specify "
"path to it via DEVELOPER_DIR env var or \"sudo xcode-select -switch <path-to-xcode-dev-dir>\".\n"
"iOS RPC application target is switched off."
)
return()
endif()
# External project with custom mach-o dynamic loader
# It is required to load unsigned shared modules on real iOS devices
ExternalProject_Add(custom_dso_loader
GIT_REPOSITORY https://github.com/octoml/macho-dyld.git
GIT_TAG d1f7032e7882bc060b49a4fb058f50a23668b074
PREFIX custom_dso_loader
LOG_DOWNLOAD TRUE
LOG_CONFIGURE TRUE
CMAKE_ARGS
-DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> # to install into local build dir
-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}
-DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}
-DCMAKE_SYSTEM_VERSION=${CMAKE_SYSTEM_VERSION}
-DCMAKE_OSX_SYSROOT=${CMAKE_OSX_SYSROOT}
-DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES}
-DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}
-DCMAKE_BUILD_WITH_INSTALL_NAME_DIR=${CMAKE_BUILD_WITH_INSTALL_NAME_DIR}
)
if(NOT CMAKE_IOS_RPC_BUNDLE)
set(CMAKE_IOS_RPC_BUNDLE org.apache.tvmrpc)
endif()
# iOS RPC Xcode project wrapper to integrate into CMake
ExternalProject_Add(ios_rpc
PREFIX ios_rpc
DEPENDS custom_dso_loader tvm_runtime
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}
CONFIGURE_COMMAND ""
INSTALL_COMMAND ""
BUILD_COMMAND xcodebuild
-target tvmrpc
-configuration ${CMAKE_BUILD_TYPE}
-project <SOURCE_DIR>/tvmrpc.xcodeproj
-sdk ${CMAKE_OSX_SYSROOT}
-arch ${CMAKE_OSX_ARCHITECTURES}
-hideShellScriptEnvironment
-allowProvisioningUpdates
build
SYMROOT=<BINARY_DIR>
IPHONEOS_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}
DEVELOPMENT_TEAM=${CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM}
TVM_BUILD_DIR=${CMAKE_BINARY_DIR}
USE_CUSTOM_DSO_LOADER=1
PRODUCT_BUNDLE_IDENTIFIER=${CMAKE_IOS_RPC_BUNDLE}
)
+256
View File
@@ -0,0 +1,256 @@
<!--- Licensed to the Apache Software Foundation (ASF) under one -->
<!--- or more contributor license agreements. See the NOTICE file -->
<!--- distributed with this work for additional information -->
<!--- regarding copyright ownership. The ASF licenses this file -->
<!--- to you 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. -->
# iOS TVM RPC
This folder contains iOS RPC app that allows us to launch an rpc server on a iOS
device. You will need XCode and an iOS device to use this.
## Table of Contents
* [Building](#building)
* [Building TVM runtime and custom DSO loader plugin](#building-tvm-runtime-and-custom-dso-loader-plugin)
* [Building iOS TVM RPC application](#building-ios-tvm-rpc-application)
* [Workflow](#workflow)
* [Standalone RPC](#standalone-rpc)
* [iOS RPC App with proxy](#ios-rpc-app-with-proxy)
* [iOS RPC App with tracker](#ios-rpc-app-with-tracker)
* [Communication without Wi-Fi and speed up in case of slow Wi-Fi](#communication-without-wi-fi-and-speed-up-in-case-of-slow-wi-fi)
## Building
### Building TVM runtime and custom DSO loader plugin
While iOS platform itself doesn't allow us to run an unsigned binary, there is a
partial ability to run JIT code on real iOS devices. While application is
running under debug session, system allows allocating memory with write and
execute permissions (a debugger requirement). So we can use this feature to
implement the `tvm.rpc.server.load_module` PackedFunc, used to load code over
RPC. For this purpose we use custom version of `dlopen` function which doesn't
check signature and permissions for module loading. This custom `dlopen`
mechanic is integrated into TVM RPC as plugin and registered to execution only
inside iOS RPC application.
The custom implementation of `dlopen` and other functions from `dlfcn.h` header are placed in separate repository,
and will be downloaded automatically during CMake build for iOS.
Also, it is necessary to build `libtvm_runtime.dylib` for our iOS device. The
iOS TVM RPC application will be linked with this library.
Run the build using the following commands:
```shell
export DEVELOPER_DIR=/Applications/Xcode.app # iOS SDK is part of Xcode bundle. Have to set it as default Dev Env
cmake ..
-DCMAKE_BUILD_TYPE=Debug
-DCMAKE_SYSTEM_NAME=iOS
-DCMAKE_SYSTEM_VERSION=14.0
-DCMAKE_OSX_SYSROOT=iphoneos
-DCMAKE_OSX_ARCHITECTURES=arm64
-DCMAKE_OSX_DEPLOYMENT_TARGET=14.0
-DCMAKE_BUILD_WITH_INSTALL_NAME_DIR=ON
-DUSE_IOS_RPC=ON # to enable build iOS RPC application from TVM project tree
-DUSE_METAL=ON # to enable Metal runtime
cmake --build . --target custom_dso_loader tvm_runtime
```
### Building iOS TVM RPC application
Before start, please run [init_proj.py](./init_proj.py) to update XCode developer metadata:
```shell
python3 init_proj.py --team_id XXXXXXXXXX --tvm_build_dir "/path/to/tvm/ios/build/folder"
```
You can get value of your `team_id` in the following ways:
- **You have registered Apple Developer Profile**. In this case you developer
Team ID available at https://developer.apple.com/account/#/membership
- You are using your local developer profile. In this case, leave `XXXXXXXXXX`
in the command instead of substituting a Team ID. Then open `tvmrpc.xcodeproj`
by using XCode, click on the project name (`tvmrpc`) on the left panel. Then
select target `tvmrpc`. At the bottom of this panel go to `Signing &
Capabilities` tab and in the field `Team` select your local developer profile
(`Your Name (Personal Team)`).
On the first run of the application you may see message `Could not launch
"tvmrpc"` in the XCode and message `Untrusted Developer` on your device. In
this case it will be necessary to check the certificate. Open
`Settings -> General -> Device Management -> Apple Development: <your_email>
-> Trust "Apple Development: <your_email>"` and click `Trust`. After than you
should rerun your application in the XCode.
After this step, open `tvmrpc.xcodeproj` by using XCode, build the App and
install the App on the phone.
## Workflow
Due to security restriction of iOS10. We cannot upload dynamic libraries to the
App and load it from sandbox. Instead, we need to build a list of libraries,
pack them into the app bundle, launch the RPC server and connect to test the
bundled libraries. For more on the approach we use to work around this
limitation, please take a look into section
[Building TVM runtime and custom DSO loader integration](#building-tvm-runtime-and-custom-DSO-loader-plugin).
The test script [tests/ios_rpc_test.py](tests/ios_rpc_test.py) and
[tests/ios_rpc_mobilenet.py](tests/ios_rpc_mobilenet.py) are good templates for
demonstrating the workflow.
We have three different modes for iOS RPC server:
- [Standalone RPC](#standalone-rpc): In this mode RPC server open port on the device and listening. Then
client connects to the server directly without any mediators.
- [iOS RPC application with Proxy](#ios-rpc-app-with-proxy): RPC server and RPC client communicates through
`rpc_proxy`. The RPC server on iOS device notify `rpc_proxy` which was run on
host machine about itself and wait for incoming connections. Communications
between client and server works through `rpc_proxy`.
- [iOS RPC application with Tracker](#ios-rpc-app-with-tracker): RPC server registered in the `rpc_tracker`
and client connects to the RPC server through `rpc_tracker`.
### Standalone RPC
Start RPC server on your iOS device:
- Push on the `Connect` button.
After that you supposed to see something like this in the app on the device:
```
IP: <device_ip>
Port: <rpc_server_port>
```
Printed `IP` is the IP address of your device and `PORT` is the number of port
which was open for RPC connection. Next you should use them for connect your RPC
client to the server.
Let's check that direct RPC connection works and we can upload a library with
model and execute it on the device. For this purpose we will use
[ios_rpc_test.py](tests/ios_rpc_test.py). Run it:
```shell
python3 tests/ios_rpc_test.py --host <device_ip> --port <rpc_server_port> --mode "standalone"
```
This will compile TVM IR to shared libraries (CPU and Metal) and run vector
addition on your iOS device. You are supposed to see something like this:
```
Metal: 0.000338692 secs/op
CPU: 0.000219308 secs/op
```
### iOS RPC App with proxy
Start the RPC proxy by running in a terminal:
```shell
python3 -m tvm.exec.rpc_proxy --host 0.0.0.0 --port 9090
```
On success, you should see something like this:
```
INFO:root:RPCProxy: client port bind to 0.0.0.0:9090
INFO:root:RPCProxy: Websock port bind to 8888
```
Connect your iOS device to the RPC proxy via the iOS TVM RPC application. Set
the `Address` and `Port` fields to the address and port of the RPC tracker
respectively. Select mode `Proxy` and push `Connect` button. In success the
text on the button will be changed to `Disconnect` and `Disconnected` in the top
of the screen will be changed to `Connected`.
On RPC proxy side you can see the next message in a log:
```
INFO:root:Handler ready TCPSocketProxy:<iPhone IP address>:server:iphone
```
Then we can check that RPC connection works and we can upload a library with
model and execute it on the target device. For this purpose we will use
[ios_rpc_test.py](tests/ios_rpc_test.py). Run it:
```shell
python3 tests/ios_rpc_test.py --host <host_ip_address> --port 9090 --mode "proxy"
```
The output should be the same as it was in previous section.
### iOS RPC App with tracker
First start an RPC tracker using
```shell
python3 -m tvm.exec.rpc_tracker --host 0.0.0.0 --port 9190
```
On success, you should see something like this:
```
INFO:RPCTracker:bind to 0.0.0.0:9190
```
Connect your iOS device to the RPC tracker via the iOS TVM RPC applcation. Set
the `Address` and `Port` fields to the address and port of the RPC tracker
respectively. Select mode `Tracker` and push `Connect` button. In success the
text on the button will be changed to `Disconnect` and `Disconnected` in the top
of the screen will be changed to `Connected`. On the host side you can check the
connect by the following command:
```shell
python3 -m tvm.exec.query_rpc_tracker --port 9190
```
You are supposed to see something like this:
```
Tracker address 127.0.0.1:9190
Server List
----------------------------
server-address key
----------------------------
192.168.1.57:9190 server:iphone
----------------------------
Queue Status
------------------------------
key total free pending
------------------------------
iphone 1 1 0
------------------------------
```
Then we can check that RPC connection works and we can upload a library with
model and execute it on the target device. For this purpose we will use
[ios_rpc_test.py](tests/ios_rpc_test.py). Run it:
```shell
python3 tests/ios_rpc_test.py --host <host_ip_address> --port 9190 --mode "tracker"
```
The output will be the same as in section
[Standalone RPC](#standalone-rpc).
## Communication without Wi-Fi and speed up in case of slow Wi-Fi
Connection to the RPC server through `usbmux` can be used then you have slow,
unstable or don't have any Wi-Fi connection. `usbmux` is used for binding local
TCP port to port on the device and transfer packages between these ports by USB
cable.
First of all you should install `usbmux` to your system. You can do it with
brew:
```shell
brew install usbmuxd
```
After that you can use `iproxy` program for binding ports. You can use it for
all described workflows. Let's take a look how it works for
[Standalone RPC](#standalone-rpc).
First, start RPC server on your iOS device. You may see something like this in
the app on the device:
```
IP: unknown
Port: <rpc_server_port>
```
**Note.** Here `IP: unknown` because there was no Internet connection on the iOS
device.
Printed `Port` is the port of the RPC server on your iOS device. We will use it
in binding ports. Run `iproxy`, specify local port which should be used for
communication with device and the printed port on the device:
```shell
iproxy <local_port>:<rpc_server_port>
```
After this command you should see something like this:
```
Creating listening port <local_port> for device port <rpc_server_port>
waiting for connection
```
Now we can check that RPC connection through `usbmux` works and we can upload a
library with model and execute it on the device. For this purpose we will use
[ios_rpc_test.py](tests/ios_rpc_test.py). Run it:
```shell
python3 tests/ios_rpc_test.py --host 0.0.0.0 --port <local_port> --mode standalone
```
The output should be the same as in all previous runs.
+56
View File
@@ -0,0 +1,56 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
import argparse
default_team_id = "3FR42MXLK9"
default_tvm_build_dir = "path-to-tvm-ios-build-folder"
parser = argparse.ArgumentParser(
description="Update tvmrpc.xcodeproj\
developer information"
)
parser.add_argument(
"--team_id",
type=str,
required=True,
help=f"Apple Developer Team ID.\n\
Can be found here:\n\
\n\
https://developer.apple.com/account/#/membership\n\
(example: {default_team_id})",
)
parser.add_argument(
"--tvm_build_dir",
type=str,
required=True,
help="Path to directory with libtvm_runtime.dylib",
)
args = parser.parse_args()
team_id = args.team_id
tvm_build_dir = args.tvm_build_dir
fi = open("tvmrpc.xcodeproj/project.pbxproj")
proj_config = fi.read()
fi.close()
proj_config = proj_config.replace(default_team_id, team_id)
proj_config = proj_config.replace(default_tvm_build_dir, tvm_build_dir)
fo = open("tvmrpc.xcodeproj/project.pbxproj", "w")
fo.write(proj_config)
fo.close()
+97
View File
@@ -0,0 +1,97 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
"""Testcode for iOS RPC.
To use it, start a rpc proxy with "python -m tvm.exec.rpc_proxy".
And configure the proxy host field as commented.
"""
import argparse
import numpy as np
import tvm
from tvm import rpc, te
from tvm.support import utils, xcode
# Change target configuration, this is setting for iphone6s
arch = "arm64"
sdk = "iphoneos"
target = {"kind": "llvm", "mtriple": f"{arch}-apple-darwin"}
MODES = {"proxy": rpc.connect, "tracker": rpc.connect_tracker, "standalone": rpc.connect}
# override metal compiler to compile to iphone
@tvm.register_global_func("tvm_callback_metal_compile")
def compile_metal(src, target):
return xcode.compile_metal(src, sdk=sdk)
def test_rpc_module(host, port, key, mode):
# graph
n = tvm.runtime.convert(1024)
A = te.placeholder((n,), name="A")
B = te.compute(A.shape, lambda *i: A(*i) + 1.0, name="B")
temp = utils.tempdir()
mod = tvm.IRModule.from_expr(te.create_prim_func([A, B]).with_attr("global_symbol", "myadd"))
sch = tvm.s_tir.Schedule(mod)
(i,) = sch.get_loops(block=sch.get_sblock("B"))
i0, i1 = sch.split(i, [None, 32])
sch.bind(i0, "blockIdx.x")
sch.bind(i1, "threadIdx.x")
# Build the dynamic lib.
# If we don't want to do metal and only use cpu, just set target to be target
f = tvm.compile(sch.mod, target=tvm.target.Target("metal", host=target))
path_dso1 = temp.relpath("dev_lib.dylib")
f.export_library(path_dso1, fcompile=xcode.create_dylib, arch=arch, sdk=sdk)
# connect to the proxy
if mode == "tracker":
remote = MODES[mode](host, port).request(key)
else:
remote = MODES[mode](host, port, key=key)
remote.upload(path_dso1)
dev = remote.metal(0)
f1 = remote.load_module("dev_lib.dylib")
a_np = np.random.uniform(size=1024).astype(A.dtype)
a = tvm.runtime.tensor(a_np, dev)
b = tvm.runtime.tensor(np.zeros(1024, dtype=A.dtype), dev)
time_f = f1.time_evaluator(f1.entry_name, dev, number=10)
cost = time_f(a, b).mean
print(f"Metal: {cost:g} secs/op")
np.testing.assert_equal(b.numpy(), a.numpy() + 1)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Demo app demonstrates how ios_rpc works.")
parser.add_argument("--host", required=True, type=str, help="Address of rpc server")
parser.add_argument("--port", type=int, default=9090, help="rpc port (default: 9090)")
parser.add_argument("--key", type=str, default="iphone", help="device key (default: iphone)")
parser.add_argument(
"--mode",
type=str,
default="tracker",
help="type of RPC connection (default: tracker), possible values: {}".format(
", ".join(MODES.keys())
),
)
args = parser.parse_args()
assert args.mode in MODES.keys()
test_rpc_module(args.host, args.port, args.key, args.mode)
@@ -0,0 +1,442 @@
// !$*UTF8*$!
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
016B19C22657B390002E1719 /* RPCServer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 016B19C12657B390002E1719 /* RPCServer.mm */; };
01A1DB432652CBA700655BBC /* RPCArgs.mm in Sources */ = {isa = PBXBuildFile; fileRef = 01A1DB412652CBA700655BBC /* RPCArgs.mm */; };
01A9B7B3265BD1FD000D092F /* libtvm_runtime.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 01A9B7B2265BD1FD000D092F /* libtvm_runtime.dylib */; };
01A9B7B8265BD307000D092F /* libtvm_runtime.dylib in Embed Libraries */ = {isa = PBXBuildFile; fileRef = 01A9B7B2265BD1FD000D092F /* libtvm_runtime.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
C02637501F1C25E8007247A9 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C026374F1F1C25E8007247A9 /* main.m */; };
C02637531F1C25E8007247A9 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = C02637521F1C25E8007247A9 /* AppDelegate.m */; };
C02637591F1C25E8007247A9 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C02637571F1C25E8007247A9 /* Main.storyboard */; };
C026375B1F1C25E8007247A9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C026375A1F1C25E8007247A9 /* Assets.xcassets */; };
C026375E1F1C25E8007247A9 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C026375C1F1C25E8007247A9 /* LaunchScreen.storyboard */; };
C02637661F1C2690007247A9 /* TVMRuntime.mm in Sources */ = {isa = PBXBuildFile; fileRef = C02637651F1C2690007247A9 /* TVMRuntime.mm */; };
C02637691F1C26AF007247A9 /* ViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = C02637681F1C26AF007247A9 /* ViewController.mm */; };
D7685AD324390EAE00D1469C /* CoreML.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D7685AD224390EAD00D1469C /* CoreML.framework */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
01A9B7B9265BD307000D092F /* Embed Libraries */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
01A9B7B8265BD307000D092F /* libtvm_runtime.dylib in Embed Libraries */,
);
name = "Embed Libraries";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
016B19C02657B390002E1719 /* RPCServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RPCServer.h; sourceTree = "<group>"; };
016B19C12657B390002E1719 /* RPCServer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RPCServer.mm; sourceTree = "<group>"; };
01A1DB402652CBA700655BBC /* RPCArgs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RPCArgs.h; sourceTree = "<group>"; };
01A1DB412652CBA700655BBC /* RPCArgs.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RPCArgs.mm; sourceTree = "<group>"; };
01A9B7B2265BD1FD000D092F /* libtvm_runtime.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libtvm_runtime.dylib; path = "${TVM_BUILD_DIR}/libtvm_runtime.dylib"; sourceTree = "<group>"; };
C026374B1F1C25E8007247A9 /* tvmrpc.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = tvmrpc.app; sourceTree = BUILT_PRODUCTS_DIR; };
C026374F1F1C25E8007247A9 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
C02637511F1C25E8007247A9 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
C02637521F1C25E8007247A9 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
C02637541F1C25E8007247A9 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
C02637581F1C25E8007247A9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
C026375A1F1C25E8007247A9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
C026375D1F1C25E8007247A9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
C026375F1F1C25E8007247A9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
C02637651F1C2690007247A9 /* TVMRuntime.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TVMRuntime.mm; sourceTree = "<group>"; };
C02637681F1C26AF007247A9 /* ViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ViewController.mm; sourceTree = "<group>"; };
D7685AD224390EAD00D1469C /* CoreML.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreML.framework; path = System/Library/Frameworks/CoreML.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
C02637481F1C25E8007247A9 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
01A9B7B3265BD1FD000D092F /* libtvm_runtime.dylib in Frameworks */,
D7685AD324390EAE00D1469C /* CoreML.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
C02637421F1C25E8007247A9 = {
isa = PBXGroup;
children = (
C026374D1F1C25E8007247A9 /* tvmrpc */,
C026374C1F1C25E8007247A9 /* Products */,
D7685AD124390EAD00D1469C /* Frameworks */,
);
indentWidth = 2;
sourceTree = "<group>";
tabWidth = 2;
};
C026374C1F1C25E8007247A9 /* Products */ = {
isa = PBXGroup;
children = (
C026374B1F1C25E8007247A9 /* tvmrpc.app */,
);
name = Products;
sourceTree = "<group>";
};
C026374D1F1C25E8007247A9 /* tvmrpc */ = {
isa = PBXGroup;
children = (
016B19C02657B390002E1719 /* RPCServer.h */,
016B19C12657B390002E1719 /* RPCServer.mm */,
01A1DB402652CBA700655BBC /* RPCArgs.h */,
01A1DB412652CBA700655BBC /* RPCArgs.mm */,
C02637651F1C2690007247A9 /* TVMRuntime.mm */,
C02637511F1C25E8007247A9 /* AppDelegate.h */,
C02637521F1C25E8007247A9 /* AppDelegate.m */,
C02637541F1C25E8007247A9 /* ViewController.h */,
C02637681F1C26AF007247A9 /* ViewController.mm */,
C02637571F1C25E8007247A9 /* Main.storyboard */,
C026375A1F1C25E8007247A9 /* Assets.xcassets */,
C026375C1F1C25E8007247A9 /* LaunchScreen.storyboard */,
C026375F1F1C25E8007247A9 /* Info.plist */,
C026374E1F1C25E8007247A9 /* Supporting Files */,
);
path = tvmrpc;
sourceTree = "<group>";
};
C026374E1F1C25E8007247A9 /* Supporting Files */ = {
isa = PBXGroup;
children = (
C026374F1F1C25E8007247A9 /* main.m */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
D7685AD124390EAD00D1469C /* Frameworks */ = {
isa = PBXGroup;
children = (
01A9B7B2265BD1FD000D092F /* libtvm_runtime.dylib */,
D7685AD224390EAD00D1469C /* CoreML.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
C026374A1F1C25E8007247A9 /* tvmrpc */ = {
isa = PBXNativeTarget;
buildConfigurationList = C02637621F1C25E8007247A9 /* Build configuration list for PBXNativeTarget "tvmrpc" */;
buildPhases = (
C02637471F1C25E8007247A9 /* Sources */,
C02637481F1C25E8007247A9 /* Frameworks */,
C02637491F1C25E8007247A9 /* Resources */,
01A9B7B9265BD307000D092F /* Embed Libraries */,
);
buildRules = (
);
dependencies = (
);
name = tvmrpc;
productName = tvmrpc;
productReference = C026374B1F1C25E8007247A9 /* tvmrpc.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
C02637431F1C25E8007247A9 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0830;
ORGANIZATIONNAME = dmlc;
TargetAttributes = {
C026374A1F1C25E8007247A9 = {
CreatedOnToolsVersion = 8.3.3;
DevelopmentTeam = 3FR42MXLK9;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = C02637461F1C25E8007247A9 /* Build configuration list for PBXProject "tvmrpc" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
English,
en,
Base,
);
mainGroup = C02637421F1C25E8007247A9;
productRefGroup = C026374C1F1C25E8007247A9 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
C026374A1F1C25E8007247A9 /* tvmrpc */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
C02637491F1C25E8007247A9 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C026375E1F1C25E8007247A9 /* LaunchScreen.storyboard in Resources */,
C026375B1F1C25E8007247A9 /* Assets.xcassets in Resources */,
C02637591F1C25E8007247A9 /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
C02637471F1C25E8007247A9 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C02637691F1C26AF007247A9 /* ViewController.mm in Sources */,
01A1DB432652CBA700655BBC /* RPCArgs.mm in Sources */,
016B19C22657B390002E1719 /* RPCServer.mm in Sources */,
C02637531F1C25E8007247A9 /* AppDelegate.m in Sources */,
C02637661F1C2690007247A9 /* TVMRuntime.mm in Sources */,
C02637501F1C25E8007247A9 /* main.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
C02637571F1C25E8007247A9 /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
C02637581F1C25E8007247A9 /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
C026375C1F1C25E8007247A9 /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
C026375D1F1C25E8007247A9 /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
C02637601F1C25E8007247A9 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_BITCODE = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
"TVM_LOG_CUSTOMIZE=1",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 10.3;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
C02637611F1C25E8007247A9 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_BITCODE = NO;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
"TVM_LOG_CUSTOMIZE=1",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 10.3;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
C02637631F1C25E8007247A9 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
DEVELOPMENT_TEAM = 3FR42MXLK9;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
"USE_CUSTOM_DSO_LOADER=${USE_CUSTOM_DSO_LOADER}",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
HEADER_SEARCH_PATHS = (
../../include,
../../3rdparty/dlpack/include,
"${TVM_BUILD_DIR}/apps/ios_rpc/custom_dso_loader/include",
);
INFOPLIST_FILE = tvmrpc/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"${TVM_BUILD_DIR}/apps/ios_rpc/custom_dso_loader/lib",
"${TVM_BUILD_DIR}",
);
OTHER_LDFLAGS = "${_DSO_LOADER_NAME_${USE_CUSTOM_DSO_LOADER}}";
PRODUCT_BUNDLE_IDENTIFIER = org.apache.tvmiosrpc;
PRODUCT_NAME = "$(TARGET_NAME)";
TVM_BUILD_DIR = "path-to-tvm-ios-build-folder";
USE_CUSTOM_DSO_LOADER = 1;
WARNING_CFLAGS = "-Wno-shorten-64-to-32";
_DSO_LOADER_NAME_0 = "";
_DSO_LOADER_NAME_1 = "-lmacho_dyld";
};
name = Debug;
};
C02637641F1C25E8007247A9 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
DEVELOPMENT_TEAM = 3FR42MXLK9;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
"USE_CUSTOM_DSO_LOADER=${USE_CUSTOM_DSO_LOADER}",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
HEADER_SEARCH_PATHS = (
../../include,
../../3rdparty/dlpack/include,
"${TVM_BUILD_DIR}/apps/ios_rpc/custom_dso_loader/include",
);
INFOPLIST_FILE = tvmrpc/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"${TVM_BUILD_DIR}/apps/ios_rpc/custom_dso_loader/lib",
"${TVM_BUILD_DIR}",
);
OTHER_LDFLAGS = "${_DSO_LOADER_NAME_${USE_CUSTOM_DSO_LOADER}}";
PRODUCT_BUNDLE_IDENTIFIER = org.apache.tvmiosrpc;
PRODUCT_NAME = "$(TARGET_NAME)";
TVM_BUILD_DIR = "path-to-tvm-ios-build-folder";
USE_CUSTOM_DSO_LOADER = 1;
WARNING_CFLAGS = "-Wno-shorten-64-to-32";
_DSO_LOADER_NAME_0 = "";
_DSO_LOADER_NAME_1 = "-lmacho_dyld";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
C02637461F1C25E8007247A9 /* Build configuration list for PBXProject "tvmrpc" */ = {
isa = XCConfigurationList;
buildConfigurations = (
C02637601F1C25E8007247A9 /* Debug */,
C02637611F1C25E8007247A9 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
C02637621F1C25E8007247A9 /* Build configuration list for PBXNativeTarget "tvmrpc" */ = {
isa = XCConfigurationList;
buildConfigurations = (
C02637631F1C25E8007247A9 /* Debug */,
C02637641F1C25E8007247A9 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = C02637431F1C25E8007247A9 /* Project object */;
}
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--- Licensed to the Apache Software Foundation (ASF) under one -->
<!--- or more contributor license agreements. See the NOTICE file -->
<!--- distributed with this work for additional information -->
<!--- regarding copyright ownership. The ASF licenses this file -->
<!--- to you 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. -->
<Workspace
version = "1.0">
<FileRef
location = "self:tvmrpc.xcodeproj">
</FileRef>
</Workspace>
@@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--- Licensed to the Apache Software Foundation (ASF) under one -->
<!--- or more contributor license agreements. See the NOTICE file -->
<!--- distributed with this work for additional information -->
<!--- regarding copyright ownership. The ASF licenses this file -->
<!--- to you 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. -->
<Scheme
LastUpgradeVersion = "1250"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C026374A1F1C25E8007247A9"
BuildableName = "tvmrpc.app"
BlueprintName = "tvmrpc"
ReferencedContainer = "container:tvmrpc.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C026374A1F1C25E8007247A9"
BuildableName = "tvmrpc.app"
BlueprintName = "tvmrpc"
ReferencedContainer = "container:tvmrpc.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C026374A1F1C25E8007247A9"
BuildableName = "tvmrpc.app"
BlueprintName = "tvmrpc"
ReferencedContainer = "container:tvmrpc.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
+30
View File
@@ -0,0 +1,30 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/*!
* \file AppDelegate.h
*/
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property(strong, nonatomic) UIWindow* window;
@end
+68
View File
@@ -0,0 +1,68 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/*!
* \file AppDelegate.mm
*/
#import "AppDelegate.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
// Override point for customization after application launch.
return YES;
}
- (void)applicationWillResignActive:(UIApplication*)application {
// Sent when the application is about to move from active to inactive state. This can occur for
// certain types of temporary interruptions (such as an incoming phone call or SMS message) or
// when the user quits the application and it begins the transition to the background state. Use
// this method to pause ongoing tasks, disable timers, and invalidate graphics rendering
// callbacks. Games should use this method to pause the game.
}
- (void)applicationDidEnterBackground:(UIApplication*)application {
// Use this method to release shared resources, save user data, invalidate timers, and store
// enough application state information to restore your application to its current state in case
// it is terminated later. If your application supports background execution, this method is
// called instead of applicationWillTerminate: when the user quits.
}
- (void)applicationWillEnterForeground:(UIApplication*)application {
// Called as part of the transition from the background to the active state; here you can undo
// many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication*)application {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If
// the application was previously in the background, optionally refresh the user interface.
}
- (void)applicationWillTerminate:(UIApplication*)application {
// Called when the application is about to terminate. Save data if appropriate. See also
// applicationDidEnterBackground:.
}
@end
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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.
-->
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11134" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11106"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>
@@ -0,0 +1,127 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--- Licensed to the Apache Software Foundation (ASF) under one -->
<!--- or more contributor license agreements. See the NOTICE file -->
<!--- distributed with this work for additional information -->
<!--- regarding copyright ownership. The ASF licenses this file -->
<!--- to you 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. -->
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina5_5" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="9090" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="eLf-oe-8c9">
<rect key="frame" x="110" y="186" width="234" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits"/>
</textField>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="iphone" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="o4Q-bP-8Cn">
<rect key="frame" x="110" y="224" width="234" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits"/>
</textField>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Address" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Vkh-HN-g3u">
<rect key="frame" x="39" y="148" width="63" height="27"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Port" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vtu-2M-JLP">
<rect key="frame" x="61" y="186" width="41" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Key" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5MW-dM-69p">
<rect key="frame" x="61" y="224" width="41" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="7yQ-tQ-Qqc">
<rect key="frame" x="110" y="148" width="234" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits"/>
</textField>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" fixedFrame="YES" editable="NO" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="8jt-IO-eTb">
<rect key="frame" x="69" y="374" width="254" height="207"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
<segmentedControl opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="ocx-rT-xCk">
<rect key="frame" x="110" y="262" width="234" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<segments>
<segment title="Tracker"/>
<segment title="Proxy"/>
<segment title="RPC"/>
</segments>
</segmentedControl>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Disconnected" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9Zy-zO-r1A">
<rect key="frame" x="143" y="99" width="106" height="20.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="m1Y-7m-LeH">
<rect key="frame" x="110" y="306" width="234" height="60"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<state key="normal" title="Connect"/>
<connections>
<action selector="connect:" destination="BYZ-38-t0r" eventType="touchUpInside" id="1cx-nV-onb"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
<connections>
<outlet property="ConnectButton" destination="m1Y-7m-LeH" id="Pc5-Cl-uxz"/>
<outlet property="ModeSelector" destination="ocx-rT-xCk" id="5r7-XQ-tMk"/>
<outlet property="infoText" destination="8jt-IO-eTb" id="TmG-H2-eJY"/>
<outlet property="proxyKey" destination="o4Q-bP-8Cn" id="XFh-Qz-86e"/>
<outlet property="proxyPort" destination="eLf-oe-8c9" id="f1g-tu-Q5U"/>
<outlet property="proxyURL" destination="7yQ-tQ-Qqc" id="KBr-zC-3sD"/>
<outlet property="statusLabel" destination="9Zy-zO-r1A" id="gHN-jG-4ii"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="24.800000000000001" y="36.431784107946029"/>
</scene>
</scenes>
</document>
+62
View File
@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--- Licensed to the Apache Software Foundation (ASF) under one -->
<!--- or more contributor license agreements. See the NOTICE file -->
<!--- distributed with this work for additional information -->
<!--- regarding copyright ownership. The ASF licenses this file -->
<!--- to you 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. -->
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
+77
View File
@@ -0,0 +1,77 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef TVM_APPS_IOS_RPC_ARGS_H_
#define TVM_APPS_IOS_RPC_ARGS_H_
#import "RPCServer.h"
#ifdef __cplusplus
extern "C" {
#endif
/*!
* \brief Struct representing arguments of iOS RPC app
*/
typedef struct RPCArgs_t {
/// Tracker or Proxy address (actually ip)
const char* host_url;
/// Tracker or Proxy port
int host_port;
/// device key to report
const char* key;
/// custom address to report into Tracker. Ignored for other server modes.
const char* custom_addr;
/// Verbose mode. Will print status messages to std out.
/// 0 - no prints , 1 - print state to output
bool verbose;
/// Immediate server launch. No UI interaction.
/// 0 - UI interaction, 1 - automatically connect on launch
bool immediate_connect;
/// Server mode
RPCServerMode server_mode;
} RPCArgs;
/*!
* \brief Get current global RPC args
*/
RPCArgs get_current_rpc_args(void);
/*!
* \brief Set current global RPC args and update values in app cache
*/
void set_current_rpc_args(RPCArgs args);
/*!
* \brief Pars command line args and update current global RPC args
* Also updates values in app cache
*/
void update_rpc_args(int argc, char* argv[]);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // TVM_APPS_IOS_RPC_ARGS_H_
+197
View File
@@ -0,0 +1,197 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#import "RPCArgs.h"
#import <Foundation/Foundation.h>
#import "../../../src/support/socket.h"
#import "../../../src/support/utils.h"
#import <string>
using std::string;
const char* kUsage =
"\n"
"iOS tvmrpc application supported flags:\n"
"--host_url The tracker/proxy address, Default=0.0.0.0\n"
"--host_port The tracker/proxy port, Default=9190\n"
"--key The key used to identify the device type in tracker. Default=\"\"\n"
"--custom_addr Custom IP Address to Report to RPC Tracker. Default=\"\"\n"
"--immediate_connect No UI interconnection, connect to tracker immediately. Default=False\n"
"--verbose Allow to print status info to std out. Default=False\n"
"--server_mode Server mode. Can be \"standalone\", \"proxy\" or \"tracker\". "
"Default=standalone \n"
"\n";
struct RPCArgs_cpp {
string host_url = "0.0.0.0";
int host_port = 9190;
string key;
string custom_addr = "null";
bool immediate_connect = false;
bool verbose = false;
RPCServerMode server_mode = RPCServerMode_Tracker;
operator RPCArgs() const {
return RPCArgs{.host_url = host_url.c_str(),
.host_port = host_port,
.key = key.c_str(),
.custom_addr = custom_addr.c_str(),
.verbose = verbose,
.immediate_connect = immediate_connect,
.server_mode = server_mode};
};
RPCArgs_cpp& operator=(const RPCArgs& args) {
host_url = args.host_url;
host_port = args.host_port;
key = args.key;
custom_addr = args.custom_addr;
verbose = args.verbose;
immediate_connect = args.immediate_connect;
server_mode = args.server_mode;
return *this;
}
};
struct RPCArgs_cpp g_rpc_args;
static void restore_from_cache() {
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
auto get_string_from_cache = [defaults](const char* key) {
NSString* ns_key = [NSString stringWithUTF8String:key];
NSString* ns_val = [defaults stringForKey:ns_key];
return std::string(ns_val != nil ? [ns_val UTF8String] : "");
};
auto get_int_from_cache = [defaults](const char* key) {
NSString* ns_key = [NSString stringWithUTF8String:key];
return static_cast<int>([defaults integerForKey:ns_key]);
};
g_rpc_args.host_url = get_string_from_cache("RPCArgs_url");
g_rpc_args.host_port = get_int_from_cache("RPCArgs_port");
g_rpc_args.key = get_string_from_cache("RPCArgs_key");
}
static void update_in_cache() {
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:[NSString stringWithUTF8String:g_rpc_args.host_url.c_str()]
forKey:@"RPCArgs_url"];
[defaults setInteger:g_rpc_args.host_port forKey:@"RPCArgs_port"];
[defaults setObject:[NSString stringWithUTF8String:g_rpc_args.key.c_str()] forKey:@"RPCArgs_key"];
}
string GetCmdOption(int argc, char* argv[], string option, bool key = false) {
string cmd;
for (int i = 1; i < argc; ++i) {
string arg = argv[i];
if (arg.find(option) == 0) {
if (key) {
cmd = argv[i];
return cmd;
}
// We assume "=" is the end of option.
TVM_FFI_ICHECK_EQ(*option.rbegin(), '=');
cmd = arg.substr(arg.find('=') + 1);
return cmd;
}
}
return cmd;
}
void update_rpc_args(int argc, char* argv[]) {
restore_from_cache();
RPCArgs_cpp& args = g_rpc_args;
using tvm::support::IsNumber;
using tvm::support::ValidateIP;
constexpr int MAX_PORT_NUM = 65535;
const string immediate_connect = GetCmdOption(argc, argv, "--immediate_connect", true);
args.immediate_connect = !immediate_connect.empty();
const string verbose = GetCmdOption(argc, argv, "--verbose", true);
args.verbose = !verbose.empty();
const string server_mode = GetCmdOption(argc, argv, "--server_mode=", false);
if (!server_mode.empty()) {
if (server_mode == "tracker") {
args.server_mode = RPCServerMode_Tracker;
} else if (server_mode == "proxy") {
args.server_mode = RPCServerMode_Proxy;
} else if (server_mode == "standalone") {
args.server_mode = RPCServerMode_Standalone;
} else {
LOG(WARNING) << "Wrong server_mode value.";
LOG(INFO) << kUsage;
exit(1);
}
}
const string host_url = GetCmdOption(argc, argv, "--host_url=");
if (!host_url.empty()) {
if (!ValidateIP(host_url)) {
LOG(WARNING) << "Wrong tracker address format.";
LOG(INFO) << kUsage;
exit(1);
}
args.host_url = host_url;
}
const string host_port = GetCmdOption(argc, argv, "--host_port=");
if (!host_port.empty()) {
if (!IsNumber(host_port) || stoi(host_port) > MAX_PORT_NUM) {
LOG(WARNING) << "Wrong trackerport number.";
LOG(INFO) << kUsage;
exit(1);
}
args.host_port = stoi(host_port);
}
const string key = GetCmdOption(argc, argv, "--key=");
if (!key.empty()) {
args.key = key;
}
const string custom_addr = GetCmdOption(argc, argv, "--custom_addr=");
if (!custom_addr.empty()) {
if (!ValidateIP(custom_addr)) {
LOG(WARNING) << "Wrong custom address format.";
LOG(INFO) << kUsage;
exit(1);
}
args.custom_addr = '"' + custom_addr + '"';
}
update_in_cache();
}
RPCArgs get_current_rpc_args(void) { return g_rpc_args; }
void set_current_rpc_args(RPCArgs args) {
g_rpc_args = args;
update_in_cache();
}
+100
View File
@@ -0,0 +1,100 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/*!
* \file Provide interfaces to launch and control RPC Service routine
*/
#import <Foundation/Foundation.h>
/*!
* \brief Enum with possible status of RPC server
* Used to report state to listener
*/
typedef enum {
RPCServerStatus_Launched, // Worker thread is launched
RPCServerStatus_Stopped, // Worker thread stopped
RPCServerStatus_Connected, // Connected to Proxy/Tracker
RPCServerStatus_Disconnected, // Disconnected from Proxy/Tracker
RPCServerStatus_RPCSessionStarted, // RPC session is started
RPCServerStatus_RPCSessionFinished // RPC session is finished
} RPCServerStatus;
/*!
* \brief Enum with modes of servicing supported by RPCServer
*/
typedef enum {
/// Tracker mode. Same as Standalone Server plus register it into Tracker.
RPCServerMode_Tracker,
/// Proxy mode. Connect to proxy server and wait response.
RPCServerMode_Proxy,
/// Standalone RPC server mode. Open port with RPC server and wait incoming connection.
RPCServerMode_Standalone
} RPCServerMode;
/*!
* \brief Listener for events happened with RPCServer
*/
@protocol RPCServerEventListener <NSObject>
/// Callback to notifying about new status
- (void)onError:(NSString*)msg;
/// Callback to notifying about error
- (void)onStatusChanged:(RPCServerStatus)status;
@end
/*!
* \brief RPC Server instance
* Contains internal worker thread plus
*/
@interface RPCServer : NSObject <NSStreamDelegate>
/// Event listener delegate to set
@property(retain) id<RPCServerEventListener> delegate;
/// Device key to report during RPC session
@property(retain) NSString* key;
/// Host address of Proxy/Tracker server (generally IPv4). Ignored for Standalone mode.
@property(retain) NSString* host;
/// Port of Proxy/Tracker server. Ignored for Standalone mode.
@property int port;
/// Custom address to report into tracker server (optional). Ignored for Standalone/Proxy modes
@property(retain) NSString* custom_addr;
/// Trigger to enable printing of server state info
@property BOOL verbose;
/// RPC port opened on the device. Ignored for Proxy/Tracker modes
@property int actual_port;
/// IP address of the device. Ignored for Proxy/Tracker modes
@property(retain) NSString* device_addr;
/*!
* \brief Create server with specified servicing mode
* \param mode Mode of server
*/
+ (instancetype)serverWithMode:(RPCServerMode)mode;
/*!
* \brief Start RPC server with options. Non blocking method
*/
- (void)start;
/*!
* \brief Stop RPC server. Non blocking method
*/
- (void)stop;
@end
+814
View File
@@ -0,0 +1,814 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/*!
* \file ViewController.mm
*/
#import "RPCServer.h"
#include <tvm/ffi/function.h>
#include <random>
#include <string>
// To get device WiFi IP
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <netinet/in.h>
#include <sys/socket.h>
// TVM internal header to access Magic keys like kRPCMagic and others
#include "../../../src/runtime/rpc/rpc_endpoint.h"
namespace tvm {
namespace runtime {
/*!
* \brief Message handling function for event driven server.
*
* \param in_bytes The incoming bytes.
* \param event_flag 1: read_available, 2: write_avaiable.
* \return State flag.
* 1: continue running, no need to write,
* 2: need to write
* 0: shutdown
*/
using FEventHandler = ffi::Function;
/*!
* \brief Create a server event handler.
*
* \param outputStream The output stream used to send outputs.
* \param name The name of the server.
* \param remote_key The remote key
* \return The event handler.
*/
FEventHandler CreateServerEventHandler(NSOutputStream* outputStream, std::string name,
std::string remote_key) {
auto event_handler_factory = tvm::ffi::Function::GetGlobal("rpc.CreateEventDrivenServer");
TVM_FFI_ICHECK(event_handler_factory.has_value())
<< "You are using tvm_runtime module built without RPC support. "
<< "Please rebuild it with USE_RPC flag.";
ffi::Function writer_func([outputStream](ffi::PackedArgs args, ffi::Any* rv) {
TVMByteArray* data = args[0].ptr<TVMByteArray>();
int64_t nbytes = [outputStream write:reinterpret_cast<const uint8_t*>(data->data)
maxLength:data->size];
if (nbytes < 0) {
NSLog(@"%@", [outputStream streamError].localizedDescription);
throw tvm::Error("Stream error");
}
*rv = nbytes;
});
return (*event_handler_factory)(writer_func, name, remote_key);
}
/*!
* \brief Helper function to query real IP of device in WiFi network
* \return string with IPv4 in format "192.168.0.1" or "unknown" if cannot detect
*/
static std::string getWiFiAddress() {
std::string address = "unknown";
ifaddrs* interfaces = nullptr;
int success = getifaddrs(&interfaces);
if (success == 0) {
ifaddrs* temp_addr = interfaces;
while (temp_addr != NULL) {
if (temp_addr->ifa_addr->sa_family == AF_INET) {
// Check if interface is en0 which is the wifi connection on the iPhone
if (std::string(temp_addr->ifa_name) == "en0") {
address = inet_ntoa(((sockaddr_in*)temp_addr->ifa_addr)->sin_addr);
}
}
temp_addr = temp_addr->ifa_next;
}
}
freeifaddrs(interfaces);
return address;
}
} // namespace runtime
} // namespace tvm
// Base class for any type of RPC servicing
@interface RPCServerBase : RPCServer
/*!
* Methods should be implemented in inherited classes
*/
- (bool)onReadHandler; // return true - continue feeding, false - stop, try to drain output buffer
- (bool)onWriteHandler; // return true - continue draining, false - no data to write
- (void)onEndEncountered; // called on disconnect or session decided that it's shutdown time
- (void)open; // Initiate listening objects like i/o streams and other resources
- (void)close; // Deinitialize resources opend in "open" method
@end
@implementation RPCServerBase {
// Worker thread
NSThread* worker_thread_;
// Trigger to continue RunLoop processing inside worker_thread_
BOOL shouldKeepRunning;
// Input socket stream
@protected
NSInputStream* inputStream_;
// Output socket stream
NSOutputStream* outputStream_;
// Temporal buffer with data to send
std::string sendBuffer_;
// Temporal receive buffer
std::string recvBuffer_;
// Requested data size to accumulate in recvBuffer_ before continue processing
int requiredToRecv_;
}
/*!
* Start internal worker thread with RunLoop and submit corresponding open handlers into it
* Not blocking
*/
- (void)start {
worker_thread_ = [[NSThread alloc] initWithBlock:^{
@autoreleasepool {
[self notifyState:RPCServerStatus_Launched];
[self open];
shouldKeepRunning = YES;
while (shouldKeepRunning && [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate distantFuture]]);
[self notifyState:RPCServerStatus_Stopped];
}
}];
[worker_thread_ start];
}
/*!
* Send message to workel thread runloop to finish processing
* Not blocking
*/
- (void)stop {
if (worker_thread_ == nil) return;
[self performSelector:@selector(stop_) onThread:worker_thread_ withObject:nil waitUntilDone:NO];
worker_thread_ = nil; // TODO: is it valid? may be better to do that inside NSThread?
}
- (void)stop_ {
[self close];
shouldKeepRunning = NO;
}
/*!
* Base implementation to setup i/o streams
* Will connect to host and port specified in corresponding properties
*/
- (void)open {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)self.host, self.port, &readStream,
&writeStream);
inputStream_ = (__bridge NSInputStream*)readStream;
outputStream_ = (__bridge NSOutputStream*)writeStream;
[inputStream_ setDelegate:self];
[outputStream_ setDelegate:self];
[inputStream_ scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream_ scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream_ open];
[inputStream_ open];
}
/*!
* Base implementation to setup i/o streams
* Will assign i/o streams to provided socket connection.
*/
- (void)openWithSocket:(CFSocketNativeHandle)sock {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocket(NULL, sock, &readStream, &writeStream);
inputStream_ = (__bridge NSInputStream*)readStream;
outputStream_ = (__bridge NSOutputStream*)writeStream;
[inputStream_ setDelegate:self];
[outputStream_ setDelegate:self];
[inputStream_ scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream_ scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream_ open];
[inputStream_ open];
}
/*!
* Close i/o streams associated with connection
*/
- (void)close {
[inputStream_ close];
[outputStream_ close];
[inputStream_ removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream_ removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream_ setDelegate:nil];
[outputStream_ setDelegate:nil];
inputStream_ = nil;
outputStream_ = nil;
}
/// Unimplemented stubs
- (bool)onReadHandler {
return false;
}
- (bool)onWriteHandler {
return false;
}
- (void)onEndEncountered {
}
/*!
* Try to read data from stream and call processing handler
*/
- (void)tryToRead {
const int kBufferSize = 4 << 10; // 4kB buffer
const int prev_size = recvBuffer_.size();
recvBuffer_.resize(kBufferSize);
size_t nbytes = [inputStream_ read:(uint8_t*)recvBuffer_.data() + prev_size
maxLength:recvBuffer_.size() - prev_size];
recvBuffer_.resize(nbytes + prev_size);
// feed while it accept or requested particulat buffer size
while (!recvBuffer_.empty() && requiredToRecv_ <= recvBuffer_.size() && [self onReadHandler]);
}
/*!
* Try to write remaining data to stream and call processing handler
*/
- (void)tryToWrite {
if (!sendBuffer_.empty()) {
size_t nbytes = [outputStream_ write:(uint8_t*)sendBuffer_.data() maxLength:sendBuffer_.size()];
sendBuffer_.erase(0, nbytes);
}
// call write handler while it want be called and space is available
while (sendBuffer_.empty() && [outputStream_ hasSpaceAvailable] && [self onWriteHandler]);
}
/*!
* Main event handler of socket stream events
*/
- (void)stream:(NSStream*)strm handleEvent:(NSStreamEvent)event {
std::string buffer;
switch (event) {
case NSStreamEventOpenCompleted: {
// Nothing
break;
}
case NSStreamEventHasBytesAvailable:
if (strm == inputStream_) {
[self tryToRead];
if ([outputStream_ hasSpaceAvailable]) [self tryToWrite];
}
break;
case NSStreamEventHasSpaceAvailable: {
if (strm == outputStream_) {
[self tryToWrite];
if ([inputStream_ hasBytesAvailable]) [self tryToRead];
}
break;
}
case NSStreamEventErrorOccurred: {
[self notifyError:[strm streamError].localizedDescription];
break;
}
case NSStreamEventEndEncountered: {
[self onEndEncountered];
break;
}
default: {
NSLog(@"Unknown event");
}
}
}
#pragma mark - Helpers
/*!
* Set buffer to send into stream. Try to send immediately or submit to lazy sending
* Non blocking operation
*/
- (void)toSend:(NSData*)data {
sendBuffer_.append(static_cast<const char*>(data.bytes), data.length);
// try to flush buffer
NSInteger sent_size = [outputStream_ write:(uint8_t*)sendBuffer_.data()
maxLength:sendBuffer_.size()];
sendBuffer_.erase(0, sent_size);
}
/*!
* Set buffer to send in packet format [size, data]. Behaviour is same as for toSend.
*/
- (void)toSendPacked:(NSData*)data {
uint32_t packet_size = data.length;
[self toSend:[NSData dataWithBytes:&packet_size length:sizeof(packet_size)]];
[self toSend:data];
}
/*!
*/
- (NSData*)requestInputDataWithSize:(NSInteger)size {
if (recvBuffer_.size() < size) {
requiredToRecv_ = size;
return nil;
}
NSData* res = [NSData dataWithBytes:recvBuffer_.data() length:size];
recvBuffer_.erase(0, size);
return res;
}
/*!
*/
- (NSData*)requestInputDataPacked {
uint32_t size;
if (recvBuffer_.size() < sizeof(size)) {
requiredToRecv_ = sizeof(size);
return nil;
}
size = *(uint32_t*)recvBuffer_.data();
if (recvBuffer_.size() < sizeof(size) + size) {
requiredToRecv_ = sizeof(size) + size;
return nil;
}
NSData* res = [NSData dataWithBytes:recvBuffer_.data() + sizeof(size) length:size];
recvBuffer_.erase(0, sizeof(size) + size);
return res;
};
#pragma mark - Notifiers
/*!
* Notify external listener about error.
* Also print error message to std out in case of Verbose mode
*/
- (void)notifyError:(NSString*)msg {
// Duplicate error message in std output. Host launcher script may listen it.
if (self.verbose) NSLog(@"[IOS-RPC] ERROR: %@", msg);
if (self.delegate) [self.delegate onError:msg];
}
/*!
* Notify external listener about server state changes.
* Also print information to std out in case of Verbose mode
*/
- (void)notifyState:(RPCServerStatus)state {
// Duplicate sattus changing in std output. Host launcher script may listen it.
if (self.verbose) NSLog(@"[IOS-RPC] STATE: %d", state);
if (self.delegate != nil) [self.delegate onStatusChanged:state];
}
@end
@interface RPCServerProxy : RPCServerBase
@end
typedef enum {
RPCServerProxyState_Idle,
RPCServerProxyState_HandshakeToSend,
RPCServerProxyState_HandshakeToRecv,
RPCServerProxyState_Processing,
} RPCServerProxyState;
@implementation RPCServerProxy {
/// Original TVM RPC event handler
tvm::runtime::FEventHandler handler_;
@protected
/// Sate of Proxy client implementation
RPCServerProxyState state_;
}
- (instancetype)init {
if (self = [super init]) {
handler_ = nullptr;
state_ = RPCServerProxyState_Idle;
}
return self;
}
/*!
* Implement matching of internat state on state available for outside users
*/
- (void)setState:(RPCServerProxyState)new_state {
// Send Connected notification because Proxy doesn't response until client connected.
if (new_state == RPCServerProxyState_HandshakeToRecv)
[self notifyState:RPCServerStatus_Connected];
if (new_state == RPCServerProxyState_Idle) [self notifyState:RPCServerStatus_Disconnected];
if (state_ == RPCServerProxyState_HandshakeToRecv && new_state == RPCServerProxyState_Processing)
[self notifyState:RPCServerStatus_RPCSessionStarted];
if (state_ == RPCServerProxyState_Processing && new_state == RPCServerProxyState_Idle)
[self notifyState:RPCServerStatus_RPCSessionStarted];
state_ = new_state;
}
- (bool)onWriteHandler {
switch (state_) {
case RPCServerProxyState_HandshakeToSend: {
// Send together kRPCMagic and server descriptor because of Proxy
int32_t code = tvm::runtime::kRPCMagic;
[self toSend:[NSData dataWithBytes:&code length:sizeof(code)]];
std::string full_key = std::string("server:") + self.key.UTF8String;
[self toSendPacked:[NSData dataWithBytes:full_key.data() length:full_key.size()]];
self.state = RPCServerProxyState_HandshakeToRecv;
return TRUE;
}
case RPCServerProxyState_Processing: {
try {
TVMByteArray dummy{nullptr, 0};
int flag = handler_(dummy, 2);
if (flag == 0) {
[self onEndEncountered];
}
return flag == 2;
} catch (const tvm::Error& e) {
[self close];
}
break;
}
default:
// Nothing
break;
}
return FALSE;
}
- (bool)onReadHandler {
switch (state_) {
case RPCServerProxyState_HandshakeToRecv: {
int32_t code = tvm::runtime::kRPCMagic;
NSData* data = [self requestInputDataWithSize:sizeof(code)];
if (data == nil) return FALSE;
if (*(int32_t*)data.bytes != tvm::runtime::kRPCMagic) {
[self notifyError:@"Wrong response, is not RPC client."];
[self close];
return FALSE;
break;
}
handler_ = tvm::runtime::CreateServerEventHandler(outputStream_, "iphone", "%toinit");
self.state = RPCServerProxyState_Processing;
return TRUE;
break;
}
case RPCServerProxyState_Processing: {
int flag = 1;
if ([outputStream_ hasSpaceAvailable]) {
flag |= 2;
}
// always try to write
try {
TVMByteArray arr{recvBuffer_.data(), recvBuffer_.size()};
flag = handler_(arr, flag);
recvBuffer_.clear();
if (flag == 0) {
[self onEndEncountered];
}
return flag == 1;
} catch (const tvm::Error& e) {
[self close];
}
break;
}
default:
// Nothing
break;
}
return FALSE;
}
- (void)onEndEncountered {
// Automatic reconnection when session is finished.
[self close];
[self open];
}
- (void)open {
[super open];
self.state = RPCServerProxyState_HandshakeToSend;
}
- (void)close {
[super close];
handler_ = nullptr;
self.state = RPCServerProxyState_Idle;
}
@end
@interface RPCServerStandalone : RPCServerProxy
@property(readonly) int rpc_port;
@end
@implementation RPCServerStandalone {
// Socket to listen incoming connections
CFSocketRef socket_;
/// Current socket connection handler
CFSocketNativeHandle connection_;
/// Port range to try bind to socket
int port_range_start;
int port_range_end;
}
- (instancetype)init {
if (self = [super init]) {
connection_ = 0;
port_range_start = 9090;
port_range_end = 9099;
}
return self;
}
- (void)setState:(RPCServerProxyState)new_state {
if (state_ == RPCServerProxyState_Idle && new_state == RPCServerProxyState_HandshakeToSend) {
self.actual_port = _rpc_port;
self.device_addr = [NSString stringWithUTF8String:tvm::runtime::getWiFiAddress().c_str()];
if (self.verbose) {
// Notify host runner script with actual address
NSLog(@"[IOS-RPC] IP: %s", tvm::runtime::getWiFiAddress().c_str());
NSLog(@"[IOS-RPC] PORT: %d", _rpc_port);
}
[self notifyState:RPCServerStatus_Connected];
}
if (new_state == RPCServerProxyState_Idle) [self notifyState:RPCServerStatus_Disconnected];
if (state_ == RPCServerProxyState_HandshakeToRecv && new_state == RPCServerProxyState_Processing)
[self notifyState:RPCServerStatus_RPCSessionStarted];
if (state_ == RPCServerProxyState_Processing && new_state == RPCServerProxyState_HandshakeToSend)
[self notifyState:RPCServerStatus_RPCSessionFinished];
state_ = new_state;
}
- (void)handleConnect:(CFSocketNativeHandle)hdl {
connection_ = hdl;
[super openWithSocket:connection_];
self.state = RPCServerProxyState_HandshakeToSend;
}
static void handleConnect(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address,
const void* data, void* info) {
RPCServerStandalone* it = (__bridge RPCServerStandalone*)(info);
[it handleConnect:*static_cast<const CFSocketNativeHandle*>(data)];
}
- (void)open {
CFSocketContext ctx{};
ctx.info = (__bridge void*)self;
socket_ = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP,
kCFSocketAcceptCallBack, handleConnect, &ctx);
self->_rpc_port = 0;
// Try to bind with range
for (int port = port_range_start; port < port_range_end; port++) {
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_len = sizeof(sin);
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
sin.sin_addr.s_addr = INADDR_ANY;
CFDataRef sincfd = CFDataCreate(kCFAllocatorDefault, (UInt8*)&sin, sizeof(sin));
CFSocketError res = CFSocketSetAddress(socket_, sincfd);
CFRelease(sincfd);
if (res == kCFSocketSuccess) {
self->_rpc_port = port;
break;
}
}
if (self->_rpc_port == 0) {
@throw
[NSException exceptionWithName:@"SocketError"
reason:[NSString stringWithFormat:@"Unable bind socket to port"
"in range [%d, %d]",
port_range_start, port_range_end]
userInfo:nil];
}
CFRunLoopSourceRef socketsource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket_, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), socketsource, kCFRunLoopDefaultMode);
self.state = RPCServerProxyState_HandshakeToSend;
}
- (void)closeSocket {
CFSocketInvalidate(socket_);
}
- (void)close {
[super close];
close(connection_);
}
- (void)onEndEncountered {
[self close];
[self notifyState:RPCServerStatus_RPCSessionFinished];
}
@end
@interface RPCServerTracker : RPCServerBase <RPCServerEventListener>
@end
typedef enum {
RPCServerTracker_Idle,
RPCServerTracker_HandshakeToSend,
RPCServerTracker_HandshakeToRecv,
RPCServerTracker_ServerInfoToSend,
RPCServerTracker_ServerInfoToRecv,
RPCServerTracker_ReportResToSend,
RPCServerTracker_ReportResToRecv,
RPCServerTracker_UpdateKeyToSend,
RPCServerTracker_UpdateKeyToRecv,
RPCServerTracker_WaitConnection
} RPCServerTrackerState;
@implementation RPCServerTracker {
RPCServerTrackerState state_;
RPCServerStandalone* rpc_server_;
}
- (void)setState:(RPCServerTrackerState)new_state {
if (state_ == RPCServerTracker_ReportResToRecv && new_state == RPCServerTracker_WaitConnection)
[self notifyState:RPCServerStatus_Connected];
if (state_ == RPCServerTracker_WaitConnection && new_state == RPCServerTracker_Idle)
[self notifyState:RPCServerStatus_Disconnected];
state_ = new_state;
}
- (bool)onWriteHandler {
switch (state_) {
case RPCServerTracker_HandshakeToSend: {
int32_t code = tvm::runtime::kRPCTrackerMagic;
[self toSend:[NSData dataWithBytes:&code length:sizeof(code)]];
self.state = RPCServerTracker_HandshakeToRecv;
return TRUE;
break;
}
case RPCServerTracker_ServerInfoToSend: {
std::ostringstream ss;
ss << "[" << static_cast<int>(tvm::runtime::TrackerCode::kUpdateInfo)
<< ", {\"key\": \"server:" << self.key.UTF8String << "\", \"addr\": ["
<< self.custom_addr.UTF8String << ", \"" << self.port << "\"]}]";
std::string data_s = ss.str();
[self toSendPacked:[NSData dataWithBytes:data_s.data() length:data_s.length()]];
self.state = RPCServerTracker_ServerInfoToRecv;
return TRUE;
break;
}
case RPCServerTracker_ReportResToSend: {
std::mt19937 gen(std::random_device{}());
std::uniform_real_distribution<float> dis(0.0, 1.0);
std::string address_to_report = "null";
if (self.custom_addr != nil && self.custom_addr.length != 0) {
address_to_report = self.custom_addr.UTF8String;
}
std::string matchkey = std::string(self.key.UTF8String) + ":" + std::to_string(dis(gen));
std::ostringstream ss;
ss << "[" << static_cast<int>(tvm::runtime::TrackerCode::kPut) << ", \""
<< self.key.UTF8String << "\", [" << rpc_server_.rpc_port << ", \"" << matchkey << "\"], "
<< address_to_report << "]";
std::string data_s = ss.str();
[self toSendPacked:[NSData dataWithBytes:data_s.data() length:data_s.length()]];
self.state = RPCServerTracker_ReportResToRecv;
return TRUE;
break;
}
default:
// Nothing
break;
}
return FALSE;
}
- (bool)onReadHandler {
static const std::string resp_OK =
std::to_string(static_cast<int>(tvm::runtime::TrackerCode::kSuccess));
switch (state_) {
case RPCServerTracker_HandshakeToRecv: {
NSData* data = [self requestInputDataWithSize:sizeof(int)];
if (data == nil) return FALSE;
if (*(int*)data.bytes != tvm::runtime::kRPCTrackerMagic) {
[self notifyError:@"Wrong response, is not RPC Tracker."];
[self close];
return FALSE;
break;
}
self.state = RPCServerTracker_ServerInfoToSend;
return TRUE;
break;
}
case RPCServerTracker_ServerInfoToRecv: {
NSData* data = [self requestInputDataPacked];
if (data == nil) return FALSE;
if (std::string((char*)data.bytes, data.length) != resp_OK) {
[self notifyError:@"Failed to Update info on tracker. Response is not OK."];
[self close];
return FALSE;
break;
}
self.state = RPCServerTracker_ReportResToSend;
return TRUE;
break;
}
case RPCServerTracker_ReportResToRecv: {
NSData* data = [self requestInputDataPacked];
if (data == nil) return FALSE;
if (std::string((char*)data.bytes, data.length) != resp_OK) {
[self notifyError:@"Failed to Put server into tracker. Response is not OK."];
[self close];
return FALSE;
break;
}
self.state = RPCServerTracker_WaitConnection;
return TRUE;
break;
}
default:
// Nothing
break;
}
return FALSE;
}
- (void)onEndEncountered {
[self close];
}
- (void)close {
[rpc_server_ close];
[rpc_server_ closeSocket];
[super close];
self.state = RPCServerTracker_Idle;
}
- (void)open {
// Start internal Standalone RPC server at first
rpc_server_ = [[RPCServerStandalone alloc] init];
rpc_server_.key = self.key;
rpc_server_.delegate = self;
[rpc_server_ open];
[super open];
self.state = RPCServerTracker_HandshakeToSend;
}
- (void)onError:(NSString*)msg {
// transfer error form internal rpc_server_ to real delegate
[self notifyError:msg];
}
- (void)onStatusChanged:(RPCServerStatus)status {
if (status == RPCServerStatus_RPCSessionFinished) {
[self notifyState:status];
self.state = RPCServerTracker_ReportResToSend;
[self tryToWrite];
}
}
@end
@implementation RPCServer
+ (instancetype)serverWithMode:(RPCServerMode)mode {
if (mode == RPCServerMode_Standalone) return [[RPCServerStandalone alloc] init];
if (mode == RPCServerMode_Proxy) return [[RPCServerProxy alloc] init];
if (mode == RPCServerMode_Tracker) return [[RPCServerTracker alloc] init];
return nil;
}
/// Unimplemented stubs
- (void)start {
}
- (void)stop {
}
@end
+128
View File
@@ -0,0 +1,128 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/*!
* \file TVMRuntime.mm
*/
#import <Foundation/Foundation.h>
#include <tvm/ffi/function.h>
#include <tvm/ffi/reflection/registry.h>
#include "RPCArgs.h"
// internal TVM header
#include <../../../src/runtime/file_utils.h>
#if defined(USE_CUSTOM_DSO_LOADER) && USE_CUSTOM_DSO_LOADER == 1
// internal TVM header to achieve Library class
#include <../../../3rdparty/tvm-ffi/src/ffi/extra/library_module.h>
#include <custom_dlfcn.h>
#endif
namespace tvm {
namespace runtime {
namespace detail {
// Override logging mechanism
[[noreturn]] void LogFatalImpl(const std::string& file, int lineno, const std::string& message) {
throw tvm::runtime::InternalError(file, lineno, message);
}
void LogMessageImpl(const std::string& file, int lineno, int level, const std::string& message) {
NSLog(@"%s:%d: %s", file.c_str(), lineno, message.c_str());
}
} // namespace detail
TVM_FFI_STATIC_INIT_BLOCK() {
namespace refl = tvm::ffi::reflection;
refl::GlobalDef()
.def_packed("tvm.rpc.server.workpath",
[](ffi::PackedArgs args, ffi::Any* rv) {
static const std::string base_ = NSTemporaryDirectory().UTF8String;
const auto path = args[0].cast<std::string>();
*rv = base_ + "/" + path;
})
.def_packed("tvm.rpc.server.load_module", [](ffi::PackedArgs args, ffi::Any* rv) {
auto name = args[0].cast<std::string>();
std::string fmt = GetFileFormat(name, "");
NSString* base;
if (fmt == "dylib") {
// only load dylib from frameworks.
NSBundle* bundle = [NSBundle mainBundle];
base = [[bundle privateFrameworksPath] stringByAppendingPathComponent:@"tvm"];
if (tvm::ffi::Function::GetGlobal("ffi.Module.load_from_file.dylib_custom")) {
// Custom dso loader is present. Will use it.
base = NSTemporaryDirectory();
fmt = "dylib_custom";
}
} else {
// Load other modules in tempdir.
base = NSTemporaryDirectory();
}
NSString* path =
[base stringByAppendingPathComponent:[NSString stringWithUTF8String:name.c_str()]];
name = [path UTF8String];
*rv = Module::LoadFromFile(name, fmt);
LOG(INFO) << "Load module from " << name << " ...";
});
}
#if defined(USE_CUSTOM_DSO_LOADER) && USE_CUSTOM_DSO_LOADER == 1
// Custom dynamic library loader. Supports unsigned binary
class UnsignedDSOLoader final : public Library {
public:
~UnsignedDSOLoader() {
if (lib_handle_) {
custom_dlclose(lib_handle_);
lib_handle_ = nullptr;
};
}
void Init(const std::string& name) {
lib_handle_ = custom_dlopen(name.c_str(), RTLD_NOW | RTLD_LOCAL);
TVM_FFI_ICHECK(lib_handle_ != nullptr)
<< "Failed to load dynamic shared library " << name << " " << custom_dlerror();
}
void* GetSymbol(const char* name) final { return custom_dlsym(lib_handle_, name); }
private:
// Library handle
void* lib_handle_{nullptr};
};
// Add UnsignedDSOLoader plugin in global registry
TVM_FFI_STATIC_INIT_BLOCK() {
namespace refl = tvm::ffi::reflection;
refl::GlobalDef().def_packed("ffi.Module.load_from_file.dylib_custom",
[](ffi::PackedArgs args, ffi::Any* rv) {
auto n = ffi::make_object<UnsignedDSOLoader>();
n->Init(args[0]);
*rv = tvm::ffi::CreateLibraryModule(n);
});
}
#endif
} // namespace runtime
} // namespace tvm
+39
View File
@@ -0,0 +1,39 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/*!
* \file ViewController.h
*/
#import <UIKit/UIKit.h>
#import "RPCServer.h"
@interface ViewController : UIViewController <RPCServerEventListener, UITextFieldDelegate>
@property(weak, nonatomic) IBOutlet UITextField* proxyURL;
@property(weak, nonatomic) IBOutlet UITextField* proxyPort;
@property(weak, nonatomic) IBOutlet UITextField* proxyKey;
@property(weak, nonatomic) IBOutlet UILabel* statusLabel;
@property(weak, nonatomic) IBOutlet UITextView* infoText;
- (IBAction)connect:(id)sender;
@property(retain, nonatomic) IBOutlet UIButton* ConnectButton;
@property(retain, nonatomic) IBOutlet UISegmentedControl* ModeSelector;
@end
+169
View File
@@ -0,0 +1,169 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/*!
* \file ViewController.mm
*/
#import "ViewController.h"
#import "RPCArgs.h"
@implementation ViewController {
// server implementation
RPCServer* server_;
// Button state. True - push will start connection, false - push will disconnect
bool to_connect_;
}
- (void)viewDidLoad {
// To handle end editing events
self.proxyURL.delegate = self;
self.proxyPort.delegate = self;
self.proxyKey.delegate = self;
RPCArgs args = get_current_rpc_args();
self.proxyURL.text = @(args.host_url);
self.proxyPort.text = @(args.host_port).stringValue;
self.proxyKey.text = @(args.key);
self.ModeSelector.selectedSegmentIndex = args.server_mode;
self->to_connect_ = true;
// Add border to button
void (^addBorder)(UIButton* btn) = ^(UIButton* btn) {
btn.layer.borderWidth = 2.0f;
btn.layer.borderColor = self.ConnectButton.currentTitleColor.CGColor;
btn.layer.cornerRadius = 10;
};
addBorder(self.ConnectButton);
// Connect to tracker immediately
if (args.immediate_connect) {
[self disableUIInteraction];
[self open];
}
}
/*!
* \brief Disable all UI elements
*/
- (void)disableUIInteraction {
void (^disable)(UITextField* field) = ^(UITextField* field) {
field.enabled = NO;
field.backgroundColor = [UIColor lightGrayColor];
};
void (^disableButton)(UIButton* btn) = ^(UIButton* btn) {
btn.enabled = NO;
btn.layer.borderColor = btn.currentTitleColor.CGColor;
};
disable(self.proxyURL);
disable(self.proxyPort);
disable(self.proxyKey);
disableButton(self.ConnectButton);
self.ModeSelector.enabled = NO;
}
/*!
* \brief Start RPC server
*/
- (void)open {
RPCArgs args = get_current_rpc_args();
RPCServerMode server_mode = static_cast<RPCServerMode>(self.ModeSelector.selectedSegmentIndex);
server_ = [RPCServer serverWithMode:server_mode];
server_.host = self.proxyURL.text;
server_.port = self.proxyPort.text.intValue;
server_.key = self.proxyKey.text;
server_.custom_addr = [NSString stringWithUTF8String:args.custom_addr];
server_.verbose = args.verbose;
server_.delegate = self;
[server_ start];
self.infoText.text = @"";
self.statusLabel.text = @"Connecting...";
}
/*!
* \brief Stop RPC server
*/
- (void)close {
[server_ stop];
self.statusLabel.text = @"Disconnecting...";
}
#pragma mark - Button responders
/*!
* \brief Connect/disconnect button handler
*/
- (IBAction)connect:(id)sender {
[[self view] endEditing:YES]; // to hide keyboard
(to_connect_ ^= true) ? [self close] : [self open];
[self.ConnectButton setTitle:to_connect_ ? @"Connect" : @"Disconenct"
forState:UIControlStateNormal];
}
#pragma mark - UITextFieldDelegate
- (BOOL)textFieldShouldReturn:(UITextField*)textField {
[[self view] endEditing:YES]; // to hide keyboard on ret key
return FALSE;
}
- (void)textFieldDidEndEditing:(UITextField*)textField {
// Update values in app arg cache
RPCArgs args = get_current_rpc_args();
args.host_url = [self.proxyURL.text UTF8String];
args.host_port = [self.proxyPort.text intValue];
args.key = [self.proxyKey.text UTF8String];
set_current_rpc_args(args);
}
#pragma mark - RPCServerEvenlListener
- (void)onError:(NSString*)msg {
dispatch_sync(dispatch_get_main_queue(), ^{
self.infoText.text = [NSString stringWithFormat:@"Error: %@", msg];
});
}
- (void)onStatusChanged:(RPCServerStatus)status {
dispatch_sync(dispatch_get_main_queue(), ^{
switch (status) {
case RPCServerStatus_Connected:
if (self.ModeSelector.selectedSegmentIndex == RPCServerMode_Standalone) {
self.infoText.text = [NSString
stringWithFormat:@"IP: %@\nPort: %d", server_.device_addr, server_.actual_port];
}
self.statusLabel.text = @"Connected";
break;
case RPCServerStatus_Disconnected:
self.statusLabel.text = @"Disconnected";
break;
default:
// Nothing
break;
}
});
}
@end
+33
View File
@@ -0,0 +1,33 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/*!
* \file main.m
*/
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "RPCArgs.h"
int main(int argc, char* argv[]) {
update_rpc_args(argc, argv);
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
+94
View File
@@ -0,0 +1,94 @@
<!--- Licensed to the Apache Software Foundation (ASF) under one -->
<!--- or more contributor license agreements. See the NOTICE file -->
<!--- distributed with this work for additional information -->
<!--- regarding copyright ownership. The ASF licenses this file -->
<!--- to you 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. -->
# Apache TVM Continuous Integration (CI)
## Overview
TVM's Continuous Integration is responsible for verifying the code in `apache/tvm` and testing PRs
before they merge to inform TVM contributors and committers. These jobs are essential to keeping the
TVM project in a healthy state and preventing breakages. CI in TVM is broken into these pieces:
- Lint scripts in [`tests/lint`](../tests/lint).
- The tests themselves, all of which live underneath [`tests`](../tests).
- Definitions of test suites, with each suite defined as a separate `task_` script in
[`tests/scripts`](../tests/scripts).
- Scripts and automation [`ci/scripts`](../ci/scripts).
- The linux test sequence (in [`Jenkinsfile`](../ci/jenkins/templates/)), which lints and builds TVM and runs test
suites using Docker on Linux.
- The Windows and Mac test sequences (in [`.github/actions`](../.github/actions)).
- GitHub Actions that support the code review process (in [`.github/actions`](../.github/actions)).
- Tools to reproduce the CI locally (in `tests/scripts`).
- Infrastructure-as-Code that configures the cloud services that provide Jenkins for the TVM CI (in the
[`tlc-pack/ci`](https://github.com/tlc-pack/ci) repo).
## CI Documentation Index
The CI documentation belongs with the implementation it describes. To make that concrete, the
documentation is split like so:
1. An overview of the CI is in this file.
1. User-facing documentation lives in `apache/tvm`'s `docs/contribute` sub-directory and is served on the
[TVM docs site](https://tvm.apache.org/docs/contribute/ci.html).
2. Documentation of the tools that run TVM's various regression tests locally and the test suites
are in this sub-directory.
3. Documentation of the cloud services and their configuration lives in the
[`tlc-pack/ci`](https://github.com/tlc-pack/ci) repo.
## Jenkins
Jenkins runs all of the Linux-based TVM CI-enabled regression tests. This includes tests against accelerated hardware such as GPUs. It excludes those regression tests that run against hardware not available in the cloud (those tests aren't currently exercised in TVM CI). The tests run by Jenkins represent most of the merge-blocking tests (and passing Jenkins should mostly correlate with passing the remaining Windows/Mac builds).
## GitHub Actions
GitHub Actions is used to run Windows jobs, MacOS jobs, and various on-GitHub automations. These are defined in [`.github/workflows`](../.github/workflows/). These automations include bots to:
* [allow non-committers to merge approved / CI passing PRs](https://discuss.tvm.apache.org/t/rfc-allow-merging-via-pr-comments/12220)
https://github.com/apache/tvm/actions has the logs for each of these workflows. Note that when debugging these workflows changes from PRs from forked repositories won't be reflected in the PR. These should be tested in the forked repository first and linked in the PR body.
## Docker Images
Each CI job runs most of its work inside a Docker container, built from files
in the [`docker/`](../docker) folder. These
files are built nightly in Jenkins via the [tvm-docker](https://ci.tlcpack.ai/job/tvm-docker/) job.
The images for these containers are hosted in the [tlcpack Docker Hub](https://hub.docker.com/u/tlcpack)
and referenced in the [`jenkins/templates`](/ci/jenkins/templates/). These can be inspected and run
locally via standard Docker commands.
### `ci-docker-staging`
The [ci-docker-staging](https://github.com/apache/tvm/tree/ci-docker-staging)
branch is used to test updates to Docker images and `Jenkinsfile` changes. When
running a build for a normal PR from a forked repository, Jenkins uses the code
from the PR except for the `Jenkinsfile` itself, which comes from the base branch.
When branches are built, the `Jenkinsfile` in the branch is used, so a committer
with write access must push PRs to a branch in apache/tvm to properly test
`Jenkinsfile` changes. If your PR makes changes to the `Jenkinsfile`, make sure
to @ a [committer](/CONTRIBUTORS.md)
and ask them to push your PR as a branch to test the changes.
# Jenkins CI
TVM uses Jenkins for running Linux continuous integration (CI) tests on
[branches](https://ci.tlcpack.ai/job/tvm/) and
[pull requests](https://ci.tlcpack.ai/job/tvm/view/change-requests/) through a
build configuration specified in a [`Jenkinsfile`](/ci/jenkins/templates/).
Other jobs run in GitHub Actions for Windows and MacOS jobs.
## `Jenkinsfile`
The template files in this directory are used to generate the [`Jenkinsfile`](/ci/jenkins/templates/) used by Jenkins to run CI jobs for each commit to PRs and branches.
To regenerate the `Jenkinsfile`, run `make` in the `ci/jenkins` dir.
+1
View File
@@ -0,0 +1 @@
/_venv
+55
View File
@@ -0,0 +1,55 @@
<!--- Licensed to the Apache Software Foundation (ASF) under one -->
<!--- or more contributor license agreements. See the NOTICE file -->
<!--- distributed with this work for additional information -->
<!--- regarding copyright ownership. The ASF licenses this file -->
<!--- to you 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. -->
# TVM CI
TVM runs CI jobs on every commit to an open pull request and to branches in the apache/tvm repo (such as `main`). These jobs are essential to keeping the TVM project in a healthy state and preventing breakages.
## Jenkins
Jenkins runs all of the linux-based TVM CI-enabled regression tests. This includes tests against accelerated hardware such as GPUs. It excludes those regression tests that run against hardware not available in the cloud (those tests aren't currently exercised in TVM CI). The tests run by Jenkins represent most of the merge-blocking tests (and passing Jenkins should mostly correlate with passing the remaining Windows/Mac builds).
## GitHub Actions
GitHub Actions is used to run Windows jobs, MacOS jobs, and various on-GitHub automations. These are defined in [`.github/workflows`](../../.github/workflows/). These automations include bots to:
* [allow non-committers to merge approved / CI passing PRs](https://discuss.tvm.apache.org/t/rfc-allow-merging-via-pr-comments/12220)
https://github.com/apache/tvm/actions has the logs for each of these workflows. Note that when debugging these workflows changes from PRs from forked repositories won't be reflected in the PR. These should be tested in the forked repository first and linked in the PR body.
# Jenkins CI
TVM uses Jenkins for running Linux continuous integration (CI) tests on
[branches](https://ci.tlcpack.ai/job/tvm/) and
[pull requests](https://ci.tlcpack.ai/job/tvm/view/change-requests/) through a
build configuration specified in a [`Jenkinsfile`](/ci/jenkins/templates/).
Other jobs run in GitHub Actions for Windows and MacOS jobs.
## `Jenkinsfile`
The template files in this directory are used to generate the [`Jenkinsfile`](/ci/jenkins/templates/) used by Jenkins to run CI jobs for each commit to PRs and branches.
To regenerate the `Jenkinsfile`, run
```bash
python3 -mvenv _venv
_venv/bin/pip3 install -r ci/jenkins/requirements.txt
_venv/bin/python3 ci/jenkins/generate.py
```
# Infrastructure
While all TVM tests are contained within the apache/tvm repository, the infrastructure used to run the tests is donated by the TVM Community. To encourage collaboration, the configuration for TVM's CI infrastructure is stored in a public GitHub repository. TVM community members are encouraged to contribute improvements. The configuration, along with documentation of TVM's CI infrastructure, is in the [tlc-pack/ci](https://github.com/tlc-pack/ci) repo.
+67
View File
@@ -0,0 +1,67 @@
#!/usr/bin/env python3
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
"""Bundle registry for CI artifact stashing.
Single source of truth for the file lists uploaded to / downloaded from S3 by
``ci/scripts/jenkins/s3.py``. This module deliberately carries nothing else —
docker image tags live in ``ci/jenkins/docker-images.ini`` and Jinja-template
metadata (image platforms, AWS endpoints) lives in ``ci/jenkins/generate.py``.
CLI: ``python3 ci/jenkins/data.py <bundle> [<bundle> ...]`` resolves bundle
names to their file paths (one per line; exit 1 on unknown name). Used by
``s3.py`` at Jenkins runtime and by any external caller that needs
data-driven artifact lists.
"""
import sys
files_to_stash = {
# Executables and build files needed to run c++ tests
"cpptest": ["build/cpptest", "build/build.ninja", "build/CMakeFiles/rules.ninja"],
# runtime files
"tvm_runtime": ["build/lib/libtvm_runtime.so", "build/config.cmake"],
# compiler files
"tvm_lib": [
"build/lib/libtvm_compiler.so",
"build/lib/libtvm_runtime.so",
"build/lib/libtvm_ffi.so",
"build/lib/libtvm_runtime_cuda.so",
"build/lib/libtvm_runtime_vulkan.so",
"build/lib/libtvm_runtime_opencl.so",
"build/lib/libtvm_runtime_rocm.so",
"build/lib/libtvm_runtime_extra.so",
"build/libtvm_allvisible.so",
"build/config.cmake",
],
# gpu related compiler files
"tvm_lib_gpu_extra": [
"build/3rdparty/libflash_attn/src/libflash_attn.so",
"build/3rdparty/cutlass_fpA_intB_gemm/cutlass_kernels/libfpA_intB_gemm.so",
],
}
if __name__ == "__main__":
paths = []
for name in sys.argv[1:]:
if name not in files_to_stash:
print(f"unknown bundle: {name}", file=sys.stderr)
sys.exit(1)
paths.extend(files_to_stash[name])
for p in paths:
print(p)

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