CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
320 lines
14 KiB
Markdown
320 lines
14 KiB
Markdown
# How to Add a New Instruction
|
|
|
|
This guide shows you how to add a new instruction from bottom (Assembly IR) to top (Logical IR).
|
|
|
|
**Note**: Python API is decoupled via a separate wrapper layer and is not directly affected by these changes.
|
|
|
|
**Instruction definitions and costs** use the [DEF_T system](#chapter-1-assembly-ir-hardware-layer): they live in `hardware/src/gfx/GfxXXX/GfxXXXInstructions.def` (and optionally `GfxXXXFormats.def` in the same folder), using `DEF_T` for standalone instructions or `DEF_BATCH` for groups sharing a format. Tablegen generates `*_init.inc`, `*_costs.inc`, and `*_operands.inc`; the architecture `.cpp`/`.hpp` files only include those. Do not add DEF_T or cost tables manually in the `.cpp`.
|
|
|
|
---
|
|
|
|
## Quick checklist: adding an instruction (as few edits as possible)
|
|
|
|
| Goal | What to do |
|
|
|------|------------|
|
|
| **Assembly-only instruction** | Add `DEF_T(Class, "mnemonic", .format = X, .flags = {...})` or an entry inside a `DEF_BATCH` in `hardware/src/gfx/GfxXXX/GfxXXXInstructions.def`. Rebuild. |
|
|
| **Group of related instructions** | Use `DEF_BATCH(.format = X, ...)` to share the format across entries. Each entry is `Class, "mnemonic"` with optional per-entry `.flags`, `.cost`, `.operand_fields`, `.logical`. |
|
|
| **Used from Logical IR / Rocisa** | Add `.logical = "LogicalName"` to the `DEF_T` / `DEF_BATCH` entry. Tablegen auto-generates the `setGfxXXXLogicalToArchMap()` — no manual `.cpp` edit needed. Also add an entry in `tools/tablegen/LogicalInstructionDefs.inc`. |
|
|
| **Non-default cost** | Add `.cost = {cycle, latency}` to the `DEF_T` or `DEF_BATCH` entry. No .cpp edit. |
|
|
| **Modifier-dependent cost** (e.g. matrix fmt) | Add `.costOverride = { { MatrixFmtModifiers(FP4, FP4), cycle, latency } }` alongside `.cost`. |
|
|
| **Operand requirements** (e.g. 4 SGPRs, 8 SGPRs) | Define `.fields` in the format (e.g., `DEF_FORMAT(TENSOR, .fields = { {S0, ..., sgpr, 128}, {S1, ..., sgpr, 256} })`) or override per-instruction via `.operand_fields = { {D0, .size=64} }`. Tablegen generates `*_operands.inc`. No manual .hpp edit. |
|
|
| **Dual-encoding operand override** | Add `.alt_operand_fields = { ... }` for the promoted encoding (e.g. VOP3 form of a VOP2 instruction). |
|
|
| **New flag or format** | Add flag in `include/stinkytofu/hardware/Flags.def` and/or format in `hardware/src/gfx/GfxXXX/GfxXXXFormats.def`; then use in DEF_T / DEF_BATCH. |
|
|
| **New instruction flag** | Add flag in `include/stinkytofu/hardware/Flags.def` and use in DEF_T / DEF_BATCH `.flags`. |
|
|
|
|
**Single place to add a normal instruction:** `hardware/src/gfx/GfxXXX/GfxXXXInstructions.def` — one `DEF_T` or one entry in an existing `DEF_BATCH`. Rebuild. If it must be visible to Logical IR, add `.logical = "Name"` in the same `.def` entry and add the Logical def in `LogicalInstructionDefs.inc`.
|
|
|
|
---
|
|
|
|
## Chapter 1: Assembly IR (Hardware Layer)
|
|
|
|
To add a new StinkyTofu assembly IR, you'll need to access the following files:
|
|
|
|
```bash
|
|
include/stinkytofu/hardware/Flags.def # Instruction flags (optional)
|
|
hardware/src/gfx/GfxXXX/GfxXXXInstructions.def # Instruction defs + costs + operands (DEF_T / DEF_BATCH)
|
|
hardware/src/gfx/GfxXXX/GfxXXXFormats.def # Format definitions (if new format needed)
|
|
hardware/src/gfx/GfxXXX/GfxXXX.cpp # Rocisa maps (only if not using .logical in .def)
|
|
```
|
|
|
|
Here we will add the instruction `s_wait_tensorcnt` step by step.
|
|
|
|
### Step 1. Add a flag (Optional)
|
|
|
|
Add a flag in `include/stinkytofu/hardware/Flags.def` for a new instruction type if needed.
|
|
|
|
```c++
|
|
MACRO(IF_WaitTensorCnt)
|
|
```
|
|
|
|
### Step 2. Use the flag in DEF_T (Optional)
|
|
|
|
If you added a new flag in Step 1, use it in the instruction's `.flags` in the .def file. Tablegen will emit `DEF_T("mnemonic", IF_WaitTensorCnt)` in the generated _init.inc. No separate struct needed.
|
|
|
|
**Note**: If your instruction is commutative (e.g., `v_add_f32` where `a+b = b+a`), mark it as such:
|
|
|
|
```c++
|
|
struct FloatAddInst : GfxInstDef
|
|
{
|
|
FloatAddInst()
|
|
{
|
|
hwInstDesc.flags.set(IF_Commutative); // Allow operand swapping in optimizer
|
|
}
|
|
};
|
|
```
|
|
|
|
### Step 3. Add definition and optional cost in the architecture .def file
|
|
|
|
`s_wait_tensorcnt` is a Gfx1250 feature. Add it to `hardware/src/gfx/Gfx1250/Gfx1250Instructions.def` (not in the .cpp).
|
|
|
|
Use `DEF_T(ClassName, "mnemonic", ...)` or add an entry inside a `DEF_BATCH(...)`. Both accept the same optional fields: `.format`, `.flags`, `.cost`, `.logical`, `.operand_fields`. The tablegen generates the init and cost tables; the .cpp only includes the generated files.
|
|
|
|
#### DEF_T — single instruction
|
|
|
|
Example (minimal — uses arch default cost). Use a flag that exists in `include/stinkytofu/hardware/Flags.def` (without the `IF_` prefix, e.g. `WaitTensorCnt`):
|
|
|
|
```c
|
|
DEF_T(SWaitTensorcntInst, "s_wait_tensorcnt",
|
|
.format = SOPP,
|
|
.flags = {WaitTensorCnt},
|
|
.logical = "SWaitTensorcnt"
|
|
)
|
|
```
|
|
|
|
Example with explicit cost (cycle, latency):
|
|
|
|
```c
|
|
DEF_T(SWaitTensorcntInst, "s_wait_tensorcnt",
|
|
.format = SOPP,
|
|
.flags = {WaitTensorCnt},
|
|
.cost = {1, 1},
|
|
.logical = "SWaitTensorcnt"
|
|
)
|
|
```
|
|
|
|
#### DEF_BATCH — group of instructions sharing a format
|
|
|
|
When several instructions share the same format, use `DEF_BATCH` to avoid repeating `.format` on every entry. Shared header fields (`.format`, `.flags`, etc.) are inherited by all entries; per-entry fields override or extend them (flags are additive).
|
|
|
|
```c
|
|
DEF_BATCH(.format = SOPP_WAIT16,
|
|
SWaitLoadcntInst, "s_wait_loadcnt", .flags = {WaitCnt},
|
|
SWaitStorecntInst, "s_wait_storecnt", .flags = {WaitCnt},
|
|
SWaitTensorcntInst, "s_wait_tensorcnt", .flags = {WaitTensorCnt}, .logical = "SWaitTensorcnt",
|
|
SWaitAluInst, "s_wait_alu", .flags = {HasSideEffect},
|
|
.operand_fields = { {S0, .type=wait_alu} },
|
|
)
|
|
```
|
|
|
|
Each entry is: `ClassName, "mnemonic"` followed by optional per-entry fields (`.flags`, `.cost`, `.logical`, `.operand_fields`, `.costOverride`). Entries must **not** redefine `.format` — that comes from the batch header.
|
|
|
|
Real-world example with load instructions, costs, and operand overrides:
|
|
|
|
```c
|
|
DEF_BATCH(.format = MUBUF_LOAD,
|
|
BufferLoadU8Inst, "buffer_load_u8", .cost = {12, 108}, .logical = "BufferLoadU8",
|
|
BufferLoadB32Inst, "buffer_load_b32", .cost = {12, 108}, .logical = "BufferLoadB32",
|
|
BufferLoadB128Inst, "buffer_load_b128", .cost = {12, 116}, .logical = "BufferLoadB128",
|
|
.operand_fields = { {D0, .size=128} },
|
|
)
|
|
```
|
|
|
|
Then rebuild so tablegen runs (e.g. `cmake --build .`). See [Architecture Overview](architecture.md) for full syntax and format inheritance.
|
|
|
|
### Step 4. Rocisa LogicalToArch mapping (automatic via `.logical`)
|
|
|
|
If you added `.logical = "LogicalName"` to the `DEF_T` or `DEF_BATCH` entry in Step 3, the tablegen **auto-generates** the `setGfxXXXLogicalToArchMap()` function. No manual `.cpp` edit is needed.
|
|
|
|
Multiple logical names can map to the same mnemonic:
|
|
|
|
```c
|
|
SMovB32Inst, "s_mov_b32", .logical = {"SMovB32", "SSetMask"},
|
|
```
|
|
|
|
If you do **not** use `.logical` in the `.def`, you can still add the mapping manually in `hardware/src/gfx/Gfx1250/Gfx1250.cpp` inside `setGfx1250LogicalToArchMap()`:
|
|
|
|
```c++
|
|
{"SWaitTensorcnt", "s_wait_tensorcnt"},
|
|
```
|
|
|
|
Prefer `.logical` in the `.def` — it keeps the mapping next to the definition.
|
|
|
|
---
|
|
|
|
## Chapter 2: Logical IR (High-Level IR Layer)
|
|
|
|
### Step 5: Add Logical IR Definition
|
|
|
|
Add to `shared/stinkytofu/tools/tablegen/LogicalInstructionDefs.inc`:
|
|
|
|
```c++
|
|
{"SWaitTensorcnt", // className
|
|
"s_wait_tensorcnt", // mnemonic (must match assembly)
|
|
"Wait for tensor operations to complete", // comment
|
|
0, // numSrcs (no source operands)
|
|
false, // hasDest (no destination)
|
|
"Synchronization", // category
|
|
false, // supportsDPP
|
|
false, // supportsSDWA
|
|
false, // hasDS
|
|
false}, // isCommutative
|
|
```
|
|
|
|
### Step 6: Add Hardware Metadata (Costs + Operand Requirements)
|
|
|
|
#### 6a. Instruction Costs
|
|
|
|
Costs are defined in the **architecture .def file** and **arch.cmake**, not in the .cpp.
|
|
|
|
- **Default cost**: Each arch sets a default in `arch.cmake` (ARCH_DEFAULT_CYCLE, ARCH_DEFAULT_LATENCY). E.g. Gfx1250 uses 1,1. Instructions without `.cost` in the .def use that default.
|
|
- **Override**: In `hardware/src/gfx/GfxXXX/GfxXXXInstructions.def`, add `.cost = {cycle, latency}` to the instruction's `DEF_T(...)` or `DEF_BATCH` entry. Tablegen emits only non-default costs into `GfxXXX_costs.inc`; the generated block applies them.
|
|
|
|
Example — standalone `DEF_T` with cost:
|
|
|
|
```c
|
|
DEF_T(WaitTensorCntInst, "s_wait_tensorcnt",
|
|
.format = SOPP,
|
|
.flags = {SALU},
|
|
.cost = {1, 1}
|
|
)
|
|
```
|
|
|
|
Example — inside a `DEF_BATCH`:
|
|
|
|
```c
|
|
DEF_BATCH(.format = MUBUF_LOAD,
|
|
BufferLoadU8Inst, "buffer_load_u8", .cost = {12, 108}, .logical = "BufferLoadU8",
|
|
BufferLoadB32Inst, "buffer_load_b32", .cost = {12, 108}, .logical = "BufferLoadB32",
|
|
)
|
|
```
|
|
|
|
##### Modifier-dependent cost overrides (`.costOverride`)
|
|
|
|
Matrix instructions can have different costs depending on the data format. Use `.costOverride` to specify alternate `{cycle, latency}` for specific format combinations:
|
|
|
|
```c
|
|
DEF_T(VWmmaF3216x16x128F8f6f4Inst, "v_wmma_f32_16x16x128_f8f6f4",
|
|
.format = WMMA,
|
|
.cost = {5, 8},
|
|
.costOverride = { { MatrixFmtModifiers(FP4, FP4), 3, 4 } },
|
|
.operand_fields = { {S0, .size=512}, {S1, .size=512} }
|
|
)
|
|
```
|
|
|
|
Inside a `DEF_BATCH`:
|
|
|
|
```c
|
|
DEF_BATCH(.format = MXWMMA_SCALE16,
|
|
VWmmaScale16F3216x16x128F8f6f4Inst, "v_wmma_scale16_f32_16x16x128_f8f6f4",
|
|
.cost = {6, 8}, .costOverride = { { MatrixFmtModifiers(FP4, FP4), 4, 4 } },
|
|
)
|
|
```
|
|
|
|
Do **not** add cost entries to any array in `GfxXXX.cpp`; they are generated from the .def. See [Architecture Overview](architecture.md).
|
|
|
|
#### 6b. Add Operand Requirements (Optional)
|
|
|
|
If your instruction has specific register width or type requirements (e.g., `tensor_load_to_lds` requires 4 SGPRs for src0, 8 SGPRs for src1), define them via format-level `.fields` in `GfxXXXFormats.def` or per-instruction `.operand_fields` overrides in `GfxXXXInstructions.def`. Tablegen generates `GfxXXX_operands.inc`; each `GfxXXX.hpp` already includes it and applies requirements to the MCID table. **You do not edit the .hpp.**
|
|
|
|
Example — format-level fields in `Gfx1250Formats.def` (applies to all instructions using this format):
|
|
|
|
```c
|
|
DEF_FORMAT(TENSOR,
|
|
.unit = TensorUnit,
|
|
.encoding = {64},
|
|
.maxOperands = 4,
|
|
.fields = {
|
|
{S0, vaddr0, sgpr_srg0, sgpr, 128},
|
|
{S1, vaddr1, sgpr_srg1, sgpr, 256},
|
|
{S2, vaddr2, sgpr_srg2, sreg, 128},
|
|
{S3, vaddr3, sgpr_addr, sreg, 128},
|
|
},
|
|
)
|
|
```
|
|
|
|
Example — per-instruction override with `DEF_T`:
|
|
|
|
```c
|
|
DEF_T(BufferLoadB128Inst, "buffer_load_b128",
|
|
.format = MUBUF_LOAD,
|
|
.operand_fields = { {D0, .size=128} }
|
|
)
|
|
```
|
|
|
|
Example — per-instruction override inside `DEF_BATCH`:
|
|
|
|
```c
|
|
DEF_BATCH(.format = MUBUF_LOAD,
|
|
BufferLoadB32Inst, "buffer_load_b32", .cost = {12, 108}, .logical = "BufferLoadB32",
|
|
BufferLoadB128Inst, "buffer_load_b128", .cost = {12, 116}, .logical = "BufferLoadB128",
|
|
.operand_fields = { {D0, .size=128} },
|
|
)
|
|
```
|
|
|
|
##### Dual-encoding operand overrides (`.alt_operand_fields`)
|
|
|
|
Instructions with dual encodings (e.g. VOP2 primary + VOP3 promoted) may need different operand layouts for each encoding. Use `.alt_operand_fields` for the promoted encoding:
|
|
|
|
```c
|
|
DEF_T(VCndmaskB32Inst, "v_cndmask_b32", .format = VOP2, .logical = "VCndMaskB32",
|
|
.operand_fields = {
|
|
{D0, vdst, vgpr, 32},
|
|
{S0, src0, src, 32},
|
|
{S1, vsrc1, vgpr, 32},
|
|
{S2, src2, vcc, 64},
|
|
},
|
|
.alt_operand_fields = {
|
|
{D0, vdst, vgpr, 32},
|
|
{S0, src0, src, 32},
|
|
{S1, src1, src, 32},
|
|
{S2, src2, sreg, 64},
|
|
},
|
|
)
|
|
```
|
|
|
|
**When to add**: Instruction requires non-default register sizes or types for the IR verifier. Single-register instructions (32-bit) usually need no override.
|
|
|
|
### Step 7: Run Tablegen
|
|
|
|
Regenerate IR classes and bindings:
|
|
|
|
```bash
|
|
cd build
|
|
cmake ..
|
|
make tablegen_generated
|
|
```
|
|
|
|
This auto-generates:
|
|
- `LogicalInstructions_generated.hpp` - C++ IR classes
|
|
- `LogicalOpcodes_generated.inc` - Opcode enums
|
|
- `StinkyBuilder_*.inc` - C++ builder methods
|
|
- `IRMnemonics_generated.inc` - Mnemonic -> ASM mappings
|
|
- `RocisaGfx1250Mappings.inc` - Rocisa -> ASM opcode mappings
|
|
|
|
The mnemonic mapping automatically enables Logical IR -> Assembly IR conversion.
|
|
|
|
---
|
|
|
|
## Quick Reference: Instruction Metadata Locations
|
|
|
|
Instruction **definitions** and **costs** live in `.def` files; tablegen generates `.inc` files that the `.cpp` includes. Rocisa mappings stay in the `.cpp`.
|
|
|
|
### Per-architecture layout
|
|
|
|
| Metadata Type | Location | What to Modify |
|
|
|---------------|----------|----------------|
|
|
| **Definitions + costs** (DEF_T / DEF_BATCH) | `hardware/src/gfx/GfxXXX/GfxXXXInstructions.def` | Add `DEF_T(...)` or entry in `DEF_BATCH(...)` with `.format`, `.flags`, `.cost`, `.logical` |
|
|
| **Formats** (optional) | `hardware/src/gfx/GfxXXX/GfxXXXFormats.def` | Add `DEF_FORMAT` if needed |
|
|
| **Generated** (do not edit) | `hardware/generated/GfxXXX_init.inc`, `GfxXXX_costs.inc`, `GfxXXX_operands.inc` | Produced by tablegen from .def |
|
|
| **Rocisa LogicalToArch map** | Auto-generated from `.logical` in .def (or manually in `GfxXXX.cpp`) | Prefer `.logical = "Name"` in the DEF_T / DEF_BATCH entry |
|
|
| **Operand requirements** | `GfxXXXFormats.def` / `GfxXXXInstructions.def` | Define `.fields` in format or `.operand_fields` / `.alt_operand_fields` per-instruction; tablegen -> `*_operands.inc` (included by GfxXXX.hpp) |
|
|
| **Modifier-dependent costs** | `GfxXXXInstructions.def` | `.costOverride = { { MatrixFmtModifiers(...), cycle, latency } }` alongside `.cost` |
|
|
|
|
### Gfx1250
|
|
|
|
Edit `hardware/src/gfx/Gfx1250/Gfx1250Instructions.def` (and optionally `Gfx1250Formats.def` in same folder), then rebuild. Use `DEF_T` for standalone instructions or `DEF_BATCH` for groups sharing a format. Add `.logical` in the `.def` to auto-generate the Rocisa LogicalToArch map. The corresponding `Gfx1250.cpp` only holds manually-added Rocisa maps if needed. Instruction definitions and costs come from the .def files and generated .inc files.
|
|
|
|
### See also
|
|
|
|
- [Architecture Overview](architecture.md) -- design and data flow for .def -> tablegen -> .inc.
|