diff options
Diffstat (limited to 'src/cmd/compile/internal/walk/expr.go')
-rw-r--r-- | src/cmd/compile/internal/walk/expr.go | 1033 |
1 files changed, 1033 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/walk/expr.go b/src/cmd/compile/internal/walk/expr.go new file mode 100644 index 0000000..24fe0d0 --- /dev/null +++ b/src/cmd/compile/internal/walk/expr.go @@ -0,0 +1,1033 @@ +// 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.OUNSAFESTRING: + n := n.(*ir.BinaryExpr) + return walkUnsafeString(n, init) + + case ir.OUNSAFESTRINGDATA, ir.OUNSAFESLICEDATA: + n := n.(*ir.UnaryExpr) + return walkUnsafeData(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.OSLICE2ARR: + n := n.(*ir.ConvExpr) + return walkSliceToArray(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.OSTRINGHEADER: + n := n.(*ir.StringHeaderExpr) + return walkStringHeader(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, 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 +} + +type hookInfo struct { + paramType types.Kind + argsNum int + runtimeFunc string +} + +var hooks = map[string]hookInfo{ + "strings.EqualFold": {paramType: types.TSTRING, argsNum: 2, runtimeFunc: "libfuzzerHookEqualFold"}, +} + +// 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()) + return typecheck.Expr(ir.NewConvExpr(n.Pos(), ir.OCONVNOP, n.Type(), 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.SetTypecheck(1) + e = ir.NewStarExpr(n.Pos(), e) + e.SetType(n.Type()) + e.SetTypecheck(1) + 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 + funSym := n.X.Sym() + if base.Debug.Libfuzzer != 0 && funSym != nil { + if hook, found := hooks[funSym.Pkg.Path+"."+funSym.Name]; found { + if len(args) != hook.argsNum { + panic(fmt.Sprintf("%s.%s expects %d arguments, but received %d", funSym.Pkg.Path, funSym.Name, hook.argsNum, len(args))) + } + var hookArgs []ir.Node + for _, arg := range args { + hookArgs = append(hookArgs, tracecmpArg(arg, types.Types[hook.paramType], init)) + } + hookArgs = append(hookArgs, fakePC(n)) + init.Append(mkcall(hook.runtimeFunc, nil, init, hookArgs...)) + } + } +} + +// 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.RType = walkExpr(n.RType, init) + n.ITab = walkExpr(n.ITab, 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") + } + } 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") + } + } + return n +} + +// mapKeyArg returns an expression for key that is suitable to be passed +// as the key argument for runtime map* functions. +// n is is the map indexing or delete Node (to provide Pos). +func mapKeyArg(fast int, n, key ir.Node, assigned bool) ir.Node { + if fast == mapslow { + // standard version takes key by reference. + // orderState.expr made sure key is addressable. + return typecheck.NodAddr(key) + } + if assigned { + // mapassign does distinguish pointer vs. integer key. + return key + } + // mapaccess and mapdelete don't distinguish pointer vs. integer key. + switch fast { + case mapfast32ptr: + return ir.NewConvExpr(n.Pos(), ir.OCONVNOP, types.Types[types.TUINT32], key) + case mapfast64ptr: + 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. +// It replaces m[k] with *map{access1,assign}(maptype, m, &k) +func walkIndexMap(n *ir.IndexExpr, init *ir.Nodes) ir.Node { + n.X = walkExpr(n.X, init) + n.Index = walkExpr(n.Index, init) + map_ := n.X + t := map_.Type() + fast := mapfast(t) + key := mapKeyArg(fast, n, n.Index, n.Assigned) + args := []ir.Node{reflectdata.IndexMapRType(base.Pos, n), map_, key} + + var mapFn ir.Node + switch { + case n.Assigned: + mapFn = mapfn(mapassign[fast], t, false) + case t.Elem().Size() > zeroValSize: + args = append(args, reflectdata.ZeroAddr(t.Elem().Size())) + mapFn = mapfn("mapaccess1_fat", t, true) + default: + mapFn = mapfn(mapaccess1[fast], t, false) + } + call := mkcall1(mapFn, nil, init, args...) + 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() == 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 +} + +// 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 +} + +// walkStringHeader walks an OSTRINGHEADER node. +func walkStringHeader(n *ir.StringHeaderExpr, init *ir.Nodes) ir.Node { + n.Ptr = walkExpr(n.Ptr, init) + n.Len = walkExpr(n.Len, init) + 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") + } + + sym := reflectdata.TrackSym(outer, field) + if ir.CurFunc.FieldTrack == nil { + ir.CurFunc.FieldTrack = make(map[*obj.LSym]struct{}) + } + ir.CurFunc.FieldTrack[sym] = struct{}{} +} |