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
705 lines
12 KiB
Markdown
705 lines
12 KiB
Markdown
# Pattern Grammar Reference
|
|
|
|
Complete syntax reference for writing peephole optimization patterns.
|
|
|
|
## Quick Reference
|
|
|
|
### Pattern Structure
|
|
|
|
'''
|
|
pattern PatternName {
|
|
match {
|
|
// Instructions to match
|
|
}
|
|
constraints {
|
|
// Conditions to check
|
|
}
|
|
rewrite {
|
|
// How to transform
|
|
}
|
|
}
|
|
'''
|
|
|
|
### Variable Syntax
|
|
|
|
- '$variable' - Pattern variable (starts with '$')
|
|
- '$inst' - Instruction variable (represents the instruction itself)
|
|
- '$reg' - Register/operand variable
|
|
|
|
---
|
|
|
|
## Match Block
|
|
|
|
### Syntax
|
|
|
|
'''
|
|
match {
|
|
$inst_var = opcode $dest, $src1, $src2, ...
|
|
}
|
|
'''
|
|
|
|
### Rules
|
|
|
|
1. **First operand is destination** (always)
|
|
2. **Variables start with '$'**
|
|
3. **Instructions listed in temporal order** (first executed -> last executed)
|
|
|
|
### Examples
|
|
|
|
#### Single Instruction Match
|
|
|
|
'''
|
|
match {
|
|
$mov = v_mov_b32 $dst, $src
|
|
}
|
|
'''
|
|
|
|
#### Two Instruction Match (Data Flow)
|
|
|
|
'''
|
|
match {
|
|
$mul = v_mul_f32 $tmp, $a, $b // First: multiply
|
|
$add = v_add_f32 $dst, $tmp, $c // Second: add (uses $tmp)
|
|
}
|
|
'''
|
|
|
|
#### With Constants
|
|
|
|
'''
|
|
match {
|
|
$fma = v_fma_f32 $result, $a, $b, $const // $const can be literal
|
|
$add = v_add_f32 $dst, 1.0, $result // Or hardcoded value
|
|
}
|
|
'''
|
|
|
|
### Variable Types
|
|
|
|
| Variable | Represents | Example |
|
|
|----------|------------|---------|
|
|
| '$inst' | The instruction itself | '$fma', '$add_inst' |
|
|
| '$reg' | Register or operand | '$tmp', '$result', '$a' |
|
|
| '$const' | Constant operand | '$c1', '$fma_const' |
|
|
|
|
**Note:** All variables are just names - the system figures out what they represent from context.
|
|
|
|
---
|
|
|
|
## Constraints Block
|
|
|
|
### Syntax
|
|
|
|
'''
|
|
constraints {
|
|
ConstraintName($arg1)
|
|
ConstraintName($arg1, $arg2)
|
|
}
|
|
'''
|
|
|
|
### Available Constraints
|
|
|
|
#### 'HasOneUse($register)'
|
|
|
|
Check if register has exactly one use.
|
|
|
|
**Use case:** Safe to eliminate intermediate result.
|
|
|
|
'''
|
|
constraints {
|
|
HasOneUse($tmp) // $tmp only used once
|
|
}
|
|
'''
|
|
|
|
**Example:**
|
|
'''
|
|
match {
|
|
$mul = v_mul_f32 $tmp, $a, $b
|
|
$add = v_add_f32 $dst, $tmp, $c
|
|
}
|
|
constraints {
|
|
HasOneUse($tmp) // $tmp only used by $add
|
|
}
|
|
'''
|
|
|
|
#### 'IsConstant($operand)'
|
|
|
|
Check if operand is a constant (not a register).
|
|
|
|
**Use case:** Required for constant folding.
|
|
|
|
'''
|
|
constraints {
|
|
IsConstant($c1) // Must be literal value
|
|
}
|
|
'''
|
|
|
|
**Example:**
|
|
'''
|
|
match {
|
|
$fma = v_fma_f32 $result, $a, $b, $c
|
|
}
|
|
constraints {
|
|
IsConstant($c) // Ensure $c is a constant
|
|
}
|
|
'''
|
|
|
|
#### 'SameValue($reg1, $reg2)'
|
|
|
|
Check if two operands are the same register.
|
|
|
|
**Use case:** Detect in-place operations.
|
|
|
|
'''
|
|
constraints {
|
|
SameValue($dst, $src) // In-place: dst = dst + x
|
|
}
|
|
'''
|
|
|
|
#### 'DifferentValue($reg1, $reg2)'
|
|
|
|
Check if two operands are different registers.
|
|
|
|
**Use case:** Avoid incorrect transformations.
|
|
|
|
'''
|
|
constraints {
|
|
DifferentValue($a, $b) // Ensure different regs
|
|
}
|
|
'''
|
|
|
|
### Multiple Constraints
|
|
|
|
'''
|
|
constraints {
|
|
HasOneUse($tmp)
|
|
IsConstant($c1)
|
|
IsConstant($c2)
|
|
DifferentValue($a, $b)
|
|
}
|
|
'''
|
|
|
|
---
|
|
|
|
## Rewrite Block
|
|
|
|
### Constant Folding
|
|
|
|
#### 'AddConstants($c1, $c2)'
|
|
|
|
Add two constants.
|
|
|
|
'''
|
|
rewrite {
|
|
$sum = AddConstants($c1, $c2)
|
|
}
|
|
'''
|
|
|
|
**Example:**
|
|
'''
|
|
$c1 = 1.0, $c2 = 1.0 -> $sum = 2.0
|
|
'''
|
|
|
|
#### 'SubConstants($c1, $c2)'
|
|
|
|
Subtract constants: 'c1 - c2'
|
|
|
|
'''
|
|
rewrite {
|
|
$diff = SubConstants($c1, $c2)
|
|
}
|
|
'''
|
|
|
|
#### 'MulConstants($c1, $c2)'
|
|
|
|
Multiply constants.
|
|
|
|
'''
|
|
rewrite {
|
|
$product = MulConstants($c1, $c2)
|
|
}
|
|
'''
|
|
|
|
#### 'NegateConstant($c)'
|
|
|
|
Negate constant: '-c'
|
|
|
|
'''
|
|
rewrite {
|
|
$neg = NegateConstant($c)
|
|
}
|
|
'''
|
|
|
|
### Current Behavior
|
|
|
|
**Currently, rewrites are automatic** for fusion patterns:
|
|
- Constants are folded
|
|
- Instructions modified in-place
|
|
- Dead instructions removed
|
|
|
|
The rewrite block mainly specifies **which constants to fold**.
|
|
|
|
---
|
|
|
|
## Complete Examples
|
|
|
|
### Example 1: Add+FMA Fusion
|
|
|
|
**Transform:**
|
|
'''asm
|
|
v_fma_f32 v0, a, b, 1.0
|
|
v_add_f32 v2, 1.0, v0
|
|
=>
|
|
v_fma_f32 v2, a, b, 2.0
|
|
'''
|
|
|
|
**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) // Safe to eliminate
|
|
IsConstant($fma_const) // Can fold
|
|
IsConstant($add_const) // Can fold
|
|
}
|
|
|
|
rewrite {
|
|
$new_const = AddConstants($fma_const, $add_const)
|
|
}
|
|
}
|
|
'''
|
|
|
|
### Example 2: Multiply-Add Fusion
|
|
|
|
**Transform:**
|
|
'''asm
|
|
v_mul_f32 v0, a, b
|
|
v_add_f32 v2, v0, c
|
|
=>
|
|
v_fma_f32 v2, a, b, c
|
|
'''
|
|
|
|
**Pattern:**
|
|
'''
|
|
pattern MulAddFusion_F32 {
|
|
match {
|
|
$mul = v_mul_f32 $tmp, $a, $b
|
|
$add = v_add_f32 $dst, $tmp, $c
|
|
}
|
|
|
|
constraints {
|
|
HasOneUse($tmp)
|
|
}
|
|
|
|
rewrite {
|
|
// Automatically transforms to FMA
|
|
}
|
|
}
|
|
'''
|
|
|
|
### Example 3: Negate-Negate Elimination
|
|
|
|
**Transform:**
|
|
'''asm
|
|
v_mul_f32 v0, a, -1.0 // Negate
|
|
v_mul_f32 v1, v0, -1.0 // Negate again
|
|
=>
|
|
v_mov_b32 v1, a // Just copy
|
|
'''
|
|
|
|
**Pattern:**
|
|
'''
|
|
pattern NegateNegate_F32 {
|
|
match {
|
|
$neg1 = v_mul_f32 $tmp, $a, -1.0
|
|
$neg2 = v_mul_f32 $dst, $tmp, -1.0
|
|
}
|
|
|
|
constraints {
|
|
HasOneUse($tmp)
|
|
}
|
|
|
|
rewrite {
|
|
// Replaces with move
|
|
}
|
|
}
|
|
'''
|
|
|
|
---
|
|
|
|
## Common Patterns
|
|
|
|
### Pattern: In-Place Operation
|
|
|
|
'''
|
|
match {
|
|
$op = v_add_f32 $dst, $dst, $val // dst = dst + val
|
|
}
|
|
constraints {
|
|
SameValue($dst, $dst) // Verify in-place
|
|
}
|
|
'''
|
|
|
|
### Pattern: Constant Propagation
|
|
|
|
'''
|
|
match {
|
|
$mul = v_mul_f32 $dst, $src, 1.0 // Multiply by 1
|
|
}
|
|
constraints {
|
|
IsConstant(1.0)
|
|
}
|
|
rewrite {
|
|
// Replaces with move: dst = src
|
|
}
|
|
'''
|
|
|
|
### Pattern: Zero Elimination
|
|
|
|
'''
|
|
match {
|
|
$mul = v_mul_f32 $dst, $src, 0.0 // Multiply by 0
|
|
}
|
|
constraints {
|
|
IsConstant(0.0)
|
|
}
|
|
rewrite {
|
|
// Replaces with: dst = 0
|
|
}
|
|
'''
|
|
|
|
---
|
|
|
|
## Operand Order
|
|
|
|
### Destination First, Always
|
|
|
|
'''
|
|
[x] Correct:
|
|
$inst = v_add_f32 $dst, $src1, $src2
|
|
^^^^^
|
|
destination first
|
|
|
|
[ ] Wrong:
|
|
$inst = v_add_f32 $src1, $src2, $dst
|
|
'''
|
|
|
|
### Match Instruction Signature
|
|
|
|
For 'v_fma_f32 dest, src0, src1, src2':
|
|
|
|
'''
|
|
[x] Correct:
|
|
$fma = v_fma_f32 $dst, $a, $b, $c
|
|
^^^^ ^^ ^^ ^^
|
|
dest src0 src1 src2
|
|
|
|
[ ] Wrong:
|
|
$fma = v_fma_f32 $a, $b, $c, $dst
|
|
'''
|
|
|
|
---
|
|
|
|
## Temporal Order
|
|
|
|
Instructions must be listed in **execution order**:
|
|
|
|
'''
|
|
[x] Correct (top-to-bottom = first-to-last):
|
|
match {
|
|
$mul = v_mul_f32 $tmp, $a, $b // Executes first
|
|
$add = v_add_f32 $dst, $tmp, $c // Executes second
|
|
}
|
|
|
|
[ ] Wrong (reversed order):
|
|
match {
|
|
$add = v_add_f32 $dst, $tmp, $c // Can't use $tmp before it's defined!
|
|
$mul = v_mul_f32 $tmp, $a, $b
|
|
}
|
|
'''
|
|
|
|
---
|
|
|
|
## Naming Conventions
|
|
|
|
### Instruction Variables
|
|
|
|
Use descriptive names:
|
|
|
|
'''
|
|
[x] Good:
|
|
$fma_inst, $add_inst, $mul, $store
|
|
|
|
[ ] Bad:
|
|
$i1, $i2, $x, $temp
|
|
'''
|
|
|
|
### Operand Variables
|
|
|
|
Name by role or purpose:
|
|
|
|
'''
|
|
[x] Good:
|
|
$result, $tmp, $intermediate, $fma_const, $add_const
|
|
|
|
[ ] Bad:
|
|
$r1, $r2, $x, $y
|
|
'''
|
|
|
|
### Example with Good Names
|
|
|
|
'''
|
|
pattern AddFMAFusion_F32 {
|
|
match {
|
|
$fma_inst = v_fma_f32 $fma_result, $a, $b, $fma_const
|
|
$add_inst = 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)
|
|
}
|
|
}
|
|
'''
|
|
|
|
---
|
|
|
|
## Comments
|
|
|
|
### Use Comments Liberally
|
|
|
|
'''
|
|
pattern Example {
|
|
match {
|
|
// First: FMA computes a*b + c
|
|
$fma = v_fma_f32 $result, $a, $b, $c
|
|
|
|
// Second: ADD adds constant to FMA result
|
|
$add = v_add_f32 $dst, $const, $result
|
|
}
|
|
|
|
constraints {
|
|
HasOneUse($result) // Safe to eliminate $result
|
|
IsConstant($c) // Required for folding
|
|
IsConstant($const) // Required for folding
|
|
}
|
|
|
|
rewrite {
|
|
// Fold constants: c + const
|
|
$new_const = AddConstants($c, $const)
|
|
}
|
|
}
|
|
'''
|
|
|
|
### Pattern Header Comments
|
|
|
|
'''
|
|
//===----------------------------------------------------------------------===//
|
|
// Pattern: AddFMAFusion_F32
|
|
//
|
|
// Fuses v_add_f32 into v_fma_f32 by folding constants.
|
|
//
|
|
// Before: v_fma_f32 v0, a, b, 1.0
|
|
// v_add_f32 v2, 1.0, v0
|
|
//
|
|
// After: v_fma_f32 v2, a, b, 2.0
|
|
//
|
|
// Benefit: 2 instructions -> 1, frees v0
|
|
//===----------------------------------------------------------------------===//
|
|
pattern AddFMAFusion_F32 {
|
|
...
|
|
}
|
|
'''
|
|
|
|
---
|
|
|
|
## Grammar Summary (EBNF)
|
|
|
|
For reference, here's the formal grammar:
|
|
|
|
'''ebnf
|
|
pattern := 'pattern' IDENT '{' match_block constraints_block? rewrite_block '}'
|
|
|
|
match_block := 'match' '{' match_stmt+ '}'
|
|
match_stmt := VAR '=' IDENT VAR (',' VAR)*
|
|
|
|
constraints_block := 'constraints' '{' constraint+ '}'
|
|
constraint := IDENT '(' VAR (',' VAR)* ')'
|
|
|
|
rewrite_block := 'rewrite' '{' rewrite_stmt* '}'
|
|
rewrite_stmt := VAR '=' IDENT '(' VAR (',' VAR)* ')'
|
|
|
|
VAR := '$' IDENT
|
|
IDENT := [a-zA-Z_][a-zA-Z0-9_]*
|
|
'''
|
|
|
|
**But you don't need to memorize this!** Just follow the examples.
|
|
|
|
---
|
|
|
|
## Validation
|
|
|
|
The parser checks:
|
|
|
|
- [x] All variables defined before use
|
|
- [x] Opcodes exist ('v_fma_f32', 'v_add_f32', etc.)
|
|
- [x] Constraint functions are valid
|
|
- [x] Rewrite operations reference matched variables
|
|
- [x] Syntax is correct
|
|
|
|
**Errors are caught at build time**, not runtime!
|
|
|
|
---
|
|
|
|
## Tips
|
|
|
|
### Start with Existing Patterns
|
|
|
|
Copy and modify existing patterns from 'PeepholePatterns.pattern'.
|
|
|
|
### Test Incrementally
|
|
|
|
1. Add pattern
|
|
2. Build
|
|
3. Check generated code: 'cat build/PeepholePatterns.inc'
|
|
4. Test with IR
|
|
|
|
### Use Simple Names
|
|
|
|
'''
|
|
[x] Good: $result, $tmp, $dst
|
|
[ ] Bad: $intermediate_computation_result_temporary_value
|
|
'''
|
|
|
|
### One Constraint Per Line
|
|
|
|
'''
|
|
[x] Good:
|
|
constraints {
|
|
HasOneUse($tmp)
|
|
IsConstant($c1)
|
|
IsConstant($c2)
|
|
}
|
|
|
|
[ ] Bad:
|
|
constraints { HasOneUse($tmp) IsConstant($c1) IsConstant($c2) }
|
|
'''
|
|
|
|
---
|
|
|
|
## Common Mistakes
|
|
|
|
### Mistake 1: Wrong Operand Order
|
|
|
|
'''
|
|
[ ] Wrong:
|
|
$add = v_add_f32 $src1, $src2, $dst
|
|
|
|
[x] Correct:
|
|
$add = v_add_f32 $dst, $src1, $src2
|
|
'''
|
|
|
|
### Mistake 2: Missing Constraints
|
|
|
|
'''
|
|
[ ] Wrong (might crash if $tmp used multiple times):
|
|
match {
|
|
$mul = v_mul_f32 $tmp, $a, $b
|
|
$add = v_add_f32 $dst, $tmp, $c
|
|
}
|
|
|
|
[x] Correct:
|
|
match {
|
|
$mul = v_mul_f32 $tmp, $a, $b
|
|
$add = v_add_f32 $dst, $tmp, $c
|
|
}
|
|
constraints {
|
|
HasOneUse($tmp) // Required!
|
|
}
|
|
'''
|
|
|
|
### Mistake 3: Wrong Temporal Order
|
|
|
|
'''
|
|
[ ] Wrong (uses $tmp before it's defined):
|
|
match {
|
|
$add = v_add_f32 $dst, $tmp, $c
|
|
$mul = v_mul_f32 $tmp, $a, $b
|
|
}
|
|
|
|
[x] Correct:
|
|
match {
|
|
$mul = v_mul_f32 $tmp, $a, $b
|
|
$add = v_add_f32 $dst, $tmp, $c
|
|
}
|
|
'''
|
|
|
|
---
|
|
|
|
## File Location
|
|
|
|
**Where to write patterns:**
|
|
'''
|
|
src/transforms/asm/PeepholePatterns.pattern
|
|
'''
|
|
|
|
**Generated code (auto):**
|
|
'''
|
|
build/PeepholePatterns.inc
|
|
'''
|
|
|
|
---
|
|
|
|
## See Also
|
|
|
|
- **[Adding Peephole Patterns](adding-peephole-patterns.md)** - How-to guide with examples
|
|
- **[Architecture Overview](architecture.md)** - System architecture and pass pipeline
|
|
- **'src/transforms/asm/PeepholePatterns.pattern'** - Real pattern examples
|
|
|
|
---
|
|
|
|
## Quick Reference Card
|
|
|
|
'''
|
|
Pattern Structure:
|
|
pattern Name { match {} constraints {} rewrite {} }
|
|
|
|
Match:
|
|
$inst = opcode $dst, $src1, $src2, ...
|
|
|
|
Constraints:
|
|
HasOneUse($reg) // Exactly one use
|
|
IsConstant($operand) // Literal value
|
|
SameValue($r1, $r2) // Same register
|
|
DifferentValue($r1, $r2) // Different registers
|
|
|
|
Rewrite:
|
|
$new = AddConstants($c1, $c2) // c1 + c2
|
|
$new = SubConstants($c1, $c2) // c1 - c2
|
|
$new = MulConstants($c1, $c2) // c1 * c2
|
|
$new = NegateConstant($c) // -c
|
|
|
|
Variables:
|
|
$inst // Instruction variable
|
|
$reg // Register/operand variable
|
|
$const // Constant variable (use IsConstant)
|
|
|
|
Rules:
|
|
1. Destination operand comes first
|
|
2. Instructions in temporal order
|
|
3. Variables start with $
|
|
4. Add HasOneUse for intermediate values
|
|
5. Add IsConstant for constant folding
|
|
'''
|
|
|
|
---
|
|
|
|
## Questions?
|
|
|
|
Just look at existing patterns in 'PeepholePatterns.pattern' and copy the structure!
|