1
2
3
4
5
6
7
8
9
10
11
12 package devirtualize
13
14 import (
15 "cmd/compile/internal/base"
16 "cmd/compile/internal/ir"
17 "cmd/compile/internal/typecheck"
18 "cmd/compile/internal/types"
19 )
20
21 const go126ImprovedConcreteTypeAnalysis = true
22
23
24
25 func StaticCall(s *State, call *ir.CallExpr) {
26
27
28
29
30
31
32
33
34
35
36 if call.GoDefer {
37 return
38 }
39
40 if call.Op() != ir.OCALLINTER {
41 return
42 }
43
44 sel := call.Fun.(*ir.SelectorExpr)
45 var typ *types.Type
46 if go126ImprovedConcreteTypeAnalysis {
47 typ = concreteType(s, sel.X)
48 if typ == nil {
49 return
50 }
51
52
53
54
55
56
57 if !typecheck.Implements(typ, sel.X.Type()) {
58 return
59 }
60 } else {
61 r := ir.StaticValue(sel.X)
62 if r.Op() != ir.OCONVIFACE {
63 return
64 }
65 recv := r.(*ir.ConvExpr)
66 typ = recv.X.Type()
67 if typ.IsInterface() {
68 return
69 }
70 }
71
72
73
74
75 if typ.IsShape() {
76 return
77 }
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94 if typ.HasShape() {
95 if base.Flag.LowerM != 0 {
96 base.WarnfAt(call.Pos(), "cannot devirtualize %v: shaped receiver %v", call, typ)
97 }
98 return
99 }
100
101
102
103
104
105
106
107
108
109
110
111
112
113 if sel.X.Type().HasShape() {
114 if base.Flag.LowerM != 0 {
115 base.WarnfAt(call.Pos(), "cannot devirtualize %v: shaped interface %v", call, sel.X.Type())
116 }
117 return
118 }
119
120 dt := ir.NewTypeAssertExpr(sel.Pos(), sel.X, typ)
121
122 if go126ImprovedConcreteTypeAnalysis {
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137 dt.UseNilPanic = true
138 dt.SetPos(call.Pos())
139 }
140
141 x := typecheck.XDotMethod(sel.Pos(), dt, sel.Sel, true)
142 switch x.Op() {
143 case ir.ODOTMETH:
144 if base.Flag.LowerM != 0 {
145 base.WarnfAt(call.Pos(), "devirtualizing %v to %v", sel, typ)
146 }
147 call.SetOp(ir.OCALLMETH)
148 call.Fun = x
149 case ir.ODOTINTER:
150
151 if base.Flag.LowerM != 0 {
152 base.WarnfAt(call.Pos(), "partially devirtualizing %v to %v", sel, typ)
153 }
154 call.SetOp(ir.OCALLINTER)
155 call.Fun = x
156 default:
157 base.FatalfAt(call.Pos(), "failed to devirtualize %v (%v)", x, x.Op())
158 }
159
160
161
162
163
164
165
166 types.CheckSize(x.Type())
167 switch ft := x.Type(); ft.NumResults() {
168 case 0:
169 case 1:
170 call.SetType(ft.Result(0).Type)
171 default:
172 call.SetType(ft.ResultsTuple())
173 }
174
175
176 typecheck.FixMethodCall(call)
177 }
178
179 const concreteTypeDebug = false
180
181
182
183
184 func concreteType(s *State, n ir.Node) (typ *types.Type) {
185 if concreteTypeDebug {
186 base.Warn("concreteType(%v) - analyzing", n)
187 defer func() {
188 t := typ.String()
189 if typ == nil {
190 t = "<nil> (unknown static type)"
191 }
192 base.Warn("concreteType(%v) -> %v", n, t)
193 }()
194 }
195
196 typ = concreteType1(s, n, make(map[*ir.Name]struct{}))
197 if typ == &noType {
198 return nil
199 }
200 if typ != nil && typ.IsInterface() {
201 base.FatalfAt(n.Pos(), "typ.IsInterface() = true; want = false; typ = %v", typ)
202 }
203 return typ
204 }
205
206
207 var noType types.Type
208
209
210
211
212 func concreteType1(s *State, n ir.Node, seen map[*ir.Name]struct{}) (outT *types.Type) {
213 nn := n
214
215 if concreteTypeDebug {
216 defer func() {
217 t := "&noType"
218 if outT != &noType {
219 t = outT.String()
220 }
221 if outT == nil {
222 t = "<nil> (unknown static type)"
223 }
224 base.Warn("concreteType1(%v) -> %v", nn, t)
225 }()
226 }
227
228 for {
229 if concreteTypeDebug {
230 base.Warn("concreteType1(%v): analyzing %v", nn, n)
231 }
232
233 if !n.Type().IsInterface() {
234 return n.Type()
235 }
236
237 switch n1 := n.(type) {
238 case *ir.ConvExpr:
239 if n1.Op() == ir.OCONVNOP {
240 if !n1.Type().IsInterface() || !types.Identical(n1.Type().Underlying(), n1.X.Type().Underlying()) {
241
242
243 base.FatalfAt(n1.Pos(), "not identical/interface types found n1.Type = %v; n1.X.Type = %v", n1.Type(), n1.X.Type())
244 }
245 n = n1.X
246 continue
247 }
248 if n1.Op() == ir.OCONVIFACE {
249 n = n1.X
250 continue
251 }
252 case *ir.InlinedCallExpr:
253 if n1.Op() == ir.OINLCALL {
254 n = n1.SingleResult()
255 continue
256 }
257 case *ir.ParenExpr:
258 n = n1.X
259 continue
260 case *ir.TypeAssertExpr:
261 n = n1.X
262 continue
263 }
264 break
265 }
266
267 if n.Op() != ir.ONAME {
268 return nil
269 }
270
271 name := n.(*ir.Name).Canonical()
272 if name.Class != ir.PAUTO {
273 return nil
274 }
275
276 if name.Op() != ir.ONAME {
277 base.FatalfAt(name.Pos(), "name.Op = %v; want = ONAME", n.Op())
278 }
279
280
281 if name.Curfn == nil {
282 base.FatalfAt(name.Pos(), "name.Curfn = nil; want not nil")
283 }
284
285 if name.Addrtaken() {
286 return nil
287 }
288
289 if _, ok := seen[name]; ok {
290 return &noType
291 }
292 seen[name] = struct{}{}
293
294 if concreteTypeDebug {
295 base.Warn("concreteType1(%v): analyzing assignments to %v", nn, name)
296 }
297
298 var typ *types.Type
299 for _, v := range s.assignments(name) {
300 var t *types.Type
301 switch v := v.(type) {
302 case *types.Type:
303 t = v
304 case ir.Node:
305 t = concreteType1(s, v, seen)
306 if t == &noType {
307 continue
308 }
309 }
310 if t == nil {
311 return nil
312 }
313
314
315
316
317
318
319
320
321
322 if typ != nil && typ != t {
323 return nil
324 }
325
326 typ = t
327 }
328
329 if typ == nil {
330
331 return &noType
332 }
333
334 return typ
335 }
336
337
338
339
340
341
342
343
344
345
346
347 type assignment any
348
349
350 type State struct {
351
352
353
354 ifaceAssignments map[*ir.Name][]assignment
355
356
357
358 ifaceCallExprAssigns map[*ir.CallExpr][]ifaceAssignRef
359
360
361 analyzedFuncs map[*ir.Func]struct{}
362 }
363
364 type ifaceAssignRef struct {
365 name *ir.Name
366 assignmentIndex int
367 returnIndex int
368 }
369
370
371 func (s *State) InlinedCall(fun *ir.Func, origCall *ir.CallExpr, inlinedCall *ir.InlinedCallExpr) {
372 if _, ok := s.analyzedFuncs[fun]; !ok {
373
374
375 return
376 }
377
378
379 s.analyze(inlinedCall.Init())
380 s.analyze(inlinedCall.Body)
381
382 refs, ok := s.ifaceCallExprAssigns[origCall]
383 if !ok {
384 return
385 }
386 delete(s.ifaceCallExprAssigns, origCall)
387
388
389 for _, ref := range refs {
390 vt := &s.ifaceAssignments[ref.name][ref.assignmentIndex]
391 if *vt != nil {
392 base.Fatalf("unexpected non-nil assignment")
393 }
394 if concreteTypeDebug {
395 base.Warn(
396 "InlinedCall(%v, %v): replacing interface node in (%v,%v) to %v (typ %v)",
397 origCall, inlinedCall, ref.name, ref.assignmentIndex,
398 inlinedCall.ReturnVars[ref.returnIndex],
399 inlinedCall.ReturnVars[ref.returnIndex].Type(),
400 )
401 }
402
403
404
405
406
407 *vt = inlinedCall.ReturnVars[ref.returnIndex]
408 }
409 }
410
411
412 func (s *State) assignments(n *ir.Name) []assignment {
413 fun := n.Curfn
414 if fun == nil {
415 base.FatalfAt(n.Pos(), "n.Curfn = <nil>")
416 }
417 if n.Class != ir.PAUTO {
418 base.FatalfAt(n.Pos(), "n.Class = %v; want = PAUTO", n.Class)
419 }
420
421 if !n.Type().IsInterface() {
422 base.FatalfAt(n.Pos(), "name passed to assignments is not of an interface type: %v", n.Type())
423 }
424
425
426 if _, ok := s.analyzedFuncs[fun]; !ok {
427 if concreteTypeDebug {
428 base.Warn("assignments(): analyzing assignments in %v func", fun)
429 }
430 if s.analyzedFuncs == nil {
431 s.ifaceAssignments = make(map[*ir.Name][]assignment)
432 s.ifaceCallExprAssigns = make(map[*ir.CallExpr][]ifaceAssignRef)
433 s.analyzedFuncs = make(map[*ir.Func]struct{})
434 }
435 s.analyzedFuncs[fun] = struct{}{}
436 s.analyze(fun.Init())
437 s.analyze(fun.Body)
438 }
439
440 return s.ifaceAssignments[n]
441 }
442
443
444 func (s *State) analyze(nodes ir.Nodes) {
445 assign := func(name ir.Node, assignment assignment) (*ir.Name, int) {
446 if name == nil || name.Op() != ir.ONAME || ir.IsBlank(name) {
447 return nil, -1
448 }
449
450 n, ok := ir.OuterValue(name).(*ir.Name)
451 if !ok || n.Curfn == nil {
452 return nil, -1
453 }
454
455
456
457 if !n.Type().IsInterface() {
458 return nil, -1
459 }
460
461 n = n.Canonical()
462 if n.Op() != ir.ONAME {
463 base.FatalfAt(n.Pos(), "n.Op = %v; want = ONAME", n.Op())
464 }
465 if n.Class != ir.PAUTO {
466 return nil, -1
467 }
468
469 switch a := assignment.(type) {
470 case nil:
471 case *types.Type:
472 if a != nil && a.IsInterface() {
473 assignment = nil
474 }
475 case ir.Node:
476
477 if ir.IsNil(a) {
478 return nil, -1
479 }
480 default:
481 base.Fatalf("unexpected type: %v", assignment)
482 }
483
484 if concreteTypeDebug {
485 base.Warn("analyze(): assignment found %v = %v", name, assignment)
486 }
487
488 s.ifaceAssignments[n] = append(s.ifaceAssignments[n], assignment)
489 return n, len(s.ifaceAssignments[n]) - 1
490 }
491
492 var do func(n ir.Node)
493 do = func(n ir.Node) {
494 switch n.Op() {
495 case ir.OAS:
496 n := n.(*ir.AssignStmt)
497 if rhs := n.Y; rhs != nil {
498 for {
499 if r, ok := rhs.(*ir.ParenExpr); ok {
500 rhs = r.X
501 continue
502 }
503 break
504 }
505 if call, ok := rhs.(*ir.CallExpr); ok && call.Fun != nil {
506 retTyp := call.Fun.Type().Results()[0].Type
507 n, idx := assign(n.X, retTyp)
508 if n != nil && retTyp.IsInterface() {
509
510
511
512 s.ifaceCallExprAssigns[call] = append(s.ifaceCallExprAssigns[call], ifaceAssignRef{n, idx, 0})
513 }
514 } else {
515 assign(n.X, rhs)
516 }
517 }
518 case ir.OAS2:
519 n := n.(*ir.AssignListStmt)
520 for i, p := range n.Lhs {
521 if n.Rhs[i] != nil {
522 assign(p, n.Rhs[i])
523 }
524 }
525 case ir.OAS2DOTTYPE:
526 n := n.(*ir.AssignListStmt)
527 if n.Rhs[0] == nil {
528 base.FatalfAt(n.Pos(), "n.Rhs[0] == nil; n = %v", n)
529 }
530 assign(n.Lhs[0], n.Rhs[0])
531 assign(n.Lhs[1], nil)
532 case ir.OAS2MAPR, ir.OAS2RECV, ir.OSELRECV2:
533 n := n.(*ir.AssignListStmt)
534 if n.Rhs[0] == nil {
535 base.FatalfAt(n.Pos(), "n.Rhs[0] == nil; n = %v", n)
536 }
537 assign(n.Lhs[0], n.Rhs[0].Type())
538 assign(n.Lhs[1], nil)
539 case ir.OAS2FUNC:
540 n := n.(*ir.AssignListStmt)
541 rhs := n.Rhs[0]
542 for {
543 if r, ok := rhs.(*ir.ParenExpr); ok {
544 rhs = r.X
545 continue
546 }
547 break
548 }
549 if call, ok := rhs.(*ir.CallExpr); ok {
550 for i, p := range n.Lhs {
551 retTyp := call.Fun.Type().Results()[i].Type
552 n, idx := assign(p, retTyp)
553 if n != nil && retTyp.IsInterface() {
554
555
556
557 s.ifaceCallExprAssigns[call] = append(s.ifaceCallExprAssigns[call], ifaceAssignRef{n, idx, i})
558 }
559 }
560 } else if call, ok := rhs.(*ir.InlinedCallExpr); ok {
561 for i, p := range n.Lhs {
562 assign(p, call.ReturnVars[i])
563 }
564 } else {
565 base.FatalfAt(n.Pos(), "unexpected type %T in OAS2FUNC Rhs[0]", call)
566 }
567 case ir.ORANGE:
568 n := n.(*ir.RangeStmt)
569 xTyp := n.X.Type()
570
571
572 if xTyp.IsPtr() && xTyp.Elem().IsArray() {
573 xTyp = xTyp.Elem()
574 }
575
576 if xTyp.IsArray() || xTyp.IsSlice() {
577 assign(n.Key, nil)
578 assign(n.Value, xTyp.Elem())
579 } else if xTyp.IsChan() {
580 assign(n.Key, xTyp.Elem())
581 base.AssertfAt(n.Value == nil, n.Pos(), "n.Value != nil in range over chan")
582 } else if xTyp.IsMap() {
583 assign(n.Key, xTyp.Key())
584 assign(n.Value, xTyp.Elem())
585 } else if xTyp.IsInteger() || xTyp.IsString() {
586
587 assign(n.Key, nil)
588 assign(n.Value, nil)
589 } else {
590
591
592 base.FatalfAt(n.Pos(), "range over unexpected type %v", n.X.Type())
593 }
594 case ir.OSWITCH:
595 n := n.(*ir.SwitchStmt)
596 if guard, ok := n.Tag.(*ir.TypeSwitchGuard); ok {
597 for _, v := range n.Cases {
598 if v.Var == nil {
599 base.Assert(guard.Tag == nil)
600 continue
601 }
602 assign(v.Var, guard.X)
603 }
604 }
605 case ir.OCLOSURE:
606 n := n.(*ir.ClosureExpr)
607 if _, ok := s.analyzedFuncs[n.Func]; !ok {
608 s.analyzedFuncs[n.Func] = struct{}{}
609 ir.Visit(n.Func, do)
610 }
611 }
612 }
613 ir.VisitList(nodes, do)
614 }
615
View as plain text