commit 26446540fadaf1a5bdd5dfc29e594b312da09221 Author: wehub-resource-sync Date: Mon Jul 13 13:36:25 2026 +0800 chore: import upstream snapshot with attribution diff --git a/.agents/scripts/monitor_gpu.sh b/.agents/scripts/monitor_gpu.sh new file mode 100755 index 0000000..85963da --- /dev/null +++ b/.agents/scripts/monitor_gpu.sh @@ -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 " >&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 diff --git a/.agents/skills/tir-build/SKILL.md b/.agents/skills/tir-build/SKILL.md new file mode 100644 index 0000000..ee43dfa --- /dev/null +++ b/.agents/skills/tir-build/SKILL.md @@ -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. diff --git a/.agents/skills/tir-test/SKILL.md b/.agents/skills/tir-test/SKILL.md new file mode 100644 index 0000000..e4a5fe6 --- /dev/null +++ b/.agents/skills/tir-test/SKILL.md @@ -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 3–4. 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: ` 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. diff --git a/.asf.yaml b/.asf.yaml new file mode 100644 index 0000000..c629d77 --- /dev/null +++ b/.asf.yaml @@ -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 diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..b61ed53 --- /dev/null +++ b/.clang-format @@ -0,0 +1,8 @@ +# Run the following command to reformat a file: +# clang-format -i -style=Google +# 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 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..d82bd54 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +Jenkinsfile linguist-generated=true +ci/jenkins/generated/* linguist-generated=true diff --git a/.github/CODEOWNERSHIP b/.github/CODEOWNERSHIP new file mode 100644 index 0000000..9e4c6f4 --- /dev/null +++ b/.github/CODEOWNERSHIP @@ -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 diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 0000000..e373b6d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -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 diff --git a/.github/ISSUE_TEMPLATE/ci-problem.md b/.github/ISSUE_TEMPLATE/ci-problem.md new file mode 100644 index 0000000..62d07b9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/ci-problem.md @@ -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 diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..2832233 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -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 😺 diff --git a/.github/ISSUE_TEMPLATE/documentation.md b/.github/ISSUE_TEMPLATE/documentation.md new file mode 100644 index 0000000..0f18d72 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation.md @@ -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 diff --git a/.github/ISSUE_TEMPLATE/feature-tracking.md b/.github/ISSUE_TEMPLATE/feature-tracking.md new file mode 100644 index 0000000..3a7c6e3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-tracking.md @@ -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 diff --git a/.github/ISSUE_TEMPLATE/flaky-test.md b/.github/ISSUE_TEMPLATE/flaky-test.md new file mode 100644 index 0000000..7f905ac --- /dev/null +++ b/.github/ISSUE_TEMPLATE/flaky-test.md @@ -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 diff --git a/.github/actions/build-wheel-for-publish/action.yml b/.github/actions/build-wheel-for-publish/action.yml new file mode 100644 index 0000000..d4a3ca1 --- /dev/null +++ b/.github/actions/build-wheel-for-publish/action.yml @@ -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 }}" diff --git a/.github/actions/detect-env-vars/action.yml b/.github/actions/detect-env-vars/action.yml new file mode 100644 index 0000000..afd9efe --- /dev/null +++ b/.github/actions/detect-env-vars/action.yml @@ -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 }} diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml new file mode 100644 index 0000000..980e5d0 --- /dev/null +++ b/.github/actions/setup/action.yml @@ -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 diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..946ba53 --- /dev/null +++ b/.github/dependabot.yml @@ -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 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..a9bff51 --- /dev/null +++ b/.github/workflows/lint.yml @@ -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 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..4a7ef63 --- /dev/null +++ b/.github/workflows/main.yml @@ -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 diff --git a/.github/workflows/nightly_docker_update.yml b/.github/workflows/nightly_docker_update.yml new file mode 100644 index 0000000..7b05634 --- /dev/null +++ b/.github/workflows/nightly_docker_update.yml @@ -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 diff --git a/.github/workflows/publish_wheel.yml b/.github/workflows/publish_wheel.yml new file mode 100644 index 0000000..a2fedda --- /dev/null +++ b/.github/workflows/publish_wheel.yml @@ -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 diff --git a/.github/workflows/tvmbot.yml b/.github/workflows/tvmbot.yml new file mode 100644 index 0000000..aa6bbca --- /dev/null +++ b/.github/workflows/tvmbot.yml @@ -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" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2a59857 --- /dev/null +++ b/.gitignore @@ -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/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..a7cbc9d --- /dev/null +++ b/.gitmodules @@ -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 diff --git a/.markdownlint-cli2.yaml b/.markdownlint-cli2.yaml new file mode 100644 index 0000000..8d56f07 --- /dev/null +++ b/.markdownlint-cli2.yaml @@ -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 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..2569d61 --- /dev/null +++ b/.pre-commit-config.yaml @@ -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 diff --git a/.yamllint.yaml b/.yamllint.yaml new file mode 100644 index 0000000..417d517 --- /dev/null +++ b/.yamllint.yaml @@ -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" diff --git a/3rdparty/compiler-rt/builtin_fp16.h b/3rdparty/compiler-rt/builtin_fp16.h new file mode 100644 index 0000000..51465b7 --- /dev/null +++ b/3rdparty/compiler-rt/builtin_fp16.h @@ -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 + +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 +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 +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_ diff --git a/3rdparty/tensorrt_llm/custom_allreduce_kernels.cu b/3rdparty/tensorrt_llm/custom_allreduce_kernels.cu new file mode 100644 index 0000000..dc5ff6d --- /dev/null +++ b/3rdparty/tensorrt_llm/custom_allreduce_kernels.cu @@ -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 +#include +#include +#include +#include + +#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 +struct PackedOn16Bytes {}; + +template <> +struct PackedOn16Bytes { + using Type = PackedFloat; +}; + +template <> +struct PackedOn16Bytes { + 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 +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 +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::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(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(&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(&reinterpret_cast(params.local_output_buffer_ptr)[iter_offset]) = + sums.packed; + } +} + +template +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::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(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(&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(&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(&reinterpret_cast(params.local_output_buffer_ptr)[offset_rank]) = + *reinterpret_cast(&src_d[ii][offset_rank]); + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +inline int divUp(int a, int b) { return (a + b - 1) / b; } + +std::tuple 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(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(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 +void dispatchARKernels(AllReduceStrategyType algo, AllReduceParams& param, int blocks_per_grid, + int threads_per_block, cudaStream_t stream) { + if (algo == AllReduceStrategyType::ONESHOT) { + oneShotAllReduceKernel + <<>>(param); + } else { + twoShotAllReduceKernel + <<>>(param); + } +} + +template +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(strat, param, blocks_per_grid, threads_per_block, stream); + break; + case 4: + dispatchARKernels(strat, param, blocks_per_grid, threads_per_block, stream); + break; + case 6: + dispatchARKernels(strat, param, blocks_per_grid, threads_per_block, stream); + break; + case 8: + dispatchARKernels(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(params, strat, stream); + } else if (dataType.code == kDLFloat && dataType.bits == 16) { + invokeOneOrTwoShotAllReduceKernel(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 diff --git a/3rdparty/tensorrt_llm/custom_allreduce_kernels.h b/3rdparty/tensorrt_llm/custom_allreduce_kernels.h new file mode 100644 index 0000000..7c515a0 --- /dev/null +++ b/3rdparty/tensorrt_llm/custom_allreduce_kernels.h @@ -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 +#include + +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 diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..cd38b2a --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,99 @@ + + +# 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 ... +``` + +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. diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..3d18325 --- /dev/null +++ b/CMakeLists.txt @@ -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 ) 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//script/. The list below is intentionally explicit + # (not src/script/*.cc) so that any new file accidentally added under + # src/script/{printer,ir_builder}// 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 + $ + ${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 + $ + ${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 "$") +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_link_libraries(tvm_compiler PUBLIC tvm_runtime tvm_ffi_shared) +target_include_directories(tvm_compiler PUBLIC "$") +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 $) + 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 $<$:${FILE_PREFIX_MAP_FLAG}>) + target_compile_options(tvm_objs PRIVATE $<$:${FILE_PREFIX_MAP_FLAG}>) + target_compile_options(tvm_runtime PRIVATE $<$:${FILE_PREFIX_MAP_FLAG}>) + target_compile_options(tvm_runtime_objs PRIVATE $<$:${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 /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() diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md new file mode 100644 index 0000000..d9a0082 --- /dev/null +++ b/CONTRIBUTORS.md @@ -0,0 +1,243 @@ + + + + + + + + + + + + + + + + + +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 diff --git a/KEYS b/KEYS new file mode 100644 index 0000000..c5297eb --- /dev/null +++ b/KEYS @@ -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 and append it to this file. + (pgpk -ll && pgpk -xa ) >> this file. + (gpg --list-sigs + && gpg --armor --export ) >> this file. + +----------------------------------------------------------------------------------- +pub rsa4096 2019-11-15 [SC] + EF52D68AD5276994249816836754EA97C55E3DEB +uid [ultimate] Tianqi Chen (CODE SIGNING KEY) +sig 3 6754EA97C55E3DEB 2019-11-15 Tianqi Chen (CODE SIGNING KEY) +sub rsa4096 2019-11-15 [E] +sig 6754EA97C55E3DEB 2019-11-15 Tianqi Chen (CODE SIGNING KEY) + +-----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 +sig 3 CA751254E97B9FE4 2018-02-07 Yizhi Liu +sub rsa4096 2018-02-07 [E] +sig CA751254E97B9FE4 2018-02-07 Yizhi Liu + +-----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 +sig 3 ED03B26E4FC3509F 2020-09-24 Ziheng Jiang +sub rsa4096 2020-09-24 [E] +sig ED03B26E4FC3509F 2020-09-24 Ziheng Jiang + +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=nhJ/ +-----END PGP PUBLIC KEY BLOCK----- +pub 4096R/D75EFD4B 2020-09-24 +uid Zhi Chen +sig 3 D75EFD4B 2020-09-24 Zhi Chen +sub 4096R/285DD7CB 2020-09-24 +sig D75EFD4B 2020-09-24 Zhi Chen + +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBF9tJNABEADpv6LjNjEkb62cIXKHLcg0vugPgGzui+cR/YyJc0zNMPnQNnvD +D0VisV+vdorYDmXMLRhGC4KbD6vmoFnriBJrdKYpl0geV8Uzw3S3Ecyh4ELK1ayD +aVpyE5A+73LbLTSnnmfonXcWdW03hZe8ilKc8vapvibCMQ7SVvEJ8nHHd/YmAteL +6HNLfTkm/pBZ2MxX0k2Cm0qX4UnERKEWFPGjXWViRaMLWl2ufIocA0KXWRRgiprb +FwMUTUCjWiPE5L6/8OKjKHdIv0gKi3WeMPYpN0PNTclqlZ5YAbxuSOnuRXmw4D8d +bnM/V+Hq4s0VQLMItbdGX1pmiJmHsXRygjgvZV6giusNge0KWcKEUvJaU0wOOyTC +luAgqWo54pMDHEd8561uvSishgG5/ePDNTdouR05J4MOl8cVBFrPwqPvymk/Wztt +qsYXiZhJ1vdMD1X4UUNYg3peDHsIZWdNIQTcEgRSArSahOlM5dyjSLFbHj2gjWES +pP8EqmkTn1/nRUaFvhZCX1r37GYYbHH8iD58omG38eVW4uOIyz3nwl5DxUO2+4it +NGigNoDQ7c2OMn/F7P9m3juxhx2hyaR5nTDm48ZaUbMUFbfWqTIhPBSqP9qZr/8g +72zY1AkvPPOfgGChJ7/XGnyzf94C6DtDJQgubjnNsZCHGQyNdPAz3F7U3QARAQAB +tBpaaGkgQ2hlbiA8emhpY0BhcGFjaGUub3JnPokCOAQTAQIAIgUCX20k0AIbAwYL +CQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQn77L09de/Uu8Rg//aoDx5Sus1FQ+ +BIctSl7gc/grs6rHYFPi6d/ZH0vXbsa1c5oARVAJd+gnhrG0Lylmf0kbcixriNwT +iVoo32CyA3VnT7vT46moRCe9UQBuEjIYWo1YiAXq5we7stsWqwpCDhR/h7weuhXR +Rr4nIzLnFEBzvLcuqorVL3xTkisnu3u3i8XQsPbLkQQXohP0BWJoBZKN+VEGciWD +26rriDeGw7ew5L4qJe7AKwS9Zt0jQoiEINp/CtINOrNNIhDxaKy6AwW/1wohVOtq +TG7kyU+zgtYV7+nZpXT62lKn3rOsX6OxsEU9IPqrCRMW2WExxUJ1w6iUBUcxwlUz +QbVdCRLMfR8HE/BgFVQLP8ESka2gH/tcFEp+tsv7p47wsrs9NbIaL+yYWQFat5r0 +h5ynjFAFmc89weC5h/bVKv/W0Hrt5YZmWxVjiSPZW13eDQ00PliJTyMlIGxsboQG +3k0+MxH+bpj8VK5cfnbTDg77eiX2XYyGuV4Vs5Yv+y3qiShD/nGJKNrKk6LaF75f +hwzxEv04tBdNVKjYm9IY64pg+3e916vTpVrhkU3+frzQPr5SYKr/v1vYnknADgn5 +TYdtSKoWx6D98JWllNcOhfYw3auiF4f1E47yJ00JFVvNQzlVDaqeGWbRj9DMt590 +BDkAVsr+//2SrmjYvr/Q/b1EVC3nwxC5Ag0EX20k0AEQALaqrORp/Y/GHDgGxYfF +Wu4YNwN4W3JCFKVu7s7yC4T7ndcGrn4MEq0b3kuJeucl8UO9IGEILokQzOHadPDm +2mvUYzzZULgUCRQZTUpU4AhKgbh4eB6LMcxiIRVDaCO/xg2+dFyry7+kk/gpZSy2 +HCot0jvFCDFOxXk6WRCycy/EXuWvZyKMm7Bp+CViX9UoV/S9VkdaxFbSCBWdfmTn +d1QT+GivOLxY8xy21VO5qugnH7nEvcnbniF8zqprpJlgvM8MRW9voFCZg3UOjpDI +cT4foqX+h8FL6BxLKMrYEAx0rdYdn0weaptqkdDc0BAz561q9bcprOmIIEapQ7yE +N9vcUt2i2/Op/CQsKs2dZx13RtHhDCJ2awZ/eiKdMowJKx0dNOdsBVK49TKYSt6G +jQiIKaFB+x4ta6IWDVq8v8zctaDI2ud4OR0kvwP9A4rbuC3k3P+4ojo0OoUO1Ais +g3wSX+bHtOrXQVPuJiSaKp/zJShiSH1k7DCgqPav5SuF+fnuIJ95yLPdDlmxKqDS +JxlCaWgWX6j622zTxJLlm5YgedBi1wKx6BqLdKE1Lvv8As/VwFMxDv9smDwVaMlD +rrwPc3/BT2ES1Pqp1CmgZKVmQ0/4UvvIOe5MeNI5pQ7NBO+0rTN+JkANEVSxTi/u +Ne4tPZk7C/Y5iQvAIYq5BZUJABEBAAGJAh8EGAECAAkFAl9tJNACGwwACgkQn77L +09de/UuLYxAA1T1YT5e5BoxjVm1WvwDmg8Vagwj1E1ZVBWG2H0/uL5ew+7/4Jy4C +iy5DZOdGUbF7hIH0J6Kn/UJGXyRPQh3rMk/hwaColpA6AjrV8LxMjPwgjgUWzBMK +NlhSsGj4UE84tblGL65boDaGeJKYbRU4b9Rw8nx3jQOrSk7xwmLW8qJYyQIgUKPv +hLhq4Ni4pfxDkKOa2GT58Yg8wadN/ZB38q9r2cU33XjyJo9AeWeeqYS4LZvc1mDu +DUV3C+RJpNh8+njYSYeO5G8CrQljvUYVWaylL51HPVcRfc+9u+uPfdf4pdcng4Rj +U1LyQQX+JaiukZsX0xXk2CiWSAhWfP3i92baX00SeFkNXxVmUVFjr+WkEPun+EVs +4cnJUHqgH2dkAU3WAChGnI9gJEa63gyN8N4IbTmQnLOnyFt57jmtfMrRGpU/BqFN +b3FNtmtnms9QjqrOr8QxFG78jfNluuEIslP0ul0fbwovItpZxLF3cIPr9M+irKEv +9w0IxAzfZ1IPpl7EltKVK+gN9Wt99Lqkx/kJHw5R/HTUuM6FxjL+1PGWYybhuU9n +Q2YsCQ/Br0XhJvC+i6OYgCI1iGLINTe9wjsi2ei8ZI+2G9XY62sN0orIIjIadns+ +8WGuWI9h3RBLY7aFMLpl02cXrsOiMcXC1Uk/e6e14Xpu+Y6IG4KKkUM= +=GEwA +-----END PGP PUBLIC KEY BLOCK----- +pub rsa4096 2021-11-10 [SC] + C5E5C09030E7BD32DF9A67CE35ABC9676004ADAE +uid [ultimate] Junru Shao +sig 3 35ABC9676004ADAE 2021-11-10 Junru Shao +sub rsa4096 2021-11-10 [E] +sig 35ABC9676004ADAE 2021-11-10 Junru Shao + +-----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 +sig 3 2C75E5A496C80880 2021-11-10 Wuwei Lin +sub rsa4096 2021-11-10 [E] +sig 2C75E5A496C80880 2021-11-10 Wuwei Lin + +-----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 +sig 3 07FA463F1C926F48 2022-07-12 David Riazati +sub rsa3072 2022-07-12 [E] [expires: 2024-07-11] +sig 07FA463F1C926F48 2022-07-12 David Riazati + +-----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 +sig 3 C9A56ABD5CCA3EB8 2022-10-10 Andrew Zhao Luo +sub rsa4096 2022-10-10 [E] +sig C9A56ABD5CCA3EB8 2022-10-10 Andrew Zhao Luo + +-----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 +sig 3 28D4862222B8EC31 2023-02-02 Leandro Nunes +sub rsa4096 2023-02-02 [E] +sig 28D4862222B8EC31 2023-02-02 Leandro Nunes + +-----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) +sig 3 06D051CA84EF3749 2023-05-05 Siyuan Feng (CODE SIGNING KEY) +sub rsa4096 2023-05-05 [E] +sig 06D051CA84EF3749 2023-05-05 Siyuan Feng (CODE SIGNING KEY) + +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +wS/UuN04NxDy7V6mPXE= +=MTba +-----END PGP PUBLIC KEY BLOCK----- +pub rsa4096 2024-01-15 [SC] + A4D9228E55761E665BF01CBB5CE869CB7DEC048C +uid [ultimate] Star Yuan (CODE SIGNING KEY) +sig 3 5CE869CB7DEC048C 2024-01-15 Star Yuan (CODE SIGNING KEY) +sub rsa4096 2024-01-15 [E] +sig 5CE869CB7DEC048C 2024-01-15 Star Yuan (CODE SIGNING KEY) + +-----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) +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----- diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..94abb35 --- /dev/null +++ b/LICENSE @@ -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 diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..35d3709 --- /dev/null +++ b/NOTICE @@ -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/). diff --git a/README.md b/README.md new file mode 100644 index 0000000..fb9e9bc --- /dev/null +++ b/README.md @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + 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. diff --git a/README.wehub.md b/README.wehub.md new file mode 100644 index 0000000..185c11b --- /dev/null +++ b/README.wehub.md @@ -0,0 +1,7 @@ +# WeHub 来源说明 + +- 原始项目:`apache/tvm` +- 原始仓库:https://github.com/apache/tvm +- 导入方式:上游默认分支的最新快照 +- 原作者、版权和许可证信息以原始仓库及本仓库 LICENSE 为准 +- 本文件仅用于记录来源,不代表 WeHub 是原项目作者 diff --git a/apps/android_rpc/.gitignore b/apps/android_rpc/.gitignore new file mode 100644 index 0000000..1f8bc3b --- /dev/null +++ b/apps/android_rpc/.gitignore @@ -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 diff --git a/apps/android_rpc/README.md b/apps/android_rpc/README.md new file mode 100644 index 0000000..f74942e --- /dev/null +++ b/apps/android_rpc/README.md @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + +# 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 + +### Build APK + +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. diff --git a/apps/android_rpc/app/.gitignore b/apps/android_rpc/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/apps/android_rpc/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/apps/android_rpc/app/build.gradle b/apps/android_rpc/app/build.gradle new file mode 100644 index 0000000..4d4c835 --- /dev/null +++ b/apps/android_rpc/app/build.gradle @@ -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' +} diff --git a/apps/android_rpc/app/src/main/AndroidManifest.xml b/apps/android_rpc/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a895f32 --- /dev/null +++ b/apps/android_rpc/app/src/main/AndroidManifest.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/android_rpc/app/src/main/java/org/apache/tvm/tvmrpc/MainActivity.java b/apps/android_rpc/app/src/main/java/org/apache/tvm/tvmrpc/MainActivity.java new file mode 100644 index 0000000..2e36768 --- /dev/null +++ b/apps/android_rpc/app/src/main/java/org/apache/tvm/tvmrpc/MainActivity.java @@ -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); + } + } +} diff --git a/apps/android_rpc/app/src/main/java/org/apache/tvm/tvmrpc/RPCActivity.java b/apps/android_rpc/app/src/main/java/org/apache/tvm/tvmrpc/RPCActivity.java new file mode 100644 index 0000000..5b4600e --- /dev/null +++ b/apps/android_rpc/app/src/main/java/org/apache/tvm/tvmrpc/RPCActivity.java @@ -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()); + } +} diff --git a/apps/android_rpc/app/src/main/java/org/apache/tvm/tvmrpc/RPCAndroidWatchdog.java b/apps/android_rpc/app/src/main/java/org/apache/tvm/tvmrpc/RPCAndroidWatchdog.java new file mode 100644 index 0000000..3b253ee --- /dev/null +++ b/apps/android_rpc/app/src/main/java/org/apache/tvm/tvmrpc/RPCAndroidWatchdog.java @@ -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(); + } +} diff --git a/apps/android_rpc/app/src/main/java/org/apache/tvm/tvmrpc/RPCProcessor.java b/apps/android_rpc/app/src/main/java/org/apache/tvm/tvmrpc/RPCProcessor.java new file mode 100644 index 0000000..14b7294 --- /dev/null +++ b/apps/android_rpc/app/src/main/java/org/apache/tvm/tvmrpc/RPCProcessor.java @@ -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(); + } +} diff --git a/apps/android_rpc/app/src/main/jni/Android.mk b/apps/android_rpc/app/src/main/jni/Android.mk new file mode 100644 index 0000000..b754052 --- /dev/null +++ b/apps/android_rpc/app/src/main/jni/Android.mk @@ -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) diff --git a/apps/android_rpc/app/src/main/jni/Application.mk b/apps/android_rpc/app/src/main/jni/Application.mk new file mode 100644 index 0000000..a799654 --- /dev/null +++ b/apps/android_rpc/app/src/main/jni/Application.mk @@ -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 diff --git a/apps/android_rpc/app/src/main/jni/tvm_runtime.h b/apps/android_rpc/app/src/main/jni/tvm_runtime.h new file mode 100644 index 0000000..ea9c7eb --- /dev/null +++ b/apps/android_rpc/app/src/main/jni/tvm_runtime.h @@ -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 + +#include + +/* 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 + +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 diff --git a/apps/android_rpc/app/src/main/res/layout/activity_main.xml b/apps/android_rpc/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..f317a1c --- /dev/null +++ b/apps/android_rpc/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + diff --git a/apps/android_rpc/app/src/main/res/layout/activity_rpc.xml b/apps/android_rpc/app/src/main/res/layout/activity_rpc.xml new file mode 100644 index 0000000..9c586e0 --- /dev/null +++ b/apps/android_rpc/app/src/main/res/layout/activity_rpc.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + diff --git a/apps/android_rpc/app/src/main/res/layout/content_main.xml b/apps/android_rpc/app/src/main/res/layout/content_main.xml new file mode 100644 index 0000000..483f60a --- /dev/null +++ b/apps/android_rpc/app/src/main/res/layout/content_main.xml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/android_rpc/app/src/main/res/layout/content_rpc.xml b/apps/android_rpc/app/src/main/res/layout/content_rpc.xml new file mode 100644 index 0000000..5611083 --- /dev/null +++ b/apps/android_rpc/app/src/main/res/layout/content_rpc.xml @@ -0,0 +1,34 @@ + + + + +