Files
TheRock/rocm-libraries/shared/stinkytofu/docs/developer/adding-intrinsics.md
T
derek.holloway 7502bf64a4
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
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

4.9 KiB

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

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:

#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

cd build
./tools/intrinsic-compiler/intrinsic-compiler
# Should output: Compiled 5 intrinsics to intrinsics.st.bc (1117 bytes)

Test Loading

./tools/intrinsic-compiler/demo_auto_load
# Should show your intrinsic in the list

Add Unit Test

Edit tests/unit/IntrinsicFlowTest.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