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