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
308 lines
6.7 KiB
Go
308 lines
6.7 KiB
Go
//
|
|
// Copyright (c) 2020-2023 Markku Rossi
|
|
//
|
|
// All rights reserved.
|
|
//
|
|
|
|
package ssa
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
|
|
"source.quilibrium.com/quilibrium/monorepo/bedlam/types"
|
|
)
|
|
|
|
// Block implements a basic block.
|
|
type Block struct {
|
|
ID BlockID
|
|
Name string
|
|
From []*Block
|
|
Next *Block
|
|
BranchCond Value
|
|
Branch *Block
|
|
Instr []Instr
|
|
Bindings *Bindings
|
|
Dead bool
|
|
Processed bool
|
|
}
|
|
|
|
// BlockID defines unique block IDs.
|
|
type BlockID int
|
|
|
|
func (id BlockID) String() string {
|
|
return fmt.Sprintf("l%d", id)
|
|
}
|
|
|
|
func (b *Block) String() string {
|
|
return b.ID.String()
|
|
}
|
|
|
|
// Equals tests if the argument block is equal to this basic block.
|
|
func (b *Block) Equals(o *Block) bool {
|
|
return b.ID == o.ID
|
|
}
|
|
|
|
// SetNext sets the next basic block.
|
|
func (b *Block) SetNext(o *Block) {
|
|
if b.Next != nil && b.Next.ID != o.ID {
|
|
panic(fmt.Sprintf("%s.Next already set to %s, now setting to %s",
|
|
b, b.Next, o))
|
|
}
|
|
b.Next = o
|
|
o.addFrom(b)
|
|
}
|
|
|
|
// SetBranch sets the argument block being a branch block for this
|
|
// basic block.
|
|
func (b *Block) SetBranch(o *Block) {
|
|
if b.Branch != nil && b.Branch.ID != o.ID {
|
|
panic(fmt.Sprintf("%s.Branch already set to %s, now setting to %s",
|
|
b, b.Next, o))
|
|
}
|
|
b.Branch = o
|
|
o.addFrom(b)
|
|
}
|
|
|
|
func (b *Block) addFrom(o *Block) {
|
|
for _, f := range b.From {
|
|
if f.Equals(o) {
|
|
return
|
|
}
|
|
}
|
|
b.From = append(b.From, o)
|
|
}
|
|
|
|
// AddInstr adds an instruction to this basic block.
|
|
func (b *Block) AddInstr(instr Instr) {
|
|
instr.Check()
|
|
b.Instr = append(b.Instr, instr)
|
|
}
|
|
|
|
type returnBindingKey struct {
|
|
BlockID BlockID
|
|
Name string
|
|
}
|
|
|
|
// ReturnBindingValue define return binding value for a return value.
|
|
type ReturnBindingValue struct {
|
|
v Value
|
|
diff bool
|
|
ok bool
|
|
}
|
|
|
|
// ReturnBindingCTX defines a context for return binding resolve
|
|
// operation.
|
|
type ReturnBindingCTX struct {
|
|
cache map[returnBindingKey]ReturnBindingValue
|
|
}
|
|
|
|
// Get gets the return binding for the return value in the basic
|
|
// block.
|
|
func (ctx *ReturnBindingCTX) Get(b *Block, name string) (
|
|
ReturnBindingValue, bool) {
|
|
value, ok := ctx.cache[returnBindingKey{
|
|
BlockID: b.ID,
|
|
Name: name,
|
|
}]
|
|
return value, ok
|
|
}
|
|
|
|
// Set sets the return binding for the return value in the basic
|
|
// block.
|
|
func (ctx *ReturnBindingCTX) Set(b *Block, name string, v Value,
|
|
diff, ok bool) {
|
|
|
|
ctx.cache[returnBindingKey{
|
|
BlockID: b.ID,
|
|
Name: name,
|
|
}] = ReturnBindingValue{
|
|
v: v,
|
|
diff: diff,
|
|
ok: ok,
|
|
}
|
|
}
|
|
|
|
// NewReturnBindingCTX creates a new return binding resolve context.
|
|
func NewReturnBindingCTX() *ReturnBindingCTX {
|
|
return &ReturnBindingCTX{
|
|
cache: make(map[returnBindingKey]ReturnBindingValue),
|
|
}
|
|
}
|
|
|
|
// ReturnBinding returns the return statement binding for the argument
|
|
// value. If the block contains a branch and value is modified in both
|
|
// branches, the function adds a Phi instruction to resolve the value
|
|
// binding after this basic block. The return value diff tells if
|
|
// branch return values has different sizes.
|
|
func (b *Block) ReturnBinding(ctx *ReturnBindingCTX, name string,
|
|
retBlock *Block, gen *Generator) (v Value, diff, ok bool) {
|
|
|
|
binding, ok := ctx.Get(b, name)
|
|
if ok {
|
|
return binding.v, binding.diff, binding.ok
|
|
}
|
|
v, diff, ok = b.returnBinding(ctx, name, retBlock, gen)
|
|
ctx.Set(b, name, v, diff, ok)
|
|
return v, diff, ok
|
|
}
|
|
|
|
func (b *Block) returnBinding(ctx *ReturnBindingCTX, name string,
|
|
retBlock *Block, gen *Generator) (v Value, diff, ok bool) {
|
|
|
|
// XXX Check if the if-ssagen could omit branch in this case?
|
|
if b.Branch == nil || b.Next == b.Branch {
|
|
// Sequential block, return latest value
|
|
if b.Next != nil {
|
|
v, diff, ok = b.Next.ReturnBinding(ctx, name, retBlock, gen)
|
|
if ok {
|
|
return v, diff, true
|
|
}
|
|
// Next didn't have value, take ours below.
|
|
}
|
|
bind, ok := b.Bindings.Get(name)
|
|
if !ok {
|
|
return v, false, false
|
|
}
|
|
return bind.Value(retBlock, gen), false, true
|
|
}
|
|
vTrue, diffTrue, ok := b.Branch.ReturnBinding(ctx, name, retBlock, gen)
|
|
if !ok {
|
|
return v, false, false
|
|
}
|
|
vFalse, diffFalse, ok := b.Next.ReturnBinding(ctx, name, retBlock, gen)
|
|
if !ok {
|
|
return v, false, false
|
|
}
|
|
if vTrue.Equal(&vFalse) {
|
|
return vTrue, diffTrue || diffFalse, true
|
|
}
|
|
|
|
var rType types.Info
|
|
if vTrue.Type.Bits > vFalse.Type.Bits {
|
|
rType = vTrue.Type
|
|
} else {
|
|
rType = vFalse.Type
|
|
}
|
|
|
|
v = gen.AnonVal(rType)
|
|
retBlock.AddInstr(NewPhiInstr(b.BranchCond, vTrue, vFalse, v))
|
|
|
|
return v, vTrue.Type.Bits != vFalse.Type.Bits || diffTrue || diffFalse, true
|
|
}
|
|
|
|
// Serialize serializes the basic block's instructions.
|
|
func (b *Block) Serialize() []Step {
|
|
seen := make(map[BlockID]bool)
|
|
return b.serialize(nil, seen)
|
|
}
|
|
|
|
func (b *Block) serialize(code []Step, seen map[BlockID]bool) []Step {
|
|
if seen[b.ID] {
|
|
return code
|
|
}
|
|
// Have all predecessors been processed?
|
|
for _, from := range b.From {
|
|
if !seen[from.ID] {
|
|
return code
|
|
}
|
|
}
|
|
seen[b.ID] = true
|
|
|
|
var label string
|
|
if len(b.Name) > 0 {
|
|
label = b.Name
|
|
}
|
|
|
|
for _, instr := range b.Instr {
|
|
code = append(code, Step{
|
|
Label: label,
|
|
Instr: instr,
|
|
})
|
|
label = ""
|
|
}
|
|
|
|
if b.Branch != nil {
|
|
code = b.Branch.serialize(code, seen)
|
|
}
|
|
if b.Next != nil {
|
|
code = b.Next.serialize(code, seen)
|
|
}
|
|
return code
|
|
}
|
|
|
|
// DotNodes creates graphviz dot description of this basic block.
|
|
func (b *Block) DotNodes(out io.Writer, seen map[BlockID]bool) {
|
|
if seen[b.ID] {
|
|
return
|
|
}
|
|
seen[b.ID] = true
|
|
|
|
var label string
|
|
if len(b.Instr) == 1 {
|
|
label = b.Instr[0].string(0, false)
|
|
} else {
|
|
var maxLen int
|
|
for _, i := range b.Instr {
|
|
l := len(i.Op.String())
|
|
if l > maxLen {
|
|
maxLen = l
|
|
}
|
|
}
|
|
for _, i := range b.Instr {
|
|
label += i.string(maxLen, false)
|
|
label += "\\l"
|
|
}
|
|
}
|
|
|
|
fmt.Fprintf(out, " %s [label=\"%s\"]\n", b,
|
|
strings.ReplaceAll(label, `"`, `\"`))
|
|
|
|
if b.Next != nil {
|
|
b.Next.DotNodes(out, seen)
|
|
}
|
|
if b.Branch != nil {
|
|
b.Branch.DotNodes(out, seen)
|
|
}
|
|
}
|
|
|
|
// DotLinks creates graphviz dot description of the links to and from
|
|
// this basic block.
|
|
func (b *Block) DotLinks(out io.Writer, seen map[BlockID]bool) {
|
|
if seen[b.ID] {
|
|
return
|
|
}
|
|
seen[b.ID] = true
|
|
if b.Next != nil {
|
|
fmt.Fprintf(out, " %s -> %s [label=\"%s\"];\n",
|
|
b, b.Next, b.Next)
|
|
}
|
|
if b.Branch != nil {
|
|
fmt.Fprintf(out, " %s -> %s [label=\"%s\"];\n",
|
|
b, b.Branch, b.Branch)
|
|
}
|
|
|
|
if b.Next != nil {
|
|
b.Next.DotLinks(out, seen)
|
|
}
|
|
if b.Branch != nil {
|
|
b.Branch.DotLinks(out, seen)
|
|
}
|
|
}
|
|
|
|
// Dot creates a graphviz dot description of this basic block.
|
|
func Dot(out io.Writer, block *Block) {
|
|
fontname := "Courier"
|
|
fontsize := 10
|
|
|
|
fmt.Fprintln(out, "digraph program {")
|
|
fmt.Fprintf(out, " node [shape=box fontname=\"%s\" fontsize=\"%d\"]\n",
|
|
fontname, fontsize)
|
|
fmt.Fprintf(out, " edge [fontname=\"%s\" fontsize=\"%d\"]\n",
|
|
fontname, fontsize)
|
|
block.DotNodes(out, make(map[BlockID]bool))
|
|
block.DotLinks(out, make(map[BlockID]bool))
|
|
fmt.Fprintln(out, "}")
|
|
}
|