mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
[dev.ssa] cmd/internal/ssa: SSA backend compiler skeleton
First pass adding code for SSA backend. It is standalone for now. I've included just a few passes to make the review size manageable - I have more passes coming. cmd/internal/ssa is the library containing the ssa compiler proper. cmd/internal/ssa/ssac is a driver that loads an sexpr-based IR, converts it to SSA form, and calls the above library. It is essentially throwaway code - it will disappear once the Go compiler calls cmd/internal/ssa itself. The .goir files in ssac/ are dumps of fibonacci programs I made from a hacked-up compiler. They are just for testing. Change-Id: I5ee89356ec12c87cd916681097cd3c2cd591040c Reviewed-on: https://go-review.googlesource.com/6681 Reviewed-by: Alan Donovan <adonovan@google.com>
This commit is contained in:
parent
de7f6c77bc
commit
f52b234579
26 changed files with 2438 additions and 0 deletions
153
src/cmd/internal/ssa/deadcode.go
Normal file
153
src/cmd/internal/ssa/deadcode.go
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
// 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
|
||||
|
||||
import "log"
|
||||
|
||||
// deadcode removes dead code from f.
|
||||
func deadcode(f *Func) {
|
||||
|
||||
// Find all reachable basic blocks.
|
||||
reachable := make([]bool, f.NumBlocks())
|
||||
reachable[f.Entry.ID] = true
|
||||
p := []*Block{f.Entry} // stack-like worklist
|
||||
for len(p) > 0 {
|
||||
// pop a reachable block
|
||||
b := p[len(p)-1]
|
||||
p = p[:len(p)-1]
|
||||
|
||||
// constant-fold conditionals
|
||||
// TODO: rewrite rules instead?
|
||||
if b.Kind == BlockIf && b.Control.Op == OpConstBool {
|
||||
cond := b.Control.Aux.(bool)
|
||||
var c *Block
|
||||
if cond {
|
||||
// then branch is always taken
|
||||
c = b.Succs[1]
|
||||
} else {
|
||||
// else branch is always taken
|
||||
c = b.Succs[0]
|
||||
b.Succs[0] = b.Succs[1]
|
||||
}
|
||||
b.Succs[1] = nil // aid GC
|
||||
b.Succs = b.Succs[:1]
|
||||
removePredecessor(b, c)
|
||||
b.Kind = BlockPlain
|
||||
b.Control = nil
|
||||
}
|
||||
|
||||
for _, c := range b.Succs {
|
||||
if !reachable[c.ID] {
|
||||
reachable[c.ID] = true
|
||||
p = append(p, c) // push
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find all live values
|
||||
live := make([]bool, f.NumValues()) // flag to set for each live value
|
||||
var q []*Value // stack-like worklist of unscanned values
|
||||
|
||||
// Starting set: all control values of reachable blocks are live.
|
||||
for _, b := range f.Blocks {
|
||||
if !reachable[b.ID] {
|
||||
continue
|
||||
}
|
||||
if v := b.Control; v != nil && !live[v.ID] {
|
||||
live[v.ID] = true
|
||||
q = append(q, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Compute transitive closure of live values.
|
||||
for len(q) > 0 {
|
||||
// pop a reachable value
|
||||
v := q[len(q)-1]
|
||||
q = q[:len(q)-1]
|
||||
for _, x := range v.Args {
|
||||
if !live[x.ID] {
|
||||
live[x.ID] = true
|
||||
q = append(q, x) // push
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove dead values from blocks' value list. Return dead
|
||||
// value ids to the allocator.
|
||||
for _, b := range f.Blocks {
|
||||
i := 0
|
||||
for _, v := range b.Values {
|
||||
if live[v.ID] {
|
||||
b.Values[i] = v
|
||||
i++
|
||||
} else {
|
||||
f.vid.put(v.ID)
|
||||
}
|
||||
}
|
||||
for j := i; j < len(b.Values); j++ {
|
||||
b.Values[j] = nil // aid GC
|
||||
}
|
||||
b.Values = b.Values[:i]
|
||||
}
|
||||
|
||||
// Remove unreachable blocks. Return dead block ids to allocator.
|
||||
i := 0
|
||||
for _, b := range f.Blocks {
|
||||
if reachable[b.ID] {
|
||||
f.Blocks[i] = b
|
||||
i++
|
||||
} else {
|
||||
if len(b.Values) > 0 {
|
||||
panic("live value in unreachable block")
|
||||
}
|
||||
f.bid.put(b.ID)
|
||||
}
|
||||
}
|
||||
// zero remainder to help gc
|
||||
for j := i; j < len(f.Blocks); j++ {
|
||||
f.Blocks[j] = nil
|
||||
}
|
||||
f.Blocks = f.Blocks[:i]
|
||||
|
||||
// TODO: renumber Blocks and Values densely?
|
||||
}
|
||||
|
||||
// There was an edge b->c. It has been removed from b's successors.
|
||||
// Fix up c to handle that fact.
|
||||
func removePredecessor(b, c *Block) {
|
||||
n := len(c.Preds) - 1
|
||||
if n == 0 {
|
||||
// c is now dead - don't bother working on it
|
||||
if c.Preds[0] != b {
|
||||
log.Panicf("%s.Preds[0]==%s, want %s", c, c.Preds[0], b)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// find index of b in c's predecessor list
|
||||
var i int
|
||||
for j, p := range c.Preds {
|
||||
if p == b {
|
||||
i = j
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
c.Preds[i] = c.Preds[n]
|
||||
c.Preds[n] = nil // aid GC
|
||||
c.Preds = c.Preds[:n]
|
||||
// rewrite phi ops to match the new predecessor list
|
||||
for _, v := range c.Values {
|
||||
if v.Op != OpPhi {
|
||||
continue
|
||||
}
|
||||
v.Args[i] = v.Args[n]
|
||||
v.Args[n] = nil // aid GC
|
||||
v.Args = v.Args[:n]
|
||||
if n == 1 {
|
||||
v.Op = OpCopy
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue