github.com/jd-ly/tools@v0.5.7/go/cfg/builder.go (about)

     1  // Copyright 2016 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package cfg
     6  
     7  // This file implements the CFG construction pass.
     8  
     9  import (
    10  	"fmt"
    11  	"go/ast"
    12  	"go/token"
    13  )
    14  
    15  type builder struct {
    16  	cfg       *CFG
    17  	mayReturn func(*ast.CallExpr) bool
    18  	current   *Block
    19  	lblocks   map[*ast.Object]*lblock // labeled blocks
    20  	targets   *targets                // linked stack of branch targets
    21  }
    22  
    23  func (b *builder) stmt(_s ast.Stmt) {
    24  	// The label of the current statement.  If non-nil, its _goto
    25  	// target is always set; its _break and _continue are set only
    26  	// within the body of switch/typeswitch/select/for/range.
    27  	// It is effectively an additional default-nil parameter of stmt().
    28  	var label *lblock
    29  start:
    30  	switch s := _s.(type) {
    31  	case *ast.BadStmt,
    32  		*ast.SendStmt,
    33  		*ast.IncDecStmt,
    34  		*ast.GoStmt,
    35  		*ast.DeferStmt,
    36  		*ast.EmptyStmt,
    37  		*ast.AssignStmt:
    38  		// No effect on control flow.
    39  		b.add(s)
    40  
    41  	case *ast.ExprStmt:
    42  		b.add(s)
    43  		if call, ok := s.X.(*ast.CallExpr); ok && !b.mayReturn(call) {
    44  			// Calls to panic, os.Exit, etc, never return.
    45  			b.current = b.newBlock("unreachable.call")
    46  		}
    47  
    48  	case *ast.DeclStmt:
    49  		// Treat each var ValueSpec as a separate statement.
    50  		d := s.Decl.(*ast.GenDecl)
    51  		if d.Tok == token.VAR {
    52  			for _, spec := range d.Specs {
    53  				if spec, ok := spec.(*ast.ValueSpec); ok {
    54  					b.add(spec)
    55  				}
    56  			}
    57  		}
    58  
    59  	case *ast.LabeledStmt:
    60  		label = b.labeledBlock(s.Label)
    61  		b.jump(label._goto)
    62  		b.current = label._goto
    63  		_s = s.Stmt
    64  		goto start // effectively: tailcall stmt(g, s.Stmt, label)
    65  
    66  	case *ast.ReturnStmt:
    67  		b.add(s)
    68  		b.current = b.newBlock("unreachable.return")
    69  
    70  	case *ast.BranchStmt:
    71  		b.branchStmt(s)
    72  
    73  	case *ast.BlockStmt:
    74  		b.stmtList(s.List)
    75  
    76  	case *ast.IfStmt:
    77  		if s.Init != nil {
    78  			b.stmt(s.Init)
    79  		}
    80  		then := b.newBlock("if.then")
    81  		done := b.newBlock("if.done")
    82  		_else := done
    83  		if s.Else != nil {
    84  			_else = b.newBlock("if.else")
    85  		}
    86  		b.add(s.Cond)
    87  		b.ifelse(then, _else)
    88  		b.current = then
    89  		b.stmt(s.Body)
    90  		b.jump(done)
    91  
    92  		if s.Else != nil {
    93  			b.current = _else
    94  			b.stmt(s.Else)
    95  			b.jump(done)
    96  		}
    97  
    98  		b.current = done
    99  
   100  	case *ast.SwitchStmt:
   101  		b.switchStmt(s, label)
   102  
   103  	case *ast.TypeSwitchStmt:
   104  		b.typeSwitchStmt(s, label)
   105  
   106  	case *ast.SelectStmt:
   107  		b.selectStmt(s, label)
   108  
   109  	case *ast.ForStmt:
   110  		b.forStmt(s, label)
   111  
   112  	case *ast.RangeStmt:
   113  		b.rangeStmt(s, label)
   114  
   115  	default:
   116  		panic(fmt.Sprintf("unexpected statement kind: %T", s))
   117  	}
   118  }
   119  
   120  func (b *builder) stmtList(list []ast.Stmt) {
   121  	for _, s := range list {
   122  		b.stmt(s)
   123  	}
   124  }
   125  
   126  func (b *builder) branchStmt(s *ast.BranchStmt) {
   127  	var block *Block
   128  	switch s.Tok {
   129  	case token.BREAK:
   130  		if s.Label != nil {
   131  			if lb := b.labeledBlock(s.Label); lb != nil {
   132  				block = lb._break
   133  			}
   134  		} else {
   135  			for t := b.targets; t != nil && block == nil; t = t.tail {
   136  				block = t._break
   137  			}
   138  		}
   139  
   140  	case token.CONTINUE:
   141  		if s.Label != nil {
   142  			if lb := b.labeledBlock(s.Label); lb != nil {
   143  				block = lb._continue
   144  			}
   145  		} else {
   146  			for t := b.targets; t != nil && block == nil; t = t.tail {
   147  				block = t._continue
   148  			}
   149  		}
   150  
   151  	case token.FALLTHROUGH:
   152  		for t := b.targets; t != nil && block == nil; t = t.tail {
   153  			block = t._fallthrough
   154  		}
   155  
   156  	case token.GOTO:
   157  		if s.Label != nil {
   158  			block = b.labeledBlock(s.Label)._goto
   159  		}
   160  	}
   161  	if block == nil {
   162  		block = b.newBlock("undefined.branch")
   163  	}
   164  	b.jump(block)
   165  	b.current = b.newBlock("unreachable.branch")
   166  }
   167  
   168  func (b *builder) switchStmt(s *ast.SwitchStmt, label *lblock) {
   169  	if s.Init != nil {
   170  		b.stmt(s.Init)
   171  	}
   172  	if s.Tag != nil {
   173  		b.add(s.Tag)
   174  	}
   175  	done := b.newBlock("switch.done")
   176  	if label != nil {
   177  		label._break = done
   178  	}
   179  	// We pull the default case (if present) down to the end.
   180  	// But each fallthrough label must point to the next
   181  	// body block in source order, so we preallocate a
   182  	// body block (fallthru) for the next case.
   183  	// Unfortunately this makes for a confusing block order.
   184  	var defaultBody *[]ast.Stmt
   185  	var defaultFallthrough *Block
   186  	var fallthru, defaultBlock *Block
   187  	ncases := len(s.Body.List)
   188  	for i, clause := range s.Body.List {
   189  		body := fallthru
   190  		if body == nil {
   191  			body = b.newBlock("switch.body") // first case only
   192  		}
   193  
   194  		// Preallocate body block for the next case.
   195  		fallthru = done
   196  		if i+1 < ncases {
   197  			fallthru = b.newBlock("switch.body")
   198  		}
   199  
   200  		cc := clause.(*ast.CaseClause)
   201  		if cc.List == nil {
   202  			// Default case.
   203  			defaultBody = &cc.Body
   204  			defaultFallthrough = fallthru
   205  			defaultBlock = body
   206  			continue
   207  		}
   208  
   209  		var nextCond *Block
   210  		for _, cond := range cc.List {
   211  			nextCond = b.newBlock("switch.next")
   212  			b.add(cond) // one half of the tag==cond condition
   213  			b.ifelse(body, nextCond)
   214  			b.current = nextCond
   215  		}
   216  		b.current = body
   217  		b.targets = &targets{
   218  			tail:         b.targets,
   219  			_break:       done,
   220  			_fallthrough: fallthru,
   221  		}
   222  		b.stmtList(cc.Body)
   223  		b.targets = b.targets.tail
   224  		b.jump(done)
   225  		b.current = nextCond
   226  	}
   227  	if defaultBlock != nil {
   228  		b.jump(defaultBlock)
   229  		b.current = defaultBlock
   230  		b.targets = &targets{
   231  			tail:         b.targets,
   232  			_break:       done,
   233  			_fallthrough: defaultFallthrough,
   234  		}
   235  		b.stmtList(*defaultBody)
   236  		b.targets = b.targets.tail
   237  	}
   238  	b.jump(done)
   239  	b.current = done
   240  }
   241  
   242  func (b *builder) typeSwitchStmt(s *ast.TypeSwitchStmt, label *lblock) {
   243  	if s.Init != nil {
   244  		b.stmt(s.Init)
   245  	}
   246  	if s.Assign != nil {
   247  		b.add(s.Assign)
   248  	}
   249  
   250  	done := b.newBlock("typeswitch.done")
   251  	if label != nil {
   252  		label._break = done
   253  	}
   254  	var default_ *ast.CaseClause
   255  	for _, clause := range s.Body.List {
   256  		cc := clause.(*ast.CaseClause)
   257  		if cc.List == nil {
   258  			default_ = cc
   259  			continue
   260  		}
   261  		body := b.newBlock("typeswitch.body")
   262  		var next *Block
   263  		for _, casetype := range cc.List {
   264  			next = b.newBlock("typeswitch.next")
   265  			// casetype is a type, so don't call b.add(casetype).
   266  			// This block logically contains a type assertion,
   267  			// x.(casetype), but it's unclear how to represent x.
   268  			_ = casetype
   269  			b.ifelse(body, next)
   270  			b.current = next
   271  		}
   272  		b.current = body
   273  		b.typeCaseBody(cc, done)
   274  		b.current = next
   275  	}
   276  	if default_ != nil {
   277  		b.typeCaseBody(default_, done)
   278  	} else {
   279  		b.jump(done)
   280  	}
   281  	b.current = done
   282  }
   283  
   284  func (b *builder) typeCaseBody(cc *ast.CaseClause, done *Block) {
   285  	b.targets = &targets{
   286  		tail:   b.targets,
   287  		_break: done,
   288  	}
   289  	b.stmtList(cc.Body)
   290  	b.targets = b.targets.tail
   291  	b.jump(done)
   292  }
   293  
   294  func (b *builder) selectStmt(s *ast.SelectStmt, label *lblock) {
   295  	// First evaluate channel expressions.
   296  	// TODO(adonovan): fix: evaluate only channel exprs here.
   297  	for _, clause := range s.Body.List {
   298  		if comm := clause.(*ast.CommClause).Comm; comm != nil {
   299  			b.stmt(comm)
   300  		}
   301  	}
   302  
   303  	done := b.newBlock("select.done")
   304  	if label != nil {
   305  		label._break = done
   306  	}
   307  
   308  	var defaultBody *[]ast.Stmt
   309  	for _, cc := range s.Body.List {
   310  		clause := cc.(*ast.CommClause)
   311  		if clause.Comm == nil {
   312  			defaultBody = &clause.Body
   313  			continue
   314  		}
   315  		body := b.newBlock("select.body")
   316  		next := b.newBlock("select.next")
   317  		b.ifelse(body, next)
   318  		b.current = body
   319  		b.targets = &targets{
   320  			tail:   b.targets,
   321  			_break: done,
   322  		}
   323  		switch comm := clause.Comm.(type) {
   324  		case *ast.ExprStmt: // <-ch
   325  			// nop
   326  		case *ast.AssignStmt: // x := <-states[state].Chan
   327  			b.add(comm.Lhs[0])
   328  		}
   329  		b.stmtList(clause.Body)
   330  		b.targets = b.targets.tail
   331  		b.jump(done)
   332  		b.current = next
   333  	}
   334  	if defaultBody != nil {
   335  		b.targets = &targets{
   336  			tail:   b.targets,
   337  			_break: done,
   338  		}
   339  		b.stmtList(*defaultBody)
   340  		b.targets = b.targets.tail
   341  		b.jump(done)
   342  	}
   343  	b.current = done
   344  }
   345  
   346  func (b *builder) forStmt(s *ast.ForStmt, label *lblock) {
   347  	//	...init...
   348  	//      jump loop
   349  	// loop:
   350  	//      if cond goto body else done
   351  	// body:
   352  	//      ...body...
   353  	//      jump post
   354  	// post:				 (target of continue)
   355  	//      ...post...
   356  	//      jump loop
   357  	// done:                                 (target of break)
   358  	if s.Init != nil {
   359  		b.stmt(s.Init)
   360  	}
   361  	body := b.newBlock("for.body")
   362  	done := b.newBlock("for.done") // target of 'break'
   363  	loop := body                   // target of back-edge
   364  	if s.Cond != nil {
   365  		loop = b.newBlock("for.loop")
   366  	}
   367  	cont := loop // target of 'continue'
   368  	if s.Post != nil {
   369  		cont = b.newBlock("for.post")
   370  	}
   371  	if label != nil {
   372  		label._break = done
   373  		label._continue = cont
   374  	}
   375  	b.jump(loop)
   376  	b.current = loop
   377  	if loop != body {
   378  		b.add(s.Cond)
   379  		b.ifelse(body, done)
   380  		b.current = body
   381  	}
   382  	b.targets = &targets{
   383  		tail:      b.targets,
   384  		_break:    done,
   385  		_continue: cont,
   386  	}
   387  	b.stmt(s.Body)
   388  	b.targets = b.targets.tail
   389  	b.jump(cont)
   390  
   391  	if s.Post != nil {
   392  		b.current = cont
   393  		b.stmt(s.Post)
   394  		b.jump(loop) // back-edge
   395  	}
   396  	b.current = done
   397  }
   398  
   399  func (b *builder) rangeStmt(s *ast.RangeStmt, label *lblock) {
   400  	b.add(s.X)
   401  
   402  	if s.Key != nil {
   403  		b.add(s.Key)
   404  	}
   405  	if s.Value != nil {
   406  		b.add(s.Value)
   407  	}
   408  
   409  	//      ...
   410  	// loop:                                   (target of continue)
   411  	// 	if ... goto body else done
   412  	// body:
   413  	//      ...
   414  	// 	jump loop
   415  	// done:                                   (target of break)
   416  
   417  	loop := b.newBlock("range.loop")
   418  	b.jump(loop)
   419  	b.current = loop
   420  
   421  	body := b.newBlock("range.body")
   422  	done := b.newBlock("range.done")
   423  	b.ifelse(body, done)
   424  	b.current = body
   425  
   426  	if label != nil {
   427  		label._break = done
   428  		label._continue = loop
   429  	}
   430  	b.targets = &targets{
   431  		tail:      b.targets,
   432  		_break:    done,
   433  		_continue: loop,
   434  	}
   435  	b.stmt(s.Body)
   436  	b.targets = b.targets.tail
   437  	b.jump(loop) // back-edge
   438  	b.current = done
   439  }
   440  
   441  // -------- helpers --------
   442  
   443  // Destinations associated with unlabeled for/switch/select stmts.
   444  // We push/pop one of these as we enter/leave each construct and for
   445  // each BranchStmt we scan for the innermost target of the right type.
   446  //
   447  type targets struct {
   448  	tail         *targets // rest of stack
   449  	_break       *Block
   450  	_continue    *Block
   451  	_fallthrough *Block
   452  }
   453  
   454  // Destinations associated with a labeled block.
   455  // We populate these as labels are encountered in forward gotos or
   456  // labeled statements.
   457  //
   458  type lblock struct {
   459  	_goto     *Block
   460  	_break    *Block
   461  	_continue *Block
   462  }
   463  
   464  // labeledBlock returns the branch target associated with the
   465  // specified label, creating it if needed.
   466  //
   467  func (b *builder) labeledBlock(label *ast.Ident) *lblock {
   468  	lb := b.lblocks[label.Obj]
   469  	if lb == nil {
   470  		lb = &lblock{_goto: b.newBlock(label.Name)}
   471  		if b.lblocks == nil {
   472  			b.lblocks = make(map[*ast.Object]*lblock)
   473  		}
   474  		b.lblocks[label.Obj] = lb
   475  	}
   476  	return lb
   477  }
   478  
   479  // newBlock appends a new unconnected basic block to b.cfg's block
   480  // slice and returns it.
   481  // It does not automatically become the current block.
   482  // comment is an optional string for more readable debugging output.
   483  func (b *builder) newBlock(comment string) *Block {
   484  	g := b.cfg
   485  	block := &Block{
   486  		Index:   int32(len(g.Blocks)),
   487  		comment: comment,
   488  	}
   489  	block.Succs = block.succs2[:0]
   490  	g.Blocks = append(g.Blocks, block)
   491  	return block
   492  }
   493  
   494  func (b *builder) add(n ast.Node) {
   495  	b.current.Nodes = append(b.current.Nodes, n)
   496  }
   497  
   498  // jump adds an edge from the current block to the target block,
   499  // and sets b.current to nil.
   500  func (b *builder) jump(target *Block) {
   501  	b.current.Succs = append(b.current.Succs, target)
   502  	b.current = nil
   503  }
   504  
   505  // ifelse emits edges from the current block to the t and f blocks,
   506  // and sets b.current to nil.
   507  func (b *builder) ifelse(t, f *Block) {
   508  	b.current.Succs = append(b.current.Succs, t, f)
   509  	b.current = nil
   510  }