]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: don't allow go:notinheap on the heap or stack
authorKeith Randall <khr@golang.org>
Sat, 22 Aug 2020 03:20:12 +0000 (20:20 -0700)
committerKeith Randall <khr@golang.org>
Tue, 25 Aug 2020 01:46:05 +0000 (01:46 +0000)
Right now we just prevent such types from being on the heap. This CL
makes it so they cannot appear on the stack either. The distinction
between heap and stack is pretty vague at the language level (e.g. it
is affected by -N), and we don't need the flexibility anyway.

Once go:notinheap types cannot be in either place, we don't need to
consider pointers to such types to be pointers, at least according to
the garbage collector and stack copying. (This is the big win of this
CL, in my opinion.)

The distinction between HasPointers and HasHeapPointer no longer
exists. There is only HasPointers.

This CL is cleanup before possible use of go:notinheap to fix #40954.

Update #13386

Change-Id: Ibd895aadf001c0385078a6d4809c3f374991231a
Reviewed-on: https://go-review.googlesource.com/c/go/+/249917
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
16 files changed:
src/cmd/compile/internal/gc/escape.go
src/cmd/compile/internal/gc/pgen_test.go
src/cmd/compile/internal/gc/plive.go
src/cmd/compile/internal/gc/range.go
src/cmd/compile/internal/gc/walk.go
src/cmd/compile/internal/ssa/decompose.go
src/cmd/compile/internal/ssa/gen/dec.rules
src/cmd/compile/internal/ssa/nilcheck.go
src/cmd/compile/internal/ssa/rewritedec.go
src/cmd/compile/internal/ssa/writebarrier.go
src/cmd/compile/internal/types/type.go
src/runtime/export_test.go
src/runtime/mgcmark.go
src/runtime/mgcstack.go
src/runtime/runtime2.go
test/notinheap2.go

index ddf89f6159a339b02e2b09244d42cee78397e6da..d5cca4a38bdcb770dab5af7f010772bc5ae50309 100644 (file)
@@ -1029,6 +1029,9 @@ func (e *Escape) newLoc(n *Node, transient bool) *EscLocation {
        if e.curfn == nil {
                Fatalf("e.curfn isn't set")
        }
+       if n != nil && n.Type != nil && n.Type.NotInHeap() {
+               yyerrorl(n.Pos, "%v is go:notinheap; stack allocation disallowed", n.Type)
+       }
 
        n = canonicalNode(n)
        loc := &EscLocation{
index 41f0808a1ca3891fd61628da5810d5320105f104..b1db29825c2c1efa1bc84834cbfb5eb65af5e5f5 100644 (file)
@@ -20,7 +20,7 @@ func typeWithoutPointers() *types.Type {
 
 func typeWithPointers() *types.Type {
        t := types.New(TSTRUCT)
-       f := &types.Field{Type: types.New(TPTR)}
+       f := &types.Field{Type: types.NewPtr(types.New(TINT))}
        t.SetFields([]*types.Field{f})
        return t
 }
@@ -181,14 +181,6 @@ func TestStackvarSort(t *testing.T) {
                nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{Name: "xyz"}}, PAUTO),
                nodeWithClass(Node{Type: typeWithoutPointers(), Sym: &types.Sym{}}, PAUTO),
        }
-       // haspointers updates Type.Haspointers as a side effect, so
-       // exercise this function on all inputs so that reflect.DeepEqual
-       // doesn't produce false positives.
-       for i := range want {
-               want[i].Type.HasPointers()
-               inp[i].Type.HasPointers()
-       }
-
        sort.Sort(byStackVar(inp))
        if !reflect.DeepEqual(want, inp) {
                t.Error("sort failed")
index 398bfe5baa44da521dac571a40f3d94d8acb26ab..8976ed657ab67c720f8b1cabcbb6a8ea63e237ae 100644 (file)
@@ -436,7 +436,7 @@ func (lv *Liveness) regEffects(v *ssa.Value) (uevar, kill liveRegMask) {
                case ssa.LocalSlot:
                        return mask
                case *ssa.Register:
-                       if ptrOnly && !v.Type.HasHeapPointer() {
+                       if ptrOnly && !v.Type.HasPointers() {
                                return mask
                        }
                        regs[0] = loc
@@ -451,7 +451,7 @@ func (lv *Liveness) regEffects(v *ssa.Value) (uevar, kill liveRegMask) {
                                if loc1 == nil {
                                        continue
                                }
-                               if ptrOnly && !v.Type.FieldType(i).HasHeapPointer() {
+                               if ptrOnly && !v.Type.FieldType(i).HasPointers() {
                                        continue
                                }
                                regs[nreg] = loc1.(*ssa.Register)
@@ -568,13 +568,13 @@ func onebitwalktype1(t *types.Type, off int64, bv bvec) {
        if t.Align > 0 && off&int64(t.Align-1) != 0 {
                Fatalf("onebitwalktype1: invalid initial alignment: type %v has alignment %d, but offset is %v", t, t.Align, off)
        }
+       if !t.HasPointers() {
+               // Note: this case ensures that pointers to go:notinheap types
+               // are not considered pointers by garbage collection and stack copying.
+               return
+       }
 
        switch t.Etype {
-       case TINT8, TUINT8, TINT16, TUINT16,
-               TINT32, TUINT32, TINT64, TUINT64,
-               TINT, TUINT, TUINTPTR, TBOOL,
-               TFLOAT32, TFLOAT64, TCOMPLEX64, TCOMPLEX128:
-
        case TPTR, TUNSAFEPTR, TFUNC, TCHAN, TMAP:
                if off&int64(Widthptr-1) != 0 {
                        Fatalf("onebitwalktype1: invalid alignment, %v", t)
index d78a5f0d8dec8bbef5cf627ae258d6a3e5142378..5434b0167aa482de2ed1b4fa2fd607b49378d38b 100644 (file)
@@ -586,7 +586,7 @@ func arrayClear(n, v1, v2, a *Node) bool {
        n.Nbody.Append(nod(OAS, hn, tmp))
 
        var fn *Node
-       if a.Type.Elem().HasHeapPointer() {
+       if a.Type.Elem().HasPointers() {
                // memclrHasPointers(hp, hn)
                Curfn.Func.setWBPos(stmt.Pos)
                fn = mkcall("memclrHasPointers", nil, nil, hp, hn)
index 77f88d89965648146319e8bae80f7214fc4d58be..90ecb50d6a48971077c482ef554d639dc6379794 100644 (file)
@@ -1156,6 +1156,9 @@ opswitch:
                }
 
        case ONEW:
+               if n.Type.Elem().NotInHeap() {
+                       yyerror("%v is go:notinheap; heap allocation disallowed", n.Type.Elem())
+               }
                if n.Esc == EscNone {
                        if n.Type.Elem().Width >= maxImplicitStackVarSize {
                                Fatalf("large ONEW with EscNone: %v", n)
@@ -1324,6 +1327,9 @@ opswitch:
                        l = r
                }
                t := n.Type
+               if t.Elem().NotInHeap() {
+                       yyerror("%v is go:notinheap; heap allocation disallowed", t.Elem())
+               }
                if n.Esc == EscNone {
                        if !isSmallMakeSlice(n) {
                                Fatalf("non-small OMAKESLICE with EscNone: %v", n)
@@ -1365,10 +1371,6 @@ opswitch:
                        // When len and cap can fit into int, use makeslice instead of
                        // makeslice64, which is faster and shorter on 32 bit platforms.
 
-                       if t.Elem().NotInHeap() {
-                               yyerror("%v is go:notinheap; heap allocation disallowed", t.Elem())
-                       }
-
                        len, cap := l, r
 
                        fnname := "makeslice64"
@@ -1403,7 +1405,7 @@ opswitch:
 
                t := n.Type
                if t.Elem().NotInHeap() {
-                       Fatalf("%v is go:notinheap; heap allocation disallowed", t.Elem())
+                       yyerror("%v is go:notinheap; heap allocation disallowed", t.Elem())
                }
 
                length := conv(n.Left, types.Types[TINT])
@@ -2012,9 +2014,6 @@ func walkprint(nn *Node, init *Nodes) *Node {
 }
 
 func callnew(t *types.Type) *Node {
-       if t.NotInHeap() {
-               yyerror("%v is go:notinheap; heap allocation disallowed", t)
-       }
        dowidth(t)
        n := nod(ONEWOBJ, typename(t), nil)
        n.Type = types.NewPtr(t)
@@ -2589,7 +2588,7 @@ func mapfast(t *types.Type) int {
        }
        switch algtype(t.Key()) {
        case AMEM32:
-               if !t.Key().HasHeapPointer() {
+               if !t.Key().HasPointers() {
                        return mapfast32
                }
                if Widthptr == 4 {
@@ -2597,7 +2596,7 @@ func mapfast(t *types.Type) int {
                }
                Fatalf("small pointer %v", t.Key())
        case AMEM64:
-               if !t.Key().HasHeapPointer() {
+               if !t.Key().HasPointers() {
                        return mapfast64
                }
                if Widthptr == 8 {
@@ -2744,7 +2743,7 @@ func appendslice(n *Node, init *Nodes) *Node {
        nodes.Append(nod(OAS, s, nt))
 
        var ncopy *Node
-       if elemtype.HasHeapPointer() {
+       if elemtype.HasPointers() {
                // copy(s[len(l1):], l2)
                nptr1 := nod(OSLICE, s, nil)
                nptr1.Type = s.Type
@@ -3082,7 +3081,7 @@ func walkappend(n *Node, init *Nodes, dst *Node) *Node {
 // Also works if b is a string.
 //
 func copyany(n *Node, init *Nodes, runtimecall bool) *Node {
-       if n.Left.Type.Elem().HasHeapPointer() {
+       if n.Left.Type.Elem().HasPointers() {
                Curfn.Func.setWBPos(n.Pos)
                fn := writebarrierfn("typedslicecopy", n.Left.Type.Elem(), n.Right.Type.Elem())
                n.Left = cheapexpr(n.Left, init)
index c59ec4c77d764db4189b227e2e033ff8c8131481..6e72e3825ce0a7fa585d3f14c2d4ed41e31f6aef 100644 (file)
@@ -139,7 +139,7 @@ func decomposeStringPhi(v *Value) {
 
 func decomposeSlicePhi(v *Value) {
        types := &v.Block.Func.Config.Types
-       ptrType := types.BytePtr
+       ptrType := v.Type.Elem().PtrTo()
        lenType := types.Int
 
        ptr := v.Block.NewValue0(v.Pos, OpPhi, ptrType)
index 3fd2be409ff9790c065339378e72fdbc8a3ed6b2..4c677f8418f61b9e5192c05e7290c0015ad27e13 100644 (file)
     (Load <typ.Int>
       (OffPtr <typ.IntPtr> [2*config.PtrSize] ptr)
       mem))
-(Store dst (SliceMake ptr len cap) mem) =>
+(Store {t} dst (SliceMake ptr len cap) mem) =>
   (Store {typ.Int}
     (OffPtr <typ.IntPtr> [2*config.PtrSize] dst)
     cap
     (Store {typ.Int}
       (OffPtr <typ.IntPtr> [config.PtrSize] dst)
       len
-      (Store {typ.BytePtr} dst ptr mem)))
+      (Store {t.Elem().PtrTo()} dst ptr mem)))
 
 // interface ops
 (ITab (IMake itab _)) => itab
index 6b24371ac7a95ebe8f839fcc5a2e70593b5e9d29..d1bad529e700cecf3c20b13fe4d5f99bc07a9cb2 100644 (file)
@@ -235,7 +235,7 @@ func nilcheckelim2(f *Func) {
                                continue
                        }
                        if v.Type.IsMemory() || v.Type.IsTuple() && v.Type.FieldType(1).IsMemory() {
-                               if v.Op == OpVarKill || v.Op == OpVarLive || (v.Op == OpVarDef && !v.Aux.(GCNode).Typ().HasHeapPointer()) {
+                               if v.Op == OpVarKill || v.Op == OpVarLive || (v.Op == OpVarDef && !v.Aux.(GCNode).Typ().HasPointers()) {
                                        // These ops don't really change memory.
                                        continue
                                        // Note: OpVarDef requires that the defined variable not have pointers.
index cef781ffaa4e934b4c3f5b492b13ffc78cb9a64e..e0fa9768d959bc70b15691965c024ea45c473b1b 100644 (file)
@@ -328,9 +328,10 @@ func rewriteValuedec_OpStore(v *Value) bool {
                v.AddArg3(v0, len, v1)
                return true
        }
-       // match: (Store dst (SliceMake ptr len cap) mem)
-       // result: (Store {typ.Int} (OffPtr <typ.IntPtr> [2*config.PtrSize] dst) cap (Store {typ.Int} (OffPtr <typ.IntPtr> [config.PtrSize] dst) len (Store {typ.BytePtr} dst ptr mem)))
+       // match: (Store {t} dst (SliceMake ptr len cap) mem)
+       // result: (Store {typ.Int} (OffPtr <typ.IntPtr> [2*config.PtrSize] dst) cap (Store {typ.Int} (OffPtr <typ.IntPtr> [config.PtrSize] dst) len (Store {t.Elem().PtrTo()} dst ptr mem)))
        for {
+               t := auxToType(v.Aux)
                dst := v_0
                if v_1.Op != OpSliceMake {
                        break
@@ -350,7 +351,7 @@ func rewriteValuedec_OpStore(v *Value) bool {
                v2.AuxInt = int64ToAuxInt(config.PtrSize)
                v2.AddArg(dst)
                v3 := b.NewValue0(v.Pos, OpStore, types.TypeMem)
-               v3.Aux = typeToAux(typ.BytePtr)
+               v3.Aux = typeToAux(t.Elem().PtrTo())
                v3.AddArg3(dst, ptr, mem)
                v1.AddArg3(v2, len, v3)
                v.AddArg3(v0, cap, v1)
index c7fb0594759b00d19ef5b6b3aa6938e652cd652c..214798a1ab39d649c2bce5b87e51a320f6e55a2b 100644 (file)
@@ -31,7 +31,7 @@ func needwb(v *Value, zeroes map[ID]ZeroRegion) bool {
        if !ok {
                v.Fatalf("store aux is not a type: %s", v.LongString())
        }
-       if !t.HasHeapPointer() {
+       if !t.HasPointers() {
                return false
        }
        if IsStackAddr(v.Args[0]) {
index 20ae856bba8f7ab2e2c34c666028ea2c2fddfd86..e4b3d885d94c7d97ad3a4c9fd7e2ea6a8be5b8bd 100644 (file)
@@ -1398,14 +1398,9 @@ func (t *Type) IsUntyped() bool {
        return false
 }
 
-// TODO(austin): We probably only need HasHeapPointer. See
-// golang.org/cl/73412 for discussion.
-
+// HasPointers reports whether t contains a heap pointer.
+// Note that this function ignores pointers to go:notinheap types.
 func (t *Type) HasPointers() bool {
-       return t.hasPointers1(false)
-}
-
-func (t *Type) hasPointers1(ignoreNotInHeap bool) bool {
        switch t.Etype {
        case TINT, TUINT, TINT8, TUINT8, TINT16, TUINT16, TINT32, TUINT32, TINT64,
                TUINT64, TUINTPTR, TFLOAT32, TFLOAT64, TCOMPLEX64, TCOMPLEX128, TBOOL, TSSA:
@@ -1415,34 +1410,27 @@ func (t *Type) hasPointers1(ignoreNotInHeap bool) bool {
                if t.NumElem() == 0 { // empty array has no pointers
                        return false
                }
-               return t.Elem().hasPointers1(ignoreNotInHeap)
+               return t.Elem().HasPointers()
 
        case TSTRUCT:
                for _, t1 := range t.Fields().Slice() {
-                       if t1.Type.hasPointers1(ignoreNotInHeap) {
+                       if t1.Type.HasPointers() {
                                return true
                        }
                }
                return false
 
        case TPTR, TSLICE:
-               return !(ignoreNotInHeap && t.Elem().NotInHeap())
+               return !t.Elem().NotInHeap()
 
        case TTUPLE:
                ttup := t.Extra.(*Tuple)
-               return ttup.first.hasPointers1(ignoreNotInHeap) || ttup.second.hasPointers1(ignoreNotInHeap)
+               return ttup.first.HasPointers() || ttup.second.HasPointers()
        }
 
        return true
 }
 
-// HasHeapPointer reports whether t contains a heap pointer.
-// This is used for write barrier insertion, so it ignores
-// pointers to go:notinheap types.
-func (t *Type) HasHeapPointer() bool {
-       return t.hasPointers1(true)
-}
-
 func (t *Type) Symbol() *obj.LSym {
        return TypeLinkSym(t)
 }
index d591fdc4e949858b5797d636fb734431641865be..3307000c51a9294a2894d10185378e9ff411fd58 100644 (file)
@@ -981,9 +981,8 @@ func MapHashCheck(m interface{}, k interface{}) (uintptr, uintptr) {
 }
 
 func MSpanCountAlloc(bits []byte) int {
-       s := mspan{
-               nelems:     uintptr(len(bits) * 8),
-               gcmarkBits: (*gcBits)(unsafe.Pointer(&bits[0])),
-       }
+       s := (*mspan)(mheap_.spanalloc.alloc())
+       s.nelems = uintptr(len(bits) * 8)
+       s.gcmarkBits = (*gcBits)(unsafe.Pointer(&bits[0]))
        return s.countAlloc()
 }
index 2b84945471b3908f0d78e6eb68cf9dc56873cc2f..79df59d6d694a1562ab8761474285577783ffaef 100644 (file)
@@ -837,7 +837,8 @@ func scanstack(gp *g, gcw *gcWork) {
                x := state.head
                state.head = x.next
                if stackTraceDebug {
-                       for _, obj := range x.obj[:x.nobj] {
+                       for i := 0; i < x.nobj; i++ {
+                               obj := &x.obj[i]
                                if obj.typ == nil { // reachable
                                        continue
                                }
index 211d882fa661104872e72e112ad2d75119613e6c..8eb941a3282a97bfd9a40d20b727b6fc974addb6 100644 (file)
@@ -167,8 +167,6 @@ func (obj *stackObject) setType(typ *_type) {
 
 // A stackScanState keeps track of the state used during the GC walk
 // of a goroutine.
-//
-//go:notinheap
 type stackScanState struct {
        cache pcvalueCache
 
index b7d0739e543e3984c14ffc9287fc00ad57f12c6b..64c6cc7198300239156cb07eb32e5de6180d2427 100644 (file)
@@ -909,15 +909,12 @@ type _defer struct {
 
 // A _panic holds information about an active panic.
 //
-// This is marked go:notinheap because _panic values must only ever
-// live on the stack.
+// A _panic value must only ever live on the stack.
 //
 // The argp and link fields are stack pointers, but don't need special
 // handling during stack growth: because they are pointer-typed and
 // _panic values only live on the stack, regular stack pointer
 // adjustment takes care of them.
-//
-//go:notinheap
 type _panic struct {
        argp      unsafe.Pointer // pointer to arguments of deferred call run during panic; cannot move - known to liblink
        arg       interface{}    // argument to panic
index 944f2993ab171b9744165d1dfb950ff4e9e8b230..de1e6db1d31a05af50c0864bb114fe0042d3224e 100644 (file)
@@ -13,12 +13,14 @@ type nih struct {
        next *nih
 }
 
-// Globals and stack variables are okay.
+// Global variables are okay.
 
 var x nih
 
+// Stack variables are not okay.
+
 func f() {
-       var y nih
+       var y nih // ERROR "nih is go:notinheap; stack allocation disallowed"
        x = y
 }
 
@@ -26,11 +28,17 @@ func f() {
 
 var y *nih
 var z []nih
+var w []nih
+var n int
 
 func g() {
        y = new(nih)       // ERROR "heap allocation disallowed"
        z = make([]nih, 1) // ERROR "heap allocation disallowed"
        z = append(z, x)   // ERROR "heap allocation disallowed"
+       // Test for special case of OMAKESLICECOPY
+       x := make([]nih, n) // ERROR "heap allocation disallowed"
+       copy(x, z)
+       z = x
 }
 
 // Writes don't produce write barriers.