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 typ = concreteType1(s, n, make(map[*ir.Name]struct{}))
186 if typ == &noType {
187 return nil
188 }
189 if typ != nil && typ.IsInterface() {
190 base.FatalfAt(n.Pos(), "typ.IsInterface() = true; want = false; typ = %v", typ)
191 }
192 return typ
193 }
194
195
196 var noType types.Type
197
198
199
200
201 func concreteType1(s *State, n ir.Node, seen map[*ir.Name]struct{}) (outT *types.Type) {
202 nn := n
203
204 if concreteTypeDebug {
205 defer func() {
206 t := "&noType"
207 if outT != &noType {
208 t = outT.String()
209 }
210 base.Warn("concreteType1(%v) -> %v", nn, t)
211 }()
212 }
213
214 for {
215 if concreteTypeDebug {
216 base.Warn("concreteType1(%v): analyzing %v", nn, n)
217 }
218
219 if !n.Type().IsInterface() {
220 return n.Type()
221 }
222
223 switch n1 := n.(type) {
224 case *ir.ConvExpr:
225 if n1.Op() == ir.OCONVNOP {
226 if !n1.Type().IsInterface() || !types.Identical(n1.Type().Underlying(), n1.X.Type().Underlying()) {
227
228
229 base.FatalfAt(n1.Pos(), "not identical/interface types found n1.Type = %v; n1.X.Type = %v", n1.Type(), n1.X.Type())
230 }
231 n = n1.X
232 continue
233 }
234 if n1.Op() == ir.OCONVIFACE {
235 n = n1.X
236 continue
237 }
238 case *ir.InlinedCallExpr:
239 if n1.Op() == ir.OINLCALL {
240 n = n1.SingleResult()
241 continue
242 }
243 case *ir.ParenExpr:
244 n = n1.X
245 continue
246 case *ir.TypeAssertExpr:
247 n = n1.X
248 continue
249 }
250 break
251 }
252
253 if n.Op() != ir.ONAME {
254 return nil
255 }
256
257 name := n.(*ir.Name).Canonical()
258 if name.Class != ir.PAUTO {
259 return nil
260 }
261
262 if name.Op() != ir.ONAME {
263 base.FatalfAt(name.Pos(), "name.Op = %v; want = ONAME", n.Op())
264 }
265
266
267 if name.Curfn == nil {
268 base.FatalfAt(name.Pos(), "name.Curfn = nil; want not nil")
269 }
270
271 if name.Addrtaken() {
272 return nil
273 }
274
275 if _, ok := seen[name]; ok {
276 return &noType
277 }
278 seen[name] = struct{}{}
279
280 if concreteTypeDebug {
281 base.Warn("concreteType1(%v): analyzing assignments to %v", nn, name)
282 }
283
284 var typ *types.Type
285 for _, v := range s.assignments(name) {
286 var t *types.Type
287 switch v := v.(type) {
288 case *types.Type:
289 t = v
290 case ir.Node:
291 t = concreteType1(s, v, seen)
292 if t == &noType {
293 continue
294 }
295 }
296 if t == nil {
297 return nil
298 }
299
300
301
302
303
304
305
306
307
308 if typ != nil && typ != t {
309 return nil
310 }
311
312 typ = t
313 }
314
315 if typ == nil {
316
317 return &noType
318 }
319
320 return typ
321 }
322
323
324
325
326
327
328
329
330
331
332
333 type assignment any
334
335
336 type State struct {
337
338
339
340 ifaceAssignments map[*ir.Name][]assignment
341
342
343
344 ifaceCallExprAssigns map[*ir.CallExpr][]ifaceAssignRef
345
346
347 analyzedFuncs map[*ir.Func]struct{}
348 }
349
350 type ifaceAssignRef struct {
351 name *ir.Name
352 assignmentIndex int
353 returnIndex int
354 }
355
356
357 func (s *State) InlinedCall(fun *ir.Func, origCall *ir.CallExpr, inlinedCall *ir.InlinedCallExpr) {
358 if _, ok := s.analyzedFuncs[fun]; !ok {
359
360
361 return
362 }
363
364
365 s.analyze(inlinedCall.Init())
366 s.analyze(inlinedCall.Body)
367
368 refs, ok := s.ifaceCallExprAssigns[origCall]
369 if !ok {
370 return
371 }
372 delete(s.ifaceCallExprAssigns, origCall)
373
374
375 for _, ref := range refs {
376 vt := &s.ifaceAssignments[ref.name][ref.assignmentIndex]
377 if *vt != nil {
378 base.Fatalf("unexpected non-nil assignment")
379 }
380 if concreteTypeDebug {
381 base.Warn(
382 "InlinedCall(%v, %v): replacing interface node in (%v,%v) to %v (typ %v)",
383 origCall, inlinedCall, ref.name, ref.assignmentIndex,
384 inlinedCall.ReturnVars[ref.returnIndex],
385 inlinedCall.ReturnVars[ref.returnIndex].Type(),
386 )
387 }
388
389
390
391
392
393 *vt = inlinedCall.ReturnVars[ref.returnIndex]
394 }
395 }
396
397
398 func (s *State) assignments(n *ir.Name) []assignment {
399 fun := n.Curfn
400 if fun == nil {
401 base.FatalfAt(n.Pos(), "n.Curfn = <nil>")
402 }
403 if n.Class != ir.PAUTO {
404 base.FatalfAt(n.Pos(), "n.Class = %v; want = PAUTO", n.Class)
405 }
406
407 if !n.Type().IsInterface() {
408 base.FatalfAt(n.Pos(), "name passed to assignments is not of an interface type: %v", n.Type())
409 }
410
411
412 if _, ok := s.analyzedFuncs[fun]; !ok {
413 if concreteTypeDebug {
414 base.Warn("assignments(): analyzing assignments in %v func", fun)
415 }
416 if s.analyzedFuncs == nil {
417 s.ifaceAssignments = make(map[*ir.Name][]assignment)
418 s.ifaceCallExprAssigns = make(map[*ir.CallExpr][]ifaceAssignRef)
419 s.analyzedFuncs = make(map[*ir.Func]struct{})
420 }
421 s.analyzedFuncs[fun] = struct{}{}
422 s.analyze(fun.Init())
423 s.analyze(fun.Body)
424 }
425
426 return s.ifaceAssignments[n]
427 }
428
429
430 func (s *State) analyze(nodes ir.Nodes) {
431 assign := func(name ir.Node, assignment assignment) (*ir.Name, int) {
432 if name == nil || name.Op() != ir.ONAME || ir.IsBlank(name) {
433 return nil, -1
434 }
435
436 n, ok := ir.OuterValue(name).(*ir.Name)
437 if !ok || n.Curfn == nil {
438 return nil, -1
439 }
440
441
442
443 if !n.Type().IsInterface() {
444 return nil, -1
445 }
446
447 n = n.Canonical()
448 if n.Op() != ir.ONAME {
449 base.FatalfAt(n.Pos(), "n.Op = %v; want = ONAME", n.Op())
450 }
451 if n.Class != ir.PAUTO {
452 return nil, -1
453 }
454
455 switch a := assignment.(type) {
456 case nil:
457 case *types.Type:
458 if a != nil && a.IsInterface() {
459 assignment = nil
460 }
461 case ir.Node:
462
463 if ir.IsNil(a) {
464 return nil, -1
465 }
466 default:
467 base.Fatalf("unexpected type: %v", assignment)
468 }
469
470 if concreteTypeDebug {
471 base.Warn("analyze(): assignment found %v = %v", name, assignment)
472 }
473
474 s.ifaceAssignments[n] = append(s.ifaceAssignments[n], assignment)
475 return n, len(s.ifaceAssignments[n]) - 1
476 }
477
478 var do func(n ir.Node)
479 do = func(n ir.Node) {
480 switch n.Op() {
481 case ir.OAS:
482 n := n.(*ir.AssignStmt)
483 if rhs := n.Y; rhs != nil {
484 for {
485 if r, ok := rhs.(*ir.ParenExpr); ok {
486 rhs = r.X
487 continue
488 }
489 break
490 }
491 if call, ok := rhs.(*ir.CallExpr); ok && call.Fun != nil {
492 retTyp := call.Fun.Type().Results()[0].Type
493 n, idx := assign(n.X, retTyp)
494 if n != nil && retTyp.IsInterface() {
495
496
497
498 s.ifaceCallExprAssigns[call] = append(s.ifaceCallExprAssigns[call], ifaceAssignRef{n, idx, 0})
499 }
500 } else {
501 assign(n.X, rhs)
502 }
503 }
504 case ir.OAS2:
505 n := n.(*ir.AssignListStmt)
506 for i, p := range n.Lhs {
507 if n.Rhs[i] != nil {
508 assign(p, n.Rhs[i])
509 }
510 }
511 case ir.OAS2DOTTYPE:
512 n := n.(*ir.AssignListStmt)
513 if n.Rhs[0] == nil {
514 base.FatalfAt(n.Pos(), "n.Rhs[0] == nil; n = %v", n)
515 }
516 assign(n.Lhs[0], n.Rhs[0])
517 assign(n.Lhs[1], nil)
518 case ir.OAS2MAPR, ir.OAS2RECV, ir.OSELRECV2:
519 n := n.(*ir.AssignListStmt)
520 if n.Rhs[0] == nil {
521 base.FatalfAt(n.Pos(), "n.Rhs[0] == nil; n = %v", n)
522 }
523 assign(n.Lhs[0], n.Rhs[0].Type())
524 assign(n.Lhs[1], nil)
525 case ir.OAS2FUNC:
526 n := n.(*ir.AssignListStmt)
527 rhs := n.Rhs[0]
528 for {
529 if r, ok := rhs.(*ir.ParenExpr); ok {
530 rhs = r.X
531 continue
532 }
533 break
534 }
535 if call, ok := rhs.(*ir.CallExpr); ok {
536 for i, p := range n.Lhs {
537 retTyp := call.Fun.Type().Results()[i].Type
538 n, idx := assign(p, retTyp)
539 if n != nil && retTyp.IsInterface() {
540
541
542
543 s.ifaceCallExprAssigns[call] = append(s.ifaceCallExprAssigns[call], ifaceAssignRef{n, idx, i})
544 }
545 }
546 } else if call, ok := rhs.(*ir.InlinedCallExpr); ok {
547 for i, p := range n.Lhs {
548 assign(p, call.ReturnVars[i])
549 }
550 } else {
551 base.FatalfAt(n.Pos(), "unexpected type %T in OAS2FUNC Rhs[0]", call)
552 }
553 case ir.ORANGE:
554 n := n.(*ir.RangeStmt)
555 xTyp := n.X.Type()
556
557
558 if xTyp.IsPtr() && xTyp.Elem().IsArray() {
559 xTyp = xTyp.Elem()
560 }
561
562 if xTyp.IsArray() || xTyp.IsSlice() {
563 assign(n.Key, nil)
564 assign(n.Value, xTyp.Elem())
565 } else if xTyp.IsChan() {
566 assign(n.Key, xTyp.Elem())
567 base.AssertfAt(n.Value == nil, n.Pos(), "n.Value != nil in range over chan")
568 } else if xTyp.IsMap() {
569 assign(n.Key, xTyp.Key())
570 assign(n.Value, xTyp.Elem())
571 } else if xTyp.IsInteger() || xTyp.IsString() {
572
573 assign(n.Key, nil)
574 assign(n.Value, nil)
575 } else {
576
577
578 base.FatalfAt(n.Pos(), "range over unexpected type %v", n.X.Type())
579 }
580 case ir.OSWITCH:
581 n := n.(*ir.SwitchStmt)
582 if guard, ok := n.Tag.(*ir.TypeSwitchGuard); ok {
583 for _, v := range n.Cases {
584 if v.Var == nil {
585 base.Assert(guard.Tag == nil)
586 continue
587 }
588 assign(v.Var, guard.X)
589 }
590 }
591 case ir.OCLOSURE:
592 n := n.(*ir.ClosureExpr)
593 if _, ok := s.analyzedFuncs[n.Func]; !ok {
594 s.analyzedFuncs[n.Func] = struct{}{}
595 ir.Visit(n.Func, do)
596 }
597 }
598 }
599 ir.VisitList(nodes, do)
600 }
601
View as plain text