aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/error.go
diff options
context:
space:
mode:
authorJosh Bleecher Snyder <josharian@gmail.com>2017-02-10 21:49:07 -0800
committerJosh Bleecher Snyder <josharian@gmail.com>2017-02-11 23:27:35 +0000
commit2c91bb4c8af6112074dca24b97b096c888e3b583 (patch)
tree7ad5bc7ac04138a9f1137e4f2a79231f06f68a27 /src/runtime/error.go
parent61e963e9c649714a88433d50afa679ec91e05e33 (diff)
downloadgo-2c91bb4c8af6112074dca24b97b096c888e3b583.tar.gz
go-2c91bb4c8af6112074dca24b97b096c888e3b583.zip
cmd/compile: make panicwrap argument-free
When code defines a method on T, the compiler generates a corresponding wrapper method on *T. The first thing the wrapper does is check whether the pointer is nil and if so, call panicwrap. This is done to provide a useful error message. The existing implementation gets its information from arguments set up by the compiler. However, with some trouble, this information can be extracted from the name of the wrapper method itself. Removing the arguments to panicwrap simplifies and shrinks the wrapper method. It also means that the call to panicwrap does not require any stack space. This enables a further optimization on amd64/x86, which is to skip the function prologue if nothing else in the method requires stack space. This is frequently the case in simple, hot methods, such as Less and Swap in sort.Interface implementations. Fixes #19040. Benchmarks for package sort on amd64: name old time/op new time/op delta SearchWrappers-8 104ns ± 1% 104ns ± 1% ~ (p=0.286 n=27+27) SortString1K-8 128µs ± 1% 128µs ± 1% -0.44% (p=0.004 n=30+30) SortString1K_Slice-8 118µs ± 2% 117µs ± 1% ~ (p=0.106 n=30+30) StableString1K-8 18.6µs ± 1% 18.6µs ± 1% ~ (p=0.446 n=28+26) SortInt1K-8 65.9µs ± 1% 60.7µs ± 1% -7.96% (p=0.000 n=28+30) StableInt1K-8 75.3µs ± 2% 72.8µs ± 1% -3.41% (p=0.000 n=30+30) StableInt1K_Slice-8 57.7µs ± 1% 57.7µs ± 1% ~ (p=0.515 n=30+30) SortInt64K-8 6.28ms ± 1% 6.01ms ± 1% -4.19% (p=0.000 n=28+28) SortInt64K_Slice-8 5.04ms ± 1% 5.04ms ± 1% ~ (p=0.927 n=28+27) StableInt64K-8 6.65ms ± 1% 6.38ms ± 1% -3.97% (p=0.000 n=26+30) Sort1e2-8 37.9µs ± 1% 37.2µs ± 1% -1.89% (p=0.000 n=29+27) Stable1e2-8 77.0µs ± 1% 74.7µs ± 1% -3.06% (p=0.000 n=27+30) Sort1e4-8 8.21ms ± 2% 7.98ms ± 1% -2.77% (p=0.000 n=29+30) Stable1e4-8 24.8ms ± 1% 24.3ms ± 1% -2.31% (p=0.000 n=28+30) Sort1e6-8 1.27s ± 4% 1.22s ± 1% -3.42% (p=0.000 n=30+29) Stable1e6-8 5.06s ± 1% 4.92s ± 1% -2.77% (p=0.000 n=25+29) [Geo mean] 731µs 714µs -2.29% Before/after assembly for sort.(*intPairs).Less follows. It can be optimized further, but that's for a follow-up CL. Before: "".(*intPairs).Less t=1 size=214 args=0x20 locals=0x38 0x0000 00000 (<autogenerated>:1) TEXT "".(*intPairs).Less(SB), $56-32 0x0000 00000 (<autogenerated>:1) MOVQ (TLS), CX 0x0009 00009 (<autogenerated>:1) CMPQ SP, 16(CX) 0x000d 00013 (<autogenerated>:1) JLS 204 0x0013 00019 (<autogenerated>:1) SUBQ $56, SP 0x0017 00023 (<autogenerated>:1) MOVQ BP, 48(SP) 0x001c 00028 (<autogenerated>:1) LEAQ 48(SP), BP 0x0021 00033 (<autogenerated>:1) MOVQ 32(CX), BX 0x0025 00037 (<autogenerated>:1) TESTQ BX, BX 0x0028 00040 (<autogenerated>:1) JEQ 55 0x002a 00042 (<autogenerated>:1) LEAQ 64(SP), DI 0x002f 00047 (<autogenerated>:1) CMPQ (BX), DI 0x0032 00050 (<autogenerated>:1) JNE 55 0x0034 00052 (<autogenerated>:1) MOVQ SP, (BX) 0x0037 00055 (<autogenerated>:1) NOP 0x0037 00055 (<autogenerated>:1) FUNCDATA $0, gclocals·4032f753396f2012ad1784f398b170f4(SB) 0x0037 00055 (<autogenerated>:1) FUNCDATA $1, gclocals·69c1753bd5f81501d95132d08af04464(SB) 0x0037 00055 (<autogenerated>:1) MOVQ ""..this+64(FP), AX 0x003c 00060 (<autogenerated>:1) TESTQ AX, AX 0x003f 00063 (<autogenerated>:1) JEQ $0, 135 0x0041 00065 (<autogenerated>:1) MOVQ (AX), CX 0x0044 00068 (<autogenerated>:1) MOVQ 8(AX), AX 0x0048 00072 (<autogenerated>:1) MOVQ "".i+72(FP), DX 0x004d 00077 (<autogenerated>:1) CMPQ DX, AX 0x0050 00080 (<autogenerated>:1) JCC $0, 128 0x0052 00082 (<autogenerated>:1) SHLQ $4, DX 0x0056 00086 (<autogenerated>:1) MOVQ (CX)(DX*1), DX 0x005a 00090 (<autogenerated>:1) MOVQ "".j+80(FP), BX 0x005f 00095 (<autogenerated>:1) CMPQ BX, AX 0x0062 00098 (<autogenerated>:1) JCC $0, 128 0x0064 00100 (<autogenerated>:1) SHLQ $4, BX 0x0068 00104 (<autogenerated>:1) MOVQ (CX)(BX*1), AX 0x006c 00108 (<autogenerated>:1) CMPQ DX, AX 0x006f 00111 (<autogenerated>:1) SETLT AL 0x0072 00114 (<autogenerated>:1) MOVB AL, "".~r2+88(FP) 0x0076 00118 (<autogenerated>:1) MOVQ 48(SP), BP 0x007b 00123 (<autogenerated>:1) ADDQ $56, SP 0x007f 00127 (<autogenerated>:1) RET 0x0080 00128 (<autogenerated>:1) PCDATA $0, $1 0x0080 00128 (<autogenerated>:1) CALL runtime.panicindex(SB) 0x0085 00133 (<autogenerated>:1) UNDEF 0x0087 00135 (<autogenerated>:1) LEAQ go.string."sort_test"(SB), AX 0x008e 00142 (<autogenerated>:1) MOVQ AX, (SP) 0x0092 00146 (<autogenerated>:1) MOVQ $9, 8(SP) 0x009b 00155 (<autogenerated>:1) LEAQ go.string."intPairs"(SB), AX 0x00a2 00162 (<autogenerated>:1) MOVQ AX, 16(SP) 0x00a7 00167 (<autogenerated>:1) MOVQ $8, 24(SP) 0x00b0 00176 (<autogenerated>:1) LEAQ go.string."Less"(SB), AX 0x00b7 00183 (<autogenerated>:1) MOVQ AX, 32(SP) 0x00bc 00188 (<autogenerated>:1) MOVQ $4, 40(SP) 0x00c5 00197 (<autogenerated>:1) PCDATA $0, $1 0x00c5 00197 (<autogenerated>:1) CALL runtime.panicwrap(SB) 0x00ca 00202 (<autogenerated>:1) UNDEF 0x00cc 00204 (<autogenerated>:1) NOP 0x00cc 00204 (<autogenerated>:1) PCDATA $0, $-1 0x00cc 00204 (<autogenerated>:1) CALL runtime.morestack_noctxt(SB) 0x00d1 00209 (<autogenerated>:1) JMP 0 After: "".(*intPairs).Swap t=1 size=147 args=0x18 locals=0x8 0x0000 00000 (<autogenerated>:1) TEXT "".(*intPairs).Swap(SB), $8-24 0x0000 00000 (<autogenerated>:1) MOVQ (TLS), CX 0x0009 00009 (<autogenerated>:1) SUBQ $8, SP 0x000d 00013 (<autogenerated>:1) MOVQ BP, (SP) 0x0011 00017 (<autogenerated>:1) LEAQ (SP), BP 0x0015 00021 (<autogenerated>:1) MOVQ 32(CX), BX 0x0019 00025 (<autogenerated>:1) TESTQ BX, BX 0x001c 00028 (<autogenerated>:1) JEQ 43 0x001e 00030 (<autogenerated>:1) LEAQ 16(SP), DI 0x0023 00035 (<autogenerated>:1) CMPQ (BX), DI 0x0026 00038 (<autogenerated>:1) JNE 43 0x0028 00040 (<autogenerated>:1) MOVQ SP, (BX) 0x002b 00043 (<autogenerated>:1) NOP 0x002b 00043 (<autogenerated>:1) FUNCDATA $0, gclocals·e6397a44f8e1b6e77d0f200b4fba5269(SB) 0x002b 00043 (<autogenerated>:1) FUNCDATA $1, gclocals·69c1753bd5f81501d95132d08af04464(SB) 0x002b 00043 (<autogenerated>:1) MOVQ ""..this+16(FP), AX 0x0030 00048 (<autogenerated>:1) TESTQ AX, AX 0x0033 00051 (<autogenerated>:1) JEQ $0, 140 0x0035 00053 (<autogenerated>:1) MOVQ (AX), CX 0x0038 00056 (<autogenerated>:1) MOVQ 8(AX), AX 0x003c 00060 (<autogenerated>:1) MOVQ "".i+24(FP), DX 0x0041 00065 (<autogenerated>:1) CMPQ DX, AX 0x0044 00068 (<autogenerated>:1) JCC $0, 133 0x0046 00070 (<autogenerated>:1) SHLQ $4, DX 0x004a 00074 (<autogenerated>:1) MOVQ 8(CX)(DX*1), BX 0x004f 00079 (<autogenerated>:1) MOVQ (CX)(DX*1), SI 0x0053 00083 (<autogenerated>:1) MOVQ "".j+32(FP), DI 0x0058 00088 (<autogenerated>:1) CMPQ DI, AX 0x005b 00091 (<autogenerated>:1) JCC $0, 133 0x005d 00093 (<autogenerated>:1) SHLQ $4, DI 0x0061 00097 (<autogenerated>:1) MOVQ 8(CX)(DI*1), AX 0x0066 00102 (<autogenerated>:1) MOVQ (CX)(DI*1), R8 0x006a 00106 (<autogenerated>:1) MOVQ R8, (CX)(DX*1) 0x006e 00110 (<autogenerated>:1) MOVQ AX, 8(CX)(DX*1) 0x0073 00115 (<autogenerated>:1) MOVQ SI, (CX)(DI*1) 0x0077 00119 (<autogenerated>:1) MOVQ BX, 8(CX)(DI*1) 0x007c 00124 (<autogenerated>:1) MOVQ (SP), BP 0x0080 00128 (<autogenerated>:1) ADDQ $8, SP 0x0084 00132 (<autogenerated>:1) RET 0x0085 00133 (<autogenerated>:1) PCDATA $0, $1 0x0085 00133 (<autogenerated>:1) CALL runtime.panicindex(SB) 0x008a 00138 (<autogenerated>:1) UNDEF 0x008c 00140 (<autogenerated>:1) PCDATA $0, $1 0x008c 00140 (<autogenerated>:1) CALL runtime.panicwrap(SB) 0x0091 00145 (<autogenerated>:1) UNDEF Change-Id: I15bb8435f0690badb868799f313ed8817335efd3 Reviewed-on: https://go-review.googlesource.com/36809 Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Diffstat (limited to 'src/runtime/error.go')
-rw-r--r--src/runtime/error.go38
1 files changed, 37 insertions, 1 deletions
diff --git a/src/runtime/error.go b/src/runtime/error.go
index 0238c5e592..f5b015c091 100644
--- a/src/runtime/error.go
+++ b/src/runtime/error.go
@@ -4,6 +4,8 @@
package runtime
+import _ "unsafe" // for go:linkname
+
// The Error interface identifies a run time error.
type Error interface {
error
@@ -91,7 +93,41 @@ func printany(i interface{}) {
}
}
+// strings.IndexByte is implemented in runtime/asm_$goarch.s
+// but amusingly we need go:linkname to get access to it here in the runtime.
+//go:linkname stringsIndexByte strings.IndexByte
+func stringsIndexByte(s string, c byte) int
+
// called from generated code
-func panicwrap(pkg, typ, meth string) {
+func panicwrap() {
+ pc := make([]uintptr, 1)
+ n := Callers(2, pc)
+ if n == 0 {
+ throw("panicwrap: Callers failed")
+ }
+ frames := CallersFrames(pc)
+ frame, _ := frames.Next()
+ name := frame.Function
+ // name is something like "main.(*T).F".
+ // We want to extract pkg ("main"), typ ("T"), and meth ("F").
+ // Do it by finding the parens.
+ i := stringsIndexByte(name, '(')
+ if i < 0 {
+ throw("panicwrap: no ( in " + frame.Function)
+ }
+ pkg := name[:i-1]
+ if i+2 >= len(name) || name[i-1:i+2] != ".(*" {
+ throw("panicwrap: unexpected string after package name: " + frame.Function)
+ }
+ name = name[i+2:]
+ i = stringsIndexByte(name, ')')
+ if i < 0 {
+ throw("panicwrap: no ) in " + frame.Function)
+ }
+ if i+2 >= len(name) || name[i:i+2] != ")." {
+ throw("panicwrap: unexpected string after type name: " + frame.Function)
+ }
+ typ := name[:i]
+ meth := name[i+2:]
panic(plainError("value method " + pkg + "." + typ + "." + meth + " called using nil *" + typ + " pointer"))
}