mirror of
https://github.com/QuilibriumNetwork/ceremonyclient.git
synced 2026-02-21 18:37:26 +08:00
* v2.1.0 [omit consensus and adjacent] - this commit will be amended with the full release after the file copy is complete * 2.1.0 main node rollup
228 lines
4.2 KiB
Go
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
|
|
}
|