Commit e3a89ef7 authored by Etienne Renault's avatar Etienne Renault

cfg: update package

* transform/cfg/cfg/README,
transform/cfg/cfg/builder.go,
transform/cfg/cfg/cfg.go: Here.
parent 46db0b55
This package was adapted from https://golang.org/src/cmd/vet/internal/cfg/.
This package was adapted from
https://golang.org/src/cmd/vendor/golang.org/x/tools/go/cfg/
See LICENSE for the corresponding license information.
......@@ -42,7 +42,7 @@ start:
b.add(s)
if call, ok := s.X.(*ast.CallExpr); ok && !b.mayReturn(call) {
// Calls to panic, os.Exit, etc, never return.
b.current = b.newUnreachableBlock("unreachable.call")
b.current = b.newBlock("unreachable.call")
}
case *ast.DeclStmt:
......@@ -65,48 +65,10 @@ start:
case *ast.ReturnStmt:
b.add(s)
b.current = b.newUnreachableBlock("unreachable.return")
b.current = b.newBlock("unreachable.return")
case *ast.BranchStmt:
var block *Block
switch s.Tok {
case token.BREAK:
if s.Label != nil {
if lb := b.labeledBlock(s.Label); lb != nil {
block = lb._break
}
} else {
for t := b.targets; t != nil && block == nil; t = t.tail {
block = t._break
}
}
case token.CONTINUE:
if s.Label != nil {
if lb := b.labeledBlock(s.Label); lb != nil {
block = lb._continue
}
} else {
for t := b.targets; t != nil && block == nil; t = t.tail {
block = t._continue
}
}
case token.FALLTHROUGH:
for t := b.targets; t != nil; t = t.tail {
block = t._fallthrough
}
case token.GOTO:
if s.Label != nil {
block = b.labeledBlock(s.Label)._goto
}
}
if block == nil {
block = b.newBlock("undefined.branch")
}
b.jump(block)
b.current = b.newUnreachableBlock("unreachable.branch")
b.branchStmt(s)
case *ast.BlockStmt:
b.stmtList(s.List)
......@@ -161,6 +123,48 @@ func (b *builder) stmtList(list []ast.Stmt) {
}
}
func (b *builder) branchStmt(s *ast.BranchStmt) {
var block *Block
switch s.Tok {
case token.BREAK:
if s.Label != nil {
if lb := b.labeledBlock(s.Label); lb != nil {
block = lb._break
}
} else {
for t := b.targets; t != nil && block == nil; t = t.tail {
block = t._break
}
}
case token.CONTINUE:
if s.Label != nil {
if lb := b.labeledBlock(s.Label); lb != nil {
block = lb._continue
}
} else {
for t := b.targets; t != nil && block == nil; t = t.tail {
block = t._continue
}
}
case token.FALLTHROUGH:
for t := b.targets; t != nil && block == nil; t = t.tail {
block = t._fallthrough
}
case token.GOTO:
if s.Label != nil {
block = b.labeledBlock(s.Label)._goto
}
}
if block == nil {
block = b.newBlock("undefined.branch")
}
b.jump(block)
b.current = b.newBlock("unreachable.branch")
}
func (b *builder) switchStmt(s *ast.SwitchStmt, label *lblock) {
if s.Init != nil {
b.stmt(s.Init)
......@@ -494,12 +498,6 @@ func (b *builder) newBlock(comment string) *Block {
return block
}
func (b *builder) newUnreachableBlock(comment string) *Block {
block := b.newBlock(comment)
block.unreachable = true
return block
}
func (b *builder) add(n ast.Node) {
b.current.Nodes = append(b.current.Nodes, n)
}
......
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This package constructs a simple control-flow graph (CFG) of the
// Package cfg constructs a simple control-flow graph (CFG) of the
// statements and expressions within a single function.
//
// Use cfg.New to construct the CFG for a function body.
......@@ -39,11 +39,6 @@
//
package cfg
// Although the vet tool has type information, it is often extremely
// fragmentary, so for simplicity this package does not depend on
// go/types. Consequently control-flow conditions are ignored even
// when constant, and "mayReturn" information must be provided by the
// client.
import (
"bytes"
"fmt"
......@@ -69,10 +64,10 @@ type Block struct {
Nodes []ast.Node // statements, expressions, and ValueSpecs
Succs []*Block // successor nodes in the graph
Index int32 // index within CFG.Blocks
Live bool // block is reachable from entry
comment string // for debugging
unreachable bool // is block of stmts following return/panic/for{}
succs2 [2]*Block // underlying array for Succs
comment string // for debugging
succs2 [2]*Block // underlying array for Succs
}
// New returns a new control-flow graph for the specified function body,
......@@ -91,9 +86,22 @@ func New(body *ast.BlockStmt, mayReturn func(*ast.CallExpr) bool) *CFG {
b.current = b.newBlock("entry")
b.stmt(body)
// Compute liveness (reachability from entry point), breadth-first.
q := make([]*Block, 0, len(b.cfg.Blocks))
q = append(q, b.cfg.Blocks[0]) // entry point
for len(q) > 0 {
b := q[len(q)-1]
q = q[:len(q)-1]
if !b.Live {
b.Live = true
q = append(q, b.Succs...)
}
}
// Does control fall off the end of the function's body?
// Make implicit return explicit.
if b.current != nil && !b.current.unreachable {
if b.current != nil && b.current.Live {
b.add(&ast.ReturnStmt{
Return: body.End() - 1,
})
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment