Files
TheRock/rocm-libraries/shared/stinkytofu/docs/developer/adding-peephole-patterns.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

9.5 KiB

How to Add Peephole Optimization Patterns

This guide shows you how to add new optimization patterns to the StinkyTofu compiler.

Quick Start

Three steps:

  1. Write pattern in '.pattern' file
  2. Run 'make'
  3. Done!

Example: Adding a Multiply-Add Fusion

Step 1: Write the Pattern

Edit 'src/transforms/asm/PeepholePatterns.pattern':

''' //===----------------------------------------------------------------------===// // Pattern: Multiply-Add Fusion (F32) // // Transforms: // v_mul_f32 v0, a, b // v_add_f32 v2, v0, c // Into: // v_fma_f32 v2, a, b, c //===----------------------------------------------------------------------===//

pattern MulAddFusion_F32 { match { $mul = v_mul_f32 $tmp, $a, $b $add = v_add_f32 $dst, $tmp, $c }

constraints { HasOneUse($tmp) }

rewrite { $new_const = AddConstants($fma_const, $add_const) // Pattern system automatically handles the transformation } } '''

Step 2: Build

'''bash cd build make -j$(nproc) '''

What happens:

  • Pattern is parsed
  • C++ matcher code is generated
  • Pattern is automatically registered
  • Pass will try your pattern on every instruction

Step 3: Test

Your pattern is now active! It will match and transform automatically.

''' [PeepholePass] Loaded 3 pattern(s) [Peephole] Applied MulAddFusion_F32 '''


Pattern Syntax

Basic Structure

''' pattern PatternName { match { // Instructions to match }

constraints { // Conditions that must be true }

rewrite { // How to transform (currently automatic) } } '''

Match Section

Syntax: ''' $instruction_var = opcode $dest, $src1, $src2, ... '''

Rules:

  • First operand is always the destination register
  • Pattern variables start with '$'
  • Instructions are matched in temporal order (first executed first)

Example: ''' match { // First: FMA instruction $fma = v_fma_f32 $result, $a, $b, $const1

// Second: ADD instruction using FMA result $add = v_add_f32 $output, $const2, $result } '''

Constraints Section

Available constraints:

Constraint Meaning Example
'HasOneUse($reg)' Register used exactly once 'HasOneUse($tmp)'
'IsConstant($reg)' Register is a literal 'IsConstant($const)'
'SameValue($r1, $r2)' Registers have same value 'SameValue($a, $b)'
'DifferentValue($r1, $r2)' Registers differ 'DifferentValue($x, $y)'

Example: ''' constraints { HasOneUse($tmp) // $tmp only used by next instruction IsConstant($c1) // $c1 must be a literal value IsConstant($c2) // $c2 must be a literal value } '''

Rewrite Section

Currently, rewrites are automatic for fusion patterns:

  • Constants are folded
  • Instructions are modified in-place
  • Dead instructions are removed

Built-in constant folding: ''' rewrite { $new = AddConstants($c1, $c2) // c1 + c2 $new = SubConstants($c1, $c2) // c1 - c2 $new = MulConstants($c1, $c2) // c1 * c2 $new = NegateConstant($c) // -c } '''


Complete Examples

Example 1: Add+FMA Fusion (F32)

Before: '''asm v_fma_f32 v0, a, b, 1.0 v_add_f32 v2, 1.0, v0 '''

After: '''asm v_fma_f32 v2, a, b, 2.0 # v0 eliminated! '''

Pattern: ''' pattern AddFMAFusion_F32 { match { $fma = v_fma_f32 $fma_result, $a, $b, $fma_const $add = v_add_f32 $add_dst, $add_const, $fma_result }

constraints { HasOneUse($fma_result) IsConstant($fma_const) IsConstant($add_const) }

rewrite { $new_const = AddConstants($fma_const, $add_const) } } '''

Example 2: Add+FMA Fusion (F16)

Before: '''asm v_fma_f16 v0, a, b, 0.5 v_add_f16 v1, 0.5, v0 '''

After: '''asm v_fma_f16 v1, a, b, 1.0 '''

Pattern: ''' pattern AddFMAFusion_F16 { match { $fma = v_fma_f16 $fma_result, $a, $b, $fma_const $add = v_add_f16 $add_dst, $add_const, $fma_result }

constraints { HasOneUse($fma_result) IsConstant($fma_const) IsConstant($add_const) }

rewrite { $new_const = AddConstants($fma_const, $add_const) } } '''


Tips & Best Practices

1. Always Use 'HasOneUse' for Intermediate Values

''' [x] Good: constraints { HasOneUse($tmp) // Safe to eliminate $tmp }

[ ] Bad: // Missing HasOneUse - might eliminate value that's used elsewhere! '''

2. Check for Constants When Folding

''' [x] Good: constraints { IsConstant($c1) IsConstant($c2) } rewrite { $result = AddConstants($c1, $c2) }

[ ] Bad: // Missing IsConstant - can't fold non-constant values! '''

3. Name Variables Descriptively

''' [x] Good: $fma_inst = v_fma_f32 $fma_result, $a, $b, $const

[ ] Bad: $i1 = v_fma_f32 $r1, $r2, $r3, $r4 '''

4. Add Comments Explaining the Optimization

''' //===----------------------------------------------------------------------===// // Pattern: NegateNegate // // Eliminates double negation: -(-x) => x // Benefit: Eliminates 2 instructions, frees registers //===----------------------------------------------------------------------===// '''

5. Order Patterns by Specificity

More specific patterns should come before general ones: ''' // Specific: mul by constant 1.0 pattern MulByOne { ... }

// General: any multiplication pattern MulFusion { ... } '''

6. Patterns Work Across Non-Consecutive Instructions

The pattern system uses def-use analysis, so patterns don't require strict consecutiveness:

''' [x] This works fine: v_fma_f32 v0, v1, v2, 1.0 v_mul_f32 v5, v6, v7 // Intervening instruction v_add_f32 v0, 1.0, v0 // Still matches and fuses! '''

The pattern matcher automatically verifies that:

  • Dependencies are correct (FMA comes before ADD)
  • Intermediate registers haven't been overwritten

''' [ ] This correctly doesn't match: v_fma_f32 v0, v1, v2, 1.0 v_mul_f32 v0, v6, v7 // Overwrites v0! v_add_f32 v3, 1.0, v0 // Uses v0 from v_mul, not v_fma '''

7. In-Place Operations Are Handled Correctly

Patterns work even when destination == source:

''' [x] In-place fusion works: v_fma_f32 v0, v1, v2, 1.0 // Defines v0 v_add_f32 v0, 1.0, v0 // Uses v0, writes v0 => v_fma_f32 v0, v1, v2, 2.0 // Correctly fused! '''

The pattern system uses position-aware def-use analysis to find the correct previous definition.


Testing Your Pattern

1. Write Test IR

Create test input: '''asm v_fma_f32 v0, a, b, 1.0 v_add_f32 v2, 1.0, v0 '''

2. Run the Pass

'''cpp auto pass = createPeepholeOptimizationPass(); pass->run(module); '''

3. Check Output

Expected output: '''asm v_fma_f32 v2, a, b, 2.0 '''

Console should show: ''' [PeepholePass] Loaded N pattern(s) [Peephole] Applied YourPatternName '''


Troubleshooting

Pattern Not Matching?

Check:

  1. Instruction opcodes are correct (e.g., 'v_fma_f32', not 'v_fma')
  2. Operand order is correct (destination first)
  3. Constraints are satisfied
  4. Instructions appear in the right order

Debug: '''bash

Check generated code

cat build/PeepholePatterns.inc | grep "YourPatternName"

Verbose build

make VERBOSE=1 | grep pattern '''

Build Errors?

Common issues:

  • Syntax error in '.pattern' file -> Check pattern grammar
  • Undefined opcode -> Verify opcode exists in GFX ISA
  • Missing constraint -> Add required constraints

Fix: '''bash

Clean rebuild

cd build rm PeepholePatterns.inc make tablegen make '''


What's Automatic

You don't need to:

  • Write C++ matcher code
  • Write C++ rewrite code
  • Register patterns manually
  • Update the pass
  • Handle def-use analysis
  • Remove dead instructions

The system handles all of this automatically! [x]


What You Control

You do control:

  • Which instructions to match
  • What constraints to check
  • How constants are folded
  • Pattern priority (order in file)

Advanced: Pattern Variants

Same Pattern for Multiple Types

Create separate patterns for each type: ''' pattern AddFMAFusion_F32 { ... v_fma_f32 ... } pattern AddFMAFusion_F16 { ... v_fma_f16 ... } pattern AddFMAFusion_F64 { ... v_fma_f64 ... } '''

Pattern Families

Group related patterns: ''' //===----------------------------------------------------------------------===// // FMA Fusion Family //===----------------------------------------------------------------------===//

pattern AddFMAFusion_F32 { ... } pattern MulAddFusion_F32 { ... } pattern SubFMAFusion_F32 { ... } '''


File Locations

File Purpose
'src/transforms/asm/PeepholePatterns.pattern' Your patterns go here
'build/PeepholePatterns.inc' Generated C++ code (auto)
'src/transforms/asm/PeepholeOptimizationPass.cpp' Generic pass (never edit)
'tools/tablegen/GenPatterns.cpp' Code generator (rarely edit)

Summary

To add a pattern:

  1. Edit 'PeepholePatterns.pattern'
  2. Run 'make'
  3. Done!

Pattern structure: ''' pattern Name { match { ... } // Instructions to find constraints { ... } // Conditions to check rewrite { ... } // How to transform } '''

The system automatically:

  • Parses your pattern
  • Generates efficient C++ code
  • Registers your pattern
  • Applies it during compilation

You just write the pattern! !


Next Steps


Questions?

Common patterns are already implemented. Just copy and modify them for your needs!