Skip to content

Commit fb020a0

Browse files
committed
go/cfg: record block kind and associated statement
This change adds Block.Kind, and enumeration of possible control structures that give rise to blocks, and Block.Stmt, which records the syntax node associated with it. This allows clients to reconstruct the control more accurately, and compute positions. It also adds a CFG.Digraph method to dump the graph in AT&T GraphViz form for debugging convenience, and a simple helper command to call this function. Also, stop using ast.Object. Fixes golang/go#53367 Change-Id: I19557d636eb4c620899463c489411c360540289b Reviewed-on: https://go-review.googlesource.com/c/tools/+/555255 Reviewed-by: Robert Findley <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Tim King <[email protected]>
1 parent 054c06d commit fb020a0

File tree

4 files changed

+288
-52
lines changed

4 files changed

+288
-52
lines changed

go/cfg/builder.go

Lines changed: 46 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ type builder struct {
1616
cfg *CFG
1717
mayReturn func(*ast.CallExpr) bool
1818
current *Block
19-
lblocks map[*ast.Object]*lblock // labeled blocks
20-
targets *targets // linked stack of branch targets
19+
lblocks map[string]*lblock // labeled blocks
20+
targets *targets // linked stack of branch targets
2121
}
2222

2323
func (b *builder) stmt(_s ast.Stmt) {
@@ -42,7 +42,7 @@ start:
4242
b.add(s)
4343
if call, ok := s.X.(*ast.CallExpr); ok && !b.mayReturn(call) {
4444
// Calls to panic, os.Exit, etc, never return.
45-
b.current = b.newBlock("unreachable.call")
45+
b.current = b.newBlock(KindUnreachable, s)
4646
}
4747

4848
case *ast.DeclStmt:
@@ -57,15 +57,15 @@ start:
5757
}
5858

5959
case *ast.LabeledStmt:
60-
label = b.labeledBlock(s.Label)
60+
label = b.labeledBlock(s.Label, s)
6161
b.jump(label._goto)
6262
b.current = label._goto
6363
_s = s.Stmt
6464
goto start // effectively: tailcall stmt(g, s.Stmt, label)
6565

6666
case *ast.ReturnStmt:
6767
b.add(s)
68-
b.current = b.newBlock("unreachable.return")
68+
b.current = b.newBlock(KindUnreachable, s)
6969

7070
case *ast.BranchStmt:
7171
b.branchStmt(s)
@@ -77,11 +77,11 @@ start:
7777
if s.Init != nil {
7878
b.stmt(s.Init)
7979
}
80-
then := b.newBlock("if.then")
81-
done := b.newBlock("if.done")
80+
then := b.newBlock(KindIfThen, s)
81+
done := b.newBlock(KindIfDone, s)
8282
_else := done
8383
if s.Else != nil {
84-
_else = b.newBlock("if.else")
84+
_else = b.newBlock(KindIfElse, s)
8585
}
8686
b.add(s.Cond)
8787
b.ifelse(then, _else)
@@ -128,7 +128,7 @@ func (b *builder) branchStmt(s *ast.BranchStmt) {
128128
switch s.Tok {
129129
case token.BREAK:
130130
if s.Label != nil {
131-
if lb := b.labeledBlock(s.Label); lb != nil {
131+
if lb := b.labeledBlock(s.Label, nil); lb != nil {
132132
block = lb._break
133133
}
134134
} else {
@@ -139,7 +139,7 @@ func (b *builder) branchStmt(s *ast.BranchStmt) {
139139

140140
case token.CONTINUE:
141141
if s.Label != nil {
142-
if lb := b.labeledBlock(s.Label); lb != nil {
142+
if lb := b.labeledBlock(s.Label, nil); lb != nil {
143143
block = lb._continue
144144
}
145145
} else {
@@ -155,14 +155,14 @@ func (b *builder) branchStmt(s *ast.BranchStmt) {
155155

156156
case token.GOTO:
157157
if s.Label != nil {
158-
block = b.labeledBlock(s.Label)._goto
158+
block = b.labeledBlock(s.Label, nil)._goto
159159
}
160160
}
161-
if block == nil {
162-
block = b.newBlock("undefined.branch")
161+
if block == nil { // ill-typed (e.g. undefined label)
162+
block = b.newBlock(KindUnreachable, s)
163163
}
164164
b.jump(block)
165-
b.current = b.newBlock("unreachable.branch")
165+
b.current = b.newBlock(KindUnreachable, s)
166166
}
167167

168168
func (b *builder) switchStmt(s *ast.SwitchStmt, label *lblock) {
@@ -172,7 +172,7 @@ func (b *builder) switchStmt(s *ast.SwitchStmt, label *lblock) {
172172
if s.Tag != nil {
173173
b.add(s.Tag)
174174
}
175-
done := b.newBlock("switch.done")
175+
done := b.newBlock(KindSwitchDone, s)
176176
if label != nil {
177177
label._break = done
178178
}
@@ -188,13 +188,13 @@ func (b *builder) switchStmt(s *ast.SwitchStmt, label *lblock) {
188188
for i, clause := range s.Body.List {
189189
body := fallthru
190190
if body == nil {
191-
body = b.newBlock("switch.body") // first case only
191+
body = b.newBlock(KindSwitchCaseBody, clause) // first case only
192192
}
193193

194194
// Preallocate body block for the next case.
195195
fallthru = done
196196
if i+1 < ncases {
197-
fallthru = b.newBlock("switch.body")
197+
fallthru = b.newBlock(KindSwitchCaseBody, s.Body.List[i+1])
198198
}
199199

200200
cc := clause.(*ast.CaseClause)
@@ -208,7 +208,7 @@ func (b *builder) switchStmt(s *ast.SwitchStmt, label *lblock) {
208208

209209
var nextCond *Block
210210
for _, cond := range cc.List {
211-
nextCond = b.newBlock("switch.next")
211+
nextCond = b.newBlock(KindSwitchNextCase, cc)
212212
b.add(cond) // one half of the tag==cond condition
213213
b.ifelse(body, nextCond)
214214
b.current = nextCond
@@ -247,7 +247,7 @@ func (b *builder) typeSwitchStmt(s *ast.TypeSwitchStmt, label *lblock) {
247247
b.add(s.Assign)
248248
}
249249

250-
done := b.newBlock("typeswitch.done")
250+
done := b.newBlock(KindSwitchDone, s)
251251
if label != nil {
252252
label._break = done
253253
}
@@ -258,10 +258,10 @@ func (b *builder) typeSwitchStmt(s *ast.TypeSwitchStmt, label *lblock) {
258258
default_ = cc
259259
continue
260260
}
261-
body := b.newBlock("typeswitch.body")
261+
body := b.newBlock(KindSwitchCaseBody, cc)
262262
var next *Block
263263
for _, casetype := range cc.List {
264-
next = b.newBlock("typeswitch.next")
264+
next = b.newBlock(KindSwitchNextCase, cc)
265265
// casetype is a type, so don't call b.add(casetype).
266266
// This block logically contains a type assertion,
267267
// x.(casetype), but it's unclear how to represent x.
@@ -300,7 +300,7 @@ func (b *builder) selectStmt(s *ast.SelectStmt, label *lblock) {
300300
}
301301
}
302302

303-
done := b.newBlock("select.done")
303+
done := b.newBlock(KindSelectDone, s)
304304
if label != nil {
305305
label._break = done
306306
}
@@ -312,8 +312,8 @@ func (b *builder) selectStmt(s *ast.SelectStmt, label *lblock) {
312312
defaultBody = &clause.Body
313313
continue
314314
}
315-
body := b.newBlock("select.body")
316-
next := b.newBlock("select.next")
315+
body := b.newBlock(KindSelectCaseBody, clause)
316+
next := b.newBlock(KindSelectAfterCase, clause)
317317
b.ifelse(body, next)
318318
b.current = body
319319
b.targets = &targets{
@@ -358,15 +358,15 @@ func (b *builder) forStmt(s *ast.ForStmt, label *lblock) {
358358
if s.Init != nil {
359359
b.stmt(s.Init)
360360
}
361-
body := b.newBlock("for.body")
362-
done := b.newBlock("for.done") // target of 'break'
363-
loop := body // target of back-edge
361+
body := b.newBlock(KindForBody, s)
362+
done := b.newBlock(KindForDone, s) // target of 'break'
363+
loop := body // target of back-edge
364364
if s.Cond != nil {
365-
loop = b.newBlock("for.loop")
365+
loop = b.newBlock(KindForLoop, s)
366366
}
367367
cont := loop // target of 'continue'
368368
if s.Post != nil {
369-
cont = b.newBlock("for.post")
369+
cont = b.newBlock(KindForPost, s)
370370
}
371371
if label != nil {
372372
label._break = done
@@ -414,12 +414,12 @@ func (b *builder) rangeStmt(s *ast.RangeStmt, label *lblock) {
414414
// jump loop
415415
// done: (target of break)
416416

417-
loop := b.newBlock("range.loop")
417+
loop := b.newBlock(KindRangeLoop, s)
418418
b.jump(loop)
419419
b.current = loop
420420

421-
body := b.newBlock("range.body")
422-
done := b.newBlock("range.done")
421+
body := b.newBlock(KindRangeBody, s)
422+
done := b.newBlock(KindRangeDone, s)
423423
b.ifelse(body, done)
424424
b.current = body
425425

@@ -461,14 +461,19 @@ type lblock struct {
461461

462462
// labeledBlock returns the branch target associated with the
463463
// specified label, creating it if needed.
464-
func (b *builder) labeledBlock(label *ast.Ident) *lblock {
465-
lb := b.lblocks[label.Obj]
464+
func (b *builder) labeledBlock(label *ast.Ident, stmt *ast.LabeledStmt) *lblock {
465+
lb := b.lblocks[label.Name]
466466
if lb == nil {
467-
lb = &lblock{_goto: b.newBlock(label.Name)}
467+
lb = &lblock{_goto: b.newBlock(KindLabel, nil)}
468468
if b.lblocks == nil {
469-
b.lblocks = make(map[*ast.Object]*lblock)
469+
b.lblocks = make(map[string]*lblock)
470470
}
471-
b.lblocks[label.Obj] = lb
471+
b.lblocks[label.Name] = lb
472+
}
473+
// Fill in the label later (in case of forward goto).
474+
// Stmt may be set already if labels are duplicated (ill-typed).
475+
if stmt != nil && lb._goto.Stmt == nil {
476+
lb._goto.Stmt = stmt
472477
}
473478
return lb
474479
}
@@ -477,11 +482,12 @@ func (b *builder) labeledBlock(label *ast.Ident) *lblock {
477482
// slice and returns it.
478483
// It does not automatically become the current block.
479484
// comment is an optional string for more readable debugging output.
480-
func (b *builder) newBlock(comment string) *Block {
485+
func (b *builder) newBlock(kind BlockKind, stmt ast.Stmt) *Block {
481486
g := b.cfg
482487
block := &Block{
483-
Index: int32(len(g.Blocks)),
484-
comment: comment,
488+
Index: int32(len(g.Blocks)),
489+
Kind: kind,
490+
Stmt: stmt,
485491
}
486492
block.Succs = block.succs2[:0]
487493
g.Blocks = append(g.Blocks, block)

0 commit comments

Comments
 (0)