2014-11-11 15:05:19 -07:00
|
|
|
// 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.
|
|
|
|
|
2015-01-21 23:31:49 -07:00
|
|
|
// Runtime type representation.
|
2014-11-11 15:05:19 -07:00
|
|
|
|
|
|
|
package runtime
|
|
|
|
|
2016-03-31 08:02:10 -06:00
|
|
|
import "unsafe"
|
2014-11-11 15:05:19 -07:00
|
|
|
|
2016-04-07 14:29:16 -06:00
|
|
|
// tflag is documented in reflect/type.go.
|
|
|
|
//
|
|
|
|
// tflag values must be kept in sync with copies in:
|
|
|
|
// cmd/compile/internal/gc/reflect.go
|
|
|
|
// cmd/link/internal/ld/decodesym.go
|
|
|
|
// reflect/type.go
|
2016-02-20 20:54:15 -07:00
|
|
|
type tflag uint8
|
|
|
|
|
2016-04-07 14:29:16 -06:00
|
|
|
const (
|
|
|
|
tflagUncommon tflag = 1 << 0
|
|
|
|
tflagExtraStar tflag = 1 << 1
|
|
|
|
)
|
2016-02-20 20:54:15 -07:00
|
|
|
|
2015-08-20 21:23:57 -06:00
|
|
|
// Needs to be in sync with ../cmd/compile/internal/ld/decodesym.go:/^func.commonsize,
|
|
|
|
// ../cmd/compile/internal/gc/reflect.go:/^func.dcommontype and
|
2015-05-04 12:37:45 -06:00
|
|
|
// ../reflect/type.go:/^type.rtype.
|
2014-11-11 15:05:19 -07:00
|
|
|
type _type struct {
|
|
|
|
size uintptr
|
2015-05-04 19:43:30 -06:00
|
|
|
ptrdata uintptr // size of memory prefix holding all pointers
|
2014-11-11 15:05:19 -07:00
|
|
|
hash uint32
|
2016-02-20 20:54:15 -07:00
|
|
|
tflag tflag
|
2014-11-11 15:05:19 -07:00
|
|
|
align uint8
|
|
|
|
fieldalign uint8
|
|
|
|
kind uint8
|
2014-12-27 21:32:11 -07:00
|
|
|
alg *typeAlg
|
runtime: replace GC programs with simpler encoding, faster decoder
Small types record the location of pointers in their memory layout
by using a simple bitmap. In Go 1.4 the bitmap held 4-bit entries,
and in Go 1.5 the bitmap holds 1-bit entries, but in both cases using
a bitmap for a large type containing arrays does not make sense:
if someone refers to the type [1<<28]*byte in a program in such
a way that the type information makes it into the binary, it would be
a waste of space to write a 128 MB (for 4-bit entries) or even 32 MB
(for 1-bit entries) bitmap full of 1s into the binary or even to keep
one in memory during the execution of the program.
For large types containing arrays, it is much more compact to describe
the locations of pointers using a notation that can express repetition
than to lay out a bitmap of pointers. Go 1.4 included such a notation,
called ``GC programs'' but it was complex, required recursion during
decoding, and was generally slow. Dmitriy measured the execution of
these programs writing directly to the heap bitmap as being 7x slower
than copying from a preunrolled 4-bit mask (and frankly that code was
not terribly fast either). For some tests, unrollgcprog1 was seen costing
as much as 3x more than the rest of malloc combined.
This CL introduces a different form for the GC programs. They use a
simple Lempel-Ziv-style encoding of the 1-bit pointer information,
in which the only operations are (1) emit the following n bits
and (2) repeat the last n bits c more times. This encoding can be
generated directly from the Go type information (using repetition
only for arrays or large runs of non-pointer data) and it can be decoded
very efficiently. In particular the decoding requires little state and
no recursion, so that the entire decoding can run without any memory
accesses other than the reads of the encoding and the writes of the
decoded form to the heap bitmap. For recursive types like arrays of
arrays of arrays, the inner instructions are only executed once, not
n times, so that large repetitions run at full speed. (In contrast, large
repetitions in the old programs repeated the individual bit-level layout
of the inner data over and over.) The result is as much as 25x faster
decoding compared to the old form.
Because the old decoder was so slow, Go 1.4 had three (or so) cases
for how to set the heap bitmap bits for an allocation of a given type:
(1) If the type had an even number of words up to 32 words, then
the 4-bit pointer mask for the type fit in no more than 16 bytes;
store the 4-bit pointer mask directly in the binary and copy from it.
(1b) If the type had an odd number of words up to 15 words, then
the 4-bit pointer mask for the type, doubled to end on a byte boundary,
fit in no more than 16 bytes; store that doubled mask directly in the
binary and copy from it.
(2) If the type had an even number of words up to 128 words,
or an odd number of words up to 63 words (again due to doubling),
then the 4-bit pointer mask would fit in a 64-byte unrolled mask.
Store a GC program in the binary, but leave space in the BSS for
the unrolled mask. Execute the GC program to construct the mask the
first time it is needed, and thereafter copy from the mask.
(3) Otherwise, store a GC program and execute it to write directly to
the heap bitmap each time an object of that type is allocated.
(This is the case that was 7x slower than the other two.)
Because the new pointer masks store 1-bit entries instead of 4-bit
entries and because using the decoder no longer carries a significant
overhead, after this CL (that is, for Go 1.5) there are only two cases:
(1) If the type is 128 words or less (no condition about odd or even),
store the 1-bit pointer mask directly in the binary and use it to
initialize the heap bitmap during malloc. (Implemented in CL 9702.)
(2) There is no case 2 anymore.
(3) Otherwise, store a GC program and execute it to write directly to
the heap bitmap each time an object of that type is allocated.
Executing the GC program directly into the heap bitmap (case (3) above)
was disabled for the Go 1.5 dev cycle, both to avoid needing to use
GC programs for typedmemmove and to avoid updating that code as
the heap bitmap format changed. Typedmemmove no longer uses this
type information; as of CL 9886 it uses the heap bitmap directly.
Now that the heap bitmap format is stable, we reintroduce GC programs
and their space savings.
Benchmarks for heapBitsSetType, before this CL vs this CL:
name old mean new mean delta
SetTypePtr 7.59ns × (0.99,1.02) 5.16ns × (1.00,1.00) -32.05% (p=0.000)
SetTypePtr8 21.0ns × (0.98,1.05) 21.4ns × (1.00,1.00) ~ (p=0.179)
SetTypePtr16 24.1ns × (0.99,1.01) 24.6ns × (1.00,1.00) +2.41% (p=0.001)
SetTypePtr32 31.2ns × (0.99,1.01) 32.4ns × (0.99,1.02) +3.72% (p=0.001)
SetTypePtr64 45.2ns × (1.00,1.00) 47.2ns × (1.00,1.00) +4.42% (p=0.000)
SetTypePtr126 75.8ns × (0.99,1.01) 79.1ns × (1.00,1.00) +4.25% (p=0.000)
SetTypePtr128 74.3ns × (0.99,1.01) 77.6ns × (1.00,1.01) +4.55% (p=0.000)
SetTypePtrSlice 726ns × (1.00,1.01) 712ns × (1.00,1.00) -1.95% (p=0.001)
SetTypeNode1 20.0ns × (0.99,1.01) 20.7ns × (1.00,1.00) +3.71% (p=0.000)
SetTypeNode1Slice 112ns × (1.00,1.00) 113ns × (0.99,1.00) ~ (p=0.070)
SetTypeNode8 23.9ns × (1.00,1.00) 24.7ns × (1.00,1.01) +3.18% (p=0.000)
SetTypeNode8Slice 294ns × (0.99,1.02) 287ns × (0.99,1.01) -2.38% (p=0.015)
SetTypeNode64 52.8ns × (0.99,1.03) 51.8ns × (0.99,1.01) ~ (p=0.069)
SetTypeNode64Slice 1.13µs × (0.99,1.05) 1.14µs × (0.99,1.00) ~ (p=0.767)
SetTypeNode64Dead 36.0ns × (1.00,1.01) 32.5ns × (0.99,1.00) -9.67% (p=0.000)
SetTypeNode64DeadSlice 1.43µs × (0.99,1.01) 1.40µs × (1.00,1.00) -2.39% (p=0.001)
SetTypeNode124 75.7ns × (1.00,1.01) 79.0ns × (1.00,1.00) +4.44% (p=0.000)
SetTypeNode124Slice 1.94µs × (1.00,1.01) 2.04µs × (0.99,1.01) +4.98% (p=0.000)
SetTypeNode126 75.4ns × (1.00,1.01) 77.7ns × (0.99,1.01) +3.11% (p=0.000)
SetTypeNode126Slice 1.95µs × (0.99,1.01) 2.03µs × (1.00,1.00) +3.74% (p=0.000)
SetTypeNode128 85.4ns × (0.99,1.01) 122.0ns × (1.00,1.00) +42.89% (p=0.000)
SetTypeNode128Slice 2.20µs × (1.00,1.01) 2.36µs × (0.98,1.02) +7.48% (p=0.001)
SetTypeNode130 83.3ns × (1.00,1.00) 123.0ns × (1.00,1.00) +47.61% (p=0.000)
SetTypeNode130Slice 2.30µs × (0.99,1.01) 2.40µs × (0.98,1.01) +4.37% (p=0.000)
SetTypeNode1024 498ns × (1.00,1.00) 537ns × (1.00,1.00) +7.96% (p=0.000)
SetTypeNode1024Slice 15.5µs × (0.99,1.01) 17.8µs × (1.00,1.00) +15.27% (p=0.000)
The above compares always using a cached pointer mask (and the
corresponding waste of memory) against using the programs directly.
Some slowdown is expected, in exchange for having a better general algorithm.
The GC programs kick in for SetTypeNode128, SetTypeNode130, SetTypeNode1024,
along with the slice variants of those.
It is possible that the cutoff of 128 words (bits) should be raised
in a followup CL, but even with this low cutoff the GC programs are
faster than Go 1.4's "fast path" non-GC program case.
Benchmarks for heapBitsSetType, Go 1.4 vs this CL:
name old mean new mean delta
SetTypePtr 6.89ns × (1.00,1.00) 5.17ns × (1.00,1.00) -25.02% (p=0.000)
SetTypePtr8 25.8ns × (0.97,1.05) 21.5ns × (1.00,1.00) -16.70% (p=0.000)
SetTypePtr16 39.8ns × (0.97,1.02) 24.7ns × (0.99,1.01) -37.81% (p=0.000)
SetTypePtr32 68.8ns × (0.98,1.01) 32.2ns × (1.00,1.01) -53.18% (p=0.000)
SetTypePtr64 130ns × (1.00,1.00) 47ns × (1.00,1.00) -63.67% (p=0.000)
SetTypePtr126 241ns × (0.99,1.01) 79ns × (1.00,1.01) -67.25% (p=0.000)
SetTypePtr128 2.07µs × (1.00,1.00) 0.08µs × (1.00,1.00) -96.27% (p=0.000)
SetTypePtrSlice 1.05µs × (0.99,1.01) 0.72µs × (0.99,1.02) -31.70% (p=0.000)
SetTypeNode1 16.0ns × (0.99,1.01) 20.8ns × (0.99,1.03) +29.91% (p=0.000)
SetTypeNode1Slice 184ns × (0.99,1.01) 112ns × (0.99,1.01) -39.26% (p=0.000)
SetTypeNode8 29.5ns × (0.97,1.02) 24.6ns × (1.00,1.00) -16.50% (p=0.000)
SetTypeNode8Slice 624ns × (0.98,1.02) 285ns × (1.00,1.00) -54.31% (p=0.000)
SetTypeNode64 135ns × (0.96,1.08) 52ns × (0.99,1.02) -61.32% (p=0.000)
SetTypeNode64Slice 3.83µs × (1.00,1.00) 1.14µs × (0.99,1.01) -70.16% (p=0.000)
SetTypeNode64Dead 134ns × (0.99,1.01) 32ns × (1.00,1.01) -75.74% (p=0.000)
SetTypeNode64DeadSlice 3.83µs × (0.99,1.00) 1.40µs × (1.00,1.01) -63.42% (p=0.000)
SetTypeNode124 240ns × (0.99,1.01) 79ns × (1.00,1.01) -67.05% (p=0.000)
SetTypeNode124Slice 7.27µs × (1.00,1.00) 2.04µs × (1.00,1.00) -71.95% (p=0.000)
SetTypeNode126 2.06µs × (0.99,1.01) 0.08µs × (0.99,1.01) -96.23% (p=0.000)
SetTypeNode126Slice 64.4µs × (1.00,1.00) 2.0µs × (1.00,1.00) -96.85% (p=0.000)
SetTypeNode128 2.09µs × (1.00,1.01) 0.12µs × (1.00,1.00) -94.15% (p=0.000)
SetTypeNode128Slice 65.4µs × (1.00,1.00) 2.4µs × (0.99,1.03) -96.39% (p=0.000)
SetTypeNode130 2.11µs × (1.00,1.00) 0.12µs × (1.00,1.00) -94.18% (p=0.000)
SetTypeNode130Slice 66.3µs × (1.00,1.00) 2.4µs × (0.97,1.08) -96.34% (p=0.000)
SetTypeNode1024 16.0µs × (1.00,1.01) 0.5µs × (1.00,1.00) -96.65% (p=0.000)
SetTypeNode1024Slice 512µs × (1.00,1.00) 18µs × (0.98,1.04) -96.45% (p=0.000)
SetTypeNode124 uses a 124 data + 2 ptr = 126-word allocation.
Both Go 1.4 and this CL are using pointer bitmaps for this case,
so that's an overall 3x speedup for using pointer bitmaps.
SetTypeNode128 uses a 128 data + 2 ptr = 130-word allocation.
Both Go 1.4 and this CL are running the GC program for this case,
so that's an overall 17x speedup when using GC programs (and
I've seen >20x on other systems).
Comparing Go 1.4's SetTypeNode124 (pointer bitmap) against
this CL's SetTypeNode128 (GC program), the slow path in the
code in this CL is 2x faster than the fast path in Go 1.4.
The Go 1 benchmarks are basically unaffected compared to just before this CL.
Go 1 benchmarks, before this CL vs this CL:
name old mean new mean delta
BinaryTree17 5.87s × (0.97,1.04) 5.91s × (0.96,1.04) ~ (p=0.306)
Fannkuch11 4.38s × (1.00,1.00) 4.37s × (1.00,1.01) -0.22% (p=0.006)
FmtFprintfEmpty 90.7ns × (0.97,1.10) 89.3ns × (0.96,1.09) ~ (p=0.280)
FmtFprintfString 282ns × (0.98,1.04) 287ns × (0.98,1.07) +1.72% (p=0.039)
FmtFprintfInt 269ns × (0.99,1.03) 282ns × (0.97,1.04) +4.87% (p=0.000)
FmtFprintfIntInt 478ns × (0.99,1.02) 481ns × (0.99,1.02) +0.61% (p=0.048)
FmtFprintfPrefixedInt 399ns × (0.98,1.03) 400ns × (0.98,1.05) ~ (p=0.533)
FmtFprintfFloat 563ns × (0.99,1.01) 570ns × (1.00,1.01) +1.37% (p=0.000)
FmtManyArgs 1.89µs × (0.99,1.01) 1.92µs × (0.99,1.02) +1.88% (p=0.000)
GobDecode 15.2ms × (0.99,1.01) 15.2ms × (0.98,1.05) ~ (p=0.609)
GobEncode 11.6ms × (0.98,1.03) 11.9ms × (0.98,1.04) +2.17% (p=0.000)
Gzip 648ms × (0.99,1.01) 648ms × (1.00,1.01) ~ (p=0.835)
Gunzip 142ms × (1.00,1.00) 143ms × (1.00,1.01) ~ (p=0.169)
HTTPClientServer 90.5µs × (0.98,1.03) 91.5µs × (0.98,1.04) +1.04% (p=0.045)
JSONEncode 31.5ms × (0.98,1.03) 31.4ms × (0.98,1.03) ~ (p=0.549)
JSONDecode 111ms × (0.99,1.01) 107ms × (0.99,1.01) -3.21% (p=0.000)
Mandelbrot200 6.01ms × (1.00,1.00) 6.01ms × (1.00,1.00) ~ (p=0.878)
GoParse 6.54ms × (0.99,1.02) 6.61ms × (0.99,1.03) +1.08% (p=0.004)
RegexpMatchEasy0_32 160ns × (1.00,1.01) 161ns × (1.00,1.00) +0.40% (p=0.000)
RegexpMatchEasy0_1K 560ns × (0.99,1.01) 559ns × (0.99,1.01) ~ (p=0.088)
RegexpMatchEasy1_32 138ns × (0.99,1.01) 138ns × (1.00,1.00) ~ (p=0.380)
RegexpMatchEasy1_1K 877ns × (1.00,1.00) 878ns × (1.00,1.00) ~ (p=0.157)
RegexpMatchMedium_32 251ns × (0.99,1.00) 251ns × (1.00,1.01) +0.28% (p=0.021)
RegexpMatchMedium_1K 72.6µs × (1.00,1.00) 72.6µs × (1.00,1.00) ~ (p=0.539)
RegexpMatchHard_32 3.84µs × (1.00,1.00) 3.84µs × (1.00,1.00) ~ (p=0.378)
RegexpMatchHard_1K 117µs × (1.00,1.00) 117µs × (1.00,1.00) ~ (p=0.067)
Revcomp 904ms × (0.99,1.02) 904ms × (0.99,1.01) ~ (p=0.943)
Template 125ms × (0.99,1.02) 127ms × (0.99,1.01) +1.79% (p=0.000)
TimeParse 627ns × (0.99,1.01) 622ns × (0.99,1.01) -0.88% (p=0.000)
TimeFormat 655ns × (0.99,1.02) 655ns × (0.99,1.02) ~ (p=0.976)
For the record, Go 1 benchmarks, Go 1.4 vs this CL:
name old mean new mean delta
BinaryTree17 4.61s × (0.97,1.05) 5.91s × (0.98,1.03) +28.35% (p=0.000)
Fannkuch11 4.40s × (0.99,1.03) 4.41s × (0.99,1.01) ~ (p=0.212)
FmtFprintfEmpty 102ns × (0.99,1.01) 84ns × (0.99,1.02) -18.38% (p=0.000)
FmtFprintfString 302ns × (0.98,1.01) 303ns × (0.99,1.02) ~ (p=0.203)
FmtFprintfInt 313ns × (0.97,1.05) 270ns × (0.99,1.01) -13.69% (p=0.000)
FmtFprintfIntInt 524ns × (0.98,1.02) 477ns × (0.99,1.00) -8.87% (p=0.000)
FmtFprintfPrefixedInt 424ns × (0.98,1.02) 386ns × (0.99,1.01) -8.96% (p=0.000)
FmtFprintfFloat 652ns × (0.98,1.02) 594ns × (0.97,1.05) -8.97% (p=0.000)
FmtManyArgs 2.13µs × (0.99,1.02) 1.94µs × (0.99,1.01) -8.92% (p=0.000)
GobDecode 17.1ms × (0.99,1.02) 14.9ms × (0.98,1.03) -13.07% (p=0.000)
GobEncode 13.5ms × (0.98,1.03) 11.5ms × (0.98,1.03) -15.25% (p=0.000)
Gzip 656ms × (0.99,1.02) 647ms × (0.99,1.01) -1.29% (p=0.000)
Gunzip 143ms × (0.99,1.02) 144ms × (0.99,1.01) ~ (p=0.204)
HTTPClientServer 88.2µs × (0.98,1.02) 90.8µs × (0.98,1.01) +2.93% (p=0.000)
JSONEncode 32.2ms × (0.98,1.02) 30.9ms × (0.97,1.04) -4.06% (p=0.001)
JSONDecode 121ms × (0.98,1.02) 110ms × (0.98,1.05) -8.95% (p=0.000)
Mandelbrot200 6.06ms × (0.99,1.01) 6.11ms × (0.98,1.04) ~ (p=0.184)
GoParse 6.76ms × (0.97,1.04) 6.58ms × (0.98,1.05) -2.63% (p=0.003)
RegexpMatchEasy0_32 195ns × (1.00,1.01) 155ns × (0.99,1.01) -20.43% (p=0.000)
RegexpMatchEasy0_1K 479ns × (0.98,1.03) 535ns × (0.99,1.02) +11.59% (p=0.000)
RegexpMatchEasy1_32 169ns × (0.99,1.02) 131ns × (0.99,1.03) -22.44% (p=0.000)
RegexpMatchEasy1_1K 1.53µs × (0.99,1.01) 0.87µs × (0.99,1.02) -43.07% (p=0.000)
RegexpMatchMedium_32 334ns × (0.99,1.01) 242ns × (0.99,1.01) -27.53% (p=0.000)
RegexpMatchMedium_1K 125µs × (1.00,1.01) 72µs × (0.99,1.03) -42.53% (p=0.000)
RegexpMatchHard_32 6.03µs × (0.99,1.01) 3.79µs × (0.99,1.01) -37.12% (p=0.000)
RegexpMatchHard_1K 189µs × (0.99,1.02) 115µs × (0.99,1.01) -39.20% (p=0.000)
Revcomp 935ms × (0.96,1.03) 926ms × (0.98,1.02) ~ (p=0.083)
Template 146ms × (0.97,1.05) 119ms × (0.99,1.01) -18.37% (p=0.000)
TimeParse 660ns × (0.99,1.01) 624ns × (0.99,1.02) -5.43% (p=0.000)
TimeFormat 670ns × (0.98,1.02) 710ns × (1.00,1.01) +5.97% (p=0.000)
This CL is a bit larger than I would like, but the compiler, linker, runtime,
and package reflect all need to be in sync about the format of these programs,
so there is no easy way to split this into independent changes (at least
while keeping the build working at each change).
Fixes #9625.
Fixes #10524.
Change-Id: I9e3e20d6097099d0f8532d1cb5b1af528804989a
Reviewed-on: https://go-review.googlesource.com/9888
Reviewed-by: Austin Clements <austin@google.com>
Run-TryBot: Russ Cox <rsc@golang.org>
2015-05-07 23:43:18 -06:00
|
|
|
// gcdata stores the GC type data for the garbage collector.
|
|
|
|
// If the KindGCProg bit is set in kind, gcdata is a GC program.
|
|
|
|
// Otherwise it is a ptrmask bitmap. See mbitmap.go for details.
|
2016-04-07 14:29:16 -06:00
|
|
|
gcdata *byte
|
|
|
|
str nameOff
|
|
|
|
_ int32
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *_type) string() string {
|
|
|
|
s := t.nameOff(t.str).name()
|
|
|
|
if t.tflag&tflagExtraStar != 0 {
|
|
|
|
return s[1:]
|
|
|
|
}
|
|
|
|
return s
|
2016-02-20 20:54:15 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *_type) uncommon() *uncommontype {
|
|
|
|
if t.tflag&tflagUncommon == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
switch t.kind & kindMask {
|
|
|
|
case kindStruct:
|
|
|
|
type u struct {
|
|
|
|
structtype
|
|
|
|
u uncommontype
|
|
|
|
}
|
|
|
|
return &(*u)(unsafe.Pointer(t)).u
|
|
|
|
case kindPtr:
|
|
|
|
type u struct {
|
|
|
|
ptrtype
|
|
|
|
u uncommontype
|
|
|
|
}
|
|
|
|
return &(*u)(unsafe.Pointer(t)).u
|
|
|
|
case kindFunc:
|
|
|
|
type u struct {
|
|
|
|
functype
|
|
|
|
u uncommontype
|
|
|
|
}
|
|
|
|
return &(*u)(unsafe.Pointer(t)).u
|
|
|
|
case kindSlice:
|
|
|
|
type u struct {
|
|
|
|
slicetype
|
|
|
|
u uncommontype
|
|
|
|
}
|
|
|
|
return &(*u)(unsafe.Pointer(t)).u
|
|
|
|
case kindArray:
|
|
|
|
type u struct {
|
|
|
|
arraytype
|
|
|
|
u uncommontype
|
|
|
|
}
|
|
|
|
return &(*u)(unsafe.Pointer(t)).u
|
|
|
|
case kindChan:
|
|
|
|
type u struct {
|
|
|
|
chantype
|
|
|
|
u uncommontype
|
|
|
|
}
|
|
|
|
return &(*u)(unsafe.Pointer(t)).u
|
|
|
|
case kindMap:
|
|
|
|
type u struct {
|
|
|
|
maptype
|
|
|
|
u uncommontype
|
|
|
|
}
|
|
|
|
return &(*u)(unsafe.Pointer(t)).u
|
|
|
|
case kindInterface:
|
|
|
|
type u struct {
|
|
|
|
interfacetype
|
|
|
|
u uncommontype
|
|
|
|
}
|
|
|
|
return &(*u)(unsafe.Pointer(t)).u
|
|
|
|
default:
|
|
|
|
type u struct {
|
|
|
|
_type
|
|
|
|
u uncommontype
|
|
|
|
}
|
|
|
|
return &(*u)(unsafe.Pointer(t)).u
|
|
|
|
}
|
2014-11-11 15:05:19 -07:00
|
|
|
}
|
|
|
|
|
2016-02-17 11:03:21 -07:00
|
|
|
func hasPrefix(s, prefix string) bool {
|
|
|
|
return len(s) >= len(prefix) && s[:len(prefix)] == prefix
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *_type) name() string {
|
2016-04-07 14:29:16 -06:00
|
|
|
s := t.string()
|
|
|
|
if hasPrefix(s, "map[") {
|
2016-02-17 11:03:21 -07:00
|
|
|
return ""
|
|
|
|
}
|
2016-04-07 14:29:16 -06:00
|
|
|
if hasPrefix(s, "struct {") {
|
2016-02-17 11:03:21 -07:00
|
|
|
return ""
|
|
|
|
}
|
2016-04-07 14:29:16 -06:00
|
|
|
if hasPrefix(s, "chan ") {
|
2016-02-17 11:03:21 -07:00
|
|
|
return ""
|
|
|
|
}
|
2016-04-07 14:29:16 -06:00
|
|
|
if hasPrefix(s, "chan<-") {
|
2016-03-04 12:53:26 -07:00
|
|
|
return ""
|
|
|
|
}
|
2016-04-07 14:29:16 -06:00
|
|
|
if hasPrefix(s, "func(") {
|
2016-02-17 11:03:21 -07:00
|
|
|
return ""
|
|
|
|
}
|
2016-04-07 14:29:16 -06:00
|
|
|
switch s[0] {
|
2016-03-04 12:53:26 -07:00
|
|
|
case '[', '*', '<':
|
2016-02-17 11:03:21 -07:00
|
|
|
return ""
|
|
|
|
}
|
2016-04-07 14:29:16 -06:00
|
|
|
i := len(s) - 1
|
2016-02-17 11:03:21 -07:00
|
|
|
for i >= 0 {
|
2016-04-07 14:29:16 -06:00
|
|
|
if s[i] == '.' {
|
2016-02-17 11:03:21 -07:00
|
|
|
break
|
|
|
|
}
|
|
|
|
i--
|
|
|
|
}
|
2016-04-07 14:29:16 -06:00
|
|
|
return s[i+1:]
|
2016-02-17 11:03:21 -07:00
|
|
|
}
|
|
|
|
|
cmd/compile, etc: store method tables as offsets
This CL introduces the typeOff type and a lookup method of the same
name that can turn a typeOff offset into an *rtype.
In a typical Go binary (built with buildmode=exe, pie, c-archive, or
c-shared), there is one moduledata and all typeOff values are offsets
relative to firstmoduledata.types. This makes computing the pointer
cheap in typical programs.
With buildmode=shared (and one day, buildmode=plugin) there are
multiple modules whose relative offset is determined at runtime.
We identify a type in the general case by the pair of the original
*rtype that references it and its typeOff value. We determine
the module from the original pointer, and then use the typeOff from
there to compute the final *rtype.
To ensure there is only one *rtype representing each type, the
runtime initializes a typemap for each module, using any identical
type from an earlier module when resolving that offset. This means
that types computed from an offset match the type mapped by the
pointer dynamic relocations.
A series of followup CLs will replace other *rtype values with typeOff
(and name/*string with nameOff).
For types created at runtime by reflect, type offsets are treated as
global IDs and reference into a reflect offset map kept by the runtime.
darwin/amd64:
cmd/go: -57KB (0.6%)
jujud: -557KB (0.8%)
linux/amd64 PIE:
cmd/go: -361KB (3.0%)
jujud: -3.5MB (4.2%)
For #6853.
Change-Id: Icf096fd884a0a0cb9f280f46f7a26c70a9006c96
Reviewed-on: https://go-review.googlesource.com/21285
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: David Crawshaw <crawshaw@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2016-03-28 08:32:27 -06:00
|
|
|
// reflectOffs holds type offsets defined at run time by the reflect package.
|
|
|
|
//
|
|
|
|
// When a type is defined at run time, its *rtype data lives on the heap.
|
|
|
|
// There are a wide range of possible addresses the heap may use, that
|
|
|
|
// may not be representable as a 32-bit offset. Moreover the GC may
|
|
|
|
// one day start moving heap memory, in which case there is no stable
|
|
|
|
// offset that can be defined.
|
|
|
|
//
|
|
|
|
// To provide stable offsets, we add pin *rtype objects in a global map
|
|
|
|
// and treat the offset as an identifier. We use negative offsets that
|
|
|
|
// do not overlap with any compile-time module offsets.
|
|
|
|
//
|
|
|
|
// Entries are created by reflect.addReflectOff.
|
|
|
|
var reflectOffs struct {
|
|
|
|
lock mutex
|
|
|
|
next int32
|
|
|
|
m map[int32]unsafe.Pointer
|
|
|
|
minv map[unsafe.Pointer]int32
|
|
|
|
}
|
|
|
|
|
2016-03-31 08:02:10 -06:00
|
|
|
func resolveNameOff(ptrInModule unsafe.Pointer, off nameOff) name {
|
|
|
|
if off == 0 {
|
|
|
|
return name{}
|
|
|
|
}
|
|
|
|
base := uintptr(ptrInModule)
|
|
|
|
var md *moduledata
|
|
|
|
for next := &firstmoduledata; next != nil; next = next.next {
|
|
|
|
if base >= next.types && base < next.etypes {
|
|
|
|
md = next
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if md == nil {
|
2016-03-28 19:51:10 -06:00
|
|
|
lock(&reflectOffs.lock)
|
|
|
|
res, found := reflectOffs.m[int32(off)]
|
|
|
|
unlock(&reflectOffs.lock)
|
|
|
|
if !found {
|
|
|
|
println("runtime: nameOff", hex(off), "base", hex(base), "not in ranges:")
|
|
|
|
for next := &firstmoduledata; next != nil; next = next.next {
|
|
|
|
println("\ttypes", hex(next.types), "etypes", hex(next.etypes))
|
|
|
|
}
|
|
|
|
throw("runtime: name offset base pointer out of range")
|
2016-03-31 08:02:10 -06:00
|
|
|
}
|
2016-03-28 19:51:10 -06:00
|
|
|
return name{(*byte)(res)}
|
2016-03-31 08:02:10 -06:00
|
|
|
}
|
|
|
|
res := md.types + uintptr(off)
|
|
|
|
if res > md.etypes {
|
|
|
|
println("runtime: nameOff", hex(off), "out of range", hex(md.types), "-", hex(md.etypes))
|
|
|
|
throw("runtime: name offset out of range")
|
|
|
|
}
|
|
|
|
return name{(*byte)(unsafe.Pointer(res))}
|
|
|
|
}
|
|
|
|
|
2016-03-28 19:51:10 -06:00
|
|
|
func (t *_type) nameOff(off nameOff) name {
|
|
|
|
return resolveNameOff(unsafe.Pointer(t), off)
|
|
|
|
}
|
|
|
|
|
cmd/compile, etc: store method tables as offsets
This CL introduces the typeOff type and a lookup method of the same
name that can turn a typeOff offset into an *rtype.
In a typical Go binary (built with buildmode=exe, pie, c-archive, or
c-shared), there is one moduledata and all typeOff values are offsets
relative to firstmoduledata.types. This makes computing the pointer
cheap in typical programs.
With buildmode=shared (and one day, buildmode=plugin) there are
multiple modules whose relative offset is determined at runtime.
We identify a type in the general case by the pair of the original
*rtype that references it and its typeOff value. We determine
the module from the original pointer, and then use the typeOff from
there to compute the final *rtype.
To ensure there is only one *rtype representing each type, the
runtime initializes a typemap for each module, using any identical
type from an earlier module when resolving that offset. This means
that types computed from an offset match the type mapped by the
pointer dynamic relocations.
A series of followup CLs will replace other *rtype values with typeOff
(and name/*string with nameOff).
For types created at runtime by reflect, type offsets are treated as
global IDs and reference into a reflect offset map kept by the runtime.
darwin/amd64:
cmd/go: -57KB (0.6%)
jujud: -557KB (0.8%)
linux/amd64 PIE:
cmd/go: -361KB (3.0%)
jujud: -3.5MB (4.2%)
For #6853.
Change-Id: Icf096fd884a0a0cb9f280f46f7a26c70a9006c96
Reviewed-on: https://go-review.googlesource.com/21285
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: David Crawshaw <crawshaw@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2016-03-28 08:32:27 -06:00
|
|
|
func (t *_type) typeOff(off typeOff) *_type {
|
|
|
|
if off == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
base := uintptr(unsafe.Pointer(t))
|
|
|
|
var md *moduledata
|
|
|
|
for next := &firstmoduledata; next != nil; next = next.next {
|
|
|
|
if base >= next.types && base < next.etypes {
|
|
|
|
md = next
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if md == nil {
|
|
|
|
lock(&reflectOffs.lock)
|
|
|
|
res := reflectOffs.m[int32(off)]
|
|
|
|
unlock(&reflectOffs.lock)
|
|
|
|
if res == nil {
|
|
|
|
println("runtime: typeOff", hex(off), "base", hex(base), "not in ranges:")
|
|
|
|
for next := &firstmoduledata; next != nil; next = next.next {
|
|
|
|
println("\ttypes", hex(next.types), "etypes", hex(next.etypes))
|
|
|
|
}
|
|
|
|
throw("runtime: type offset base pointer out of range")
|
|
|
|
}
|
|
|
|
return (*_type)(res)
|
|
|
|
}
|
|
|
|
if t := md.typemap[off]; t != nil {
|
|
|
|
return t
|
|
|
|
}
|
|
|
|
res := md.types + uintptr(off)
|
|
|
|
if res > md.etypes {
|
|
|
|
println("runtime: typeOff", hex(off), "out of range", hex(md.types), "-", hex(md.etypes))
|
|
|
|
throw("runtime: type offset out of range")
|
|
|
|
}
|
|
|
|
return (*_type)(unsafe.Pointer(res))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *_type) textOff(off textOff) unsafe.Pointer {
|
|
|
|
base := uintptr(unsafe.Pointer(t))
|
|
|
|
var md *moduledata
|
|
|
|
for next := &firstmoduledata; next != nil; next = next.next {
|
|
|
|
if base >= next.types && base < next.etypes {
|
|
|
|
md = next
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if md == nil {
|
|
|
|
lock(&reflectOffs.lock)
|
|
|
|
res := reflectOffs.m[int32(off)]
|
|
|
|
unlock(&reflectOffs.lock)
|
|
|
|
if res == nil {
|
|
|
|
println("runtime: textOff", hex(off), "base", hex(base), "not in ranges:")
|
|
|
|
for next := &firstmoduledata; next != nil; next = next.next {
|
|
|
|
println("\ttypes", hex(next.types), "etypes", hex(next.etypes))
|
|
|
|
}
|
|
|
|
throw("runtime: text offset base pointer out of range")
|
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
res := md.text + uintptr(off)
|
|
|
|
if res > md.etext {
|
|
|
|
println("runtime: textOff", hex(off), "out of range", hex(md.text), "-", hex(md.etext))
|
|
|
|
throw("runtime: text offset out of range")
|
|
|
|
}
|
|
|
|
return unsafe.Pointer(res)
|
|
|
|
}
|
|
|
|
|
2016-02-23 09:31:13 -07:00
|
|
|
func (t *functype) in() []*_type {
|
|
|
|
// See funcType in reflect/type.go for details on data layout.
|
|
|
|
uadd := uintptr(unsafe.Sizeof(functype{}))
|
|
|
|
if t.typ.tflag&tflagUncommon != 0 {
|
|
|
|
uadd += unsafe.Sizeof(uncommontype{})
|
|
|
|
}
|
|
|
|
return (*[1 << 20]*_type)(add(unsafe.Pointer(t), uadd))[:t.inCount]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *functype) out() []*_type {
|
|
|
|
// See funcType in reflect/type.go for details on data layout.
|
|
|
|
uadd := uintptr(unsafe.Sizeof(functype{}))
|
|
|
|
if t.typ.tflag&tflagUncommon != 0 {
|
|
|
|
uadd += unsafe.Sizeof(uncommontype{})
|
|
|
|
}
|
|
|
|
outCount := t.outCount & (1<<15 - 1)
|
|
|
|
return (*[1 << 20]*_type)(add(unsafe.Pointer(t), uadd))[t.inCount : t.inCount+outCount]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *functype) dotdotdot() bool {
|
|
|
|
return t.outCount&(1<<15) != 0
|
|
|
|
}
|
|
|
|
|
2016-03-31 08:02:10 -06:00
|
|
|
type nameOff int32
|
cmd/compile, etc: store method tables as offsets
This CL introduces the typeOff type and a lookup method of the same
name that can turn a typeOff offset into an *rtype.
In a typical Go binary (built with buildmode=exe, pie, c-archive, or
c-shared), there is one moduledata and all typeOff values are offsets
relative to firstmoduledata.types. This makes computing the pointer
cheap in typical programs.
With buildmode=shared (and one day, buildmode=plugin) there are
multiple modules whose relative offset is determined at runtime.
We identify a type in the general case by the pair of the original
*rtype that references it and its typeOff value. We determine
the module from the original pointer, and then use the typeOff from
there to compute the final *rtype.
To ensure there is only one *rtype representing each type, the
runtime initializes a typemap for each module, using any identical
type from an earlier module when resolving that offset. This means
that types computed from an offset match the type mapped by the
pointer dynamic relocations.
A series of followup CLs will replace other *rtype values with typeOff
(and name/*string with nameOff).
For types created at runtime by reflect, type offsets are treated as
global IDs and reference into a reflect offset map kept by the runtime.
darwin/amd64:
cmd/go: -57KB (0.6%)
jujud: -557KB (0.8%)
linux/amd64 PIE:
cmd/go: -361KB (3.0%)
jujud: -3.5MB (4.2%)
For #6853.
Change-Id: Icf096fd884a0a0cb9f280f46f7a26c70a9006c96
Reviewed-on: https://go-review.googlesource.com/21285
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: David Crawshaw <crawshaw@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2016-03-28 08:32:27 -06:00
|
|
|
type typeOff int32
|
|
|
|
type textOff int32
|
|
|
|
|
2014-11-11 15:05:19 -07:00
|
|
|
type method struct {
|
2016-03-28 19:51:10 -06:00
|
|
|
name nameOff
|
cmd/compile, etc: store method tables as offsets
This CL introduces the typeOff type and a lookup method of the same
name that can turn a typeOff offset into an *rtype.
In a typical Go binary (built with buildmode=exe, pie, c-archive, or
c-shared), there is one moduledata and all typeOff values are offsets
relative to firstmoduledata.types. This makes computing the pointer
cheap in typical programs.
With buildmode=shared (and one day, buildmode=plugin) there are
multiple modules whose relative offset is determined at runtime.
We identify a type in the general case by the pair of the original
*rtype that references it and its typeOff value. We determine
the module from the original pointer, and then use the typeOff from
there to compute the final *rtype.
To ensure there is only one *rtype representing each type, the
runtime initializes a typemap for each module, using any identical
type from an earlier module when resolving that offset. This means
that types computed from an offset match the type mapped by the
pointer dynamic relocations.
A series of followup CLs will replace other *rtype values with typeOff
(and name/*string with nameOff).
For types created at runtime by reflect, type offsets are treated as
global IDs and reference into a reflect offset map kept by the runtime.
darwin/amd64:
cmd/go: -57KB (0.6%)
jujud: -557KB (0.8%)
linux/amd64 PIE:
cmd/go: -361KB (3.0%)
jujud: -3.5MB (4.2%)
For #6853.
Change-Id: Icf096fd884a0a0cb9f280f46f7a26c70a9006c96
Reviewed-on: https://go-review.googlesource.com/21285
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: David Crawshaw <crawshaw@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2016-03-28 08:32:27 -06:00
|
|
|
mtyp typeOff
|
|
|
|
ifn textOff
|
|
|
|
tfn textOff
|
2014-11-11 15:05:19 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
type uncommontype struct {
|
2016-03-31 08:02:10 -06:00
|
|
|
pkgpath name
|
cmd/compile, etc: store method tables as offsets
This CL introduces the typeOff type and a lookup method of the same
name that can turn a typeOff offset into an *rtype.
In a typical Go binary (built with buildmode=exe, pie, c-archive, or
c-shared), there is one moduledata and all typeOff values are offsets
relative to firstmoduledata.types. This makes computing the pointer
cheap in typical programs.
With buildmode=shared (and one day, buildmode=plugin) there are
multiple modules whose relative offset is determined at runtime.
We identify a type in the general case by the pair of the original
*rtype that references it and its typeOff value. We determine
the module from the original pointer, and then use the typeOff from
there to compute the final *rtype.
To ensure there is only one *rtype representing each type, the
runtime initializes a typemap for each module, using any identical
type from an earlier module when resolving that offset. This means
that types computed from an offset match the type mapped by the
pointer dynamic relocations.
A series of followup CLs will replace other *rtype values with typeOff
(and name/*string with nameOff).
For types created at runtime by reflect, type offsets are treated as
global IDs and reference into a reflect offset map kept by the runtime.
darwin/amd64:
cmd/go: -57KB (0.6%)
jujud: -557KB (0.8%)
linux/amd64 PIE:
cmd/go: -361KB (3.0%)
jujud: -3.5MB (4.2%)
For #6853.
Change-Id: Icf096fd884a0a0cb9f280f46f7a26c70a9006c96
Reviewed-on: https://go-review.googlesource.com/21285
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: David Crawshaw <crawshaw@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2016-03-28 08:32:27 -06:00
|
|
|
mcount uint16 // number of methods
|
|
|
|
moff uint16 // offset from this uncommontype to [mcount]method
|
2014-11-11 15:05:19 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
type imethod struct {
|
2016-03-28 19:51:10 -06:00
|
|
|
name nameOff
|
|
|
|
ityp typeOff
|
2014-11-11 15:05:19 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
type interfacetype struct {
|
2016-03-21 11:21:55 -06:00
|
|
|
typ _type
|
2016-03-31 08:02:10 -06:00
|
|
|
pkgpath name
|
2016-03-21 11:21:55 -06:00
|
|
|
mhdr []imethod
|
2014-11-11 15:05:19 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
type maptype struct {
|
|
|
|
typ _type
|
|
|
|
key *_type
|
|
|
|
elem *_type
|
2015-01-21 23:31:49 -07:00
|
|
|
bucket *_type // internal type representing a hash bucket
|
2016-02-23 00:46:01 -07:00
|
|
|
hmap *_type // internal type representing a hmap
|
2014-11-11 15:05:19 -07:00
|
|
|
keysize uint8 // size of key slot
|
|
|
|
indirectkey bool // store ptr to key instead of key itself
|
|
|
|
valuesize uint8 // size of value slot
|
|
|
|
indirectvalue bool // store ptr to value instead of value itself
|
|
|
|
bucketsize uint16 // size of bucket
|
2014-12-12 14:45:19 -07:00
|
|
|
reflexivekey bool // true if k==k for all keys
|
2015-06-08 09:42:28 -06:00
|
|
|
needkeyupdate bool // true if we need to update key on an overwrite
|
2014-11-11 15:05:19 -07:00
|
|
|
}
|
|
|
|
|
2015-10-16 16:26:00 -06:00
|
|
|
type arraytype struct {
|
|
|
|
typ _type
|
|
|
|
elem *_type
|
|
|
|
slice *_type
|
|
|
|
len uintptr
|
|
|
|
}
|
|
|
|
|
2014-11-11 15:05:19 -07:00
|
|
|
type chantype struct {
|
|
|
|
typ _type
|
|
|
|
elem *_type
|
|
|
|
dir uintptr
|
|
|
|
}
|
|
|
|
|
|
|
|
type slicetype struct {
|
|
|
|
typ _type
|
|
|
|
elem *_type
|
|
|
|
}
|
|
|
|
|
|
|
|
type functype struct {
|
2016-02-23 09:31:13 -07:00
|
|
|
typ _type
|
|
|
|
inCount uint16
|
|
|
|
outCount uint16
|
2014-11-11 15:05:19 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
type ptrtype struct {
|
|
|
|
typ _type
|
|
|
|
elem *_type
|
|
|
|
}
|
2015-10-16 16:26:00 -06:00
|
|
|
|
|
|
|
type structfield struct {
|
2016-03-21 11:21:55 -06:00
|
|
|
name name
|
|
|
|
typ *_type
|
|
|
|
offset uintptr
|
2015-10-16 16:26:00 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
type structtype struct {
|
2016-03-21 11:21:55 -06:00
|
|
|
typ _type
|
2016-03-31 08:02:10 -06:00
|
|
|
pkgPath name
|
2016-03-21 11:21:55 -06:00
|
|
|
fields []structfield
|
|
|
|
}
|
|
|
|
|
|
|
|
// name is an encoded type name with optional extra data.
|
|
|
|
// See reflect/type.go for details.
|
|
|
|
type name struct {
|
|
|
|
bytes *byte
|
|
|
|
}
|
|
|
|
|
2016-03-28 19:51:10 -06:00
|
|
|
func (n name) data(off int) *byte {
|
2016-03-21 11:21:55 -06:00
|
|
|
return (*byte)(add(unsafe.Pointer(n.bytes), uintptr(off)))
|
|
|
|
}
|
|
|
|
|
2016-03-28 19:51:10 -06:00
|
|
|
func (n name) isExported() bool {
|
2016-03-21 11:21:55 -06:00
|
|
|
return (*n.bytes)&(1<<0) != 0
|
|
|
|
}
|
|
|
|
|
2016-03-28 19:51:10 -06:00
|
|
|
func (n name) nameLen() int {
|
2016-03-21 11:21:55 -06:00
|
|
|
return int(uint16(*n.data(1))<<8 | uint16(*n.data(2)))
|
|
|
|
}
|
|
|
|
|
2016-03-28 19:51:10 -06:00
|
|
|
func (n name) tagLen() int {
|
2016-03-21 11:21:55 -06:00
|
|
|
if *n.data(0)&(1<<1) == 0 {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
off := 3 + n.nameLen()
|
|
|
|
return int(uint16(*n.data(off))<<8 | uint16(*n.data(off + 1)))
|
|
|
|
}
|
|
|
|
|
2016-03-28 19:51:10 -06:00
|
|
|
func (n name) name() (s string) {
|
2016-03-31 08:02:10 -06:00
|
|
|
if n.bytes == nil {
|
|
|
|
return ""
|
|
|
|
}
|
2016-03-21 11:21:55 -06:00
|
|
|
nl := n.nameLen()
|
|
|
|
if nl == 0 {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
hdr := (*stringStruct)(unsafe.Pointer(&s))
|
|
|
|
hdr.str = unsafe.Pointer(n.data(3))
|
|
|
|
hdr.len = nl
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
2016-03-28 19:51:10 -06:00
|
|
|
func (n name) tag() (s string) {
|
cmd/compile, etc: store method tables as offsets
This CL introduces the typeOff type and a lookup method of the same
name that can turn a typeOff offset into an *rtype.
In a typical Go binary (built with buildmode=exe, pie, c-archive, or
c-shared), there is one moduledata and all typeOff values are offsets
relative to firstmoduledata.types. This makes computing the pointer
cheap in typical programs.
With buildmode=shared (and one day, buildmode=plugin) there are
multiple modules whose relative offset is determined at runtime.
We identify a type in the general case by the pair of the original
*rtype that references it and its typeOff value. We determine
the module from the original pointer, and then use the typeOff from
there to compute the final *rtype.
To ensure there is only one *rtype representing each type, the
runtime initializes a typemap for each module, using any identical
type from an earlier module when resolving that offset. This means
that types computed from an offset match the type mapped by the
pointer dynamic relocations.
A series of followup CLs will replace other *rtype values with typeOff
(and name/*string with nameOff).
For types created at runtime by reflect, type offsets are treated as
global IDs and reference into a reflect offset map kept by the runtime.
darwin/amd64:
cmd/go: -57KB (0.6%)
jujud: -557KB (0.8%)
linux/amd64 PIE:
cmd/go: -361KB (3.0%)
jujud: -3.5MB (4.2%)
For #6853.
Change-Id: Icf096fd884a0a0cb9f280f46f7a26c70a9006c96
Reviewed-on: https://go-review.googlesource.com/21285
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: David Crawshaw <crawshaw@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2016-03-28 08:32:27 -06:00
|
|
|
tl := n.tagLen()
|
|
|
|
if tl == 0 {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
nl := n.nameLen()
|
|
|
|
hdr := (*stringStruct)(unsafe.Pointer(&s))
|
|
|
|
hdr.str = unsafe.Pointer(n.data(3 + nl + 2))
|
|
|
|
hdr.len = tl
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
2016-03-28 19:51:10 -06:00
|
|
|
func (n name) pkgPath() string {
|
2016-03-31 08:02:10 -06:00
|
|
|
if n.bytes == nil || *n.data(0)&(1<<2) == 0 {
|
|
|
|
return ""
|
2016-03-21 11:21:55 -06:00
|
|
|
}
|
|
|
|
off := 3 + n.nameLen()
|
|
|
|
if tl := n.tagLen(); tl > 0 {
|
|
|
|
off += 2 + tl
|
|
|
|
}
|
2016-03-31 08:02:10 -06:00
|
|
|
var nameOff nameOff
|
|
|
|
copy((*[4]byte)(unsafe.Pointer(&nameOff))[:], (*[4]byte)(unsafe.Pointer(n.data(off)))[:])
|
|
|
|
pkgPathName := resolveNameOff(unsafe.Pointer(n.bytes), nameOff)
|
|
|
|
return pkgPathName.name()
|
2015-10-16 16:26:00 -06:00
|
|
|
}
|
cmd/compile, etc: store method tables as offsets
This CL introduces the typeOff type and a lookup method of the same
name that can turn a typeOff offset into an *rtype.
In a typical Go binary (built with buildmode=exe, pie, c-archive, or
c-shared), there is one moduledata and all typeOff values are offsets
relative to firstmoduledata.types. This makes computing the pointer
cheap in typical programs.
With buildmode=shared (and one day, buildmode=plugin) there are
multiple modules whose relative offset is determined at runtime.
We identify a type in the general case by the pair of the original
*rtype that references it and its typeOff value. We determine
the module from the original pointer, and then use the typeOff from
there to compute the final *rtype.
To ensure there is only one *rtype representing each type, the
runtime initializes a typemap for each module, using any identical
type from an earlier module when resolving that offset. This means
that types computed from an offset match the type mapped by the
pointer dynamic relocations.
A series of followup CLs will replace other *rtype values with typeOff
(and name/*string with nameOff).
For types created at runtime by reflect, type offsets are treated as
global IDs and reference into a reflect offset map kept by the runtime.
darwin/amd64:
cmd/go: -57KB (0.6%)
jujud: -557KB (0.8%)
linux/amd64 PIE:
cmd/go: -361KB (3.0%)
jujud: -3.5MB (4.2%)
For #6853.
Change-Id: Icf096fd884a0a0cb9f280f46f7a26c70a9006c96
Reviewed-on: https://go-review.googlesource.com/21285
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: David Crawshaw <crawshaw@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2016-03-28 08:32:27 -06:00
|
|
|
|
|
|
|
// typelinksinit scans the types from extra modules and builds the
|
|
|
|
// moduledata typemap used to de-duplicate type pointers.
|
|
|
|
func typelinksinit() {
|
|
|
|
if firstmoduledata.next == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
typehash := make(map[uint32][]*_type)
|
|
|
|
|
|
|
|
modules := []*moduledata{}
|
|
|
|
for md := &firstmoduledata; md != nil; md = md.next {
|
|
|
|
modules = append(modules, md)
|
|
|
|
}
|
|
|
|
prev, modules := modules[len(modules)-1], modules[:len(modules)-1]
|
|
|
|
for len(modules) > 0 {
|
|
|
|
// Collect types from the previous module into typehash.
|
|
|
|
collect:
|
|
|
|
for _, tl := range prev.typelinks {
|
|
|
|
var t *_type
|
|
|
|
if prev.typemap == nil {
|
|
|
|
t = (*_type)(unsafe.Pointer(prev.types + uintptr(tl)))
|
|
|
|
} else {
|
|
|
|
t = prev.typemap[typeOff(tl)]
|
|
|
|
}
|
|
|
|
// Add to typehash if not seen before.
|
|
|
|
tlist := typehash[t.hash]
|
|
|
|
for _, tcur := range tlist {
|
|
|
|
if tcur == t {
|
|
|
|
continue collect
|
|
|
|
}
|
|
|
|
}
|
|
|
|
typehash[t.hash] = append(tlist, t)
|
|
|
|
}
|
|
|
|
|
|
|
|
// If any of this module's typelinks match a type from a
|
|
|
|
// prior module, prefer that prior type by adding the offset
|
|
|
|
// to this module's typemap.
|
|
|
|
md := modules[len(modules)-1]
|
|
|
|
md.typemap = make(map[typeOff]*_type, len(md.typelinks))
|
|
|
|
for _, tl := range md.typelinks {
|
|
|
|
t := (*_type)(unsafe.Pointer(md.types + uintptr(tl)))
|
|
|
|
for _, candidate := range typehash[t.hash] {
|
|
|
|
if typesEqual(t, candidate) {
|
|
|
|
t = candidate
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
md.typemap[typeOff(tl)] = t
|
|
|
|
}
|
|
|
|
|
|
|
|
prev, modules = md, modules[:len(modules)-1]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// typesEqual reports whether two types are equal.
|
|
|
|
//
|
|
|
|
// Everywhere in the runtime and reflect packages, it is assumed that
|
|
|
|
// there is exactly one *_type per Go type, so that pointer equality
|
|
|
|
// can be used to test if types are equal. There is one place that
|
|
|
|
// breaks this assumption: buildmode=shared. In this case a type can
|
|
|
|
// appear as two different pieces of memory. This is hidden from the
|
|
|
|
// runtime and reflect package by the per-module typemap built in
|
|
|
|
// typelinksinit. It uses typesEqual to map types from later modules
|
|
|
|
// back into earlier ones.
|
|
|
|
//
|
|
|
|
// Only typelinksinit needs this function.
|
|
|
|
func typesEqual(t, v *_type) bool {
|
|
|
|
if t == v {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
kind := t.kind & kindMask
|
|
|
|
if kind != v.kind&kindMask {
|
|
|
|
return false
|
|
|
|
}
|
2016-04-07 14:29:16 -06:00
|
|
|
if t.string() != v.string() {
|
cmd/compile, etc: store method tables as offsets
This CL introduces the typeOff type and a lookup method of the same
name that can turn a typeOff offset into an *rtype.
In a typical Go binary (built with buildmode=exe, pie, c-archive, or
c-shared), there is one moduledata and all typeOff values are offsets
relative to firstmoduledata.types. This makes computing the pointer
cheap in typical programs.
With buildmode=shared (and one day, buildmode=plugin) there are
multiple modules whose relative offset is determined at runtime.
We identify a type in the general case by the pair of the original
*rtype that references it and its typeOff value. We determine
the module from the original pointer, and then use the typeOff from
there to compute the final *rtype.
To ensure there is only one *rtype representing each type, the
runtime initializes a typemap for each module, using any identical
type from an earlier module when resolving that offset. This means
that types computed from an offset match the type mapped by the
pointer dynamic relocations.
A series of followup CLs will replace other *rtype values with typeOff
(and name/*string with nameOff).
For types created at runtime by reflect, type offsets are treated as
global IDs and reference into a reflect offset map kept by the runtime.
darwin/amd64:
cmd/go: -57KB (0.6%)
jujud: -557KB (0.8%)
linux/amd64 PIE:
cmd/go: -361KB (3.0%)
jujud: -3.5MB (4.2%)
For #6853.
Change-Id: Icf096fd884a0a0cb9f280f46f7a26c70a9006c96
Reviewed-on: https://go-review.googlesource.com/21285
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: David Crawshaw <crawshaw@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2016-03-28 08:32:27 -06:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
ut := t.uncommon()
|
|
|
|
uv := v.uncommon()
|
|
|
|
if ut != nil || uv != nil {
|
|
|
|
if ut == nil || uv == nil {
|
|
|
|
return false
|
|
|
|
}
|
2016-03-31 08:02:10 -06:00
|
|
|
if ut.pkgpath.name() != uv.pkgpath.name() {
|
cmd/compile, etc: store method tables as offsets
This CL introduces the typeOff type and a lookup method of the same
name that can turn a typeOff offset into an *rtype.
In a typical Go binary (built with buildmode=exe, pie, c-archive, or
c-shared), there is one moduledata and all typeOff values are offsets
relative to firstmoduledata.types. This makes computing the pointer
cheap in typical programs.
With buildmode=shared (and one day, buildmode=plugin) there are
multiple modules whose relative offset is determined at runtime.
We identify a type in the general case by the pair of the original
*rtype that references it and its typeOff value. We determine
the module from the original pointer, and then use the typeOff from
there to compute the final *rtype.
To ensure there is only one *rtype representing each type, the
runtime initializes a typemap for each module, using any identical
type from an earlier module when resolving that offset. This means
that types computed from an offset match the type mapped by the
pointer dynamic relocations.
A series of followup CLs will replace other *rtype values with typeOff
(and name/*string with nameOff).
For types created at runtime by reflect, type offsets are treated as
global IDs and reference into a reflect offset map kept by the runtime.
darwin/amd64:
cmd/go: -57KB (0.6%)
jujud: -557KB (0.8%)
linux/amd64 PIE:
cmd/go: -361KB (3.0%)
jujud: -3.5MB (4.2%)
For #6853.
Change-Id: Icf096fd884a0a0cb9f280f46f7a26c70a9006c96
Reviewed-on: https://go-review.googlesource.com/21285
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: David Crawshaw <crawshaw@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2016-03-28 08:32:27 -06:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if kindBool <= kind && kind <= kindComplex128 {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
switch kind {
|
|
|
|
case kindString, kindUnsafePointer:
|
|
|
|
return true
|
|
|
|
case kindArray:
|
|
|
|
at := (*arraytype)(unsafe.Pointer(t))
|
|
|
|
av := (*arraytype)(unsafe.Pointer(v))
|
|
|
|
return typesEqual(at.elem, av.elem) && at.len == av.len
|
|
|
|
case kindChan:
|
|
|
|
ct := (*chantype)(unsafe.Pointer(t))
|
|
|
|
cv := (*chantype)(unsafe.Pointer(v))
|
|
|
|
return ct.dir == cv.dir && typesEqual(ct.elem, cv.elem)
|
|
|
|
case kindFunc:
|
|
|
|
ft := (*functype)(unsafe.Pointer(t))
|
|
|
|
fv := (*functype)(unsafe.Pointer(v))
|
|
|
|
if ft.outCount != fv.outCount || ft.inCount != fv.inCount {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
tin, vin := ft.in(), fv.in()
|
|
|
|
for i := 0; i < len(tin); i++ {
|
|
|
|
if !typesEqual(tin[i], vin[i]) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tout, vout := ft.out(), fv.out()
|
|
|
|
for i := 0; i < len(tout); i++ {
|
|
|
|
if !typesEqual(tout[i], vout[i]) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
case kindInterface:
|
|
|
|
it := (*interfacetype)(unsafe.Pointer(t))
|
|
|
|
iv := (*interfacetype)(unsafe.Pointer(v))
|
2016-03-31 08:02:10 -06:00
|
|
|
if it.pkgpath.name() != iv.pkgpath.name() {
|
cmd/compile, etc: store method tables as offsets
This CL introduces the typeOff type and a lookup method of the same
name that can turn a typeOff offset into an *rtype.
In a typical Go binary (built with buildmode=exe, pie, c-archive, or
c-shared), there is one moduledata and all typeOff values are offsets
relative to firstmoduledata.types. This makes computing the pointer
cheap in typical programs.
With buildmode=shared (and one day, buildmode=plugin) there are
multiple modules whose relative offset is determined at runtime.
We identify a type in the general case by the pair of the original
*rtype that references it and its typeOff value. We determine
the module from the original pointer, and then use the typeOff from
there to compute the final *rtype.
To ensure there is only one *rtype representing each type, the
runtime initializes a typemap for each module, using any identical
type from an earlier module when resolving that offset. This means
that types computed from an offset match the type mapped by the
pointer dynamic relocations.
A series of followup CLs will replace other *rtype values with typeOff
(and name/*string with nameOff).
For types created at runtime by reflect, type offsets are treated as
global IDs and reference into a reflect offset map kept by the runtime.
darwin/amd64:
cmd/go: -57KB (0.6%)
jujud: -557KB (0.8%)
linux/amd64 PIE:
cmd/go: -361KB (3.0%)
jujud: -3.5MB (4.2%)
For #6853.
Change-Id: Icf096fd884a0a0cb9f280f46f7a26c70a9006c96
Reviewed-on: https://go-review.googlesource.com/21285
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: David Crawshaw <crawshaw@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2016-03-28 08:32:27 -06:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
if len(it.mhdr) != len(iv.mhdr) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
for i := range it.mhdr {
|
|
|
|
tm := &it.mhdr[i]
|
|
|
|
vm := &iv.mhdr[i]
|
2016-03-28 19:51:10 -06:00
|
|
|
tname := it.typ.nameOff(tm.name)
|
|
|
|
vname := iv.typ.nameOff(vm.name)
|
|
|
|
if tname.name() != vname.name() {
|
cmd/compile, etc: store method tables as offsets
This CL introduces the typeOff type and a lookup method of the same
name that can turn a typeOff offset into an *rtype.
In a typical Go binary (built with buildmode=exe, pie, c-archive, or
c-shared), there is one moduledata and all typeOff values are offsets
relative to firstmoduledata.types. This makes computing the pointer
cheap in typical programs.
With buildmode=shared (and one day, buildmode=plugin) there are
multiple modules whose relative offset is determined at runtime.
We identify a type in the general case by the pair of the original
*rtype that references it and its typeOff value. We determine
the module from the original pointer, and then use the typeOff from
there to compute the final *rtype.
To ensure there is only one *rtype representing each type, the
runtime initializes a typemap for each module, using any identical
type from an earlier module when resolving that offset. This means
that types computed from an offset match the type mapped by the
pointer dynamic relocations.
A series of followup CLs will replace other *rtype values with typeOff
(and name/*string with nameOff).
For types created at runtime by reflect, type offsets are treated as
global IDs and reference into a reflect offset map kept by the runtime.
darwin/amd64:
cmd/go: -57KB (0.6%)
jujud: -557KB (0.8%)
linux/amd64 PIE:
cmd/go: -361KB (3.0%)
jujud: -3.5MB (4.2%)
For #6853.
Change-Id: Icf096fd884a0a0cb9f280f46f7a26c70a9006c96
Reviewed-on: https://go-review.googlesource.com/21285
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: David Crawshaw <crawshaw@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2016-03-28 08:32:27 -06:00
|
|
|
return false
|
|
|
|
}
|
2016-03-28 19:51:10 -06:00
|
|
|
if tname.pkgPath() != vname.pkgPath() {
|
cmd/compile, etc: store method tables as offsets
This CL introduces the typeOff type and a lookup method of the same
name that can turn a typeOff offset into an *rtype.
In a typical Go binary (built with buildmode=exe, pie, c-archive, or
c-shared), there is one moduledata and all typeOff values are offsets
relative to firstmoduledata.types. This makes computing the pointer
cheap in typical programs.
With buildmode=shared (and one day, buildmode=plugin) there are
multiple modules whose relative offset is determined at runtime.
We identify a type in the general case by the pair of the original
*rtype that references it and its typeOff value. We determine
the module from the original pointer, and then use the typeOff from
there to compute the final *rtype.
To ensure there is only one *rtype representing each type, the
runtime initializes a typemap for each module, using any identical
type from an earlier module when resolving that offset. This means
that types computed from an offset match the type mapped by the
pointer dynamic relocations.
A series of followup CLs will replace other *rtype values with typeOff
(and name/*string with nameOff).
For types created at runtime by reflect, type offsets are treated as
global IDs and reference into a reflect offset map kept by the runtime.
darwin/amd64:
cmd/go: -57KB (0.6%)
jujud: -557KB (0.8%)
linux/amd64 PIE:
cmd/go: -361KB (3.0%)
jujud: -3.5MB (4.2%)
For #6853.
Change-Id: Icf096fd884a0a0cb9f280f46f7a26c70a9006c96
Reviewed-on: https://go-review.googlesource.com/21285
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: David Crawshaw <crawshaw@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2016-03-28 08:32:27 -06:00
|
|
|
return false
|
|
|
|
}
|
2016-03-28 19:51:10 -06:00
|
|
|
if !typesEqual(it.typ.typeOff(tm.ityp), iv.typ.typeOff(vm.ityp)) {
|
cmd/compile, etc: store method tables as offsets
This CL introduces the typeOff type and a lookup method of the same
name that can turn a typeOff offset into an *rtype.
In a typical Go binary (built with buildmode=exe, pie, c-archive, or
c-shared), there is one moduledata and all typeOff values are offsets
relative to firstmoduledata.types. This makes computing the pointer
cheap in typical programs.
With buildmode=shared (and one day, buildmode=plugin) there are
multiple modules whose relative offset is determined at runtime.
We identify a type in the general case by the pair of the original
*rtype that references it and its typeOff value. We determine
the module from the original pointer, and then use the typeOff from
there to compute the final *rtype.
To ensure there is only one *rtype representing each type, the
runtime initializes a typemap for each module, using any identical
type from an earlier module when resolving that offset. This means
that types computed from an offset match the type mapped by the
pointer dynamic relocations.
A series of followup CLs will replace other *rtype values with typeOff
(and name/*string with nameOff).
For types created at runtime by reflect, type offsets are treated as
global IDs and reference into a reflect offset map kept by the runtime.
darwin/amd64:
cmd/go: -57KB (0.6%)
jujud: -557KB (0.8%)
linux/amd64 PIE:
cmd/go: -361KB (3.0%)
jujud: -3.5MB (4.2%)
For #6853.
Change-Id: Icf096fd884a0a0cb9f280f46f7a26c70a9006c96
Reviewed-on: https://go-review.googlesource.com/21285
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: David Crawshaw <crawshaw@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2016-03-28 08:32:27 -06:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
case kindMap:
|
|
|
|
mt := (*maptype)(unsafe.Pointer(t))
|
|
|
|
mv := (*maptype)(unsafe.Pointer(v))
|
|
|
|
return typesEqual(mt.key, mv.key) && typesEqual(mt.elem, mv.elem)
|
|
|
|
case kindPtr:
|
|
|
|
pt := (*ptrtype)(unsafe.Pointer(t))
|
|
|
|
pv := (*ptrtype)(unsafe.Pointer(v))
|
|
|
|
return typesEqual(pt.elem, pv.elem)
|
|
|
|
case kindSlice:
|
|
|
|
st := (*slicetype)(unsafe.Pointer(t))
|
|
|
|
sv := (*slicetype)(unsafe.Pointer(v))
|
|
|
|
return typesEqual(st.elem, sv.elem)
|
|
|
|
case kindStruct:
|
|
|
|
st := (*structtype)(unsafe.Pointer(t))
|
|
|
|
sv := (*structtype)(unsafe.Pointer(v))
|
|
|
|
if len(st.fields) != len(sv.fields) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
for i := range st.fields {
|
|
|
|
tf := &st.fields[i]
|
|
|
|
vf := &sv.fields[i]
|
|
|
|
if tf.name.name() != vf.name.name() {
|
|
|
|
return false
|
|
|
|
}
|
2016-03-31 08:02:10 -06:00
|
|
|
if tf.name.pkgPath() != vf.name.pkgPath() {
|
cmd/compile, etc: store method tables as offsets
This CL introduces the typeOff type and a lookup method of the same
name that can turn a typeOff offset into an *rtype.
In a typical Go binary (built with buildmode=exe, pie, c-archive, or
c-shared), there is one moduledata and all typeOff values are offsets
relative to firstmoduledata.types. This makes computing the pointer
cheap in typical programs.
With buildmode=shared (and one day, buildmode=plugin) there are
multiple modules whose relative offset is determined at runtime.
We identify a type in the general case by the pair of the original
*rtype that references it and its typeOff value. We determine
the module from the original pointer, and then use the typeOff from
there to compute the final *rtype.
To ensure there is only one *rtype representing each type, the
runtime initializes a typemap for each module, using any identical
type from an earlier module when resolving that offset. This means
that types computed from an offset match the type mapped by the
pointer dynamic relocations.
A series of followup CLs will replace other *rtype values with typeOff
(and name/*string with nameOff).
For types created at runtime by reflect, type offsets are treated as
global IDs and reference into a reflect offset map kept by the runtime.
darwin/amd64:
cmd/go: -57KB (0.6%)
jujud: -557KB (0.8%)
linux/amd64 PIE:
cmd/go: -361KB (3.0%)
jujud: -3.5MB (4.2%)
For #6853.
Change-Id: Icf096fd884a0a0cb9f280f46f7a26c70a9006c96
Reviewed-on: https://go-review.googlesource.com/21285
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: David Crawshaw <crawshaw@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2016-03-28 08:32:27 -06:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
if !typesEqual(tf.typ, vf.typ) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if tf.name.tag() != vf.name.tag() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if tf.offset != vf.offset {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
default:
|
|
|
|
println("runtime: impossible type kind", kind)
|
|
|
|
throw("runtime: impossible type kind")
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|