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
224 lines
4.9 KiB
Markdown
224 lines
4.9 KiB
Markdown
# Adding Intrinsics
|
|
|
|
This guide shows you how to add new intrinsics to StinkyTofu.
|
|
|
|
## What is an Intrinsic?
|
|
|
|
An intrinsic is a high-level operation that expands into multiple instructions. Examples:
|
|
- `ReluF32`: Implements ReLU activation (max(x, 0))
|
|
- `ClampF32`: Clamps values to a range [min, max]
|
|
- `SigmoidF32`: Applies sigmoid activation function
|
|
|
|
Intrinsics are automatically available throughout StinkyTofu via the `IntrinsicRegistry`.
|
|
|
|
## Quick Start
|
|
|
|
### Step 1: Define Your Intrinsic
|
|
|
|
Edit `src/ir/logical/Intrinsics.intrinsic`:
|
|
|
|
```
|
|
intrinsic ReluF32 {
|
|
arguments {
|
|
dest: vgpr_f32
|
|
src: vgpr_f32
|
|
temp: vgpr_f32
|
|
}
|
|
|
|
body {
|
|
v[temp] = v_cmp_gt_f32(v[src], 0.0)
|
|
v[dest] = v_cndmask_b32(0.0, v[src], v[temp])
|
|
}
|
|
|
|
comment "ReLU F32 activation"
|
|
python_binding true
|
|
}
|
|
```
|
|
|
|
### Step 2: Rebuild
|
|
|
|
```bash
|
|
cd build
|
|
ninja
|
|
```
|
|
|
|
The build system automatically:
|
|
1. Parses `Intrinsics.intrinsic`
|
|
2. Compiles it to `intrinsics.st.bc` (binary format)
|
|
3. Makes it available to the runtime
|
|
|
|
### Step 3: Use Your Intrinsic
|
|
|
|
In TensileLite or any code generator:
|
|
|
|
```cpp
|
|
#include "stinkytofu/ir/logical/IntrinsicRegistry.hpp"
|
|
|
|
auto& intrinsics = IntrinsicRegistry::instance();
|
|
|
|
if (intrinsics.hasIntrinsic("ReluF32")) {
|
|
auto pattern = intrinsics.lookup("ReluF32");
|
|
// Use pattern to generate code
|
|
}
|
|
```
|
|
|
|
## Intrinsic Definition Syntax
|
|
|
|
### Basic Structure
|
|
|
|
```
|
|
intrinsic <Name> {
|
|
arguments { ... }
|
|
body { ... }
|
|
comment "..."
|
|
python_binding true/false
|
|
}
|
|
```
|
|
|
|
### Arguments Block
|
|
|
|
List all registers needed, including temporaries:
|
|
|
|
```
|
|
arguments {
|
|
dest: vgpr_f32 // Output register
|
|
src: vgpr_f32 // Input register
|
|
temp: vgpr_f32 // Temporary register
|
|
min_val: vgpr_f32 // Another input
|
|
}
|
|
```
|
|
|
|
**Important:** You must explicitly list temporary registers. StinkyTofu does not use SSA form, so register allocation happens before intrinsic expansion.
|
|
|
|
### Body Block
|
|
|
|
Define the instruction sequence using high-level IR operations:
|
|
|
|
```
|
|
body {
|
|
v[temp] = v_max_f32(v[src], v[min_val])
|
|
v[dest] = v_min_f32(v[temp], v[max_val])
|
|
}
|
|
```
|
|
|
|
**Syntax:**
|
|
- Register reference: `v[name]` for VGPR, `s[name]` for SGPR
|
|
- Operation: `operation(operands...)`
|
|
- Assignment: `dest = operation(...)`
|
|
|
|
### Metadata
|
|
|
|
```
|
|
comment "Brief description of what this intrinsic does"
|
|
python_binding true // Generate Python API (future feature)
|
|
```
|
|
|
|
## Examples
|
|
|
|
### Simple Intrinsic (ReLU)
|
|
|
|
```
|
|
intrinsic ReluF32 {
|
|
arguments {
|
|
dest: vgpr_f32
|
|
src: vgpr_f32
|
|
temp: vgpr_f32
|
|
}
|
|
body {
|
|
v[temp] = v_cmp_gt_f32(v[src], 0.0)
|
|
v[dest] = v_cndmask_b32(0.0, v[src], v[temp])
|
|
}
|
|
comment "ReLU F32 activation"
|
|
python_binding true
|
|
}
|
|
```
|
|
|
|
### Complex Intrinsic (Clamp)
|
|
|
|
```
|
|
intrinsic ClampF32 {
|
|
arguments {
|
|
dest: vgpr_f32
|
|
src: vgpr_f32
|
|
min_val: vgpr_f32
|
|
max_val: vgpr_f32
|
|
temp: vgpr_f32
|
|
}
|
|
body {
|
|
v[temp] = v_max_f32(v[src], v[min_val])
|
|
v[dest] = v_min_f32(v[temp], v[max_val])
|
|
}
|
|
comment "Clamp F32 to range [min, max]"
|
|
python_binding true
|
|
}
|
|
```
|
|
|
|
## Testing Your Intrinsic
|
|
|
|
### Verify Compilation
|
|
|
|
```bash
|
|
cd build
|
|
./tools/intrinsic-compiler/intrinsic-compiler
|
|
# Should output: Compiled 5 intrinsics to intrinsics.st.bc (1117 bytes)
|
|
```
|
|
|
|
### Test Loading
|
|
|
|
```bash
|
|
./tools/intrinsic-compiler/demo_auto_load
|
|
# Should show your intrinsic in the list
|
|
```
|
|
|
|
### Add Unit Test
|
|
|
|
Edit `tests/unit/IntrinsicFlowTest.cpp`:
|
|
|
|
```cpp
|
|
TEST_F(IntrinsicFlowTest, MyNewIntrinsic) {
|
|
auto& registry = IntrinsicRegistry::instance();
|
|
ASSERT_TRUE(registry.hasIntrinsic("MyNewIntrinsic"));
|
|
|
|
auto pattern = registry.lookup("MyNewIntrinsic");
|
|
ASSERT_NE(pattern, nullptr);
|
|
EXPECT_EQ(pattern->arguments.size(), 3);
|
|
EXPECT_EQ(pattern->body.size(), 2);
|
|
}
|
|
```
|
|
|
|
## Tips
|
|
|
|
? **DO:**
|
|
- Use clear, descriptive names (`ReluF32`, not `r32`)
|
|
- List all temporaries in `arguments`
|
|
- Keep intrinsics focused (single operation)
|
|
- Add comments explaining what it does
|
|
|
|
? **DON'T:**
|
|
- Use SSA form (StinkyTofu isn't SSA-based)
|
|
- Assume register allocation happens during expansion
|
|
- Create very large intrinsics (consider breaking them up)
|
|
- Use assembly syntax (use high-level IR operations)
|
|
|
|
## Troubleshooting
|
|
|
|
**Intrinsic not found at runtime:**
|
|
- Rebuild: `ninja` in the build directory
|
|
- Check `intrinsics.st.bc` was generated
|
|
- Verify syntax in `Intrinsics.intrinsic`
|
|
|
|
**Parse errors:**
|
|
- Check matching braces `{ }`
|
|
- Ensure `arguments` and `body` are present
|
|
- Verify register syntax: `v[name]` or `s[name]`
|
|
|
|
**Build errors:**
|
|
- Run `ninja clean && ninja` to rebuild from scratch
|
|
- Check the TableGen output for syntax errors
|
|
|
|
## See Also
|
|
|
|
- [Architecture Overview](architecture.md) - System architecture and intrinsic system overview
|
|
- [IntrinsicRegistry API](../../include/stinkytofu/ir/logical/IntrinsicRegistry.hpp) - Runtime API
|
|
- Example intrinsics: `src/ir/logical/Intrinsics.intrinsic`
|