Compare commits

..
Author SHA1 Message Date
nimlgenandGitHub 76dce1eb8d tiny hcq2 changes (#17797)
* tiny hcq2 changes

* x

* x
2026-08-28 11:29:00 +03:00
qazalandGitHub 6242b09066 cleaner mxfp4 gemm prelude (#17796)
* cleaner mxfp4 prelude

* rename sgprs

* min diff
2026-08-28 15:59:35 +09:00
wozeparrotandGitHub 92d92c70a9 gptoss: faster grad handling (#17795) 2026-08-27 22:06:33 -07:00
Teddy TennantandGitHub 26c4b6319e fix interpolate with align_corners and an output size of 1 (#17790) 2026-08-27 19:17:05 -07:00
qazalandGitHub 53cad325c7 copy movement op tests construct copies (#17792)
Autogen / In-tree Autogen (macos) (push) Waiting to run
Benchmarks / Mac pytest (push) Waiting to run
Benchmarks / LLM (DEV=AMD) (push) Waiting to run
Benchmarks / LLM (DEV=METAL) (push) Waiting to run
Benchmarks / LLM (DEV=NV) (push) Waiting to run
Benchmarks / HLB-CIFAR10 (DEV=AMD) (push) Waiting to run
Benchmarks / HLB-CIFAR10 (DEV=METAL) (push) Waiting to run
Benchmarks / HLB-CIFAR10 (DEV=NV) (push) Waiting to run
Benchmarks / MLPerf (AMD) (push) Waiting to run
Benchmarks / MLPerf (NV) (push) Waiting to run
Benchmarks / Stable Diffusion (DEV=AMD) (push) Waiting to run
Benchmarks / Stable Diffusion (DEV=METAL) (push) Waiting to run
Benchmarks / Stable Diffusion (DEV=NV) (push) Waiting to run
Benchmarks / Multi-GPU Benchmarks (DEV=AMD) (push) Waiting to run
Benchmarks / Multi-GPU Benchmarks (DEV=NV) (push) Waiting to run
Benchmarks / Tests (DEV=AMD) (push) Waiting to run
Benchmarks / Tests (DEV=METAL) (push) Waiting to run
Benchmarks / Tests (DEV=NV) (push) Waiting to run
Benchmarks / UsbGPU Benchmark (push) Waiting to run
Benchmarks / openpilot 0.11.0 compile3 dmonitoring (DEV=QCOM) (push) Waiting to run
Benchmarks / openpilot 0.11.2 compile3 dmonitoring (DEV=QCOM) (push) Waiting to run
Benchmarks / openpilot 0.11.0 compile3 policy (DEV=QCOM) (push) Waiting to run
Benchmarks / openpilot 0.11.2 compile3 supercombo (DEV=QCOM) (push) Waiting to run
Benchmarks / openpilot 0.11.0 compile3 vision (DEV=QCOM) (push) Waiting to run
Benchmarks / openpilot 0.11.0 compile3 dmonitoring (DEV=QCOM:IR3) (push) Waiting to run
Benchmarks / openpilot 0.11.2 compile3 dmonitoring (DEV=QCOM:IR3) (push) Waiting to run
Benchmarks / openpilot 0.11.0 compile3 policy (DEV=QCOM:IR3) (push) Waiting to run
Benchmarks / openpilot 0.11.2 compile3 supercombo (DEV=QCOM:IR3) (push) Waiting to run
Benchmarks / openpilot 0.11.0 compile3 vision (DEV=QCOM:IR3) (push) Waiting to run
Benchmarks / DSP Benchmark (push) Waiting to run
Benchmarks / UsbGPU Benchmark (comma) (push) Waiting to run
Benchmarks / PCI Driver Benchmark (DEV=AMD) (push) Waiting to run
Benchmarks / PCI Driver Benchmark (DEV=NV) (push) Waiting to run
Benchmarks / LLVM Speed (push) Waiting to run
Platform Tests / MacOS (unit) (push) Waiting to run
Platform Tests / MacOS (unit, mock) (push) Waiting to run
Platform Tests / MacOS (DEV=METAL) (1) (push) Waiting to run
Platform Tests / MacOS (DEV=METAL) (2) (push) Waiting to run
Platform Tests / MacOS (DEV=CPU:CLANG) (push) Waiting to run
Platform Tests / MacOS (DEV=CPU:LLVM) (push) Waiting to run
Platform Tests / MacOS (DEV=CPU:LVP) (push) Waiting to run
Platform Tests / MacOS (DEV=WEBGPU) (push) Waiting to run
Platform Tests / Windows (DEV=CPU:CLANG) (push) Waiting to run
Platform Tests / Windows (DEV=CPU:LLVM) (push) Waiting to run
Platform Tests / Windows (DEV=CPU:X86) (push) Waiting to run
Platform Tests / Windows (DEV=WEBGPU) (push) Waiting to run
Unit Tests / Models (push) Successful in 1m22s
Unit Tests / Linux (DSP) (push) Successful in 1m51s
Unit Tests / ONNX (CPU) Tests (push) Successful in 1m55s
Unit Tests / Linters (push) Successful in 1m59s
Unit Tests / Test LLM (push) Successful in 2m6s
Unit Tests / Fuzzing (push) Successful in 2m8s
Unit Tests / hcq2 (push) Successful in 2m43s
Unit Tests / Docs (push) Successful in 2m50s
Unit Tests / AMD ASM IDE (push) Successful in 2m47s
Unit Tests / Null Tests (push) Successful in 2m59s
Unit Tests / openpilot Compile Tests (push) Successful in 2m59s
Unit Tests / Torch Backend Training (push) Successful in 3m3s
Unit Tests / Unit Tests (push) Successful in 3m9s
Unit Tests / Python Backend (push) Successful in 3m16s
Unit Tests / Linux (amdllvm gfx1100) (push) Successful in 3m31s
Unit Tests / Linux (amdllvm gfx1201) (push) Successful in 3m31s
Unit Tests / SPEC=2 (2) (push) Successful in 3m39s
Unit Tests / Linux (DEV=CPU:LVP) (push) Successful in 3m36s
Unit Tests / Linux (DEV=CPU:LLVM) (push) Successful in 3m41s
Unit Tests / SPEC=2 (1) (push) Failing after 3m48s
Unit Tests / Compile-only (DEV=NULL:NAK:sm_120) (push) Successful in 1m43s
Unit Tests / Linux (DEV=CL) (push) Successful in 3m51s
Unit Tests / CL IMAGE Tests (push) Successful in 3m53s
Unit Tests / Optimization Tests (push) Successful in 3m53s
Unit Tests / Linux (DEV=WEBGPU) (push) Successful in 3m49s
Unit Tests / Linux (DEV=CPU:X86) (push) Successful in 3m58s
Unit Tests / Linux (am) (push) Successful in 3m59s
Unit Tests / Linux (amd gfx1100) (push) Successful in 4m0s
Unit Tests / Linux (amd gfx1201) (push) Successful in 4m0s
Unit Tests / Linux (DEV=CPU:CLANG) (push) Successful in 4m17s
Unit Tests / Torch Backend Tests (push) Successful in 4m29s
Deploy Docs / deploy (push) Successful in 4m34s
Unit Tests / Compile-only (DEV=NULL:IR3:a630) (push) Successful in 2m35s
Unit Tests / Linux (amdllvm gfx950) (push) Successful in 3m2s
Unit Tests / Linux (ptx) (push) Successful in 3m0s
Unit Tests / Linux (amd gfx950) (push) Successful in 4m12s
Unit Tests / Linux (nv) (push) Successful in 3m47s
Unit Tests / Compile-only (DEV=NULL:QCOMCL:a630) (push) Successful in 3m36s
Autogen / In-tree Autogen (push) Successful in 11m1s
* copy movement op tests construct copies

* hop 3 gpus

* assert the amount of bytes moved

* work

* arange 4, everyone copies 16B first
2026-08-28 11:09:11 +09:00
sirhcmandGitHub 4456e62343 ci: skip apt steps if packages are already installed (#17794) 2026-08-27 22:06:08 -04:00
George HotzandGitHub 39d7fd0981 axis arg is dead (#17793) 2026-08-27 18:41:09 -07:00
geohot 4e6bdac412 hotfix: disable HCQ2 2026-08-27 18:23:52 -07:00
chenyuandGitHub bc485d271f fix CMPLT between float16 and const 0 (#17791)
* fix CMPLT between float16 and const 0

* fix
2026-08-27 21:14:34 -04:00
George HotzandGitHub 6b361e4de7 move shape into arg for param/buffer (#17783)
* move shape into arg for param/buffer

* no param_from_shape

* drop gratuitous syntax changes

* image is a in-graph view, folded into the param arg at render; drop dead multi param sharding

* view_as helper, simpler resolve_function, spec update

* spec: param/buffer are flat storage, no shape input

* image dims live in the param arg from transform_to_image; tighten kernel graph spec

* kernel graph spec: only RESHAPE/SHRINK over storage values, not all movement

* kernel graph: call args are storage, not views (pm_no_view_args); assert in spec

* strip views at the kernel graph level (pm_no_views), move into rangeify

* touchups
2026-08-27 18:13:24 -07:00
chenyuandGitHub 38e0fe103b fix where with float64 branches (#17789)
* fix where with float64 branches

* fix
2026-08-27 21:03:43 -04:00
sirhcmandGitHub e6ad2907a8 onnxruntime respects NUM_CPU_THREADS (#17788)
Autogen / In-tree Autogen (macos) (push) Waiting to run
Benchmarks / Mac pytest (push) Waiting to run
Benchmarks / LLM (DEV=AMD) (push) Waiting to run
Benchmarks / LLM (DEV=METAL) (push) Waiting to run
Benchmarks / LLM (DEV=NV) (push) Waiting to run
Benchmarks / HLB-CIFAR10 (DEV=AMD) (push) Waiting to run
Benchmarks / HLB-CIFAR10 (DEV=METAL) (push) Waiting to run
Benchmarks / HLB-CIFAR10 (DEV=NV) (push) Waiting to run
Benchmarks / MLPerf (AMD) (push) Waiting to run
Benchmarks / MLPerf (NV) (push) Waiting to run
Benchmarks / Stable Diffusion (DEV=AMD) (push) Waiting to run
Benchmarks / Stable Diffusion (DEV=METAL) (push) Waiting to run
Benchmarks / Stable Diffusion (DEV=NV) (push) Waiting to run
Benchmarks / Multi-GPU Benchmarks (DEV=AMD) (push) Waiting to run
Benchmarks / Multi-GPU Benchmarks (DEV=NV) (push) Waiting to run
Benchmarks / Tests (DEV=AMD) (push) Waiting to run
Benchmarks / Tests (DEV=METAL) (push) Waiting to run
Benchmarks / Tests (DEV=NV) (push) Waiting to run
Benchmarks / UsbGPU Benchmark (push) Waiting to run
Benchmarks / openpilot 0.11.0 compile3 dmonitoring (DEV=QCOM) (push) Waiting to run
Benchmarks / openpilot 0.11.2 compile3 dmonitoring (DEV=QCOM) (push) Waiting to run
Benchmarks / openpilot 0.11.0 compile3 policy (DEV=QCOM) (push) Waiting to run
Benchmarks / openpilot 0.11.2 compile3 supercombo (DEV=QCOM) (push) Waiting to run
Benchmarks / openpilot 0.11.0 compile3 vision (DEV=QCOM) (push) Waiting to run
Benchmarks / openpilot 0.11.0 compile3 dmonitoring (DEV=QCOM:IR3) (push) Waiting to run
Benchmarks / openpilot 0.11.2 compile3 dmonitoring (DEV=QCOM:IR3) (push) Waiting to run
Benchmarks / openpilot 0.11.0 compile3 policy (DEV=QCOM:IR3) (push) Waiting to run
Benchmarks / openpilot 0.11.2 compile3 supercombo (DEV=QCOM:IR3) (push) Waiting to run
Benchmarks / openpilot 0.11.0 compile3 vision (DEV=QCOM:IR3) (push) Waiting to run
Benchmarks / DSP Benchmark (push) Waiting to run
Benchmarks / UsbGPU Benchmark (comma) (push) Waiting to run
Benchmarks / PCI Driver Benchmark (DEV=AMD) (push) Waiting to run
Benchmarks / PCI Driver Benchmark (DEV=NV) (push) Waiting to run
Benchmarks / LLVM Speed (push) Waiting to run
Platform Tests / MacOS (unit) (push) Waiting to run
Platform Tests / MacOS (unit, mock) (push) Waiting to run
Platform Tests / MacOS (DEV=METAL) (1) (push) Waiting to run
Platform Tests / MacOS (DEV=METAL) (2) (push) Waiting to run
Platform Tests / MacOS (DEV=CPU:CLANG) (push) Waiting to run
Platform Tests / MacOS (DEV=CPU:LLVM) (push) Waiting to run
Platform Tests / MacOS (DEV=CPU:LVP) (push) Waiting to run
Platform Tests / MacOS (DEV=WEBGPU) (push) Waiting to run
Platform Tests / Windows (DEV=CPU:CLANG) (push) Waiting to run
Platform Tests / Windows (DEV=CPU:LLVM) (push) Waiting to run
Platform Tests / Windows (DEV=CPU:X86) (push) Waiting to run
Platform Tests / Windows (DEV=WEBGPU) (push) Waiting to run
Unit Tests / Models (push) Successful in 1m43s
Unit Tests / Linux (DSP) (push) Successful in 1m54s
Unit Tests / Linters (push) Successful in 2m3s
Unit Tests / Test LLM (push) Successful in 2m5s
Unit Tests / ONNX (CPU) Tests (push) Successful in 2m15s
Unit Tests / Docs (push) Successful in 2m52s
Unit Tests / AMD ASM IDE (push) Successful in 2m56s
Unit Tests / hcq2 (push) Successful in 2m59s
Unit Tests / Fuzzing (push) Successful in 3m13s
Unit Tests / Python Backend (push) Successful in 3m15s
Unit Tests / openpilot Compile Tests (push) Successful in 3m18s
Unit Tests / Null Tests (push) Successful in 3m21s
Unit Tests / CL IMAGE Tests (push) Successful in 3m25s
Unit Tests / Torch Backend Training (push) Successful in 3m31s
Unit Tests / Linux (DEV=CPU:X86) (push) Successful in 3m28s
Unit Tests / Unit Tests (push) Successful in 3m48s
Unit Tests / Linux (DEV=CPU:LVP) (push) Successful in 3m48s
Unit Tests / Linux (DEV=CPU:LLVM) (push) Successful in 3m50s
Unit Tests / Linux (amdllvm gfx1201) (push) Successful in 3m51s
Unit Tests / Linux (amdllvm gfx1100) (push) Successful in 3m53s
Unit Tests / SPEC=2 (2) (push) Successful in 4m2s
Unit Tests / SPEC=2 (1) (push) Successful in 4m8s
Unit Tests / Linux (DEV=CL) (push) Successful in 4m9s
Unit Tests / Optimization Tests (push) Successful in 4m13s
Unit Tests / Linux (DEV=WEBGPU) (push) Successful in 4m11s
Unit Tests / Linux (am) (push) Successful in 4m16s
Unit Tests / Linux (amd gfx1100) (push) Successful in 4m19s
Unit Tests / Torch Backend Tests (push) Successful in 4m26s
Unit Tests / Linux (amd gfx1201) (push) Successful in 4m19s
Unit Tests / Compile-only (DEV=NULL:NAK:sm_120) (push) Successful in 1m39s
Unit Tests / Compile-only (DEV=NULL:IR3:a630) (push) Successful in 2m18s
Deploy Docs / deploy (push) Successful in 4m38s
Unit Tests / Linux (DEV=CPU:CLANG) (push) Successful in 4m41s
Unit Tests / Linux (amdllvm gfx950) (push) Successful in 3m12s
Unit Tests / Linux (ptx) (push) Successful in 3m12s
Unit Tests / Linux (nv) (push) Successful in 3m48s
Unit Tests / Linux (amd gfx950) (push) Successful in 4m18s
Unit Tests / Compile-only (DEV=NULL:QCOMCL:a630) (push) Successful in 3m49s
Autogen / In-tree Autogen (push) Successful in 11m29s
2026-08-27 20:11:27 -04:00
chenyuandGitHub fede358811 fix fancy indexing with uint8 index (#17787)
* fix fancy indexing with uint8 index

* fix
2026-08-27 19:59:27 -04:00
sirhcmandGitHub f06832bf6f test llm with --no_chat_template (#17785)
Autogen / In-tree Autogen (macos) (push) Waiting to run
Benchmarks / Mac pytest (push) Waiting to run
Benchmarks / LLM (DEV=AMD) (push) Waiting to run
Benchmarks / LLM (DEV=METAL) (push) Waiting to run
Benchmarks / LLM (DEV=NV) (push) Waiting to run
Benchmarks / HLB-CIFAR10 (DEV=AMD) (push) Waiting to run
Benchmarks / HLB-CIFAR10 (DEV=METAL) (push) Waiting to run
Benchmarks / HLB-CIFAR10 (DEV=NV) (push) Waiting to run
Benchmarks / MLPerf (AMD) (push) Waiting to run
Benchmarks / MLPerf (NV) (push) Waiting to run
Benchmarks / Stable Diffusion (DEV=AMD) (push) Waiting to run
Benchmarks / Stable Diffusion (DEV=METAL) (push) Waiting to run
Benchmarks / Stable Diffusion (DEV=NV) (push) Waiting to run
Benchmarks / Multi-GPU Benchmarks (DEV=AMD) (push) Waiting to run
Benchmarks / Multi-GPU Benchmarks (DEV=NV) (push) Waiting to run
Benchmarks / Tests (DEV=AMD) (push) Waiting to run
Benchmarks / Tests (DEV=METAL) (push) Waiting to run
Benchmarks / Tests (DEV=NV) (push) Waiting to run
Benchmarks / UsbGPU Benchmark (push) Waiting to run
Benchmarks / openpilot 0.11.0 compile3 dmonitoring (DEV=QCOM) (push) Waiting to run
Benchmarks / openpilot 0.11.2 compile3 dmonitoring (DEV=QCOM) (push) Waiting to run
Benchmarks / openpilot 0.11.0 compile3 policy (DEV=QCOM) (push) Waiting to run
Benchmarks / openpilot 0.11.2 compile3 supercombo (DEV=QCOM) (push) Waiting to run
Benchmarks / openpilot 0.11.0 compile3 vision (DEV=QCOM) (push) Waiting to run
Benchmarks / openpilot 0.11.0 compile3 dmonitoring (DEV=QCOM:IR3) (push) Waiting to run
Benchmarks / openpilot 0.11.2 compile3 dmonitoring (DEV=QCOM:IR3) (push) Waiting to run
Benchmarks / openpilot 0.11.0 compile3 policy (DEV=QCOM:IR3) (push) Waiting to run
Benchmarks / openpilot 0.11.2 compile3 supercombo (DEV=QCOM:IR3) (push) Waiting to run
Benchmarks / openpilot 0.11.0 compile3 vision (DEV=QCOM:IR3) (push) Waiting to run
Benchmarks / DSP Benchmark (push) Waiting to run
Benchmarks / UsbGPU Benchmark (comma) (push) Waiting to run
Benchmarks / PCI Driver Benchmark (DEV=AMD) (push) Waiting to run
Benchmarks / PCI Driver Benchmark (DEV=NV) (push) Waiting to run
Benchmarks / LLVM Speed (push) Waiting to run
Platform Tests / MacOS (unit) (push) Waiting to run
Platform Tests / MacOS (unit, mock) (push) Waiting to run
Platform Tests / MacOS (DEV=METAL) (1) (push) Waiting to run
Platform Tests / MacOS (DEV=METAL) (2) (push) Waiting to run
Platform Tests / MacOS (DEV=CPU:CLANG) (push) Waiting to run
Platform Tests / MacOS (DEV=CPU:LLVM) (push) Waiting to run
Platform Tests / MacOS (DEV=CPU:LVP) (push) Waiting to run
Platform Tests / MacOS (DEV=WEBGPU) (push) Waiting to run
Platform Tests / Windows (DEV=CPU:CLANG) (push) Waiting to run
Platform Tests / Windows (DEV=CPU:LLVM) (push) Waiting to run
Platform Tests / Windows (DEV=CPU:X86) (push) Waiting to run
Platform Tests / Windows (DEV=WEBGPU) (push) Waiting to run
Unit Tests / Models (push) Successful in 1m36s
Unit Tests / Linux (DSP) (push) Successful in 1m50s
Unit Tests / Test LLM (push) Successful in 2m0s
Unit Tests / Linters (push) Successful in 2m2s
Unit Tests / Fuzzing (push) Successful in 2m16s
Unit Tests / hcq2 (push) Failing after 2m29s
Unit Tests / Docs (push) Successful in 2m57s
Unit Tests / Python Backend (push) Successful in 3m15s
Unit Tests / openpilot Compile Tests (push) Successful in 3m16s
Unit Tests / AMD ASM IDE (push) Successful in 3m12s
Unit Tests / Null Tests (push) Successful in 3m21s
Unit Tests / CL IMAGE Tests (push) Successful in 3m22s
Unit Tests / Unit Tests (push) Successful in 3m24s
Unit Tests / Torch Backend Training (push) Successful in 3m26s
Unit Tests / Linux (DEV=CPU:X86) (push) Successful in 3m22s
Unit Tests / Linux (DEV=CPU:LVP) (push) Successful in 3m53s
Unit Tests / SPEC=2 (2) (push) Successful in 3m57s
Unit Tests / Linux (DEV=CPU:LLVM) (push) Successful in 3m57s
Unit Tests / SPEC=2 (1) (push) Successful in 4m6s
Unit Tests / Linux (amdllvm gfx1100) (push) Successful in 3m59s
Unit Tests / Linux (amdllvm gfx1201) (push) Successful in 3m58s
Unit Tests / Optimization Tests (push) Successful in 4m14s
Unit Tests / Compile-only (DEV=NULL:NAK:sm_120) (push) Successful in 1m45s
Unit Tests / Linux (DEV=CL) (push) Successful in 4m17s
Unit Tests / Torch Backend Tests (push) Successful in 4m22s
Unit Tests / Linux (DEV=WEBGPU) (push) Successful in 4m24s
Unit Tests / Linux (am) (push) Successful in 4m23s
Unit Tests / Linux (amd gfx1100) (push) Successful in 4m25s
Unit Tests / Linux (amd gfx1201) (push) Successful in 4m24s
Unit Tests / ONNX (CPU) Tests (push) Failing after 4m32s
Unit Tests / Compile-only (DEV=NULL:IR3:a630) (push) Successful in 2m25s
Deploy Docs / deploy (push) Successful in 5m1s
Unit Tests / Linux (DEV=CPU:CLANG) (push) Successful in 5m5s
Unit Tests / Linux (amdllvm gfx950) (push) Successful in 3m32s
Unit Tests / Linux (ptx) (push) Successful in 3m22s
Unit Tests / Linux (nv) (push) Successful in 4m17s
Unit Tests / Linux (amd gfx950) (push) Successful in 5m3s
Unit Tests / Compile-only (DEV=NULL:QCOMCL:a630) (push) Successful in 4m28s
Autogen / In-tree Autogen (push) Successful in 12m36s
2026-08-27 19:01:55 -04:00
41 changed files with 287 additions and 263 deletions
+11 -3
View File
@@ -194,21 +194,29 @@ runs:
echo "pkgs=$pkgs" >> "$GITHUB_OUTPUT"
echo "hash=$(echo -n "$pkgs" | sha256sum | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"
installed=true
for pkg in $pkgs; do
info=$(dpkg-query -W -f='${db:Status-Abbrev} ${Version}' "$pkg" 2> /dev/null || true)
echo "${pkg}: ${info:-not in dpkg database}"
[[ "$info" == ii* ]] || installed=false
done
echo "installed=$installed" >> "$GITHUB_OUTPUT"
- name: Cache apt (PR)
if: runner.os == 'Linux' && (inputs.opencl == 'true' || inputs.amd == 'true' || inputs.webgpu == 'true' || inputs.llvm == 'true' || inputs.qemu == 'true' || inputs.ninja == 'true') && github.event_name == 'pull_request'
if: runner.os == 'Linux' && (inputs.opencl == 'true' || inputs.amd == 'true' || inputs.webgpu == 'true' || inputs.llvm == 'true' || inputs.qemu == 'true' || inputs.ninja == 'true') && github.event_name == 'pull_request' && steps.apt-pkgs.outputs.installed == 'false'
uses: actions/cache/restore@v5
with:
path: /var/cache/apt/archives/
key: ${{ runner.os }}-${{ runner.arch }}-apt-${{ steps.apt-pkgs.outputs.hash }}-${{ env.CACHE_VERSION }}
- name: Cache apt
if: runner.os == 'Linux' && (inputs.opencl == 'true' || inputs.amd == 'true' || inputs.webgpu == 'true' || inputs.llvm == 'true' || inputs.qemu == 'true' || inputs.ninja == 'true') && github.event_name != 'pull_request'
if: runner.os == 'Linux' && (inputs.opencl == 'true' || inputs.amd == 'true' || inputs.webgpu == 'true' || inputs.llvm == 'true' || inputs.qemu == 'true' || inputs.ninja == 'true') && github.event_name != 'pull_request' && steps.apt-pkgs.outputs.installed == 'false'
uses: actions/cache@v5
with:
path: /var/cache/apt/archives/
key: ${{ runner.os }}-${{ runner.arch }}-apt-${{ steps.apt-pkgs.outputs.hash }}-${{ env.CACHE_VERSION }}
- name: Run apt Update + Install
if: runner.os == 'Linux' && (inputs.opencl == 'true' || inputs.amd == 'true' || inputs.webgpu == 'true' || inputs.llvm == 'true' || inputs.qemu == 'true' || inputs.ninja == 'true')
if: runner.os == 'Linux' && (inputs.opencl == 'true' || inputs.amd == 'true' || inputs.webgpu == 'true' || inputs.llvm == 'true' || inputs.qemu == 'true' || inputs.ninja == 'true') && steps.apt-pkgs.outputs.installed == 'false'
shell: bash
run: |
sudo apt -qq update || true
+5 -5
View File
@@ -94,7 +94,7 @@ jobs:
shell: bash -e -o pipefail {0}
env:
DEV: ${{ matrix.dev }}
HCQ2: ${{ matrix.dev == 'AMD' && '1' || '0' }}
HCQ2: '0'
if: github.repository_owner == 'tinygrad'
steps:
- name: Checkout Code
@@ -140,7 +140,7 @@ jobs:
shell: bash -e -o pipefail {0}
env:
DEV: ${{ matrix.dev }}
HCQ2: ${{ matrix.dev == 'AMD' && '1' || '0' }}
HCQ2: '0'
if: github.repository_owner == 'tinygrad'
steps:
- name: Checkout Code
@@ -188,7 +188,7 @@ jobs:
shell: bash -e -o pipefail {0}
env:
DEV: ${{ matrix.dev }}
HCQ2: ${{ matrix.dev == 'AMD' && '1' || '0' }}
HCQ2: '0'
if: github.repository_owner == 'tinygrad'
steps:
- name: Checkout Code
@@ -230,7 +230,7 @@ jobs:
shell: bash -e -o pipefail {0}
env:
DEV: ${{ matrix.dev }}
HCQ2: ${{ matrix.dev == 'AMD' && '1' || '0' }}
HCQ2: '0'
if: github.repository_owner == 'tinygrad'
steps:
- name: Checkout Code
@@ -275,7 +275,7 @@ jobs:
shell: bash -e -o pipefail {0}
env:
DEV: ${{ matrix.dev }}
HCQ2: ${{ matrix.dev == 'AMD' && '1' || '0' }}
HCQ2: '0'
if: github.repository_owner == 'tinygrad'
steps:
- name: Checkout Code
+3 -5
View File
@@ -1667,7 +1667,7 @@ def train_llama3():
def train_gptoss():
from examples.mlperf.models.gpt_oss import GPTOSS, GPT_OSS_20B, apply_grad, FP8_DTYPE
from examples.mlperf.lr_schedulers import CosineAnnealingLRWithWarmup
from examples.mlperf.optim import GradAccClipAdamW, GradAccClipAdamWGroup, clip_grads
from examples.mlperf.optim import GradAccClipAdamW, GradAccClipAdamWGroup, fclip_grads
BENCHMARK = getenv("BENCHMARK")
@@ -1785,12 +1785,10 @@ def train_gptoss():
Tensor.realize(loss, *grads)
grad_norm = clip_grads(grads, 1, 1.0)
optim.fstep(grads, grad_norm)
clipped_grads, grad_norm = fclip_grads(grads, 1.0)
optim.fstep(clipped_grads, grad_norm)
scheduler.step()
for g in grads: g.assign(0)
loss_cpu = loss.flatten().float().to("CPU")
lr_cpu = optim.lr.float().to("CPU")
grad_norm_cpu = grad_norm.float().to("CPU")
+3 -3
View File
@@ -282,14 +282,14 @@ def apply_grad(grad_buf:Tensor, new_grad:UOp):
pads = _get_pads(new_grad)
if len(pads) <= 1:
new_grad = new_grad.cast(grad_buf.dtype)
grad_buf.uop = grad_buf.uop.after(grad_buf.uop.store(grad_buf.uop + new_grad))
grad_buf.uop = grad_buf.uop.after(grad_buf.uop.store(new_grad))
return
cur = grad_buf.uop
for pad in sorted(pads, key=lambda p: p.marg[0][0] if p.op == Ops.PAD else 0, reverse=True):
if pad.op == Ops.PAD:
grad_shrink = tuple([(p[0], s+p[0]) for s,p in zip(pad.src[0].shape, pad.marg)])
grad_shrink = tuple((p[0], s+p[0]) for s,p in zip(pad.src[0].shape, pad.marg))
buf_slice = cur.shrink(grad_shrink)
cur = cur.after(buf_slice.store(buf_slice + pad.src[0].cast(cur.dtype)))
cur = cur.after(buf_slice.store(pad.src[0].cast(cur.dtype)))
else:
cur = cur.after(cur.store(cur + pad.cast(cur.dtype)))
grad_buf.uop = cur
+5
View File
@@ -27,6 +27,11 @@ def clip_grads(grads:list[Tensor], grad_acc, clip_norm) -> Tensor:
for g in grads: g.assign((g * (clip_norm / (total_norm + 1e-6)).clamp(max_=1.0)).cast(g.dtype))
return total_norm
def fclip_grads(grads:list[Tensor], clip_norm) -> Tensor:
total_norm = Tensor.stack(*[g.float().square().sum() for g in grads]).sum().sqrt().contiguous()
scale = (clip_norm / (total_norm + 1e-6)).clamp(max_=1.0)
return [(g * scale).cast(g.dtype) for g in grads], total_norm
class GradAccClipAdamW(Optimizer):
def __init__(self, params:list[Tensor], lr=0.001, b1=0.9, b2=0.999, eps=1e-6, weight_decay=0.0, grad_acc=1, clip_norm=1.0, device=None, fused=FUSE_OPTIM):
super().__init__(params, lr, device, fused)
+68 -115
View File
@@ -20,34 +20,39 @@ def v_mfma_fp4(dst, a, b, opsel, opsel_hi, scale_a, scale_b):
def build_kernel(M: int, N: int, K: int, tile_m: int, tile_n: int):
k = Kernel()
scale_k = K // 32
k.emit(s_and_b32(s[1], s[1], LIT, 65535))
if (tile_m, tile_n) == (128, 512):
k.emit(s_and_b32(s[1], s[1], LIT, 65535))
k.emit(s_mov_b32(s[47], s[2]))
k.emit(s_mov_b32(s[48], s[3]))
k.emit(s_load_dwordx2(s[4:5], s[0:1], s[0], 0, 0, 0, 0, 1))
k.emit(s_mov_b32(s[8], 0))
k.emit(s_mov_b32(s[9], 0))
k.emit(s_load_dwordx2(s[12:13], s[0:1], s[0], 8, 0, 0, 0, 1))
k.emit(s_load_dwordx2(s[16:17], s[0:1], s[0], 16, 0, 0, 0, 1))
k.emit(s_mov_b32(s[36], N))
k.emit(s_mov_b32(s[37], K))
k.emit(s_mov_b32(s[38], K))
k.emit(s_mov_b32(s[43], M))
k.emit(s_mov_b32(s[44], N))
k.emit(s_mov_b32(s[45], K))
k.emit(s_load_dwordx2(s[20:21], s[0:1], s[0], 24, 0, 0, 0, 1))
k.emit(s_load_dwordx2(s[24:25], s[0:1], s[0], 32, 0, 0, 0, 1))
k.emit(s_mov_b32(s[39], scale_k))
k.emit(s_mov_b32(s[40], scale_k))
k.emit(v_lshrrev_b32_e32(v[1], 10))
k.emit(v_lshrrev_b32_e32(v[2], 10, v[1]))
k.emit(v_and_b32_e32(v[2], LIT, v[2], 1023))
k.emit(v_and_b32_e32(v[1], LIT, v[1], 1023))
k.emit(v_and_b32_e32(v[0], LIT, v[0], 1023))
k.emit(v_lshrrev_b32_e32(v[3], 6))
k.emit(v_and_b32_e32(v[0], 63))
k.emit(v_readfirstlane_b32_e32(v[46], v[3]))
k.emit(s_waitcnt(49279))
k.emit(s_load_dwordx2(s[4:5], s[0:1], s[0], 0, 0, 0, 0, 1))
k.emit(s_mov_b32(s[8], 0))
k.emit(s_mov_b32(s[9], 0))
k.emit(s_load_dwordx2(s[12:13], s[0:1], s[0], 8, 0, 0, 0, 1))
k.emit(s_load_dwordx2(s[16:17], s[0:1], s[0], 16, 0, 0, 0, 1))
k.emit(s_mov_b32(s[36], N))
k.emit(s_mov_b32(s[37], K))
k.emit(s_mov_b32(s[38], K))
k.emit(s_mov_b32(s[43], M))
k.emit(s_mov_b32(s[44], N))
k.emit(s_mov_b32(s[45], K))
k.emit(s_load_dwordx2(s[20:21], s[0:1], s[0], 24, 0, 0, 0, 1))
k.emit(s_load_dwordx2(s[24:25], s[0:1], s[0], 32, 0, 0, 0, 1))
k.emit(s_mov_b32(s[39], scale_k))
k.emit(s_mov_b32(s[40], scale_k))
k.emit(v_lshrrev_b32_e32(v[1], 10))
k.emit(v_lshrrev_b32_e32(v[2], 10, v[1]))
k.emit(v_and_b32_e32(v[2], LIT, v[2], 1023))
k.emit(v_and_b32_e32(v[1], LIT, v[1], 1023))
k.emit(v_and_b32_e32(v[0], LIT, v[0], 1023))
k.emit(v_lshrrev_b32_e32(v[3], 6))
k.emit(v_and_b32_e32(v[0], 63))
if (tile_m, tile_n) == (256, 256):
k.emit(s_mov_b32(s[49], s[2]))
k.emit(s_mov_b32(s[47], s[3]))
k.emit(v_readfirstlane_b32_e32(v[46], v[3]))
k.emit(s_waitcnt(49279))
if (tile_m, tile_n) == (128, 512):
for i in range(2):
k.emit(s_mov_b32(s[6 + i * 8], -16))
k.emit(s_mov_b32(s[10 + i * 12], -16))
@@ -1213,31 +1218,6 @@ def build_kernel(M: int, N: int, K: int, tile_m: int, tile_n: int):
k.emit(s_waitcnt())
k.emit(s_endpgm())
elif (tile_m, tile_n) == (192, 256):
k.emit(s_and_b32(s[1], s[1], LIT, 65535))
k.emit(s_load_dwordx2(s[4:5], s[0:1], s[0], 0, 0, 0, 0, 1))
k.emit(s_mov_b32(s[8], 0))
k.emit(s_mov_b32(s[9], 0))
k.emit(s_load_dwordx2(s[12:13], s[0:1], s[0], 8, 0, 0, 0, 1))
k.emit(s_load_dwordx2(s[16:17], s[0:1], s[0], 16, 0, 0, 0, 1))
k.emit(s_mov_b32(s[36], N))
k.emit(s_mov_b32(s[37], K))
k.emit(s_mov_b32(s[38], K))
k.emit(s_mov_b32(s[43], M))
k.emit(s_mov_b32(s[44], N))
k.emit(s_mov_b32(s[45], K))
k.emit(s_load_dwordx2(s[20:21], s[0:1], s[0], 24, 0, 0, 0, 1))
k.emit(s_load_dwordx2(s[24:25], s[0:1], s[0], 32, 0, 0, 0, 1))
k.emit(s_mov_b32(s[39], scale_k))
k.emit(s_mov_b32(s[40], scale_k))
k.emit(v_lshrrev_b32_e32(v[1], 10))
k.emit(v_lshrrev_b32_e32(v[2], 10, v[1]))
k.emit(v_and_b32_e32(v[2], LIT, v[2], 1023))
k.emit(v_and_b32_e32(v[1], LIT, v[1], 1023))
k.emit(v_and_b32_e32(v[0], LIT, v[0], 1023))
k.emit(v_lshrrev_b32_e32(v[3], 6))
k.emit(v_and_b32_e32(v[0], 63))
k.emit(v_readfirstlane_b32_e32(v[46], v[3]))
k.emit(s_waitcnt(49279))
k.emit(s_mul_i32(s[63], LIT, 8, 192))
k.emit(v_cvt_f32_u32_e32(v[4], s[63]))
k.emit(s_sub_i32(s[62], 0, s[63]))
@@ -2234,49 +2214,22 @@ def build_kernel(M: int, N: int, K: int, tile_m: int, tile_n: int):
k.emit(s_waitcnt())
k.emit(s_endpgm())
elif (tile_m, tile_n) == (256, 256):
k.emit(s_and_b32(s[1], s[1], LIT, 65535))
k.emit(s_load_dwordx2(s[4:5], s[0:1], s[0], 0, 0, 0, 0, 1))
k.emit(s_mov_b32(s[8], 0))
k.emit(s_mov_b32(s[9], 0))
k.emit(s_load_dwordx2(s[12:13], s[0:1], s[0], 8, 0, 0, 0, 1))
k.emit(s_load_dwordx2(s[16:17], s[0:1], s[0], 16, 0, 0, 0, 1))
k.emit(s_mov_b32(s[40], N))
k.emit(s_mov_b32(s[41], K))
k.emit(s_mov_b32(s[42], K))
k.emit(s_mov_b32(s[43], M))
k.emit(s_mov_b32(s[44], N))
k.emit(s_mov_b32(s[45], K))
k.emit(s_load_dwordx2(s[20:21], s[0:1], s[0], 24, 0, 0, 0, 1))
k.emit(s_load_dwordx2(s[24:25], s[0:1], s[0], 32, 0, 0, 0, 1))
k.emit(s_mov_b32(s[36], scale_k))
k.emit(s_mov_b32(s[37], scale_k))
k.emit(v_lshrrev_b32_e32(v[1], 10))
k.emit(v_lshrrev_b32_e32(v[2], 10, v[1]))
k.emit(v_and_b32_e32(v[2], LIT, v[2], 1023))
k.emit(v_and_b32_e32(v[1], LIT, v[1], 1023))
k.emit(v_and_b32_e32(v[0], LIT, v[0], 1023))
k.emit(v_lshrrev_b32_e32(v[3], 6))
k.emit(v_and_b32_e32(v[0], 63))
k.emit(s_mov_b32(s[46], s[2]))
k.emit(s_mov_b32(s[47], s[3]))
k.emit(v_readfirstlane_b32_e32(v[49], v[3]))
k.emit(s_waitcnt(49279))
k.emit(s_add_u32(s[55], s[44], LIT, 255))
k.emit(s_lshr_b32(s[54], s[55], 8))
k.emit(s_mul_i32(s[48], s[54], s[47]))
k.emit(s_add_i32(s[48], s[48], s[46]))
k.emit(s_add_i32(s[48], s[48], s[49]))
k.emit(s_add_u32(s[55], s[43], LIT, 255))
k.emit(s_lshr_b32(s[52], s[55], 8))
k.emit(s_lshl_b32(s[52], s[52], 5))
k.emit(s_mov_b32(s[46], 0))
k.emit(s_mov_b32(s[49], 0))
k.label('L2_00E8')
k.emit(s_cmp_lt_i32(s[48], s[52]))
k.emit(s_cbranch_scc1(3), target='L2_00FC')
k.emit(s_sub_i32(s[48], s[48], s[52]))
k.emit(s_add_i32(s[46], s[46], 32))
k.emit(s_add_i32(s[49], s[49], 32))
k.emit(s_branch(65531), target='L2_00E8')
k.label('L2_00FC')
k.emit(s_sub_i32(s[54], s[54], s[46]))
k.emit(s_sub_i32(s[54], s[54], s[49]))
k.emit(s_cmp_lt_i32(s[54], 32))
k.emit(s_cbranch_scc1(3), target='L2_0114')
k.emit(s_lshr_b32(s[47], s[48], 5))
@@ -2311,7 +2264,7 @@ def build_kernel(M: int, N: int, K: int, tile_m: int, tile_n: int):
k.emit(s_mul_i32(s[52], s[54], s[47]))
k.emit(s_sub_i32(s[52], s[48], s[52]))
k.label('L2_0194')
k.emit(s_add_i32(s[46], s[52], s[46]))
k.emit(s_add_i32(s[49], s[52], s[49]))
k.emit(s_mov_b32(s[6], -16))
k.emit(s_mov_b32(s[10], -16))
k.emit(s_mov_b32(s[18], -16))
@@ -2328,18 +2281,18 @@ def build_kernel(M: int, N: int, K: int, tile_m: int, tile_n: int):
k.emit(s_or_b32(s[9], s[9], LIT, 262144))
k.emit(s_or_b32(s[17], s[17], LIT, 262144))
k.emit(s_or_b32(s[13], s[13], LIT, 262144))
k.emit(s_lshr_b32(s[41], s[41], 1))
k.emit(s_mul_i32(s[52], s[41], s[43]))
k.emit(s_lshr_b32(s[37], s[37], 1))
k.emit(s_mul_i32(s[52], s[37], s[43]))
k.emit(s_mov_b32(s[14], s[52]))
k.emit(s_lshr_b32(s[42], s[42], 1))
k.emit(s_mul_i32(s[52], s[42], s[44]))
k.emit(s_lshr_b32(s[38], s[38], 1))
k.emit(s_mul_i32(s[52], s[38], s[44]))
k.emit(s_mov_b32(s[18], s[52]))
k.emit(s_add_u32(s[52], s[43], 31))
k.emit(s_lshr_b32(s[52], s[52], 5))
k.emit(s_lshl_b32(s[52], s[52], 5))
k.emit(s_mul_i32(s[53], s[52], s[36]))
k.emit(s_mul_i32(s[53], s[52], s[39]))
k.emit(s_mov_b32(s[22], s[53]))
k.emit(s_mul_i32(s[53], s[44], s[37]))
k.emit(s_mul_i32(s[53], s[44], s[40]))
k.emit(s_mov_b32(s[26], s[53]))
k.emit(s_mov_b32(s[23], LIT, 131072))
k.emit(s_mov_b32(s[27], LIT, 131072))
@@ -2356,23 +2309,23 @@ def build_kernel(M: int, N: int, K: int, tile_m: int, tile_n: int):
k.emit(v_add_u32_e32(v[5], v[5], v[6]))
k.emit(v_and_b32_e32(v[4], 1, v[4]))
k.emit(v_add_u32_e32(v[5], v[5], v[4]))
k.emit(v_mul_lo_u32(v[212], s[41], v[5]))
k.emit(v_mul_lo_u32(v[212], s[37], v[5]))
k.emit(v_and_b32_e32(v[4], 7))
k.emit(v_lshlrev_b32_e32(v[4], 4, v[4]))
k.emit(v_add_u32_e32(v[212], v[212], v[4]))
k.emit(s_lshr_b32(s[52], s[49], 1))
k.emit(s_lshr_b32(s[52], s[46], 1))
k.emit(s_mul_i32(s[52], s[52], 8))
k.emit(s_and_b32(s[53], s[49], 1))
k.emit(s_and_b32(s[53], s[46], 1))
k.emit(s_mul_i32(s[53], s[53], 2))
k.emit(s_add_u32(s[52], s[52], s[53]))
k.emit(s_mul_i32(s[53], s[47], LIT, 256))
k.emit(s_add_u32(s[52], s[52], s[53]))
k.emit(s_mul_i32(s[52], s[41], s[52]))
k.emit(s_mul_i32(s[52], s[37], s[52]))
k.emit(v_add_u32_e32(v[212], s[52], v[212]))
k.emit(s_mul_i32(s[52], s[41], 32))
k.emit(s_mul_i32(s[52], s[37], 32))
for i in range(7):
k.emit(v_add_u32_e32(v[213 + i * 1], s[52], v[212 + i * 1]))
k.emit(s_mul_i32(s[59], LIT, s[49], 1056))
k.emit(s_mul_i32(s[59], LIT, s[46], 1056))
k.emit(s_add_u32(s[59], LIT, s[59], 4096))
k.emit(v_and_b32_e32(v[4], 15))
k.emit(v_lshrrev_b32_e32(v[5], 3, v[4]))
@@ -2396,35 +2349,35 @@ def build_kernel(M: int, N: int, K: int, tile_m: int, tile_n: int):
k.emit(v_add_u32_e32(v[221], LIT, v[220], 33792))
k.emit(v_lshlrev_b32_e32(v[222], 2))
k.emit(s_mul_i32(s[52], s[47], LIT, 256))
k.emit(s_mul_i32(s[53], s[49], 32))
k.emit(s_mul_i32(s[53], s[46], 32))
k.emit(s_add_i32(s[52], s[53], s[52]))
k.emit(s_mul_i32(s[53], s[52], s[36]))
k.emit(s_mul_i32(s[53], s[52], s[39]))
k.emit(v_add_u32_e32(v[222], s[53], v[222]))
k.emit(s_mul_i32(s[53], LIT, s[36], 128))
k.emit(s_mul_i32(s[53], LIT, s[39], 128))
k.emit(v_add_u32_e32(v[223], s[53], v[222]))
k.emit(s_mul_i32(s[60], s[49], LIT, 256))
k.emit(s_mul_i32(s[60], s[46], LIT, 256))
k.emit(s_add_i32(s[60], s[60], 0))
k.emit(v_lshlrev_b32_e32(v[224], 2))
k.emit(v_add_u32_e32(v[224], 0, v[224]))
k.emit(v_lshlrev_b32_e32(v[225], 4))
k.emit(s_mul_i32(s[52], s[46], LIT, 256))
k.emit(s_mul_i32(s[53], s[49], 64))
k.emit(s_mul_i32(s[52], s[49], LIT, 256))
k.emit(s_mul_i32(s[53], s[46], 64))
k.emit(s_add_u32(s[52], s[52], s[53]))
k.emit(s_mul_i32(s[52], s[52], s[42]))
k.emit(s_mul_i32(s[52], s[52], s[38]))
k.emit(v_add_u32_e32(v[225], s[52], v[225]))
k.emit(s_mul_i32(s[52], 16, s[42]))
k.emit(s_mul_i32(s[52], 16, s[38]))
k.emit(v_add_u32_e32(v[226], s[52], v[225]))
k.emit(v_add_u32_e32(v[227], s[52], v[226]))
k.emit(v_add_u32_e32(v[228], s[52], v[227]))
for i in range(4):
k.emit(v_add_u32_e32(v[229 + i * 1], LIT, v[225 + i * 1], 1024))
k.emit(v_lshlrev_b32_e32(v[233], 2))
k.emit(s_mul_i32(s[52], s[46], LIT, 256))
k.emit(s_mul_i32(s[53], s[49], 64))
k.emit(s_mul_i32(s[52], s[49], LIT, 256))
k.emit(s_mul_i32(s[53], s[46], 64))
k.emit(s_add_i32(s[52], s[53], s[52]))
k.emit(s_mul_i32(s[53], s[52], s[37]))
k.emit(s_mul_i32(s[53], s[52], s[40]))
k.emit(v_add_u32_e32(v[233], s[53], v[233]))
k.emit(s_mul_i32(s[52], 32, s[37]))
k.emit(s_mul_i32(s[52], 32, s[40]))
k.emit(v_add_u32_e32(v[234], s[52], v[233]))
k.emit(s_mov_b32(s[61], LIT, 128))
k.emit(s_mov_b32(s[62], LIT, 2048))
@@ -2510,18 +2463,18 @@ def build_kernel(M: int, N: int, K: int, tile_m: int, tile_n: int):
k.emit(ds_read_b32(v[201], v[224], v[0], v[0], 0, 0, 1))
k.emit(ds_read_b32(v[202], v[224], v[0], v[0], 0, 0, 2))
k.emit(ds_read_b32(v[203], v[224], v[0], v[0], 0, 0, 3))
k.emit(s_lshl_b32(s[40], s[40], 1))
k.emit(s_lshl_b32(s[36], s[36], 1))
k.emit(s_mul_i32(s[52], s[47], LIT, 256))
k.emit(s_mul_hi_u32(s[53], s[52], s[40]))
k.emit(s_mul_hi_u32(s[53], s[52], s[36]))
k.emit(s_add_u32(s[5], s[5], s[53]))
k.emit(s_mul_i32(s[53], s[52], s[40]))
k.emit(s_mul_i32(s[53], s[52], s[36]))
k.emit(s_add_u32(s[4], s[4], s[53]))
k.emit(s_addc_u32(s[5], 0, s[5]))
k.emit(s_sub_i32(s[52], s[43], s[52]))
k.emit(s_mul_i32(s[52], s[52], s[40]))
k.emit(s_mul_i32(s[52], s[52], s[36]))
k.emit(s_mov_b32(s[6], s[52]))
k.emit(v_and_b32_e64(v[235], v[0], 15))
k.emit(v_mul_lo_u32(v[235], v[235], s[40]))
k.emit(v_mul_lo_u32(v[235], v[235], s[36]))
k.emit(v_lshrrev_b32_e32(v[4], 5))
k.emit(v_mul_i32_i24_e32(v[4], 16, v[4]))
k.emit(v_add_u32_e32(v[235], v[4], v[235]))
@@ -2529,12 +2482,12 @@ def build_kernel(M: int, N: int, K: int, tile_m: int, tile_n: int):
k.emit(v_and_b32_e32(v[4], 1, v[4]))
k.emit(v_mul_i32_i24_e32(v[4], 32, v[4]))
k.emit(v_add_u32_e32(v[235], v[4], v[235]))
k.emit(s_mul_i32(s[52], s[46], LIT, 256))
k.emit(s_mul_i32(s[53], s[49], 64))
k.emit(s_mul_i32(s[52], s[49], LIT, 256))
k.emit(s_mul_i32(s[53], s[46], 64))
k.emit(s_add_i32(s[52], s[52], s[53]))
k.emit(s_lshl_b32(s[52], s[52], 1))
k.emit(v_add_u32_e32(v[235], s[52], v[235]))
k.emit(s_mul_i32(s[53], s[40], 16))
k.emit(s_mul_i32(s[53], s[36], 16))
for i in range(15):
k.emit(v_add_u32_e64(v[236 + i * 1], v[235 + i * 1], s[53]))
k.emit(s_mov_b32(s[50], 0))
@@ -2543,7 +2496,7 @@ def build_kernel(M: int, N: int, K: int, tile_m: int, tile_n: int):
k.emit(s_cmp_lt_u32(LIT, s[51], 512 + i * -256))
k.emit(s_cselect_b32(s[61 + i * 1], s[61 + i * 1], 0))
k.emit(s_cselect_b32(s[63 + i * 1], s[63 + i * 1], 0))
k.emit(s_cmp_lt_i32(s[49], 2))
k.emit(s_cmp_lt_i32(s[46], 2))
k.emit(s_cbranch_scc0(1367), target='L2_25B8')
k.label('L2_105C')
k.emit(s_waitcnt(122))
+2 -4
View File
@@ -1,8 +1,6 @@
from __future__ import annotations
import functools, pathlib
from dataclasses import replace
from tinygrad import Tensor, dtypes
from tinygrad.uop.ops import shape_to_shape_arg
from tinygrad import Tensor
from tinygrad.runtime.support.compiler_amd import HIPCCCompiler
FP8_MAX = 448.0
@@ -12,7 +10,7 @@ NUM_WG, THREADS_PER_WG = 1024, 256
@functools.cache
def _local_abs_max_fxn(x_p, device):
x = Tensor(x_p, device=device)
inner = Tensor(x.uop.replace(src=(shape_to_shape_arg(x.uop.shard_shape),), arg=replace(x.uop.arg, axis=None))) if x.uop.axis is not None else x
inner = Tensor(x.uop.src[0]) if x.uop.axis is not None else x # the per-shard view of the flat param
return (inner.abs().max(),)
def local_abs_max(x:Tensor) -> Tensor:
+2
View File
@@ -1,10 +1,12 @@
from tinygrad import Tensor
from tinygrad.helpers import NUM_CPU_THREADS
from tinygrad.tensor import _to_np_dtype
from tinygrad.nn.onnx import OnnxRunner, OnnxValue
import numpy as np
import onnxruntime as ort
ort_options = ort.SessionOptions()
ort_options.log_severity_level = 3
ort_options.intra_op_num_threads = NUM_CPU_THREADS.value
def get_example_inputs(graph_inputs:dict[str, OnnxValue], config={}):
"""
BIN
View File
Binary file not shown.
+6 -6
View File
@@ -50,10 +50,10 @@ All nodes in the tinygrad graph are \textbf{UOps}. A UOp is a tuple $(\mathrm{op
\toprule
\textbf{Op} & \textbf{src} & \textbf{arg} & \textbf{Semantics} \\
\midrule
\op{Param} & $(\mathbf{s})$ & slot, dtype, device?, addrspace? &
Placeholder with shape $\mathbf{s}$. Substituted in \op{Function}. \\[4pt]
\op{Buffer} & $(\mathbf{s})$ & slot, dtype, device, addrspace &
Concrete buffer slot with shape $\mathbf{s}$. If device is a tuple, it creates the fully sized buffer across multiple devices. \\
\op{Param} & () & slot, dtype, size?, device?, addrspace? &
Placeholder with flat storage of $\mathrm{size}$ elements. Substituted in \op{Call}. \\[4pt]
\op{Buffer} & () & slot, dtype, size, device, addrspace &
Concrete buffer slot with flat storage of $\mathrm{size}$ elements. \\
\op{Const} & () & value, dtype &
A scalar constant with shape $(\ )$. \\
& & & Form vector consts with \op{Stack} \\
@@ -256,9 +256,9 @@ Every UOp has a \textbf{dtype}, \textbf{shape}, \textbf{device}, \textbf{addrspa
\toprule
\textbf{Op} & \textbf{dtype} & \textbf{shape} & \textbf{device} & \textbf{min\_max} \\
\midrule
\op{Buffer} & from arg & from $\mathrm{src}[0]$ & from arg & dtype range \\
\op{Buffer} & from arg & from arg ($\mathrm{size}$) & from arg & dtype range \\
\op{Const} & from arg & $()$ & \textsc{null} & $[v, v]$ \\
\op{Param} & from arg & from $\mathrm{src}[0]$ & from arg & from src or dtype range \\[3pt]
\op{Param} & from arg & from arg ($\mathrm{size}$) & from arg & from src or dtype range \\[3pt]
Movement ops & $\mathrm{src}[0].\mathrm{dtype}$ & (see op) & $\mathrm{src}[0].\mathrm{device}$ & $\mathrm{src}[0]$ \\
\op{Unshard} & $\mathrm{src}[0].\mathrm{dtype}$ & $\mathrm{src}[0]$, each $a_k \times n_k$ & $\mathrm{src}[0].\mathrm{device}$ & $\mathrm{src}[0]$ \\
\op{Reduce} & $\mathrm{src}[0].\mathrm{dtype}$ & remove first $n$ axes & $\mathrm{src}[0].\mathrm{device}$ & dtype range \\[3pt]
+10
View File
@@ -258,6 +258,11 @@ class TestDoubleDType(TestDType):
a = [2, 3, 4]
np.testing.assert_allclose(func(Tensor(a, dtype=self.DTYPE)).numpy(), func(torch.tensor(a, dtype=torch.float64)), rtol=1e-12, atol=1e-12)
def test_float32_compare_selecting_float64(self):
a = Tensor([1.0, 2.0, 5.0, 9.0], dtype=dtypes.float32)
p, q = Tensor([10., 20., 30., 40.], dtype=self.DTYPE), Tensor([50., 60., 70., 80.], dtype=self.DTYPE)
_test_op(lambda: (a < 3.0).where(p, q), self.DTYPE, [10., 20., 70., 80.])
def test_float64_to_float32_cast_inf(self):
_test_op(lambda: Tensor([3.4e40, 3.4e38, 1, 0], dtype=dtypes.float64).cast(dtypes.float32),
dtypes.float32, [float('inf'), 3.4e38, 1, 0])
@@ -423,6 +428,11 @@ class TestDtypeUsage(unittest.TestCase):
t = Tensor([[1, 2], [3, 4]], dtype=d)
(t*t).max().item()
def test_where_float16_compare_to_const(self):
# t > 0 is CMPLT(0, t): the float16 operand is on the right
t = Tensor([-1.0, 1.0], dtype=dtypes.float16)
np.testing.assert_equal((t > 0).where(Tensor.ones(2, dtype=dtypes.float16), Tensor.zeros(2, dtype=dtypes.float16)).numpy(), [0.0, 1.0])
@unittest.skipUnless(dtypes.bfloat16 in supported_dtypes, f"no bfloat16 on {Device.DEFAULT}")
class TestOpsBFloat16(unittest.TestCase):
def test_cast(self):
+2 -1
View File
@@ -6,7 +6,7 @@ from test.helpers import assert_jit_cache_len, call_is_graph, not_support_multi_
from test.unit.test_jit import _simple_test
from tinygrad import Tensor, Variable, TinyJit, Device, dtypes
from tinygrad.engine.jit import graph_class
from tinygrad.helpers import JIT, DEV, GlobalCounters
from tinygrad.helpers import JIT, DEV, GlobalCounters, HCQ2
from tinygrad.uop.ops import Ops
from tinygrad.renderer.isa.x86 import X86Renderer
@@ -235,6 +235,7 @@ class TestJitPrune(unittest.TestCase):
assert_jit_cache_len(w2_prune, 1)
class TestJitFree(unittest.TestCase):
@unittest.skipIf(HCQ2, "hcq2 keeps refs to intermediate buffers")
def test_free_intermediates(self):
ext_tensor = Tensor([1,24,23,45,1])
@TinyJit
+5 -1
View File
@@ -2810,7 +2810,7 @@ class TestOps(unittest.TestCase):
lambda x: Tensor.interpolate(x, size=out_sz, mode="linear"))
def test_interpolate_linear_corners_aligned(self):
for in_sz, out_sz in [((52,),(29,)), ((29,),(52,))]:
for in_sz, out_sz in [((52,),(29,)), ((29,),(52,)), ((29,),(1,))]:
helper_test_op([(2,3)+in_sz],
lambda x: torch.nn.functional.interpolate(x, size=out_sz, mode="linear", align_corners=True),
lambda x: Tensor.interpolate(x, size=out_sz, mode="linear", align_corners=True))
@@ -2963,6 +2963,10 @@ class TestOps(unittest.TestCase):
data = [math.inf, -math.inf, math.nan]
helper_test_op((), lambda: torch.tensor(data)[torch.tensor([0, 1, 2])], lambda: Tensor(data)[Tensor([0, 1, 2])])
def test_fancy_indexing_index_dtypes(self):
helper_test_op((), lambda: torch.tensor([10., 20., 30., 40.])[torch.tensor([1, 2, 3, 0])],
lambda: Tensor([10., 20., 30., 40.])[Tensor([1, 2, 3, 0], dtype=dtypes.uint8)])
@slow_test
def test_slice_fancy_indexing_no_dim_collapse(self):
a,b,c,d,e,i,j,k,o,p = self._get_index_randoms()
+20 -17
View File
@@ -379,27 +379,30 @@ class TestCopyFolding(unittest.TestCase):
check_schedule(a.clone(), 1, filter_sink=False)
def test_shrink_copy(self):
a = Tensor.arange(4)
view = a.shrink(((0, 2),))
b = view.clone()
run_linear(*check_schedule(b, 1, filter_sink=False))
self.assertEqual(b.uop.base.buffer.size, 2)
self.assertEqual(b.uop.numel(), 2)
self.assertListEqual(b.tolist(), [0, 1])
a = Tensor.arange(4).clone("CPU:1").realize()
b = a.to("CPU:2").shrink(((1, 3),)).to("CPU:3")
GlobalCounters.reset()
run_linear(*check_schedule(b, 3, filter_sink=False))
# extra E kernel, copy exactly 4 bytes
self.assertEqual(GlobalCounters.global_mem, 4*4 + 2*4*2 + 2*4)
self.assertListEqual(b.tolist(), [1, 2])
def test_expanded_copy(self):
a = Tensor.arange(2)
view = a.reshape(2, 1).expand(2, 2)
b = view.clone()
run_linear(*check_schedule(b, 1, filter_sink=False))
self.assertEqual(b.uop.base.buffer.size, 4)
self.assertEqual(b.uop.numel(), 4)
self.assertListEqual(b.tolist(), [[0, 0], [1, 1]])
a = Tensor.arange(4).clone("CPU:1").realize()
b = a.to("CPU:2").reshape(4, 1).expand(4, 2).to("CPU:3")
GlobalCounters.reset()
run_linear(*check_schedule(b, 3, filter_sink=False))
# TODO: expands before copy
self.assertEqual(GlobalCounters.global_mem, 4*4 + (4*4 + 8*4) + 8*4)
self.assertListEqual(b.tolist(), [[0, 0], [1, 1], [2, 2], [3, 3]])
def test_permuted_copy(self):
a = Tensor.arange(4)
b = a.reshape(2, 2).permute(1, 0)
b.realize()
a = Tensor.arange(4).clone("CPU:1").realize()
b = a.to("CPU:2").reshape(2, 2).permute(1, 0).to("CPU:3")
GlobalCounters.reset()
run_linear(*check_schedule(b, 3, filter_sink=False))
# permutes before copy
self.assertEqual(GlobalCounters.global_mem, 4*4 + (4*4 + 4*4) + 4*4)
self.assertListEqual(b.tolist(), [[0, 2], [1, 3]])
def test_permute_on_disk(self):
+2 -2
View File
@@ -3,10 +3,10 @@ from unittest.mock import patch
from tinygrad import Device, Tensor
from tinygrad.device import Buffer
from tinygrad.dtype import dtypes
from tinygrad.helpers import getenv
from tinygrad.helpers import HCQ2
from tinygrad.runtime.support.hcq2 import HCQ_DEVS, all_devices_in
@unittest.skipUnless(getenv("HCQ2") and all_devices_in(Device.DEFAULT, HCQ_DEVS), "hcq2 device required")
@unittest.skipUnless(HCQ2 and all_devices_in(Device.DEFAULT, HCQ_DEVS), "hcq2 device required")
class TestHCQ2(unittest.TestCase):
def test_copy_without_copy_queue(self):
with patch.object(Device[Device.DEFAULT], "has_copy_queue", False):
+2 -1
View File
@@ -441,7 +441,8 @@ class TestMovementOps(unittest.TestCase):
self.assertEqual(result.op, Ops.INDEX)
self.assertIs(result.src[0], src)
self.assertEqual(result.shape, (4,))
self.assertNotIn(Ops.RESHAPE, [u.op for u in result.toposort()])
# the only RESHAPE is src itself: the view of the flat param, the extra reshape was folded into the INDEX
self.assertEqual([u for u in result.toposort() if u.op is Ops.RESHAPE], [src])
def test_pm_mops_partial_reshape_index_suffix_mismatch_does_nothing(self):
from tinygrad.schedule.prepare import pm_mops
+1 -2
View File
@@ -84,11 +84,10 @@ class TestLowerIndexDtype(unittest.TestCase):
def test_reg_buffer_size_lowers(self):
reg = UOp.placeholder((4,), dtypes.float, 0, addrspace=AddrSpace.REG)
self.assertEqual(reg.src[0].dtype, dtypes.weakint)
self.assertEqual(reg.arg.size, 4)
lowered = graph_rewrite(reg.sink(), pm_lower_weak)
self.assertTrue(all(u.op is Ops.CONST for u in lowered.backward_slice_with_self if u.dtype in dtypes.weaks),
"lowering must resolve every weak width, except a typed literal's value half")
self.assertEqual(next(u for u in lowered.backward_slice_with_self if u.op is Ops.BUFFER).src[0].dtype, dtypes.int)
class TestSafeCast(unittest.TestCase):
def test_cast_folds(self):
+1 -1
View File
@@ -244,7 +244,7 @@ class TestViz(unittest.TestCase):
self.assertEqual(len(lst), 1)
graphs = [x["graph"] for x in viz.get_details(0, 0)]
# const is always in the graph, client side hides exclude=True nodes by default
self.assertEqual(list(graphs[0]), [id(a.src[0]), id(a), id(z), id(alu), id(y), id(sink)])
self.assertEqual(list(graphs[0]), [id(a), id(z), id(alu), id(y), id(sink)])
self.assertTrue(graphs[0][id(z)]["exclude"])
self.assertTrue(graphs[0][id(y)]["exclude"])
self.assertFalse(graphs[0][id(alu)]["exclude"])
+5
View File
@@ -52,6 +52,11 @@ class TestCall(unittest.TestCase):
np.testing.assert_allclose(a.grad.numpy(), gt_a_grad, rtol=1e-5)
np.testing.assert_allclose(b.grad.numpy(), gt_b_grad, rtol=1e-5)
def test_call_scalar_param_shape_mismatch(self):
scalar_fxn = UOp.param(0, dtypes.float, ()) * 2
with self.assertRaisesRegex(TypeError, "shape mismatch: expected scalar"):
Tensor.call(Tensor.ones(2), fxn=scalar_fxn).realize()
def test_call_gemm(self):
M, K, N = 4, 8, 4
a = Tensor.randn(M, K)
+9 -1
View File
@@ -391,7 +391,15 @@ def full_rewrite_to_sink(ast:UOp, ren:Renderer, optimize:bool=True) -> UOp:
sink = graph_rewrite(sink, pm_number_params, ctx=[num_params], name="number params with -1", walk=True)
if VIZ: graph_rewrite(sink, PatternMatcher([]), name="View Output AST")
if SPEC: type_verify(sink, spec_program)
if SPEC:
import os
if os.environ.get("DBGTV"):
try: type_verify(sink, spec_program)
except RuntimeError:
from tinygrad.uop.render import print_uops
print_uops(list(sink.toposort()))
raise
else: type_verify(sink, spec_program)
# return the rewritten sink
return sink
+2 -2
View File
@@ -140,8 +140,8 @@ def f2f_store(st, idx, val, fr:DType, to:DType):
pm_long_decomp: PatternMatcher = PatternMatcher([
# the decomp's own bottom-up rewrite can mint bare consts mid-flight: word splitting commits them at the long sibling's dtype
(UPat(GroupOp.All, name='x'), lambda x: commit_weak_consts(x, next((s.dtype for s in x.src if s.dtype in l2i_dt), None))),
(UPat(GroupOp.Defines, tuple(l2i_dt.keys()), src=(UPat.var("sz"),), name="x"), lambda x,sz:
UOp(x.op, src=(sz*2,), arg=replace(x.arg, dtype=l2i_dt[x.dtype]), tag=x.tag)),
(UPat(GroupOp.Defines, tuple(l2i_dt.keys()), name="x"), lambda x:
UOp(x.op, arg=replace(x.arg, dtype=l2i_dt[x.dtype], size=None if x.arg.size is None else x.arg.size*2), tag=x.tag)),
(UPat(Ops.INDEX, tuple(l2i_dt.keys()), name='x'), lambda x:
reindex(x, x.tag[0]).replace(tag=None) if x.tag is not None else None),
(UPat(Ops.STORE, src=(UPat.var('idx', tuple(l2i_dt.keys())), UPat.var('val')), name='st'), lambda st,idx,val:
+1 -1
View File
@@ -73,7 +73,7 @@ def add_gpudims(ctx:Renderer, s:UOp):
subs = {}
for r in s_topo:
# look for local INDEXes that are not used in the GLOBAL store, then add them as an INVALID
if r.op is Ops.STORE and (idx := r.src[0]).src[0].addrspace == AddrSpace.GLOBAL:
if r.op is Ops.STORE and len((idx := r.src[0]).src) and idx.src[0].addrspace == AddrSpace.GLOBAL:
missing_locals = [all_ranges[rng] for rng in local_dims if all_ranges[rng] not in idx.ranges]
if len(missing_locals):
assert len(idx.src) == 2, "index has 2 sources"
+4 -2
View File
@@ -1,7 +1,8 @@
import itertools, functools
from collections import defaultdict
from dataclasses import replace
from tinygrad.dtype import dtypes, AddrSpace, Invalid, DType
from tinygrad.uop.ops import UOp, Ops, PatternMatcher, UPat, GroupOp, shape_to_shape_arg, graph_rewrite
from tinygrad.uop.ops import UOp, Ops, PatternMatcher, UPat, GroupOp, graph_rewrite
from tinygrad.uop.symbolic import uop_given_valid, parse_valid, invalid_gate, sym
from tinygrad.helpers import getenv, IMAGE, OSX, ceildiv, is_image_shape
from tinygrad.renderer import Renderer
@@ -85,7 +86,8 @@ def transform_to_image(ctx, buf:UOp, x:UOp) -> UOp|None:
if len(cands) == 0: return None
# and tiebreak with indexing complexity (ie. number of nodes)
h, w, cidx = cands[0] if len(cands) == 1 else min(cands, key=lambda cand: len(cand[2].index(1).simplify().backward_slice))
buf = buf.replace(src=(shape_to_shape_arg((h, w, 4)),))
# the image dims are stored in the param's arg, the size stays the flat buffer len
buf = buf.replace(arg=replace(buf.arg, image=(h, w)))
shapes[buf.arg.slot] = (h, w)
if valid.op is not Ops.CONST or valid.val is not True:
return buf.index(cidx.src[1].valid(valid), cidx.src[0].valid(valid))
+1 -1
View File
@@ -67,7 +67,7 @@ def jit_lower(linear:UOp, held_bufs:set[UOp], input_uops:list[UOp]) -> UOp:
if VIZ: graph_rewrite(linear, PatternMatcher([]), name="View captured linear")
# parametrize input buffers: map each input buffer UOp to a PARAM with the correct slot index
linear = linear.substitute({u: UOp.param(i, u.dtype, u.shape, u.device) for i,u in enumerate(input_uops)}, walk=True)
linear = linear.substitute({u: UOp.param(i, u.dtype, u.max_numel(), u.device) for i,u in enumerate(input_uops)}, walk=True)
linear = memory_plan_rewrite(linear, held_bufs)
linear = compile_linear(linear, beam=getenv("JITBEAM", BEAM.value))
if JIT < 2: linear = graph_split_rewrite(linear, max_batch_size=JIT_BATCH_SIZE.value)
+5 -5
View File
@@ -2,8 +2,8 @@ from __future__ import annotations
from typing import cast, Iterator, Any, Sequence
import random, itertools, math, weakref, array, decimal
from dataclasses import dataclass, replace, field
from tinygrad.helpers import colored, DEBUG, GlobalCounters, ansipad, all_int, prod, flatten, Context, getenv, to_tuple, tqdm, dedup
from tinygrad.helpers import BEAM, size_to_str, time_to_str, VALIDATE_WITH_CPU, PROFILE, ProfilePointEvent, cpu_events, perf_counter_us
from tinygrad.helpers import colored, DEBUG, GlobalCounters, ansipad, all_int, prod, flatten, Context, to_tuple, tqdm, dedup
from tinygrad.helpers import BEAM, size_to_str, time_to_str, VALIDATE_WITH_CPU, HCQ2, PROFILE, ProfilePointEvent, cpu_events, perf_counter_us
from tinygrad.uop.ops import Ops, PatternMatcher, UOp, UPat, AxisType, sym_infer, graph_rewrite, ProgramInfo
from tinygrad.device import Device, Buffer, MultiBuffer, ProfileGraphEntry
from tinygrad.dtype import dtypes
@@ -305,17 +305,17 @@ pm_exec = PatternMatcher([
(UPat(Ops.CALL, src=(UPat(Ops.CUSTOM_FUNCTION, arg="validate", name="ast"),), name="call", allow_any_len=True), exec_validate),
])
if getenv("HCQ2"): from tinygrad.runtime.support.hcq2 import hcq_compile, hcq_link, HCQ_RUNTIME_DEV # noqa: E402 # down here, hcq2 imports realize
from tinygrad.runtime.support.hcq2 import hcq_compile, hcq_link, HCQ_RUNTIME_DEV # noqa: E402 # down here, hcq2 imports realize
def compile_linear(linear:UOp, beam:int|None=None, validate=False, input_uops:list[UOp]|None=None, profile:bool|None=None) -> UOp:
if validate: linear = graph_rewrite(linear, pm_validate, name="validate", walk=True)
if (beam_val:=BEAM.value if beam is None else beam) >= 1: linear = graph_rewrite(linear, pm_beam, ctx=beam_val, walk=True)
linear = lower_and_compile(linear)
linear = graph_rewrite(linear, pm_optimize_local_size, name="optimize local size", walk=True)
if getenv("HCQ2"): linear = hcq_compile(linear, input_uops, bool(PROFILE or DEBUG >= 2) if profile is None else profile)
if HCQ2: linear = hcq_compile(linear, input_uops, bool(PROFILE or DEBUG >= 2) if profile is None else profile)
return linear
def link_linear(linear:UOp, cache=True) -> UOp: return hcq_link(linear, cache=cache) if getenv("HCQ2") else linear
def link_linear(linear:UOp, cache=True) -> UOp: return hcq_link(linear, cache=cache) if HCQ2 else linear
def run_linear(linear:UOp, var_vals:dict[str, int]|None=None, input_uops:Sequence[UOp]=(), update_stats=True, jit=False, wait=False):
inputs = list(input_uops)
+1 -1
View File
@@ -240,7 +240,7 @@ TRANSCENDENTAL, NOLOCALS = ContextVar("TRANSCENDENTAL", 1), ContextVar("NOLOCALS
SPLIT_REDUCEOP, NO_MEMORY_PLANNER, LRU = ContextVar("SPLIT_REDUCEOP", 1), ContextVar("NO_MEMORY_PLANNER", 0), ContextVar("LRU", 1)
RING, ALL2ALL, ALLREDUCE_CAST = ContextVar("RING", 1), ContextVar("ALL2ALL", 0), ContextVar("ALLREDUCE_CAST", 1)
CACHELEVEL, IGNORE_BEAM_CACHE = ContextVar("CACHELEVEL", 2), ContextVar("IGNORE_BEAM_CACHE", 0)
VALIDATE_WITH_CPU = ContextVar("VALIDATE_WITH_CPU", 0)
VALIDATE_WITH_CPU, HCQ2 = ContextVar("VALIDATE_WITH_CPU", 0), ContextVar("HCQ2", 0)
# TODO: this is broken for some indexing
DISABLE_FAST_IDIV = ContextVar("DISABLE_FAST_IDIV", 1)
FUSE_OPTIM = ContextVar("FUSE_OPTIM", 0)
+5 -1
View File
@@ -31,6 +31,10 @@ def call_gradient(ctx:UOp, k:UOp, needed:set[int]) -> tuple[UOp|None, ...]:
return (None,) + k.arg.grad_fxn(on_dev(ctx, 0), k)
assert fxn.op is Ops.TUPLE, f"expected TUPLE body for gradient, got {fxn.op}"
params = {x.arg.slot:x for x in fxn.toposort(enter_calls=False) if x.op == Ops.PARAM}
# grads are collected at the flat param storage: reshape to each arg's view (max view shrunk to symbolic)
def shaped_grad(grad:UOp, i:int) -> UOp:
a = args[i]
return grad.view_as(a.shard_shape, a.axis) if a.axis is not None and isinstance(a.device, tuple) else grad.view_as(a._shape)
grad_args = ctx.src
root_grad = UOp(Ops.TUPLE, src=tuple(UOp(Ops.NOOP) if g.op is Ops.NOOP else
g if g.device is None else g.param_like(len(args)+i) for i,g in enumerate(grad_args)))
@@ -39,7 +43,7 @@ def call_gradient(ctx:UOp, k:UOp, needed:set[int]) -> tuple[UOp|None, ...]:
fwd_subs = {src: src.param_like(len(args)+len(grad_args)+i) for i, src in enumerate(fxn.src)} if k.arg.precompile else {}
fwd_outs = tuple(k.gettuple(i) for i in range(len(fxn.src))) if k.arg.precompile else ()
# collect needed gradient bodies, compact unused params, create a single backward CALL
grad_bodies = [(i, grads[p]) for i in needed if (p:=params.get(i)) is not None and p in grads]
grad_bodies = [(i, shaped_grad(grads[p], i)) for i in needed if (p:=params.get(i)) is not None and p in grads]
bwd_body = UOp.maketuple(*(gb for _, gb in grad_bodies)).substitute(fwd_subs, walk=True)
bwd_body = renumber_invalid_outputs(bwd_body)
bwd_body, compact_args = _compact_params(bwd_body, (*args, *grad_args, *fwd_outs))
+1 -1
View File
@@ -1067,7 +1067,7 @@ class OpMixin(ElementwiseMixin, ReduceMixin):
reshape[i] = expand[i] = size[i]
if mode == "linear":
arr = type(self).arange(size[i])
num, den = (arr*(in_sz-1), size[i]-1) if align_corners else ((arr*2+1)*in_sz - size[i], size[i]*2)
num, den = (arr*(in_sz-1), max(size[i]-1, 1)) if align_corners else ((arr*2+1)*in_sz - size[i], size[i]*2)
num = num.clip(0, (in_sz-1)*den)
low, high, perc = [y.reshape(reshape).expand(expand) for y in (num//den, (num+den-1)//den, (num % den).cast(dtypes.float32)/den)]
x = x.gather(i, low).lerp(x.gather(i, high), perc)
+6 -6
View File
@@ -4,7 +4,7 @@ import sys, struct, functools
from typing import cast
from tinygrad.dtype import dtypes, DType, truncate, AddrSpace
from tinygrad.uop import FastEnum, auto, Ops, GroupOp
from tinygrad.uop.ops import UOp, UPat, PatternMatcher
from tinygrad.uop.ops import UOp, UPat, PatternMatcher, promo_dtype
from tinygrad.renderer.isa import ISARenderer, IselContext, Register, PreRegAllocContext, greg
from tinygrad.helpers import getenv, NUM_CPU_THREADS, unwrap, Target
@@ -145,14 +145,14 @@ extra_matcher = PatternMatcher([
# float16 alus are done in float32
(UPat(GroupOp.ALU, dtypes.float16, name="x"), lambda x: UOp(x.op,
src=tuple(s.cast(dtypes.float) if s.dtype != dtypes.bool else s for s in x.src)).cast(x.dtype)),
(UPat(GroupOp.Comparison, src=(UPat.var("a", dtypes.float16), UPat.var("b")), name="x"),
lambda x,a,b: UOp(x.op, src=(a.cast(dtypes.float32), b.cast(dtypes.float32))).cast(x.dtype)),
(UPat(GroupOp.Comparison, src=[UPat(dtype=dtypes.float16), UPat()], name="x"),
lambda x: UOp(x.op, src=tuple(s.cast(dtypes.float32) for s in x.src)).cast(x.dtype)),
# no cmpne for packed ints, y != x => !(y==x)
(UPat(Ops.CMPNE, src=(UPat.var("y", dtypes.ints), UPat.var("x")), name="cmp"),
lambda y,x,cmp: UOp(Ops.CMPEQ, src=(y,x))^True if y.max_numel() > 1 else None),
# float WHERE needs a mask unless its comparison already has a float operand
# a float WHERE blends at the width of its value, so it needs a comparison at that width to make the mask
(UPat.var("m", dtypes.bool).where(UPat.var("a", dtypes.floats+(dtypes.weakfloat,)), UPat.var("b")).named("w"),
lambda m,a,b,w: m.cast(w.dtype).ne(0).where(a, b) if w.dtype in dtypes.floats and not dtypes.is_float(m.src[0].dtype) else None),
lambda m,a,b,w: m.cast(w.dtype).ne(0).where(a, b) if w.dtype in dtypes.floats and promo_dtype(m.src) is not w.dtype else None),
# rewrite -x -> 0 - x
(UPat(Ops.NEG, name="x"), lambda x: UOp(Ops.SUB, src=(x.const_like(0),) + x.src)),
# TODO: add support for mod, requires support for accessing the 2nd+ reg of a multi output instruction
@@ -282,7 +282,7 @@ def shift(x:UOp, op:X86Ops) -> UOp:
# it is materialized as an immediate so the address stays correct if the base register is ever spilled and refilled
def fold_address(x:UOp) -> tuple[UOp, UOp, UOp, UOp]:
def _disp(v:int) -> UOp: return imm(dtypes.int32 if abs(v) > dtypes.int8.max else dtypes.int8, v)
def _cast(v:UOp) -> UOp: return v.cast(dtypes.int64) if v.vmin < 0 else v
def _cast(v:UOp) -> UOp: return v.cast(dtypes.int64) if v.vmin < 0 else v.cast(dtypes.uint32) if v.dtype.itemsize < 4 else v
if x.op not in {Ops.INDEX, Ops.SHRINK}: return (x, UOp(Ops.NOOP), _disp(0), imm(dtypes.uint8, x.dtype.itemsize))
base, idx = x.src[0], x.src[1]
# buffers are indexed by element, everything else (the stack pointer) by byte
+2 -2
View File
@@ -8,7 +8,7 @@ from tinygrad.runtime.support.hcq import MMIOInterface, BumpAllocator, hcq_filte
from tinygrad.uop.ops import sint
from tinygrad.device import Compiled, BufferSpec, TinyELF
from tinygrad.helpers import getenv, round_up, data64_le, DEBUG, PROFILE, ProfileEvent, lo32, hi32, colored, prod, ContextVar, TracingKey
from tinygrad.helpers import VIZ, ceildiv, unwrap, pluralize
from tinygrad.helpers import VIZ, HCQ2, ceildiv, unwrap, pluralize
from tinygrad.renderer.cstyle import HIPRenderer, HIPCCRenderer
from tinygrad.renderer.llvmir import AMDLLVMRenderer
from tinygrad.runtime.autogen import kfd, hsa, sqtt, amdgpu_kd, amdgpu_drm
@@ -1153,4 +1153,4 @@ class AMDDevice(HCQCompiled):
def hw_copy_queues(self): return [(f"SDMA:{i}", functools.partial(unwrap(self.hw_copy_queue_t), queue_idx=i)) for i in self.sdma_queues]
if getenv("HCQ2"): from extra.hcq2.ops_amd2 import * # noqa: F401, F403 # pylint: disable=unused-import
if HCQ2: from extra.hcq2.ops_amd2 import * # noqa: F401, F403 # pylint: disable=unused-import
+3 -11
View File
@@ -1,24 +1,16 @@
from __future__ import annotations
from typing import cast, Callable, Type, TypeVar, Generic, Any
import contextlib, decimal, statistics, time, ctypes, array, os, struct, collections, itertools
import contextlib, decimal, statistics, time, ctypes, array, os, collections, itertools
try: import fcntl # windows misses that
except ImportError: fcntl = None #type:ignore[assignment]
from tinygrad.helpers import DEV, PROFILE, getenv, to_mv, from_mv, cpu_profile, ProfileRangeEvent, unwrap
from tinygrad.helpers import DEV, PROFILE, getenv, from_mv, cpu_profile, ProfileRangeEvent, unwrap
from tinygrad.helpers import suppress_finalizing, pluralize, TracingKey
from tinygrad.device import Device, BufferSpec, Compiled, LRUAllocator, ProfileDeviceEvent, ProfileProgramEvent, Program, TinyELF
from tinygrad.uop.ops import sym_infer, sint, UOp
from tinygrad.runtime.autogen import libc
from tinygrad.runtime.support.memory import BumpAllocator
from tinygrad.runtime.support.memory import BumpAllocator, MMIOInterface
from tinygrad.renderer import Renderer
class MMIOInterface:
def __init__(self, addr:int, nbytes:int, fmt='B'): self.mv, self.addr, self.nbytes, self.fmt = to_mv(addr, nbytes).cast(fmt), addr, nbytes, fmt
def __len__(self): return self.nbytes // struct.calcsize(self.fmt)
def __getitem__(self, k): return (self.mv[k] if self.fmt == 'B' else self.mv[k].tolist()) if isinstance(k, slice) else self.mv[k]
def __setitem__(self, k, v): self.mv[k] = v
def view(self, offset:int=0, size:int|None=None, fmt=None) -> MMIOInterface:
return MMIOInterface(self.addr+offset, (self.nbytes - offset) if size is None else size, fmt=fmt or self.fmt)
class FileIOInterface:
"""
Hardware Abstraction Layer for HCQ devices. The class provides a unified interface for interacting with hardware devices.
+6 -6
View File
@@ -1,5 +1,5 @@
from __future__ import annotations
from typing import cast, TypeVar, Generic, Any, Sequence, Iterable
from typing import cast, TypeVar, Generic, Any, Sequence, Iterable, TYPE_CHECKING
import struct, functools, time, collections, itertools, decimal, statistics
from dataclasses import replace, dataclass, field
from tinygrad.helpers import suppress_finalizing, dedup, pluralize, JIT_BATCH_SIZE, unwrap, PROFILE
@@ -9,11 +9,11 @@ from tinygrad.device import ProfileDeviceEvent, ProfileGraphEntry, ProfileGraphE
from tinygrad.uop.ops import Ops, sint, UOp, UPat, PatternMatcher, KernelInfo, graph_rewrite, rewrite_group, GroupOp
from tinygrad.uop.symbolic import symbolic
from tinygrad.dtype import dtypes, truncate, DType
from tinygrad.runtime.support.hcq import MMIOInterface, HCQBuffer
from tinygrad.runtime.support.memory import BumpAllocator
from tinygrad.runtime.support.memory import BumpAllocator, MMIOInterface
from tinygrad.renderer import Renderer, Estimates
from tinygrad.engine.realize import to_program, get_call_arg_uops, get_call_name, get_call_outs_ins, estimate_uop
from tinygrad.engine.realize import pm_flatten_linear, lower_and_compile
from tinygrad.engine.realize import to_program, get_call_arg_uops, get_call_name, get_call_outs_ins, estimate_uop, pm_flatten_linear,lower_and_compile
if TYPE_CHECKING: from tinygrad.runtime.support.hcq import HCQBuffer # TODO: remove that
# *****************
# 0. helpers
@@ -462,7 +462,7 @@ def hcq_lower(linear:UOp, pm_encode:PatternMatcher) -> UOp:
linear = graph_rewrite(linear, pm_split_patches, walk=True, name="split patches")
# and compile it
return lower_and_compile(graph_rewrite(linear, pm_replace_params, walk=True, name="replace params"))
with Context(EMULATED_DTYPES=""): return lower_and_compile(graph_rewrite(linear, pm_replace_params, walk=True, name="replace params"))
@rewrite_group(lambda linear,input_uops,profile,ret: f"HCQ Compile {pluralize('Kernel', len(ret.src))}")
def hcq_compile(linear:UOp, input_uops:list[UOp]|None, profile:bool) -> UOp:
+11 -2
View File
@@ -1,6 +1,15 @@
import collections, functools, dataclasses, enum
from __future__ import annotations
import collections, functools, dataclasses, enum, struct
from typing import Any, ClassVar
from tinygrad.helpers import round_up, getenv
from tinygrad.helpers import round_up, getenv, to_mv
class MMIOInterface:
def __init__(self, addr:int, nbytes:int, fmt='B'): self.mv, self.addr, self.nbytes, self.fmt = to_mv(addr, nbytes).cast(fmt), addr, nbytes, fmt
def __len__(self): return self.nbytes // struct.calcsize(self.fmt)
def __getitem__(self, k): return (self.mv[k] if self.fmt == 'B' else self.mv[k].tolist()) if isinstance(k, slice) else self.mv[k]
def __setitem__(self, k, v): self.mv[k] = v
def view(self, offset:int=0, size:int|None=None, fmt=None) -> MMIOInterface:
return MMIOInterface(self.addr+offset, (self.nbytes - offset) if size is None else size, fmt=fmt or self.fmt)
class BumpAllocator:
def __init__(self, size:int, base:int=0, wrap:bool=True): self.size, self.ptr, self.base, self.wrap = size, 0, base, wrap
+1 -1
View File
@@ -94,7 +94,7 @@ pm_post_sched_cache = PatternMatcher([
# only resolve buffer PARAMs (slot>=0); ALU/shape vars use slot=-1 and must not be swapped for call args
(UPat(Ops.PARAM, name="x"), lambda ctx,x: ctx[1][x.arg.slot] if x.arg.slot >= 0 else None),
# create new BUFFERs
(UPat(Ops.BUFFER, src=(UPat(),), name="b"), lambda ctx,b:
(UPat(Ops.BUFFER, src=(), name="b"), lambda ctx,b:
create_new_buffer(ctx, b) if isinstance(b.arg, ParamArg) and b.addrspace is AddrSpace.GLOBAL else None),
])
-5
View File
@@ -280,13 +280,8 @@ def rewrite_into_function(call:UOp):
for i, s in enumerate(new_body.src)])
return call.replace(src=(new_body,)+new_args)
def param_to_multi(p:UOp):
if p.axis is None: return None
return UOp.param(p.arg.slot, p.dtype, p.shard_shape, p.device, p.arg.vmin_vmax, p.arg.multiple_of, p.arg.name, p.arg.addrspace).unshard(p.axis)
# NOTE: this is the same pattern as unrolled ranges
multi_pm = PatternMatcher([
(UPat(Ops.PARAM, name="p"), param_to_multi),
(UPat(GroupOp.ALU, name="root", custom_early_reject=set([Ops.UNSHARD])), alu_multi),
(UPat(Ops.REDUCE, src=(UPat(Ops.UNSHARD, name="multi"), ), name="root"), reduce_multi),
(UPat(Ops.RESHAPE, src=(UPat(Ops.UNSHARD, name="multi"), UPat()), name="root"), reshape_multi),
+14 -5
View File
@@ -1,7 +1,7 @@
import itertools
from tinygrad.dtype import dtypes, to_dtype
from tinygrad.uop.ops import PatternMatcher, UPat, Ops, UOp, resolve, GroupOp
from tinygrad.uop.ops import graph_rewrite, rewrite_group, shape_to_shape_arg, ParamArg, identity_element
from tinygrad.uop.ops import graph_rewrite, rewrite_group, ParamArg, identity_element
from tinygrad.uop.movement import mop_cleanup
from tinygrad.helpers import prod, getenv, all_int, DEBUG, SPLIT_REDUCEOP, OPENPILOT_HACKS, FLOAT16, argsort
from tinygrad.schedule.indexing import apply_movement_op
@@ -100,10 +100,19 @@ def resolve_function(c:UOp, allow_param_mismatch=True) -> UOp|None:
if [x.arg.slot for x in params] != list(range(len(params))): raise RuntimeError(f"params not in order: {[x.arg.slot for x in params]}")
if len(params) != len(args): raise TypeError(f"expected {len(params)} args, got {len(args)}")
# params have a flat storage size in the arg, the logical shape is a view (RESHAPE/SHRINK/UNSHARD) on top of it.
# substitute args by their flat max-shaped storage view so the movement views on the params stay valid
def flat_storage(a:UOp) -> tuple[int, UOp]: # returns (size, view of a as flat max-shaped storage)
shp = a.max_shard_shape if a.axis is not None and isinstance(a.device, tuple) else a.max_shape
return (n:=prod(shp)), a if a.shape == (n,) else a.pad_to(shp).reshape((n,))
dict_map = {x:args[x.arg.slot] for x in params}
for i, (p, a) in enumerate(dict_map.items()):
if p.axis != a.axis: raise TypeError(f"arg {i} axis mismatch: expected {p.axis}, got {a.axis}")
if p.max_shape != a.max_shape: raise TypeError(f"arg {i} shape mismatch: expected {p.shape}, got {a.shape}")
if p.arg.size is not None:
n, flat = flat_storage(a)
if p.arg.size != n: raise TypeError(f"arg {i} shape mismatch: expected size {p.arg.size}, got {a.shape}")
dict_map[p] = flat
elif a.shape != ():
raise TypeError(f"arg {i} shape mismatch: expected scalar, got {a.shape}")
if p.dtype != a.dtype: raise TypeError(f"arg {i} dtype mismatch: expected {p.dtype}, got {a.dtype}")
return c.src[0].substitute(dict_map, walk=True)
@@ -192,9 +201,9 @@ def convert_copy_to_store(ctx, copy:UOp, existing_buf:UOp|None=None):
# if there's already a buffer, we just use it
return existing_buf.flatten().store(input_src)
# create the output buffer
buf = UOp(Ops.BUFFER, src=(shape_to_shape_arg(input_src.max_shape),), arg=ParamArg(next(ctx), copy.dtype, device=copy.device))
buf = UOp(Ops.BUFFER, arg=ParamArg(next(ctx), copy.dtype, size=prod(input_src.max_shape), device=copy.device))
# reshape back to input
return buf.after(buf.store(input_src)).reshape(copy.shape)
return buf.reshape(input_src.max_shape).after(buf.store(input_src)).reshape(copy.shape)
pm_copy_to_store = PatternMatcher([
(UPat(name="existing_buf").store(UPat(Ops.COPY, name="copy")), convert_copy_to_store),
+10 -4
View File
@@ -2,7 +2,7 @@ from dataclasses import dataclass, field, replace
from typing import cast
import itertools
from tinygrad.dtype import dtypes, AddrSpace, Invalid
from tinygrad.uop.ops import PatternMatcher, UPat, Ops, UOp, resolve, GroupOp, KernelInfo, ParamArg, shape_to_shape_arg
from tinygrad.uop.ops import PatternMatcher, UPat, Ops, UOp, resolve, GroupOp, KernelInfo, ParamArg
from tinygrad.uop.ops import graph_rewrite, sint, AxisType, BottomUpGate, rewrite_group
from tinygrad.uop.symbolic import symbolic
from tinygrad.helpers import prod, dedup, DEBUG_RANGEIFY, VIZ, MAX_KERNEL_BUFFERS, SPEC
@@ -162,6 +162,12 @@ pm_no_indexing_calls = PatternMatcher([
(UPat(Ops.CALL, name="u"), no_indexing_calls),
])
# the kernel graph is what gets executed: no shape views left in it, the storage of a value is just the storage
pm_no_views = PatternMatcher([
(UPat((Ops.RESHAPE, Ops.SHRINK), name="v", src=(UPat((Ops.AFTER, Ops.PARAM, Ops.UNSHARD, Ops.MSTACK, Ops.BUFFER)),), allow_any_len=True), lambda v:
v.src[0]),
])
DEVICE_MAX_BUFS = {"METAL": 31, "WEBGPU": 8, "CPU": 31} # TODO: get from device?
@dataclass
class LimitBufsContext:
@@ -223,7 +229,7 @@ def bufferize_to_store(ctx:itertools.count, x:UOp, idx:UOp, allow_locals=True):
# NOTE: the local BUFFER needs to be disambiguated here
if x.arg.addrspace == AddrSpace.GLOBAL:
buf = UOp(Ops.BUFFER, src=(shape_to_shape_arg((size,)),), arg=ParamArg(next(ctx), x.dtype, device=x.arg.device, addrspace=AddrSpace.GLOBAL))
buf = UOp(Ops.BUFFER, arg=ParamArg(next(ctx), x.dtype, size=size, device=x.arg.device, addrspace=AddrSpace.GLOBAL))
do_store = buf.index(idx).store(x.src[0]).end(*rngs)
return buf.after(do_store)
@@ -282,8 +288,7 @@ class LocalAddBufferContext:
def debuf(ctx:LocalAddBufferContext, buf:UOp):
# Variables (ALU buffers with a value range) are scalar symbolic values, not real buffers: they become ALU params with no slot
if buf.is_variable: return buf.replace(op=Ops.PARAM)
param = UOp(Ops.PARAM, src=(UOp.const(prod(buf.max_shape)),),
arg=ParamArg(ctx.dg, buf.dtype, addrspace=buf.addrspace, device=buf.device))
param = UOp(Ops.PARAM, arg=ParamArg(ctx.dg, buf.dtype, prod(buf.max_shape), addrspace=buf.addrspace, device=buf.device))
ret = param.reshape(buf.max_shape)
# if the buffer has symbolic shape, shrink the max-sized view to the actual shape
if buf.max_shape != buf.shape: ret = ret.shrink(tuple((0, s) for s in buf.shape))
@@ -383,6 +388,7 @@ def get_kernel_graph(tsink:UOp) -> UOp:
tsink = graph_rewrite(tsink, pm_add_buffers+pm_add_param_range_tags, ctx=itertools.count(paramarg_start), bottom_up=True, name="stage to store")
tsink = graph_rewrite(tsink, split_kernels, bottom_up=True, name="split kernels")
tsink = graph_rewrite(tsink, pm_no_indexing_calls, name="remove indexing from call args")
tsink = graph_rewrite(tsink, pm_no_views, name="remove views from the kernel graph")
if VIZ: graph_rewrite(tsink, PatternMatcher([]), name="View Kernel Graph")
if SPEC:
+3 -5
View File
@@ -198,9 +198,7 @@ def finalize_after(ctx:AllocCtx, x:UOp):
def replace_input_buffer(ctx:AllocCtx, b:UOp):
ctx.replacements.append(b)
if b.is_bound_var or b.is_variable: return b.param_like(len(ctx.replacements)-1)
return UOp.param(len(ctx.replacements)-1, b.dtype, b.shape, b.device,
addrspace=b.addrspace if b.addrspace is not None else AddrSpace.GLOBAL)
return b.param_like(len(ctx.replacements)-1)
def replace_input_view(ctx:AllocCtx, b:UOp): return replace_input_buffer(ctx, b) if b in ctx.views else None
@@ -211,7 +209,7 @@ pm_finalize_call = PatternMatcher([
pm_replace_buf = PatternMatcher([
# replace BUFFER with PARAM for cache key normalization
(UPat(Ops.BUFFER, src=(UPat(),), name="b"), lambda ctx,b:
(UPat(Ops.BUFFER, src=(), name="b"), lambda ctx,b:
replace_input_buffer(ctx, b) if isinstance(b.arg, ParamArg) and b.addrspace is AddrSpace.GLOBAL else None),
# replace SHRINK with PARAM
(UPat(Ops.SHRINK, src=(UPat(Ops.BUFFER),), name="b", allow_any_len=True), replace_input_view),
@@ -381,7 +379,7 @@ class Tensor(RandMixin):
# ***** data handlers ****
def as_param(self, slot:int):
return Tensor(UOp.param(slot, self.dtype, self.uop.shard_shape, self.device, axis=self.uop.axis))
return Tensor(self.uop.param_like(slot))
def call(self, *lst:Tensor, fxn:Tensor|UOp, grad_fxn:Callable|None=None) -> Tensor:
fret = fxn._uop.call(*[t.uop for t in (self,)+lst], grad_fxn=grad_fxn)
+39 -23
View File
@@ -23,17 +23,21 @@ class AxisType(Enum):
class ParamArg:
slot: int
dtype: DType
# number of elements in the buffer. always a concrete int (never symbolic), None for scalars (shape ())
size: int|None = None
vmin_vmax: tuple[PyConst, PyConst]|None = None
multiple_of: int|None = None
name: str|None = None
addrspace: AddrSpace|None = AddrSpace.GLOBAL
axis: int|None = None
device: str|tuple[str, ...]|None = None
volatile: bool = False
# (h, w) if this is an image2d buffer, then size == h*w*4
image: tuple[int, int]|None = None
def __repr__(self):
fields = (("vmin_vmax", None), ("multiple_of", None), ("name", None), ("addrspace", AddrSpace.GLOBAL), ("axis", None), ("device", None),
("volatile", False))
args = [repr(self.slot), repr(self.dtype)] + [f"{k}={v!r}" for k,default in fields if (v:=getattr(self, k)) != default]
fields = (("vmin_vmax", None), ("multiple_of", None), ("name", None), ("addrspace", AddrSpace.GLOBAL), ("device", None),
("volatile", False), ("image", None))
args = [repr(self.slot), repr(self.dtype)] + ([repr(self.size)] if self.size is not None else []) + \
[f"{k}={v!r}" for k,default in fields if (v:=getattr(self, k)) != default]
return f"ParamArg({', '.join(args)})"
axis_letters = {AxisType.DEVICE: "d", AxisType.GLOBAL: "g", AxisType.THREAD: "t", AxisType.LOCAL: "l", AxisType.WARP: "w", AxisType.WEAK: "L",
AxisType.LOOP: "L", AxisType.UPCAST: "u", AxisType.GROUP_REDUCE: "G", AxisType.REDUCE: "R", AxisType.UNROLL: "r"}
@@ -381,9 +385,10 @@ class UOp(RandMixin, metaclass=UOpMetaClass):
case Ops.GETADDR: return ()
case Ops.RANGE | Ops.SPECIAL: return ()
case Ops.BINARY: return (len(self.arg),)
case Ops.BUFFER:
if len(self.src): return self.src[0].as_shape
return ()
case Ops.BUFFER | Ops.PARAM:
# PARAM/BUFFER don't have a shape input, they have a size in the arg: int gives shape (size,), None gives ()
if (img:=self.arg.image) is not None: return (img[0], img[1], 4)
return () if self.arg.size is None else (self.arg.size,)
case Ops.CUSTOM | Ops.CUSTOMI:
if self.dtype is dtypes.void: return None
input_shapes = [x._shape for x in self.src if x._shape is not None]
@@ -394,10 +399,6 @@ class UOp(RandMixin, metaclass=UOpMetaClass):
# STAGE adds the existing shape to the front, opposite of INDEX
return tuple([int(r.vmax+1) for r in self.src[1:]])+self.src[0].shape
# param has shape as the only arg
case Ops.PARAM:
return self.src[0].as_shape
# wmma output shape = accumulator shape (src[2])
case Ops.WMMA:
wmma_b = _broadcast_shape(self.src[0].shape[:-1], self.src[1].shape[:-1], self.src[2].shape[:-1])
@@ -708,7 +709,7 @@ class UOp(RandMixin, metaclass=UOpMetaClass):
if self.op is Ops.GETTUPLE:
in_tuple = self.src[0].src[0] if self.src[0].op is Ops.FUNCTION else self.src[0]
return in_tuple.src[self.arg].axis if in_tuple.op is Ops.TUPLE else None
if self.op is Ops.PARAM: return self.arg.axis
if self.op is Ops.PARAM: return None
# NOTE: they all have to share an axis, we always choose [-1]. src axes are right-aligned into the output shape
if self.op in GroupOp.ALU.union({Ops.STACK}):
return axes[-1] if (axes := dedup([x.axis+len(self.shape)-len(x.shape) for x in self.src if x.axis is not None])) else None
@@ -821,8 +822,9 @@ class UOp(RandMixin, metaclass=UOpMetaClass):
@staticmethod
def new_buffer(device:str|tuple[str, ...], size:int, dtype:DType, num=None):
if dtype in dtypes.weaks: raise RuntimeError(f"cannot create storage for weak dtype {dtype}")
assert isinstance(size, int), f"new_buffer size must be a concrete int, got {size}"
slot = next(UOp.unique_num) if num is None else num
return UOp(Ops.BUFFER, src=(shape_to_shape_arg((size,)),), arg=ParamArg(slot, dtype, device=device))
return UOp(Ops.BUFFER, arg=ParamArg(slot, dtype, size=size, device=device))
@staticmethod
def from_buffer(opaque:Buffer, device:str|tuple[str, ...]|None=None):
if (uop:=UOp.new_buffer(device or opaque.device, opaque.size, opaque.dtype, num=-id(opaque))) not in buffers: buffers[uop] = opaque.ref(1)
@@ -978,7 +980,7 @@ class UOp(RandMixin, metaclass=UOpMetaClass):
# a Variable is a 0-d BUFFER in the ALU addrspace; binding it is storing a CONST into it
# param=True creates the kernel-side form directly: an ALU PARAM (what the BUFFER becomes inside kernels)
arg = ParamArg(-1, dtype, name=name, vmin_vmax=(min_val, max_val), multiple_of=multiple_of, addrspace=AddrSpace.ALU)
return UOp(Ops.PARAM if param else Ops.BUFFER, src=(shape_to_shape_arg(()),), arg=arg)
return UOp(Ops.PARAM if param else Ops.BUFFER, arg=arg)
@property
def is_variable(self) -> bool:
# a Variable is a 0-d BUFFER in the ALU addrspace that carries a value range (it becomes a PARAM inside kernels)
@@ -1148,11 +1150,11 @@ class UOp(RandMixin, metaclass=UOpMetaClass):
dtype = strong_dtype(dtype) # storage is never weak: a placeholder commits the width of what's put in it
if slot is None: slot = next(UOp.unique_num)
if addrspace is AddrSpace.GLOBAL:
ret = UOp(Ops.PARAM, src=(shape_to_shape_arg((prod(shape),)),), arg=ParamArg(slot, dtype, addrspace=addrspace, device=device,volatile=volatile))
ret = UOp(Ops.PARAM, arg=ParamArg(slot, dtype, size=prod(shape), addrspace=addrspace, device=device, volatile=volatile))
else:
assert addrspace in (AddrSpace.LOCAL, AddrSpace.REG)
assert device is None, "LOCAL and REG placeholders cannot have a device"
ret = UOp(Ops.BUFFER, src=(shape_to_shape_arg((prod(shape),)),), arg=ParamArg(slot, dtype, addrspace=addrspace))
ret = UOp(Ops.BUFFER, arg=ParamArg(slot, dtype, size=prod(shape), addrspace=addrspace))
if tag is not None: ret = ret.rtag(tag)
if len(shape) > 1: ret = ret.reshape(shape)
return ret
@@ -1167,20 +1169,34 @@ class UOp(RandMixin, metaclass=UOpMetaClass):
# TODO: this should replace placeholder
@staticmethod
def param(slot:int, dtype:DType, shape:tuple[sint, ...]|sint|None=None, device=None, vmin_vmax:tuple[PyConst, PyConst]|None=None,
multiple_of:int|None=None, name=None, addrspace=AddrSpace.GLOBAL, axis:int|None=None, volatile:bool=False):
multiple_of:int|None=None, name=None, addrspace=AddrSpace.GLOBAL, volatile:bool=False):
"""create a PARAM: a single sint or 1-d shape gives a flat param of that size, a None shape gives a scalar param.
the arg only stores the concrete max size (never symbolic): a multi-dim shape is a RESHAPE on top of the flat param,
a symbolic shape is a max-size param shrunk to the real shape"""
if dtype in dtypes.weaks: raise RuntimeError(f"cannot create param for weak dtype {dtype}")
if isinstance(shape, (int, UOp)): shape = (shape,)
if shape is not None and axis is not None and isinstance(device, tuple):
shape = tuple(s*len(device) if i == axis else s for i,s in enumerate(shape))
src: tuple[UOp, ...] = (UOp(Ops.NOOP) if shape is None else shape_to_shape_arg(shape),)
return UOp(Ops.PARAM, src=src, arg=ParamArg(slot, dtype, vmin_vmax, multiple_of, name, addrspace, axis, device, volatile))
if shape is None or len(shape) == 0:
return UOp(Ops.PARAM, arg=ParamArg(slot, dtype, None, vmin_vmax, multiple_of, name, addrspace, device, volatile))
max_shape = to_max_shape(shape)
ret = UOp(Ops.PARAM, arg=ParamArg(slot, dtype, prod(max_shape), vmin_vmax, multiple_of, name, addrspace, device, volatile))
return ret.view_as(shape)
def param_like(self, slot:int):
# Variables become ALU params in the call body; the stored value (if bound) stays in the call args
if self.is_bound_var or self.is_variable:
b = self.src[0] if self.op is Ops.AFTER else self
return UOp(Ops.PARAM, src=b.src, arg=replace(b.arg, slot=slot, name=f"p{slot}"))
return UOp(Ops.PARAM, arg=replace(b.arg, slot=slot, name=f"p{slot}"))
addrspace = self.addrspace if self.addrspace is not None else AddrSpace.GLOBAL
return UOp.param(slot, self.dtype, self.shard_shape if self.axis is not None else self._shape, self.device, addrspace=addrspace, axis=self.axis)
# multi-device values become a per-shard sized param wrapped in UNSHARD: the sharding lives in the graph, not the arg
if self.axis is not None and isinstance(self.device, tuple):
return UOp(Ops.PARAM, arg=ParamArg(slot, self.dtype, prod(to_max_shape(self.shard_shape)),
addrspace=addrspace, device=self.device)).view_as(self.shard_shape, self.axis)
return UOp.param(slot, self.dtype, self._shape, self.device, addrspace=addrspace)
def view_as(self:UOp, shape:tuple[sint, ...], axis:int|None=None) -> UOp:
"""view flat storage as the given (possibly symbolic) shape, optionally sharded on axis, the UNSHARD gives back the multiplied shape"""
max_shape = to_max_shape(shape)
ret = self.reshape(max_shape) if len(shape) > 1 else self
if tuple(max_shape) != tuple(shape): ret = ret.shrink_to(shape)
return ret if axis is None else ret.unshard(axis)
@staticmethod
def custom_function(name:str, *src:UOp) -> UOp: return UOp(Ops.CUSTOM_FUNCTION, src=src, arg=name)
+1 -1
View File
@@ -84,7 +84,7 @@ pm_pyrender_extra = PatternMatcher([
(UPat(Ops.CONST, src=(), name="x"), lambda x: f"UOp.const({x.val})"),
(UPat((Ops.CAST, Ops.BITCAST), name="x"), lambda ctx,x: f"{ctx[x.src[0]]}.{x.op.name.lower()}({x.dtype})" if x.dtype != x.src[0].dtype else None),
(UPat(Ops.SPECIAL, src=(UPat(Ops.CONST),), name="x"), lambda x: f"UOp.special({x.src[0].val}, {repr(x.arg)})"),
(UPat(Ops.BUFFER, src=(UPat(),), name="x"), lambda x:
(UPat(Ops.BUFFER, src=(), name="x"), lambda x:
f"UOp.new_buffer({repr(x.arg.device)}, {x.max_numel()}, {x.dtype}, {x.arg.slot})"
if isinstance(x.arg, ParamArg) and x.addrspace is AddrSpace.GLOBAL else None),
(UPat(Ops.COPY, src=(UPat(name="x"),), name="copy"), lambda ctx,x,copy: f"{ctx[x]}.copy_to_device({repr(copy.arg)})"),
+9 -11
View File
@@ -87,10 +87,9 @@ spec_shared = PatternMatcher([
# a loop-ended END requires a trailing bool condition for the backedge (loop again while true)
(UPat(Ops.END, src=(UPat(), UPat(Ops.RANGE, dtypes.void), UPat(dtype=dtypes.bool))), lambda: True),
# PARAM
(UPat(Ops.PARAM, name="x"), lambda x: isinstance(x.arg, ParamArg)),
(UPat(Ops.BUFFER, src=(UPat(),), name="x"), lambda x:
isinstance(x.arg, ParamArg) and x.addrspace in (AddrSpace.REG, AddrSpace.LOCAL)),
# PARAM/BUFFER have a size in the arg, no shape input
(UPat(Ops.PARAM, src=(), name="x"), lambda x: isinstance(x.arg, ParamArg)),
(UPat(Ops.BUFFER, src=(), name="x"), lambda x: isinstance(x.arg, ParamArg) and x.addrspace in (AddrSpace.REG, AddrSpace.LOCAL)),
# GROUP of stores (or groups, or NOOPs)
(UPat(Ops.GROUP, dtypes.void, src=UPat((Ops.GROUP, Ops.STORE, Ops.NOOP, Ops.INS, Ops.END))), lambda: True),
@@ -141,12 +140,12 @@ spec_tensor = PatternMatcher([
lambda u: dtypes.is_float(u.dtype) or u.src[0].base.is_invalid),
# BUFFER
(UPat(Ops.BUFFER, src=(UPat(),), name="buf"), lambda buf:
(isinstance(buf.dtype, DType) and matches_dtype(buf.src[0], dtypes.weakint) and is_device(buf.arg.device))
(UPat(Ops.BUFFER, src=(), name="buf"), lambda buf:
(isinstance(buf.dtype, DType) and isinstance(buf.arg.size, int) and is_device(buf.arg.device))
if isinstance(buf.arg, ParamArg) and buf.addrspace is AddrSpace.GLOBAL else None),
# a Variable is a 0-d ALU BUFFER with a value range and no device
(UPat(Ops.BUFFER, src=(UPat(),), name="buf"), lambda buf: buf.arg.device is None if buf.is_variable else None),
(UPat(Ops.BUFFER, src=(), name="buf"), lambda buf: buf.arg.device is None if buf.is_variable else None),
# custom function
(UPat(Ops.CUSTOM_FUNCTION, name="x"), lambda x: isinstance(x.arg, str)),
@@ -260,11 +259,10 @@ spec_kernel_graph = PatternMatcher([
(UPat(Ops.STACK, name="s"), lambda s: all(x.op in (Ops.CONST, Ops.PARAM) or x.is_variable or x.is_bound_var for x in s.src) or None),
# linear for more kernels (TODO: we should enter non sink calls)
#(UPat(Ops.LINEAR), lambda: True),
# param is outside buffer, buffer is local buffer
(UPat(Ops.PARAM, name="x"), lambda x: isinstance(x.arg, ParamArg)),
# param is outside buffer, buffer is local buffer. params have a size in the arg, no shape input
(UPat(Ops.PARAM, src=(), name="x"), lambda x: isinstance(x.arg, ParamArg)),
(UPat(Ops.BUFFER, name="x"), lambda x: isinstance(x.arg, ParamArg) and x.addrspace in (AddrSpace.GLOBAL, AddrSpace.ALU)),
# RESHAPE/BITCAST are NOOPs in the kernel graph (do we need them?)
(UPat((Ops.RESHAPE, Ops.BITCAST)), lambda: True),
(UPat(Ops.BITCAST), lambda: True),
# mstack/mselect
(UPat(Ops.MSTACK, name="x"), lambda x: all(isinstance(s.device, str) for s in x.src) or (all_same(x.src) and x.src[0].device is None)),
(UPat(Ops.MSELECT, name="x"), lambda x: isinstance(x.src[0].device, tuple) and x.arg < len(x.src[0].device)),