type Pragma syntax.Pragma
const (
+ // Func pragmas.
Nointerface Pragma = 1 << iota
Noescape // func parameters don't escape
Norace // func must not have race detector annotations
CgoUnsafeArgs // treat a pointer to one arg as a pointer to them all
UintptrEscapes // pointers converted to uintptr escape
- // Runtime-only pragmas.
+ // Runtime-only func pragmas.
// See ../../../../runtime/README.md for detailed descriptions.
-
Systemstack // func must run on system stack
Nowritebarrier // emit compiler error instead of write barrier
Nowritebarrierrec // error on write barrier in this or recursive callees
Yeswritebarrierrec // cancels Nowritebarrierrec in this function and callees
+
+ // Runtime-only type pragmas
+ NotInHeap // values of this type must not be heap allocated
)
func pragmaValue(verb string) Pragma {
// in the argument list.
// Used in syscall/dll_windows.go.
return UintptrEscapes
+ case "go:notinheap":
+ return NotInHeap
}
return 0
}
func (p *noder) typeDecl(decl *syntax.TypeDecl) *Node {
name := typedcl0(p.name(decl.Name))
+ name.Name.Param.Pragma = Pragma(decl.Pragma)
var typ *Node
if decl.Type != nil {
}
name := typedcl0(p.sym())
+ name.Name.Param.Pragma = p.pragma
typ := p.try_ntype()
// handle case where type is missing
return 0
}
+ // Conversions from regular to go:notinheap are not allowed
+ // (unless it's unsafe.Pointer). This is a runtime-specific
+ // rule.
+ if src.IsPtr() && dst.IsPtr() && dst.Elem().NotInHeap && !src.Elem().NotInHeap {
+ if why != nil {
+ *why = fmt.Sprintf(":\n\t%v is go:notinheap, but %v is not", dst.Elem(), src.Elem())
+ }
+ return 0
+ }
+
// 1. src can be assigned to dst.
op := assignop(src, dst, why)
if op != 0 {
// and x.Innermost/Outer means x.Name.Param.Innermost/Outer.
Innermost *Node
Outer *Node
+
+ // OTYPE pragmas
+ //
+ // TODO: Should Func pragmas also be stored on the Name?
+ Pragma Pragma
}
// Func holds Node fields used only with function-like nodes.
Deferwidth bool
Broke bool // broken type definition.
Align uint8 // the required alignment of this type, in bytes
+ NotInHeap bool // type cannot be heap allocated
}
// MapType contains Type fields specific to maps.
}
t := typ(TARRAY)
t.Extra = &ArrayType{Elem: elem, Bound: bound}
+ t.NotInHeap = elem.NotInHeap
return t
}
func typDDDArray(elem *Type) *Type {
t := typ(TARRAY)
t.Extra = &ArrayType{Elem: elem, Bound: -1}
+ t.NotInHeap = elem.NotInHeap
return t
}
// SetFields sets struct/interface type t's fields/methods to fields.
func (t *Type) SetFields(fields []*Field) {
+ for _, f := range fields {
+ // If type T contains a field F with a go:notinheap
+ // type, then T must also be go:notinheap. Otherwise,
+ // you could heap allocate T and then get a pointer F,
+ // which would be a heap pointer to a go:notinheap
+ // type.
+ if f.Type != nil && f.Type.NotInHeap {
+ t.NotInHeap = true
+ break
+ }
+ }
t.Fields().Set(fields)
}
n.Type = nil
return n
}
+ if l.Type.NotInHeap {
+ yyerror("go:notinheap map key not allowed")
+ }
+ if r.Type.NotInHeap {
+ yyerror("go:notinheap map value not allowed")
+ }
n.Op = OTYPE
n.Type = typMap(l.Type, r.Type)
n.Type = nil
return n
}
+ if l.Type.NotInHeap {
+ yyerror("chan of go:notinheap type not allowed")
+ }
t := typChan(l.Type, ChanDir(n.Etype)) // TODO(marvin): Fix Node.EType type union.
n.Op = OTYPE
n.Type = t
ok |= Etop
n.Left = typecheck(n.Left, Etype)
checkwidth(n.Left.Type)
+ if n.Left.Type != nil && n.Left.Type.NotInHeap && n.Left.Name.Param.Pragma&NotInHeap == 0 {
+ // The type contains go:notinheap types, so it
+ // must be marked as such (alternatively, we
+ // could silently propagate go:notinheap).
+ yyerror("type %v must be go:notinheap", n.Left.Type)
+ }
break OpSwitch
}
t.ptrTo = ptrTo
t.sliceOf = sliceOf
+ // Propagate go:notinheap pragma from the Name to the Type.
+ if n.Name != nil && n.Name.Param != nil && n.Name.Param.Pragma&NotInHeap != 0 {
+ t.NotInHeap = true
+ }
+
// Update nodes waiting on this type.
for _, n := range l {
copytype(n, t)
case OAPPEND:
// x = append(...)
r := n.Right
+ if r.Type.Elem().NotInHeap {
+ yyerror("%v is go:notinheap; heap allocation disallowed", r.Type.Elem())
+ }
if r.Isddd {
r = appendslice(r, init) // also works for append(slice, string).
} else {
// 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"
}
func callnew(t *Type) *Node {
+ if t.NotInHeap {
+ yyerror("%v is go:notinheap; heap allocation disallowed", t)
+ }
dowidth(t)
fn := syslook("newobject")
fn = substArgTypes(fn, t)
return false
}
+ // No write barrier if this is a pointer to a go:notinheap
+ // type, since the write barrier's inheap(ptr) check will fail.
+ if l.Type.IsPtr() && l.Type.Elem().NotInHeap {
+ return false
+ }
+
// Ignore no-op conversions when making decision.
// Ensures that xp = unsafe.Pointer(&x) is treated
// the same as xp = &x.
// Name Type
TypeDecl struct {
- Name *Name
- Type Expr
- Alias bool
- Group *Group // nil means not part of a group
+ Name *Name
+ Type Expr
+ Alias bool
+ Group *Group // nil means not part of a group
+ Pragma Pragma
decl
}
p.advance(_Semi, _Rparen)
}
d.Group = group
+ d.Pragma = p.pragma
return d
}
type Mode uint
-// A Pragma value is a set of flags that augment a function
-// declaration. Callers may assign meaning to the flags as
+// A Pragma value is a set of flags that augment a function or
+// type declaration. Callers may assign meaning to the flags as
// appropriate.
type Pragma uint16
`go:yeswritebarrierrec` is used when code re-acquires an active P.
Since these are function-level annotations, code that releases or
acquires a P may need to be split across two functions.
+
+go:notinheap
+------------
+
+`go:notinheap` applies to type declarations. It indicates that a type
+must never be heap allocated. Specifically, pointers to this type must
+always fail the `runtime.inheap` check. The type may be used for
+global variables, for stack variables, or for objects in unmanaged
+memory (e.g., allocated with `sysAlloc`, `persistentalloc`, or
+`fixalloc`). Specifically:
+
+1. `new(T)`, `make([]T)`, `append([]T, ...)` and implicit heap
+ allocation of T are disallowed. (Though implicit allocations are
+ disallowed in the runtime anyway.)
+
+2. A pointer to a regular type (other than `unsafe.Pointer`) cannot be
+ converted to a pointer to a `go:notinheap` type, even if they have
+ the same underlying type.
+
+3. Any type that contains a `go:notinheap` type is itself
+ `go:notinheap`. Structs and arrays are `go:notinheap` if their
+ elements are. Maps and channels of `go:notinheap` types are
+ disallowed. To keep things explicit, any type declaration where the
+ type is implicitly `go:notinheap` must be explicitly marked
+ `go:notinheap` as well.
+
+4. Write barriers on pointers to `go:notinheap` types can be omitted.
+
+The last point is the real benefit of `go:notinheap`. The runtime uses
+it for low-level internal structures to avoid memory barriers in the
+scheduler and the memory allocator where they are illegal or simply
+inefficient. This mechanism is reasonably safe and does not compromise
+the readability of the runtime.
--- /dev/null
+// errorcheck -+
+
+// Copyright 2016 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.
+
+// Test type-checking errors for go:notinheap.
+
+package p
+
+//go:notinheap
+type nih struct{}
+
+// Types embedding notinheap types must be notinheap.
+
+type embed1 struct {
+ x nih
+} // ERROR "must be go:notinheap"
+
+type embed2 [1]nih // ERROR "must be go:notinheap"
+
+type embed3 struct {
+ x [1]nih
+} // ERROR "must be go:notinheap"
+
+type embed4 map[nih]int // ERROR "go:notinheap map key not allowed"
+
+type embed5 map[int]nih // ERROR "go:notinheap map value not allowed"
+
+type emebd6 chan nih // ERROR "chan of go:notinheap type not allowed"
+
+type okay1 *nih
+
+type okay2 []nih
+
+type okay3 func(x nih) nih
+
+type okay4 interface {
+ f(x nih) nih
+}
+
+// Type conversions don't let you sneak past notinheap.
+
+type t1 struct{ x int }
+
+//go:notinheap
+type t2 t1
+
+var sink interface{}
+
+func i() {
+ sink = new(t1) // no error
+ sink = (*t2)(new(t1)) // ERROR "cannot convert(.|\n)*t2 is go:notinheap"
+ sink = (*t2)(new(struct{ x int })) // ERROR "cannot convert(.|\n)*t2 is go:notinheap"
+}
--- /dev/null
+// errorcheck -+
+
+// Copyright 2016 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.
+
+// Test walk errors for go:notinheap.
+
+package p
+
+//go:notinheap
+type nih struct {
+ next *nih
+}
+
+// Globals and stack variables are okay.
+
+var x nih
+
+func f() {
+ var y nih
+ x = y
+}
+
+// Heap allocation is not okay.
+
+var y *nih
+var z []nih
+
+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"
+}
+
+// Writes don't produce write barriers.
+
+var p *nih
+
+//go:nowritebarrier
+func h() {
+ y.next = p.next
+}