mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
The SSA implementation logs for three purposes: * debug logging * fatal errors * unimplemented features Separating these three uses lets us attempt an SSA implementation for all functions, not just _ssa functions. This turns the entire standard library into a compilation test, and makes it easy to figure out things like "how much coverage does SSA have now" and "what should we do next to get more coverage?". Functions called _ssa are still special. They log profusely by default and the output of the SSA implementation is used. For all other functions, logging is off, and the implementation is built and discarded, due to lack of support for the runtime. While we're here, fix a few minor bugs and add some extra Unimplementeds to allow all.bash to pass. As of now, SSA handles 20.79% of the functions in the standard library (689 of 3314). The top missing features are: 10.03% 2597 SSA unimplemented: zero for type error not implemented 7.79% 2016 SSA unimplemented: addr: bad op DOTPTR 7.33% 1898 SSA unimplemented: unhandled expr EQ 6.10% 1579 SSA unimplemented: unhandled expr OROR 4.91% 1271 SSA unimplemented: unhandled expr NE 4.49% 1163 SSA unimplemented: unhandled expr LROT 4.00% 1036 SSA unimplemented: unhandled expr LEN 3.56% 923 SSA unimplemented: unhandled stmt CALLFUNC 2.37% 615 SSA unimplemented: zero for type []byte not implemented 1.90% 492 SSA unimplemented: unhandled stmt CALLMETH 1.74% 450 SSA unimplemented: unhandled expr CALLINTER 1.74% 450 SSA unimplemented: unhandled expr DOT 1.71% 444 SSA unimplemented: unhandled expr ANDAND 1.65% 426 SSA unimplemented: unhandled expr CLOSUREVAR 1.54% 400 SSA unimplemented: unhandled expr CALLMETH 1.51% 390 SSA unimplemented: unhandled stmt SWITCH 1.47% 380 SSA unimplemented: unhandled expr CONV 1.33% 345 SSA unimplemented: addr: bad op * 1.30% 336 SSA unimplemented: unhandled OLITERAL 6 Change-Id: I4ca07951e276714dc13c31de28640aead17a1be7 Reviewed-on: https://go-review.googlesource.com/11160 Reviewed-by: Keith Randall <khr@golang.org>
119 lines
2.9 KiB
Go
119 lines
2.9 KiB
Go
// Copyright 2015 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package ssa
|
|
|
|
// This file contains code to compute the dominator tree
|
|
// of a control-flow graph.
|
|
|
|
// postorder computes a postorder traversal ordering for the
|
|
// basic blocks in f. Unreachable blocks will not appear.
|
|
func postorder(f *Func) []*Block {
|
|
mark := make([]byte, f.NumBlocks())
|
|
// mark values
|
|
const (
|
|
notFound = 0 // block has not been discovered yet
|
|
notExplored = 1 // discovered and in queue, outedges not processed yet
|
|
explored = 2 // discovered and in queue, outedges processed
|
|
done = 3 // all done, in output ordering
|
|
)
|
|
|
|
// result ordering
|
|
var order []*Block
|
|
|
|
// stack of blocks
|
|
var s []*Block
|
|
s = append(s, f.Entry)
|
|
mark[f.Entry.ID] = notExplored
|
|
for len(s) > 0 {
|
|
b := s[len(s)-1]
|
|
switch mark[b.ID] {
|
|
case explored:
|
|
// Children have all been visited. Pop & output block.
|
|
s = s[:len(s)-1]
|
|
mark[b.ID] = done
|
|
order = append(order, b)
|
|
case notExplored:
|
|
// Children have not been visited yet. Mark as explored
|
|
// and queue any children we haven't seen yet.
|
|
mark[b.ID] = explored
|
|
for _, c := range b.Succs {
|
|
if mark[c.ID] == notFound {
|
|
mark[c.ID] = notExplored
|
|
s = append(s, c)
|
|
}
|
|
}
|
|
default:
|
|
b.Fatal("bad stack state %v %d", b, mark[b.ID])
|
|
}
|
|
}
|
|
return order
|
|
}
|
|
|
|
// dominators computes the dominator tree for f. It returns a slice
|
|
// which maps block ID to the immediate dominator of that block.
|
|
// Unreachable blocks map to nil. The entry block maps to nil.
|
|
func dominators(f *Func) []*Block {
|
|
// A simple algorithm for now
|
|
// Cooper, Harvey, Kennedy
|
|
idom := make([]*Block, f.NumBlocks())
|
|
|
|
// Compute postorder walk
|
|
post := postorder(f)
|
|
|
|
// Make map from block id to order index (for intersect call)
|
|
postnum := make([]int, f.NumBlocks())
|
|
for i, b := range post {
|
|
postnum[b.ID] = i
|
|
}
|
|
|
|
// Make the entry block a self-loop
|
|
idom[f.Entry.ID] = f.Entry
|
|
if postnum[f.Entry.ID] != len(post)-1 {
|
|
f.Fatal("entry block %v not last in postorder", f.Entry)
|
|
}
|
|
|
|
// Compute relaxation of idom entries
|
|
for {
|
|
changed := false
|
|
|
|
for i := len(post) - 2; i >= 0; i-- {
|
|
b := post[i]
|
|
var d *Block
|
|
for _, p := range b.Preds {
|
|
if idom[p.ID] == nil {
|
|
continue
|
|
}
|
|
if d == nil {
|
|
d = p
|
|
continue
|
|
}
|
|
d = intersect(d, p, postnum, idom)
|
|
}
|
|
if d != idom[b.ID] {
|
|
idom[b.ID] = d
|
|
changed = true
|
|
}
|
|
}
|
|
if !changed {
|
|
break
|
|
}
|
|
}
|
|
// Set idom of entry block to nil instead of itself.
|
|
idom[f.Entry.ID] = nil
|
|
return idom
|
|
}
|
|
|
|
// intersect finds the closest dominator of both b and c.
|
|
// It requires a postorder numbering of all the blocks.
|
|
func intersect(b, c *Block, postnum []int, idom []*Block) *Block {
|
|
for b != c {
|
|
if postnum[b.ID] < postnum[c.ID] {
|
|
b = idom[b.ID]
|
|
} else {
|
|
c = idom[c.ID]
|
|
}
|
|
}
|
|
return b
|
|
}
|