| // Copyright 2009 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 walk |
| |
| import ( |
| "fmt" |
| "go/constant" |
| "internal/buildcfg" |
| "strings" |
| |
| "cmd/compile/internal/base" |
| "cmd/compile/internal/ir" |
| "cmd/compile/internal/reflectdata" |
| "cmd/compile/internal/staticdata" |
| "cmd/compile/internal/typecheck" |
| "cmd/compile/internal/types" |
| "cmd/internal/obj" |
| ) |
| |
| // The result of walkExpr MUST be assigned back to n, e.g. |
| // n.Left = walkExpr(n.Left, init) |
| func walkExpr(n ir.Node, init *ir.Nodes) ir.Node { |
| if n == nil { |
| return n |
| } |
| |
| if n, ok := n.(ir.InitNode); ok && init == n.PtrInit() { |
| // not okay to use n->ninit when walking n, |
| // because we might replace n with some other node |
| // and would lose the init list. |
| base.Fatalf("walkExpr init == &n->ninit") |
| } |
| |
| if len(n.Init()) != 0 { |
| walkStmtList(n.Init()) |
| init.Append(ir.TakeInit(n)...) |
| } |
| |
| lno := ir.SetPos(n) |
| |
| if base.Flag.LowerW > 1 { |
| ir.Dump("before walk expr", n) |
| } |
| |
| if n.Typecheck() != 1 { |
| base.Fatalf("missed typecheck: %+v", n) |
| } |
| |
| if n.Type().IsUntyped() { |
| base.Fatalf("expression has untyped type: %+v", n) |
| } |
| |
| n = walkExpr1(n, init) |
| |
| // Eagerly compute sizes of all expressions for the back end. |
| if typ := n.Type(); typ != nil && typ.Kind() != types.TBLANK && !typ.IsFuncArgStruct() { |
| types.CheckSize(typ) |
| } |
| if n, ok := n.(*ir.Name); ok && n.Heapaddr != nil { |
| types.CheckSize(n.Heapaddr.Type()) |
| } |
| if ir.IsConst(n, constant.String) { |
| // Emit string symbol now to avoid emitting |
| // any concurrently during the backend. |
| _ = staticdata.StringSym(n.Pos(), constant.StringVal(n.Val())) |
| } |
| |
| if base.Flag.LowerW != 0 && n != nil { |
| ir.Dump("after walk expr", n) |
| } |
| |
| base.Pos = lno |
| return n |
| } |
| |
| func walkExpr1(n ir.Node, init *ir.Nodes) ir.Node { |
| switch n.Op() { |
| default: |
| ir.Dump("walk", n) |
| base.Fatalf("walkExpr: switch 1 unknown op %+v", n.Op()) |
| panic("unreachable") |
| |
| case ir.OGETG, ir.OGETCALLERPC, ir.OGETCALLERSP: |
| return n |
| |
| case ir.OTYPE, ir.ONAME, ir.OLITERAL, ir.ONIL, ir.OLINKSYMOFFSET: |
| // TODO(mdempsky): Just return n; see discussion on CL 38655. |
| // Perhaps refactor to use Node.mayBeShared for these instead. |
| // If these return early, make sure to still call |
| // StringSym for constant strings. |
| return n |
| |
| case ir.OMETHEXPR: |
| // TODO(mdempsky): Do this right after type checking. |
| n := n.(*ir.SelectorExpr) |
| return n.FuncName() |
| |
| case ir.ONOT, ir.ONEG, ir.OPLUS, ir.OBITNOT, ir.OREAL, ir.OIMAG, ir.OSPTR, ir.OITAB, ir.OIDATA: |
| n := n.(*ir.UnaryExpr) |
| n.X = walkExpr(n.X, init) |
| return n |
| |
| case ir.ODOTMETH, ir.ODOTINTER: |
| n := n.(*ir.SelectorExpr) |
| n.X = walkExpr(n.X, init) |
| return n |
| |
| case ir.OADDR: |
| n := n.(*ir.AddrExpr) |
| n.X = walkExpr(n.X, init) |
| return n |
| |
| case ir.ODEREF: |
| n := n.(*ir.StarExpr) |
| n.X = walkExpr(n.X, init) |
| return n |
| |
| case ir.OEFACE, ir.OAND, ir.OANDNOT, ir.OSUB, ir.OMUL, ir.OADD, ir.OOR, ir.OXOR, ir.OLSH, ir.ORSH, |
| ir.OUNSAFEADD: |
| n := n.(*ir.BinaryExpr) |
| n.X = walkExpr(n.X, init) |
| n.Y = walkExpr(n.Y, init) |
| return n |
| |
| case ir.OUNSAFESLICE: |
| n := n.(*ir.BinaryExpr) |
| return walkUnsafeSlice(n, init) |
| |
| case ir.ODOT, ir.ODOTPTR: |
| n := n.(*ir.SelectorExpr) |
| return walkDot(n, init) |
| |
| case ir.ODOTTYPE, ir.ODOTTYPE2: |
| n := n.(*ir.TypeAssertExpr) |
| return walkDotType(n, init) |
| |
| case ir.ODYNAMICDOTTYPE, ir.ODYNAMICDOTTYPE2: |
| n := n.(*ir.DynamicTypeAssertExpr) |
| return walkDynamicDotType(n, init) |
| |
| case ir.OLEN, ir.OCAP: |
| n := n.(*ir.UnaryExpr) |
| return walkLenCap(n, init) |
| |
| case ir.OCOMPLEX: |
| n := n.(*ir.BinaryExpr) |
| n.X = walkExpr(n.X, init) |
| n.Y = walkExpr(n.Y, init) |
| return n |
| |
| case ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE: |
| n := n.(*ir.BinaryExpr) |
| return walkCompare(n, init) |
| |
| case ir.OANDAND, ir.OOROR: |
| n := n.(*ir.LogicalExpr) |
| return walkLogical(n, init) |
| |
| case ir.OPRINT, ir.OPRINTN: |
| return walkPrint(n.(*ir.CallExpr), init) |
| |
| case ir.OPANIC: |
| n := n.(*ir.UnaryExpr) |
| return mkcall("gopanic", nil, init, n.X) |
| |
| case ir.ORECOVERFP: |
| return walkRecoverFP(n.(*ir.CallExpr), init) |
| |
| case ir.OCFUNC: |
| return n |
| |
| case ir.OCALLINTER, ir.OCALLFUNC: |
| n := n.(*ir.CallExpr) |
| return walkCall(n, init) |
| |
| case ir.OAS, ir.OASOP: |
| return walkAssign(init, n) |
| |
| case ir.OAS2: |
| n := n.(*ir.AssignListStmt) |
| return walkAssignList(init, n) |
| |
| // a,b,... = fn() |
| case ir.OAS2FUNC: |
| n := n.(*ir.AssignListStmt) |
| return walkAssignFunc(init, n) |
| |
| // x, y = <-c |
| // order.stmt made sure x is addressable or blank. |
| case ir.OAS2RECV: |
| n := n.(*ir.AssignListStmt) |
| return walkAssignRecv(init, n) |
| |
| // a,b = m[i] |
| case ir.OAS2MAPR: |
| n := n.(*ir.AssignListStmt) |
| return walkAssignMapRead(init, n) |
| |
| case ir.ODELETE: |
| n := n.(*ir.CallExpr) |
| return walkDelete(init, n) |
| |
| case ir.OAS2DOTTYPE: |
| n := n.(*ir.AssignListStmt) |
| return walkAssignDotType(n, init) |
| |
| case ir.OCONVIFACE: |
| n := n.(*ir.ConvExpr) |
| return walkConvInterface(n, init) |
| |
| case ir.OCONVIDATA: |
| n := n.(*ir.ConvExpr) |
| return walkConvIData(n, init) |
| |
| case ir.OCONV, ir.OCONVNOP: |
| n := n.(*ir.ConvExpr) |
| return walkConv(n, init) |
| |
| case ir.OSLICE2ARRPTR: |
| n := n.(*ir.ConvExpr) |
| n.X = walkExpr(n.X, init) |
| return n |
| |
| case ir.ODIV, ir.OMOD: |
| n := n.(*ir.BinaryExpr) |
| return walkDivMod(n, init) |
| |
| case ir.OINDEX: |
| n := n.(*ir.IndexExpr) |
| return walkIndex(n, init) |
| |
| case ir.OINDEXMAP: |
| n := n.(*ir.IndexExpr) |
| return walkIndexMap(n, init) |
| |
| case ir.ORECV: |
| base.Fatalf("walkExpr ORECV") // should see inside OAS only |
| panic("unreachable") |
| |
| case ir.OSLICEHEADER: |
| n := n.(*ir.SliceHeaderExpr) |
| return walkSliceHeader(n, init) |
| |
| case ir.OSLICE, ir.OSLICEARR, ir.OSLICESTR, ir.OSLICE3, ir.OSLICE3ARR: |
| n := n.(*ir.SliceExpr) |
| return walkSlice(n, init) |
| |
| case ir.ONEW: |
| n := n.(*ir.UnaryExpr) |
| return walkNew(n, init) |
| |
| case ir.OADDSTR: |
| return walkAddString(n.(*ir.AddStringExpr), init) |
| |
| case ir.OAPPEND: |
| // order should make sure we only see OAS(node, OAPPEND), which we handle above. |
| base.Fatalf("append outside assignment") |
| panic("unreachable") |
| |
| case ir.OCOPY: |
| return walkCopy(n.(*ir.BinaryExpr), init, base.Flag.Cfg.Instrumenting && !base.Flag.CompilingRuntime) |
| |
| case ir.OCLOSE: |
| n := n.(*ir.UnaryExpr) |
| return walkClose(n, init) |
| |
| case ir.OMAKECHAN: |
| n := n.(*ir.MakeExpr) |
| return walkMakeChan(n, init) |
| |
| case ir.OMAKEMAP: |
| n := n.(*ir.MakeExpr) |
| return walkMakeMap(n, init) |
| |
| case ir.OMAKESLICE: |
| n := n.(*ir.MakeExpr) |
| return walkMakeSlice(n, init) |
| |
| case ir.OMAKESLICECOPY: |
| n := n.(*ir.MakeExpr) |
| return walkMakeSliceCopy(n, init) |
| |
| case ir.ORUNESTR: |
| n := n.(*ir.ConvExpr) |
| return walkRuneToString(n, init) |
| |
| case ir.OBYTES2STR, ir.ORUNES2STR: |
| n := n.(*ir.ConvExpr) |
| return walkBytesRunesToString(n, init) |
| |
| case ir.OBYTES2STRTMP: |
| n := n.(*ir.ConvExpr) |
| return walkBytesToStringTemp(n, init) |
| |
| case ir.OSTR2BYTES: |
| n := n.(*ir.ConvExpr) |
| return walkStringToBytes(n, init) |
| |
| case ir.OSTR2BYTESTMP: |
| n := n.(*ir.ConvExpr) |
| return walkStringToBytesTemp(n, init) |
| |
| case ir.OSTR2RUNES: |
| n := n.(*ir.ConvExpr) |
| return walkStringToRunes(n, init) |
| |
| case ir.OARRAYLIT, ir.OSLICELIT, ir.OMAPLIT, ir.OSTRUCTLIT, ir.OPTRLIT: |
| return walkCompLit(n, init) |
| |
| case ir.OSEND: |
| n := n.(*ir.SendStmt) |
| return walkSend(n, init) |
| |
| case ir.OCLOSURE: |
| return walkClosure(n.(*ir.ClosureExpr), init) |
| |
| case ir.OMETHVALUE: |
| return walkMethodValue(n.(*ir.SelectorExpr), init) |
| } |
| |
| // No return! Each case must return (or panic), |
| // to avoid confusion about what gets returned |
| // in the presence of type assertions. |
| } |
| |
| // walk the whole tree of the body of an |
| // expression or simple statement. |
| // the types expressions are calculated. |
| // compile-time constants are evaluated. |
| // complex side effects like statements are appended to init |
| func walkExprList(s []ir.Node, init *ir.Nodes) { |
| for i := range s { |
| s[i] = walkExpr(s[i], init) |
| } |
| } |
| |
| func walkExprListCheap(s []ir.Node, init *ir.Nodes) { |
| for i, n := range s { |
| s[i] = cheapExpr(n, init) |
| s[i] = walkExpr(s[i], init) |
| } |
| } |
| |
| func walkExprListSafe(s []ir.Node, init *ir.Nodes) { |
| for i, n := range s { |
| s[i] = safeExpr(n, init) |
| s[i] = walkExpr(s[i], init) |
| } |
| } |
| |
| // return side-effect free and cheap n, appending side effects to init. |
| // result may not be assignable. |
| func cheapExpr(n ir.Node, init *ir.Nodes) ir.Node { |
| switch n.Op() { |
| case ir.ONAME, ir.OLITERAL, ir.ONIL: |
| return n |
| } |
| |
| return copyExpr(n, n.Type(), init) |
| } |
| |
| // return side effect-free n, appending side effects to init. |
| // result is assignable if n is. |
| func safeExpr(n ir.Node, init *ir.Nodes) ir.Node { |
| if n == nil { |
| return nil |
| } |
| |
| if len(n.Init()) != 0 { |
| walkStmtList(n.Init()) |
| init.Append(ir.TakeInit(n)...) |
| } |
| |
| switch n.Op() { |
| case ir.ONAME, ir.OLITERAL, ir.ONIL, ir.OLINKSYMOFFSET: |
| return n |
| |
| case ir.OLEN, ir.OCAP: |
| n := n.(*ir.UnaryExpr) |
| l := safeExpr(n.X, init) |
| if l == n.X { |
| return n |
| } |
| a := ir.Copy(n).(*ir.UnaryExpr) |
| a.X = l |
| return walkExpr(typecheck.Expr(a), init) |
| |
| case ir.ODOT, ir.ODOTPTR: |
| n := n.(*ir.SelectorExpr) |
| l := safeExpr(n.X, init) |
| if l == n.X { |
| return n |
| } |
| a := ir.Copy(n).(*ir.SelectorExpr) |
| a.X = l |
| return walkExpr(typecheck.Expr(a), init) |
| |
| case ir.ODEREF: |
| n := n.(*ir.StarExpr) |
| l := safeExpr(n.X, init) |
| if l == n.X { |
| return n |
| } |
| a := ir.Copy(n).(*ir.StarExpr) |
| a.X = l |
| return walkExpr(typecheck.Expr(a), init) |
| |
| case ir.OINDEX, ir.OINDEXMAP: |
| n := n.(*ir.IndexExpr) |
| l := safeExpr(n.X, init) |
| r := safeExpr(n.Index, init) |
| if l == n.X && r == n.Index { |
| return n |
| } |
| a := ir.Copy(n).(*ir.IndexExpr) |
| a.X = l |
| a.Index = r |
| return walkExpr(typecheck.Expr(a), init) |
| |
| case ir.OSTRUCTLIT, ir.OARRAYLIT, ir.OSLICELIT: |
| n := n.(*ir.CompLitExpr) |
| if isStaticCompositeLiteral(n) { |
| return n |
| } |
| } |
| |
| // make a copy; must not be used as an lvalue |
| if ir.IsAddressable(n) { |
| base.Fatalf("missing lvalue case in safeExpr: %v", n) |
| } |
| return cheapExpr(n, init) |
| } |
| |
| func copyExpr(n ir.Node, t *types.Type, init *ir.Nodes) ir.Node { |
| l := typecheck.Temp(t) |
| appendWalkStmt(init, ir.NewAssignStmt(base.Pos, l, n)) |
| return l |
| } |
| |
| func walkAddString(n *ir.AddStringExpr, init *ir.Nodes) ir.Node { |
| c := len(n.List) |
| |
| if c < 2 { |
| base.Fatalf("walkAddString count %d too small", c) |
| } |
| |
| buf := typecheck.NodNil() |
| if n.Esc() == ir.EscNone { |
| sz := int64(0) |
| for _, n1 := range n.List { |
| if n1.Op() == ir.OLITERAL { |
| sz += int64(len(ir.StringVal(n1))) |
| } |
| } |
| |
| // Don't allocate the buffer if the result won't fit. |
| if sz < tmpstringbufsize { |
| // Create temporary buffer for result string on stack. |
| buf = stackBufAddr(tmpstringbufsize, types.Types[types.TUINT8]) |
| } |
| } |
| |
| // build list of string arguments |
| args := []ir.Node{buf} |
| for _, n2 := range n.List { |
| args = append(args, typecheck.Conv(n2, types.Types[types.TSTRING])) |
| } |
| |
| var fn string |
| if c <= 5 { |
| // small numbers of strings use direct runtime helpers. |
| // note: order.expr knows this cutoff too. |
| fn = fmt.Sprintf("concatstring%d", c) |
| } else { |
| // large numbers of strings are passed to the runtime as a slice. |
| fn = "concatstrings" |
| |
| t := types.NewSlice(types.Types[types.TSTRING]) |
| // args[1:] to skip buf arg |
| slice := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, ir.TypeNode(t), args[1:]) |
| slice.Prealloc = n.Prealloc |
| args = []ir.Node{buf, slice} |
| slice.SetEsc(ir.EscNone) |
| } |
| |
| cat := typecheck.LookupRuntime(fn) |
| r := ir.NewCallExpr(base.Pos, ir.OCALL, cat, nil) |
| r.Args = args |
| r1 := typecheck.Expr(r) |
| r1 = walkExpr(r1, init) |
| r1.SetType(n.Type()) |
| |
| return r1 |
| } |
| |
| // walkCall walks an OCALLFUNC or OCALLINTER node. |
| func walkCall(n *ir.CallExpr, init *ir.Nodes) ir.Node { |
| if n.Op() == ir.OCALLMETH { |
| base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck") |
| } |
| if n.Op() == ir.OCALLINTER || n.X.Op() == ir.OMETHEXPR { |
| // We expect both interface call reflect.Type.Method and concrete |
| // call reflect.(*rtype).Method. |
| usemethod(n) |
| } |
| if n.Op() == ir.OCALLINTER { |
| reflectdata.MarkUsedIfaceMethod(n) |
| } |
| |
| if n.Op() == ir.OCALLFUNC && n.X.Op() == ir.OCLOSURE { |
| directClosureCall(n) |
| } |
| |
| if isFuncPCIntrinsic(n) { |
| // For internal/abi.FuncPCABIxxx(fn), if fn is a defined function, rewrite |
| // it to the address of the function of the ABI fn is defined. |
| name := n.X.(*ir.Name).Sym().Name |
| arg := n.Args[0] |
| var wantABI obj.ABI |
| switch name { |
| case "FuncPCABI0": |
| wantABI = obj.ABI0 |
| case "FuncPCABIInternal": |
| wantABI = obj.ABIInternal |
| } |
| if isIfaceOfFunc(arg) { |
| fn := arg.(*ir.ConvExpr).X.(*ir.Name) |
| abi := fn.Func.ABI |
| if abi != wantABI { |
| base.ErrorfAt(n.Pos(), "internal/abi.%s expects an %v function, %s is defined as %v", name, wantABI, fn.Sym().Name, abi) |
| } |
| var e ir.Node = ir.NewLinksymExpr(n.Pos(), fn.Sym().LinksymABI(abi), types.Types[types.TUINTPTR]) |
| e = ir.NewAddrExpr(n.Pos(), e) |
| e.SetType(types.Types[types.TUINTPTR].PtrTo()) |
| e = ir.NewConvExpr(n.Pos(), ir.OCONVNOP, n.Type(), e) |
| return e |
| } |
| // fn is not a defined function. It must be ABIInternal. |
| // Read the address from func value, i.e. *(*uintptr)(idata(fn)). |
| if wantABI != obj.ABIInternal { |
| base.ErrorfAt(n.Pos(), "internal/abi.%s does not accept func expression, which is ABIInternal", name) |
| } |
| arg = walkExpr(arg, init) |
| var e ir.Node = ir.NewUnaryExpr(n.Pos(), ir.OIDATA, arg) |
| e.SetType(n.Type().PtrTo()) |
| e = ir.NewStarExpr(n.Pos(), e) |
| e.SetType(n.Type()) |
| return e |
| } |
| |
| walkCall1(n, init) |
| return n |
| } |
| |
| func walkCall1(n *ir.CallExpr, init *ir.Nodes) { |
| if n.Walked() { |
| return // already walked |
| } |
| n.SetWalked(true) |
| |
| if n.Op() == ir.OCALLMETH { |
| base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck") |
| } |
| |
| args := n.Args |
| params := n.X.Type().Params() |
| |
| n.X = walkExpr(n.X, init) |
| walkExprList(args, init) |
| |
| for i, arg := range args { |
| // Validate argument and parameter types match. |
| param := params.Field(i) |
| if !types.Identical(arg.Type(), param.Type) { |
| base.FatalfAt(n.Pos(), "assigning %L to parameter %v (type %v)", arg, param.Sym, param.Type) |
| } |
| |
| // For any argument whose evaluation might require a function call, |
| // store that argument into a temporary variable, |
| // to prevent that calls from clobbering arguments already on the stack. |
| if mayCall(arg) { |
| // assignment of arg to Temp |
| tmp := typecheck.Temp(param.Type) |
| init.Append(convas(typecheck.Stmt(ir.NewAssignStmt(base.Pos, tmp, arg)).(*ir.AssignStmt), init)) |
| // replace arg with temp |
| args[i] = tmp |
| } |
| } |
| |
| n.Args = args |
| } |
| |
| // walkDivMod walks an ODIV or OMOD node. |
| func walkDivMod(n *ir.BinaryExpr, init *ir.Nodes) ir.Node { |
| n.X = walkExpr(n.X, init) |
| n.Y = walkExpr(n.Y, init) |
| |
| // rewrite complex div into function call. |
| et := n.X.Type().Kind() |
| |
| if types.IsComplex[et] && n.Op() == ir.ODIV { |
| t := n.Type() |
| call := mkcall("complex128div", types.Types[types.TCOMPLEX128], init, typecheck.Conv(n.X, types.Types[types.TCOMPLEX128]), typecheck.Conv(n.Y, types.Types[types.TCOMPLEX128])) |
| return typecheck.Conv(call, t) |
| } |
| |
| // Nothing to do for float divisions. |
| if types.IsFloat[et] { |
| return n |
| } |
| |
| // rewrite 64-bit div and mod on 32-bit architectures. |
| // TODO: Remove this code once we can introduce |
| // runtime calls late in SSA processing. |
| if types.RegSize < 8 && (et == types.TINT64 || et == types.TUINT64) { |
| if n.Y.Op() == ir.OLITERAL { |
| // Leave div/mod by constant powers of 2 or small 16-bit constants. |
| // The SSA backend will handle those. |
| switch et { |
| case types.TINT64: |
| c := ir.Int64Val(n.Y) |
| if c < 0 { |
| c = -c |
| } |
| if c != 0 && c&(c-1) == 0 { |
| return n |
| } |
| case types.TUINT64: |
| c := ir.Uint64Val(n.Y) |
| if c < 1<<16 { |
| return n |
| } |
| if c != 0 && c&(c-1) == 0 { |
| return n |
| } |
| } |
| } |
| var fn string |
| if et == types.TINT64 { |
| fn = "int64" |
| } else { |
| fn = "uint64" |
| } |
| if n.Op() == ir.ODIV { |
| fn += "div" |
| } else { |
| fn += "mod" |
| } |
| return mkcall(fn, n.Type(), init, typecheck.Conv(n.X, types.Types[et]), typecheck.Conv(n.Y, types.Types[et])) |
| } |
| return n |
| } |
| |
| // walkDot walks an ODOT or ODOTPTR node. |
| func walkDot(n *ir.SelectorExpr, init *ir.Nodes) ir.Node { |
| usefield(n) |
| n.X = walkExpr(n.X, init) |
| return n |
| } |
| |
| // walkDotType walks an ODOTTYPE or ODOTTYPE2 node. |
| func walkDotType(n *ir.TypeAssertExpr, init *ir.Nodes) ir.Node { |
| n.X = walkExpr(n.X, init) |
| // Set up interface type addresses for back end. |
| if !n.Type().IsInterface() && !n.X.Type().IsEmptyInterface() { |
| n.Itab = reflectdata.ITabAddr(n.Type(), n.X.Type()) |
| } |
| return n |
| } |
| |
| // walkDynamicdotType walks an ODYNAMICDOTTYPE or ODYNAMICDOTTYPE2 node. |
| func walkDynamicDotType(n *ir.DynamicTypeAssertExpr, init *ir.Nodes) ir.Node { |
| n.X = walkExpr(n.X, init) |
| n.T = walkExpr(n.T, init) |
| return n |
| } |
| |
| // walkIndex walks an OINDEX node. |
| func walkIndex(n *ir.IndexExpr, init *ir.Nodes) ir.Node { |
| n.X = walkExpr(n.X, init) |
| |
| // save the original node for bounds checking elision. |
| // If it was a ODIV/OMOD walk might rewrite it. |
| r := n.Index |
| |
| n.Index = walkExpr(n.Index, init) |
| |
| // if range of type cannot exceed static array bound, |
| // disable bounds check. |
| if n.Bounded() { |
| return n |
| } |
| t := n.X.Type() |
| if t != nil && t.IsPtr() { |
| t = t.Elem() |
| } |
| if t.IsArray() { |
| n.SetBounded(bounded(r, t.NumElem())) |
| if base.Flag.LowerM != 0 && n.Bounded() && !ir.IsConst(n.Index, constant.Int) { |
| base.Warn("index bounds check elided") |
| } |
| if ir.IsSmallIntConst(n.Index) && !n.Bounded() { |
| base.Errorf("index out of bounds") |
| } |
| } else if ir.IsConst(n.X, constant.String) { |
| n.SetBounded(bounded(r, int64(len(ir.StringVal(n.X))))) |
| if base.Flag.LowerM != 0 && n.Bounded() && !ir.IsConst(n.Index, constant.Int) { |
| base.Warn("index bounds check elided") |
| } |
| if ir.IsSmallIntConst(n.Index) && !n.Bounded() { |
| base.Errorf("index out of bounds") |
| } |
| } |
| |
| if ir.IsConst(n.Index, constant.Int) { |
| if v := n.Index.Val(); constant.Sign(v) < 0 || ir.ConstOverflow(v, types.Types[types.TINT]) { |
| base.Errorf("index out of bounds") |
| } |
| } |
| return n |
| } |
| |
| // mapKeyArg returns an expression for key that is suitable to be passed |
| // as the key argument for mapaccess and mapdelete functions. |
| // n is is the map indexing or delete Node (to provide Pos). |
| // Note: this is not used for mapassign, which does distinguish pointer vs. |
| // integer key. |
| func mapKeyArg(fast int, n, key ir.Node) ir.Node { |
| switch fast { |
| case mapslow: |
| // standard version takes key by reference. |
| // order.expr made sure key is addressable. |
| return typecheck.NodAddr(key) |
| case mapfast32ptr: |
| // mapaccess and mapdelete don't distinguish pointer vs. integer key. |
| return ir.NewConvExpr(n.Pos(), ir.OCONVNOP, types.Types[types.TUINT32], key) |
| case mapfast64ptr: |
| // mapaccess and mapdelete don't distinguish pointer vs. integer key. |
| return ir.NewConvExpr(n.Pos(), ir.OCONVNOP, types.Types[types.TUINT64], key) |
| default: |
| // fast version takes key by value. |
| return key |
| } |
| } |
| |
| // walkIndexMap walks an OINDEXMAP node. |
| func walkIndexMap(n *ir.IndexExpr, init *ir.Nodes) ir.Node { |
| // Replace m[k] with *map{access1,assign}(maptype, m, &k) |
| n.X = walkExpr(n.X, init) |
| n.Index = walkExpr(n.Index, init) |
| map_ := n.X |
| key := n.Index |
| t := map_.Type() |
| var call *ir.CallExpr |
| if n.Assigned { |
| // This m[k] expression is on the left-hand side of an assignment. |
| fast := mapfast(t) |
| if fast == mapslow { |
| // standard version takes key by reference. |
| // order.expr made sure key is addressable. |
| key = typecheck.NodAddr(key) |
| } |
| call = mkcall1(mapfn(mapassign[fast], t, false), nil, init, reflectdata.TypePtr(t), map_, key) |
| } else { |
| // m[k] is not the target of an assignment. |
| fast := mapfast(t) |
| key = mapKeyArg(fast, n, key) |
| if w := t.Elem().Size(); w <= zeroValSize { |
| call = mkcall1(mapfn(mapaccess1[fast], t, false), types.NewPtr(t.Elem()), init, reflectdata.TypePtr(t), map_, key) |
| } else { |
| z := reflectdata.ZeroAddr(w) |
| call = mkcall1(mapfn("mapaccess1_fat", t, true), types.NewPtr(t.Elem()), init, reflectdata.TypePtr(t), map_, key, z) |
| } |
| } |
| call.SetType(types.NewPtr(t.Elem())) |
| call.MarkNonNil() // mapaccess1* and mapassign always return non-nil pointers. |
| star := ir.NewStarExpr(base.Pos, call) |
| star.SetType(t.Elem()) |
| star.SetTypecheck(1) |
| return star |
| } |
| |
| // walkLogical walks an OANDAND or OOROR node. |
| func walkLogical(n *ir.LogicalExpr, init *ir.Nodes) ir.Node { |
| n.X = walkExpr(n.X, init) |
| |
| // cannot put side effects from n.Right on init, |
| // because they cannot run before n.Left is checked. |
| // save elsewhere and store on the eventual n.Right. |
| var ll ir.Nodes |
| |
| n.Y = walkExpr(n.Y, &ll) |
| n.Y = ir.InitExpr(ll, n.Y) |
| return n |
| } |
| |
| // walkSend walks an OSEND node. |
| func walkSend(n *ir.SendStmt, init *ir.Nodes) ir.Node { |
| n1 := n.Value |
| n1 = typecheck.AssignConv(n1, n.Chan.Type().Elem(), "chan send") |
| n1 = walkExpr(n1, init) |
| n1 = typecheck.NodAddr(n1) |
| return mkcall1(chanfn("chansend1", 2, n.Chan.Type()), nil, init, n.Chan, n1) |
| } |
| |
| // walkSlice walks an OSLICE, OSLICEARR, OSLICESTR, OSLICE3, or OSLICE3ARR node. |
| func walkSlice(n *ir.SliceExpr, init *ir.Nodes) ir.Node { |
| n.X = walkExpr(n.X, init) |
| n.Low = walkExpr(n.Low, init) |
| if n.Low != nil && ir.IsZero(n.Low) { |
| // Reduce x[0:j] to x[:j] and x[0:j:k] to x[:j:k]. |
| n.Low = nil |
| } |
| n.High = walkExpr(n.High, init) |
| n.Max = walkExpr(n.Max, init) |
| |
| if n.Op().IsSlice3() { |
| if n.Max != nil && n.Max.Op() == ir.OCAP && ir.SameSafeExpr(n.X, n.Max.(*ir.UnaryExpr).X) { |
| // Reduce x[i:j:cap(x)] to x[i:j]. |
| if n.Op() == ir.OSLICE3 { |
| n.SetOp(ir.OSLICE) |
| } else { |
| n.SetOp(ir.OSLICEARR) |
| } |
| return reduceSlice(n) |
| } |
| return n |
| } |
| return reduceSlice(n) |
| } |
| |
| // walkSliceHeader walks an OSLICEHEADER node. |
| func walkSliceHeader(n *ir.SliceHeaderExpr, init *ir.Nodes) ir.Node { |
| n.Ptr = walkExpr(n.Ptr, init) |
| n.Len = walkExpr(n.Len, init) |
| n.Cap = walkExpr(n.Cap, init) |
| return n |
| } |
| |
| // TODO(josharian): combine this with its caller and simplify |
| func reduceSlice(n *ir.SliceExpr) ir.Node { |
| if n.High != nil && n.High.Op() == ir.OLEN && ir.SameSafeExpr(n.X, n.High.(*ir.UnaryExpr).X) { |
| // Reduce x[i:len(x)] to x[i:]. |
| n.High = nil |
| } |
| if (n.Op() == ir.OSLICE || n.Op() == ir.OSLICESTR) && n.Low == nil && n.High == nil { |
| // Reduce x[:] to x. |
| if base.Debug.Slice > 0 { |
| base.Warn("slice: omit slice operation") |
| } |
| return n.X |
| } |
| return n |
| } |
| |
| // return 1 if integer n must be in range [0, max), 0 otherwise |
| func bounded(n ir.Node, max int64) bool { |
| if n.Type() == nil || !n.Type().IsInteger() { |
| return false |
| } |
| |
| sign := n.Type().IsSigned() |
| bits := int32(8 * n.Type().Size()) |
| |
| if ir.IsSmallIntConst(n) { |
| v := ir.Int64Val(n) |
| return 0 <= v && v < max |
| } |
| |
| switch n.Op() { |
| case ir.OAND, ir.OANDNOT: |
| n := n.(*ir.BinaryExpr) |
| v := int64(-1) |
| switch { |
| case ir.IsSmallIntConst(n.X): |
| v = ir.Int64Val(n.X) |
| case ir.IsSmallIntConst(n.Y): |
| v = ir.Int64Val(n.Y) |
| if n.Op() == ir.OANDNOT { |
| v = ^v |
| if !sign { |
| v &= 1<<uint(bits) - 1 |
| } |
| } |
| } |
| if 0 <= v && v < max { |
| return true |
| } |
| |
| case ir.OMOD: |
| n := n.(*ir.BinaryExpr) |
| if !sign && ir.IsSmallIntConst(n.Y) { |
| v := ir.Int64Val(n.Y) |
| if 0 <= v && v <= max { |
| return true |
| } |
| } |
| |
| case ir.ODIV: |
| n := n.(*ir.BinaryExpr) |
| if !sign && ir.IsSmallIntConst(n.Y) { |
| v := ir.Int64Val(n.Y) |
| for bits > 0 && v >= 2 { |
| bits-- |
| v >>= 1 |
| } |
| } |
| |
| case ir.ORSH: |
| n := n.(*ir.BinaryExpr) |
| if !sign && ir.IsSmallIntConst(n.Y) { |
| v := ir.Int64Val(n.Y) |
| if v > int64(bits) { |
| return true |
| } |
| bits -= int32(v) |
| } |
| } |
| |
| if !sign && bits <= 62 && 1<<uint(bits) <= max { |
| return true |
| } |
| |
| return false |
| } |
| |
| // usemethod checks calls for uses of reflect.Type.{Method,MethodByName}. |
| func usemethod(n *ir.CallExpr) { |
| // Don't mark reflect.(*rtype).Method, etc. themselves in the reflect package. |
| // Those functions may be alive via the itab, which should not cause all methods |
| // alive. We only want to mark their callers. |
| if base.Ctxt.Pkgpath == "reflect" { |
| switch ir.CurFunc.Nname.Sym().Name { // TODO: is there a better way than hardcoding the names? |
| case "(*rtype).Method", "(*rtype).MethodByName", "(*interfaceType).Method", "(*interfaceType).MethodByName": |
| return |
| } |
| } |
| |
| dot, ok := n.X.(*ir.SelectorExpr) |
| if !ok { |
| return |
| } |
| |
| // Looking for either direct method calls and interface method calls of: |
| // reflect.Type.Method - func(int) reflect.Method |
| // reflect.Type.MethodByName - func(string) (reflect.Method, bool) |
| var pKind types.Kind |
| |
| switch dot.Sel.Name { |
| case "Method": |
| pKind = types.TINT |
| case "MethodByName": |
| pKind = types.TSTRING |
| default: |
| return |
| } |
| |
| t := dot.Selection.Type |
| if t.NumParams() != 1 || t.Params().Field(0).Type.Kind() != pKind { |
| return |
| } |
| switch t.NumResults() { |
| case 1: |
| // ok |
| case 2: |
| if t.Results().Field(1).Type.Kind() != types.TBOOL { |
| return |
| } |
| default: |
| return |
| } |
| |
| // Check that first result type is "reflect.Method". Note that we have to check sym name and sym package |
| // separately, as we can't check for exact string "reflect.Method" reliably (e.g., see #19028 and #38515). |
| if s := t.Results().Field(0).Type.Sym(); s != nil && s.Name == "Method" && types.IsReflectPkg(s.Pkg) { |
| ir.CurFunc.SetReflectMethod(true) |
| // The LSym is initialized at this point. We need to set the attribute on the LSym. |
| ir.CurFunc.LSym.Set(obj.AttrReflectMethod, true) |
| } |
| } |
| |
| func usefield(n *ir.SelectorExpr) { |
| if !buildcfg.Experiment.FieldTrack { |
| return |
| } |
| |
| switch n.Op() { |
| default: |
| base.Fatalf("usefield %v", n.Op()) |
| |
| case ir.ODOT, ir.ODOTPTR: |
| break |
| } |
| |
| field := n.Selection |
| if field == nil { |
| base.Fatalf("usefield %v %v without paramfld", n.X.Type(), n.Sel) |
| } |
| if field.Sym != n.Sel { |
| base.Fatalf("field inconsistency: %v != %v", field.Sym, n.Sel) |
| } |
| if !strings.Contains(field.Note, "go:\"track\"") { |
| return |
| } |
| |
| outer := n.X.Type() |
| if outer.IsPtr() { |
| outer = outer.Elem() |
| } |
| if outer.Sym() == nil { |
| base.Errorf("tracked field must be in named struct type") |
| } |
| if !types.IsExported(field.Sym.Name) { |
| base.Errorf("tracked field must be exported (upper case)") |
| } |
| |
| sym := reflectdata.TrackSym(outer, field) |
| if ir.CurFunc.FieldTrack == nil { |
| ir.CurFunc.FieldTrack = make(map[*obj.LSym]struct{}) |
| } |
| ir.CurFunc.FieldTrack[sym] = struct{}{} |
| } |