ceremonyclient/bedlam/compiler/ssa/peephole.go
Cassandra Heart e51992f3e8
OT
2025-03-23 21:11:16 -05:00

228 lines
4.2 KiB
Go

//
// Copyright (c) 2020-2021 Markku Rossi
//
// All rights reserved.
//
package ssa
import (
"fmt"
"regexp"
"strings"
)
// Rule defines a peephole optimization rule.
type Rule struct {
Name string
Source string
Pattern []Template
Replace []Template
}
// Template defines an instruction template.
type Template struct {
Op Operand
In []string
Out string
}
// Expand expands the template with given environment bindings.
func (t Template) Expand(env map[string]Value) (Instr, error) {
var in []Value
var out Value
for _, i := range t.In {
v, ok := env[i]
if !ok {
return Instr{}, fmt.Errorf("input value %s not bound", i)
}
in = append(in, v)
}
out, ok := env[t.Out]
if !ok {
return Instr{}, fmt.Errorf("output value %s not bound", t.Out)
}
return Instr{
Op: t.Op,
In: in,
Out: &out,
}, nil
}
// Match tests if the rule matches the steps span.
func (rule Rule) Match(steps []Step) []Step {
env := make(map[string]Value)
// Match all patterns
for idx, p := range rule.Pattern {
step := steps[idx]
if step.Instr.Op != p.Op {
return nil
}
if len(step.Instr.In) != len(p.In) {
return nil
}
for i, in := range p.In {
if !matchVar(env, in, step.Instr.In[i]) {
return nil
}
}
if step.Instr.Out == nil {
return nil
}
if !matchVar(env, p.Out, *step.Instr.Out) {
return nil
}
}
var result []Step
for _, r := range rule.Replace {
instr, err := r.Expand(env)
if err != nil {
fmt.Printf("template expansion failed: %s\n", err)
return nil
}
// Base liveness from the first replaced instruction.
live := steps[0].Live.Copy()
if instr.Out != nil {
live.Add(*instr.Out)
}
result = append(result, Step{
Instr: instr,
Live: live,
})
}
return result
}
func matchVar(env map[string]Value, pattern string, v Value) bool {
binding, ok := env[pattern]
if ok {
// Pattern already bound, must be equal binding.
return v.Equal(&binding)
}
switch pattern[0] {
case 'V':
case 'C':
if !v.Const {
return false
}
case '$':
if pattern != v.Name {
return false
}
}
env[pattern] = v
return true
}
var rules = []*Rule{
{
Name: "Constant-shift-test => bts",
Source: `rshift V1 C1 V2
band V2 $1 V3
neq V3 $0 V4
=>
bts V1 C1 V4`,
},
{
Name: "Constant-shift-test => btc",
Source: `rshift V1 C1 V2
band V2 $1 V3
eq V3 $0 V4
=>
btc V1 C1 V4`,
},
{
Name: "Constant-shift-test => bts",
Source: `rshift V1 C1 V2
band V2 $1 V3
eq V3 $1 V4
=>
bts V1 C1 V4`,
},
{
Name: "Constant-shift-test => btc",
Source: `rshift V1 C1 V2
band V2 $1 V3
neq V3 $1 V4
=>
btc V1 C1 V4`,
},
}
var reSpace = regexp.MustCompilePOSIX(`[[:space:]]+`)
func init() {
for _, r := range rules {
var inReplace bool
for _, line := range strings.Split(r.Source, "\n") {
parts := reSpace.Split(strings.TrimSpace(line), -1)
if len(parts) == 1 && parts[0] == "=>" {
inReplace = true
continue
}
if len(parts) < 3 {
panic(fmt.Sprintf("unexpected pattern: %s", line))
}
var op Operand
var found bool
for k, v := range operands {
if v == parts[0] {
op = k
found = true
break
}
}
if !found {
panic(fmt.Sprintf("unknown operand '%s'", parts[0]))
}
tmpl := Template{
Op: op,
In: parts[1 : len(parts)-1],
Out: parts[len(parts)-1],
}
if inReplace {
r.Replace = append(r.Replace, tmpl)
} else {
r.Pattern = append(r.Pattern, tmpl)
}
}
}
}
// Peephole runs the peephole optimizer for the program.
func (prog *Program) Peephole() error {
prog.liveness()
outer:
for i := 0; i < len(prog.Steps); i++ {
for _, rule := range rules {
if len(rule.Pattern) > len(prog.Steps)-i {
continue
}
match := rule.Match(prog.Steps[i : i+len(rule.Pattern)])
if match == nil {
continue
}
var n []Step
n = append(n, prog.Steps[:i]...)
n = append(n, match...)
n = append(n, prog.Steps[i+len(rule.Pattern):]...)
prog.Steps = n
continue outer
}
}
return nil
}