// 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 } }