| // Copyright 2018 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package escape |
| |
| import ( |
| "cmd/compile/internal/base" |
| "cmd/compile/internal/ir" |
| ) |
| |
| // addr evaluates an addressable expression n and returns a hole |
| // that represents storing into the represented location. |
| func (e *escape) addr(n ir.Node) hole { |
| if n == nil || ir.IsBlank(n) { |
| // Can happen in select case, range, maybe others. |
| return e.discardHole() |
| } |
| |
| k := e.heapHole() |
| |
| switch n.Op() { |
| default: |
| base.Fatalf("unexpected addr: %v", n) |
| case ir.ONAME: |
| n := n.(*ir.Name) |
| if n.Class == ir.PEXTERN { |
| break |
| } |
| k = e.oldLoc(n).asHole() |
| case ir.OLINKSYMOFFSET: |
| break |
| case ir.ODOT: |
| n := n.(*ir.SelectorExpr) |
| k = e.addr(n.X) |
| case ir.OINDEX: |
| n := n.(*ir.IndexExpr) |
| e.discard(n.Index) |
| if n.X.Type().IsArray() { |
| k = e.addr(n.X) |
| } else { |
| e.discard(n.X) |
| } |
| case ir.ODEREF, ir.ODOTPTR: |
| e.discard(n) |
| case ir.OINDEXMAP: |
| n := n.(*ir.IndexExpr) |
| e.discard(n.X) |
| e.assignHeap(n.Index, "key of map put", n) |
| } |
| |
| return k |
| } |
| |
| func (e *escape) addrs(l ir.Nodes) []hole { |
| var ks []hole |
| for _, n := range l { |
| ks = append(ks, e.addr(n)) |
| } |
| return ks |
| } |
| |
| func (e *escape) assignHeap(src ir.Node, why string, where ir.Node) { |
| e.expr(e.heapHole().note(where, why), src) |
| } |
| |
| // assignList evaluates the assignment dsts... = srcs.... |
| func (e *escape) assignList(dsts, srcs []ir.Node, why string, where ir.Node) { |
| ks := e.addrs(dsts) |
| for i, k := range ks { |
| var src ir.Node |
| if i < len(srcs) { |
| src = srcs[i] |
| } |
| |
| if dst := dsts[i]; dst != nil { |
| // Detect implicit conversion of uintptr to unsafe.Pointer when |
| // storing into reflect.{Slice,String}Header. |
| if dst.Op() == ir.ODOTPTR && ir.IsReflectHeaderDataField(dst) { |
| e.unsafeValue(e.heapHole().note(where, why), src) |
| continue |
| } |
| |
| // Filter out some no-op assignments for escape analysis. |
| if src != nil && isSelfAssign(dst, src) { |
| if base.Flag.LowerM != 0 { |
| base.WarnfAt(where.Pos(), "%v ignoring self-assignment in %v", e.curfn, where) |
| } |
| k = e.discardHole() |
| } |
| } |
| |
| e.expr(k.note(where, why), src) |
| } |
| |
| e.reassigned(ks, where) |
| } |
| |
| // reassigned marks the locations associated with the given holes as |
| // reassigned, unless the location represents a variable declared and |
| // assigned exactly once by where. |
| func (e *escape) reassigned(ks []hole, where ir.Node) { |
| if as, ok := where.(*ir.AssignStmt); ok && as.Op() == ir.OAS && as.Y == nil { |
| if dst, ok := as.X.(*ir.Name); ok && dst.Op() == ir.ONAME && dst.Defn == nil { |
| // Zero-value assignment for variable declared without an |
| // explicit initial value. Assume this is its initialization |
| // statement. |
| return |
| } |
| } |
| |
| for _, k := range ks { |
| loc := k.dst |
| // Variables declared by range statements are assigned on every iteration. |
| if n, ok := loc.n.(*ir.Name); ok && n.Defn == where && where.Op() != ir.ORANGE { |
| continue |
| } |
| loc.reassigned = true |
| } |
| } |