mirror of
https://github.com/golang/go
synced 2024-11-14 05:50:27 -07:00
cmd/compile/internal/reflectdata: share hmap and hiter types
There's no need for distinct hmap and hiter types for each map. Shaves 9kB off cmd/go binary size. Change-Id: I7bc3b2d8ec82e7fcd78c1cb17733ebd8b615990a Reviewed-on: https://go-review.googlesource.com/c/go/+/521615 Run-TryBot: Matthew Dempsky <mdempsky@google.com> Reviewed-by: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
parent
ba6fe5172d
commit
42f4ccb6f9
@ -195,14 +195,14 @@ func MapBucketType(t *types.Type) *types.Type {
|
|||||||
return bucket
|
return bucket
|
||||||
}
|
}
|
||||||
|
|
||||||
// MapType builds a type representing a Hmap structure for the given map type.
|
var hmapType *types.Type
|
||||||
// Make sure this stays in sync with runtime/map.go.
|
|
||||||
func MapType(t *types.Type) *types.Type {
|
|
||||||
if t.MapType().Hmap != nil {
|
|
||||||
return t.MapType().Hmap
|
|
||||||
}
|
|
||||||
|
|
||||||
bmap := MapBucketType(t)
|
// MapType returns a type interchangeable with runtime.hmap.
|
||||||
|
// Make sure this stays in sync with runtime/map.go.
|
||||||
|
func MapType() *types.Type {
|
||||||
|
if hmapType != nil {
|
||||||
|
return hmapType
|
||||||
|
}
|
||||||
|
|
||||||
// build a struct:
|
// build a struct:
|
||||||
// type hmap struct {
|
// type hmap struct {
|
||||||
@ -211,8 +211,8 @@ func MapType(t *types.Type) *types.Type {
|
|||||||
// B uint8
|
// B uint8
|
||||||
// noverflow uint16
|
// noverflow uint16
|
||||||
// hash0 uint32
|
// hash0 uint32
|
||||||
// buckets *bmap
|
// buckets unsafe.Pointer
|
||||||
// oldbuckets *bmap
|
// oldbuckets unsafe.Pointer
|
||||||
// nevacuate uintptr
|
// nevacuate uintptr
|
||||||
// extra unsafe.Pointer // *mapextra
|
// extra unsafe.Pointer // *mapextra
|
||||||
// }
|
// }
|
||||||
@ -222,15 +222,19 @@ func MapType(t *types.Type) *types.Type {
|
|||||||
makefield("flags", types.Types[types.TUINT8]),
|
makefield("flags", types.Types[types.TUINT8]),
|
||||||
makefield("B", types.Types[types.TUINT8]),
|
makefield("B", types.Types[types.TUINT8]),
|
||||||
makefield("noverflow", types.Types[types.TUINT16]),
|
makefield("noverflow", types.Types[types.TUINT16]),
|
||||||
makefield("hash0", types.Types[types.TUINT32]), // Used in walk.go for OMAKEMAP.
|
makefield("hash0", types.Types[types.TUINT32]), // Used in walk.go for OMAKEMAP.
|
||||||
makefield("buckets", types.NewPtr(bmap)), // Used in walk.go for OMAKEMAP.
|
makefield("buckets", types.Types[types.TUNSAFEPTR]), // Used in walk.go for OMAKEMAP.
|
||||||
makefield("oldbuckets", types.NewPtr(bmap)),
|
makefield("oldbuckets", types.Types[types.TUNSAFEPTR]),
|
||||||
makefield("nevacuate", types.Types[types.TUINTPTR]),
|
makefield("nevacuate", types.Types[types.TUINTPTR]),
|
||||||
makefield("extra", types.Types[types.TUNSAFEPTR]),
|
makefield("extra", types.Types[types.TUNSAFEPTR]),
|
||||||
}
|
}
|
||||||
|
|
||||||
hmap := types.NewStruct(fields)
|
n := ir.NewDeclNameAt(src.NoXPos, ir.OTYPE, ir.Pkgs.Runtime.Lookup("hmap"))
|
||||||
hmap.SetNoalg(true)
|
hmap := types.NewNamed(n)
|
||||||
|
n.SetType(hmap)
|
||||||
|
n.SetTypecheck(1)
|
||||||
|
|
||||||
|
hmap.SetUnderlying(types.NewStruct(fields))
|
||||||
types.CalcSize(hmap)
|
types.CalcSize(hmap)
|
||||||
|
|
||||||
// The size of hmap should be 48 bytes on 64 bit
|
// The size of hmap should be 48 bytes on 64 bit
|
||||||
@ -239,29 +243,29 @@ func MapType(t *types.Type) *types.Type {
|
|||||||
base.Fatalf("hmap size not correct: got %d, want %d", hmap.Size(), size)
|
base.Fatalf("hmap size not correct: got %d, want %d", hmap.Size(), size)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.MapType().Hmap = hmap
|
hmapType = hmap
|
||||||
hmap.StructType().Map = t
|
|
||||||
return hmap
|
return hmap
|
||||||
}
|
}
|
||||||
|
|
||||||
// MapIterType builds a type representing an Hiter structure for the given map type.
|
var hiterType *types.Type
|
||||||
|
|
||||||
|
// MapIterType returns a type interchangeable with runtime.hiter.
|
||||||
// Make sure this stays in sync with runtime/map.go.
|
// Make sure this stays in sync with runtime/map.go.
|
||||||
func MapIterType(t *types.Type) *types.Type {
|
func MapIterType() *types.Type {
|
||||||
if t.MapType().Hiter != nil {
|
if hiterType != nil {
|
||||||
return t.MapType().Hiter
|
return hiterType
|
||||||
}
|
}
|
||||||
|
|
||||||
hmap := MapType(t)
|
hmap := MapType()
|
||||||
bmap := MapBucketType(t)
|
|
||||||
|
|
||||||
// build a struct:
|
// build a struct:
|
||||||
// type hiter struct {
|
// type hiter struct {
|
||||||
// key *Key
|
// key unsafe.Pointer // *Key
|
||||||
// elem *Elem
|
// elem unsafe.Pointer // *Elem
|
||||||
// t unsafe.Pointer // *MapType
|
// t unsafe.Pointer // *MapType
|
||||||
// h *hmap
|
// h *hmap
|
||||||
// buckets *bmap
|
// buckets unsafe.Pointer
|
||||||
// bptr *bmap
|
// bptr unsafe.Pointer // *bmap
|
||||||
// overflow unsafe.Pointer // *[]*bmap
|
// overflow unsafe.Pointer // *[]*bmap
|
||||||
// oldoverflow unsafe.Pointer // *[]*bmap
|
// oldoverflow unsafe.Pointer // *[]*bmap
|
||||||
// startBucket uintptr
|
// startBucket uintptr
|
||||||
@ -274,12 +278,12 @@ func MapIterType(t *types.Type) *types.Type {
|
|||||||
// }
|
// }
|
||||||
// must match runtime/map.go:hiter.
|
// must match runtime/map.go:hiter.
|
||||||
fields := []*types.Field{
|
fields := []*types.Field{
|
||||||
makefield("key", types.NewPtr(t.Key())), // Used in range.go for TMAP.
|
makefield("key", types.Types[types.TUNSAFEPTR]), // Used in range.go for TMAP.
|
||||||
makefield("elem", types.NewPtr(t.Elem())), // Used in range.go for TMAP.
|
makefield("elem", types.Types[types.TUNSAFEPTR]), // Used in range.go for TMAP.
|
||||||
makefield("t", types.Types[types.TUNSAFEPTR]),
|
makefield("t", types.Types[types.TUNSAFEPTR]),
|
||||||
makefield("h", types.NewPtr(hmap)),
|
makefield("h", types.NewPtr(hmap)),
|
||||||
makefield("buckets", types.NewPtr(bmap)),
|
makefield("buckets", types.Types[types.TUNSAFEPTR]),
|
||||||
makefield("bptr", types.NewPtr(bmap)),
|
makefield("bptr", types.Types[types.TUNSAFEPTR]),
|
||||||
makefield("overflow", types.Types[types.TUNSAFEPTR]),
|
makefield("overflow", types.Types[types.TUNSAFEPTR]),
|
||||||
makefield("oldoverflow", types.Types[types.TUNSAFEPTR]),
|
makefield("oldoverflow", types.Types[types.TUNSAFEPTR]),
|
||||||
makefield("startBucket", types.Types[types.TUINTPTR]),
|
makefield("startBucket", types.Types[types.TUINTPTR]),
|
||||||
@ -292,14 +296,18 @@ func MapIterType(t *types.Type) *types.Type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// build iterator struct holding the above fields
|
// build iterator struct holding the above fields
|
||||||
hiter := types.NewStruct(fields)
|
n := ir.NewDeclNameAt(src.NoXPos, ir.OTYPE, ir.Pkgs.Runtime.Lookup("hiter"))
|
||||||
hiter.SetNoalg(true)
|
hiter := types.NewNamed(n)
|
||||||
|
n.SetType(hiter)
|
||||||
|
n.SetTypecheck(1)
|
||||||
|
|
||||||
|
hiter.SetUnderlying(types.NewStruct(fields))
|
||||||
types.CalcSize(hiter)
|
types.CalcSize(hiter)
|
||||||
if hiter.Size() != int64(12*types.PtrSize) {
|
if hiter.Size() != int64(12*types.PtrSize) {
|
||||||
base.Fatalf("hash_iter size not correct %d %d", hiter.Size(), 12*types.PtrSize)
|
base.Fatalf("hash_iter size not correct %d %d", hiter.Size(), 12*types.PtrSize)
|
||||||
}
|
}
|
||||||
t.MapType().Hiter = hiter
|
|
||||||
hiter.StructType().Map = t
|
hiterType = hiter
|
||||||
return hiter
|
return hiter
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -947,16 +955,11 @@ func writeType(t *types.Type) *obj.LSym {
|
|||||||
|
|
||||||
s := types.TypeSym(t)
|
s := types.TypeSym(t)
|
||||||
lsym := s.Linksym()
|
lsym := s.Linksym()
|
||||||
if s.Siggen() {
|
|
||||||
return lsym
|
|
||||||
}
|
|
||||||
s.SetSiggen(true)
|
|
||||||
|
|
||||||
// special case (look for runtime below):
|
// special case (look for runtime below):
|
||||||
// when compiling package runtime,
|
// when compiling package runtime,
|
||||||
// emit the type structures for int, float, etc.
|
// emit the type structures for int, float, etc.
|
||||||
tbase := t
|
tbase := t
|
||||||
|
|
||||||
if t.IsPtr() && t.Sym() == nil && t.Elem().Sym() != nil {
|
if t.IsPtr() && t.Sym() == nil && t.Elem().Sym() != nil {
|
||||||
tbase = t.Elem()
|
tbase = t.Elem()
|
||||||
}
|
}
|
||||||
@ -964,6 +967,19 @@ func writeType(t *types.Type) *obj.LSym {
|
|||||||
base.Fatalf("unresolved defined type: %v", tbase)
|
base.Fatalf("unresolved defined type: %v", tbase)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is a fake type we generated for our builtin pseudo-runtime
|
||||||
|
// package. We'll emit a description for the real type while
|
||||||
|
// compiling package runtime, so we don't need or want to emit one
|
||||||
|
// from this fake type.
|
||||||
|
if sym := tbase.Sym(); sym != nil && sym.Pkg == ir.Pkgs.Runtime {
|
||||||
|
return lsym
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Siggen() {
|
||||||
|
return lsym
|
||||||
|
}
|
||||||
|
s.SetSiggen(true)
|
||||||
|
|
||||||
if !NeedEmit(tbase) {
|
if !NeedEmit(tbase) {
|
||||||
if i := typecheck.BaseTypeIndex(t); i >= 0 {
|
if i := typecheck.BaseTypeIndex(t); i >= 0 {
|
||||||
lsym.Pkg = tbase.Sym().Pkg.Prefix
|
lsym.Pkg = tbase.Sym().Pkg.Prefix
|
||||||
|
@ -86,6 +86,7 @@ func InitConfig() {
|
|||||||
_ = types.NewPtr(types.Types[types.TINT16]) // *int16
|
_ = types.NewPtr(types.Types[types.TINT16]) // *int16
|
||||||
_ = types.NewPtr(types.Types[types.TINT64]) // *int64
|
_ = types.NewPtr(types.Types[types.TINT64]) // *int64
|
||||||
_ = types.NewPtr(types.ErrorType) // *error
|
_ = types.NewPtr(types.ErrorType) // *error
|
||||||
|
_ = types.NewPtr(reflectdata.MapType()) // *runtime.hmap
|
||||||
types.NewPtrCacheEnabled = false
|
types.NewPtrCacheEnabled = false
|
||||||
ssaConfig = ssa.NewConfig(base.Ctxt.Arch.Name, *types_, base.Ctxt, base.Flag.N == 0, Arch.SoftFloat)
|
ssaConfig = ssa.NewConfig(base.Ctxt.Arch.Name, *types_, base.Ctxt, base.Flag.N == 0, Arch.SoftFloat)
|
||||||
ssaConfig.Race = base.Flag.Race
|
ssaConfig.Race = base.Flag.Race
|
||||||
@ -2802,8 +2803,7 @@ func (s *state) exprCheckPtr(n ir.Node, checkPtrOK bool) *ssa.Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// map <--> *hmap
|
// map <--> *hmap
|
||||||
if to.Kind() == types.TMAP && from.IsPtr() &&
|
if to.Kind() == types.TMAP && from == types.NewPtr(reflectdata.MapType()) {
|
||||||
to.MapType().Hmap == from.Elem() {
|
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -537,15 +537,7 @@ func Convertop(srcConstant bool, src, dst *types.Type) (ir.Op, string) {
|
|||||||
return ir.OCONVNOP, ""
|
return ir.OCONVNOP, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// 10. src is map and dst is a pointer to corresponding hmap.
|
// 10. src is a slice and dst is an array or pointer-to-array.
|
||||||
// This rule is needed for the implementation detail that
|
|
||||||
// go gc maps are implemented as a pointer to a hmap struct.
|
|
||||||
if src.Kind() == types.TMAP && dst.IsPtr() &&
|
|
||||||
src.MapType().Hmap == dst.Elem() {
|
|
||||||
return ir.OCONVNOP, ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// 11. src is a slice and dst is an array or pointer-to-array.
|
|
||||||
// They must have same element type.
|
// They must have same element type.
|
||||||
if src.IsSlice() {
|
if src.IsSlice() {
|
||||||
if dst.IsArray() && types.Identical(src.Elem(), dst.Elem()) {
|
if dst.IsArray() && types.Identical(src.Elem(), dst.Elem()) {
|
||||||
|
@ -511,10 +511,6 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type
|
|||||||
switch t {
|
switch t {
|
||||||
case mt.Bucket:
|
case mt.Bucket:
|
||||||
b.WriteString("map.bucket[")
|
b.WriteString("map.bucket[")
|
||||||
case mt.Hmap:
|
|
||||||
b.WriteString("map.hdr[")
|
|
||||||
case mt.Hiter:
|
|
||||||
b.WriteString("map.iter[")
|
|
||||||
default:
|
default:
|
||||||
base.Fatalf("unknown internal map type")
|
base.Fatalf("unknown internal map type")
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ func TestSizeof(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{Sym{}, 32, 64},
|
{Sym{}, 32, 64},
|
||||||
{Type{}, 56, 96},
|
{Type{}, 56, 96},
|
||||||
{Map{}, 20, 40},
|
{Map{}, 12, 24},
|
||||||
{Forward{}, 20, 32},
|
{Forward{}, 20, 32},
|
||||||
{Func{}, 20, 32},
|
{Func{}, 20, 32},
|
||||||
{Struct{}, 12, 24},
|
{Struct{}, 12, 24},
|
||||||
|
@ -276,8 +276,6 @@ type Map struct {
|
|||||||
Elem *Type // Val (elem) type
|
Elem *Type // Val (elem) type
|
||||||
|
|
||||||
Bucket *Type // internal struct type representing a hash bucket
|
Bucket *Type // internal struct type representing a hash bucket
|
||||||
Hmap *Type // internal struct type representing the Hmap (map header object)
|
|
||||||
Hiter *Type // internal struct type representing hash iterator state
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MapType returns t's extra map-specific fields.
|
// MapType returns t's extra map-specific fields.
|
||||||
|
@ -302,7 +302,7 @@ func walkMakeChan(n *ir.MakeExpr, init *ir.Nodes) ir.Node {
|
|||||||
// walkMakeMap walks an OMAKEMAP node.
|
// walkMakeMap walks an OMAKEMAP node.
|
||||||
func walkMakeMap(n *ir.MakeExpr, init *ir.Nodes) ir.Node {
|
func walkMakeMap(n *ir.MakeExpr, init *ir.Nodes) ir.Node {
|
||||||
t := n.Type()
|
t := n.Type()
|
||||||
hmapType := reflectdata.MapType(t)
|
hmapType := reflectdata.MapType()
|
||||||
hint := n.Len
|
hint := n.Len
|
||||||
|
|
||||||
// var h *hmap
|
// var h *hmap
|
||||||
@ -340,7 +340,7 @@ func walkMakeMap(n *ir.MakeExpr, init *ir.Nodes) ir.Node {
|
|||||||
|
|
||||||
// h.buckets = b
|
// h.buckets = b
|
||||||
bsym := hmapType.Field(5).Sym // hmap.buckets see reflect.go:hmap
|
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)
|
na := ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, h, bsym), typecheck.ConvNop(b, types.Types[types.TUNSAFEPTR]))
|
||||||
nif.Body.Append(na)
|
nif.Body.Append(na)
|
||||||
appendWalkStmt(init, nif)
|
appendWalkStmt(init, nif)
|
||||||
}
|
}
|
||||||
|
@ -872,7 +872,7 @@ func (o *orderState) stmt(n ir.Node) {
|
|||||||
|
|
||||||
// n.Prealloc is the temp for the iterator.
|
// n.Prealloc is the temp for the iterator.
|
||||||
// MapIterType contains pointers and needs to be zeroed.
|
// MapIterType contains pointers and needs to be zeroed.
|
||||||
n.Prealloc = o.newTemp(reflectdata.MapIterType(xt), true)
|
n.Prealloc = o.newTemp(reflectdata.MapIterType(), true)
|
||||||
}
|
}
|
||||||
n.Key = o.exprInPlace(n.Key)
|
n.Key = o.exprInPlace(n.Key)
|
||||||
n.Value = o.exprInPlace(n.Value)
|
n.Value = o.exprInPlace(n.Value)
|
||||||
|
@ -241,13 +241,13 @@ func walkRange(nrange *ir.RangeStmt) ir.Node {
|
|||||||
fn = typecheck.SubstArgTypes(fn, th)
|
fn = typecheck.SubstArgTypes(fn, th)
|
||||||
nfor.Post = mkcallstmt1(fn, typecheck.NodAddr(hit))
|
nfor.Post = mkcallstmt1(fn, typecheck.NodAddr(hit))
|
||||||
|
|
||||||
key := ir.NewStarExpr(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, hit, keysym))
|
key := ir.NewStarExpr(base.Pos, typecheck.ConvNop(ir.NewSelectorExpr(base.Pos, ir.ODOT, hit, keysym), types.NewPtr(t.Key())))
|
||||||
if v1 == nil {
|
if v1 == nil {
|
||||||
body = nil
|
body = nil
|
||||||
} else if v2 == nil {
|
} else if v2 == nil {
|
||||||
body = []ir.Node{rangeAssign(nrange, key)}
|
body = []ir.Node{rangeAssign(nrange, key)}
|
||||||
} else {
|
} else {
|
||||||
elem := ir.NewStarExpr(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, hit, elemsym))
|
elem := ir.NewStarExpr(base.Pos, typecheck.ConvNop(ir.NewSelectorExpr(base.Pos, ir.ODOT, hit, elemsym), types.NewPtr(t.Elem())))
|
||||||
body = []ir.Node{rangeAssign2(nrange, key, elem)}
|
body = []ir.Node{rangeAssign2(nrange, key, elem)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,7 +458,7 @@ func f28(b bool) {
|
|||||||
|
|
||||||
func f29(b bool) {
|
func f29(b bool) {
|
||||||
if b {
|
if b {
|
||||||
for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ map.iter\[string\]int$"
|
for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ runtime.hiter$"
|
||||||
printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$"
|
printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -667,7 +667,7 @@ func bad40() {
|
|||||||
|
|
||||||
func good40() {
|
func good40() {
|
||||||
ret := T40{} // ERROR "stack object ret T40$"
|
ret := T40{} // ERROR "stack object ret T40$"
|
||||||
ret.m = make(map[int]int) // ERROR "live at call to fastrand: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ map.hdr\[int\]int$"
|
ret.m = make(map[int]int) // ERROR "live at call to fastrand: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ runtime.hmap$"
|
||||||
t := &ret
|
t := &ret
|
||||||
printnl() // ERROR "live at call to printnl: ret$"
|
printnl() // ERROR "live at call to printnl: ret$"
|
||||||
// Note: ret is live at the printnl because the compiler moves &ret
|
// Note: ret is live at the printnl because the compiler moves &ret
|
||||||
|
@ -27,14 +27,14 @@ func newT40() *T40 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func bad40() {
|
func bad40() {
|
||||||
t := newT40() // ERROR "stack object ret T40$" "stack object .autotmp_[0-9]+ map.hdr\[int\]int$"
|
t := newT40() // ERROR "stack object ret T40$" "stack object .autotmp_[0-9]+ runtime.hmap$"
|
||||||
printnl() // ERROR "live at call to printnl: ret$"
|
printnl() // ERROR "live at call to printnl: ret$"
|
||||||
useT40(t)
|
useT40(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func good40() {
|
func good40() {
|
||||||
ret := T40{} // ERROR "stack object ret T40$"
|
ret := T40{} // ERROR "stack object ret T40$"
|
||||||
ret.m = make(map[int]int, 42) // ERROR "stack object .autotmp_[0-9]+ map.hdr\[int\]int$"
|
ret.m = make(map[int]int, 42) // ERROR "stack object .autotmp_[0-9]+ runtime.hmap$"
|
||||||
t := &ret
|
t := &ret
|
||||||
printnl() // ERROR "live at call to printnl: ret$"
|
printnl() // ERROR "live at call to printnl: ret$"
|
||||||
useT40(t)
|
useT40(t)
|
||||||
|
@ -455,7 +455,7 @@ func f28(b bool) {
|
|||||||
|
|
||||||
func f29(b bool) {
|
func f29(b bool) {
|
||||||
if b {
|
if b {
|
||||||
for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ map.iter\[string\]int$"
|
for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ runtime.hiter$"
|
||||||
printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$"
|
printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -664,7 +664,7 @@ func bad40() {
|
|||||||
|
|
||||||
func good40() {
|
func good40() {
|
||||||
ret := T40{} // ERROR "stack object ret T40$"
|
ret := T40{} // ERROR "stack object ret T40$"
|
||||||
ret.m = make(map[int]int) // ERROR "live at call to fastrand: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ map.hdr\[int\]int$"
|
ret.m = make(map[int]int) // ERROR "live at call to fastrand: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ runtime.hmap$"
|
||||||
t := &ret
|
t := &ret
|
||||||
printnl() // ERROR "live at call to printnl: ret$"
|
printnl() // ERROR "live at call to printnl: ret$"
|
||||||
// Note: ret is live at the printnl because the compiler moves &ret
|
// Note: ret is live at the printnl because the compiler moves &ret
|
||||||
|
Loading…
Reference in New Issue
Block a user