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 || (typ != nil && !types.Identical(typ, t)) {
297 return nil
298 }
299 typ = t
300 }
301
302 if typ == nil {
303
304 return &noType
305 }
306
307 return typ
308 }
309
310
311
312
313
314
315
316
317
318
319
320 type assignment any
321
322
323 type State struct {
324
325
326
327 ifaceAssignments map[*ir.Name][]assignment
328
329
330
331 ifaceCallExprAssigns map[*ir.CallExpr][]ifaceAssignRef
332
333
334 analyzedFuncs map[*ir.Func]struct{}
335 }
336
337 type ifaceAssignRef struct {
338 name *ir.Name
339 assignmentIndex int
340 returnIndex int
341 }
342
343
344 func (s *State) InlinedCall(fun *ir.Func, origCall *ir.CallExpr, inlinedCall *ir.InlinedCallExpr) {
345 if _, ok := s.analyzedFuncs[fun]; !ok {
346
347
348 return
349 }
350
351
352 s.analyze(inlinedCall.Init())
353 s.analyze(inlinedCall.Body)
354
355 refs, ok := s.ifaceCallExprAssigns[origCall]
356 if !ok {
357 return
358 }
359 delete(s.ifaceCallExprAssigns, origCall)
360
361
362 for _, ref := range refs {
363 vt := &s.ifaceAssignments[ref.name][ref.assignmentIndex]
364 if *vt != nil {
365 base.Fatalf("unexpected non-nil assignment")
366 }
367 if concreteTypeDebug {
368 base.Warn(
369 "InlinedCall(%v, %v): replacing interface node in (%v,%v) to %v (typ %v)",
370 origCall, inlinedCall, ref.name, ref.assignmentIndex,
371 inlinedCall.ReturnVars[ref.returnIndex],
372 inlinedCall.ReturnVars[ref.returnIndex].Type(),
373 )
374 }
375
376
377
378
379
380 *vt = inlinedCall.ReturnVars[ref.returnIndex]
381 }
382 }
383
384
385 func (s *State) assignments(n *ir.Name) []assignment {
386 fun := n.Curfn
387 if fun == nil {
388 base.FatalfAt(n.Pos(), "n.Curfn = <nil>")
389 }
390 if n.Class != ir.PAUTO {
391 base.FatalfAt(n.Pos(), "n.Class = %v; want = PAUTO", n.Class)
392 }
393
394 if !n.Type().IsInterface() {
395 base.FatalfAt(n.Pos(), "name passed to assignments is not of an interface type: %v", n.Type())
396 }
397
398
399 if _, ok := s.analyzedFuncs[fun]; !ok {
400 if concreteTypeDebug {
401 base.Warn("assignments(): analyzing assignments in %v func", fun)
402 }
403 if s.analyzedFuncs == nil {
404 s.ifaceAssignments = make(map[*ir.Name][]assignment)
405 s.ifaceCallExprAssigns = make(map[*ir.CallExpr][]ifaceAssignRef)
406 s.analyzedFuncs = make(map[*ir.Func]struct{})
407 }
408 s.analyzedFuncs[fun] = struct{}{}
409 s.analyze(fun.Init())
410 s.analyze(fun.Body)
411 }
412
413 return s.ifaceAssignments[n]
414 }
415
416
417 func (s *State) analyze(nodes ir.Nodes) {
418 assign := func(name ir.Node, assignment assignment) (*ir.Name, int) {
419 if name == nil || name.Op() != ir.ONAME || ir.IsBlank(name) {
420 return nil, -1
421 }
422
423 n, ok := ir.OuterValue(name).(*ir.Name)
424 if !ok || n.Curfn == nil {
425 return nil, -1
426 }
427
428
429
430 if !n.Type().IsInterface() {
431 return nil, -1
432 }
433
434 n = n.Canonical()
435 if n.Op() != ir.ONAME {
436 base.FatalfAt(n.Pos(), "n.Op = %v; want = ONAME", n.Op())
437 }
438 if n.Class != ir.PAUTO {
439 return nil, -1
440 }
441
442 switch a := assignment.(type) {
443 case nil:
444 case *types.Type:
445 if a != nil && a.IsInterface() {
446 assignment = nil
447 }
448 case ir.Node:
449
450 if ir.IsNil(a) {
451 return nil, -1
452 }
453 default:
454 base.Fatalf("unexpected type: %v", assignment)
455 }
456
457 if concreteTypeDebug {
458 base.Warn("analyze(): assignment found %v = %v", name, assignment)
459 }
460
461 s.ifaceAssignments[n] = append(s.ifaceAssignments[n], assignment)
462 return n, len(s.ifaceAssignments[n]) - 1
463 }
464
465 var do func(n ir.Node)
466 do = func(n ir.Node) {
467 switch n.Op() {
468 case ir.OAS:
469 n := n.(*ir.AssignStmt)
470 if rhs := n.Y; rhs != nil {
471 for {
472 if r, ok := rhs.(*ir.ParenExpr); ok {
473 rhs = r.X
474 continue
475 }
476 break
477 }
478 if call, ok := rhs.(*ir.CallExpr); ok && call.Fun != nil {
479 retTyp := call.Fun.Type().Results()[0].Type
480 n, idx := assign(n.X, retTyp)
481 if n != nil && retTyp.IsInterface() {
482
483
484
485 s.ifaceCallExprAssigns[call] = append(s.ifaceCallExprAssigns[call], ifaceAssignRef{n, idx, 0})
486 }
487 } else {
488 assign(n.X, rhs)
489 }
490 }
491 case ir.OAS2:
492 n := n.(*ir.AssignListStmt)
493 for i, p := range n.Lhs {
494 if n.Rhs[i] != nil {
495 assign(p, n.Rhs[i])
496 }
497 }
498 case ir.OAS2DOTTYPE:
499 n := n.(*ir.AssignListStmt)
500 if n.Rhs[0] == nil {
501 base.FatalfAt(n.Pos(), "n.Rhs[0] == nil; n = %v", n)
502 }
503 assign(n.Lhs[0], n.Rhs[0])
504 assign(n.Lhs[1], nil)
505 case ir.OAS2MAPR, ir.OAS2RECV, ir.OSELRECV2:
506 n := n.(*ir.AssignListStmt)
507 if n.Rhs[0] == nil {
508 base.FatalfAt(n.Pos(), "n.Rhs[0] == nil; n = %v", n)
509 }
510 assign(n.Lhs[0], n.Rhs[0].Type())
511 assign(n.Lhs[1], nil)
512 case ir.OAS2FUNC:
513 n := n.(*ir.AssignListStmt)
514 rhs := n.Rhs[0]
515 for {
516 if r, ok := rhs.(*ir.ParenExpr); ok {
517 rhs = r.X
518 continue
519 }
520 break
521 }
522 if call, ok := rhs.(*ir.CallExpr); ok {
523 for i, p := range n.Lhs {
524 retTyp := call.Fun.Type().Results()[i].Type
525 n, idx := assign(p, retTyp)
526 if n != nil && retTyp.IsInterface() {
527
528
529
530 s.ifaceCallExprAssigns[call] = append(s.ifaceCallExprAssigns[call], ifaceAssignRef{n, idx, i})
531 }
532 }
533 } else if call, ok := rhs.(*ir.InlinedCallExpr); ok {
534 for i, p := range n.Lhs {
535 assign(p, call.ReturnVars[i])
536 }
537 } else {
538 base.FatalfAt(n.Pos(), "unexpected type %T in OAS2FUNC Rhs[0]", call)
539 }
540 case ir.ORANGE:
541 n := n.(*ir.RangeStmt)
542 xTyp := n.X.Type()
543
544
545 if xTyp.IsPtr() && xTyp.Elem().IsArray() {
546 xTyp = xTyp.Elem()
547 }
548
549 if xTyp.IsArray() || xTyp.IsSlice() {
550 assign(n.Key, nil)
551 assign(n.Value, xTyp.Elem())
552 } else if xTyp.IsChan() {
553 assign(n.Key, xTyp.Elem())
554 base.AssertfAt(n.Value == nil, n.Pos(), "n.Value != nil in range over chan")
555 } else if xTyp.IsMap() {
556 assign(n.Key, xTyp.Key())
557 assign(n.Value, xTyp.Elem())
558 } else if xTyp.IsInteger() || xTyp.IsString() {
559
560 assign(n.Key, nil)
561 assign(n.Value, nil)
562 } else {
563
564
565 base.FatalfAt(n.Pos(), "range over unexpected type %v", n.X.Type())
566 }
567 case ir.OSWITCH:
568 n := n.(*ir.SwitchStmt)
569 if guard, ok := n.Tag.(*ir.TypeSwitchGuard); ok {
570 for _, v := range n.Cases {
571 if v.Var == nil {
572 base.Assert(guard.Tag == nil)
573 continue
574 }
575 assign(v.Var, guard.X)
576 }
577 }
578 case ir.OCLOSURE:
579 n := n.(*ir.ClosureExpr)
580 if _, ok := s.analyzedFuncs[n.Func]; !ok {
581 s.analyzedFuncs[n.Func] = struct{}{}
582 ir.Visit(n.Func, do)
583 }
584 }
585 }
586 ir.VisitList(nodes, do)
587 }
588
View as plain text