| // 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" |
| "go/token" |
| "strings" |
| |
| "cmd/compile/internal/base" |
| "cmd/compile/internal/escape" |
| "cmd/compile/internal/ir" |
| "cmd/compile/internal/reflectdata" |
| "cmd/compile/internal/typecheck" |
| "cmd/compile/internal/types" |
| ) |
| |
| // Rewrite append(src, x, y, z) so that any side effects in |
| // x, y, z (including runtime panics) are evaluated in |
| // initialization statements before the append. |
| // For normal code generation, stop there and leave the |
| // rest to cgen_append. |
| // |
| // For race detector, expand append(src, a [, b]* ) to |
| // |
| // init { |
| // s := src |
| // const argc = len(args) - 1 |
| // if cap(s) - len(s) < argc { |
| // s = growslice(s, len(s)+argc) |
| // } |
| // n := len(s) |
| // s = s[:n+argc] |
| // s[n] = a |
| // s[n+1] = b |
| // ... |
| // } |
| // s |
| func walkAppend(n *ir.CallExpr, init *ir.Nodes, dst ir.Node) ir.Node { |
| if !ir.SameSafeExpr(dst, n.Args[0]) { |
| n.Args[0] = safeExpr(n.Args[0], init) |
| n.Args[0] = walkExpr(n.Args[0], init) |
| } |
| walkExprListSafe(n.Args[1:], init) |
| |
| nsrc := n.Args[0] |
| |
| // walkExprListSafe will leave OINDEX (s[n]) alone if both s |
| // and n are name or literal, but those may index the slice we're |
| // modifying here. Fix explicitly. |
| // Using cheapExpr also makes sure that the evaluation |
| // of all arguments (and especially any panics) happen |
| // before we begin to modify the slice in a visible way. |
| ls := n.Args[1:] |
| for i, n := range ls { |
| n = cheapExpr(n, init) |
| if !types.Identical(n.Type(), nsrc.Type().Elem()) { |
| n = typecheck.AssignConv(n, nsrc.Type().Elem(), "append") |
| n = walkExpr(n, init) |
| } |
| ls[i] = n |
| } |
| |
| argc := len(n.Args) - 1 |
| if argc < 1 { |
| return nsrc |
| } |
| |
| // General case, with no function calls left as arguments. |
| // Leave for gen, except that instrumentation requires old form. |
| if !base.Flag.Cfg.Instrumenting || base.Flag.CompilingRuntime { |
| return n |
| } |
| |
| var l []ir.Node |
| |
| ns := typecheck.Temp(nsrc.Type()) |
| l = append(l, ir.NewAssignStmt(base.Pos, ns, nsrc)) // s = src |
| |
| na := ir.NewInt(int64(argc)) // const argc |
| nif := ir.NewIfStmt(base.Pos, nil, nil, nil) // if cap(s) - len(s) < argc |
| nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OLT, ir.NewBinaryExpr(base.Pos, ir.OSUB, ir.NewUnaryExpr(base.Pos, ir.OCAP, ns), ir.NewUnaryExpr(base.Pos, ir.OLEN, ns)), na) |
| |
| fn := typecheck.LookupRuntime("growslice") // growslice(<type>, old []T, mincap int) (ret []T) |
| fn = typecheck.SubstArgTypes(fn, ns.Type().Elem(), ns.Type().Elem()) |
| |
| nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, ns, mkcall1(fn, ns.Type(), nif.PtrInit(), reflectdata.TypePtr(ns.Type().Elem()), ns, |
| ir.NewBinaryExpr(base.Pos, ir.OADD, ir.NewUnaryExpr(base.Pos, ir.OLEN, ns), na)))} |
| |
| l = append(l, nif) |
| |
| nn := typecheck.Temp(types.Types[types.TINT]) |
| l = append(l, ir.NewAssignStmt(base.Pos, nn, ir.NewUnaryExpr(base.Pos, ir.OLEN, ns))) // n = len(s) |
| |
| slice := ir.NewSliceExpr(base.Pos, ir.OSLICE, ns, nil, ir.NewBinaryExpr(base.Pos, ir.OADD, nn, na), nil) // ...s[:n+argc] |
| slice.SetBounded(true) |
| l = append(l, ir.NewAssignStmt(base.Pos, ns, slice)) // s = s[:n+argc] |
| |
| ls = n.Args[1:] |
| for i, n := range ls { |
| ix := ir.NewIndexExpr(base.Pos, ns, nn) // s[n] ... |
| ix.SetBounded(true) |
| l = append(l, ir.NewAssignStmt(base.Pos, ix, n)) // s[n] = arg |
| if i+1 < len(ls) { |
| l = append(l, ir.NewAssignStmt(base.Pos, nn, ir.NewBinaryExpr(base.Pos, ir.OADD, nn, ir.NewInt(1)))) // n = n + 1 |
| } |
| } |
| |
| typecheck.Stmts(l) |
| walkStmtList(l) |
| init.Append(l...) |
| return ns |
| } |
| |
| // walkClose walks an OCLOSE node. |
| func walkClose(n *ir.UnaryExpr, init *ir.Nodes) ir.Node { |
| // cannot use chanfn - closechan takes any, not chan any |
| fn := typecheck.LookupRuntime("closechan") |
| fn = typecheck.SubstArgTypes(fn, n.X.Type()) |
| return mkcall1(fn, nil, init, n.X) |
| } |
| |
| // Lower copy(a, b) to a memmove call or a runtime call. |
| // |
| // init { |
| // n := len(a) |
| // if n > len(b) { n = len(b) } |
| // if a.ptr != b.ptr { memmove(a.ptr, b.ptr, n*sizeof(elem(a))) } |
| // } |
| // n; |
| // |
| // Also works if b is a string. |
| // |
| func walkCopy(n *ir.BinaryExpr, init *ir.Nodes, runtimecall bool) ir.Node { |
| if n.X.Type().Elem().HasPointers() { |
| ir.CurFunc.SetWBPos(n.Pos()) |
| fn := writebarrierfn("typedslicecopy", n.X.Type().Elem(), n.Y.Type().Elem()) |
| n.X = cheapExpr(n.X, init) |
| ptrL, lenL := backingArrayPtrLen(n.X) |
| n.Y = cheapExpr(n.Y, init) |
| ptrR, lenR := backingArrayPtrLen(n.Y) |
| return mkcall1(fn, n.Type(), init, reflectdata.TypePtr(n.X.Type().Elem()), ptrL, lenL, ptrR, lenR) |
| } |
| |
| if runtimecall { |
| // rely on runtime to instrument: |
| // copy(n.Left, n.Right) |
| // n.Right can be a slice or string. |
| |
| n.X = cheapExpr(n.X, init) |
| ptrL, lenL := backingArrayPtrLen(n.X) |
| n.Y = cheapExpr(n.Y, init) |
| ptrR, lenR := backingArrayPtrLen(n.Y) |
| |
| fn := typecheck.LookupRuntime("slicecopy") |
| fn = typecheck.SubstArgTypes(fn, ptrL.Type().Elem(), ptrR.Type().Elem()) |
| |
| return mkcall1(fn, n.Type(), init, ptrL, lenL, ptrR, lenR, ir.NewInt(n.X.Type().Elem().Width)) |
| } |
| |
| n.X = walkExpr(n.X, init) |
| n.Y = walkExpr(n.Y, init) |
| nl := typecheck.Temp(n.X.Type()) |
| nr := typecheck.Temp(n.Y.Type()) |
| var l []ir.Node |
| l = append(l, ir.NewAssignStmt(base.Pos, nl, n.X)) |
| l = append(l, ir.NewAssignStmt(base.Pos, nr, n.Y)) |
| |
| nfrm := ir.NewUnaryExpr(base.Pos, ir.OSPTR, nr) |
| nto := ir.NewUnaryExpr(base.Pos, ir.OSPTR, nl) |
| |
| nlen := typecheck.Temp(types.Types[types.TINT]) |
| |
| // n = len(to) |
| l = append(l, ir.NewAssignStmt(base.Pos, nlen, ir.NewUnaryExpr(base.Pos, ir.OLEN, nl))) |
| |
| // if n > len(frm) { n = len(frm) } |
| nif := ir.NewIfStmt(base.Pos, nil, nil, nil) |
| |
| nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OGT, nlen, ir.NewUnaryExpr(base.Pos, ir.OLEN, nr)) |
| nif.Body.Append(ir.NewAssignStmt(base.Pos, nlen, ir.NewUnaryExpr(base.Pos, ir.OLEN, nr))) |
| l = append(l, nif) |
| |
| // if to.ptr != frm.ptr { memmove( ... ) } |
| ne := ir.NewIfStmt(base.Pos, ir.NewBinaryExpr(base.Pos, ir.ONE, nto, nfrm), nil, nil) |
| ne.Likely = true |
| l = append(l, ne) |
| |
| fn := typecheck.LookupRuntime("memmove") |
| fn = typecheck.SubstArgTypes(fn, nl.Type().Elem(), nl.Type().Elem()) |
| nwid := ir.Node(typecheck.Temp(types.Types[types.TUINTPTR])) |
| setwid := ir.NewAssignStmt(base.Pos, nwid, typecheck.Conv(nlen, types.Types[types.TUINTPTR])) |
| ne.Body.Append(setwid) |
| nwid = ir.NewBinaryExpr(base.Pos, ir.OMUL, nwid, ir.NewInt(nl.Type().Elem().Width)) |
| call := mkcall1(fn, nil, init, nto, nfrm, nwid) |
| ne.Body.Append(call) |
| |
| typecheck.Stmts(l) |
| walkStmtList(l) |
| init.Append(l...) |
| return nlen |
| } |
| |
| // walkDelete walks an ODELETE node. |
| func walkDelete(init *ir.Nodes, n *ir.CallExpr) ir.Node { |
| init.Append(ir.TakeInit(n)...) |
| map_ := n.Args[0] |
| key := n.Args[1] |
| map_ = walkExpr(map_, init) |
| key = walkExpr(key, init) |
| |
| t := map_.Type() |
| fast := mapfast(t) |
| key = mapKeyArg(fast, n, key) |
| return mkcall1(mapfndel(mapdelete[fast], t), nil, init, reflectdata.TypePtr(t), map_, key) |
| } |
| |
| // walkLenCap walks an OLEN or OCAP node. |
| func walkLenCap(n *ir.UnaryExpr, init *ir.Nodes) ir.Node { |
| if isRuneCount(n) { |
| // Replace len([]rune(string)) with runtime.countrunes(string). |
| return mkcall("countrunes", n.Type(), init, typecheck.Conv(n.X.(*ir.ConvExpr).X, types.Types[types.TSTRING])) |
| } |
| |
| n.X = walkExpr(n.X, init) |
| |
| // replace len(*[10]int) with 10. |
| // delayed until now to preserve side effects. |
| t := n.X.Type() |
| |
| if t.IsPtr() { |
| t = t.Elem() |
| } |
| if t.IsArray() { |
| safeExpr(n.X, init) |
| con := typecheck.OrigInt(n, t.NumElem()) |
| con.SetTypecheck(1) |
| return con |
| } |
| return n |
| } |
| |
| // walkMakeChan walks an OMAKECHAN node. |
| func walkMakeChan(n *ir.MakeExpr, init *ir.Nodes) ir.Node { |
| // When size fits into int, use makechan instead of |
| // makechan64, which is faster and shorter on 32 bit platforms. |
| size := n.Len |
| fnname := "makechan64" |
| argtype := types.Types[types.TINT64] |
| |
| // Type checking guarantees that TIDEAL size is positive and fits in an int. |
| // The case of size overflow when converting TUINT or TUINTPTR to TINT |
| // will be handled by the negative range checks in makechan during runtime. |
| if size.Type().IsKind(types.TIDEAL) || size.Type().Size() <= types.Types[types.TUINT].Size() { |
| fnname = "makechan" |
| argtype = types.Types[types.TINT] |
| } |
| |
| return mkcall1(chanfn(fnname, 1, n.Type()), n.Type(), init, reflectdata.TypePtr(n.Type()), typecheck.Conv(size, argtype)) |
| } |
| |
| // walkMakeMap walks an OMAKEMAP node. |
| func walkMakeMap(n *ir.MakeExpr, init *ir.Nodes) ir.Node { |
| t := n.Type() |
| hmapType := reflectdata.MapType(t) |
| hint := n.Len |
| |
| // var h *hmap |
| var h ir.Node |
| if n.Esc() == ir.EscNone { |
| // Allocate hmap on stack. |
| |
| // var hv hmap |
| // h = &hv |
| h = stackTempAddr(init, hmapType) |
| |
| // Allocate one bucket pointed to by hmap.buckets on stack if hint |
| // is not larger than BUCKETSIZE. In case hint is larger than |
| // BUCKETSIZE runtime.makemap will allocate the buckets on the heap. |
| // Maximum key and elem size is 128 bytes, larger objects |
| // are stored with an indirection. So max bucket size is 2048+eps. |
| if !ir.IsConst(hint, constant.Int) || |
| constant.Compare(hint.Val(), token.LEQ, constant.MakeInt64(reflectdata.BUCKETSIZE)) { |
| |
| // In case hint is larger than BUCKETSIZE runtime.makemap |
| // will allocate the buckets on the heap, see #20184 |
| // |
| // if hint <= BUCKETSIZE { |
| // var bv bmap |
| // b = &bv |
| // h.buckets = b |
| // } |
| |
| nif := ir.NewIfStmt(base.Pos, ir.NewBinaryExpr(base.Pos, ir.OLE, hint, ir.NewInt(reflectdata.BUCKETSIZE)), nil, nil) |
| nif.Likely = true |
| |
| // var bv bmap |
| // b = &bv |
| b := stackTempAddr(&nif.Body, reflectdata.MapBucketType(t)) |
| |
| // h.buckets = b |
| bsym := hmapType.Field(5).Sym // hmap.buckets see reflect.go:hmap |
| na := ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, h, bsym), b) |
| nif.Body.Append(na) |
| appendWalkStmt(init, nif) |
| } |
| } |
| |
| if ir.IsConst(hint, constant.Int) && constant.Compare(hint.Val(), token.LEQ, constant.MakeInt64(reflectdata.BUCKETSIZE)) { |
| // Handling make(map[any]any) and |
| // make(map[any]any, hint) where hint <= BUCKETSIZE |
| // special allows for faster map initialization and |
| // improves binary size by using calls with fewer arguments. |
| // For hint <= BUCKETSIZE overLoadFactor(hint, 0) is false |
| // and no buckets will be allocated by makemap. Therefore, |
| // no buckets need to be allocated in this code path. |
| if n.Esc() == ir.EscNone { |
| // Only need to initialize h.hash0 since |
| // hmap h has been allocated on the stack already. |
| // h.hash0 = fastrand() |
| rand := mkcall("fastrand", types.Types[types.TUINT32], init) |
| hashsym := hmapType.Field(4).Sym // hmap.hash0 see reflect.go:hmap |
| appendWalkStmt(init, ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, h, hashsym), rand)) |
| return typecheck.ConvNop(h, t) |
| } |
| // Call runtime.makehmap to allocate an |
| // hmap on the heap and initialize hmap's hash0 field. |
| fn := typecheck.LookupRuntime("makemap_small") |
| fn = typecheck.SubstArgTypes(fn, t.Key(), t.Elem()) |
| return mkcall1(fn, n.Type(), init) |
| } |
| |
| if n.Esc() != ir.EscNone { |
| h = typecheck.NodNil() |
| } |
| // Map initialization with a variable or large hint is |
| // more complicated. We therefore generate a call to |
| // runtime.makemap to initialize hmap and allocate the |
| // map buckets. |
| |
| // When hint fits into int, use makemap instead of |
| // makemap64, which is faster and shorter on 32 bit platforms. |
| fnname := "makemap64" |
| argtype := types.Types[types.TINT64] |
| |
| // Type checking guarantees that TIDEAL hint is positive and fits in an int. |
| // See checkmake call in TMAP case of OMAKE case in OpSwitch in typecheck1 function. |
| // The case of hint overflow when converting TUINT or TUINTPTR to TINT |
| // will be handled by the negative range checks in makemap during runtime. |
| if hint.Type().IsKind(types.TIDEAL) || hint.Type().Size() <= types.Types[types.TUINT].Size() { |
| fnname = "makemap" |
| argtype = types.Types[types.TINT] |
| } |
| |
| fn := typecheck.LookupRuntime(fnname) |
| fn = typecheck.SubstArgTypes(fn, hmapType, t.Key(), t.Elem()) |
| return mkcall1(fn, n.Type(), init, reflectdata.TypePtr(n.Type()), typecheck.Conv(hint, argtype), h) |
| } |
| |
| // walkMakeSlice walks an OMAKESLICE node. |
| func walkMakeSlice(n *ir.MakeExpr, init *ir.Nodes) ir.Node { |
| l := n.Len |
| r := n.Cap |
| if r == nil { |
| r = safeExpr(l, init) |
| l = r |
| } |
| t := n.Type() |
| if t.Elem().NotInHeap() { |
| base.Errorf("%v can't be allocated in Go; it is incomplete (or unallocatable)", t.Elem()) |
| } |
| if n.Esc() == ir.EscNone { |
| if why := escape.HeapAllocReason(n); why != "" { |
| base.Fatalf("%v has EscNone, but %v", n, why) |
| } |
| // var arr [r]T |
| // n = arr[:l] |
| i := typecheck.IndexConst(r) |
| if i < 0 { |
| base.Fatalf("walkExpr: invalid index %v", r) |
| } |
| |
| // cap is constrained to [0,2^31) or [0,2^63) depending on whether |
| // we're in 32-bit or 64-bit systems. So it's safe to do: |
| // |
| // if uint64(len) > cap { |
| // if len < 0 { panicmakeslicelen() } |
| // panicmakeslicecap() |
| // } |
| nif := ir.NewIfStmt(base.Pos, ir.NewBinaryExpr(base.Pos, ir.OGT, typecheck.Conv(l, types.Types[types.TUINT64]), ir.NewInt(i)), nil, nil) |
| niflen := ir.NewIfStmt(base.Pos, ir.NewBinaryExpr(base.Pos, ir.OLT, l, ir.NewInt(0)), nil, nil) |
| niflen.Body = []ir.Node{mkcall("panicmakeslicelen", nil, init)} |
| nif.Body.Append(niflen, mkcall("panicmakeslicecap", nil, init)) |
| init.Append(typecheck.Stmt(nif)) |
| |
| t = types.NewArray(t.Elem(), i) // [r]T |
| var_ := typecheck.Temp(t) |
| appendWalkStmt(init, ir.NewAssignStmt(base.Pos, var_, nil)) // zero temp |
| r := ir.NewSliceExpr(base.Pos, ir.OSLICE, var_, nil, l, nil) // arr[:l] |
| // The conv is necessary in case n.Type is named. |
| return walkExpr(typecheck.Expr(typecheck.Conv(r, n.Type())), init) |
| } |
| |
| // n escapes; set up a call to makeslice. |
| // When len and cap can fit into int, use makeslice instead of |
| // makeslice64, which is faster and shorter on 32 bit platforms. |
| |
| len, cap := l, r |
| |
| fnname := "makeslice64" |
| argtype := types.Types[types.TINT64] |
| |
| // Type checking guarantees that TIDEAL len/cap are positive and fit in an int. |
| // The case of len or cap overflow when converting TUINT or TUINTPTR to TINT |
| // will be handled by the negative range checks in makeslice during runtime. |
| if (len.Type().IsKind(types.TIDEAL) || len.Type().Size() <= types.Types[types.TUINT].Size()) && |
| (cap.Type().IsKind(types.TIDEAL) || cap.Type().Size() <= types.Types[types.TUINT].Size()) { |
| fnname = "makeslice" |
| argtype = types.Types[types.TINT] |
| } |
| fn := typecheck.LookupRuntime(fnname) |
| ptr := mkcall1(fn, types.Types[types.TUNSAFEPTR], init, reflectdata.TypePtr(t.Elem()), typecheck.Conv(len, argtype), typecheck.Conv(cap, argtype)) |
| ptr.MarkNonNil() |
| len = typecheck.Conv(len, types.Types[types.TINT]) |
| cap = typecheck.Conv(cap, types.Types[types.TINT]) |
| sh := ir.NewSliceHeaderExpr(base.Pos, t, ptr, len, cap) |
| return walkExpr(typecheck.Expr(sh), init) |
| } |
| |
| // walkMakeSliceCopy walks an OMAKESLICECOPY node. |
| func walkMakeSliceCopy(n *ir.MakeExpr, init *ir.Nodes) ir.Node { |
| if n.Esc() == ir.EscNone { |
| base.Fatalf("OMAKESLICECOPY with EscNone: %v", n) |
| } |
| |
| t := n.Type() |
| if t.Elem().NotInHeap() { |
| base.Errorf("%v can't be allocated in Go; it is incomplete (or unallocatable)", t.Elem()) |
| } |
| |
| length := typecheck.Conv(n.Len, types.Types[types.TINT]) |
| copylen := ir.NewUnaryExpr(base.Pos, ir.OLEN, n.Cap) |
| copyptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, n.Cap) |
| |
| if !t.Elem().HasPointers() && n.Bounded() { |
| // When len(to)==len(from) and elements have no pointers: |
| // replace make+copy with runtime.mallocgc+runtime.memmove. |
| |
| // We do not check for overflow of len(to)*elem.Width here |
| // since len(from) is an existing checked slice capacity |
| // with same elem.Width for the from slice. |
| size := ir.NewBinaryExpr(base.Pos, ir.OMUL, typecheck.Conv(length, types.Types[types.TUINTPTR]), typecheck.Conv(ir.NewInt(t.Elem().Width), types.Types[types.TUINTPTR])) |
| |
| // instantiate mallocgc(size uintptr, typ *byte, needszero bool) unsafe.Pointer |
| fn := typecheck.LookupRuntime("mallocgc") |
| ptr := mkcall1(fn, types.Types[types.TUNSAFEPTR], init, size, typecheck.NodNil(), ir.NewBool(false)) |
| ptr.MarkNonNil() |
| sh := ir.NewSliceHeaderExpr(base.Pos, t, ptr, length, length) |
| |
| s := typecheck.Temp(t) |
| r := typecheck.Stmt(ir.NewAssignStmt(base.Pos, s, sh)) |
| r = walkExpr(r, init) |
| init.Append(r) |
| |
| // instantiate memmove(to *any, frm *any, size uintptr) |
| fn = typecheck.LookupRuntime("memmove") |
| fn = typecheck.SubstArgTypes(fn, t.Elem(), t.Elem()) |
| ncopy := mkcall1(fn, nil, init, ir.NewUnaryExpr(base.Pos, ir.OSPTR, s), copyptr, size) |
| init.Append(walkExpr(typecheck.Stmt(ncopy), init)) |
| |
| return s |
| } |
| // Replace make+copy with runtime.makeslicecopy. |
| // instantiate makeslicecopy(typ *byte, tolen int, fromlen int, from unsafe.Pointer) unsafe.Pointer |
| fn := typecheck.LookupRuntime("makeslicecopy") |
| ptr := mkcall1(fn, types.Types[types.TUNSAFEPTR], init, reflectdata.TypePtr(t.Elem()), length, copylen, typecheck.Conv(copyptr, types.Types[types.TUNSAFEPTR])) |
| ptr.MarkNonNil() |
| sh := ir.NewSliceHeaderExpr(base.Pos, t, ptr, length, length) |
| return walkExpr(typecheck.Expr(sh), init) |
| } |
| |
| // walkNew walks an ONEW node. |
| func walkNew(n *ir.UnaryExpr, init *ir.Nodes) ir.Node { |
| t := n.Type().Elem() |
| if t.NotInHeap() { |
| base.Errorf("%v can't be allocated in Go; it is incomplete (or unallocatable)", n.Type().Elem()) |
| } |
| if n.Esc() == ir.EscNone { |
| if t.Size() >= ir.MaxImplicitStackVarSize { |
| base.Fatalf("large ONEW with EscNone: %v", n) |
| } |
| return stackTempAddr(init, t) |
| } |
| types.CalcSize(t) |
| n.MarkNonNil() |
| return n |
| } |
| |
| // generate code for print |
| func walkPrint(nn *ir.CallExpr, init *ir.Nodes) ir.Node { |
| // Hoist all the argument evaluation up before the lock. |
| walkExprListCheap(nn.Args, init) |
| |
| // For println, add " " between elements and "\n" at the end. |
| if nn.Op() == ir.OPRINTN { |
| s := nn.Args |
| t := make([]ir.Node, 0, len(s)*2) |
| for i, n := range s { |
| if i != 0 { |
| t = append(t, ir.NewString(" ")) |
| } |
| t = append(t, n) |
| } |
| t = append(t, ir.NewString("\n")) |
| nn.Args = t |
| } |
| |
| // Collapse runs of constant strings. |
| s := nn.Args |
| t := make([]ir.Node, 0, len(s)) |
| for i := 0; i < len(s); { |
| var strs []string |
| for i < len(s) && ir.IsConst(s[i], constant.String) { |
| strs = append(strs, ir.StringVal(s[i])) |
| i++ |
| } |
| if len(strs) > 0 { |
| t = append(t, ir.NewString(strings.Join(strs, ""))) |
| } |
| if i < len(s) { |
| t = append(t, s[i]) |
| i++ |
| } |
| } |
| nn.Args = t |
| |
| calls := []ir.Node{mkcall("printlock", nil, init)} |
| for i, n := range nn.Args { |
| if n.Op() == ir.OLITERAL { |
| if n.Type() == types.UntypedRune { |
| n = typecheck.DefaultLit(n, types.RuneType) |
| } |
| |
| switch n.Val().Kind() { |
| case constant.Int: |
| n = typecheck.DefaultLit(n, types.Types[types.TINT64]) |
| |
| case constant.Float: |
| n = typecheck.DefaultLit(n, types.Types[types.TFLOAT64]) |
| } |
| } |
| |
| if n.Op() != ir.OLITERAL && n.Type() != nil && n.Type().Kind() == types.TIDEAL { |
| n = typecheck.DefaultLit(n, types.Types[types.TINT64]) |
| } |
| n = typecheck.DefaultLit(n, nil) |
| nn.Args[i] = n |
| if n.Type() == nil || n.Type().Kind() == types.TFORW { |
| continue |
| } |
| |
| var on *ir.Name |
| switch n.Type().Kind() { |
| case types.TINTER: |
| if n.Type().IsEmptyInterface() { |
| on = typecheck.LookupRuntime("printeface") |
| } else { |
| on = typecheck.LookupRuntime("printiface") |
| } |
| on = typecheck.SubstArgTypes(on, n.Type()) // any-1 |
| case types.TPTR: |
| if n.Type().Elem().NotInHeap() { |
| on = typecheck.LookupRuntime("printuintptr") |
| n = ir.NewConvExpr(base.Pos, ir.OCONV, nil, n) |
| n.SetType(types.Types[types.TUNSAFEPTR]) |
| n = ir.NewConvExpr(base.Pos, ir.OCONV, nil, n) |
| n.SetType(types.Types[types.TUINTPTR]) |
| break |
| } |
| fallthrough |
| case types.TCHAN, types.TMAP, types.TFUNC, types.TUNSAFEPTR: |
| on = typecheck.LookupRuntime("printpointer") |
| on = typecheck.SubstArgTypes(on, n.Type()) // any-1 |
| case types.TSLICE: |
| on = typecheck.LookupRuntime("printslice") |
| on = typecheck.SubstArgTypes(on, n.Type()) // any-1 |
| case types.TUINT, types.TUINT8, types.TUINT16, types.TUINT32, types.TUINT64, types.TUINTPTR: |
| if types.IsRuntimePkg(n.Type().Sym().Pkg) && n.Type().Sym().Name == "hex" { |
| on = typecheck.LookupRuntime("printhex") |
| } else { |
| on = typecheck.LookupRuntime("printuint") |
| } |
| case types.TINT, types.TINT8, types.TINT16, types.TINT32, types.TINT64: |
| on = typecheck.LookupRuntime("printint") |
| case types.TFLOAT32, types.TFLOAT64: |
| on = typecheck.LookupRuntime("printfloat") |
| case types.TCOMPLEX64, types.TCOMPLEX128: |
| on = typecheck.LookupRuntime("printcomplex") |
| case types.TBOOL: |
| on = typecheck.LookupRuntime("printbool") |
| case types.TSTRING: |
| cs := "" |
| if ir.IsConst(n, constant.String) { |
| cs = ir.StringVal(n) |
| } |
| switch cs { |
| case " ": |
| on = typecheck.LookupRuntime("printsp") |
| case "\n": |
| on = typecheck.LookupRuntime("printnl") |
| default: |
| on = typecheck.LookupRuntime("printstring") |
| } |
| default: |
| badtype(ir.OPRINT, n.Type(), nil) |
| continue |
| } |
| |
| r := ir.NewCallExpr(base.Pos, ir.OCALL, on, nil) |
| if params := on.Type().Params().FieldSlice(); len(params) > 0 { |
| t := params[0].Type |
| if !types.Identical(t, n.Type()) { |
| n = ir.NewConvExpr(base.Pos, ir.OCONV, nil, n) |
| n.SetType(t) |
| } |
| r.Args.Append(n) |
| } |
| calls = append(calls, r) |
| } |
| |
| calls = append(calls, mkcall("printunlock", nil, init)) |
| |
| typecheck.Stmts(calls) |
| walkExprList(calls, init) |
| |
| r := ir.NewBlockStmt(base.Pos, nil) |
| r.List = calls |
| return walkStmt(typecheck.Stmt(r)) |
| } |
| |
| // walkRecover walks an ORECOVER node. |
| func walkRecover(nn *ir.CallExpr, init *ir.Nodes) ir.Node { |
| // Call gorecover with the FP of this frame. |
| // FP is equal to caller's SP plus FixedFrameSize(). |
| var fp ir.Node = mkcall("getcallersp", types.Types[types.TUINTPTR], init) |
| if off := base.Ctxt.FixedFrameSize(); off != 0 { |
| fp = ir.NewBinaryExpr(fp.Pos(), ir.OADD, fp, ir.NewInt(off)) |
| } |
| fp = ir.NewConvExpr(fp.Pos(), ir.OCONVNOP, types.NewPtr(types.Types[types.TINT32]), fp) |
| return mkcall("gorecover", nn.Type(), init, fp) |
| } |
| |
| func walkUnsafeSlice(n *ir.BinaryExpr, init *ir.Nodes) ir.Node { |
| len := safeExpr(n.Y, init) |
| |
| fnname := "unsafeslice64" |
| argtype := types.Types[types.TINT64] |
| |
| // Type checking guarantees that TIDEAL len/cap are positive and fit in an int. |
| // The case of len or cap overflow when converting TUINT or TUINTPTR to TINT |
| // will be handled by the negative range checks in unsafeslice during runtime. |
| if len.Type().IsKind(types.TIDEAL) || len.Type().Size() <= types.Types[types.TUINT].Size() { |
| fnname = "unsafeslice" |
| argtype = types.Types[types.TINT] |
| } |
| |
| t := n.Type() |
| |
| // Call runtime.unsafeslice[64] to check that the length argument is |
| // non-negative and smaller than the max length allowed for the |
| // element type. |
| fn := typecheck.LookupRuntime(fnname) |
| init.Append(mkcall1(fn, nil, init, reflectdata.TypePtr(t.Elem()), typecheck.Conv(len, argtype))) |
| |
| ptr := walkExpr(n.X, init) |
| |
| c := ir.NewUnaryExpr(n.Pos(), ir.OCHECKNIL, ptr) |
| c.SetTypecheck(1) |
| init.Append(c) |
| |
| // TODO(mdempsky): checkptr instrumentation. Maybe merge into length |
| // check above, along with nil check? Need to be careful about |
| // notinheap pointers though: can't pass them as unsafe.Pointer. |
| |
| h := ir.NewSliceHeaderExpr(n.Pos(), t, |
| typecheck.Conv(ptr, types.Types[types.TUNSAFEPTR]), |
| typecheck.Conv(len, types.Types[types.TINT]), |
| typecheck.Conv(len, types.Types[types.TINT])) |
| return walkExpr(typecheck.Expr(h), init) |
| } |
| |
| func badtype(op ir.Op, tl, tr *types.Type) { |
| var s string |
| if tl != nil { |
| s += fmt.Sprintf("\n\t%v", tl) |
| } |
| if tr != nil { |
| s += fmt.Sprintf("\n\t%v", tr) |
| } |
| |
| // common mistake: *struct and *interface. |
| if tl != nil && tr != nil && tl.IsPtr() && tr.IsPtr() { |
| if tl.Elem().IsStruct() && tr.Elem().IsInterface() { |
| s += "\n\t(*struct vs *interface)" |
| } else if tl.Elem().IsInterface() && tr.Elem().IsStruct() { |
| s += "\n\t(*interface vs *struct)" |
| } |
| } |
| |
| base.Errorf("illegal types for operand: %v%s", op, s) |
| } |
| |
| func writebarrierfn(name string, l *types.Type, r *types.Type) ir.Node { |
| fn := typecheck.LookupRuntime(name) |
| fn = typecheck.SubstArgTypes(fn, l, r) |
| return fn |
| } |
| |
| // isRuneCount reports whether n is of the form len([]rune(string)). |
| // These are optimized into a call to runtime.countrunes. |
| func isRuneCount(n ir.Node) bool { |
| return base.Flag.N == 0 && !base.Flag.Cfg.Instrumenting && n.Op() == ir.OLEN && n.(*ir.UnaryExpr).X.Op() == ir.OSTR2RUNES |
| } |