Skip to content

Commit 439ce71

Browse files
randall77dmitshur
authored andcommitted
[release-branch.go1.15] cmd/compile: don't allow go:notinheap on the heap or stack
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/+/255320 Trust: Keith Randall <[email protected]> Reviewed-by: Austin Clements <[email protected]> Reviewed-by: Matthew Dempsky <[email protected]>
1 parent a460a2b commit 439ce71

16 files changed

+53
-66
lines changed

src/cmd/compile/internal/gc/escape.go

+3
Original file line numberDiff line numberDiff line change
@@ -1029,6 +1029,9 @@ func (e *Escape) newLoc(n *Node, transient bool) *EscLocation {
10291029
if e.curfn == nil {
10301030
Fatalf("e.curfn isn't set")
10311031
}
1032+
if n != nil && n.Type != nil && n.Type.NotInHeap() {
1033+
yyerrorl(n.Pos, "%v is go:notinheap; stack allocation disallowed", n.Type)
1034+
}
10321035

10331036
n = canonicalNode(n)
10341037
loc := &EscLocation{

src/cmd/compile/internal/gc/pgen_test.go

+1-9
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func typeWithoutPointers() *types.Type {
2020

2121
func typeWithPointers() *types.Type {
2222
t := types.New(TSTRUCT)
23-
f := &types.Field{Type: types.New(TPTR)}
23+
f := &types.Field{Type: types.NewPtr(types.New(TINT))}
2424
t.SetFields([]*types.Field{f})
2525
return t
2626
}
@@ -181,14 +181,6 @@ func TestStackvarSort(t *testing.T) {
181181
nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{Name: "xyz"}}, PAUTO),
182182
nodeWithClass(Node{Type: typeWithoutPointers(), Sym: &types.Sym{}}, PAUTO),
183183
}
184-
// haspointers updates Type.Haspointers as a side effect, so
185-
// exercise this function on all inputs so that reflect.DeepEqual
186-
// doesn't produce false positives.
187-
for i := range want {
188-
want[i].Type.HasPointers()
189-
inp[i].Type.HasPointers()
190-
}
191-
192184
sort.Sort(byStackVar(inp))
193185
if !reflect.DeepEqual(want, inp) {
194186
t.Error("sort failed")

src/cmd/compile/internal/gc/plive.go

+7-7
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ func (lv *Liveness) regEffects(v *ssa.Value) (uevar, kill liveRegMask) {
436436
case ssa.LocalSlot:
437437
return mask
438438
case *ssa.Register:
439-
if ptrOnly && !v.Type.HasHeapPointer() {
439+
if ptrOnly && !v.Type.HasPointers() {
440440
return mask
441441
}
442442
regs[0] = loc
@@ -451,7 +451,7 @@ func (lv *Liveness) regEffects(v *ssa.Value) (uevar, kill liveRegMask) {
451451
if loc1 == nil {
452452
continue
453453
}
454-
if ptrOnly && !v.Type.FieldType(i).HasHeapPointer() {
454+
if ptrOnly && !v.Type.FieldType(i).HasPointers() {
455455
continue
456456
}
457457
regs[nreg] = loc1.(*ssa.Register)
@@ -568,13 +568,13 @@ func onebitwalktype1(t *types.Type, off int64, bv bvec) {
568568
if t.Align > 0 && off&int64(t.Align-1) != 0 {
569569
Fatalf("onebitwalktype1: invalid initial alignment: type %v has alignment %d, but offset is %v", t, t.Align, off)
570570
}
571+
if !t.HasPointers() {
572+
// Note: this case ensures that pointers to go:notinheap types
573+
// are not considered pointers by garbage collection and stack copying.
574+
return
575+
}
571576

572577
switch t.Etype {
573-
case TINT8, TUINT8, TINT16, TUINT16,
574-
TINT32, TUINT32, TINT64, TUINT64,
575-
TINT, TUINT, TUINTPTR, TBOOL,
576-
TFLOAT32, TFLOAT64, TCOMPLEX64, TCOMPLEX128:
577-
578578
case TPTR, TUNSAFEPTR, TFUNC, TCHAN, TMAP:
579579
if off&int64(Widthptr-1) != 0 {
580580
Fatalf("onebitwalktype1: invalid alignment, %v", t)

src/cmd/compile/internal/gc/range.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,7 @@ func arrayClear(n, v1, v2, a *Node) bool {
586586
n.Nbody.Append(nod(OAS, hn, tmp))
587587

588588
var fn *Node
589-
if a.Type.Elem().HasHeapPointer() {
589+
if a.Type.Elem().HasPointers() {
590590
// memclrHasPointers(hp, hn)
591591
Curfn.Func.setWBPos(stmt.Pos)
592592
fn = mkcall("memclrHasPointers", nil, nil, hp, hn)

src/cmd/compile/internal/gc/walk.go

+11-12
Original file line numberDiff line numberDiff line change
@@ -1151,6 +1151,9 @@ opswitch:
11511151
}
11521152

11531153
case ONEW:
1154+
if n.Type.Elem().NotInHeap() {
1155+
yyerror("%v is go:notinheap; heap allocation disallowed", n.Type.Elem())
1156+
}
11541157
if n.Esc == EscNone {
11551158
if n.Type.Elem().Width >= maxImplicitStackVarSize {
11561159
Fatalf("large ONEW with EscNone: %v", n)
@@ -1319,6 +1322,9 @@ opswitch:
13191322
l = r
13201323
}
13211324
t := n.Type
1325+
if t.Elem().NotInHeap() {
1326+
yyerror("%v is go:notinheap; heap allocation disallowed", t.Elem())
1327+
}
13221328
if n.Esc == EscNone {
13231329
if !isSmallMakeSlice(n) {
13241330
Fatalf("non-small OMAKESLICE with EscNone: %v", n)
@@ -1360,10 +1366,6 @@ opswitch:
13601366
// When len and cap can fit into int, use makeslice instead of
13611367
// makeslice64, which is faster and shorter on 32 bit platforms.
13621368

1363-
if t.Elem().NotInHeap() {
1364-
yyerror("%v is go:notinheap; heap allocation disallowed", t.Elem())
1365-
}
1366-
13671369
len, cap := l, r
13681370

13691371
fnname := "makeslice64"
@@ -1398,7 +1400,7 @@ opswitch:
13981400

13991401
t := n.Type
14001402
if t.Elem().NotInHeap() {
1401-
Fatalf("%v is go:notinheap; heap allocation disallowed", t.Elem())
1403+
yyerror("%v is go:notinheap; heap allocation disallowed", t.Elem())
14021404
}
14031405

14041406
length := conv(n.Left, types.Types[TINT])
@@ -2002,9 +2004,6 @@ func walkprint(nn *Node, init *Nodes) *Node {
20022004
}
20032005

20042006
func callnew(t *types.Type) *Node {
2005-
if t.NotInHeap() {
2006-
yyerror("%v is go:notinheap; heap allocation disallowed", t)
2007-
}
20082007
dowidth(t)
20092008
n := nod(ONEWOBJ, typename(t), nil)
20102009
n.Type = types.NewPtr(t)
@@ -2579,15 +2578,15 @@ func mapfast(t *types.Type) int {
25792578
}
25802579
switch algtype(t.Key()) {
25812580
case AMEM32:
2582-
if !t.Key().HasHeapPointer() {
2581+
if !t.Key().HasPointers() {
25832582
return mapfast32
25842583
}
25852584
if Widthptr == 4 {
25862585
return mapfast32ptr
25872586
}
25882587
Fatalf("small pointer %v", t.Key())
25892588
case AMEM64:
2590-
if !t.Key().HasHeapPointer() {
2589+
if !t.Key().HasPointers() {
25912590
return mapfast64
25922591
}
25932592
if Widthptr == 8 {
@@ -2734,7 +2733,7 @@ func appendslice(n *Node, init *Nodes) *Node {
27342733
nodes.Append(nod(OAS, s, nt))
27352734

27362735
var ncopy *Node
2737-
if elemtype.HasHeapPointer() {
2736+
if elemtype.HasPointers() {
27382737
// copy(s[len(l1):], l2)
27392738
nptr1 := nod(OSLICE, s, nil)
27402739
nptr1.Type = s.Type
@@ -3072,7 +3071,7 @@ func walkappend(n *Node, init *Nodes, dst *Node) *Node {
30723071
// Also works if b is a string.
30733072
//
30743073
func copyany(n *Node, init *Nodes, runtimecall bool) *Node {
3075-
if n.Left.Type.Elem().HasHeapPointer() {
3074+
if n.Left.Type.Elem().HasPointers() {
30763075
Curfn.Func.setWBPos(n.Pos)
30773076
fn := writebarrierfn("typedslicecopy", n.Left.Type.Elem(), n.Right.Type.Elem())
30783077
n.Left = cheapexpr(n.Left, init)

src/cmd/compile/internal/ssa/decompose.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ func decomposeStringPhi(v *Value) {
139139

140140
func decomposeSlicePhi(v *Value) {
141141
types := &v.Block.Func.Config.Types
142-
ptrType := types.BytePtr
142+
ptrType := v.Type.Elem().PtrTo()
143143
lenType := types.Int
144144

145145
ptr := v.Block.NewValue0(v.Pos, OpPhi, ptrType)

src/cmd/compile/internal/ssa/gen/dec.rules

+2-2
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,14 @@
6666
(Load <typ.Int>
6767
(OffPtr <typ.IntPtr> [2*config.PtrSize] ptr)
6868
mem))
69-
(Store dst (SliceMake ptr len cap) mem) =>
69+
(Store {t} dst (SliceMake ptr len cap) mem) =>
7070
(Store {typ.Int}
7171
(OffPtr <typ.IntPtr> [2*config.PtrSize] dst)
7272
cap
7373
(Store {typ.Int}
7474
(OffPtr <typ.IntPtr> [config.PtrSize] dst)
7575
len
76-
(Store {typ.BytePtr} dst ptr mem)))
76+
(Store {t.Elem().PtrTo()} dst ptr mem)))
7777

7878
// interface ops
7979
(ITab (IMake itab _)) => itab

src/cmd/compile/internal/ssa/nilcheck.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ func nilcheckelim2(f *Func) {
235235
continue
236236
}
237237
if v.Type.IsMemory() || v.Type.IsTuple() && v.Type.FieldType(1).IsMemory() {
238-
if v.Op == OpVarKill || v.Op == OpVarLive || (v.Op == OpVarDef && !v.Aux.(GCNode).Typ().HasHeapPointer()) {
238+
if v.Op == OpVarKill || v.Op == OpVarLive || (v.Op == OpVarDef && !v.Aux.(GCNode).Typ().HasPointers()) {
239239
// These ops don't really change memory.
240240
continue
241241
// Note: OpVarDef requires that the defined variable not have pointers.

src/cmd/compile/internal/ssa/rewritedec.go

+4-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cmd/compile/internal/ssa/writebarrier.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func needwb(v *Value, zeroes map[ID]ZeroRegion) bool {
3131
if !ok {
3232
v.Fatalf("store aux is not a type: %s", v.LongString())
3333
}
34-
if !t.HasHeapPointer() {
34+
if !t.HasPointers() {
3535
return false
3636
}
3737
if IsStackAddr(v.Args[0]) {

src/cmd/compile/internal/types/type.go

+6-18
Original file line numberDiff line numberDiff line change
@@ -1397,14 +1397,9 @@ func (t *Type) IsUntyped() bool {
13971397
return false
13981398
}
13991399

1400-
// TODO(austin): We probably only need HasHeapPointer. See
1401-
// golang.org/cl/73412 for discussion.
1402-
1400+
// HasPointers reports whether t contains a heap pointer.
1401+
// Note that this function ignores pointers to go:notinheap types.
14031402
func (t *Type) HasPointers() bool {
1404-
return t.hasPointers1(false)
1405-
}
1406-
1407-
func (t *Type) hasPointers1(ignoreNotInHeap bool) bool {
14081403
switch t.Etype {
14091404
case TINT, TUINT, TINT8, TUINT8, TINT16, TUINT16, TINT32, TUINT32, TINT64,
14101405
TUINT64, TUINTPTR, TFLOAT32, TFLOAT64, TCOMPLEX64, TCOMPLEX128, TBOOL, TSSA:
@@ -1414,34 +1409,27 @@ func (t *Type) hasPointers1(ignoreNotInHeap bool) bool {
14141409
if t.NumElem() == 0 { // empty array has no pointers
14151410
return false
14161411
}
1417-
return t.Elem().hasPointers1(ignoreNotInHeap)
1412+
return t.Elem().HasPointers()
14181413

14191414
case TSTRUCT:
14201415
for _, t1 := range t.Fields().Slice() {
1421-
if t1.Type.hasPointers1(ignoreNotInHeap) {
1416+
if t1.Type.HasPointers() {
14221417
return true
14231418
}
14241419
}
14251420
return false
14261421

14271422
case TPTR, TSLICE:
1428-
return !(ignoreNotInHeap && t.Elem().NotInHeap())
1423+
return !t.Elem().NotInHeap()
14291424

14301425
case TTUPLE:
14311426
ttup := t.Extra.(*Tuple)
1432-
return ttup.first.hasPointers1(ignoreNotInHeap) || ttup.second.hasPointers1(ignoreNotInHeap)
1427+
return ttup.first.HasPointers() || ttup.second.HasPointers()
14331428
}
14341429

14351430
return true
14361431
}
14371432

1438-
// HasHeapPointer reports whether t contains a heap pointer.
1439-
// This is used for write barrier insertion, so it ignores
1440-
// pointers to go:notinheap types.
1441-
func (t *Type) HasHeapPointer() bool {
1442-
return t.hasPointers1(true)
1443-
}
1444-
14451433
func (t *Type) Symbol() *obj.LSym {
14461434
return TypeLinkSym(t)
14471435
}

src/runtime/export_test.go

+3-4
Original file line numberDiff line numberDiff line change
@@ -986,9 +986,8 @@ func MapHashCheck(m interface{}, k interface{}) (uintptr, uintptr) {
986986
}
987987

988988
func MSpanCountAlloc(bits []byte) int {
989-
s := mspan{
990-
nelems: uintptr(len(bits) * 8),
991-
gcmarkBits: (*gcBits)(unsafe.Pointer(&bits[0])),
992-
}
989+
s := (*mspan)(mheap_.spanalloc.alloc())
990+
s.nelems = uintptr(len(bits) * 8)
991+
s.gcmarkBits = (*gcBits)(unsafe.Pointer(&bits[0]))
993992
return s.countAlloc()
994993
}

src/runtime/mgcmark.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -937,7 +937,8 @@ func scanstack(gp *g, gcw *gcWork) {
937937
x := state.head
938938
state.head = x.next
939939
if stackTraceDebug {
940-
for _, obj := range x.obj[:x.nobj] {
940+
for i := 0; i < x.nobj; i++ {
941+
obj := &x.obj[i]
941942
if obj.typ == nil { // reachable
942943
continue
943944
}

src/runtime/mgcstack.go

-2
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,6 @@ func (obj *stackObject) setType(typ *_type) {
167167

168168
// A stackScanState keeps track of the state used during the GC walk
169169
// of a goroutine.
170-
//
171-
//go:notinheap
172170
type stackScanState struct {
173171
cache pcvalueCache
174172

src/runtime/runtime2.go

-2
Original file line numberDiff line numberDiff line change
@@ -918,8 +918,6 @@ type _defer struct {
918918
// handling during stack growth: because they are pointer-typed and
919919
// _panic values only live on the stack, regular stack pointer
920920
// adjustment takes care of them.
921-
//
922-
//go:notinheap
923921
type _panic struct {
924922
argp unsafe.Pointer // pointer to arguments of deferred call run during panic; cannot move - known to liblink
925923
arg interface{} // argument to panic

test/notinheap2.go

+10-2
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,32 @@ type nih struct {
1313
next *nih
1414
}
1515

16-
// Globals and stack variables are okay.
16+
// Global variables are okay.
1717

1818
var x nih
1919

20+
// Stack variables are not okay.
21+
2022
func f() {
21-
var y nih
23+
var y nih // ERROR "nih is go:notinheap; stack allocation disallowed"
2224
x = y
2325
}
2426

2527
// Heap allocation is not okay.
2628

2729
var y *nih
2830
var z []nih
31+
var w []nih
32+
var n int
2933

3034
func g() {
3135
y = new(nih) // ERROR "heap allocation disallowed"
3236
z = make([]nih, 1) // ERROR "heap allocation disallowed"
3337
z = append(z, x) // ERROR "heap allocation disallowed"
38+
// Test for special case of OMAKESLICECOPY
39+
x := make([]nih, n) // ERROR "heap allocation disallowed"
40+
copy(x, z)
41+
z = x
3442
}
3543

3644
// Writes don't produce write barriers.

0 commit comments

Comments
 (0)