This is an automated email from the ASF dual-hosted git repository.
github-actions[bot] pushed a commit to branch asf-site
in repository https://gitbox.apache.org/repos/asf/tvm-site.git
The following commit(s) were added to refs/heads/asf-site by this push:
new eeab9ce358e Build at Mon Jun 22 03:55:58 UTC 2026
eeab9ce358e is described below
commit eeab9ce358e7b43fe1815fb46ec9ec2d08c602af
Author: tvm-bot <[email protected]>
AuthorDate: Mon Jun 22 03:55:59 2026 +0000
Build at Mon Jun 22 03:55:58 UTC 2026
---
2026/06/22/tirx.html | 17 +-
_static/downloads/acm%3Adesktopcta | 6 +-
assets/tirx-layout-demo/index.html | 167 -------
assets/tirx-layout-demo/layout-demo.js | 805 ---------------------------------
assets/tirx-layout-demo/viz-base.css | 113 -----
assets/tirx-layout-demo/viz-base.js | 72 ---
atom.xml | 19 +-
feed.xml | 19 +-
rss.xml | 21 +-
9 files changed, 19 insertions(+), 1220 deletions(-)
diff --git a/2026/06/22/tirx.html b/2026/06/22/tirx.html
index f4ba7797adf..dc39cf29872 100644
--- a/2026/06/22/tirx.html
+++ b/2026/06/22/tirx.html
@@ -262,20 +262,9 @@ L(i,j)_{(8,16)} &= L(i\cdot 16 + j) &&
\text{(flatten)} \\
<li>owners (×2 via replica): { warpid=6 laneid=12 }, { warpid=10 laneid=12
}</li>
</ul>
-<p><em>(Click element 57 in the interactive demo below to see exactly these
owners.)</em></p>
-
-<details>
-<summary>Unfold to see the interactive layout demo</summary>
-<iframe id="tirx-layout-demo"
src="/assets/tirx-layout-demo/index.html?preset=tensor-core&notitle&lock"
style="width:100%; height:560px; border:1px solid #dfe1e6; border-radius:10px;
margin:12px 0;" title="TIRx interactive layout demo: tensor-core tile"
loading="lazy"></iframe>
-<script>
-window.addEventListener('message', function (e) {
- var h = e.data && e.data.tirxLayoutDemoHeight;
- if (!h) return;
- var f = document.getElementById('tirx-layout-demo');
- if (f) f.style.height = h + 'px';
-});
-</script>
-</details>
+<p><em>(Open the interactive demo and click element 57 to see exactly these
owners.)</em></p>
+
+<p><a
href="https://mlc.ai/modern-gpu-programming-for-mlsys/_static/tirx-layout-demo/index.html?preset=tensor-core"
target="_blank" rel="noopener" style="display:inline-block; padding:10px 18px;
background:#3b82f6; color:#fff !important; font-weight:700; border-radius:8px;
text-decoration:none;">▶ Open the interactive layout demo ↗</a></p>
<p>TIRx’s layout interface is built around four design choices.</p>
diff --git a/_static/downloads/acm%3Adesktopcta
b/_static/downloads/acm%3Adesktopcta
index 217fd5be47f..9e7fc197211 100644
--- a/_static/downloads/acm%3Adesktopcta
+++ b/_static/downloads/acm%3Adesktopcta
@@ -64,12 +64,12 @@
<div class="cf-error-footer cf-wrapper w-240 lg:w-full py-10 sm:py-4
sm:px-8 mx-auto text-center sm:text-left border-solid border-0 border-t
border-gray-300">
<p class="text-13">
- <span class="cf-footer-item sm:block sm:mb-1">Cloudflare Ray ID: <strong
class="font-semibold">a0f849d9edef6173</strong></span>
+ <span class="cf-footer-item sm:block sm:mb-1">Cloudflare Ray ID: <strong
class="font-semibold">a0f8513d3c060568</strong></span>
<span class="cf-footer-separator sm:hidden">•</span>
<span id="cf-footer-item-ip" class="cf-footer-item hidden sm:block
sm:mb-1">
Your IP:
<button type="button" id="cf-footer-ip-reveal"
class="cf-footer-ip-reveal-btn">Click to reveal</button>
- <span class="hidden" id="cf-footer-ip">172.214.155.177</span>
+ <span class="hidden" id="cf-footer-ip">20.81.183.81</span>
<span class="cf-footer-separator sm:hidden">•</span>
</span>
<span class="cf-footer-item sm:block sm:mb-1"><span>Performance &
security by</span> <a rel="noopener noreferrer"
href="https://www.cloudflare.com/5xx-error-landing" id="brand_link"
target="_blank">Cloudflare</a></span>
@@ -86,5 +86,5 @@
</script>
-<script>(function(){function c(){var
b=a.contentDocument||a.contentWindow.document;if(b){var
d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a0f849d9edef6173',t:'MTc4MjEwMDIyMw=='};var
a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var
a=document.createElement('iframe');a.height=1;a.width=1;a.style.pos [...]
+<script>(function(){function c(){var
b=a.contentDocument||a.contentWindow.document;if(b){var
d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a0f8513d3c060568',t:'MTc4MjEwMDUyNQ=='};var
a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var
a=document.createElement('iframe');a.height=1;a.width=1;a.style.pos [...]
</html>
\ No newline at end of file
diff --git a/assets/tirx-layout-demo/index.html
b/assets/tirx-layout-demo/index.html
deleted file mode 100644
index 38d5c05ce20..00000000000
--- a/assets/tirx-layout-demo/index.html
+++ /dev/null
@@ -1,167 +0,0 @@
-<!DOCTYPE html>
-<!--
- 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.
-
- Page structure and interaction pattern adapted from the team's own course
- material (mlsyscourse/slides-modern-gpu-programming,
- data-layout/site/demo/tile_distributed.html). TIRx layout logic in
- layout-demo.js is original. Not derived from any third-party demo.
--->
-<html lang="en">
-<head>
-<meta charset="UTF-8">
-<meta name="viewport" content="width=device-width, initial-scale=1.0">
-<title>TIRx Tensor Layout — Interactive Demo</title>
-<link rel="stylesheet" href="viz-base.css">
-<script src="viz-base.js"></script>
-<style>
- /* embed mode (?notitle / ?lock) */
- body.lock .preset-row, body.lock .main-controls, body.lock #status {
display:none; }
- body.notitle, body.lock { padding:10px; }
- /* lock: lay panels at natural size; JS scales the whole block to fit the
width */
- body.lock #panels { display:flex; flex-direction:column;
align-items:flex-start; gap:20px; grid-template-columns:none;
width:max-content; max-width:none; margin:0; }
-
- /* Demo-specific overrides */
- .ti {
- padding:5px 9px; border:1px solid var(--border); border-radius:5px;
- font-family:'SF Mono','Fira Code',monospace; font-size:13px;
color:var(--text);
- background:var(--surface);
- }
- .ti.expr { min-width:440px; max-width:70vw; }
- .ti.shape { width:84px; }
- select.ti { font-family:inherit; cursor:pointer; }
-
- /* preset = secondary helper (just loads an example into the two fields) */
- .preset-row { display:flex; align-items:center; gap:8px;
justify-content:center;
- margin-bottom:8px; font-size:12px; }
- .preset-row .lbl { font-size:12px; }
- .preset-hint { color:var(--dim); font-size:12px; font-style:italic; }
-
- /* shape + layout = the first-class inputs */
- .main-controls { display:flex; gap:20px; justify-content:center;
align-items:flex-end;
- flex-wrap:wrap; padding:16px 20px; border:1px solid #c7d6f0;
border-radius:10px;
- background:#f6f9ff; max-width:960px; margin:0 auto 10px; }
- .field { display:flex; flex-direction:column; gap:5px; }
- .field.grow { flex:1; min-width:300px; }
- .flbl { font-size:13px; font-weight:700; color:var(--text); }
- .flbl-sub { font-weight:500; color:var(--dim); font-size:11px;
- font-family:'SF Mono','Fira Code',monospace; }
- .field .ti { font-size:14px; padding:8px 11px; }
- .field .ti.expr { width:100%; min-width:0; max-width:none; }
- .sw-group { display:flex; gap:6px; }
- .sw-group .ti { font-size:13px; padding:8px 8px; }
-
- #status { font-size:12px; font-family:'SF Mono','Fira Code',monospace;
color:var(--dim);
- text-align:center; margin-bottom:14px; }
-
- /* Stack the two panels vertically (logical on top, physical below) */
- .panels { grid-template-columns:1fr; max-width:1280px; align-items:start; }
- .grid { gap:2px; }
- .cell { aspect-ratio:1; font-size:21px; border-radius:5px; border-width:2px;
}
- .cell.dm { opacity:.18; }
-
- /* Right panel: physical-coordinate view (2D table or 1D wrapped tiles) */
- #phys { max-height:580px; overflow:auto; padding:2px; }
- .phys-table { display:grid; gap:3px; }
- .ax-hdr { font-size:18px; color:var(--dim); font-weight:600;
- font-family:'SF Mono','Fira Code',monospace; display:flex;
align-items:center; justify-content:center; }
- .ax-hdr.row { justify-content:flex-end; padding-right:4px;
white-space:nowrap; }
- .pcell { border:1px solid var(--border); border-radius:4px; min-height:30px;
padding:2px;
- display:flex; flex-wrap:wrap; gap:2px; align-content:flex-start;
justify-content:center; transition:all .15s; }
- .pcell.hov-cell { border-color:#222; box-shadow:0 0 7px rgba(59,130,246,.4);
}
- .pcell.dm-cell { opacity:.16; }
- /* bank mode: pack the elements of one bank word side by side (same size as
- the logical-tensor cells), not stacked */
- .phys-table.bank-mode .pcell { flex-wrap:nowrap; gap:2px; padding:2px; }
-
- .phys-1d { display:flex; flex-wrap:wrap; gap:8px; }
- .thread-tile { border:2px solid var(--border); border-radius:8px;
padding:5px 6px 6px;
- background:var(--bg); min-width:54px; transition:all .15s; }
- .thread-tile.hov-tile { box-shadow:0 0 8px rgba(59,130,246,.35);
border-color:#222; }
- .thread-tile.dm-tile { opacity:.2; }
- .thread-lbl { font-size:18px; font-weight:700; color:var(--dim);
- font-family:'SF Mono','Fira Code',monospace; text-align:center;
margin-bottom:4px; white-space:nowrap; }
- .thread-slots { display:flex; flex-wrap:wrap; gap:2px;
justify-content:center; }
-
- .gcell {
- border-radius:3px; display:flex; align-items:center;
justify-content:center;
- font-size:21px; font-weight:700; width:46px; height:46px; flex:0 0 auto;
cursor:pointer;
- border:2px solid transparent; transition:all .15s;
- }
- .gcell.hov { border-color:#222; box-shadow:0 0 6px rgba(59,130,246,.45); }
- .gcell.dm { opacity:.18; }
-
- .formula-bar { max-width:1280px; }
- /* Larger labels/headers/legend for embedded (scaled) readability */
- .panel h2 { font-size:22px; }
- .panel .nota { font-size:19px; }
- .hdr { font-size:18px; }
- .rl { font-size:18px; }
- .li { font-size:14px; }
-</style>
-</head>
-<body>
-
-<h1>TIRx Tensor Layout</h1>
-<div class="sub">how a <code>TileLayout</code> (shard / replica / offset) maps
logical tensor elements to physical threads</div>
-
-<div class="preset-row">
- <span class="lbl">load an example</span>
- <select id="preset" class="ti"></select>
- <span class="preset-hint">↓ fills the two fields below</span>
-</div>
-
-<div class="main-controls">
- <div class="field">
- <label class="flbl">Logical shape</label>
- <input id="shape" class="ti shape" value="4, 8" spellcheck="false">
- </div>
- <div class="field">
- <label class="flbl">Swizzle (SMEM)</label>
- <div class="sw-group">
- <select id="dtype" class="ti"></select>
- <select id="swmode" class="ti"></select>
- </div>
- </div>
- <div class="field grow">
- <label class="flbl">Tile layout <span class="flbl-sub">S[shard]
+ R[replica] + offset</span></label>
- <input id="expr" class="ti expr" value="S[(4,8):(8@laneid,1@laneid)]"
spellcheck="false">
- </div>
-</div>
-<div id="status"></div>
-
-<div class="panels" id="panels">
- <div class="panel">
- <h2>Logical tensor</h2>
- <div class="nota" id="n0"></div>
- <div class="grid" id="g0"></div>
- </div>
- <div class="panel">
- <h2>Physical threads</h2>
- <div class="nota" id="nphys"></div>
- <div id="phys"></div>
- </div>
- <svg class="arrow-svg" id="arrow"></svg>
-</div>
-
-<div class="formula-bar" id="fb"></div>
-<div class="leg" id="lg"></div>
-
-<script src="layout-demo.js"></script>
-</body>
-</html>
diff --git a/assets/tirx-layout-demo/layout-demo.js
b/assets/tirx-layout-demo/layout-demo.js
deleted file mode 100644
index ec117accaf3..00000000000
--- a/assets/tirx-layout-demo/layout-demo.js
+++ /dev/null
@@ -1,805 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- * TIRx tensor-layout visualizer.
- *
- * The page shell, CSS vocabulary, and the draw()/hover/arrow interaction
- * pattern are adapted from the team's own course material
- * (mlsyscourse/slides-modern-gpu-programming, data-layout/site/demo/
- * tile_distributed.html). The TIRx S/R/O parser and the logical->physical
- * mapper below are original and mirror tvm/python/tvm/tirx/layout.py
- * (_flatten_coord / _split_coord and the TileLayout forward mapping).
- * This file is NOT derived from any third-party layout demo.
- */
-
-'use strict';
-
-// ── TIRx named axes (from tvm/python/tvm/tirx/layout.py _AXIS_NAMES, plus the
-// device axis `pid` used by distributed layouts)
──────────────────────────────
-const AXIS_ORDER = [
- 'pid', 'bx', 'by', 'bz', 'cbx', 'cby', 'cbz', 'tx',
- 'warpid', 'laneid', 'wgid', 'tid_in_wg', 'wid_in_wg', 'tid',
- 'm', 'P', 'F', 'Bank', 'TCol', 'TLane',
-];
-// "Owner" axes name a physical unit that owns data (threads, devices); the
rest
-// (m, P, F, Bank, TCol, TLane) are storage/memory coordinates within an owner.
-const OWNER_AXES = new Set([
- 'pid', 'bx', 'by', 'bz', 'cbx', 'cby', 'cbz', 'tx',
- 'warpid', 'laneid', 'wgid', 'tid_in_wg', 'wid_in_wg', 'tid',
-]);
-const KNOWN_AXES = new Set(AXIS_ORDER);
-const MAX_ELEMENTS = 1024; // render cap
-
-function isOwnerAxis(a) { return OWNER_AXES.has(a); }
-function product(arr) { return arr.reduce((a, b) => a * b, 1); }
-
-// ── Parser
───────────────────────────────────────────────────────────────────
-// Grammar mirrors layout.py: S[shape:stride] + R[shape:stride] + offset,
-// stride/offset terms are "n@axis" (a bare int defaults to axis "m").
-
-function splitTopLevel(s, sep) {
- const out = [];
- let depth = 0, cur = '';
- for (const ch of s) {
- if (ch === '(' || ch === '[') depth++;
- else if (ch === ')' || ch === ']') {
- depth--;
- if (depth < 0) throw new Error('unmatched closing bracket or
parenthesis');
- }
- if (ch === sep && depth === 0) { out.push(cur); cur = ''; }
- else cur += ch;
- }
- if (depth !== 0) throw new Error('unmatched opening bracket or parenthesis');
- out.push(cur);
- return out;
-}
-
-function stripParens(s) {
- s = s.trim();
- if (s.startsWith('(') && s.endsWith(')')) return s.slice(1, -1);
- return s;
-}
-
-function parseIntStrict(s) {
- const t = s.trim();
- if (!/^-?\d+$/.test(t)) throw new Error(`expected integer, got "${t}"`);
- return parseInt(t, 10);
-}
-
-function parseTerm(tok) {
- const t = tok.trim();
- if (t.includes('@')) {
- const parts = t.split('@');
- if (parts.length !== 2) throw new Error(`bad term "${t}"`);
- const numPart = parts[0].trim();
- const axis = parts[1].trim();
- if (!KNOWN_AXES.has(axis)) throw new Error(`unknown axis "${axis}"`);
- const stride = numPart === '' ? 1 : parseIntStrict(numPart);
- return { stride, axis };
- }
- if (/^-?\d+$/.test(t)) return { stride: parseInt(t, 10), axis: 'm' };
- if (KNOWN_AXES.has(t)) return { stride: 1, axis: t };
- throw new Error(`bad term "${t}"`);
-}
-
-function defaultStrides(extents) {
- const n = extents.length;
- const strides = new Array(n).fill(1);
- for (let i = n - 2; i >= 0; i--) strides[i] = strides[i + 1] * extents[i +
1];
- return strides;
-}
-
-function parseExtents(s) {
- // Parse a comma-separated extent list and reject non-positive / oversized
- // extents, so bad input (e.g. R[1000000:...] or a 0/negative extent) can't
- // NaN-propagate or freeze the tab with huge loops in physOwners().
- const extents = splitTopLevel(stripParens(s), ',').map(parseIntStrict);
- for (const e of extents) {
- if (e <= 0) throw new Error(`extent must be positive, got ${e}`);
- if (e > MAX_ELEMENTS) throw new Error(`extent ${e} exceeds maximum
${MAX_ELEMENTS}`);
- }
- return extents;
-}
-
-function parseBracket(inner) {
- const parts = splitTopLevel(inner, ':');
- if (parts.length === 1) {
- const extents = parseExtents(parts[0]);
- const strides = defaultStrides(extents);
- return extents.map((e, i) => ({ extent: e, stride: strides[i], axis: 'm'
}));
- }
- if (parts.length !== 2) throw new Error('layout bracket must be "shape :
stride"');
- const extents = parseExtents(parts[0]);
- const terms = splitTopLevel(stripParens(parts[1]), ',').map(parseTerm);
- if (extents.length !== terms.length) {
- throw new Error(`shape has ${extents.length} dims but stride has
${terms.length}`);
- }
- return extents.map((e, i) => ({ extent: e, stride: terms[i].stride, axis:
terms[i].axis }));
-}
-
-function bracketBody(piece, prefix) {
- const open = piece.indexOf('[');
- const close = piece.lastIndexOf(']');
- if (open < 0 || close < 0 || close < open) throw new Error(`malformed
${prefix}[...]`);
- return piece.slice(open + 1, close);
-}
-
-function parseSwizzlePrefix(src) {
- // Optional "Swizzle(per_element, swizzle_len, atom_len[, inner]) [∘|o|*]
<layout>".
- const m = src.match(/^Swizzle\s*\(([^)]*)\)\s*(?:∘|o|\.|\*)?\s*([\s\S]*)$/i);
- if (!m) return { swizzle: null, rest: src };
- const a = m[1].split(',').map((s) => s.trim()).filter((s) => s.length);
- if (a.length < 3) throw new Error('Swizzle needs (per_element, swizzle_len,
atom_len)');
- const per_element = parseIntStrict(a[0]);
- const swizzle_len = parseIntStrict(a[1]);
- const atom_len = parseIntStrict(a[2]);
- if (per_element < 0 || swizzle_len < 0 || atom_len < swizzle_len
- || per_element >= 31 || atom_len >= 31) {
- // atom_len/per_element feed 32-bit bitwise shifts in swizzleAddr; cap <
31.
- throw new Error('swizzle requires 0≤per_element<31, swizzle_len≥0,
swizzle_len≤atom_len<31');
- }
- const inner = a[3] === undefined ? true : (a[3] === 'true' || a[3] === '1');
- return { swizzle: { per_element, swizzle_len, atom_len, inner }, rest:
m[2].trim() };
-}
-
-function parseLayout(srcRaw) {
- const { swizzle, rest } = parseSwizzlePrefix(srcRaw.trim());
- const src = rest;
- const layout = { shard: [], replica: [], offset: {}, swizzle };
- let sawShard = false;
- for (let piece of splitTopLevel(src, '+')) {
- piece = piece.trim();
- if (piece === '') continue;
- if (piece.startsWith('S[')) {
- layout.shard = parseBracket(bracketBody(piece, 'S'));
- sawShard = true;
- } else if (piece.startsWith('R[')) {
- layout.replica = parseBracket(bracketBody(piece, 'R'));
- } else {
- const t = parseTerm(piece);
- layout.offset[t.axis] = (layout.offset[t.axis] || 0) + t.stride;
- }
- }
- if (!sawShard) throw new Error('layout needs a shard term, e.g. S[...]');
- return layout;
-}
-
-// ── Mapper (mirrors layout.py _flatten_coord / _split_coord + forward map)
─────
-
-function flattenCoord(coord, shape) {
- let flat = 0;
- for (let i = 0; i < shape.length; i++) flat = flat * shape[i] + coord[i];
- return flat;
-}
-
-function splitCoord(flat, extents) {
- const n = extents.length;
- const res = new Array(n);
- let remaining = flat;
- for (let i = n - 1; i >= 0; i--) {
- if (i === 0) res[0] = remaining;
- else { res[i] = remaining % extents[i]; remaining = Math.floor(remaining /
extents[i]); }
- }
- return res;
-}
-
-function coordFromFlat(flat, shape) { return splitCoord(flat, shape); }
-
-function forwardBase(coord, shape, layout) {
- const flat = flattenCoord(coord, shape);
- const comps = splitCoord(flat, layout.shard.map((it) => it.extent));
- const phys = {};
- for (let k = 0; k < layout.shard.length; k++) {
- const it = layout.shard[k];
- phys[it.axis] = (phys[it.axis] || 0) + comps[k] * it.stride;
- }
- for (const axis of Object.keys(layout.offset)) {
- phys[axis] = (phys[axis] || 0) + layout.offset[axis];
- }
- return phys;
-}
-
-// Replica broadcasts the same logical element onto multiple physical owners:
-// L(x) = { D(x) + r + O | r in R }.
-function physOwners(coord, shape, layout) {
- let owners = [forwardBase(coord, shape, layout)];
- for (const rep of layout.replica) {
- const next = [];
- for (const o of owners) {
- for (let k = 0; k < rep.extent; k++) {
- const o2 = Object.assign({}, o);
- o2[rep.axis] = (o2[rep.axis] || 0) + k * rep.stride;
- next.push(o2);
- }
- }
- owners = next;
- }
- return owners;
-}
-
-function axesUsed(layout) {
- const s = new Set();
- for (const it of layout.shard) s.add(it.axis);
- for (const it of layout.replica) s.add(it.axis);
- for (const a of Object.keys(layout.offset)) s.add(a);
- return AXIS_ORDER.filter((a) => s.has(a));
-}
-
-function coordStr(phys, axes) {
- return axes.map((a) => `${a}=${phys[a] || 0}`).join(' ');
-}
-
-// Swizzle a linear memory address (mirrors
src/tirx/ir/layout/swizzle_layout.cc
-// SwizzleLayoutNode::Apply): low `per_element` bits are kept; above them, the
-// swizzle bits are XOR'd to scatter bank conflicts.
-function swizzleAddr(m, sw) {
- const base = 1 << sw.per_element;
- const innerMask = (1 << sw.swizzle_len) - 1;
- const outerMask = innerMask << sw.atom_len;
- const x = Math.floor(m / base);
- const fx = sw.inner ? (x ^ ((x & outerMask) >> sw.atom_len))
- : (x ^ ((x & innerMask) << sw.atom_len));
- return fx * base + (m % base);
-}
-
-// Resolve the swizzle from the dtype + mode dropdowns (mirrors
-// tma_utils.mma_atom_layout): per_element = bit_length(128//bits) - 1,
-// swizzle_len = mode, atom_len = 3. Falls back to a typed Swizzle(...) prefix.
-const SWIZZLE_LEN = { none: 0, '32': 1, '64': 2, '128': 3 };
-function computeSwizzle() {
- const mode = swmodeSel ? swmodeSel.value : 'off';
- if (mode === 'off') return (ST.layout && ST.layout.swizzle) ?
ST.layout.swizzle : null;
- const bits = +(dtypeSel ? dtypeSel.value : 16) || 16;
- const per_element = Math.floor(128 / bits).toString(2).length - 1;
- return {
- per_element, swizzle_len: SWIZZLE_LEN[mode] || 0, atom_len: 3, inner: true,
- bits, mode,
- };
-}
-
-// ── State + recompute
──────────────────────────────────────────────────────--
-function getComputedStyleVar(name) {
- return
getComputedStyle(document.documentElement).getPropertyValue(name).trim();
-}
-const PALETTE = Array.from({ length: 8 }, (_, i) =>
- getComputedStyleVar(`--color-group-${i}`) || '#5b9bd5');
-function paletteColor(v) { const n = PALETTE.length; return PALETTE[((v % n) +
n) % n]; }
-
-const ST = {
- shape: [4, 8],
- layout: null,
- error: null,
- tooBig: false,
- banks: 32,
- swizzle: null,
- gridAxes: [], yAxis: null, xAxis: null, cellAxes: [],
- yVals: [], xVals: [],
- byFlat: [], // flat -> { owners:[phys], keys:[gridKey], color }
- byCell: new Map(), // "y#x" -> [{flat, slot}]
-};
-let hovFlat = null;
-let drawing = false;
-
-function mk(t, c) { const d = document.createElement(t); if (c) d.className =
c; return d; }
-function gridKey(phys, axes) { return axes.map((a) => phys[a] || 0).join(',');
}
-
-function recompute() {
- ST.error = null; ST.tooBig = false;
- try {
- ST.shape = splitTopLevel(stripParens(shapeInput.value),
',').map(parseIntStrict);
- if (ST.shape.length === 0 || ST.shape.some((x) => x <= 0)) throw new
Error('shape must be positive ints');
- ST.layout = parseLayout(exprInput.value);
- } catch (e) { ST.error = e.message; return; }
-
- const total = product(ST.shape);
- if (total > MAX_ELEMENTS) { ST.tooBig = true; return; }
-
- ST.shapeTotal = total;
- ST.shardTotal = product(ST.layout.shard.map((it) => it.extent));
- ST.mismatch = ST.shardTotal !== total;
- ST.swizzle = computeSwizzle();
- // elements that share one 4-byte bank word (e.g. 2 fp16, 4 fp8, 1 fp32)
- ST.elemsPerBank = ST.swizzle ? Math.max(1, Math.round(4 / ((ST.swizzle.bits
|| 32) / 8))) : 1;
-
- // 1) owners per element; in swizzle mode, also map the memory address
through
- // the swizzle and derive synthetic line/bank coordinates.
- ST.byFlat = new Array(total);
- for (let flat = 0; flat < total; flat++) {
- const owners = physOwners(coordFromFlat(flat, ST.shape), ST.shape,
ST.layout);
- if (ST.swizzle) {
- // A shared-memory bank is 4 bytes; an element occupies dtype_bytes, so
the
- // bank word index is floor(element_addr * dtype_bytes / 4). 32 banks
per line.
- const bytes = (ST.swizzle.bits || 32) / 8;
- for (const o of owners) {
- const sm = swizzleAddr(o.m || 0, ST.swizzle);
- const word = Math.floor((sm * bytes) / 4);
- o.__sm = sm; o.__word = word; o.bank = word % ST.banks; o.line =
Math.floor(word / ST.banks);
- }
- }
- ST.byFlat[flat] = { owners };
- }
-
- // 2) choose grid + color axes
- if (ST.swizzle) {
- ST.gridAxes = ['line', 'bank']; ST.yAxis = 'line'; ST.xAxis = 'bank';
- ST.cellAxes = []; ST.colorAxis = 'bank';
- } else {
- const used = axesUsed(ST.layout);
- const owners = used.filter(isOwnerAxis);
- ST.gridAxes = owners.length ? owners : used.filter((a) => !isOwnerAxis(a));
- ST.yAxis = ST.gridAxes[0] || null;
- ST.xAxis = ST.gridAxes[1] || null;
- ST.cellAxes = used.filter((a) => !ST.gridAxes.includes(a));
- // color axis = first grid axis from shard/offset, so a replica-only row
axis
- // doesn't collapse every element to one color.
- const shardOffsetAxes = new Set(ST.layout.shard.map((it) => it.axis));
- for (const a of Object.keys(ST.layout.offset)) shardOffsetAxes.add(a);
- ST.colorAxis = ST.gridAxes.find((a) => shardOffsetAxes.has(a)) ||
ST.gridAxes[0] || null;
- }
-
- // 3) build cells, hover keys, colors
- ST.byCell = new Map();
- const yset = new Set(), xset = new Set(), cset = new Set();
- for (let flat = 0; flat < total; flat++) {
- const rec = ST.byFlat[flat];
- const keys = [];
- for (const o of rec.owners) {
- const y = ST.yAxis ? (o[ST.yAxis] || 0) : 0;
- const x = ST.xAxis ? (o[ST.xAxis] || 0) : 0;
- yset.add(y); xset.add(x);
- keys.push(gridKey(o, ST.gridAxes));
- const ck = y + '#' + x;
- if (!ST.byCell.has(ck)) ST.byCell.set(ck, []);
- ST.byCell.get(ck).push({ flat, slot: ST.swizzle ? ('addr ' + o.__sm) :
coordStr(o, ST.cellAxes) });
- }
- rec.keys = keys;
- const cv = ST.colorAxis ? (rec.owners[0][ST.colorAxis] || 0) : 0;
- rec.color = paletteColor(cv);
- cset.add(cv);
- }
- ST.yVals = [...yset].sort((a, b) => a - b);
- ST.xVals = [...xset].sort((a, b) => a - b);
- ST.colorVals = [...cset].sort((a, b) => a - b);
-}
-
-// ── Display geometry for the logical grid
──────────────────────────────────--
-function logicalGridDims() {
- if (ST.shape.length === 2) return { rows: ST.shape[0], cols: ST.shape[1] };
- if (ST.shape.length === 1) return { rows: 1, cols: ST.shape[0] };
- return { rows: 1, cols: product(ST.shape) }; // N-D -> flat strip
-}
-
-// ── Draw ────────────────────────────────────────────────────────────────────
-function resetFit() {
- const p = document.getElementById('panels');
- if (p) { p.style.transform = 'none'; p.style.marginBottom = ''; }
-}
-function fitEmbed() {
- if (!document.body.classList.contains('lock')) return;
- const p = document.getElementById('panels');
- if (!p) return;
- const natural = p.offsetWidth;
- const pad = 2 * parseFloat(getComputedStyle(document.body).paddingLeft ||
'0');
- const avail = document.documentElement.clientWidth - pad;
- if (avail > 0 && natural > avail) {
- const sc = avail / natural;
- p.style.transformOrigin = 'top left';
- p.style.transform = 'scale(' + sc + ')';
- p.style.marginBottom = (-(p.offsetHeight * (1 - sc))) + 'px';
- }
-}
-function postHeight() {
- if (window.parent === window) return;
- const h = Math.ceil(document.body.scrollHeight);
- window.parent.postMessage({ tirxLayoutDemoHeight: h + 4 }, '*');
-}
-function draw() {
- drawing = true;
- resetFit();
- const status = document.getElementById('status');
- const g0 = document.getElementById('g0');
- const phys = document.getElementById('phys');
- const fb = document.getElementById('fb');
- const lg = document.getElementById('lg');
-
- if (ST.error) {
- status.innerHTML = `<span style="color:var(--color-bad)">parse error:
${escapeHtml(ST.error)}</span>`;
- g0.innerHTML = ''; phys.innerHTML = ''; lg.innerHTML = '';
- fb.innerHTML = '<div class="ftitle">Fix the layout expression to
continue.</div>';
- setTimeout(() => { drawing = false; }, 0); return;
- }
- if (ST.tooBig) {
- status.innerHTML = `<span
style="color:var(--color-bad)">${product(ST.shape)} elements exceeds the
${MAX_ELEMENTS} render cap — use a smaller shape.</span>`;
- g0.innerHTML = ''; phys.innerHTML = ''; lg.innerHTML = '';
- fb.innerHTML = '<div class="ftitle">Shape too large to visualize.</div>';
- setTimeout(() => { drawing = false; }, 0); return;
- }
-
- status.innerHTML = `<span style="color:var(--color-good)">ok</span> `
+
- `${product(ST.shape)} logical elements | ` +
- `${ST.yVals.length * (ST.xVals.length || 1)} physical cells`;
- if (ST.mismatch) {
- status.innerHTML += ` <span style="color:#b45309;font-weight:600">` +
- `⚠ shard total ${ST.shardTotal} ≠ shape total ${ST.shapeTotal} — mapping
may be ill-formed</span>`;
- }
- if (ST.swizzle) {
- const s = ST.swizzle;
- const label = s.mode ? (s.mode === 'none' ? 'no swizzle' : s.mode + 'B
swizzle') : 'swizzle';
- status.innerHTML += ` <span style="color:var(--dim)">` +
- `${label}${s.bits ? ', ' + s.bits + '-bit' : ''} →
Swizzle(${s.per_element},${s.swizzle_len},${s.atom_len})</span>`;
- }
- document.getElementById('n0').textContent = `logical shape
(${ST.shape.join(', ')})`;
- document.getElementById('nphys').textContent =
- (ST.xAxis ? `rows = ${ST.yAxis}, cols = ${ST.xAxis}` : `tiles = ${ST.yAxis
|| '(none)'}`) +
- (ST.cellAxes.length ? ' · in-cell: ' + ST.cellAxes.join(', ') : '');
-
- const hovKeys = hovFlat !== null ? new Set(ST.byFlat[hovFlat].keys) : null;
- drawLogical(hovKeys);
- drawPhysical(hovKeys);
- drawFormula();
- drawArrow();
- drawLegend();
- fitEmbed();
- postHeight();
- setTimeout(() => { drawing = false; }, 0);
-}
-
-function sharesOwner(flat, hovKeys) {
- if (!hovKeys) return false;
- return ST.byFlat[flat].keys.some((k) => hovKeys.has(k));
-}
-
-function drawLogical(hovKeys) {
- const g = document.getElementById('g0');
- g.innerHTML = '';
- const { rows, cols } = logicalGridDims();
- g.style.gridTemplateColumns = '30px repeat(' + cols + ', 46px)';
- g.appendChild(mk('div', 'hdr'));
- for (let c = 0; c < cols; c++) { const h = mk('div', 'hdr'); h.textContent =
'c' + c; g.appendChild(h); }
- for (let r = 0; r < rows; r++) {
- const rl = mk('div', 'rl'); rl.textContent = (rows > 1) ? ('r' + r) : '';
g.appendChild(rl);
- for (let c = 0; c < cols; c++) {
- const flat = r * cols + c;
- const d = mk('div', 'cell');
- d.dataset.flat = flat;
- d.textContent = flat;
- d.style.background = ST.byFlat[flat].color;
- d.style.color = '#fff';
- if (hovFlat !== null) {
- if (flat === hovFlat) d.classList.add('hov');
- else if (!sharesOwner(flat, hovKeys)) d.classList.add('dm');
- }
- g.appendChild(d);
- }
- }
-}
-
-function cellEntries(y, x) { return ST.byCell.get(y + '#' + x) || []; }
-
-function makeSlot(entry) {
- const s = mk('div', 'gcell');
- s.style.background = ST.byFlat[entry.flat].color;
- s.style.color = '#fff';
- s.dataset.flat = entry.flat;
- s.textContent = entry.flat;
- s.title = entry.slot ? `element ${entry.flat} @ ${entry.slot}` : `element
${entry.flat}`;
- if (hovFlat !== null) {
- if (entry.flat === hovFlat) s.classList.add('hov');
- else s.classList.add('dm');
- }
- return s;
-}
-
-function drawPhysical(hovKeys) {
- const wrap = document.getElementById('phys');
- wrap.innerHTML = '';
- if (!ST.yAxis) { wrap.textContent = '(no physical axes)'; return; }
-
- if (ST.xAxis) {
- // 2D table: rows = yAxis values, cols = xAxis values.
- const table = mk('div', 'phys-table' + (ST.swizzle ? ' bank-mode' : ''));
- // In bank mode a cell is one 4-byte bank word holding elemsPerBank
elements
- // laid out horizontally; otherwise a fixed 54px cell.
- const colW = ST.swizzle ? (ST.elemsPerBank * 48 + 8) : 54;
- table.style.gridTemplateColumns = '44px repeat(' + ST.xVals.length + ', '
+ colW + 'px)';
- table.appendChild(corner());
- for (const x of ST.xVals) table.appendChild(axHdr(String(x), false,
`${ST.xAxis}=${x}`));
- for (const y of ST.yVals) {
- table.appendChild(axHdr(String(y), true, `${ST.yAxis}=${y}`));
- for (const x of ST.xVals) {
- const cell = mk('div', 'pcell');
- const entries = cellEntries(y, x);
- if (hovFlat !== null && entries.some((e) => e.flat === hovFlat))
cell.classList.add('hov-cell');
- else if (hovFlat !== null && entries.length)
cell.classList.add('dm-cell');
- for (const e of entries) cell.appendChild(makeSlot(e));
- table.appendChild(cell);
- }
- }
- wrap.appendChild(table);
- } else {
- // 1D: wrapped list of owner tiles, one per yAxis value.
- const list = mk('div', 'phys-1d');
- for (const y of ST.yVals) {
- const tile = mk('div', 'thread-tile');
- const lbl = mk('div', 'thread-lbl'); lbl.textContent =
`${ST.yAxis}=${y}`; tile.appendChild(lbl);
- const slots = mk('div', 'thread-slots');
- const entries = cellEntries(y, 0).slice().sort((a, b) => a.flat -
b.flat);
- if (hovFlat !== null && entries.some((e) => e.flat === hovFlat))
tile.classList.add('hov-tile');
- else if (hovFlat !== null && entries.length)
tile.classList.add('dm-tile');
- for (const e of entries) slots.appendChild(makeSlot(e));
- tile.appendChild(slots);
- list.appendChild(tile);
- }
- wrap.appendChild(list);
- }
-}
-
-function corner() {
- const d = mk('div', 'ax-hdr corner');
- d.textContent = '↘';
- if (ST.yAxis) d.title = `rows = ${ST.yAxis}` + (ST.xAxis ? `, cols =
${ST.xAxis}` : '');
- return d;
-}
-function axHdr(text, isRow, title) {
- const d = mk('div', 'ax-hdr' + (isRow ? ' row' : ''));
- d.textContent = text;
- if (title) d.title = title;
- return d;
-}
-
-function drawFormula() {
- const fb = document.getElementById('fb');
- if (hovFlat === null) { fb.innerHTML = '<div class="ftitle">Click a logical
element to see its mapping.</div>'; return; }
- const flat = hovFlat;
- const coord = coordFromFlat(flat, ST.shape);
- const comps = splitCoord(flat, ST.layout.shard.map((it) => it.extent));
- const perAxis = {};
- const termStrings = [];
- for (let k = 0; k < ST.layout.shard.length; k++) {
- const it = ST.layout.shard[k];
- perAxis[it.axis] = (perAxis[it.axis] || 0) + comps[k] * it.stride;
- termStrings.push(`${comps[k]}·${it.stride}@${it.axis}`);
- }
- const offStrings = [];
- for (const axis of Object.keys(ST.layout.offset)) {
- perAxis[axis] = (perAxis[axis] || 0) + ST.layout.offset[axis];
- offStrings.push(`${ST.layout.offset[axis]}@${axis}`);
- }
- const owners = ST.byFlat[flat].owners;
- let html = `<div class="ftitle">element ${flat} at logical (${coord.join(',
')})` +
- ` → shard split = (${comps.join(', ')})</div>`;
- html += '<div class="fcontent">';
- html += `terms: ${termStrings.join(' + ')}` +
- (offStrings.length ? ` + offset[${offStrings.join(', ')}]` : '') +
'<br>';
- const baseParts = AXIS_ORDER.filter((a) => perAxis[a] !== undefined).map((a)
=> `<b>${perAxis[a]}</b>@${a}`);
- html += `base location: ${baseParts.join(' , ')}`;
- if (ST.swizzle) {
- const o0 = owners[0];
- const sw = ST.swizzle;
- const bytes = (sw.bits || 32) / 8;
- html += `<br>swizzle(${sw.per_element},${sw.swizzle_len},${sw.atom_len}):
` +
- `m=${o0.m || 0} → elem ${o0.__sm} → byte ${o0.__sm * bytes} → ` +
- `<b>bank ${o0.bank}</b>, line ${o0.line} (${bytes}-byte dtype, 4-byte
banks ×32)`;
- }
- if (owners.length > 1) {
- html += `<br>owners (×${owners.length} via replica): ` +
- owners.map((o) => '{ ' + coordStr(o, ST.gridAxes) + ' }').join(' , ');
- }
- html += '</div>';
- fb.innerHTML = html;
-}
-
-// ── Arrow overlay (adapted from the course draw pattern)
───────────────────--
-const panels = document.getElementById('panels');
-const arrowSvg = document.getElementById('arrow');
-
-function drawArrow() {
- arrowSvg.innerHTML = '';
- if (hovFlat === null) return;
- const leftCell = document.querySelector('#g0 .cell.hov');
- const rightCells = document.querySelectorAll('#phys .gcell.hov');
- if (!leftCell || rightCells.length === 0) return;
- const pr = panels.getBoundingClientRect();
- const ns = 'http://www.w3.org/2000/svg';
- const defs = document.createElementNS(ns, 'defs');
- const marker = document.createElementNS(ns, 'marker');
- marker.setAttribute('id', 'ah'); marker.setAttribute('markerWidth', '8');
marker.setAttribute('markerHeight', '6');
- marker.setAttribute('refX', '7'); marker.setAttribute('refY', '3');
marker.setAttribute('orient', 'auto');
- const poly = document.createElementNS(ns, 'polygon');
- poly.setAttribute('points', '0 0, 8 3, 0 6'); poly.setAttribute('fill',
'#222');
- marker.appendChild(poly); defs.appendChild(marker);
arrowSvg.appendChild(defs);
- const a = leftCell.getBoundingClientRect();
- const x1 = a.left + a.width / 2 - pr.left, y1 = a.top + a.height / 2 -
pr.top;
- rightCells.forEach((rc) => {
- const b = rc.getBoundingClientRect();
- const x2 = b.left + b.width / 2 - pr.left, y2 = b.top + b.height / 2 -
pr.top;
- const mx = (x1 + x2) / 2, my = Math.min(y1, y2) - 24;
- const path = document.createElementNS(ns, 'path');
- path.setAttribute('d', `M${x1},${y1} Q${mx},${my} ${x2},${y2}`);
- path.setAttribute('marker-end', 'url(#ah)');
- arrowSvg.appendChild(path);
- });
-}
-
-function clearHov() {
- document.querySelectorAll('.cell.hov, .gcell.hov').forEach((d) =>
d.classList.remove('hov'));
- arrowSvg.innerHTML = '';
- hovFlat = null;
-}
-
-panels.addEventListener('click', (e) => {
- const cell = e.target.closest('.cell') || e.target.closest('.gcell');
- if (!cell || cell.dataset.flat === undefined) return;
- const flat = +cell.dataset.flat;
- if (hovFlat === flat) { clearHov(); draw(); return; }
- clearHov();
- hovFlat = flat;
- draw();
-});
-
-// ── Legend ──────────────────────────────────────────────────────────────────
-function swatchEl(color) { const w = mk('div', 'swtch'); w.style.background =
color; return w; }
-
-function drawLegend() {
- const lg = document.getElementById('lg');
- lg.innerHTML = '';
-
- // Color key: an actual swatch-per-value table using the same palette as the
grid.
- const r0 = mk('div', 'leg-row');
- const lead = mk('div', 'li');
- lead.innerHTML = `<b>color = ${ST.colorAxis || 'physical'} value:</b>`;
- r0.appendChild(lead);
- const vals = ST.colorVals || [];
- if (vals.length <= 8) {
- for (const v of vals) {
- const li = mk('div', 'li');
- li.appendChild(swatchEl(paletteColor(v)));
- li.appendChild(document.createTextNode(String(v)));
- r0.appendChild(li);
- }
- } else {
- for (let k = 0; k < 8; k++) {
- const li = mk('div', 'li');
- li.appendChild(swatchEl(PALETTE[k]));
- li.appendChild(document.createTextNode('≡' + k));
- r0.appendChild(li);
- }
- const note = mk('div', 'li');
- note.textContent = `(${ST.colorAxis} mod 8; ${vals.length} values)`;
- r0.appendChild(note);
- }
- lg.appendChild(r0);
-
- const r1 = mk('div', 'leg-row');
- const c2 = mk('div', 'li'); c2.textContent = 'number = logical element
index'; r1.appendChild(c2);
- if (ST.layout.replica.length) {
- r1.appendChild(mk('div', 'leg-sep'));
- const c3 = mk('div', 'li');
- c3.textContent = 'replica → identical copies: the same color appears in
multiple physical cells';
- r1.appendChild(c3);
- }
- lg.appendChild(r1);
-
- const r2 = mk('div', 'leg-row');
- const m = mk('div', 'li');
- const owners = axesUsed(ST.layout).filter(isOwnerAxis);
- const mem = axesUsed(ST.layout).filter((a) => !isOwnerAxis(a));
- m.textContent = 'owner axes: ' + (owners.join(', ') || '(none — pure memory
layout)') +
- ' · memory axes: ' + (mem.join(', ') || '(none)');
- r2.appendChild(m);
- lg.appendChild(r2);
-}
-
-function escapeHtml(s) {
- return s.replace(/[&<>"]/g, (c) => ({ '&': '&', '<': '<', '>':
'>', '"': '"' }[c]));
-}
-
-// ── Presets + controls
───────────────────────────────────────────────────────
-// Case-study presets use scaled-down shapes so every element renders; the
-// mapping semantics match the full-size examples in the docs.
-const PRESETS = [
- { label: 'Shard → lanes (intro)', shape: '4, 8', expr:
'S[(4,8):(8@laneid,1@laneid)]' },
- { label: 'Shard + registers', shape: '4, 8', expr:
'S[(4,2,4):(8@laneid,1@m,1@laneid)]' },
- { label: 'Shard + replica', shape: '4, 8', expr:
'S[(4,8):(8@laneid,1@laneid)] + R[2:1@warpid]' },
- {
- label: 'Tensor-core tile (doc example)', shape: '8, 16',
- expr: 'S[(8,2,4,2):(4@laneid,1@warpid,1@laneid,1)] + R[2:4@warpid] +
5@warpid',
- },
- { label: 'Distributed 2×2 GPU mesh (pid)', shape: '4, 4', expr:
'S[(2,2,2,2):(1@pid,2@m,2@pid,1@m)]' },
- { label: 'Mesh + replica (pid)', shape: '4, 4', expr:
'S[(2,2,4):(1@pid,2@m,1@m)] + R[2:2@pid]' },
- { label: 'Accelerator scratchpad (P/F)', shape: '4, 8', expr:
'S[(2,4,4):(4@F,1@P,1@F)]' },
- { label: 'Blackwell tensor memory (TLane/TCol)', shape: '4, 8', expr:
'S[(2,4,4):(4@TCol,1@TLane,1@TCol)]' },
- { label: 'SMEM, no swizzle (bank conflicts)', shape: '8, 64', expr:
'S[(8,64):(64@m,1@m)]', dtype: 16, mode: 'none' },
- { label: 'SMEM swizzle 128B (fp16)', shape: '8, 64', expr:
'S[(8,64):(64@m,1@m)]', dtype: 16, mode: '128' },
- { label: '1-D shard', shape: '8', expr: 'S[8:4@laneid]' },
- { label: 'Extents only (default strides)', shape: '8, 4', expr: 'S[(8,4)]' },
-];
-
-const DTYPES = [
- { label: 'float16 (16-bit)', bits: 16 },
- { label: 'bfloat16 (16-bit)', bits: 16 },
- { label: 'float8 (8-bit)', bits: 8 },
- { label: 'float32 (32-bit)', bits: 32 },
- { label: 'tfloat32 (32-bit)', bits: 32 },
- { label: 'float64 (64-bit)', bits: 64 },
-];
-const SWMODES = [
- { label: 'off (general layout)', value: 'off' },
- { label: 'none — raw banks', value: 'none' },
- { label: '32B swizzle', value: '32' },
- { label: '64B swizzle', value: '64' },
- { label: '128B swizzle', value: '128' },
-];
-
-const shapeInput = document.getElementById('shape');
-const exprInput = document.getElementById('expr');
-const presetSel = document.getElementById('preset');
-const dtypeSel = document.getElementById('dtype');
-const swmodeSel = document.getElementById('swmode');
-
-function applyPreset(i) {
- const p = PRESETS[i];
- shapeInput.value = p.shape;
- exprInput.value = p.expr;
- if (dtypeSel) dtypeSel.value = String(p.dtype || 16);
- if (swmodeSel) swmodeSel.value = p.mode || 'off';
- refresh();
-}
-function refresh() { clearHov(); recompute(); draw(); }
-
-function init() {
- PRESETS.forEach((p, i) => {
- const o = document.createElement('option');
- o.value = i; o.textContent = p.label; presetSel.appendChild(o);
- });
- DTYPES.forEach((d) => {
- const o = document.createElement('option');
- o.value = d.bits; o.textContent = d.label; dtypeSel.appendChild(o);
- });
- SWMODES.forEach((s) => {
- const o = document.createElement('option');
- o.value = s.value; o.textContent = s.label; swmodeSel.appendChild(o);
- });
- presetSel.addEventListener('change', () => applyPreset(+presetSel.value));
- shapeInput.addEventListener('input', refresh);
- exprInput.addEventListener('input', refresh);
- dtypeSel.addEventListener('change', refresh);
- swmodeSel.addEventListener('change', refresh);
- window.addEventListener('resize', () => { resetFit(); fitEmbed();
postHeight(); });
- window.addEventListener('load', () => { resetFit(); fitEmbed();
postHeight(); });
- // Deep-linking for embeds: ?preset=<index|label-slug>¬itle
- const params = new URLSearchParams(location.search);
- let presetIdx = 0;
- const want = params.get('preset');
- if (want !== null) {
- const slug = (x) => x.toLowerCase().replace(/[^a-z0-9]+/g,
'-').replace(/^-|-$/g, '');
- if (/^\d+$/.test(want)) {
- presetIdx = Math.min(PRESETS.length - 1, Math.max(0, parseInt(want,
10)));
- } else {
- const w = slug(want);
- const found = PRESETS.findIndex((p) => slug(p.label).includes(w));
- if (found >= 0) presetIdx = found;
- }
- }
- presetSel.value = presetIdx;
- if (params.has('notitle')) document.body.classList.add('notitle');
- if (params.has('lock')) document.body.classList.add('lock');
- applyPreset(presetIdx);
-}
-
-init();
diff --git a/assets/tirx-layout-demo/viz-base.css
b/assets/tirx-layout-demo/viz-base.css
deleted file mode 100644
index 81ffadc19cd..00000000000
--- a/assets/tirx-layout-demo/viz-base.css
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- * Derived from mlsyscourse/slides-modern-gpu-programming
- * (data-layout/site/viz-base.css). Shared base styles for the
- * TIRx layout visualization; not derived from any third-party demo.
- */
-
-/* Shared base styles for all viz HTMLs
- * Colors: --bg gray, --accent blue, --surface white
- * Fonts: Inter (body), SF Mono (code/notation)
- */
-
-:root {
- --bg:#fff; --surface:#fff; --border:#dfe1e6; --text:#222; --dim:#888;
--accent:#3b82f6;
-
- /* ── Group palette (tiles, lanes, banks, sectors) ─────── */
- --color-group-0: #5b9bd5;
- --color-group-1: #ed9a3c;
- --color-group-2: #d95555;
- --color-group-3: #45b5a5;
- --color-group-4: #5fb85f;
- --color-group-5: #e0b828;
- --color-group-6: #9d6eb5;
- --color-group-7: #e87888;
-
- /* ── Interaction ──────────────────────────────────────── */
- --color-hover-bg: #dbeafe;
- --color-hover-text: #1e40af;
-
- /* ── Status ───────────────────────────────────────────── */
- --color-good: #2e7d32;
- --color-bad: #c62828;
-
- /* ── Boundaries & neutrals ────────────────────────────── */
- --color-boundary: #8C1515;
- --color-cell-bg: #f0f1f3;
- --color-cell-bg-alt: #e8eaed;
-}
-* { box-sizing:border-box; margin:0; padding:0; }
-body { background:var(--bg); color:var(--text); font-family:'Inter','SF
Pro','Segoe UI',system-ui,sans-serif; padding:24px; }
-body.figure h1, body.figure .sub { display:none; }
-body.notitle h1, body.notitle .sub { display:none; }
-h1 { text-align:center; font-size:21px; font-weight:700; margin-bottom:2px; }
-.sub { text-align:center; color:var(--dim); font-size:14px;
margin-bottom:20px; font-family:'SF Mono','Fira Code',monospace; }
-
-/* Controls */
-.controls { display:flex; gap:18px; justify-content:center;
align-items:center; margin-bottom:10px; flex-wrap:wrap; }
-.lbl { font-size:13px; color:var(--dim); margin-right:4px; font-weight:600; }
-.bg { display:inline-flex; gap:2px; }
-.btn {
- padding:5px 11px; border:1px solid var(--border); background:var(--surface);
color:var(--text);
- cursor:pointer; border-radius:5px; font-size:13px; font-family:inherit;
font-weight:500; transition:all .12s;
-}
-.btn:hover { background:#eef0f4; }
-.btn.on { background:var(--accent); border-color:var(--accent); color:#fff; }
-.btn.on:hover { background:#2563eb; }
-
-/* Side-by-side panels */
-.panels { display:grid; grid-template-columns:1fr 1fr; gap:20px;
max-width:1100px; margin:0 auto; position:relative; }
-.panel { background:var(--surface); border-radius:10px; padding:16px 14px;
border:1px solid var(--border);
- box-shadow:0 1px 3px rgba(0,0,0,.06); }
-.panel h2 { text-align:center; font-size:14px; font-weight:700;
margin-bottom:2px; }
-.panel .nota { text-align:center; font-size:11px; color:var(--accent);
font-family:'SF Mono','Fira Code',monospace;
- margin-bottom:8px; font-weight:600; }
-
-/* Grid cells */
-.grid { display:grid; gap:3px; }
-.hdr { font-size:10px; color:var(--dim); text-align:center; padding:2px 0;
font-weight:600; }
-.rl { font-size:10px; color:var(--dim); display:flex; align-items:center;
justify-content:flex-end; padding-right:3px; font-weight:600; }
-.cell {
- aspect-ratio:1; border-radius:5px; display:flex; flex-direction:column;
align-items:center; justify-content:center;
- font-size:13px; font-weight:700; border:2.5px solid transparent;
transition:all .18s;
- min-width:0; cursor:pointer; line-height:1.15;
-}
-.cell.hov { border-color:#222; border-width:3px; z-index:2; box-shadow:0 0 8px
rgba(59,130,246,.4); }
-.cell.dm { opacity:.25; }
-
-/* Arrow SVG overlay */
-.arrow-svg { position:absolute; top:0; left:0; width:100%; height:100%;
pointer-events:none; z-index:10; overflow:visible; }
-.arrow-svg path { fill:none; stroke:#222; stroke-width:1.5; }
-.arrow-svg text { font-size:11px; font-weight:600; fill:#222;
font-family:inherit; }
-
-/* Formula bar */
-.formula-bar { max-width:1100px; margin:18px auto 0;
background:var(--surface); border-radius:10px;
- padding:12px 18px; border:1px solid var(--border); box-shadow:0 1px 3px
rgba(0,0,0,.06); }
-.formula-bar .ftitle { font-size:13px; font-weight:700; margin-bottom:6px; }
-.formula-bar .fcontent { font-size:13px; font-family:'SF Mono','Fira
Code',monospace; color:var(--accent); line-height:1.6; }
-
-/* Legend */
-.leg { display:flex; flex-direction:column; align-items:center; gap:4px;
margin-top:14px; }
-.leg-row { display:flex; gap:12px; justify-content:center; flex-wrap:wrap; }
-.leg-group { display:flex; gap:10px; align-items:center; }
-.leg-sep { width:1px; height:16px; background:var(--border); }
-.li { display:flex; align-items:center; gap:4px; font-size:12px;
color:var(--dim); }
-.swtch { width:14px; height:14px; border-radius:3px; }
-
-@media(max-width:700px) { .panels { grid-template-columns:1fr; } }
diff --git a/assets/tirx-layout-demo/viz-base.js
b/assets/tirx-layout-demo/viz-base.js
deleted file mode 100644
index c5ef10f13db..00000000000
--- a/assets/tirx-layout-demo/viz-base.js
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- * Derived from mlsyscourse/slides-modern-gpu-programming
- * (data-layout/site/viz-base.js). Shared behavior for the TIRx
- * layout visualization; not derived from any third-party demo.
- */
-
-// Shared behavior for all viz HTMLs
-document.addEventListener('DOMContentLoaded', function() {
- var p = new URLSearchParams(location.search);
- if (p.has('notitle')) document.body.classList.add('notitle');
-
- // Forward arrow keys to parent (reveal.js) when embedded
- if (window.parent !== window) {
- document.addEventListener('keydown', function(e) {
- if ([37, 38, 39, 40, 27, 32].indexOf(e.keyCode) !== -1) {
- // Left, Up, Right, Down, Escape, Space
- window.parent.postMessage({ type: 'revealKey', keyCode: e.keyCode },
'*');
- }
- });
- }
-});
-
-// Auto-height: when embedded in the book, measure our own content height and
post
-// it to the parent so it can size the iframe to fit (no inner scrollbar). This
-// demo is responsive (it fills the iframe width), so only the HEIGHT needs to
-// follow content. Push-based, so it catches our own DOM changes (editing the
-// layout, clicking a cell, switching presets) that an outside observer can
miss.
-(function () {
- if (window.parent === window) return;
- var lastH = 0;
- function report() {
- var b = document.body, de = document.documentElement;
- var h = (b ? b.scrollHeight : 0) || (de ? de.scrollHeight : 0) || 0;
- if (h && Math.abs(h - lastH) > 1) {
- lastH = h;
- window.parent.postMessage({ type: 'demoHeight', height: h }, '*');
- }
- }
- var scheduled = false;
- function schedule() {
- if (scheduled) return;
- scheduled = true;
- requestAnimationFrame(function () { scheduled = false; report(); });
- }
- try { new ResizeObserver(schedule).observe(document.documentElement); }
catch (e) {}
- try {
- new MutationObserver(schedule).observe(document.documentElement, {
- subtree: true, childList: true, attributes: true, characterData: true
- });
- } catch (e) {}
- document.addEventListener('DOMContentLoaded', schedule);
- window.addEventListener('load', schedule);
- window.addEventListener('click', function () { setTimeout(schedule, 0); },
true);
- [100, 300, 600, 1200].forEach(function (t) { setTimeout(schedule, t); });
-})();
diff --git a/atom.xml b/atom.xml
index 3ff8cc01cfe..872ca5c164f 100644
--- a/atom.xml
+++ b/atom.xml
@@ -4,7 +4,7 @@
<title>TVM</title>
<link href="https://tvm.apache.org" rel="self"/>
<link href="https://tvm.apache.org"/>
- <updated>2026-06-22T03:50:20+00:00</updated>
+ <updated>2026-06-22T03:55:23+00:00</updated>
<id>https://tvm.apache.org</id>
<author>
<name></name>
@@ -130,20 +130,9 @@ L(i,j)_{(8,16)} &amp;= L(i\cdot 16 + j)
&amp;&amp; \text{(flatten)}
<li>owners (×2 via replica): { warpid=6 laneid=12 }, { warpid=10
laneid=12 }</li>
</ul>
-<p><em>(Click element 57 in the interactive demo below to see
exactly these owners.)</em></p>
-
-<details>
-<summary>Unfold to see the interactive layout demo</summary>
-<iframe id="tirx-layout-demo"
src="/assets/tirx-layout-demo/index.html?preset=tensor-core&amp;notitle&amp;lock"
style="width:100%; height:560px; border:1px solid #dfe1e6;
border-radius:10px; margin:12px 0;" title="TIRx interactive layout
demo: tensor-core tile" loading="lazy"></iframe>
-<script>
-window.addEventListener('message', function (e) {
- var h = e.data && e.data.tirxLayoutDemoHeight;
- if (!h) return;
- var f = document.getElementById('tirx-layout-demo');
- if (f) f.style.height = h + 'px';
-});
-</script>
-</details>
+<p><em>(Open the interactive demo and click element 57 to see
exactly these owners.)</em></p>
+
+<p><a
href="https://mlc.ai/modern-gpu-programming-for-mlsys/_static/tirx-layout-demo/index.html?preset=tensor-core"
target="_blank" rel="noopener"
style="display:inline-block; padding:10px 18px; background:#3b82f6;
color:#fff !important; font-weight:700; border-radius:8px;
text-decoration:none;">▶ Open the interactive layout demo
↗</a></p>
<p>TIRx’s layout interface is built around four design choices.</p>
diff --git a/feed.xml b/feed.xml
index 18cc9a8338b..158f569915d 100644
--- a/feed.xml
+++ b/feed.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?><feed
xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/"
version="4.4.1">Jekyll</generator><link href="/feed.xml" rel="self"
type="application/atom+xml" /><link href="/" rel="alternate" type="text/html"
/><updated>2026-06-22T03:50:20+00:00</updated><id>/feed.xml</id><title
type="html">TVM</title><author><name>{"name" =>
nil}</name></author><entry><title type="html">TIRx: An Open Compiler Stack for
Evolving Fronti [...]
+<?xml version="1.0" encoding="utf-8"?><feed
xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/"
version="4.4.1">Jekyll</generator><link href="/feed.xml" rel="self"
type="application/atom+xml" /><link href="/" rel="alternate" type="text/html"
/><updated>2026-06-22T03:55:23+00:00</updated><id>/feed.xml</id><title
type="html">TVM</title><author><name>{"name" =>
nil}</name></author><entry><title type="html">TIRx: An Open Compiler Stack for
Evolving Fronti [...]
/* Theme has h3=38px but no h2 rule; size both down a notch and keep h2 > h3.
*/
.post-content h2 { font-size: 38px; line-height: 1.3; }
.post-content h3 { font-size: 30px; line-height: 1.3; }
@@ -111,20 +111,9 @@ L(i,j)_{(8,16)} &= L(i\cdot 16 + j) &&
\text{(flatten)} \\
<li>owners (×2 via replica): { warpid=6 laneid=12 }, { warpid=10 laneid=12
}</li>
</ul>
-<p><em>(Click element 57 in the interactive demo below to see exactly these
owners.)</em></p>
-
-<details>
-<summary>Unfold to see the interactive layout demo</summary>
-<iframe id="tirx-layout-demo"
src="/assets/tirx-layout-demo/index.html?preset=tensor-core&notitle&lock"
style="width:100%; height:560px; border:1px solid #dfe1e6; border-radius:10px;
margin:12px 0;" title="TIRx interactive layout demo: tensor-core tile"
loading="lazy"></iframe>
-<script>
-window.addEventListener('message', function (e) {
- var h = e.data && e.data.tirxLayoutDemoHeight;
- if (!h) return;
- var f = document.getElementById('tirx-layout-demo');
- if (f) f.style.height = h + 'px';
-});
-</script>
-</details>
+<p><em>(Open the interactive demo and click element 57 to see exactly these
owners.)</em></p>
+
+<p><a
href="https://mlc.ai/modern-gpu-programming-for-mlsys/_static/tirx-layout-demo/index.html?preset=tensor-core"
target="_blank" rel="noopener" style="display:inline-block; padding:10px 18px;
background:#3b82f6; color:#fff !important; font-weight:700; border-radius:8px;
text-decoration:none;">▶ Open the interactive layout demo ↗</a></p>
<p>TIRx’s layout interface is built around four design choices.</p>
diff --git a/rss.xml b/rss.xml
index 5cadcf703ee..793332f4a39 100644
--- a/rss.xml
+++ b/rss.xml
@@ -5,8 +5,8 @@
<description>TVM - </description>
<link>https://tvm.apache.org</link>
<atom:link href="https://tvm.apache.org" rel="self"
type="application/rss+xml" />
- <lastBuildDate>Mon, 22 Jun 2026 03:50:20 +0000</lastBuildDate>
- <pubDate>Mon, 22 Jun 2026 03:50:20 +0000</pubDate>
+ <lastBuildDate>Mon, 22 Jun 2026 03:55:23 +0000</lastBuildDate>
+ <pubDate>Mon, 22 Jun 2026 03:55:23 +0000</pubDate>
<ttl>60</ttl>
@@ -125,20 +125,9 @@ L(i,j)_{(8,16)} &amp;= L(i\cdot 16 + j)
&amp;&amp; \text{(flatten)}
<li>owners (×2 via replica): { warpid=6 laneid=12 }, { warpid=10
laneid=12 }</li>
</ul>
-<p><em>(Click element 57 in the interactive demo below to see
exactly these owners.)</em></p>
-
-<details>
-<summary>Unfold to see the interactive layout demo</summary>
-<iframe id="tirx-layout-demo"
src="/assets/tirx-layout-demo/index.html?preset=tensor-core&amp;notitle&amp;lock"
style="width:100%; height:560px; border:1px solid #dfe1e6;
border-radius:10px; margin:12px 0;" title="TIRx interactive layout
demo: tensor-core tile" loading="lazy"></iframe>
-<script>
-window.addEventListener('message', function (e) {
- var h = e.data && e.data.tirxLayoutDemoHeight;
- if (!h) return;
- var f = document.getElementById('tirx-layout-demo');
- if (f) f.style.height = h + 'px';
-});
-</script>
-</details>
+<p><em>(Open the interactive demo and click element 57 to see
exactly these owners.)</em></p>
+
+<p><a
href="https://mlc.ai/modern-gpu-programming-for-mlsys/_static/tirx-layout-demo/index.html?preset=tensor-core"
target="_blank" rel="noopener"
style="display:inline-block; padding:10px 18px; background:#3b82f6;
color:#fff !important; font-weight:700; border-radius:8px;
text-decoration:none;">▶ Open the interactive layout demo
↗</a></p>
<p>TIRx’s layout interface is built around four design choices.</p>