diff --git a/doc/asm.html b/doc/asm.html index f2f8fad576..debb1e2fc6 100644 --- a/doc/asm.html +++ b/doc/asm.html @@ -740,6 +740,7 @@ The ARM64 port is in an experimental state.

R18 is the "platform register", reserved on the Apple platform. +To prevent accidental misuse, the register is named R18_PLATFORM. R27 and R28 are reserved by the compiler and linker. R29 is the frame pointer. R30 is the link register. diff --git a/misc/cgo/test/cgo_test.go b/misc/cgo/test/cgo_test.go index ccacc50fe1..ae856a37d6 100644 --- a/misc/cgo/test/cgo_test.go +++ b/misc/cgo/test/cgo_test.go @@ -92,6 +92,7 @@ func Test25143(t *testing.T) { test25143(t) } func Test23356(t *testing.T) { test23356(t) } func Test26066(t *testing.T) { test26066(t) } func Test26213(t *testing.T) { test26213(t) } +func Test27660(t *testing.T) { test27660(t) } func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) } func BenchmarkGoString(b *testing.B) { benchGoString(b) } diff --git a/misc/cgo/test/test27660.go b/misc/cgo/test/test27660.go new file mode 100644 index 0000000000..8c23b7dc58 --- /dev/null +++ b/misc/cgo/test/test27660.go @@ -0,0 +1,54 @@ +// Copyright 2018 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. + +// Stress the interaction between the race detector and cgo in an +// attempt to reproduce the memory corruption described in #27660. +// The bug was very timing sensitive; at the time of writing this +// test would only trigger the bug about once out of every five runs. + +package cgotest + +// #include +import "C" + +import ( + "context" + "math/rand" + "runtime" + "sync" + "testing" + "time" +) + +func test27660(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ints := make([]int, 100) + locks := make([]sync.Mutex, 100) + // Slowly create threads so that ThreadSanitizer is forced to + // frequently resize its SyncClocks. + for i := 0; i < 100; i++ { + go func() { + for ctx.Err() == nil { + // Sleep in C for long enough that it is likely that the runtime + // will retake this goroutine's currently wired P. + C.usleep(1000 /* 1ms */) + runtime.Gosched() // avoid starvation (see #28701) + } + }() + go func() { + // Trigger lots of synchronization and memory reads/writes to + // increase the likelihood that the race described in #27660 + // results in corruption of ThreadSanitizer's internal state + // and thus an assertion failure or segfault. + for ctx.Err() == nil { + j := rand.Intn(100) + locks[j].Lock() + ints[j]++ + locks[j].Unlock() + } + }() + time.Sleep(time.Millisecond) + } +} diff --git a/misc/nacl/testzip.proto b/misc/nacl/testzip.proto index 7f524cac48..c2afa1020a 100644 --- a/misc/nacl/testzip.proto +++ b/misc/nacl/testzip.proto @@ -50,6 +50,9 @@ go src=.. google pprof internal + binutils + testdata + + driver testdata + diff --git a/misc/wasm/wasm_exec.js b/misc/wasm/wasm_exec.js index e47663783e..440bba104c 100644 --- a/misc/wasm/wasm_exec.js +++ b/misc/wasm/wasm_exec.js @@ -79,6 +79,10 @@ console.warn("exit code:", code); } }; + this._exitPromise = new Promise((resolve) => { + this._resolveExitPromise = resolve; + }); + this._pendingCallback = null; this._callbackTimeouts = new Map(); this._nextCallbackTimeoutID = 1; @@ -194,6 +198,11 @@ const timeOrigin = Date.now() - performance.now(); this.importObject = { go: { + // Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters) + // may trigger a synchronous callback to Go. This makes Go code get executed in the middle of the imported + // function. A goroutine can switch to a new stack if the current stack is too small (see morestack function). + // This changes the SP, thus we have to update the SP used by the imported function. + // func wasmExit(code int32) "runtime.wasmExit": (sp) => { const code = mem().getInt32(sp + 8, true); @@ -229,7 +238,7 @@ const id = this._nextCallbackTimeoutID; this._nextCallbackTimeoutID++; this._callbackTimeouts.set(id, setTimeout( - () => { this._resolveCallbackPromise(); }, + () => { this._resume(); }, getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early )); mem().setInt32(sp + 16, id, true); @@ -254,7 +263,9 @@ // func valueGet(v ref, p string) ref "syscall/js.valueGet": (sp) => { - storeValue(sp + 32, Reflect.get(loadValue(sp + 8), loadString(sp + 16))); + const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16)); + sp = this._inst.exports.getsp(); // see comment above + storeValue(sp + 32, result); }, // func valueSet(v ref, p string, x ref) @@ -278,7 +289,9 @@ const v = loadValue(sp + 8); const m = Reflect.get(v, loadString(sp + 16)); const args = loadSliceOfValues(sp + 32); - storeValue(sp + 56, Reflect.apply(m, v, args)); + const result = Reflect.apply(m, v, args); + sp = this._inst.exports.getsp(); // see comment above + storeValue(sp + 56, result); mem().setUint8(sp + 64, 1); } catch (err) { storeValue(sp + 56, err); @@ -291,7 +304,9 @@ try { const v = loadValue(sp + 8); const args = loadSliceOfValues(sp + 16); - storeValue(sp + 40, Reflect.apply(v, undefined, args)); + const result = Reflect.apply(v, undefined, args); + sp = this._inst.exports.getsp(); // see comment above + storeValue(sp + 40, result); mem().setUint8(sp + 48, 1); } catch (err) { storeValue(sp + 40, err); @@ -304,7 +319,9 @@ try { const v = loadValue(sp + 8); const args = loadSliceOfValues(sp + 16); - storeValue(sp + 40, Reflect.construct(v, args)); + const result = Reflect.construct(v, args); + sp = this._inst.exports.getsp(); // see comment above + storeValue(sp + 40, result); mem().setUint8(sp + 48, 1); } catch (err) { storeValue(sp + 40, err); @@ -355,7 +372,6 @@ this, ]; this._refs = new Map(); - this._callbackShutdown = false; this.exited = false; const mem = new DataView(this._inst.exports.mem.buffer) @@ -390,42 +406,30 @@ offset += 8; }); - while (true) { - const callbackPromise = new Promise((resolve) => { - this._resolveCallbackPromise = () => { - if (this.exited) { - throw new Error("bad callback: Go program has already exited"); - } - setTimeout(resolve, 0); // make sure it is asynchronous - }; - }); - this._inst.exports.run(argc, argv); - if (this.exited) { - break; - } - await callbackPromise; + this._inst.exports.run(argc, argv); + if (this.exited) { + this._resolveExitPromise(); + } + await this._exitPromise; + } + + _resume() { + if (this.exited) { + throw new Error("bad callback: Go program has already exited"); + } + this._inst.exports.resume(); + if (this.exited) { + this._resolveExitPromise(); } } - static _makeCallbackHelper(id, pendingCallbacks, go) { + _makeCallbackHelper(id) { + const go = this; return function () { - pendingCallbacks.push({ id: id, args: arguments }); - go._resolveCallbackPromise(); - }; - } - - static _makeEventCallbackHelper(preventDefault, stopPropagation, stopImmediatePropagation, fn) { - return function (event) { - if (preventDefault) { - event.preventDefault(); - } - if (stopPropagation) { - event.stopPropagation(); - } - if (stopImmediatePropagation) { - event.stopImmediatePropagation(); - } - fn(event); + const cb = { id: id, this: this, args: arguments }; + go._pendingCallback = cb; + go._resume(); + return cb.result; }; } } @@ -444,8 +448,8 @@ process.on("exit", (code) => { // Node.js exits if no callback is pending if (code === 0 && !go.exited) { // deadlock, make Go print error and stack traces - go._callbackShutdown = true; - go._inst.exports.run(); + go._pendingCallback = { id: 0 }; + go._resume(); } }); return go.run(result.instance); diff --git a/src/bufio/bufio_test.go b/src/bufio/bufio_test.go index c829d2b064..34d70312f7 100644 --- a/src/bufio/bufio_test.go +++ b/src/bufio/bufio_test.go @@ -550,7 +550,7 @@ func TestWriter(t *testing.T) { t.Errorf("%s: %d bytes written", context, len(written)) } for l := 0; l < len(written); l++ { - if written[i] != data[i] { + if written[l] != data[l] { t.Errorf("wrong bytes written") t.Errorf("want=%q", data[0:len(written)]) t.Errorf("have=%q", written) diff --git a/src/bytes/bytes.go b/src/bytes/bytes.go index 6492db088a..daf4a32f26 100644 --- a/src/bytes/bytes.go +++ b/src/bytes/bytes.go @@ -12,6 +12,13 @@ import ( "unicode/utf8" ) +// Equal returns a boolean reporting whether a and b +// are the same length and contain the same bytes. +// A nil argument is equivalent to an empty slice. +func Equal(a, b []byte) bool { + return bytealg.Equal(a, b) +} + func equalPortable(a, b []byte) bool { if len(a) != len(b) { return false @@ -24,6 +31,13 @@ func equalPortable(a, b []byte) bool { return true } +// Compare returns an integer comparing two byte slices lexicographically. +// The result will be 0 if a==b, -1 if a < b, and +1 if a > b. +// A nil argument is equivalent to an empty slice. +func Compare(a, b []byte) int { + return bytealg.Compare(a, b) +} + // explode splits s into a slice of UTF-8 sequences, one per Unicode code point (still slices of bytes), // up to a maximum of n byte slices. Invalid UTF-8 sequences are chopped into individual bytes. func explode(s []byte, n int) [][]byte { @@ -83,6 +97,11 @@ func ContainsRune(b []byte, r rune) bool { return IndexRune(b, r) >= 0 } +// IndexByte returns the index of the first instance of c in b, or -1 if c is not present in b. +func IndexByte(b []byte, c byte) int { + return bytealg.IndexByte(b, c) +} + func indexBytePortable(s []byte, c byte) int { for i, b := range s { if b == c { diff --git a/src/bytes/bytes_decl.go b/src/bytes/bytes_decl.go deleted file mode 100644 index af0f8b179f..0000000000 --- a/src/bytes/bytes_decl.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2010 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. - -package bytes - -//go:noescape - -// IndexByte returns the index of the first instance of c in b, or -1 if c is not present in b. -func IndexByte(b []byte, c byte) int // in internal/bytealg - -//go:noescape - -// Equal returns a boolean reporting whether a and b -// are the same length and contain the same bytes. -// A nil argument is equivalent to an empty slice. -func Equal(a, b []byte) bool // in internal/bytealg - -//go:noescape - -// Compare returns an integer comparing two byte slices lexicographically. -// The result will be 0 if a==b, -1 if a < b, and +1 if a > b. -// A nil argument is equivalent to an empty slice. -func Compare(a, b []byte) int // in internal/bytealg diff --git a/src/cmd/addr2line/main.go b/src/cmd/addr2line/main.go index 267f4170a8..018802940b 100644 --- a/src/cmd/addr2line/main.go +++ b/src/cmd/addr2line/main.go @@ -61,6 +61,7 @@ func main() { if err != nil { log.Fatal(err) } + defer f.Close() tab, err := f.PCLineTable() if err != nil { diff --git a/src/cmd/asm/internal/arch/arch.go b/src/cmd/asm/internal/arch/arch.go index ecea6ba97d..eaa5cb8958 100644 --- a/src/cmd/asm/internal/arch/arch.go +++ b/src/cmd/asm/internal/arch/arch.go @@ -258,6 +258,9 @@ func archArm64() *Arch { for i := arm64.REG_R0; i <= arm64.REG_R31; i++ { register[obj.Rconv(i)] = int16(i) } + // Rename R18 to R18_PLATFORM to avoid accidental use. + register["R18_PLATFORM"] = register["R18"] + delete(register, "R18") for i := arm64.REG_F0; i <= arm64.REG_F31; i++ { register[obj.Rconv(i)] = int16(i) } diff --git a/src/cmd/asm/internal/asm/operand_test.go b/src/cmd/asm/internal/asm/operand_test.go index df60b71ebd..69393b6b20 100644 --- a/src/cmd/asm/internal/asm/operand_test.go +++ b/src/cmd/asm/internal/asm/operand_test.go @@ -607,6 +607,7 @@ var arm64OperandTests = []operandTest{ {"R0", "R0"}, {"R10", "R10"}, {"R11", "R11"}, + {"R18_PLATFORM", "R18"}, {"$4503601774854144.0", "$(4503601774854144.0)"}, {"$runtime·badsystemstack(SB)", "$runtime.badsystemstack(SB)"}, {"ZR", "ZR"}, diff --git a/src/cmd/asm/internal/asm/testdata/arm64.s b/src/cmd/asm/internal/asm/testdata/arm64.s index 12c7adbd04..d025543e6d 100644 --- a/src/cmd/asm/internal/asm/testdata/arm64.s +++ b/src/cmd/asm/internal/asm/testdata/arm64.s @@ -47,8 +47,8 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8 ADD R2.SXTX<<1, RSP, RSP // ffe7228b ADD ZR.SXTX<<1, R2, R3 // 43e43f8b ADDW R2.SXTW, R10, R12 // 4cc1220b - ADD R18.UXTX, R14, R17 // d161328b - ADDSW R18.UXTW, R14, R17 // d141322b + ADD R19.UXTX, R14, R17 // d161338b + ADDSW R19.UXTW, R14, R17 // d141332b ADDS R12.SXTX, R3, R1 // 61e02cab SUB R19.UXTH<<4, R2, R21 // 553033cb SUBW R1.UXTX<<1, R3, R2 // 6264214b @@ -144,7 +144,7 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8 MOVD (R2)(R6.SXTW), R4 // 44c866f8 MOVD (R3)(R6), R5 // MOVD (R3)(R6*1), R5 // 656866f8 MOVD (R2)(R6), R4 // MOVD (R2)(R6*1), R4 // 446866f8 - MOVWU (R19)(R18<<2), R18 // 727a72b8 + MOVWU (R19)(R20<<2), R20 // 747a74b8 MOVD (R2)(R6<<3), R4 // 447866f8 MOVD (R3)(R7.SXTX<<3), R8 // 68f867f8 MOVWU (R5)(R4.UXTW), R10 // aa4864b8 @@ -154,7 +154,7 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8 MOVHU (R1)(R2<<1), R5 // 25786278 MOVB (R9)(R3.UXTW), R6 // 2649a338 MOVB (R10)(R6), R15 // MOVB (R10)(R6*1), R15 // 4f69a638 - MOVH (R5)(R7.SXTX<<1), R18 // b2f8a778 + MOVH (R5)(R7.SXTX<<1), R19 // b3f8a778 MOVH (R8)(R4<<1), R10 // 0a79a478 MOVW (R9)(R8.SXTW<<2), R19 // 33d9a8b8 MOVW (R1)(R4.SXTX), R11 // 2be8a4b8 @@ -195,6 +195,11 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8 CMPW $27745, R2 // 3b8c8d525f001b6b CMNW $0x3fffffc0, R2 // CMNW $1073741760, R2 // fb5f1a325f001b2b CMPW $0xffff0, R1 // CMPW $1048560, R1 // fb3f1c323f001b6b + CMP $0xffffffffffa0, R3 // CMP $281474976710560, R3 // fb0b80921b00e0f27f001beb + CMP $0xf4240, R1 // CMP $1000000, R1 // 1b4888d2fb01a0f23f001beb + ADD $0x186a0, R2, R5 // ADD $100000, R2, R5 // 45801a91a5604091 + SUB $0xe7791f700, R3, R1 // SUB $62135596800, R3, R1 // 1be09ed23bf2aef2db01c0f261001bcb + CMP $3343198598084851058, R3 // 5bae8ed2db8daef23badcdf2bbcce5f27f001beb ADD $0x3fffffffc000, R5 // ADD $70368744161280, R5 // fb7f72b2a5001b8b // LTYPE1 imsr ',' spreg ',' // { @@ -222,6 +227,13 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8 EOR $(1<<63), R1 // EOR $-9223372036854775808, R1 // 210041d2 EOR $(1<<63-1), R1 // EOR $9223372036854775807, R1 // 21f840d2 + ANDW $0x3ff00000, R2 // ANDW $1072693248, R2 // 42240c12 + BICW $0x3ff00000, R2 // BICW $1072693248, R2 // 42540212 + ORRW $0x3ff00000, R2 // ORRW $1072693248, R2 // 42240c32 + ORNW $0x3ff00000, R2 // ORNW $1072693248, R2 // 42540232 + EORW $0x3ff00000, R2 // EORW $1072693248, R2 // 42240c52 + EONW $0x3ff00000, R2 // EONW $1072693248, R2 // 42540252 + AND $0x22220000, R3, R4 // AND $572653568, R3, R4 // 5b44a4d264001b8a ORR $0x22220000, R3, R4 // ORR $572653568, R3, R4 // 5b44a4d264001baa EOR $0x22220000, R3, R4 // EOR $572653568, R3, R4 // 5b44a4d264001bca @@ -233,12 +245,21 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8 EOR $0xe03fffffffffffff, R20, R22 // EOR $-2287828610704211969, R20, R22 // 96e243d2 TSTW $0x600000006, R1 // TSTW $25769803782, R1 // 3f041f72 + TST $0x4900000049, R0 // TST $313532612681, R0 // 3b0980d23b09c0f21f001bea + ORR $0x170000, R2, R1 // ORR $1507328, R2, R1 // fb02a0d241001baa + AND $0xff00ff, R2 // AND $16711935, R2 // fb1f80d2fb1fa0f242001b8a + AND $0xff00ffff, R1 // AND $4278255615, R1 // fbff9fd21be0bff221001b8a ANDS $0xffff, R2 // ANDS $65535, R2 // 423c40f2 AND $0x7fffffff, R3 // AND $2147483647, R3 // 63784092 ANDS $0x0ffffffff80000000, R2 // ANDS $-2147483648, R2 // 428061f2 AND $0xfffff, R2 // AND $1048575, R2 // 424c4092 ANDW $0xf00fffff, R1 // ANDW $4027580415, R1 // 215c0412 ANDSW $0xff00ffff, R1 // ANDSW $4278255615, R1 // 215c0872 + TST $0x11223344, R2 // TST $287454020, R2 // 9b6886d25b24a2f25f001bea + TSTW $0xa000, R3 // TSTW $40960, R3 // 1b0094527f001b6a + BICW $0xa000, R3 // BICW $40960, R3 // 1b00945263003b0a + ORRW $0x1b000, R2, R3 // ORRW $110592, R2, R3 // 1b0096523b00a07243001b2a + TSTW $0x500000, R1 // TSTW $5242880, R1 // 1b0aa0523f001b6a TSTW $0xff00ff, R1 // TSTW $16711935, R1 // 3f9c0072 AND $8, R0, RSP // 1f007d92 @@ -249,13 +270,20 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8 EON $8, R0, RSP // 1ff87cd2 MOVD $0x3fffffffc000, R0 // MOVD $70368744161280, R0 // e07f72b2 + MOVW $1000000, R4 // 04488852e401a072 MOVW $0xaaaa0000, R1 // MOVW $2863267840, R1 // 4155b552 MOVW $0xaaaaffff, R1 // MOVW $2863333375, R1 // a1aaaa12 MOVW $0xaaaa, R1 // MOVW $43690, R1 // 41559552 MOVW $0xffffaaaa, R1 // MOVW $4294945450, R1 // a1aa8a12 MOVW $0xffff0000, R1 // MOVW $4294901760, R1 // e1ffbf52 MOVD $0xffff00000000000, R1 // MOVD $1152903912420802560, R1 // e13f54b2 + MOVD $0x1111000000001111, R1 // MOVD $1229764173248860433, R1 // 212282d22122e2f2 + MOVD $0x1111ffff1111ffff, R1 // MOVD $1230045644216991743, R1 // c1ddbd922122e2f2 + MOVD $0x1111222233334444, R1 // MOVD $1229801703532086340, R1 // 818888d26166a6f24144c4f22122e2f2 + MOVD $0xaaaaffff, R1 // MOVD $2863333375, R1 // e1ff9fd24155b5f2 MOVD $0x11110000, R1 // MOVD $286326784, R1 // 2122a2d2 + MOVD $0xaaaa0000aaaa1111, R1 // MOVD $-6149102338357718767, R1 // 212282d24155b5f24155f5f2 + MOVD $0x1111ffff1111aaaa, R1 // MOVD $1230045644216969898, R1 // a1aa8a922122a2f22122e2f2 MOVD $0, R1 // 010080d2 MOVD $-1, R1 // 01008092 MOVD $0x210000, R0 // MOVD $2162688, R0 // 2004a0d2 diff --git a/src/cmd/asm/internal/asm/testdata/arm64enc.s b/src/cmd/asm/internal/asm/testdata/arm64enc.s index 432ab74493..a2850e2e46 100644 --- a/src/cmd/asm/internal/asm/testdata/arm64enc.s +++ b/src/cmd/asm/internal/asm/testdata/arm64enc.s @@ -56,7 +56,7 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$-8 BFXILW $3, R27, $23, R14 // 6e670333 BFXIL $26, R8, $16, R20 // 14a55ab3 BICW R7@>15, R5, R16 // b03ce70a - BIC R12@>13, R12, R18 // 9235ec8a + BIC R12@>13, R12, R19 // 9335ec8a BICSW R25->20, R3, R20 // 7450b96a BICS R19->12, R1, R23 // 3730b3ea BICS R19, R1, R23 // 370033ea @@ -76,7 +76,7 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$-8 CCMN LE, R30, R12, $6 // c6d34cba CCMPW VS, R29, $15, $7 // a76b4f7a CCMP LE, R7, $19, $3 // e3d853fa - CCMPW HS, R18, R6, $0 // 4022467a + CCMPW HS, R19, R6, $0 // 6022467a CCMP LT, R30, R6, $7 // c7b346fa CCMN MI, ZR, R1, $4 // e44341ba CSINCW HS, ZR, R27, R14 // ee279b1a @@ -118,7 +118,7 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$-8 CRC32H R3, R21, R27 // bb46c31a CRC32W R22, R30, R9 // c94bd61a CRC32X R20, R4, R15 // 8f4cd49a - CRC32CB R18, R27, R22 // 7653d21a + CRC32CB R19, R27, R22 // 7653d31a CRC32CH R21, R0, R20 // 1454d51a CRC32CW R9, R3, R21 // 7558c91a CRC32CX R11, R0, R24 // 185ccb9a @@ -133,7 +133,7 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$-8 CSINVW AL, R23, R21, R5 // e5e2955a CSINV LO, R2, R11, R14 // 4e308bda CSNEGW HS, R16, R29, R10 // 0a269d5a - CSNEG NE, R21, R18, R11 // ab1692da + CSNEG NE, R21, R19, R11 // ab1693da //TODO DC DCPS1 $11378 // 418ea5d4 DCPS2 $10699 // 6239a5d4 @@ -185,23 +185,23 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$-8 MOVBU.P 42(R2), R12 // 4ca44238 MOVBU.W -27(R2), R14 // 4e5c5e38 MOVBU 2916(R24), R3 // 03936d39 - MOVBU (R18)(R14<<0), R23 // 577a6e38 + MOVBU (R19)(R14<<0), R23 // 777a6e38 MOVBU (R2)(R8.SXTX), R19 // 53e86838 MOVBU (R27)(R23), R14 // MOVBU (R27)(R23*1), R14 // 6e6b7738 MOVHU.P 107(R14), R13 // cdb54678 MOVHU.W 192(R3), R2 // 620c4c78 - MOVHU 6844(R4), R18 // 92787579 + MOVHU 6844(R4), R19 // 93787579 MOVHU (R5)(R25.SXTW), R15 // afc87978 - //TODO MOVBW.P 77(R18), R11 // 4bd6c438 + //TODO MOVBW.P 77(R19), R11 // 6bd6c438 MOVB.P 36(RSP), R27 // fb478238 - //TODO MOVBW.W -57(R18), R13 // 4d7edc38 + //TODO MOVBW.W -57(R19), R13 // 6d7edc38 MOVB.W -178(R16), R24 // 18ee9438 //TODO MOVBW 430(R8), R22 // 16b9c639 MOVB 997(R9), R23 // 37958f39 //TODO MOVBW (R2<<1)(R21), R15 // af7ae238 //TODO MOVBW (R26)(R0), R21 // 1568fa38 MOVB (R5)(R15), R16 // MOVB (R5)(R15*1), R16 // b068af38 - MOVB (R18)(R26.SXTW), R19 // 53caba38 + MOVB (R19)(R26.SXTW), R19 // 73caba38 MOVB (R29)(R30), R14 // MOVB (R29)(R30*1), R14 // ae6bbe38 //TODO MOVHW.P 218(R22), R25 // d9a6cd78 MOVH.P 179(R23), R5 // e5368b78 @@ -212,7 +212,7 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$-8 //TODO MOVHW (R22)(R24.SXTX), R4 // c4eaf878 MOVH (R26)(R30.UXTW<<1), ZR // 5f5bbe78 MOVW.P -58(R16), R2 // 02669cb8 - MOVW.W -216(R18), R8 // 488e92b8 + MOVW.W -216(R19), R8 // 688e92b8 MOVW 4764(R23), R10 // ea9e92b9 MOVW (R8)(R3.UXTW), R17 // 1149a3b8 //TODO LDTR -0x1e(R3), R4 // 64285eb8 @@ -297,7 +297,7 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$-8 RET // c0035fd6 REVW R8, R10 // 0a09c05a REV R1, R2 // 220cc0da - REV16W R21, R18 // b206c05a + REV16W R21, R19 // b306c05a REV16 R25, R4 // 2407c0da REV32 R27, R21 // 750bc0da EXTRW $27, R4, R25, R19 // 336f8413 @@ -308,7 +308,7 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$-8 ROR R0, R23, R2 // e22ec09a SBCW R4, R8, R24 // 1801045a SBC R25, R10, R26 // 5a0119da - SBCSW R27, R18, R18 // 52021b7a + SBCSW R27, R19, R19 // 73021b7a SBCS R5, R9, R5 // 250105fa SBFIZW $9, R10, $18, R22 // 56451713 SBFIZ $6, R11, $15, R20 // 74397a93 @@ -337,7 +337,7 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$-8 //TODO STNPW 44(R1), R3, R10 // 2a8c0528 //TODO STNP 0x108(R3), ZR, R7 // 67fc10a8 LDP.P -384(R3), (R22, R26) // 7668e8a8 - LDP.W 280(R8), (R18, R11) // 12add1a9 + LDP.W 280(R8), (R19, R11) // 13add1a9 STP.P (R22, R27), 352(R0) // 166c96a8 STP.W (R17, R11), 96(R8) // 112d86a9 MOVW.P R20, -28(R1) // 34441eb8 @@ -360,22 +360,22 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$-8 MOVB R2, (R29)(R26) // MOVB R2, (R29)(R26*1) // a26b3a38 MOVH R11, -80(R23) // eb021b78 MOVH R11, (R27)(R14.SXTW<<1) // 6bdb2e78 - MOVB R18, (R0)(R4) // MOVB R18, (R0)(R4*1) // 12682438 + MOVB R19, (R0)(R4) // MOVB R19, (R0)(R4*1) // 13682438 MOVB R1, (R6)(R4) // MOVB R1, (R6)(R4*1) // c1682438 MOVH R3, (R11)(R13<<1) // 63792d78 //TODO STTR 55(R4), R29 // 9d7803b8 //TODO STTR 124(R5), R25 // b9c807f8 //TODO STTRB -28(R23), R16 // f04a1e38 - //TODO STTRH 9(R10), R18 // 52990078 + //TODO STTRH 9(R10), R19 // 53990078 STXP (R1, R2), (R3), R10 // 61082ac8 STXP (R1, R2), (RSP), R10 // e10b2ac8 STXPW (R1, R2), (R3), R10 // 61082a88 STXPW (R1, R2), (RSP), R10 // e10b2a88 - STXRW R2, (R19), R18 // 627e1288 + STXRW R2, (R19), R20 // 627e1488 STXR R15, (R21), R13 // af7e0dc8 STXRB R7, (R9), R24 // 277d1808 STXRH R12, (R3), R8 // 6c7c0848 - SUBW R20.UXTW<<2, R23, R18 // f24a344b + SUBW R20.UXTW<<2, R23, R19 // f34a344b SUB R5.SXTW<<2, R1, R26 // 3ac825cb SUB $(1923<<12), R4, R27 // SUB $7876608, R4, R27 // 9b0c5ed1 SUBW $(1923<<12), R4, R27 // SUBW $7876608, R4, R27 // 9b0c5e51 @@ -410,12 +410,12 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$-8 UBFXW $3, R7, $20, R15 // ef580353 UBFX $33, R17, $25, R5 // 25e661d3 UDIVW R8, R21, R15 // af0ac81a - UDIV R2, R18, R21 // 550ac29a + UDIV R2, R19, R21 // 750ac29a UMADDL R0, R20, R17, R17 // 3152a09b UMSUBL R22, R4, R3, R7 // 6790b69b - UMNEGL R3, R18, R1 // 41fea39b + UMNEGL R3, R19, R1 // 61fea39b UMULH R24, R20, R24 // 987ed89b - UMULL R18, R22, R19 // d37eb29b + UMULL R19, R22, R19 // d37eb39b UXTBW R2, R6 // 461c0053 UXTHW R7, R20 // f43c0053 VCNT V0.B8, V0.B8 // 0058200e @@ -471,7 +471,7 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$-8 //TODO FCVTAS F27, R7 // 6703241e //TODO FCVTAS F19, R26 // 7a02249e //TODO FCVTAS F4, R0 // 8000641e - //TODO FCVTAS F3, R18 // 7200649e + //TODO FCVTAS F3, R19 // 7300649e //TODO FCVTAU F18, F28 // 5cca217e //TODO VFCVTAU V30.S4, V27.S4 // dbcb216e //TODO FCVTAU F0, R2 // 0200251e @@ -482,16 +482,16 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$-8 //TODO VFCVTL2 V15.H8, V25.S4 // f979214e //TODO FCVTMS F21, F28 // bcba215e //TODO VFCVTMS V5.D2, V2.D2 // a2b8614e - //TODO FCVTMS F31, R18 // f203301e + //TODO FCVTMS F31, R19 // f303301e //TODO FCVTMS F23, R16 // f002309e //TODO FCVTMS F16, R22 // 1602701e //TODO FCVTMS F14, R19 // d301709e //TODO FCVTMU F14, F8 // c8b9217e //TODO VFCVTMU V7.D2, V1.D2 // e1b8616e //TODO FCVTMU F2, R0 // 4000311e - //TODO FCVTMU F23, R18 // f202319e + //TODO FCVTMU F23, R19 // f302319e //TODO FCVTMU F16, R17 // 1102711e - //TODO FCVTMU F12, R18 // 9201719e + //TODO FCVTMU F12, R19 // 9301719e //TODO VFCVTN V23.D2, V26.S2 // fa6a610e //TODO VFCVTN2 V2.D2, V31.S4 // 5f68614e //TODO FCVTNS F3, F27 // 7ba8215e @@ -540,7 +540,7 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$-8 //TODO FCVTZU $14, F24, R20 // 14cb191e //TODO FCVTZU $6, F25, R17 // 31eb199e //TODO FCVTZU $5, F17, R10 // 2aee591e - //TODO FCVTZU $6, F7, R18 // f2e8599e + //TODO FCVTZU $6, F7, R19 // f3e8599e FCVTZUSW F2, R9 // 4900391e FCVTZUS F12, R29 // 9d01399e FCVTZUDW F27, R22 // 7603791e @@ -682,11 +682,11 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$-8 VLD1.P (R19)(R4), [V24.B8, V25.B8] // VLD1.P (R19)(R4*1), [V24.B8, V25.B8] // 78a2c40c VLD1.P (R20)(R8), [V7.H8, V8.H8, V9.H8] // VLD1.P (R20)(R8*1), [V7.H8, V8.H8, V9.H8] // 8766c84c VLD1.P 32(R30), [V5.B8, V6.B8, V7.B8, V8.B8] // c523df0c - VLD1 (R18), V14.B[15] // 4e1e404d + VLD1 (R19), V14.B[15] // 6e1e404d VLD1 (R29), V0.H[1] // a04b400d VLD1 (R27), V2.S[0] // 6283400d VLD1 (R21), V5.D[1] // a586404d - VLD1.P 1(R18), V10.B[14] // 4a1adf4d + VLD1.P 1(R19), V10.B[14] // 6a1adf4d VLD1.P (R3)(R14), V16.B[11] // VLD1.P (R3)(R14*1), V16.B[11] // 700cce4d VLD1.P 2(R1), V28.H[2] // 3c50df0d VLD1.P (R13)(R20), V9.H[2] // VLD1.P (R13)(R20*1), V9.H[2] // a951d40d diff --git a/src/cmd/asm/internal/lex/input.go b/src/cmd/asm/internal/lex/input.go index 5186635fe7..a43953b515 100644 --- a/src/cmd/asm/internal/lex/input.go +++ b/src/cmd/asm/internal/lex/input.go @@ -139,7 +139,7 @@ func (in *Input) Text() string { return in.text } -// hash processes a # preprocessor directive. It returns true iff it completes. +// hash processes a # preprocessor directive. It reports whether it completes. func (in *Input) hash() bool { // We have a '#'; it must be followed by a known word (define, include, etc.). tok := in.Stack.Next() diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go index 157cd94d65..08d64130df 100644 --- a/src/cmd/cgo/doc.go +++ b/src/cmd/cgo/doc.go @@ -827,6 +827,10 @@ The directives are: possibly version in the dynamic library, and the optional "" names the specific library where the symbol should be found. + On AIX, the library pattern is slightly different. It must be + "lib.a/obj.o" with obj.o the member of this library exporting + this symbol. + In the , # or @ can be used to introduce a symbol version. Examples: diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index a93ff365b0..c203873b13 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -246,7 +246,22 @@ func (p *Package) writeDefs() { init := gccgoInit.String() if init != "" { - fmt.Fprintln(fc, "static void init(void) __attribute__ ((constructor));") + // The init function does nothing but simple + // assignments, so it won't use much stack space, so + // it's OK to not split the stack. Splitting the stack + // can run into a bug in clang (as of 2018-11-09): + // this is a leaf function, and when clang sees a leaf + // function it won't emit the split stack prologue for + // the function. However, if this function refers to a + // non-split-stack function, which will happen if the + // cgo code refers to a C function not compiled with + // -fsplit-stack, then the linker will think that it + // needs to adjust the split stack prologue, but there + // won't be one. Marking the function explicitly + // no_split_stack works around this problem by telling + // the linker that it's OK if there is no split stack + // prologue. + fmt.Fprintln(fc, "static void init(void) __attribute__ ((constructor, no_split_stack));") fmt.Fprintln(fc, "static void init(void) {") fmt.Fprint(fc, init) fmt.Fprintln(fc, "}") diff --git a/src/cmd/compile/fmt_test.go b/src/cmd/compile/fmt_test.go index 6dfdea1a34..05d13b58a5 100644 --- a/src/cmd/compile/fmt_test.go +++ b/src/cmd/compile/fmt_test.go @@ -587,6 +587,7 @@ var knownFormats = map[string]string{ "*cmd/compile/internal/types.Sym %S": "", "*cmd/compile/internal/types.Sym %p": "", "*cmd/compile/internal/types.Sym %v": "", + "*cmd/compile/internal/types.Type %#L": "", "*cmd/compile/internal/types.Type %#v": "", "*cmd/compile/internal/types.Type %+v": "", "*cmd/compile/internal/types.Type %-S": "", @@ -598,6 +599,7 @@ var knownFormats = map[string]string{ "*cmd/compile/internal/types.Type %v": "", "*cmd/internal/obj.Addr %v": "", "*cmd/internal/obj.LSym %v": "", + "*math/big.Float %f": "", "*math/big.Int %#x": "", "*math/big.Int %s": "", "*math/big.Int %v": "", @@ -658,6 +660,7 @@ var knownFormats = map[string]string{ "cmd/compile/internal/ssa.rbrank %d": "", "cmd/compile/internal/ssa.regMask %d": "", "cmd/compile/internal/ssa.register %d": "", + "cmd/compile/internal/syntax.Error %q": "", "cmd/compile/internal/syntax.Expr %#v": "", "cmd/compile/internal/syntax.Node %T": "", "cmd/compile/internal/syntax.Operator %s": "", @@ -705,33 +708,34 @@ var knownFormats = map[string]string{ "interface{} %v": "", "map[*cmd/compile/internal/gc.Node]*cmd/compile/internal/ssa.Value %v": "", "map[cmd/compile/internal/ssa.ID]uint32 %v": "", - "reflect.Type %s": "", - "rune %#U": "", - "rune %c": "", - "string %-*s": "", - "string %-16s": "", - "string %-6s": "", - "string %.*s": "", - "string %q": "", - "string %s": "", - "string %v": "", - "time.Duration %d": "", - "time.Duration %v": "", - "uint %04x": "", - "uint %5d": "", - "uint %d": "", - "uint %x": "", - "uint16 %d": "", - "uint16 %v": "", - "uint16 %x": "", - "uint32 %#x": "", - "uint32 %d": "", - "uint32 %v": "", - "uint32 %x": "", - "uint64 %08x": "", - "uint64 %d": "", - "uint64 %x": "", - "uint8 %d": "", - "uint8 %x": "", - "uintptr %d": "", + "math/big.Accuracy %s": "", + "reflect.Type %s": "", + "rune %#U": "", + "rune %c": "", + "string %-*s": "", + "string %-16s": "", + "string %-6s": "", + "string %.*s": "", + "string %q": "", + "string %s": "", + "string %v": "", + "time.Duration %d": "", + "time.Duration %v": "", + "uint %04x": "", + "uint %5d": "", + "uint %d": "", + "uint %x": "", + "uint16 %d": "", + "uint16 %v": "", + "uint16 %x": "", + "uint32 %#x": "", + "uint32 %d": "", + "uint32 %v": "", + "uint32 %x": "", + "uint64 %08x": "", + "uint64 %d": "", + "uint64 %x": "", + "uint8 %d": "", + "uint8 %x": "", + "uintptr %d": "", } diff --git a/src/cmd/compile/internal/gc/builtin.go b/src/cmd/compile/internal/gc/builtin.go index 325bf4aa0e..4e9f11c8b3 100644 --- a/src/cmd/compile/internal/gc/builtin.go +++ b/src/cmd/compile/internal/gc/builtin.go @@ -51,110 +51,105 @@ var runtimeDecls = [...]struct { {"decoderune", funcTag, 50}, {"countrunes", funcTag, 51}, {"convI2I", funcTag, 52}, - {"convT2E", funcTag, 53}, - {"convT2E16", funcTag, 52}, - {"convT2E32", funcTag, 52}, - {"convT2E64", funcTag, 52}, - {"convT2Estring", funcTag, 52}, - {"convT2Eslice", funcTag, 52}, - {"convT2Enoptr", funcTag, 53}, - {"convT2I", funcTag, 53}, - {"convT2I16", funcTag, 52}, - {"convT2I32", funcTag, 52}, - {"convT2I64", funcTag, 52}, - {"convT2Istring", funcTag, 52}, - {"convT2Islice", funcTag, 52}, - {"convT2Inoptr", funcTag, 53}, + {"convT16", funcTag, 54}, + {"convT32", funcTag, 54}, + {"convT64", funcTag, 54}, + {"convTstring", funcTag, 54}, + {"convTslice", funcTag, 54}, + {"convT2E", funcTag, 55}, + {"convT2Enoptr", funcTag, 55}, + {"convT2I", funcTag, 55}, + {"convT2Inoptr", funcTag, 55}, {"assertE2I", funcTag, 52}, - {"assertE2I2", funcTag, 54}, + {"assertE2I2", funcTag, 56}, {"assertI2I", funcTag, 52}, - {"assertI2I2", funcTag, 54}, - {"panicdottypeE", funcTag, 55}, - {"panicdottypeI", funcTag, 55}, - {"panicnildottype", funcTag, 56}, - {"ifaceeq", funcTag, 59}, - {"efaceeq", funcTag, 59}, - {"fastrand", funcTag, 61}, - {"makemap64", funcTag, 63}, - {"makemap", funcTag, 64}, - {"makemap_small", funcTag, 65}, - {"mapaccess1", funcTag, 66}, - {"mapaccess1_fast32", funcTag, 67}, - {"mapaccess1_fast64", funcTag, 67}, - {"mapaccess1_faststr", funcTag, 67}, - {"mapaccess1_fat", funcTag, 68}, - {"mapaccess2", funcTag, 69}, - {"mapaccess2_fast32", funcTag, 70}, - {"mapaccess2_fast64", funcTag, 70}, - {"mapaccess2_faststr", funcTag, 70}, - {"mapaccess2_fat", funcTag, 71}, - {"mapassign", funcTag, 66}, - {"mapassign_fast32", funcTag, 67}, - {"mapassign_fast32ptr", funcTag, 67}, - {"mapassign_fast64", funcTag, 67}, - {"mapassign_fast64ptr", funcTag, 67}, - {"mapassign_faststr", funcTag, 67}, - {"mapiterinit", funcTag, 72}, - {"mapdelete", funcTag, 72}, - {"mapdelete_fast32", funcTag, 73}, - {"mapdelete_fast64", funcTag, 73}, - {"mapdelete_faststr", funcTag, 73}, - {"mapiternext", funcTag, 74}, - {"mapclear", funcTag, 75}, - {"makechan64", funcTag, 77}, - {"makechan", funcTag, 78}, - {"chanrecv1", funcTag, 80}, - {"chanrecv2", funcTag, 81}, - {"chansend1", funcTag, 83}, + {"assertI2I2", funcTag, 56}, + {"panicdottypeE", funcTag, 57}, + {"panicdottypeI", funcTag, 57}, + {"panicnildottype", funcTag, 58}, + {"ifaceeq", funcTag, 60}, + {"efaceeq", funcTag, 60}, + {"fastrand", funcTag, 62}, + {"makemap64", funcTag, 64}, + {"makemap", funcTag, 65}, + {"makemap_small", funcTag, 66}, + {"mapaccess1", funcTag, 67}, + {"mapaccess1_fast32", funcTag, 68}, + {"mapaccess1_fast64", funcTag, 68}, + {"mapaccess1_faststr", funcTag, 68}, + {"mapaccess1_fat", funcTag, 69}, + {"mapaccess2", funcTag, 70}, + {"mapaccess2_fast32", funcTag, 71}, + {"mapaccess2_fast64", funcTag, 71}, + {"mapaccess2_faststr", funcTag, 71}, + {"mapaccess2_fat", funcTag, 72}, + {"mapassign", funcTag, 67}, + {"mapassign_fast32", funcTag, 68}, + {"mapassign_fast32ptr", funcTag, 68}, + {"mapassign_fast64", funcTag, 68}, + {"mapassign_fast64ptr", funcTag, 68}, + {"mapassign_faststr", funcTag, 68}, + {"mapiterinit", funcTag, 73}, + {"mapdelete", funcTag, 73}, + {"mapdelete_fast32", funcTag, 74}, + {"mapdelete_fast64", funcTag, 74}, + {"mapdelete_faststr", funcTag, 74}, + {"mapiternext", funcTag, 75}, + {"mapclear", funcTag, 76}, + {"makechan64", funcTag, 78}, + {"makechan", funcTag, 79}, + {"chanrecv1", funcTag, 81}, + {"chanrecv2", funcTag, 82}, + {"chansend1", funcTag, 84}, {"closechan", funcTag, 23}, - {"writeBarrier", varTag, 85}, - {"typedmemmove", funcTag, 86}, - {"typedmemclr", funcTag, 87}, - {"typedslicecopy", funcTag, 88}, - {"selectnbsend", funcTag, 89}, - {"selectnbrecv", funcTag, 90}, - {"selectnbrecv2", funcTag, 92}, - {"selectsetpc", funcTag, 56}, - {"selectgo", funcTag, 93}, + {"writeBarrier", varTag, 86}, + {"typedmemmove", funcTag, 87}, + {"typedmemclr", funcTag, 88}, + {"typedslicecopy", funcTag, 89}, + {"selectnbsend", funcTag, 90}, + {"selectnbrecv", funcTag, 91}, + {"selectnbrecv2", funcTag, 93}, + {"selectsetpc", funcTag, 58}, + {"selectgo", funcTag, 94}, {"block", funcTag, 5}, - {"makeslice", funcTag, 94}, - {"makeslice64", funcTag, 95}, - {"growslice", funcTag, 97}, - {"memmove", funcTag, 98}, - {"memclrNoHeapPointers", funcTag, 99}, - {"memclrHasPointers", funcTag, 99}, - {"memequal", funcTag, 100}, - {"memequal8", funcTag, 101}, - {"memequal16", funcTag, 101}, - {"memequal32", funcTag, 101}, - {"memequal64", funcTag, 101}, - {"memequal128", funcTag, 101}, - {"int64div", funcTag, 102}, - {"uint64div", funcTag, 103}, - {"int64mod", funcTag, 102}, - {"uint64mod", funcTag, 103}, - {"float64toint64", funcTag, 104}, - {"float64touint64", funcTag, 105}, - {"float64touint32", funcTag, 106}, - {"int64tofloat64", funcTag, 107}, - {"uint64tofloat64", funcTag, 108}, - {"uint32tofloat64", funcTag, 109}, - {"complex128div", funcTag, 110}, - {"racefuncenter", funcTag, 111}, + {"makeslice", funcTag, 95}, + {"makeslice64", funcTag, 96}, + {"growslice", funcTag, 98}, + {"memmove", funcTag, 99}, + {"memclrNoHeapPointers", funcTag, 100}, + {"memclrHasPointers", funcTag, 100}, + {"memequal", funcTag, 101}, + {"memequal8", funcTag, 102}, + {"memequal16", funcTag, 102}, + {"memequal32", funcTag, 102}, + {"memequal64", funcTag, 102}, + {"memequal128", funcTag, 102}, + {"int64div", funcTag, 103}, + {"uint64div", funcTag, 104}, + {"int64mod", funcTag, 103}, + {"uint64mod", funcTag, 104}, + {"float64toint64", funcTag, 105}, + {"float64touint64", funcTag, 106}, + {"float64touint32", funcTag, 107}, + {"int64tofloat64", funcTag, 108}, + {"uint64tofloat64", funcTag, 109}, + {"uint32tofloat64", funcTag, 110}, + {"complex128div", funcTag, 111}, + {"racefuncenter", funcTag, 112}, {"racefuncenterfp", funcTag, 5}, {"racefuncexit", funcTag, 5}, - {"raceread", funcTag, 111}, - {"racewrite", funcTag, 111}, - {"racereadrange", funcTag, 112}, - {"racewriterange", funcTag, 112}, - {"msanread", funcTag, 112}, - {"msanwrite", funcTag, 112}, + {"raceread", funcTag, 112}, + {"racewrite", funcTag, 112}, + {"racereadrange", funcTag, 113}, + {"racewriterange", funcTag, 113}, + {"msanread", funcTag, 113}, + {"msanwrite", funcTag, 113}, {"support_popcnt", varTag, 11}, {"support_sse41", varTag, 11}, } func runtimeTypes() []*types.Type { - var typs [113]*types.Type + var typs [114]*types.Type typs[0] = types.Bytetype typs[1] = types.NewPtr(typs[0]) typs[2] = types.Types[TANY] @@ -208,65 +203,66 @@ func runtimeTypes() []*types.Type { typs[50] = functype(nil, []*Node{anonfield(typs[21]), anonfield(typs[32])}, []*Node{anonfield(typs[40]), anonfield(typs[32])}) typs[51] = functype(nil, []*Node{anonfield(typs[21])}, []*Node{anonfield(typs[32])}) typs[52] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2])}, []*Node{anonfield(typs[2])}) - typs[53] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, []*Node{anonfield(typs[2])}) - typs[54] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2])}, []*Node{anonfield(typs[2]), anonfield(typs[11])}) - typs[55] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[1]), anonfield(typs[1])}, nil) - typs[56] = functype(nil, []*Node{anonfield(typs[1])}, nil) - typs[57] = types.NewPtr(typs[47]) - typs[58] = types.Types[TUNSAFEPTR] - typs[59] = functype(nil, []*Node{anonfield(typs[57]), anonfield(typs[58]), anonfield(typs[58])}, []*Node{anonfield(typs[11])}) - typs[60] = types.Types[TUINT32] - typs[61] = functype(nil, nil, []*Node{anonfield(typs[60])}) - typs[62] = types.NewMap(typs[2], typs[2]) - typs[63] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[3])}, []*Node{anonfield(typs[62])}) - typs[64] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[32]), anonfield(typs[3])}, []*Node{anonfield(typs[62])}) - typs[65] = functype(nil, nil, []*Node{anonfield(typs[62])}) - typs[66] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[62]), anonfield(typs[3])}, []*Node{anonfield(typs[3])}) - typs[67] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[62]), anonfield(typs[2])}, []*Node{anonfield(typs[3])}) - typs[68] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[62]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3])}) - typs[69] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[62]), anonfield(typs[3])}, []*Node{anonfield(typs[3]), anonfield(typs[11])}) - typs[70] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[62]), anonfield(typs[2])}, []*Node{anonfield(typs[3]), anonfield(typs[11])}) - typs[71] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[62]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3]), anonfield(typs[11])}) - typs[72] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[62]), anonfield(typs[3])}, nil) - typs[73] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[62]), anonfield(typs[2])}, nil) - typs[74] = functype(nil, []*Node{anonfield(typs[3])}, nil) - typs[75] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[62])}, nil) - typs[76] = types.NewChan(typs[2], types.Cboth) - typs[77] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15])}, []*Node{anonfield(typs[76])}) - typs[78] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[32])}, []*Node{anonfield(typs[76])}) - typs[79] = types.NewChan(typs[2], types.Crecv) - typs[80] = functype(nil, []*Node{anonfield(typs[79]), anonfield(typs[3])}, nil) - typs[81] = functype(nil, []*Node{anonfield(typs[79]), anonfield(typs[3])}, []*Node{anonfield(typs[11])}) - typs[82] = types.NewChan(typs[2], types.Csend) - typs[83] = functype(nil, []*Node{anonfield(typs[82]), anonfield(typs[3])}, nil) - typs[84] = types.NewArray(typs[0], 3) - typs[85] = tostruct([]*Node{namedfield("enabled", typs[11]), namedfield("pad", typs[84]), namedfield("needed", typs[11]), namedfield("cgo", typs[11]), namedfield("alignme", typs[17])}) - typs[86] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3]), anonfield(typs[3])}, nil) - typs[87] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, nil) - typs[88] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2]), anonfield(typs[2])}, []*Node{anonfield(typs[32])}) - typs[89] = functype(nil, []*Node{anonfield(typs[82]), anonfield(typs[3])}, []*Node{anonfield(typs[11])}) - typs[90] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[79])}, []*Node{anonfield(typs[11])}) - typs[91] = types.NewPtr(typs[11]) - typs[92] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[91]), anonfield(typs[79])}, []*Node{anonfield(typs[11])}) - typs[93] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[1]), anonfield(typs[32])}, []*Node{anonfield(typs[32]), anonfield(typs[11])}) - typs[94] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[32]), anonfield(typs[32])}, []*Node{anonfield(typs[58])}) - typs[95] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[15])}, []*Node{anonfield(typs[58])}) - typs[96] = types.NewSlice(typs[2]) - typs[97] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[96]), anonfield(typs[32])}, []*Node{anonfield(typs[96])}) - typs[98] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3]), anonfield(typs[47])}, nil) - typs[99] = functype(nil, []*Node{anonfield(typs[58]), anonfield(typs[47])}, nil) - typs[100] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3]), anonfield(typs[47])}, []*Node{anonfield(typs[11])}) - typs[101] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3])}, []*Node{anonfield(typs[11])}) - typs[102] = functype(nil, []*Node{anonfield(typs[15]), anonfield(typs[15])}, []*Node{anonfield(typs[15])}) - typs[103] = functype(nil, []*Node{anonfield(typs[17]), anonfield(typs[17])}, []*Node{anonfield(typs[17])}) - typs[104] = functype(nil, []*Node{anonfield(typs[13])}, []*Node{anonfield(typs[15])}) - typs[105] = functype(nil, []*Node{anonfield(typs[13])}, []*Node{anonfield(typs[17])}) - typs[106] = functype(nil, []*Node{anonfield(typs[13])}, []*Node{anonfield(typs[60])}) - typs[107] = functype(nil, []*Node{anonfield(typs[15])}, []*Node{anonfield(typs[13])}) - typs[108] = functype(nil, []*Node{anonfield(typs[17])}, []*Node{anonfield(typs[13])}) - typs[109] = functype(nil, []*Node{anonfield(typs[60])}, []*Node{anonfield(typs[13])}) - typs[110] = functype(nil, []*Node{anonfield(typs[19]), anonfield(typs[19])}, []*Node{anonfield(typs[19])}) - typs[111] = functype(nil, []*Node{anonfield(typs[47])}, nil) - typs[112] = functype(nil, []*Node{anonfield(typs[47]), anonfield(typs[47])}, nil) + typs[53] = types.Types[TUNSAFEPTR] + typs[54] = functype(nil, []*Node{anonfield(typs[2])}, []*Node{anonfield(typs[53])}) + typs[55] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, []*Node{anonfield(typs[2])}) + typs[56] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2])}, []*Node{anonfield(typs[2]), anonfield(typs[11])}) + typs[57] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[1]), anonfield(typs[1])}, nil) + typs[58] = functype(nil, []*Node{anonfield(typs[1])}, nil) + typs[59] = types.NewPtr(typs[47]) + typs[60] = functype(nil, []*Node{anonfield(typs[59]), anonfield(typs[53]), anonfield(typs[53])}, []*Node{anonfield(typs[11])}) + typs[61] = types.Types[TUINT32] + typs[62] = functype(nil, nil, []*Node{anonfield(typs[61])}) + typs[63] = types.NewMap(typs[2], typs[2]) + typs[64] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[3])}, []*Node{anonfield(typs[63])}) + typs[65] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[32]), anonfield(typs[3])}, []*Node{anonfield(typs[63])}) + typs[66] = functype(nil, nil, []*Node{anonfield(typs[63])}) + typs[67] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[63]), anonfield(typs[3])}, []*Node{anonfield(typs[3])}) + typs[68] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[63]), anonfield(typs[2])}, []*Node{anonfield(typs[3])}) + typs[69] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[63]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3])}) + typs[70] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[63]), anonfield(typs[3])}, []*Node{anonfield(typs[3]), anonfield(typs[11])}) + typs[71] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[63]), anonfield(typs[2])}, []*Node{anonfield(typs[3]), anonfield(typs[11])}) + typs[72] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[63]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3]), anonfield(typs[11])}) + typs[73] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[63]), anonfield(typs[3])}, nil) + typs[74] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[63]), anonfield(typs[2])}, nil) + typs[75] = functype(nil, []*Node{anonfield(typs[3])}, nil) + typs[76] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[63])}, nil) + typs[77] = types.NewChan(typs[2], types.Cboth) + typs[78] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15])}, []*Node{anonfield(typs[77])}) + typs[79] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[32])}, []*Node{anonfield(typs[77])}) + typs[80] = types.NewChan(typs[2], types.Crecv) + typs[81] = functype(nil, []*Node{anonfield(typs[80]), anonfield(typs[3])}, nil) + typs[82] = functype(nil, []*Node{anonfield(typs[80]), anonfield(typs[3])}, []*Node{anonfield(typs[11])}) + typs[83] = types.NewChan(typs[2], types.Csend) + typs[84] = functype(nil, []*Node{anonfield(typs[83]), anonfield(typs[3])}, nil) + typs[85] = types.NewArray(typs[0], 3) + typs[86] = tostruct([]*Node{namedfield("enabled", typs[11]), namedfield("pad", typs[85]), namedfield("needed", typs[11]), namedfield("cgo", typs[11]), namedfield("alignme", typs[17])}) + typs[87] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3]), anonfield(typs[3])}, nil) + typs[88] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, nil) + typs[89] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2]), anonfield(typs[2])}, []*Node{anonfield(typs[32])}) + typs[90] = functype(nil, []*Node{anonfield(typs[83]), anonfield(typs[3])}, []*Node{anonfield(typs[11])}) + typs[91] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[80])}, []*Node{anonfield(typs[11])}) + typs[92] = types.NewPtr(typs[11]) + typs[93] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[92]), anonfield(typs[80])}, []*Node{anonfield(typs[11])}) + typs[94] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[1]), anonfield(typs[32])}, []*Node{anonfield(typs[32]), anonfield(typs[11])}) + typs[95] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[32]), anonfield(typs[32])}, []*Node{anonfield(typs[53])}) + typs[96] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[15])}, []*Node{anonfield(typs[53])}) + typs[97] = types.NewSlice(typs[2]) + typs[98] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[97]), anonfield(typs[32])}, []*Node{anonfield(typs[97])}) + typs[99] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3]), anonfield(typs[47])}, nil) + typs[100] = functype(nil, []*Node{anonfield(typs[53]), anonfield(typs[47])}, nil) + typs[101] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3]), anonfield(typs[47])}, []*Node{anonfield(typs[11])}) + typs[102] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3])}, []*Node{anonfield(typs[11])}) + typs[103] = functype(nil, []*Node{anonfield(typs[15]), anonfield(typs[15])}, []*Node{anonfield(typs[15])}) + typs[104] = functype(nil, []*Node{anonfield(typs[17]), anonfield(typs[17])}, []*Node{anonfield(typs[17])}) + typs[105] = functype(nil, []*Node{anonfield(typs[13])}, []*Node{anonfield(typs[15])}) + typs[106] = functype(nil, []*Node{anonfield(typs[13])}, []*Node{anonfield(typs[17])}) + typs[107] = functype(nil, []*Node{anonfield(typs[13])}, []*Node{anonfield(typs[61])}) + typs[108] = functype(nil, []*Node{anonfield(typs[15])}, []*Node{anonfield(typs[13])}) + typs[109] = functype(nil, []*Node{anonfield(typs[17])}, []*Node{anonfield(typs[13])}) + typs[110] = functype(nil, []*Node{anonfield(typs[61])}, []*Node{anonfield(typs[13])}) + typs[111] = functype(nil, []*Node{anonfield(typs[19]), anonfield(typs[19])}, []*Node{anonfield(typs[19])}) + typs[112] = functype(nil, []*Node{anonfield(typs[47])}, nil) + typs[113] = functype(nil, []*Node{anonfield(typs[47]), anonfield(typs[47])}, nil) return typs[:] } diff --git a/src/cmd/compile/internal/gc/builtin/runtime.go b/src/cmd/compile/internal/gc/builtin/runtime.go index e6d174bc4b..1eaf332e50 100644 --- a/src/cmd/compile/internal/gc/builtin/runtime.go +++ b/src/cmd/compile/internal/gc/builtin/runtime.go @@ -61,23 +61,23 @@ func slicestringcopy(to any, fr any) int func decoderune(string, int) (retv rune, retk int) func countrunes(string) int -// interface conversions +// Non-empty-interface to non-empty-interface conversion. func convI2I(typ *byte, elem any) (ret any) +// Specialized type-to-interface conversion. +// These return only a data pointer. +func convT16(val any) unsafe.Pointer // val must be uint16-like (same size and alignment as a uint16) +func convT32(val any) unsafe.Pointer // val must be uint32-like (same size and alignment as a uint32) +func convT64(val any) unsafe.Pointer // val must be uint64-like (same size and alignment as a uint64 and contains no pointers) +func convTstring(val any) unsafe.Pointer // val must be a string +func convTslice(val any) unsafe.Pointer // val must be a slice + +// Type to empty-interface conversion. func convT2E(typ *byte, elem *any) (ret any) -func convT2E16(typ *byte, val any) (ret any) -func convT2E32(typ *byte, val any) (ret any) -func convT2E64(typ *byte, val any) (ret any) -func convT2Estring(typ *byte, val any) (ret any) // val must be a string -func convT2Eslice(typ *byte, val any) (ret any) // val must be a slice func convT2Enoptr(typ *byte, elem *any) (ret any) +// Type to non-empty-interface conversion. func convT2I(tab *byte, elem *any) (ret any) -func convT2I16(tab *byte, val any) (ret any) -func convT2I32(tab *byte, val any) (ret any) -func convT2I64(tab *byte, val any) (ret any) -func convT2Istring(tab *byte, val any) (ret any) // val must be a string -func convT2Islice(tab *byte, val any) (ret any) // val must be a slice func convT2Inoptr(tab *byte, elem *any) (ret any) // interface type assertions x.(T) diff --git a/src/cmd/compile/internal/gc/closure.go b/src/cmd/compile/internal/gc/closure.go index ec19f5c112..5123df8e9d 100644 --- a/src/cmd/compile/internal/gc/closure.go +++ b/src/cmd/compile/internal/gc/closure.go @@ -314,7 +314,7 @@ func transformclosure(xfunc *Node) { lineno = lno } -// hasemptycvars returns true iff closure clo has an +// hasemptycvars reports whether closure clo has an // empty list of captured vars. func hasemptycvars(clo *Node) bool { xfunc := clo.Func.Closure diff --git a/src/cmd/compile/internal/gc/dwinl.go b/src/cmd/compile/internal/gc/dwinl.go index 51251c9139..ade76f40f8 100644 --- a/src/cmd/compile/internal/gc/dwinl.go +++ b/src/cmd/compile/internal/gc/dwinl.go @@ -300,7 +300,7 @@ func beginRange(calls []dwarf.InlCall, p *obj.Prog, ii int, imap map[int]int) *d } callIdx, found := imap[ii] if !found { - Fatalf("internal error: can't find inlIndex %d in imap for prog at %d\n", ii, p.Pc) + Fatalf("can't find inlIndex %d in imap for prog at %d\n", ii, p.Pc) } call := &calls[callIdx] diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go index 0f4b6c9936..16b81e6a88 100644 --- a/src/cmd/compile/internal/gc/fmt.go +++ b/src/cmd/compile/internal/gc/fmt.go @@ -1114,6 +1114,7 @@ var opprec = []int{ OSLICEARR: 8, OSLICE3: 8, OSLICE3ARR: 8, + OSLICEHEADER: 8, ODOTINTER: 8, ODOTMETH: 8, ODOTPTR: 8, @@ -1393,6 +1394,12 @@ func (n *Node) exprfmt(s fmt.State, prec int, mode fmtMode) { } fmt.Fprint(s, "]") + case OSLICEHEADER: + if n.List.Len() != 2 { + Fatalf("bad OSLICEHEADER list length %d", n.List.Len()) + } + mode.Fprintf(s, "sliceheader{%v,%v,%v}", n.Left, n.List.First(), n.List.Second()) + case OCOPY, OCOMPLEX: mode.Fprintf(s, "%#v(%v, %v)", n.Op, n.Left, n.Right) diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go index cfd695097f..471746ed7d 100644 --- a/src/cmd/compile/internal/gc/go.go +++ b/src/cmd/compile/internal/gc/go.go @@ -147,7 +147,6 @@ var asmhdr string var simtype [NTYPE]types.EType var ( - isforw [NTYPE]bool isInt [NTYPE]bool isFloat [NTYPE]bool isComplex [NTYPE]bool diff --git a/src/cmd/compile/internal/gc/gsubr.go b/src/cmd/compile/internal/gc/gsubr.go index f39ffc7365..16602b9988 100644 --- a/src/cmd/compile/internal/gc/gsubr.go +++ b/src/cmd/compile/internal/gc/gsubr.go @@ -185,24 +185,6 @@ func (pp *Progs) settext(fn *Node) { ptxt.From.Type = obj.TYPE_MEM ptxt.From.Name = obj.NAME_EXTERN ptxt.From.Sym = fn.Func.lsym - - p := pp.Prog(obj.AFUNCDATA) - Addrconst(&p.From, objabi.FUNCDATA_ArgsPointerMaps) - p.To.Type = obj.TYPE_MEM - p.To.Name = obj.NAME_EXTERN - p.To.Sym = &fn.Func.lsym.Func.GCArgs - - p = pp.Prog(obj.AFUNCDATA) - Addrconst(&p.From, objabi.FUNCDATA_LocalsPointerMaps) - p.To.Type = obj.TYPE_MEM - p.To.Name = obj.NAME_EXTERN - p.To.Sym = &fn.Func.lsym.Func.GCLocals - - p = pp.Prog(obj.AFUNCDATA) - Addrconst(&p.From, objabi.FUNCDATA_RegPointerMaps) - p.To.Type = obj.TYPE_MEM - p.To.Name = obj.NAME_EXTERN - p.To.Sym = &fn.Func.lsym.Func.GCRegs } func (f *Func) initLSym() { diff --git a/src/cmd/compile/internal/gc/iexport.go b/src/cmd/compile/internal/gc/iexport.go index d21378df4a..e77ca9a6c1 100644 --- a/src/cmd/compile/internal/gc/iexport.go +++ b/src/cmd/compile/internal/gc/iexport.go @@ -909,7 +909,7 @@ func (w *exportWriter) mpfloat(f *big.Float, typ *types.Type) { manti, acc := mant.Int(nil) if acc != big.Exact { - Fatalf("exporter: internal error") + Fatalf("mantissa scaling failed for %f (%s)", f, acc) } w.mpint(manti, typ) if manti.Sign() != 0 { diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go index 0b91d49188..b26758a77e 100644 --- a/src/cmd/compile/internal/gc/inl.go +++ b/src/cmd/compile/internal/gc/inl.go @@ -38,9 +38,10 @@ import ( const ( inlineMaxBudget = 80 inlineExtraAppendCost = 0 - inlineExtraCallCost = inlineMaxBudget // default is do not inline, -l=4 enables by using 1 instead. - inlineExtraPanicCost = 1 // do not penalize inlining panics. - inlineExtraThrowCost = inlineMaxBudget // with current (2018-05/1.11) code, inlining runtime.throw does not help. + // default is to inline if there's at most one call. -l=4 overrides this by using 1 instead. + inlineExtraCallCost = inlineMaxBudget * 3 / 4 + inlineExtraPanicCost = 1 // do not penalize inlining panics. + inlineExtraThrowCost = inlineMaxBudget // with current (2018-05/1.11) code, inlining runtime.throw does not help. inlineBigFunctionNodes = 5000 // Functions with this many nodes are considered "big". inlineBigFunctionMaxCost = 20 // Max cost of inlinee when inlining into a "big" function. @@ -141,6 +142,13 @@ func caninl(fn *Node) { return } + // If marked as "go:uintptrescapes", don't inline, since the + // escape information is lost during inlining. + if fn.Func.Pragma&UintptrEscapes != 0 { + reason = "marked as having an escaping uintptr argument" + return + } + // The nowritebarrierrec checker currently works at function // granularity, so inlining yeswritebarrierrec functions can // confuse it (#22342). As a workaround, disallow inlining diff --git a/src/cmd/compile/internal/gc/inl_test.go b/src/cmd/compile/internal/gc/inl_test.go index ac86cda2b8..5a8c19e2cb 100644 --- a/src/cmd/compile/internal/gc/inl_test.go +++ b/src/cmd/compile/internal/gc/inl_test.go @@ -55,6 +55,7 @@ func TestIntendedInlining(t *testing.T) { "isDirectIface", "itabHashFunc", "noescape", + "pcvalueCacheKey", "readUnaligned32", "readUnaligned64", "releasem", diff --git a/src/cmd/compile/internal/gc/lex.go b/src/cmd/compile/internal/gc/lex.go index 3b302a5124..bd68ebffff 100644 --- a/src/cmd/compile/internal/gc/lex.go +++ b/src/cmd/compile/internal/gc/lex.go @@ -114,6 +114,14 @@ func (p *noder) pragcgo(pos syntax.Pos, text string) { case len(f) == 3 && !isQuoted(f[1]) && !isQuoted(f[2]): case len(f) == 4 && !isQuoted(f[1]) && !isQuoted(f[2]) && isQuoted(f[3]): f[3] = strings.Trim(f[3], `"`) + if objabi.GOOS == "aix" && f[3] != "" { + // On Aix, library pattern must be "lib.a/object.o" + n := strings.Split(f[3], "/") + if len(n) != 2 || !strings.HasSuffix(n[0], ".a") || !strings.HasSuffix(n[1], ".o") { + p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_import_dynamic local [remote ["lib.a/object.o"]]`}) + return + } + } default: p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_import_dynamic local [remote ["library"]]`}) return diff --git a/src/cmd/compile/internal/gc/lex_test.go b/src/cmd/compile/internal/gc/lex_test.go index fecf570fa1..e05726c9f3 100644 --- a/src/cmd/compile/internal/gc/lex_test.go +++ b/src/cmd/compile/internal/gc/lex_test.go @@ -7,6 +7,7 @@ package gc import ( "cmd/compile/internal/syntax" "reflect" + "runtime" "testing" ) @@ -49,10 +50,12 @@ func TestPragmaFields(t *testing.T) { } func TestPragcgo(t *testing.T) { - var tests = []struct { + type testStruct struct { in string want []string - }{ + } + + var tests = []testStruct{ {`go:cgo_export_dynamic local`, []string{`cgo_export_dynamic`, `local`}}, {`go:cgo_export_dynamic local remote`, []string{`cgo_export_dynamic`, `local`, `remote`}}, {`go:cgo_export_dynamic local' remote'`, []string{`cgo_export_dynamic`, `local'`, `remote'`}}, @@ -61,8 +64,6 @@ func TestPragcgo(t *testing.T) { {`go:cgo_export_static local' remote'`, []string{`cgo_export_static`, `local'`, `remote'`}}, {`go:cgo_import_dynamic local`, []string{`cgo_import_dynamic`, `local`}}, {`go:cgo_import_dynamic local remote`, []string{`cgo_import_dynamic`, `local`, `remote`}}, - {`go:cgo_import_dynamic local remote "library"`, []string{`cgo_import_dynamic`, `local`, `remote`, `library`}}, - {`go:cgo_import_dynamic local' remote' "lib rary"`, []string{`cgo_import_dynamic`, `local'`, `remote'`, `lib rary`}}, {`go:cgo_import_static local`, []string{`cgo_import_static`, `local`}}, {`go:cgo_import_static local'`, []string{`cgo_import_static`, `local'`}}, {`go:cgo_dynamic_linker "/path/"`, []string{`cgo_dynamic_linker`, `/path/`}}, @@ -71,17 +72,50 @@ func TestPragcgo(t *testing.T) { {`go:cgo_ldflag "a rg"`, []string{`cgo_ldflag`, `a rg`}}, } + if runtime.GOOS != "aix" { + tests = append(tests, []testStruct{ + {`go:cgo_import_dynamic local remote "library"`, []string{`cgo_import_dynamic`, `local`, `remote`, `library`}}, + {`go:cgo_import_dynamic local' remote' "lib rary"`, []string{`cgo_import_dynamic`, `local'`, `remote'`, `lib rary`}}, + }...) + } else { + // cgo_import_dynamic with a library is slightly different on AIX + // as the library field must follow the pattern [libc.a/object.o]. + tests = append(tests, []testStruct{ + {`go:cgo_import_dynamic local remote "lib.a/obj.o"`, []string{`cgo_import_dynamic`, `local`, `remote`, `lib.a/obj.o`}}, + // This test must fail. + {`go:cgo_import_dynamic local' remote' "library"`, []string{`: usage: //go:cgo_import_dynamic local [remote ["lib.a/object.o"]]`}}, + }...) + + } + var p noder var nopos syntax.Pos for _, tt := range tests { - p.pragcgobuf = nil - p.pragcgo(nopos, tt.in) - got := p.pragcgobuf - want := [][]string{tt.want} - if !reflect.DeepEqual(got, want) { - t.Errorf("pragcgo(%q) = %q; want %q", tt.in, got, want) - continue + p.err = make(chan syntax.Error) + gotch := make(chan [][]string) + go func() { + p.pragcgobuf = nil + p.pragcgo(nopos, tt.in) + if p.pragcgobuf != nil { + gotch <- p.pragcgobuf + } + }() + + select { + case e := <-p.err: + want := tt.want[0] + if e.Error() != want { + t.Errorf("pragcgo(%q) = %q; want %q", tt.in, e, want) + continue + } + case got := <-gotch: + want := [][]string{tt.want} + if !reflect.DeepEqual(got, want) { + t.Errorf("pragcgo(%q) = %q; want %q", tt.in, got, want) + continue + } } + } } diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 8e1b3b8fca..cdc461aac1 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -229,6 +229,9 @@ func Main(archInit func(*Arch)) { flag.BoolVar(&flag_race, "race", false, "enable race detector") } objabi.Flagcount("s", "warn about composite literals that can be simplified", &Debug['s']) + if enableTrace { + flag.BoolVar(&trace, "t", false, "trace type-checking") + } flag.StringVar(&pathPrefix, "trimpath", "", "remove `prefix` from recorded source file paths") flag.BoolVar(&Debug_vlog, "v", false, "increase debug verbosity") objabi.Flagcount("w", "debug type checking", &Debug['w']) @@ -488,13 +491,17 @@ func Main(archInit func(*Arch)) { // Phase 1: const, type, and names and types of funcs. // This will gather all the information about types // and methods but doesn't depend on any of it. + // + // We also defer type alias declarations until phase 2 + // to avoid cycles like #18640. + // TODO(gri) Remove this again once we have a fix for #25838. defercheckwidth() // Don't use range--typecheck can add closures to xtop. timings.Start("fe", "typecheck", "top1") for i := 0; i < len(xtop); i++ { n := xtop[i] - if op := n.Op; op != ODCL && op != OAS && op != OAS2 { + if op := n.Op; op != ODCL && op != OAS && op != OAS2 && (op != ODCLTYPE || !n.Left.Name.Param.Alias) { xtop[i] = typecheck(n, Etop) } } @@ -506,7 +513,7 @@ func Main(archInit func(*Arch)) { timings.Start("fe", "typecheck", "top2") for i := 0; i < len(xtop); i++ { n := xtop[i] - if op := n.Op; op == ODCL || op == OAS || op == OAS2 { + if op := n.Op; op == ODCL || op == OAS || op == OAS2 || op == ODCLTYPE && n.Left.Name.Param.Alias { xtop[i] = typecheck(n, Etop) } } diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go index a9dd092b67..5630e12ace 100644 --- a/src/cmd/compile/internal/gc/obj.go +++ b/src/cmd/compile/internal/gc/obj.go @@ -273,22 +273,18 @@ func dumpglobls() { } // addGCLocals adds gcargs, gclocals, gcregs, and stack object symbols to Ctxt.Data. -// It takes care not to add any duplicates. -// Though the object file format handles duplicates efficiently, -// storing only a single copy of the data, -// failure to remove these duplicates adds a few percent to object file size. +// +// This is done during the sequential phase after compilation, since +// global symbols can't be declared during parallel compilation. func addGCLocals() { - seen := make(map[string]bool) for _, s := range Ctxt.Text { if s.Func == nil { continue } - for _, gcsym := range []*obj.LSym{&s.Func.GCArgs, &s.Func.GCLocals, &s.Func.GCRegs} { - if seen[gcsym.Name] { - continue + for _, gcsym := range []*obj.LSym{s.Func.GCArgs, s.Func.GCLocals, s.Func.GCRegs} { + if gcsym != nil && !gcsym.OnList() { + ggloblsym(gcsym, int32(len(gcsym.P)), obj.RODATA|obj.DUPOK) } - Ctxt.Data = append(Ctxt.Data, gcsym) - seen[gcsym.Name] = true } if x := s.Func.StackObjects; x != nil { ggloblsym(x, int32(len(x.P)), obj.RODATA|obj.LOCAL) diff --git a/src/cmd/compile/internal/gc/op_string.go b/src/cmd/compile/internal/gc/op_string.go index 83283c72c6..34c3249d90 100644 --- a/src/cmd/compile/internal/gc/op_string.go +++ b/src/cmd/compile/internal/gc/op_string.go @@ -4,9 +4,9 @@ package gc import "strconv" -const _Op_name = "XXXNAMENONAMETYPEPACKLITERALADDSUBORXORADDSTRADDRANDANDAPPENDARRAYBYTESTRARRAYBYTESTRTMPARRAYRUNESTRSTRARRAYBYTESTRARRAYBYTETMPSTRARRAYRUNEASAS2AS2FUNCAS2RECVAS2MAPRAS2DOTTYPEASOPCALLCALLFUNCCALLMETHCALLINTERCALLPARTCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVNOPCOPYDCLDCLFUNCDCLFIELDDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTINDINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMULDIVMODLSHRSHANDANDNOTNEWNOTCOMPLUSMINUSORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICEHEADERSLICE3SLICE3ARRRECOVERRECVRUNESTRSELRECVSELRECV2IOTAREALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFBLOCKBREAKCASEXCASECONTINUEDEFEREMPTYFALLFORFORUNTILGOTOIFLABELPROCRANGERETURNSELECTSWITCHTYPESWTCHANTMAPTSTRUCTTINTERTFUNCTARRAYDDDDDDARGINLCALLEFACEITABIDATASPTRCLOSUREVARCFUNCCHECKNILVARDEFVARKILLVARLIVEINDREGSPRETJMPGETGEND" +const _Op_name = "XXXNAMENONAMETYPEPACKLITERALADDSUBORXORADDSTRADDRANDANDAPPENDARRAYBYTESTRARRAYBYTESTRTMPARRAYRUNESTRSTRARRAYBYTESTRARRAYBYTETMPSTRARRAYRUNEASAS2AS2FUNCAS2RECVAS2MAPRAS2DOTTYPEASOPCALLCALLFUNCCALLMETHCALLINTERCALLPARTCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVNOPCOPYDCLDCLFUNCDCLFIELDDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTINDINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMULDIVMODLSHRSHANDANDNOTNEWNOTCOMPLUSMINUSORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERRECOVERRECVRUNESTRSELRECVSELRECV2IOTAREALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFBLOCKBREAKCASEXCASECONTINUEDEFEREMPTYFALLFORFORUNTILGOTOIFLABELPROCRANGERETURNSELECTSWITCHTYPESWTCHANTMAPTSTRUCTTINTERTFUNCTARRAYDDDDDDARGINLCALLEFACEITABIDATASPTRCLOSUREVARCFUNCCHECKNILVARDEFVARKILLVARLIVEINDREGSPRETJMPGETGEND" -var _Op_index = [...]uint16{0, 3, 7, 13, 17, 21, 28, 31, 34, 36, 39, 45, 49, 55, 61, 73, 88, 100, 112, 127, 139, 141, 144, 151, 158, 165, 175, 179, 183, 191, 199, 208, 216, 219, 224, 231, 238, 244, 253, 261, 269, 275, 279, 288, 295, 299, 302, 309, 317, 325, 332, 338, 341, 347, 354, 362, 366, 373, 381, 383, 385, 387, 389, 391, 393, 396, 401, 409, 412, 421, 424, 428, 436, 443, 452, 455, 458, 461, 464, 467, 470, 476, 479, 482, 485, 489, 494, 498, 503, 508, 514, 519, 523, 528, 536, 544, 555, 561, 570, 577, 581, 588, 595, 603, 607, 611, 615, 622, 629, 637, 643, 648, 653, 657, 662, 670, 675, 680, 684, 687, 695, 699, 701, 706, 710, 715, 721, 727, 733, 739, 744, 748, 755, 761, 766, 772, 775, 781, 788, 793, 797, 802, 806, 816, 821, 829, 835, 842, 849, 857, 863, 867, 870} +var _Op_index = [...]uint16{0, 3, 7, 13, 17, 21, 28, 31, 34, 36, 39, 45, 49, 55, 61, 73, 88, 100, 112, 127, 139, 141, 144, 151, 158, 165, 175, 179, 183, 191, 199, 208, 216, 219, 224, 231, 238, 244, 253, 261, 269, 275, 279, 288, 295, 299, 302, 309, 317, 325, 332, 338, 341, 347, 354, 362, 366, 373, 381, 383, 385, 387, 389, 391, 393, 396, 401, 409, 412, 421, 424, 428, 436, 443, 452, 455, 458, 461, 464, 467, 470, 476, 479, 482, 485, 489, 494, 498, 503, 508, 514, 519, 523, 528, 536, 544, 550, 559, 570, 577, 581, 588, 595, 603, 607, 611, 615, 622, 629, 637, 643, 648, 653, 657, 662, 670, 675, 680, 684, 687, 695, 699, 701, 706, 710, 715, 721, 727, 733, 739, 744, 748, 755, 761, 766, 772, 775, 781, 788, 793, 797, 802, 806, 816, 821, 829, 835, 842, 849, 857, 863, 867, 870} func (i Op) String() string { if i >= Op(len(_Op_index)-1) { diff --git a/src/cmd/compile/internal/gc/plive.go b/src/cmd/compile/internal/gc/plive.go index a38c33647e..2c31d5feb9 100644 --- a/src/cmd/compile/internal/gc/plive.go +++ b/src/cmd/compile/internal/gc/plive.go @@ -1019,7 +1019,7 @@ func (lv *Liveness) epilogue() { live := lv.livevars[index] if v.Op.IsCall() && live.regs != 0 { lv.printDebug() - v.Fatalf("internal error: %v register %s recorded as live at call", lv.fn.Func.Nname, live.regs.niceString(lv.f.Config)) + v.Fatalf("%v register %s recorded as live at call", lv.fn.Func.Nname, live.regs.niceString(lv.f.Config)) } index++ } @@ -1038,7 +1038,7 @@ func (lv *Liveness) epilogue() { // input parameters. for j, n := range lv.vars { if n.Class() != PPARAM && lv.stackMaps[0].Get(int32(j)) { - Fatalf("internal error: %v %L recorded as live on entry", lv.fn.Func.Nname, n) + lv.f.Fatalf("%v %L recorded as live on entry", lv.fn.Func.Nname, n) } } // Check that no registers are live at function entry. @@ -1047,7 +1047,7 @@ func (lv *Liveness) epilogue() { // so it doesn't appear live at entry. if regs := lv.regMaps[0]; regs != 0 { lv.printDebug() - lv.f.Fatalf("internal error: %v register %s recorded as live on entry", lv.fn.Func.Nname, regs.niceString(lv.f.Config)) + lv.f.Fatalf("%v register %s recorded as live on entry", lv.fn.Func.Nname, regs.niceString(lv.f.Config)) } } @@ -1461,7 +1461,7 @@ func (lv *Liveness) printDebug() { // first word dumped is the total number of bitmaps. The second word is the // length of the bitmaps. All bitmaps are assumed to be of equal length. The // remaining bytes are the raw bitmaps. -func (lv *Liveness) emit(argssym, livesym, regssym *obj.LSym) { +func (lv *Liveness) emit() (argsSym, liveSym, regsSym *obj.LSym) { // Size args bitmaps to be just large enough to hold the largest pointer. // First, find the largest Xoffset node we care about. // (Nodes without pointers aren't in lv.vars; see livenessShouldTrack.) @@ -1489,13 +1489,16 @@ func (lv *Liveness) emit(argssym, livesym, regssym *obj.LSym) { // This would require shifting all bitmaps. maxLocals := lv.stkptrsize + // Temporary symbols for encoding bitmaps. + var argsSymTmp, liveSymTmp, regsSymTmp obj.LSym + args := bvalloc(int32(maxArgs / int64(Widthptr))) - aoff := duint32(argssym, 0, uint32(len(lv.stackMaps))) // number of bitmaps - aoff = duint32(argssym, aoff, uint32(args.n)) // number of bits in each bitmap + aoff := duint32(&argsSymTmp, 0, uint32(len(lv.stackMaps))) // number of bitmaps + aoff = duint32(&argsSymTmp, aoff, uint32(args.n)) // number of bits in each bitmap locals := bvalloc(int32(maxLocals / int64(Widthptr))) - loff := duint32(livesym, 0, uint32(len(lv.stackMaps))) // number of bitmaps - loff = duint32(livesym, loff, uint32(locals.n)) // number of bits in each bitmap + loff := duint32(&liveSymTmp, 0, uint32(len(lv.stackMaps))) // number of bitmaps + loff = duint32(&liveSymTmp, loff, uint32(locals.n)) // number of bits in each bitmap for _, live := range lv.stackMaps { args.Clear() @@ -1503,13 +1506,13 @@ func (lv *Liveness) emit(argssym, livesym, regssym *obj.LSym) { lv.pointerMap(live, lv.vars, args, locals) - aoff = dbvec(argssym, aoff, args) - loff = dbvec(livesym, loff, locals) + aoff = dbvec(&argsSymTmp, aoff, args) + loff = dbvec(&liveSymTmp, loff, locals) } regs := bvalloc(lv.usedRegs()) - roff := duint32(regssym, 0, uint32(len(lv.regMaps))) // number of bitmaps - roff = duint32(regssym, roff, uint32(regs.n)) // number of bits in each bitmap + roff := duint32(®sSymTmp, 0, uint32(len(lv.regMaps))) // number of bitmaps + roff = duint32(®sSymTmp, roff, uint32(regs.n)) // number of bits in each bitmap if regs.n > 32 { // Our uint32 conversion below won't work. Fatalf("GP registers overflow uint32") @@ -1519,25 +1522,29 @@ func (lv *Liveness) emit(argssym, livesym, regssym *obj.LSym) { for _, live := range lv.regMaps { regs.Clear() regs.b[0] = uint32(live) - roff = dbvec(regssym, roff, regs) + roff = dbvec(®sSymTmp, roff, regs) } } // Give these LSyms content-addressable names, // so that they can be de-duplicated. // This provides significant binary size savings. - // It is safe to rename these LSyms because - // they are tracked separately from ctxt.hash. - argssym.Name = fmt.Sprintf("gclocals·%x", md5.Sum(argssym.P)) - livesym.Name = fmt.Sprintf("gclocals·%x", md5.Sum(livesym.P)) - regssym.Name = fmt.Sprintf("gclocals·%x", md5.Sum(regssym.P)) + // + // These symbols will be added to Ctxt.Data by addGCLocals + // after parallel compilation is done. + makeSym := func(tmpSym *obj.LSym) *obj.LSym { + return Ctxt.LookupInit(fmt.Sprintf("gclocals·%x", md5.Sum(tmpSym.P)), func(lsym *obj.LSym) { + lsym.P = tmpSym.P + }) + } + return makeSym(&argsSymTmp), makeSym(&liveSymTmp), makeSym(®sSymTmp) } // Entry pointer for liveness analysis. Solves for the liveness of // pointer variables in the function and emits a runtime data // structure read by the garbage collector. // Returns a map from GC safe points to their corresponding stack map index. -func liveness(e *ssafn, f *ssa.Func) LivenessMap { +func liveness(e *ssafn, f *ssa.Func, pp *Progs) LivenessMap { // Construct the global liveness state. vars, idx := getvariables(e.curfn) lv := newliveness(e.curfn, f, vars, idx, e.stkptrsize) @@ -1577,7 +1584,25 @@ func liveness(e *ssafn, f *ssa.Func) LivenessMap { // Emit the live pointer map data structures if ls := e.curfn.Func.lsym; ls != nil { - lv.emit(&ls.Func.GCArgs, &ls.Func.GCLocals, &ls.Func.GCRegs) + ls.Func.GCArgs, ls.Func.GCLocals, ls.Func.GCRegs = lv.emit() + + p := pp.Prog(obj.AFUNCDATA) + Addrconst(&p.From, objabi.FUNCDATA_ArgsPointerMaps) + p.To.Type = obj.TYPE_MEM + p.To.Name = obj.NAME_EXTERN + p.To.Sym = ls.Func.GCArgs + + p = pp.Prog(obj.AFUNCDATA) + Addrconst(&p.From, objabi.FUNCDATA_LocalsPointerMaps) + p.To.Type = obj.TYPE_MEM + p.To.Name = obj.NAME_EXTERN + p.To.Sym = ls.Func.GCLocals + + p = pp.Prog(obj.AFUNCDATA) + Addrconst(&p.From, objabi.FUNCDATA_RegPointerMaps) + p.To.Type = obj.TYPE_MEM + p.To.Name = obj.NAME_EXTERN + p.To.Sym = ls.Func.GCRegs } return lv.livenessMap } diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go index d1835b51c4..605401f090 100644 --- a/src/cmd/compile/internal/gc/reflect.go +++ b/src/cmd/compile/internal/gc/reflect.go @@ -812,7 +812,7 @@ func dcommontype(lsym *obj.LSym, t *types.Type) int { sptrWeak := true var sptr *obj.LSym - if !t.IsPtr() || t.PtrBase != nil { + if !t.IsPtr() || t.IsPtrElem() { tptr := types.NewPtr(t) if t.Sym != nil || methods(tptr) != nil { sptrWeak = false @@ -1137,7 +1137,7 @@ func dtypesym(t *types.Type) *obj.LSym { return lsym } // TODO(mdempsky): Investigate whether this can happen. - if isforw[tbase.Etype] { + if tbase.Etype == TFORW { return lsym } } diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 4607cf1912..b0ccd01752 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -5051,7 +5051,7 @@ func genssa(f *ssa.Func, pp *Progs) { e := f.Frontend().(*ssafn) - s.livenessMap = liveness(e, f) + s.livenessMap = liveness(e, f, pp) emitStackObjects(e, pp) // Remember where each block starts. diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go index 0fe6defe99..e29a3d7657 100644 --- a/src/cmd/compile/internal/gc/syntax.go +++ b/src/cmd/compile/internal/gc/syntax.go @@ -929,7 +929,7 @@ type nodeQueue struct { head, tail int } -// empty returns true if q contains no Nodes. +// empty reports whether q contains no Nodes. func (q *nodeQueue) empty() bool { return q.head == q.tail } diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 6ee52eae84..e6a8ed4bda 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -12,6 +12,50 @@ import ( "strings" ) +// To enable tracing support (-t flag), set enableTrace to true. +const enableTrace = false + +var trace bool +var traceIndent []byte + +func tracePrint(title string, n *Node) func(np **Node) { + indent := traceIndent + + // guard against nil + var pos, op string + var tc uint8 + if n != nil { + pos = linestr(n.Pos) + op = n.Op.String() + tc = n.Typecheck() + } + + fmt.Printf("%s: %s%s %p %s %v tc=%d\n", pos, indent, title, n, op, n, tc) + traceIndent = append(traceIndent, ". "...) + + return func(np **Node) { + traceIndent = traceIndent[:len(traceIndent)-2] + + // if we have a result, use that + if np != nil { + n = *np + } + + // guard against nil + // use outer pos, op so we don't get empty pos/op if n == nil (nicer output) + var tc uint8 + var typ *types.Type + if n != nil { + pos = linestr(n.Pos) + op = n.Op.String() + tc = n.Typecheck() + typ = n.Type + } + + fmt.Printf("%s: %s=> %p %s %v tc=%d type=%#L\n", pos, indent, n, op, n, tc, typ) + } +} + const ( Etop = 1 << iota // evaluated at statement level Erv // evaluated in value context @@ -31,11 +75,16 @@ const ( var typecheckdefstack []*Node // resolve ONONAME to definition, if any. -func resolve(n *Node) *Node { +func resolve(n *Node) (res *Node) { if n == nil || n.Op != ONONAME { return n } + // only trace if there's work to do + if enableTrace && trace { + defer tracePrint("resolve", n)(&res) + } + if n.Sym.Pkg != localpkg { if inimport { Fatalf("recursive inimport") @@ -150,7 +199,7 @@ var typecheck_tcstack []*Node // typecheck type checks node n. // The result of typecheck MUST be assigned back to n, e.g. // n.Left = typecheck(n.Left, top) -func typecheck(n *Node, top int) *Node { +func typecheck(n *Node, top int) (res *Node) { // cannot type check until all the source has been parsed if !typecheckok { Fatalf("early typecheck") @@ -160,6 +209,11 @@ func typecheck(n *Node, top int) *Node { return nil } + // only trace if there's work to do + if enableTrace && trace { + defer tracePrint("typecheck", n)(&res) + } + lno := setlineno(n) // Skip over parens. @@ -201,8 +255,16 @@ func typecheck(n *Node, top int) *Node { // since it would expand indefinitely when aliases // are substituted. cycle := cycleFor(n) - for _, n := range cycle { - if n.Name != nil && !n.Name.Param.Alias { + for _, n1 := range cycle { + if n1.Name != nil && !n1.Name.Param.Alias { + // Cycle is ok. But if n is an alias type and doesn't + // have a type yet, we have a recursive type declaration + // with aliases that we can't handle properly yet. + // Report an error rather than crashing later. + if n.Name != nil && n.Name.Param.Alias && n.Type == nil { + lineno = n.Pos + Fatalf("cannot handle alias type declaration (issue #25838): %v", n) + } lineno = lno return n } @@ -294,7 +356,11 @@ func indexlit(n *Node) *Node { // The result of typecheck1 MUST be assigned back to n, e.g. // n.Left = typecheck1(n.Left, top) -func typecheck1(n *Node, top int) *Node { +func typecheck1(n *Node, top int) (res *Node) { + if enableTrace && trace { + defer tracePrint("typecheck1", n)(&res) + } + switch n.Op { case OLITERAL, ONAME, ONONAME, OTYPE: if n.Sym == nil { @@ -1094,11 +1160,15 @@ func typecheck1(n *Node, top int) *Node { ok |= Erv t := n.Type + if t == nil { + Fatalf("no type specified for OSLICEHEADER") + } + if !t.IsSlice() { Fatalf("invalid type %v for OSLICEHEADER", n.Type) } - if !n.Left.Type.IsUnsafePtr() { + if n.Left == nil || n.Left.Type == nil || !n.Left.Type.IsUnsafePtr() { Fatalf("need unsafe.Pointer for OSLICEHEADER") } @@ -2381,7 +2451,11 @@ func lookdot1(errnode *Node, s *types.Sym, t *types.Type, fs *types.Fields, dost // typecheckMethodExpr checks selector expressions (ODOT) where the // base expression is a type expression (OTYPE). -func typecheckMethodExpr(n *Node) *Node { +func typecheckMethodExpr(n *Node) (res *Node) { + if enableTrace && trace { + defer tracePrint("typecheckMethodExpr", n)(&res) + } + t := n.Left.Type // Compute the method set for t. @@ -2924,7 +2998,11 @@ func pushtype(n *Node, t *types.Type) { // The result of typecheckcomplit MUST be assigned back to n, e.g. // n.Left = typecheckcomplit(n.Left) -func typecheckcomplit(n *Node) *Node { +func typecheckcomplit(n *Node) (res *Node) { + if enableTrace && trace { + defer tracePrint("typecheckcomplit", n)(&res) + } + lno := lineno defer func() { lineno = lno @@ -3337,6 +3415,10 @@ func samesafeexpr(l *Node, r *Node) bool { // if this assignment is the definition of a var on the left side, // fill in the var's type. func typecheckas(n *Node) { + if enableTrace && trace { + defer tracePrint("typecheckas", n)(nil) + } + // delicate little dance. // the definition of n may refer to this assignment // as its definition, in which case it will call typecheckas. @@ -3393,6 +3475,10 @@ func checkassignto(src *types.Type, dst *Node) { } func typecheckas2(n *Node) { + if enableTrace && trace { + defer tracePrint("typecheckas2", n)(nil) + } + ls := n.List.Slice() for i1, n1 := range ls { // delicate little dance. @@ -3521,6 +3607,10 @@ out: // type check function definition func typecheckfunc(n *Node) { + if enableTrace && trace { + defer tracePrint("typecheckfunc", n)(nil) + } + for _, ln := range n.Func.Dcl { if ln.Op == ONAME && (ln.Class() == PPARAM || ln.Class() == PPARAMOUT) { ln.Name.Decldepth = 1 @@ -3593,8 +3683,7 @@ func copytype(n *Node, t *types.Type) { embedlineno := n.Type.ForwardType().Embedlineno l := n.Type.ForwardType().Copyto - ptrBase := n.Type.PtrBase - sliceOf := n.Type.SliceOf + cache := n.Type.Cache // TODO(mdempsky): Fix Type rekinding. *n.Type = *t @@ -3615,8 +3704,7 @@ func copytype(n *Node, t *types.Type) { t.Nod = asTypesNode(n) t.SetDeferwidth(false) - t.PtrBase = ptrBase - t.SliceOf = sliceOf + t.Cache = cache // Propagate go:notinheap pragma from the Name to the Type. if n.Name != nil && n.Name.Param != nil && n.Name.Param.Pragma&NotInHeap != 0 { @@ -3637,6 +3725,10 @@ func copytype(n *Node, t *types.Type) { } func typecheckdeftype(n *Node) { + if enableTrace && trace { + defer tracePrint("typecheckdeftype", n)(nil) + } + n.Type.Sym = n.Sym n.SetTypecheck(1) n.Name.Param.Ntype = typecheck(n.Name.Param.Ntype, Etype) @@ -3654,6 +3746,10 @@ func typecheckdeftype(n *Node) { } func typecheckdef(n *Node) { + if enableTrace && trace { + defer tracePrint("typecheckdef", n)(nil) + } + lno := setlineno(n) if n.Op == ONONAME { @@ -3992,6 +4088,12 @@ func deadcode(fn *Node) { } func deadcodeslice(nn Nodes) { + var lastLabel = -1 + for i, n := range nn.Slice() { + if n != nil && n.Op == OLABEL { + lastLabel = i + } + } for i, n := range nn.Slice() { // Cut is set to true when all nodes after i'th position // should be removed. @@ -4014,10 +4116,14 @@ func deadcodeslice(nn Nodes) { // If "then" or "else" branch ends with panic or return statement, // it is safe to remove all statements after this node. // isterminating is not used to avoid goto-related complications. + // We must be careful not to deadcode-remove labels, as they + // might be the target of a goto. See issue 28616. if body := body.Slice(); len(body) != 0 { switch body[(len(body) - 1)].Op { case ORETURN, ORETJMP, OPANIC: - cut = true + if i > lastLabel { + cut = true + } } } } diff --git a/src/cmd/compile/internal/gc/universe.go b/src/cmd/compile/internal/gc/universe.go index 96980ad500..760a8e40b0 100644 --- a/src/cmd/compile/internal/gc/universe.go +++ b/src/cmd/compile/internal/gc/universe.go @@ -200,8 +200,6 @@ func typeinit() { isComplex[TCOMPLEX64] = true isComplex[TCOMPLEX128] = true - isforw[TFORW] = true - // initialize okfor for et := types.EType(0); et < NTYPE; et++ { if isInt[et] || et == TIDEAL { diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index 0e07efa0d9..37d995b1bd 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -384,41 +384,31 @@ func convFuncName(from, to *types.Type) (fnname string, needsaddr bool) { tkind := to.Tie() switch from.Tie() { case 'I': - switch tkind { - case 'I': + if tkind == 'I' { return "convI2I", false } case 'T': + switch { + case from.Size() == 2 && from.Align == 2: + return "convT16", false + case from.Size() == 4 && from.Align == 4 && !types.Haspointers(from): + return "convT32", false + case from.Size() == 8 && from.Align == types.Types[TUINT64].Align && !types.Haspointers(from): + return "convT64", false + case from.IsString(): + return "convTstring", false + case from.IsSlice(): + return "convTslice", false + } + switch tkind { case 'E': - switch { - case from.Size() == 2 && from.Align == 2: - return "convT2E16", false - case from.Size() == 4 && from.Align == 4 && !types.Haspointers(from): - return "convT2E32", false - case from.Size() == 8 && from.Align == types.Types[TUINT64].Align && !types.Haspointers(from): - return "convT2E64", false - case from.IsString(): - return "convT2Estring", false - case from.IsSlice(): - return "convT2Eslice", false - case !types.Haspointers(from): + if !types.Haspointers(from) { return "convT2Enoptr", true } return "convT2E", true case 'I': - switch { - case from.Size() == 2 && from.Align == 2: - return "convT2I16", false - case from.Size() == 4 && from.Align == 4 && !types.Haspointers(from): - return "convT2I32", false - case from.Size() == 8 && from.Align == types.Types[TUINT64].Align && !types.Haspointers(from): - return "convT2I64", false - case from.IsString(): - return "convT2Istring", false - case from.IsSlice(): - return "convT2Islice", false - case !types.Haspointers(from): + if !types.Haspointers(from) { return "convT2Inoptr", true } return "convT2I", true @@ -496,7 +486,7 @@ opswitch: OIND, OSPTR, OITAB, OIDATA, OADDR: n.Left = walkexpr(n.Left, init) - case OEFACE, OAND, OSUB, OMUL, OADD, OOR, OXOR: + case OEFACE, OAND, OSUB, OMUL, OADD, OOR, OXOR, OLSH, ORSH: n.Left = walkexpr(n.Left, init) n.Right = walkexpr(n.Right, init) @@ -548,15 +538,6 @@ opswitch: n.SetTypecheck(1) } - case OLSH, ORSH: - n.Left = walkexpr(n.Left, init) - n.Right = walkexpr(n.Right, init) - t := n.Left.Type - n.SetBounded(bounded(n.Right, 8*t.Width)) - if Debug['m'] != 0 && n.Bounded() && !Isconst(n.Right, CTINT) { - Warn("shift bounds check elided") - } - case OCOMPLEX: // Use results from call expression as arguments for complex. if n.Left == nil && n.Right == nil { @@ -834,16 +815,21 @@ opswitch: case OCONVIFACE: n.Left = walkexpr(n.Left, init) - // Optimize convT2E or convT2I as a two-word copy when T is pointer-shaped. - if isdirectiface(n.Left.Type) { - var t *Node - if n.Type.IsEmptyInterface() { - t = typename(n.Left.Type) - } else { - t = itabname(n.Left.Type, n.Type) + fromType := n.Left.Type + toType := n.Type + + // typeword generates the type word of the interface value. + typeword := func() *Node { + if toType.IsEmptyInterface() { + return typename(fromType) } - l := nod(OEFACE, t, n.Left) - l.Type = n.Type + return itabname(fromType, toType) + } + + // Optimize convT2E or convT2I as a two-word copy when T is pointer-shaped. + if isdirectiface(fromType) { + l := nod(OEFACE, typeword(), n.Left) + l.Type = toType l.SetTypecheck(n.Typecheck()) n = l break @@ -863,11 +849,11 @@ opswitch: // or creating one on the stack. var value *Node switch { - case n.Left.Type.Size() == 0: + case fromType.Size() == 0: // n.Left is zero-sized. Use zerobase. cheapexpr(n.Left, init) // Evaluate n.Left for side-effects. See issue 19246. value = zerobase - case n.Left.Type.IsBoolean() || (n.Left.Type.Size() == 1 && n.Left.Type.IsInteger()): + case fromType.IsBoolean() || (fromType.Size() == 1 && fromType.IsInteger()): // n.Left is a bool/byte. Use staticbytes[n.Left]. n.Left = cheapexpr(n.Left, init) value = nod(OINDEX, staticbytes, byteindex(n.Left)) @@ -875,23 +861,17 @@ opswitch: case n.Left.Class() == PEXTERN && n.Left.Name != nil && n.Left.Name.Readonly(): // n.Left is a readonly global; use it directly. value = n.Left - case !n.Left.Type.IsInterface() && n.Esc == EscNone && n.Left.Type.Width <= 1024: + case !fromType.IsInterface() && n.Esc == EscNone && fromType.Width <= 1024: // n.Left does not escape. Use a stack temporary initialized to n.Left. - value = temp(n.Left.Type) + value = temp(fromType) init.Append(typecheck(nod(OAS, value, n.Left), Etop)) } if value != nil { // Value is identical to n.Left. // Construct the interface directly: {type/itab, &value}. - var t *Node - if n.Type.IsEmptyInterface() { - t = typename(n.Left.Type) - } else { - t = itabname(n.Left.Type, n.Type) - } - l := nod(OEFACE, t, typecheck(nod(OADDR, value, nil), Erv)) - l.Type = n.Type + l := nod(OEFACE, typeword(), typecheck(nod(OADDR, value, nil), Erv)) + l.Type = toType l.SetTypecheck(n.Typecheck()) n = l break @@ -903,9 +883,9 @@ opswitch: // tmp = tmp.type // } // e = iface{tmp, i.data} - if n.Type.IsEmptyInterface() && n.Left.Type.IsInterface() && !n.Left.Type.IsEmptyInterface() { + if toType.IsEmptyInterface() && fromType.IsInterface() && !fromType.IsEmptyInterface() { // Evaluate the input interface. - c := temp(n.Left.Type) + c := temp(fromType) init.Append(nod(OAS, c, n.Left)) // Get the itab out of the interface. @@ -919,26 +899,43 @@ opswitch: // Build the result. e := nod(OEFACE, tmp, ifaceData(c, types.NewPtr(types.Types[TUINT8]))) - e.Type = n.Type // assign type manually, typecheck doesn't understand OEFACE. + e.Type = toType // assign type manually, typecheck doesn't understand OEFACE. e.SetTypecheck(1) n = e break } - var ll []*Node - if n.Type.IsEmptyInterface() { - if !n.Left.Type.IsInterface() { - ll = append(ll, typename(n.Left.Type)) - } - } else { - if n.Left.Type.IsInterface() { - ll = append(ll, typename(n.Type)) - } else { - ll = append(ll, itabname(n.Left.Type, n.Type)) - } + fnname, needsaddr := convFuncName(fromType, toType) + + if !needsaddr && !fromType.IsInterface() { + // Use a specialized conversion routine that only returns a data pointer. + // ptr = convT2X(val) + // e = iface{typ/tab, ptr} + fn := syslook(fnname) + dowidth(fromType) + fn = substArgTypes(fn, fromType) + dowidth(fn.Type) + call := nod(OCALL, fn, nil) + call.List.Set1(n.Left) + call = typecheck(call, Erv) + call = walkexpr(call, init) + call = safeexpr(call, init) + e := nod(OEFACE, typeword(), call) + e.Type = toType + e.SetTypecheck(1) + n = e + break + } + + var tab *Node + if fromType.IsInterface() { + // convI2I + tab = typename(toType) + } else { + // convT2x + tab = typeword() } - fnname, needsaddr := convFuncName(n.Left.Type, n.Type) v := n.Left if needsaddr { // Types of large or unknown size are passed by reference. @@ -952,14 +949,13 @@ opswitch: } v = nod(OADDR, v, nil) } - ll = append(ll, v) - dowidth(n.Left.Type) + dowidth(fromType) fn := syslook(fnname) - fn = substArgTypes(fn, n.Left.Type, n.Type) + fn = substArgTypes(fn, fromType, toType) dowidth(fn.Type) n = nod(OCALL, fn, nil) - n.List.Set(ll) + n.List.Set2(tab, v) n = typecheck(n, Erv) n = walkexpr(n, init) @@ -1343,14 +1339,17 @@ opswitch: argtype = types.Types[TINT] } + m := nod(OSLICEHEADER, nil, nil) + m.Type = t + fn := syslook(fnname) - n.Left = mkcall1(fn, types.Types[TUNSAFEPTR], init, typename(t.Elem()), conv(len, argtype), conv(cap, argtype)) - n.Left.SetNonNil(true) - n.List.Set2(conv(len, types.Types[TINT]), conv(cap, types.Types[TINT])) - n.Op = OSLICEHEADER - n.Type = t - n = typecheck(n, Erv) - n = walkexpr(n, init) + m.Left = mkcall1(fn, types.Types[TUNSAFEPTR], init, typename(t.Elem()), conv(len, argtype), conv(cap, argtype)) + m.Left.SetNonNil(true) + m.List.Set2(conv(len, types.Types[TINT]), conv(cap, types.Types[TINT])) + + m = typecheck(m, Erv) + m = walkexpr(m, init) + n = m } case ORUNESTR: diff --git a/src/cmd/compile/internal/ppc64/ssa.go b/src/cmd/compile/internal/ppc64/ssa.go index a3f8b67177..a6dd8cab5f 100644 --- a/src/cmd/compile/internal/ppc64/ssa.go +++ b/src/cmd/compile/internal/ppc64/ssa.go @@ -569,7 +569,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { ssa.OpPPC64ROTL, ssa.OpPPC64ROTLW, ssa.OpPPC64MULHD, ssa.OpPPC64MULHW, ssa.OpPPC64MULHDU, ssa.OpPPC64MULHWU, ssa.OpPPC64FMUL, ssa.OpPPC64FMULS, ssa.OpPPC64FDIV, ssa.OpPPC64FDIVS, ssa.OpPPC64FCPSGN, - ssa.OpPPC64AND, ssa.OpPPC64OR, ssa.OpPPC64ANDN, ssa.OpPPC64ORN, ssa.OpPPC64NOR, ssa.OpPPC64XOR, ssa.OpPPC64EQV: + ssa.OpPPC64AND, ssa.OpPPC64ANDCC, ssa.OpPPC64OR, ssa.OpPPC64ORCC, ssa.OpPPC64ANDN, ssa.OpPPC64ORN, ssa.OpPPC64NOR, ssa.OpPPC64XOR, ssa.OpPPC64XORCC, ssa.OpPPC64EQV: r := v.Reg() r1 := v.Args[0].Reg() r2 := v.Args[1].Reg() diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go index e79629695a..558c4b7db8 100644 --- a/src/cmd/compile/internal/ssa/config.go +++ b/src/cmd/compile/internal/ssa/config.go @@ -112,7 +112,7 @@ type Logger interface { // Logf logs a message from the compiler. Logf(string, ...interface{}) - // Log returns true if logging is not a no-op + // Log reports whether logging is not a no-op // some logging calls account for more than a few heap allocations. Log() bool diff --git a/src/cmd/compile/internal/ssa/debug.go b/src/cmd/compile/internal/ssa/debug.go index 8df8a94b76..3d0be0fe1c 100644 --- a/src/cmd/compile/internal/ssa/debug.go +++ b/src/cmd/compile/internal/ssa/debug.go @@ -790,7 +790,7 @@ func (e *pendingEntry) clear() { } } -// canMerge returns true if the location description for new is the same as +// canMerge reports whether the location description for new is the same as // pending. func canMerge(pending, new VarLoc) bool { if pending.absent() && new.absent() { diff --git a/src/cmd/compile/internal/ssa/func.go b/src/cmd/compile/internal/ssa/func.go index 2ed4086fd1..d73d39ce28 100644 --- a/src/cmd/compile/internal/ssa/func.go +++ b/src/cmd/compile/internal/ssa/func.go @@ -621,7 +621,7 @@ func (f *Func) invalidateCFG() { f.cachedLoopnest = nil } -// DebugHashMatch returns true if environment variable evname +// DebugHashMatch reports whether environment variable evname // 1) is empty (this is a special more-quickly implemented case of 3) // 2) is "y" or "Y" // 3) is a suffix of the sha1 hash of name diff --git a/src/cmd/compile/internal/ssa/gen/PPC64.rules b/src/cmd/compile/internal/ssa/gen/PPC64.rules index 0eaa88596b..24cee6f0a3 100644 --- a/src/cmd/compile/internal/ssa/gen/PPC64.rules +++ b/src/cmd/compile/internal/ssa/gen/PPC64.rules @@ -518,6 +518,13 @@ (LessEqual (InvertFlags x)) -> (GreaterEqual x) (GreaterEqual (InvertFlags x)) -> (LessEqual x) +// Elide compares of bit tests // TODO need to make both CC and result of ANDCC available. +((EQ|NE|LT|LE|GT|GE) (CMPconst [0] (ANDconst [c] x)) yes no) -> ((EQ|NE|LT|LE|GT|GE) (ANDCCconst [c] x) yes no) +((EQ|NE|LT|LE|GT|GE) (CMPWconst [0] (ANDconst [c] x)) yes no) -> ((EQ|NE|LT|LE|GT|GE) (ANDCCconst [c] x) yes no) +((EQ|NE|LT|LE|GT|GE) (CMPconst [0] z:(AND x y)) yes no) && z.Uses == 1 -> ((EQ|NE|LT|LE|GT|GE) (ANDCC x y) yes no) +((EQ|NE|LT|LE|GT|GE) (CMPconst [0] z:(OR x y)) yes no) && z.Uses == 1 -> ((EQ|NE|LT|LE|GT|GE) (ORCC x y) yes no) +((EQ|NE|LT|LE|GT|GE) (CMPconst [0] z:(XOR x y)) yes no) && z.Uses == 1 -> ((EQ|NE|LT|LE|GT|GE) (XORCC x y) yes no) + // Lowering loads (Load ptr mem) && (is64BitInt(t) || isPtr(t)) -> (MOVDload ptr mem) (Load ptr mem) && is32BitInt(t) && isSigned(t) -> (MOVWload ptr mem) diff --git a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go index ef0db69fb7..d6638b1ec7 100644 --- a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go @@ -241,24 +241,27 @@ func init() { {name: "MFVSRD", argLength: 1, reg: fpgp, asm: "MFVSRD", typ: "Int64"}, // move 64 bits of F register into G register {name: "MTVSRD", argLength: 1, reg: gpfp, asm: "MTVSRD", typ: "Float64"}, // move 64 bits of G register into F register - {name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true}, // arg0&arg1 - {name: "ANDN", argLength: 2, reg: gp21, asm: "ANDN"}, // arg0&^arg1 - {name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true}, // arg0|arg1 - {name: "ORN", argLength: 2, reg: gp21, asm: "ORN"}, // arg0|^arg1 - {name: "NOR", argLength: 2, reg: gp21, asm: "NOR", commutative: true}, // ^(arg0|arg1) - {name: "XOR", argLength: 2, reg: gp21, asm: "XOR", typ: "Int64", commutative: true}, // arg0^arg1 - {name: "EQV", argLength: 2, reg: gp21, asm: "EQV", typ: "Int64", commutative: true}, // arg0^^arg1 - {name: "NEG", argLength: 1, reg: gp11, asm: "NEG"}, // -arg0 (integer) - {name: "FNEG", argLength: 1, reg: fp11, asm: "FNEG"}, // -arg0 (floating point) - {name: "FSQRT", argLength: 1, reg: fp11, asm: "FSQRT"}, // sqrt(arg0) (floating point) - {name: "FSQRTS", argLength: 1, reg: fp11, asm: "FSQRTS"}, // sqrt(arg0) (floating point, single precision) - {name: "FFLOOR", argLength: 1, reg: fp11, asm: "FRIM"}, // floor(arg0), float64 - {name: "FCEIL", argLength: 1, reg: fp11, asm: "FRIP"}, // ceil(arg0), float64 - {name: "FTRUNC", argLength: 1, reg: fp11, asm: "FRIZ"}, // trunc(arg0), float64 - {name: "FROUND", argLength: 1, reg: fp11, asm: "FRIN"}, // round(arg0), float64 - {name: "FABS", argLength: 1, reg: fp11, asm: "FABS"}, // abs(arg0), float64 - {name: "FNABS", argLength: 1, reg: fp11, asm: "FNABS"}, // -abs(arg0), float64 - {name: "FCPSGN", argLength: 2, reg: fp21, asm: "FCPSGN"}, // copysign arg0 -> arg1, float64 + {name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true}, // arg0&arg1 + {name: "ANDN", argLength: 2, reg: gp21, asm: "ANDN"}, // arg0&^arg1 + {name: "ANDCC", argLength: 2, reg: gp21, asm: "ANDCC", commutative: true, typ: "Flags"}, // arg0&arg1 sets CC + {name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true}, // arg0|arg1 + {name: "ORN", argLength: 2, reg: gp21, asm: "ORN"}, // arg0|^arg1 + {name: "ORCC", argLength: 2, reg: gp21, asm: "ORCC", commutative: true, typ: "Flags"}, // arg0|arg1 sets CC + {name: "NOR", argLength: 2, reg: gp21, asm: "NOR", commutative: true}, // ^(arg0|arg1) + {name: "XOR", argLength: 2, reg: gp21, asm: "XOR", typ: "Int64", commutative: true}, // arg0^arg1 + {name: "XORCC", argLength: 2, reg: gp21, asm: "XORCC", commutative: true, typ: "Flags"}, // arg0^arg1 sets CC + {name: "EQV", argLength: 2, reg: gp21, asm: "EQV", typ: "Int64", commutative: true}, // arg0^^arg1 + {name: "NEG", argLength: 1, reg: gp11, asm: "NEG"}, // -arg0 (integer) + {name: "FNEG", argLength: 1, reg: fp11, asm: "FNEG"}, // -arg0 (floating point) + {name: "FSQRT", argLength: 1, reg: fp11, asm: "FSQRT"}, // sqrt(arg0) (floating point) + {name: "FSQRTS", argLength: 1, reg: fp11, asm: "FSQRTS"}, // sqrt(arg0) (floating point, single precision) + {name: "FFLOOR", argLength: 1, reg: fp11, asm: "FRIM"}, // floor(arg0), float64 + {name: "FCEIL", argLength: 1, reg: fp11, asm: "FRIP"}, // ceil(arg0), float64 + {name: "FTRUNC", argLength: 1, reg: fp11, asm: "FRIZ"}, // trunc(arg0), float64 + {name: "FROUND", argLength: 1, reg: fp11, asm: "FRIN"}, // round(arg0), float64 + {name: "FABS", argLength: 1, reg: fp11, asm: "FABS"}, // abs(arg0), float64 + {name: "FNABS", argLength: 1, reg: fp11, asm: "FNABS"}, // -abs(arg0), float64 + {name: "FCPSGN", argLength: 2, reg: fp21, asm: "FCPSGN"}, // copysign arg0 -> arg1, float64 {name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int64"}, // arg0|aux {name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int64"}, // arg0^aux diff --git a/src/cmd/compile/internal/ssa/gen/generic.rules b/src/cmd/compile/internal/ssa/gen/generic.rules index 14a67846dc..5a1bee0fa2 100644 --- a/src/cmd/compile/internal/ssa/gen/generic.rules +++ b/src/cmd/compile/internal/ssa/gen/generic.rules @@ -1804,11 +1804,25 @@ // Later passes (deadstore, elim unread auto) will remove the A -> B move, if possible. // This happens most commonly when B is an autotmp inserted earlier // during compilation to ensure correctness. -(Move {t1} [s1] dst tmp1 midmem:(Move {t2} [s2] tmp2 src _)) - && s1 == s2 +// Take care that overlapping moves are preserved. +// Restrict this optimization to the stack, to avoid duplicating loads from the heap; +// see CL 145208 for discussion. +(Move {t1} [s] dst tmp1 midmem:(Move {t2} [s] tmp2 src _)) && t1.(*types.Type).Compare(t2.(*types.Type)) == types.CMPeq && isSamePtr(tmp1, tmp2) - -> (Move {t1} [s1] dst src midmem) + && isStackPtr(src) + && disjoint(src, s, tmp2, s) + && (disjoint(src, s, dst, s) || isInlinableMemmove(dst, src, s, config)) + -> (Move {t1} [s] dst src midmem) + +// Same, but for large types that require VarDefs. +(Move {t1} [s] dst tmp1 midmem:(VarDef (Move {t2} [s] tmp2 src _))) + && t1.(*types.Type).Compare(t2.(*types.Type)) == types.CMPeq + && isSamePtr(tmp1, tmp2) + && isStackPtr(src) + && disjoint(src, s, tmp2, s) + && (disjoint(src, s, dst, s) || isInlinableMemmove(dst, src, s, config)) + -> (Move {t1} [s] dst src midmem) // Elide self-moves. This only happens rarely (e.g test/fixedbugs/bug277.go). // However, this rule is needed to prevent the previous rule from looping forever in such cases. diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go index 522ccbf893..ba8d93cf2c 100644 --- a/src/cmd/compile/internal/ssa/gen/genericOps.go +++ b/src/cmd/compile/internal/ssa/gen/genericOps.go @@ -529,7 +529,7 @@ var genericOps = []opData{ {name: "AtomicAdd64", argLength: 3, typ: "(UInt64,Mem)", hasSideEffects: true}, // Do *arg0 += arg1. arg2=memory. Returns sum and new memory. {name: "AtomicCompareAndSwap32", argLength: 4, typ: "(Bool,Mem)", hasSideEffects: true}, // if *arg0==arg1, then set *arg0=arg2. Returns true if store happens and new memory. {name: "AtomicCompareAndSwap64", argLength: 4, typ: "(Bool,Mem)", hasSideEffects: true}, // if *arg0==arg1, then set *arg0=arg2. Returns true if store happens and new memory. - {name: "AtomicCompareAndSwapRel32", argLength: 4, typ: "(Bool,Mem)", hasSideEffects: true}, // if *arg0==arg1, then set *arg0=arg2. Lock release, returns true if store happens and new memory. + {name: "AtomicCompareAndSwapRel32", argLength: 4, typ: "(Bool,Mem)", hasSideEffects: true}, // if *arg0==arg1, then set *arg0=arg2. Lock release, reports whether store happens and new memory. {name: "AtomicAnd8", argLength: 3, typ: "Mem", hasSideEffects: true}, // *arg0 &= arg1. arg2=memory. Returns memory. {name: "AtomicOr8", argLength: 3, typ: "Mem", hasSideEffects: true}, // *arg0 |= arg1. arg2=memory. Returns memory. diff --git a/src/cmd/compile/internal/ssa/gen/rulegen.go b/src/cmd/compile/internal/ssa/gen/rulegen.go index faaad974c4..34517b4cb9 100644 --- a/src/cmd/compile/internal/ssa/gen/rulegen.go +++ b/src/cmd/compile/internal/ssa/gen/rulegen.go @@ -6,7 +6,7 @@ // This program generates Go code that applies rewrite rules to a Value. // The generated code implements a function of type func (v *Value) bool -// which returns true iff if did something. +// which reports whether if did something. // Ideas stolen from Swift: http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-2000-2.html package main @@ -386,7 +386,7 @@ func genRules(arch arch) { } } -// genMatch returns true if the match can fail. +// genMatch reports whether the match can fail. func genMatch(w io.Writer, arch arch, match string, loc string) bool { return genMatch0(w, arch, match, "v", map[string]struct{}{}, true, loc) } @@ -623,7 +623,7 @@ outer: return r } -// isBlock returns true if this op is a block opcode. +// isBlock reports whether this op is a block opcode. func isBlock(name string, arch arch) bool { for _, b := range genericBlocks { if b.name == name { @@ -768,7 +768,7 @@ func typeName(typ string) string { } } -// unbalanced returns true if there aren't the same number of ( and ) in the string. +// unbalanced reports whether there aren't the same number of ( and ) in the string. func unbalanced(s string) bool { var left, right int for _, c := range s { diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index 03837b5f63..f6568be660 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -1663,10 +1663,13 @@ const ( OpPPC64MTVSRD OpPPC64AND OpPPC64ANDN + OpPPC64ANDCC OpPPC64OR OpPPC64ORN + OpPPC64ORCC OpPPC64NOR OpPPC64XOR + OpPPC64XORCC OpPPC64EQV OpPPC64NEG OpPPC64FNEG @@ -22202,6 +22205,21 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "ANDCC", + argLen: 2, + commutative: true, + asm: ppc64.AANDCC, + reg: regInfo{ + inputs: []inputInfo{ + {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + {1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + outputs: []outputInfo{ + {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + }, + }, { name: "OR", argLen: 2, @@ -22231,6 +22249,21 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "ORCC", + argLen: 2, + commutative: true, + asm: ppc64.AORCC, + reg: regInfo{ + inputs: []inputInfo{ + {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + {1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + outputs: []outputInfo{ + {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + }, + }, { name: "NOR", argLen: 2, @@ -22261,6 +22294,21 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "XORCC", + argLen: 2, + commutative: true, + asm: ppc64.AXORCC, + reg: regInfo{ + inputs: []inputInfo{ + {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + {1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + outputs: []outputInfo{ + {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + }, + }, { name: "EQV", argLen: 2, diff --git a/src/cmd/compile/internal/ssa/poset.go b/src/cmd/compile/internal/ssa/poset.go index 0e0e2789b1..4ebfb89e52 100644 --- a/src/cmd/compile/internal/ssa/poset.go +++ b/src/cmd/compile/internal/ssa/poset.go @@ -781,7 +781,7 @@ func (po *poset) DotDump(fn string, title string) error { return nil } -// Ordered returns true if n1 [o2] p2) d1 (Store {t3} (OffPtr [o3] p3) d2 (Store {t4} (OffPtr [o4] p4) d3 (Store {t5} (OffPtr [o5] p5) d4 (Zero {t6} [n] p6 _))))))) // cond: isSamePtr(p1, p2) && isSamePtr(p2, p3) && isSamePtr(p3, p4) && isSamePtr(p4, p5) && isSamePtr(p5, p6) && alignof(t2) <= alignof(t1) && alignof(t3) <= alignof(t1) && alignof(t4) <= alignof(t1) && alignof(t5) <= alignof(t1) && alignof(t6) <= alignof(t1) && registerizable(b, t2) && registerizable(b, t3) && registerizable(b, t4) && registerizable(b, t5) && n >= o2 + sizeof(t2) && n >= o3 + sizeof(t3) && n >= o4 + sizeof(t4) && n >= o5 + sizeof(t5) // result: (Store {t2} (OffPtr [o2] dst) d1 (Store {t3} (OffPtr [o3] dst) d2 (Store {t4} (OffPtr [o4] dst) d3 (Store {t5} (OffPtr [o5] dst) d4 (Zero {t1} [n] dst mem))))) @@ -17355,11 +17357,11 @@ func rewriteValuegeneric_OpMove_20(v *Value) bool { v.AddArg(v1) return true } - // match: (Move {t1} [s1] dst tmp1 midmem:(Move {t2} [s2] tmp2 src _)) - // cond: s1 == s2 && t1.(*types.Type).Compare(t2.(*types.Type)) == types.CMPeq && isSamePtr(tmp1, tmp2) - // result: (Move {t1} [s1] dst src midmem) + // match: (Move {t1} [s] dst tmp1 midmem:(Move {t2} [s] tmp2 src _)) + // cond: t1.(*types.Type).Compare(t2.(*types.Type)) == types.CMPeq && isSamePtr(tmp1, tmp2) && isStackPtr(src) && disjoint(src, s, tmp2, s) && (disjoint(src, s, dst, s) || isInlinableMemmove(dst, src, s, config)) + // result: (Move {t1} [s] dst src midmem) for { - s1 := v.AuxInt + s := v.AuxInt t1 := v.Aux _ = v.Args[2] dst := v.Args[0] @@ -17368,16 +17370,53 @@ func rewriteValuegeneric_OpMove_20(v *Value) bool { if midmem.Op != OpMove { break } - s2 := midmem.AuxInt + if midmem.AuxInt != s { + break + } t2 := midmem.Aux _ = midmem.Args[2] tmp2 := midmem.Args[0] src := midmem.Args[1] - if !(s1 == s2 && t1.(*types.Type).Compare(t2.(*types.Type)) == types.CMPeq && isSamePtr(tmp1, tmp2)) { + if !(t1.(*types.Type).Compare(t2.(*types.Type)) == types.CMPeq && isSamePtr(tmp1, tmp2) && isStackPtr(src) && disjoint(src, s, tmp2, s) && (disjoint(src, s, dst, s) || isInlinableMemmove(dst, src, s, config))) { break } v.reset(OpMove) - v.AuxInt = s1 + v.AuxInt = s + v.Aux = t1 + v.AddArg(dst) + v.AddArg(src) + v.AddArg(midmem) + return true + } + // match: (Move {t1} [s] dst tmp1 midmem:(VarDef (Move {t2} [s] tmp2 src _))) + // cond: t1.(*types.Type).Compare(t2.(*types.Type)) == types.CMPeq && isSamePtr(tmp1, tmp2) && isStackPtr(src) && disjoint(src, s, tmp2, s) && (disjoint(src, s, dst, s) || isInlinableMemmove(dst, src, s, config)) + // result: (Move {t1} [s] dst src midmem) + for { + s := v.AuxInt + t1 := v.Aux + _ = v.Args[2] + dst := v.Args[0] + tmp1 := v.Args[1] + midmem := v.Args[2] + if midmem.Op != OpVarDef { + break + } + midmem_0 := midmem.Args[0] + if midmem_0.Op != OpMove { + break + } + if midmem_0.AuxInt != s { + break + } + t2 := midmem_0.Aux + _ = midmem_0.Args[2] + tmp2 := midmem_0.Args[0] + src := midmem_0.Args[1] + if !(t1.(*types.Type).Compare(t2.(*types.Type)) == types.CMPeq && isSamePtr(tmp1, tmp2) && isStackPtr(src) && disjoint(src, s, tmp2, s) && (disjoint(src, s, dst, s) || isInlinableMemmove(dst, src, s, config))) { + break + } + v.reset(OpMove) + v.AuxInt = s v.Aux = t1 v.AddArg(dst) v.AddArg(src) diff --git a/src/cmd/compile/internal/ssa/testdata/hist.dlv-opt.nexts b/src/cmd/compile/internal/ssa/testdata/hist.dlv-opt.nexts index 7eb1d3a35b..89d0b1b637 100644 --- a/src/cmd/compile/internal/ssa/testdata/hist.dlv-opt.nexts +++ b/src/cmd/compile/internal/ssa/testdata/hist.dlv-opt.nexts @@ -58,11 +58,13 @@ 87: if a == 0 { //gdb-opt=(a,n,t) 86: for i, a := range hist { 87: if a == 0 { //gdb-opt=(a,n,t) +92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t) 91: n += a 90: t += i * a 92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t) 86: for i, a := range hist { 87: if a == 0 { //gdb-opt=(a,n,t) +92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t) 91: n += a 90: t += i * a 92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t) @@ -70,11 +72,13 @@ 87: if a == 0 { //gdb-opt=(a,n,t) 86: for i, a := range hist { 87: if a == 0 { //gdb-opt=(a,n,t) +92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t) 91: n += a 90: t += i * a 92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t) 86: for i, a := range hist { 87: if a == 0 { //gdb-opt=(a,n,t) +92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t) 91: n += a 90: t += i * a 92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t) diff --git a/src/cmd/compile/internal/ssa/testdata/hist.gdb-opt.nexts b/src/cmd/compile/internal/ssa/testdata/hist.gdb-opt.nexts index 8664ea77c4..b2f3216707 100644 --- a/src/cmd/compile/internal/ssa/testdata/hist.gdb-opt.nexts +++ b/src/cmd/compile/internal/ssa/testdata/hist.gdb-opt.nexts @@ -123,6 +123,7 @@ t = 0 92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t) 91: n += a 90: t += i * a +92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t) 86: for i, a := range hist { 87: if a == 0 { //gdb-opt=(a,n,t) a = 3 @@ -131,6 +132,7 @@ t = 3 92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t) 91: n += a 90: t += i * a +92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t) 86: for i, a := range hist { 87: if a == 0 { //gdb-opt=(a,n,t) a = 0 @@ -144,6 +146,7 @@ t = 9 92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t) 91: n += a 90: t += i * a +92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t) 86: for i, a := range hist { 87: if a == 0 { //gdb-opt=(a,n,t) a = 1 @@ -152,6 +155,7 @@ t = 17 92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t) 91: n += a 90: t += i * a +92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t) 86: for i, a := range hist { 87: if a == 0 { //gdb-opt=(a,n,t) a = 0 diff --git a/src/cmd/compile/internal/ssa/trim.go b/src/cmd/compile/internal/ssa/trim.go index d97c6baaa1..1293548aad 100644 --- a/src/cmd/compile/internal/ssa/trim.go +++ b/src/cmd/compile/internal/ssa/trim.go @@ -94,7 +94,7 @@ func trim(f *Func) { } } -// emptyBlock returns true if the block does not contain actual +// emptyBlock reports whether the block does not contain actual // instructions func emptyBlock(b *Block) bool { for _, v := range b.Values { @@ -105,7 +105,7 @@ func emptyBlock(b *Block) bool { return true } -// trimmableBlock returns true if the block can be trimmed from the CFG, +// trimmableBlock reports whether the block can be trimmed from the CFG, // subject to the following criteria: // - it should not be the first block // - it should be BlockPlain diff --git a/src/cmd/compile/internal/types/type.go b/src/cmd/compile/internal/types/type.go index 39f4d2aa7b..45355e5798 100644 --- a/src/cmd/compile/internal/types/type.go +++ b/src/cmd/compile/internal/types/type.go @@ -149,8 +149,11 @@ type Type struct { Nod *Node // canonical OTYPE node Orig *Type // original type (type literal or predefined type) - SliceOf *Type - PtrBase *Type + // Cache of composite types, with this type being the element type. + Cache struct { + ptr *Type // *T, or nil + slice *Type // []T, or nil + } Sym *Sym // symbol containing name, for named types Vargen int32 // unique name for OTYPE/ONAME @@ -488,7 +491,7 @@ func NewArray(elem *Type, bound int64) *Type { // NewSlice returns the slice Type with element type elem. func NewSlice(elem *Type) *Type { - if t := elem.SliceOf; t != nil { + if t := elem.Cache.slice; t != nil { if t.Elem() != elem { Fatalf("elem mismatch") } @@ -497,7 +500,7 @@ func NewSlice(elem *Type) *Type { t := New(TSLICE) t.Extra = Slice{Elem: elem} - elem.SliceOf = t + elem.Cache.slice = t return t } @@ -551,7 +554,7 @@ func NewPtr(elem *Type) *Type { Fatalf("NewPtr: pointer to elem Type is nil") } - if t := elem.PtrBase; t != nil { + if t := elem.Cache.ptr; t != nil { if t.Elem() != elem { Fatalf("NewPtr: elem mismatch") } @@ -563,7 +566,7 @@ func NewPtr(elem *Type) *Type { t.Width = int64(Widthptr) t.Align = uint8(Widthptr) if NewPtrCacheEnabled { - elem.PtrBase = t + elem.Cache.ptr = t } return t } @@ -662,23 +665,18 @@ func SubstAny(t *Type, types *[]*Type) *Type { } case TSTRUCT: + // Make a copy of all fields, including ones whose type does not change. + // This prevents aliasing across functions, which can lead to later + // fields getting their Offset incorrectly overwritten. fields := t.FieldSlice() - var nfs []*Field + nfs := make([]*Field, len(fields)) for i, f := range fields { nft := SubstAny(f.Type, types) - if nft == f.Type { - continue - } - if nfs == nil { - nfs = append([]*Field(nil), fields...) - } nfs[i] = f.Copy() nfs[i].Type = nft } - if nfs != nil { - t = t.copy() - t.SetFields(nfs) - } + t = t.copy() + t.SetFields(nfs) } return t @@ -1258,6 +1256,11 @@ func (t *Type) IsPtr() bool { return t.Etype == TPTR } +// IsPtrElem reports whether t is the element of a pointer (to t). +func (t *Type) IsPtrElem() bool { + return t.Cache.ptr != nil +} + // IsUnsafePtr reports whether t is an unsafe pointer. func (t *Type) IsUnsafePtr() bool { return t.Etype == TUNSAFEPTR diff --git a/src/cmd/fix/fix.go b/src/cmd/fix/fix.go index 03c828a581..2c64e9b414 100644 --- a/src/cmd/fix/fix.go +++ b/src/cmd/fix/fix.go @@ -478,7 +478,7 @@ func newPkgDot(pos token.Pos, pkg, name string) ast.Expr { } // renameTop renames all references to the top-level name old. -// It returns true if it makes any changes. +// It reports whether it makes any changes. func renameTop(f *ast.File, old, new string) bool { var fixed bool diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index b0c10c8531..f8c4d2ffa9 100644 --- a/src/cmd/go/alldocs.go +++ b/src/cmd/go/alldocs.go @@ -972,6 +972,8 @@ // and -dropreplace editing flags may be repeated, and the changes // are applied in the order given. // +// The -go=version flag sets the expected Go language version. +// // The -print flag prints the final go.mod in its text format instead of // writing it back to go.mod. // @@ -984,7 +986,8 @@ // } // // type GoMod struct { -// Module Module +// Module Module +// Go string // Require []Require // Exclude []Module // Replace []Replace @@ -1604,17 +1607,20 @@ // verb followed by arguments. For example: // // module my/thing +// go 1.12 // require other/thing v1.0.2 -// require new/thing v2.3.4 +// require new/thing/v2 v2.3.4 // exclude old/thing v1.2.3 // replace bad/thing v1.4.5 => good/thing v1.4.5 // -// The verbs are module, to define the module path; require, to require -// a particular module at a given version or later; exclude, to exclude -// a particular module version from use; and replace, to replace a module -// version with a different module version. Exclude and replace apply only -// in the main module's go.mod and are ignored in dependencies. -// See https://research.swtch.com/vgo-mvs for details. +// The verbs are +// module, to define the module path; +// go, to set the expected language version; +// require, to require a particular module at a given version or later; +// exclude, to exclude a particular module version from use; and +// replace, to replace a module version with a different module version. +// Exclude and replace apply only in the main module's go.mod and are ignored +// in dependencies. See https://research.swtch.com/vgo-mvs for details. // // The leading verb can be factored out of adjacent lines to create a block, // like in Go imports: diff --git a/src/cmd/go/internal/cache/hash.go b/src/cmd/go/internal/cache/hash.go index 0e45e7db54..e4bb2a34bb 100644 --- a/src/cmd/go/internal/cache/hash.go +++ b/src/cmd/go/internal/cache/hash.go @@ -123,7 +123,7 @@ var hashFileCache struct { m map[string][HashSize]byte } -// HashFile returns the hash of the named file. +// FileHash returns the hash of the named file. // It caches repeated lookups for a given file, // and the cache entry for a file can be initialized // using SetFileHash. diff --git a/src/cmd/go/internal/cmdflag/flag.go b/src/cmd/go/internal/cmdflag/flag.go index b2a67e6f74..7f2c53def8 100644 --- a/src/cmd/go/internal/cmdflag/flag.go +++ b/src/cmd/go/internal/cmdflag/flag.go @@ -79,15 +79,15 @@ func AddKnownFlags(cmd string, defns []*Defn) { // Parse sees if argument i is present in the definitions and if so, // returns its definition, value, and whether it consumed an extra word. -// If the flag begins (cmd+".") it is ignored for the purpose of this function. -func Parse(cmd string, defns []*Defn, args []string, i int) (f *Defn, value string, extra bool) { +// If the flag begins (cmd.Name()+".") it is ignored for the purpose of this function. +func Parse(cmd string, usage func(), defns []*Defn, args []string, i int) (f *Defn, value string, extra bool) { arg := args[i] if strings.HasPrefix(arg, "--") { // reduce two minuses to one arg = arg[1:] } switch arg { case "-?", "-h", "-help": - base.Usage() + usage() } if arg == "" || arg[0] != '-' { return diff --git a/src/cmd/go/internal/modcmd/edit.go b/src/cmd/go/internal/modcmd/edit.go index 5fea3e48e0..c589c6d4ed 100644 --- a/src/cmd/go/internal/modcmd/edit.go +++ b/src/cmd/go/internal/modcmd/edit.go @@ -62,6 +62,8 @@ The -require, -droprequire, -exclude, -dropexclude, -replace, and -dropreplace editing flags may be repeated, and the changes are applied in the order given. +The -go=version flag sets the expected Go language version. + The -print flag prints the final go.mod in its text format instead of writing it back to go.mod. @@ -74,7 +76,8 @@ writing it back to go.mod. The JSON output corresponds to these Go types: } type GoMod struct { - Module Module + Module Module + Go string Require []Require Exclude []Module Replace []Replace @@ -102,8 +105,8 @@ by invoking 'go mod edit' with -require, -exclude, and so on. } var ( - editFmt = cmdEdit.Flag.Bool("fmt", false, "") - // editGo = cmdEdit.Flag.String("go", "", "") + editFmt = cmdEdit.Flag.Bool("fmt", false, "") + editGo = cmdEdit.Flag.String("go", "", "") editJSON = cmdEdit.Flag.Bool("json", false, "") editPrint = cmdEdit.Flag.Bool("print", false, "") editModule = cmdEdit.Flag.String("module", "", "") @@ -131,6 +134,7 @@ func init() { func runEdit(cmd *base.Command, args []string) { anyFlags := *editModule != "" || + *editGo != "" || *editJSON || *editPrint || *editFmt || @@ -161,7 +165,11 @@ func runEdit(cmd *base.Command, args []string) { } } - // TODO(rsc): Implement -go= once we start advertising it. + if *editGo != "" { + if !modfile.GoVersionRE.MatchString(*editGo) { + base.Fatalf(`go mod: invalid -go option; expecting something like "-go 1.12"`) + } + } data, err := ioutil.ReadFile(gomod) if err != nil { @@ -177,6 +185,12 @@ func runEdit(cmd *base.Command, args []string) { modFile.AddModuleStmt(modload.CmdModModule) } + if *editGo != "" { + if err := modFile.AddGoStmt(*editGo); err != nil { + base.Fatalf("go: internal error: %v", err) + } + } + if len(edits) > 0 { for _, edit := range edits { edit(modFile) @@ -344,6 +358,7 @@ func flagDropReplace(arg string) { // fileJSON is the -json output data structure. type fileJSON struct { Module module.Version + Go string `json:",omitempty"` Require []requireJSON Exclude []module.Version Replace []replaceJSON @@ -364,6 +379,9 @@ type replaceJSON struct { func editPrintJSON(modFile *modfile.File) { var f fileJSON f.Module = modFile.Module.Mod + if modFile.Go != nil { + f.Go = modFile.Go.Version + } for _, r := range modFile.Require { f.Require = append(f.Require, requireJSON{Path: r.Mod.Path, Version: r.Mod.Version, Indirect: r.Indirect}) } diff --git a/src/cmd/go/internal/modfile/rule.go b/src/cmd/go/internal/modfile/rule.go index e11f0a6e31..7f9a18c6c2 100644 --- a/src/cmd/go/internal/modfile/rule.go +++ b/src/cmd/go/internal/modfile/rule.go @@ -154,7 +154,7 @@ func parseToFile(file string, data []byte, fix VersionFixer, strict bool) (*File return f, nil } -var goVersionRE = regexp.MustCompile(`([1-9][0-9]*)\.(0|[1-9][0-9]*)`) +var GoVersionRE = regexp.MustCompile(`([1-9][0-9]*)\.(0|[1-9][0-9]*)`) func (f *File) add(errs *bytes.Buffer, line *Line, verb string, args []string, fix VersionFixer, strict bool) { // If strict is false, this module is a dependency. @@ -181,7 +181,7 @@ func (f *File) add(errs *bytes.Buffer, line *Line, verb string, args []string, f fmt.Fprintf(errs, "%s:%d: repeated go statement\n", f.Syntax.Name, line.Start.Line) return } - if len(args) != 1 || !goVersionRE.MatchString(args[0]) { + if len(args) != 1 || !GoVersionRE.MatchString(args[0]) { fmt.Fprintf(errs, "%s:%d: usage: go 1.23\n", f.Syntax.Name, line.Start.Line) return } @@ -477,6 +477,22 @@ func (f *File) Cleanup() { f.Syntax.Cleanup() } +func (f *File) AddGoStmt(version string) error { + if !GoVersionRE.MatchString(version) { + return fmt.Errorf("invalid language version string %q", version) + } + if f.Go == nil { + f.Go = &Go{ + Version: version, + Syntax: f.Syntax.addLine(nil, "go", version), + } + } else { + f.Go.Version = version + f.Syntax.updateLine(f.Go.Syntax, "go", version) + } + return nil +} + func (f *File) AddRequire(path, vers string) error { need := true for _, r := range f.Require { diff --git a/src/cmd/go/internal/modload/help.go b/src/cmd/go/internal/modload/help.go index f2f3419724..d9c8ae40d8 100644 --- a/src/cmd/go/internal/modload/help.go +++ b/src/cmd/go/internal/modload/help.go @@ -393,17 +393,20 @@ no /* */ comments. Each line holds a single directive, made up of a verb followed by arguments. For example: module my/thing + go 1.12 require other/thing v1.0.2 - require new/thing v2.3.4 + require new/thing/v2 v2.3.4 exclude old/thing v1.2.3 replace bad/thing v1.4.5 => good/thing v1.4.5 -The verbs are module, to define the module path; require, to require -a particular module at a given version or later; exclude, to exclude -a particular module version from use; and replace, to replace a module -version with a different module version. Exclude and replace apply only -in the main module's go.mod and are ignored in dependencies. -See https://research.swtch.com/vgo-mvs for details. +The verbs are + module, to define the module path; + go, to set the expected language version; + require, to require a particular module at a given version or later; + exclude, to exclude a particular module version from use; and + replace, to replace a module version with a different module version. +Exclude and replace apply only in the main module's go.mod and are ignored +in dependencies. See https://research.swtch.com/vgo-mvs for details. The leading verb can be factored out of adjacent lines to create a block, like in Go imports: diff --git a/src/cmd/go/internal/modload/init.go b/src/cmd/go/internal/modload/init.go index 2bab3eede1..da778b4fad 100644 --- a/src/cmd/go/internal/modload/init.go +++ b/src/cmd/go/internal/modload/init.go @@ -19,6 +19,7 @@ import ( "cmd/go/internal/search" "encoding/json" "fmt" + "go/build" "io/ioutil" "os" "path" @@ -335,6 +336,8 @@ func legacyModInit() { modFile.AddModuleStmt(path) } + addGoStmt() + for _, name := range altConfigs { cfg := filepath.Join(ModRoot, name) data, err := ioutil.ReadFile(cfg) @@ -357,6 +360,25 @@ func legacyModInit() { } } +// InitGoStmt adds a go statement, unless there already is one. +func InitGoStmt() { + if modFile.Go == nil { + addGoStmt() + } +} + +// addGoStmt adds a go statement referring to the current version. +func addGoStmt() { + tags := build.Default.ReleaseTags + version := tags[len(tags)-1] + if !strings.HasPrefix(version, "go") || !modfile.GoVersionRE.MatchString(version[2:]) { + base.Fatalf("go: unrecognized default version %q", version) + } + if err := modFile.AddGoStmt(version[2:]); err != nil { + base.Fatalf("go: internal error: %v", err) + } +} + var altConfigs = []string{ "Gopkg.lock", diff --git a/src/cmd/go/internal/test/test.go b/src/cmd/go/internal/test/test.go index 750b515e41..b38eb4c41d 100644 --- a/src/cmd/go/internal/test/test.go +++ b/src/cmd/go/internal/test/test.go @@ -124,16 +124,6 @@ A cached test result is treated as executing in no time at all, so a successful package test result will be cached and reused regardless of -timeout setting. -` + strings.TrimSpace(testFlag1) + ` See 'go help testflag' for details. - -For more about build flags, see 'go help build'. -For more about specifying packages, see 'go help packages'. - -See also: go build, go vet. -`, -} - -const testFlag1 = ` In addition to the build flags, the flags handled by 'go test' itself are: -args @@ -164,15 +154,13 @@ In addition to the build flags, the flags handled by 'go test' itself are: The test still runs (unless -c or -i is specified). The test binary also accepts flags that control execution of the test; these -flags are also accessible by 'go test'. -` +flags are also accessible by 'go test'. See 'go help testflag' for details. -// Usage prints the usage message for 'go test -h' and exits. -func Usage() { - os.Stderr.WriteString("usage: " + testUsage + "\n\n" + - strings.TrimSpace(testFlag1) + "\n\n\t" + - strings.TrimSpace(testFlag2) + "\n") - os.Exit(2) +For more about build flags, see 'go help build'. +For more about specifying packages, see 'go help packages'. + +See also: go build, go vet. +`, } var HelpTestflag = &base.Command{ @@ -190,11 +178,6 @@ options of pprof control how the information is presented. The following flags are recognized by the 'go test' command and control the execution of any test: - ` + strings.TrimSpace(testFlag2) + ` -`, -} - -const testFlag2 = ` -bench regexp Run only those benchmarks matching a regular expression. By default, no benchmarks are run. @@ -414,7 +397,8 @@ In the first example, the -x and the second -v are passed through to the test binary unchanged and with no effect on the go command itself. In the second example, the argument math is passed through to the test binary, instead of being interpreted as the package list. -` +`, +} var HelpTestfunc = &base.Command{ UsageLine: "testfunc", @@ -532,7 +516,7 @@ var testVetFlags = []string{ func runTest(cmd *base.Command, args []string) { modload.LoadTests = true - pkgArgs, testArgs = testFlags(args) + pkgArgs, testArgs = testFlags(cmd.Usage, args) work.FindExecCmd() // initialize cached result diff --git a/src/cmd/go/internal/test/testflag.go b/src/cmd/go/internal/test/testflag.go index 73f8c69d9e..ebcf49a4e9 100644 --- a/src/cmd/go/internal/test/testflag.go +++ b/src/cmd/go/internal/test/testflag.go @@ -87,7 +87,7 @@ func init() { // to allow both // go test fmt -custom-flag-for-fmt-test // go test -x math -func testFlags(args []string) (packageNames, passToTest []string) { +func testFlags(usage func(), args []string) (packageNames, passToTest []string) { args = str.StringList(cmdflag.FindGOFLAGS(testFlagDefn), args) inPkg := false var explicitArgs []string @@ -108,7 +108,7 @@ func testFlags(args []string) (packageNames, passToTest []string) { inPkg = false } - f, value, extraWord := cmdflag.Parse(cmd, testFlagDefn, args, i) + f, value, extraWord := cmdflag.Parse(cmd, usage, testFlagDefn, args, i) if f == nil { // This is a flag we do not know; we must assume // that any args we see after this might be flag diff --git a/src/cmd/go/internal/vet/vet.go b/src/cmd/go/internal/vet/vet.go index b64bf3f8e8..616f774bf6 100644 --- a/src/cmd/go/internal/vet/vet.go +++ b/src/cmd/go/internal/vet/vet.go @@ -38,7 +38,7 @@ See also: go fmt, go fix. func runVet(cmd *base.Command, args []string) { modload.LoadTests = true - vetFlags, pkgArgs := vetFlags(args) + vetFlags, pkgArgs := vetFlags(cmd.Usage, args) work.BuildInit() work.VetFlags = vetFlags diff --git a/src/cmd/go/internal/vet/vetflag.go b/src/cmd/go/internal/vet/vetflag.go index cfa4352cb9..9b5184a4d4 100644 --- a/src/cmd/go/internal/vet/vetflag.go +++ b/src/cmd/go/internal/vet/vetflag.go @@ -23,24 +23,43 @@ import ( // go vet flag processing // -// We query the flags of the tool specified by GOVETTOOL (default: -// cmd/vet) and accept any of those flags plus any flag valid for 'go -// build'. The tool must support -flags, which prints a description of -// its flags in JSON to stdout. +// We query the flags of the tool specified by -vettool and accept any +// of those flags plus any flag valid for 'go build'. The tool must +// support -flags, which prints a description of its flags in JSON to +// stdout. -// GOVETTOOL specifies the vet command to run. -// This must be an environment variable because -// we need it before flag processing, as we execute -// $GOVETTOOL to discover the set of flags it supports. +// vetTool specifies the vet command to run. +// Any tool that supports the (still unpublished) vet +// command-line protocol may be supplied; see +// golang.org/x/tools/go/analysis/unitchecker for one +// implementation. It is also used by tests. // -// Using an environment variable also makes it easy for users to opt in -// to (and later, opt out of) the new cmd/vet analysis driver during the -// transition. It is also used by tests. -var vetTool = os.Getenv("GOVETTOOL") +// The default behavior (vetTool=="") runs 'go tool vet'. +// +var vetTool string // -vettool + +func init() { + // Extract -vettool by ad hoc flag processing: + // its value is needed even before we can declare + // the flags available during main flag processing. + for i, arg := range os.Args { + if arg == "-vettool" || arg == "--vettool" { + if i+1 >= len(os.Args) { + log.Fatalf("%s requires a filename", arg) + } + vetTool = os.Args[i+1] + break + } else if strings.HasPrefix(arg, "-vettool=") || + strings.HasPrefix(arg, "--vettool=") { + vetTool = arg[strings.IndexByte(arg, '=')+1:] + break + } + } +} // vetFlags processes the command line, splitting it at the first non-flag // into the list of flags and list of packages. -func vetFlags(args []string) (passToVet, packageNames []string) { +func vetFlags(usage func(), args []string) (passToVet, packageNames []string) { // Query the vet command for its flags. tool := vetTool if tool != "" { @@ -94,6 +113,9 @@ func vetFlags(args []string) (passToVet, packageNames []string) { // Add build flags to vetFlagDefn. var cmd base.Command work.AddBuildFlags(&cmd) + // This flag declaration is a placeholder: + // -vettool is actually parsed by the init function above. + cmd.Flag.StringVar(new(string), "vettool", "", "path to vet tool binary") cmd.Flag.VisitAll(func(f *flag.Flag) { vetFlagDefn = append(vetFlagDefn, &cmdflag.Defn{ Name: f.Name, @@ -108,7 +130,7 @@ func vetFlags(args []string) (passToVet, packageNames []string) { return args[:i], args[i:] } - f, value, extraWord := cmdflag.Parse("vet", vetFlagDefn, args, i) + f, value, extraWord := cmdflag.Parse("vet", usage, vetFlagDefn, args, i) if f == nil { fmt.Fprintf(os.Stderr, "vet: flag %q not defined\n", args[i]) fmt.Fprintf(os.Stderr, "Run \"go help vet\" for more information\n") diff --git a/src/cmd/go/internal/work/buildid.go b/src/cmd/go/internal/work/buildid.go index a6cfb50558..c5aa1db50b 100644 --- a/src/cmd/go/internal/work/buildid.go +++ b/src/cmd/go/internal/work/buildid.go @@ -178,7 +178,8 @@ func (b *Builder) toolID(name string) string { path := base.Tool(name) desc := "go tool " + name - // Special case: undocumented $GOVETTOOL overrides usual vet, for testing vet. + // Special case: undocumented -vettool overrides usual vet, + // for testing vet or supplying an alternative analysis tool. if name == "vet" && VetTool != "" { path = VetTool desc = VetTool diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go index d9c59aab80..92e814ee6f 100644 --- a/src/cmd/go/internal/work/exec.go +++ b/src/cmd/go/internal/work/exec.go @@ -434,10 +434,6 @@ func (b *Builder) build(a *Action) (err error) { return fmt.Errorf("missing or invalid binary-only package; expected file %q", a.Package.Target) } - if p.Module != nil && !allowedVersion(p.Module.GoVersion) { - return fmt.Errorf("module requires Go %s", p.Module.GoVersion) - } - if err := b.Mkdir(a.Objdir); err != nil { return err } @@ -638,12 +634,19 @@ func (b *Builder) build(a *Action) (err error) { objpkg := objdir + "_pkg_.a" ofile, out, err := BuildToolchain.gc(b, a, objpkg, icfg.Bytes(), len(sfiles) > 0, gofiles) if len(out) > 0 { - b.showOutput(a, a.Package.Dir, a.Package.Desc(), b.processOutput(out)) + output := b.processOutput(out) + if p.Module != nil && !allowedVersion(p.Module.GoVersion) { + output += "note: module requires Go " + p.Module.GoVersion + } + b.showOutput(a, a.Package.Dir, a.Package.Desc(), output) if err != nil { return errPrintedOutput } } if err != nil { + if p.Module != nil && !allowedVersion(p.Module.GoVersion) { + b.showOutput(a, a.Package.Dir, a.Package.Desc(), "note: module requires Go "+p.Module.GoVersion) + } return err } if ofile != objpkg { diff --git a/src/cmd/go/internal/work/gc.go b/src/cmd/go/internal/work/gc.go index 6e5333ccbc..5a0bd1c2cf 100644 --- a/src/cmd/go/internal/work/gc.go +++ b/src/cmd/go/internal/work/gc.go @@ -53,6 +53,9 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg []byte, a pkgpath = "main" } gcargs := []string{"-p", pkgpath} + if p.Module != nil && p.Module.GoVersion != "" && allowedVersion(p.Module.GoVersion) { + gcargs = append(gcargs, "-lang=go"+p.Module.GoVersion) + } if p.Standard { gcargs = append(gcargs, "-std") } diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go index d6934ce5e9..6a188262cc 100644 --- a/src/cmd/go/main.go +++ b/src/cmd/go/main.go @@ -235,17 +235,6 @@ func init() { } func mainUsage() { - // special case "go test -h" - if len(os.Args) > 1 && os.Args[1] == "test" { - test.Usage() - } - // Since vet shares code with test in cmdflag, it doesn't show its - // command usage properly. For now, special case it too. - // TODO(mvdan): fix the cmdflag package instead; see - // golang.org/issue/26999 - if len(os.Args) > 1 && os.Args[1] == "vet" { - vet.CmdVet.Usage() - } help.PrintUsage(os.Stderr, base.Go) os.Exit(2) } diff --git a/src/cmd/go/script_test.go b/src/cmd/go/script_test.go index f03d9840ca..31ddf02fb0 100644 --- a/src/cmd/go/script_test.go +++ b/src/cmd/go/script_test.go @@ -11,6 +11,7 @@ import ( "bytes" "context" "fmt" + "go/build" "internal/testenv" "io/ioutil" "os" @@ -104,6 +105,7 @@ func (ts *testScript) setup() { "GOROOT=" + testGOROOT, tempEnvName() + "=" + filepath.Join(ts.workdir, "tmp"), "devnull=" + os.DevNull, + "goversion=" + goVersion(ts), ":=" + string(os.PathListSeparator), } @@ -130,6 +132,16 @@ func (ts *testScript) setup() { } } +// goVersion returns the current Go version. +func goVersion(ts *testScript) string { + tags := build.Default.ReleaseTags + version := tags[len(tags)-1] + if !regexp.MustCompile(`^go([1-9][0-9]*)\.(0|[1-9][0-9]*)$`).MatchString(version) { + ts.fatalf("invalid go version %q", version) + } + return version[2:] +} + var execCache par.Cache // run runs the test script. @@ -331,6 +343,7 @@ var scriptCmds = map[string]func(*testScript, bool, []string){ "addcrlf": (*testScript).cmdAddcrlf, "cd": (*testScript).cmdCd, "cmp": (*testScript).cmdCmp, + "cmpenv": (*testScript).cmdCmpenv, "cp": (*testScript).cmdCp, "env": (*testScript).cmdEnv, "exec": (*testScript).cmdExec, @@ -396,7 +409,21 @@ func (ts *testScript) cmdCmp(neg bool, args []string) { if len(args) != 2 { ts.fatalf("usage: cmp file1 file2") } + ts.doCmdCmp(args, false) +} +// cmpenv compares two files with environment variable substitution. +func (ts *testScript) cmdCmpenv(neg bool, args []string) { + if neg { + ts.fatalf("unsupported: ! cmpenv") + } + if len(args) != 2 { + ts.fatalf("usage: cmpenv file1 file2") + } + ts.doCmdCmp(args, true) +} + +func (ts *testScript) doCmdCmp(args []string, env bool) { name1, name2 := args[0], args[1] var text1, text2 string if name1 == "stdout" { @@ -413,6 +440,11 @@ func (ts *testScript) cmdCmp(neg bool, args []string) { ts.check(err) text2 = string(data) + if env { + text1 = ts.expand(text1) + text2 = ts.expand(text2) + } + if text1 == text2 { return } diff --git a/src/cmd/go/testdata/script/README b/src/cmd/go/testdata/script/README index f28f1b87ed..22124b9fb8 100644 --- a/src/cmd/go/testdata/script/README +++ b/src/cmd/go/testdata/script/README @@ -36,6 +36,7 @@ Scripts also have access to these other environment variables: PATH= TMPDIR=$WORK/tmp devnull= + goversion= The environment variable $exe (lowercase) is an empty string on most systems, ".exe" on Windows. @@ -92,6 +93,10 @@ The commands are: from the most recent exec or go command. (If the files have differing content, the failure prints a diff.) +- cmpenv file1 file2 + Like cmp, but environment variables are substituted in the file contents + before the comparison. For example, $GOOS is replaced by the target GOOS. + - cp src... dst Copy the listed files to the target file or existing directory. diff --git a/src/cmd/go/testdata/script/help.txt b/src/cmd/go/testdata/script/help.txt index 656e680100..3d0650880e 100644 --- a/src/cmd/go/testdata/script/help.txt +++ b/src/cmd/go/testdata/script/help.txt @@ -35,7 +35,13 @@ stderr 'Run ''go help mod'' for usage.' stderr 'usage: go vet' stderr 'Run ''go help vet'' for details' +# Earlier versions of Go printed a large document here, instead of these two +# lines. +! go test -h +stderr 'usage: go test' +stderr 'Run ''go help test'' for details' + # go help get shows usage for get go help get stdout 'usage: go get' -stdout 'get when using GOPATH' \ No newline at end of file +stdout 'get when using GOPATH' diff --git a/src/cmd/go/testdata/script/mod_edit.txt b/src/cmd/go/testdata/script/mod_edit.txt index 60a6f74536..61801d5021 100644 --- a/src/cmd/go/testdata/script/mod_edit.txt +++ b/src/cmd/go/testdata/script/mod_edit.txt @@ -10,37 +10,37 @@ stderr 'cannot determine module path' go mod init x.x/y/z stderr 'creating new go.mod: module x.x/y/z' -cmp go.mod $WORK/go.mod.init +cmpenv go.mod $WORK/go.mod.init ! go mod init -cmp go.mod $WORK/go.mod.init +cmpenv go.mod $WORK/go.mod.init # go mod edits go mod edit -droprequire=x.1 -require=x.1@v1.0.0 -require=x.2@v1.1.0 -droprequire=x.2 -exclude='x.1 @ v1.2.0' -exclude=x.1@v1.2.1 -replace=x.1@v1.3.0=y.1@v1.4.0 -replace='x.1@v1.4.0 = ../z' -cmp go.mod $WORK/go.mod.edit1 +cmpenv go.mod $WORK/go.mod.edit1 go mod edit -droprequire=x.1 -dropexclude=x.1@v1.2.1 -dropreplace=x.1@v1.3.0 -require=x.3@v1.99.0 -cmp go.mod $WORK/go.mod.edit2 +cmpenv go.mod $WORK/go.mod.edit2 # go mod edit -json go mod edit -json -cmp stdout $WORK/go.mod.json +cmpenv stdout $WORK/go.mod.json # go mod edit -replace go mod edit -replace=x.1@v1.3.0=y.1/v2@v2.3.5 -replace=x.1@v1.4.0=y.1/v2@v2.3.5 -cmp go.mod $WORK/go.mod.edit3 +cmpenv go.mod $WORK/go.mod.edit3 go mod edit -replace=x.1=y.1/v2@v2.3.6 -cmp go.mod $WORK/go.mod.edit4 +cmpenv go.mod $WORK/go.mod.edit4 go mod edit -dropreplace=x.1 -cmp go.mod $WORK/go.mod.edit5 +cmpenv go.mod $WORK/go.mod.edit5 # go mod edit -fmt cp $WORK/go.mod.badfmt go.mod go mod edit -fmt -print # -print should avoid writing file -cmp stdout $WORK/go.mod.edit4 +cmpenv stdout $WORK/go.mod.edit6 cmp go.mod $WORK/go.mod.badfmt go mod edit -fmt # without -print, should write file (and nothing to stdout) ! stdout . -cmp go.mod $WORK/go.mod.edit4 +cmpenv go.mod $WORK/go.mod.edit6 -- x.go -- package x @@ -50,9 +50,13 @@ package w -- $WORK/go.mod.init -- module x.x/y/z + +go $goversion -- $WORK/go.mod.edit1 -- module x.x/y/z +go $goversion + require x.1 v1.0.0 exclude ( @@ -67,6 +71,8 @@ replace ( -- $WORK/go.mod.edit2 -- module x.x/y/z +go $goversion + exclude x.1 v1.2.0 replace x.1 v1.4.0 => ../z @@ -77,6 +83,7 @@ require x.3 v1.99.0 "Module": { "Path": "x.x/y/z" }, + "Go": "$goversion", "Require": [ { "Path": "x.3", @@ -104,6 +111,8 @@ require x.3 v1.99.0 -- $WORK/go.mod.edit3 -- module x.x/y/z +go $goversion + exclude x.1 v1.2.0 replace ( @@ -115,6 +124,8 @@ require x.3 v1.99.0 -- $WORK/go.mod.edit4 -- module x.x/y/z +go $goversion + exclude x.1 v1.2.0 replace x.1 => y.1/v2 v2.3.6 @@ -123,12 +134,26 @@ require x.3 v1.99.0 -- $WORK/go.mod.edit5 -- module x.x/y/z +go $goversion + exclude x.1 v1.2.0 +require x.3 v1.99.0 +-- $WORK/go.mod.edit6 -- +module x.x/y/z + +go 1.10 + +exclude x.1 v1.2.0 + +replace x.1 => y.1/v2 v2.3.6 + require x.3 v1.99.0 -- $WORK/go.mod.badfmt -- module x.x/y/z +go 1.10 + exclude x.1 v1.2.0 replace x.1 => y.1/v2 v2.3.6 diff --git a/src/cmd/go/testdata/script/mod_edit_go.txt b/src/cmd/go/testdata/script/mod_edit_go.txt new file mode 100644 index 0000000000..3ec8137e2d --- /dev/null +++ b/src/cmd/go/testdata/script/mod_edit_go.txt @@ -0,0 +1,16 @@ +# Test support for go mod -edit to set language version. + +env GO111MODULE=on +! go build +stderr 'type aliases only supported as of' +go mod edit -go=1.9 +grep 'go 1.9' go.mod +go build + +-- go.mod -- +module m +go 1.8 + +-- alias.go -- +package alias +type T = int diff --git a/src/cmd/go/testdata/script/mod_go_version.txt b/src/cmd/go/testdata/script/mod_go_version.txt index f2de74cee8..37f173531b 100644 --- a/src/cmd/go/testdata/script/mod_go_version.txt +++ b/src/cmd/go/testdata/script/mod_go_version.txt @@ -3,9 +3,10 @@ env GO111MODULE=on go list -! go build -stderr 'module requires Go 1.999' +go build go build sub.1 +go build subver.1 +! stderr 'module requires' ! go build badsub.1 stderr 'module requires Go 1.11111' @@ -19,11 +20,13 @@ module m go 1.999 require ( sub.1 v1.0.0 + subver.1 v1.0.0 badsub.1 v1.0.0 versioned.1 v1.0.0 ) replace ( sub.1 => ./sub + subver.1 => ./subver badsub.1 => ./badsub versioned.1 v1.0.0 => ./versioned1 versioned.1 v1.1.0 => ./versioned2 @@ -39,12 +42,20 @@ go 1.11 -- sub/x.go -- package x +-- subver/go.mod -- +module m +go 1.11111 + +-- subver/x.go -- +package x + -- badsub/go.mod -- module m go 1.11111 -- badsub/x.go -- package x +invalid syntax -- versioned1/go.mod -- module versioned @@ -59,3 +70,4 @@ go 1.99999 -- versioned2/x.go -- package x +invalid syntax diff --git a/src/cmd/go/testdata/script/mod_readonly.txt b/src/cmd/go/testdata/script/mod_readonly.txt index 1b5932e441..188a66d0e1 100644 --- a/src/cmd/go/testdata/script/mod_readonly.txt +++ b/src/cmd/go/testdata/script/mod_readonly.txt @@ -37,6 +37,8 @@ cmp go.mod go.mod.inconsistent -- go.mod -- module m +go 1.20 + -- x.go -- package x import _ "rsc.io/quote" diff --git a/src/cmd/go/testdata/script/mod_tidy.txt b/src/cmd/go/testdata/script/mod_tidy.txt index 449aa073a7..de3b52e2c0 100644 --- a/src/cmd/go/testdata/script/mod_tidy.txt +++ b/src/cmd/go/testdata/script/mod_tidy.txt @@ -5,6 +5,9 @@ go mod tidy -v stderr '^unused y.1' ! stderr '^unused [^y]' +# tidy should not touch existing go line +grep 'go 1.10' go.mod + go list -m all ! stdout '^y' stdout '^w.1 v1.2.0' @@ -12,11 +15,17 @@ stdout '^z.1 v1.2.0' # empty tidy should not crash cd triv +! grep 'go ' go.mod go mod tidy +# tidy should add missing go line +grep 'go ' go.mod + -- go.mod -- module m +go 1.10 + require ( x.1 v1.0.0 y.1 v1.0.0 diff --git a/src/cmd/internal/dwarf/dwarf.go b/src/cmd/internal/dwarf/dwarf.go index 21512a80bd..8ad84105a4 100644 --- a/src/cmd/internal/dwarf/dwarf.go +++ b/src/cmd/internal/dwarf/dwarf.go @@ -967,7 +967,7 @@ Outer: } } -// HasChildren returns true if 'die' uses an abbrev that supports children. +// HasChildren reports whether 'die' uses an abbrev that supports children. func HasChildren(die *DWDie) bool { return abbrevs[die.Abbrev].children != 0 } diff --git a/src/cmd/internal/obj/arm64/a.out.go b/src/cmd/internal/obj/arm64/a.out.go index c4c75e41d4..18cdd10f9b 100644 --- a/src/cmd/internal/obj/arm64/a.out.go +++ b/src/cmd/internal/obj/arm64/a.out.go @@ -414,6 +414,8 @@ const ( C_BITCON // bitfield and logical immediate masks C_ADDCON2 // 24-bit constant C_LCON // 32-bit constant + C_MOVCON2 // a constant that can be loaded with one MOVZ/MOVN and one MOVK + C_MOVCON3 // a constant that can be loaded with one MOVZ/MOVN and two MOVKs C_VCON // 64-bit constant C_FCON // floating-point constant C_VCONADDR // 64-bit memory address diff --git a/src/cmd/internal/obj/arm64/anames7.go b/src/cmd/internal/obj/arm64/anames7.go index f8fdc68c1e..e1703fc4ab 100644 --- a/src/cmd/internal/obj/arm64/anames7.go +++ b/src/cmd/internal/obj/arm64/anames7.go @@ -30,6 +30,8 @@ var cnames7 = []string{ "BITCON", "ADDCON2", "LCON", + "MOVCON2", + "MOVCON3", "VCON", "FCON", "VCONADDR", diff --git a/src/cmd/internal/obj/arm64/asm7.go b/src/cmd/internal/obj/arm64/asm7.go index 6a6e81807a..9746426d90 100644 --- a/src/cmd/internal/obj/arm64/asm7.go +++ b/src/cmd/internal/obj/arm64/asm7.go @@ -198,9 +198,15 @@ var optab = []Optab{ {ACMP, C_BITCON, C_RSP, C_NONE, C_NONE, 62, 8, 0, 0, 0}, {AADD, C_ADDCON2, C_RSP, C_NONE, C_RSP, 48, 8, 0, 0, 0}, {AADD, C_ADDCON2, C_NONE, C_NONE, C_RSP, 48, 8, 0, 0, 0}, - {AADD, C_VCON, C_RSP, C_NONE, C_RSP, 13, 8, 0, LFROM, 0}, - {AADD, C_VCON, C_NONE, C_NONE, C_RSP, 13, 8, 0, LFROM, 0}, - {ACMP, C_VCON, C_REG, C_NONE, C_NONE, 13, 8, 0, LFROM, 0}, + {AADD, C_MOVCON2, C_RSP, C_NONE, C_RSP, 13, 12, 0, 0, 0}, + {AADD, C_MOVCON2, C_NONE, C_NONE, C_RSP, 13, 12, 0, 0, 0}, + {AADD, C_MOVCON3, C_RSP, C_NONE, C_RSP, 13, 16, 0, 0, 0}, + {AADD, C_MOVCON3, C_NONE, C_NONE, C_RSP, 13, 16, 0, 0, 0}, + {AADD, C_VCON, C_RSP, C_NONE, C_RSP, 13, 20, 0, 0, 0}, + {AADD, C_VCON, C_NONE, C_NONE, C_RSP, 13, 20, 0, 0, 0}, + {ACMP, C_MOVCON2, C_REG, C_NONE, C_NONE, 13, 12, 0, 0, 0}, + {ACMP, C_MOVCON3, C_REG, C_NONE, C_NONE, 13, 16, 0, 0, 0}, + {ACMP, C_VCON, C_REG, C_NONE, C_NONE, 13, 20, 0, 0, 0}, {AADD, C_SHIFT, C_REG, C_NONE, C_REG, 3, 4, 0, 0, 0}, {AADD, C_SHIFT, C_NONE, C_NONE, C_REG, 3, 4, 0, 0, 0}, {AMVN, C_SHIFT, C_NONE, C_NONE, C_REG, 3, 4, 0, 0, 0}, @@ -255,11 +261,21 @@ var optab = []Optab{ {AANDS, C_MOVCON, C_REG, C_NONE, C_REG, 62, 8, 0, 0, 0}, {AANDS, C_MOVCON, C_NONE, C_NONE, C_REG, 62, 8, 0, 0, 0}, {ATST, C_MOVCON, C_REG, C_NONE, C_NONE, 62, 8, 0, 0, 0}, - {AAND, C_VCON, C_REG, C_NONE, C_REG, 28, 8, 0, LFROM, 0}, - {AAND, C_VCON, C_NONE, C_NONE, C_REG, 28, 8, 0, LFROM, 0}, - {AANDS, C_VCON, C_REG, C_NONE, C_REG, 28, 8, 0, LFROM, 0}, - {AANDS, C_VCON, C_NONE, C_NONE, C_REG, 28, 8, 0, LFROM, 0}, - {ATST, C_VCON, C_REG, C_NONE, C_NONE, 28, 8, 0, LFROM, 0}, + {AAND, C_MOVCON2, C_REG, C_NONE, C_REG, 28, 12, 0, 0, 0}, + {AAND, C_MOVCON2, C_NONE, C_NONE, C_REG, 28, 12, 0, 0, 0}, + {AAND, C_MOVCON3, C_REG, C_NONE, C_REG, 28, 16, 0, 0, 0}, + {AAND, C_MOVCON3, C_NONE, C_NONE, C_REG, 28, 16, 0, 0, 0}, + {AAND, C_VCON, C_REG, C_NONE, C_REG, 28, 20, 0, 0, 0}, + {AAND, C_VCON, C_NONE, C_NONE, C_REG, 28, 20, 0, 0, 0}, + {AANDS, C_MOVCON2, C_REG, C_NONE, C_REG, 28, 12, 0, 0, 0}, + {AANDS, C_MOVCON2, C_NONE, C_NONE, C_REG, 28, 12, 0, 0, 0}, + {AANDS, C_MOVCON3, C_REG, C_NONE, C_REG, 28, 16, 0, 0, 0}, + {AANDS, C_MOVCON3, C_NONE, C_NONE, C_REG, 28, 16, 0, 0, 0}, + {AANDS, C_VCON, C_REG, C_NONE, C_REG, 28, 20, 0, 0, 0}, + {AANDS, C_VCON, C_NONE, C_NONE, C_REG, 28, 20, 0, 0, 0}, + {ATST, C_MOVCON2, C_REG, C_NONE, C_NONE, 28, 12, 0, 0, 0}, + {ATST, C_MOVCON3, C_REG, C_NONE, C_NONE, 28, 16, 0, 0, 0}, + {ATST, C_VCON, C_REG, C_NONE, C_NONE, 28, 20, 0, 0, 0}, {AAND, C_SHIFT, C_REG, C_NONE, C_REG, 3, 4, 0, 0, 0}, {AAND, C_SHIFT, C_NONE, C_NONE, C_REG, 3, 4, 0, 0, 0}, {AANDS, C_SHIFT, C_REG, C_NONE, C_REG, 3, 4, 0, 0, 0}, @@ -278,8 +294,10 @@ var optab = []Optab{ {AMOVD, C_MOVCON, C_NONE, C_NONE, C_REG, 32, 4, 0, 0, 0}, {AMOVW, C_BITCON, C_NONE, C_NONE, C_REG, 32, 4, 0, 0, 0}, {AMOVD, C_BITCON, C_NONE, C_NONE, C_REG, 32, 4, 0, 0, 0}, - {AMOVW, C_LCON, C_NONE, C_NONE, C_REG, 12, 4, 0, LFROM, 0}, - {AMOVD, C_VCON, C_NONE, C_NONE, C_REG, 12, 4, 0, LFROM, 0}, + {AMOVW, C_MOVCON2, C_NONE, C_NONE, C_REG, 12, 8, 0, 0, 0}, + {AMOVD, C_MOVCON2, C_NONE, C_NONE, C_REG, 12, 8, 0, 0, 0}, + {AMOVD, C_MOVCON3, C_NONE, C_NONE, C_REG, 12, 12, 0, 0, 0}, + {AMOVD, C_VCON, C_NONE, C_NONE, C_REG, 12, 16, 0, 0, 0}, {AMOVK, C_VCON, C_NONE, C_NONE, C_REG, 33, 4, 0, 0, 0}, {AMOVD, C_AACON, C_NONE, C_NONE, C_REG, 4, 4, REGFROM, 0, 0}, @@ -401,8 +419,8 @@ var optab = []Optab{ {AMOVH, C_REG, C_NONE, C_NONE, C_NSOREG, 20, 4, 0, 0, 0}, {AMOVW, C_REG, C_NONE, C_NONE, C_NSAUTO, 20, 4, REGSP, 0, 0}, {AMOVW, C_REG, C_NONE, C_NONE, C_NSOREG, 20, 4, 0, 0, 0}, - {AMOVD, C_REG, C_NONE, C_NONE, C_NSOREG, 20, 4, 0, 0, 0}, {AMOVD, C_REG, C_NONE, C_NONE, C_NSAUTO, 20, 4, REGSP, 0, 0}, + {AMOVD, C_REG, C_NONE, C_NONE, C_NSOREG, 20, 4, 0, 0, 0}, {AFMOVS, C_FREG, C_NONE, C_NONE, C_NSAUTO, 20, 4, REGSP, 0, 0}, {AFMOVS, C_FREG, C_NONE, C_NONE, C_NSOREG, 20, 4, 0, 0, 0}, @@ -411,15 +429,15 @@ var optab = []Optab{ /* scaled 12-bit unsigned displacement load */ {AMOVB, C_UAUTO4K, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, - {AMOVB, C_UOREG4K, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, + {AMOVB, C_UOREG4K, C_NONE, C_NONE, C_REG, 21, 4, 0, 0, 0}, {AMOVBU, C_UAUTO4K, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, - {AMOVBU, C_UOREG4K, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, + {AMOVBU, C_UOREG4K, C_NONE, C_NONE, C_REG, 21, 4, 0, 0, 0}, {AMOVH, C_UAUTO8K, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, - {AMOVH, C_UOREG8K, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, + {AMOVH, C_UOREG8K, C_NONE, C_NONE, C_REG, 21, 4, 0, 0, 0}, {AMOVW, C_UAUTO16K, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, - {AMOVW, C_UOREG16K, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, + {AMOVW, C_UOREG16K, C_NONE, C_NONE, C_REG, 21, 4, 0, 0, 0}, {AMOVD, C_UAUTO32K, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, - {AMOVD, C_UOREG32K, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, + {AMOVD, C_UOREG32K, C_NONE, C_NONE, C_REG, 21, 4, 0, 0, 0}, {AFMOVS, C_UAUTO16K, C_NONE, C_NONE, C_FREG, 21, 4, REGSP, 0, 0}, {AFMOVS, C_UOREG16K, C_NONE, C_NONE, C_FREG, 21, 4, 0, 0, 0}, @@ -428,15 +446,15 @@ var optab = []Optab{ /* unscaled 9-bit signed displacement load */ {AMOVB, C_NSAUTO, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, - {AMOVB, C_NSOREG, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, + {AMOVB, C_NSOREG, C_NONE, C_NONE, C_REG, 21, 4, 0, 0, 0}, {AMOVBU, C_NSAUTO, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, - {AMOVBU, C_NSOREG, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, + {AMOVBU, C_NSOREG, C_NONE, C_NONE, C_REG, 21, 4, 0, 0, 0}, {AMOVH, C_NSAUTO, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, - {AMOVH, C_NSOREG, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, + {AMOVH, C_NSOREG, C_NONE, C_NONE, C_REG, 21, 4, 0, 0, 0}, {AMOVW, C_NSAUTO, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, - {AMOVW, C_NSOREG, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, + {AMOVW, C_NSOREG, C_NONE, C_NONE, C_REG, 21, 4, 0, 0, 0}, {AMOVD, C_NSAUTO, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, - {AMOVD, C_NSOREG, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, + {AMOVD, C_NSOREG, C_NONE, C_NONE, C_REG, 21, 4, 0, 0, 0}, {AFMOVS, C_NSAUTO, C_NONE, C_NONE, C_FREG, 21, 4, REGSP, 0, 0}, {AFMOVS, C_NSOREG, C_NONE, C_NONE, C_FREG, 21, 4, 0, 0, 0}, @@ -1105,6 +1123,15 @@ func isSTXPop(op obj.As) bool { return false } +func isANDop(op obj.As) bool { + switch op { + case AAND, AORR, AEOR, AANDS, ATST, + ABIC, AEON, AORN, ABICS: + return true + } + return false +} + func isANDWop(op obj.As) bool { switch op { case AANDW, AORRW, AEORW, AANDSW, ATSTW, @@ -1114,6 +1141,14 @@ func isANDWop(op obj.As) bool { return false } +func isADDop(op obj.As) bool { + switch op { + case AADD, AADDS, ASUB, ASUBS, ACMN, ACMP: + return true + } + return false +} + func isADDWop(op obj.As) bool { switch op { case AADDW, AADDSW, ASUBW, ASUBSW, ACMNW, ACMPW: @@ -1445,6 +1480,12 @@ func (c *ctxt7) con32class(a *obj.Addr) int { if isbitcon(uint64(v)) { return C_ABCON } + if movcon(int64(v)) >= 0 { + return C_AMCON + } + if movcon(int64(^v)) >= 0 { + return C_AMCON + } return C_ADDCON } @@ -1474,6 +1515,29 @@ func (c *ctxt7) con32class(a *obj.Addr) int { return C_LCON } +// con64class reclassifies the constant of C_VCON and C_LCON class. +func (c *ctxt7) con64class(a *obj.Addr) int { + zeroCount := 0 + negCount := 0 + for i := uint(0); i < 4; i++ { + immh := uint32(a.Offset >> (i * 16) & 0xffff) + if immh == 0 { + zeroCount++ + } else if immh == 0xffff { + negCount++ + } + } + if zeroCount >= 3 || negCount >= 3 { + return C_MOVCON + } else if zeroCount == 2 || negCount == 2 { + return C_MOVCON2 + } else if zeroCount == 1 || negCount == 1 { + return C_MOVCON3 + } else { + return C_VCON + } +} + func (c *ctxt7) aclass(a *obj.Addr) int { switch a.Type { case obj.TYPE_NONE: @@ -1689,21 +1753,18 @@ func (c *ctxt7) oplook(p *obj.Prog) *Optab { a1 = ra0 + 1 p.From.Class = int8(a1) } - if isANDWop(p.As) { - switch p.As { - case AANDW, AORRW, AEORW, AANDSW, ATSTW: - // For 32-bit logical instruction with constant, - // rewrite the high 32-bit to be a copy of the low - // 32-bit, so that the BITCON test can be shared - // for both 32-bit and 64-bit. - if a0 == C_BITCON { - break - } - fallthrough - default: - a1 = c.con32class(&p.From) + 1 - p.From.Class = int8(a1) - } + if isANDWop(p.As) && a0 != C_BITCON { + // For 32-bit logical instruction with constant, + // the BITCON test is special in that it looks at + // the 64-bit which has the high 32-bit as a copy + // of the low 32-bit. We have handled that and + // don't pass it to con32class. + a1 = c.con32class(&p.From) + 1 + p.From.Class = int8(a1) + } + if ((p.As == AMOVD) || isANDop(p.As) || isADDop(p.As)) && (a0 == C_LCON || a0 == C_VCON) { + a1 = c.con64class(&p.From) + 1 + p.From.Class = int8(a1) } } } @@ -1800,6 +1861,9 @@ func cmp(a int, b int) bool { return true } + case C_MOVCON2: + return cmp(C_LCON, b) + case C_VCON: return cmp(C_LCON, b) @@ -2718,6 +2782,7 @@ func (c *ctxt7) checkShiftAmount(p *obj.Prog, a *obj.Addr) { } func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { + var os [5]uint32 o1 := uint32(0) o2 := uint32(0) o3 := uint32(0) @@ -2907,13 +2972,29 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { } case 12: /* movT $vcon, reg */ - o1 = c.omovlit(p.As, p, &p.From, int(p.To.Reg)) + num := c.omovlconst(p.As, p, &p.From, int(p.To.Reg), os[:]) + if num == 0 { + c.ctxt.Diag("invalid constant: %v", p) + } + o1 = os[0] + o2 = os[1] + o3 = os[2] + o4 = os[3] case 13: /* addop $vcon, [R], R (64 bit literal); cmp $lcon,R -> addop $lcon,R, ZR */ - o1 = c.omovlit(AMOVD, p, &p.From, REGTMP) - - if o1 == 0 { - break + o := uint32(0) + num := uint8(0) + cls := oclass(&p.From) + if isADDWop(p.As) { + if (cls != C_LCON) && (cls != C_ADDCON2) { + c.ctxt.Diag("illegal combination: %v", p) + } + num = c.omovlconst(AMOVW, p, &p.From, REGTMP, os[:]) + } else { + num = c.omovlconst(AMOVD, p, &p.From, REGTMP, os[:]) + } + if num == 0 { + c.ctxt.Diag("invalid constant: %v", p) } rt := int(p.To.Reg) if p.To.Type == obj.TYPE_NONE { @@ -2924,16 +3005,23 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { r = rt } if p.To.Type != obj.TYPE_NONE && (p.To.Reg == REGSP || r == REGSP) { - o2 = c.opxrrr(p, p.As, false) - o2 |= REGTMP & 31 << 16 - o2 |= LSL0_64 + o = c.opxrrr(p, p.As, false) + o |= REGTMP & 31 << 16 + o |= LSL0_64 } else { - o2 = c.oprrr(p, p.As) - o2 |= REGTMP & 31 << 16 /* shift is 0 */ + o = c.oprrr(p, p.As) + o |= REGTMP & 31 << 16 /* shift is 0 */ } - o2 |= uint32(r&31) << 5 - o2 |= uint32(rt & 31) + o |= uint32(r&31) << 5 + o |= uint32(rt & 31) + + os[num] = o + o1 = os[0] + o2 = os[1] + o3 = os[2] + o4 = os[3] + o5 = os[4] case 14: /* word */ if c.aclass(&p.To) == C_ADDR { @@ -3179,10 +3267,20 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { o1 |= (uint32(r&31) << 5) | uint32(rt&31) case 28: /* logop $vcon, [R], R (64 bit literal) */ - o1 = c.omovlit(AMOVD, p, &p.From, REGTMP) + o := uint32(0) + num := uint8(0) + cls := oclass(&p.From) + if isANDWop(p.As) { + if (cls != C_LCON) && (cls != C_ADDCON) { + c.ctxt.Diag("illegal combination: %v", p) + } + num = c.omovlconst(AMOVW, p, &p.From, REGTMP, os[:]) + } else { + num = c.omovlconst(AMOVD, p, &p.From, REGTMP, os[:]) + } - if o1 == 0 { - break + if num == 0 { + c.ctxt.Diag("invalid constant: %v", p) } rt := int(p.To.Reg) if p.To.Type == obj.TYPE_NONE { @@ -3192,10 +3290,17 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { if r == 0 { r = rt } - o2 = c.oprrr(p, p.As) - o2 |= REGTMP & 31 << 16 /* shift is 0 */ - o2 |= uint32(r&31) << 5 - o2 |= uint32(rt & 31) + o = c.oprrr(p, p.As) + o |= REGTMP & 31 << 16 /* shift is 0 */ + o |= uint32(r&31) << 5 + o |= uint32(rt & 31) + + os[num] = o + o1 = os[0] + o2 = os[1] + o3 = os[2] + o4 = os[3] + o5 = os[4] case 29: /* op Rn, Rd */ fc := c.aclass(&p.From) @@ -6326,10 +6431,155 @@ func (c *ctxt7) omovconst(as obj.As, p *obj.Prog, a *obj.Addr, rt int) (o1 uint3 } o1 |= MOVCONST(d, s, rt) } - return o1 } +// load a 32-bit/64-bit large constant (LCON or VCON) in a.Offset into rt +// put the instruction sequence in os and return the number of instructions. +func (c *ctxt7) omovlconst(as obj.As, p *obj.Prog, a *obj.Addr, rt int, os []uint32) (num uint8) { + switch as { + case AMOVW: + d := uint32(a.Offset) + // use MOVZW and MOVKW to load a constant to rt + os[0] = c.opirr(p, AMOVZW) + os[0] |= MOVCONST(int64(d), 0, rt) + os[1] = c.opirr(p, AMOVKW) + os[1] |= MOVCONST(int64(d), 1, rt) + return 2 + + case AMOVD: + d := a.Offset + dn := ^d + var immh [4]uint64 + var i int + zeroCount := int(0) + negCount := int(0) + for i = 0; i < 4; i++ { + immh[i] = uint64((d >> uint(i*16)) & 0xffff) + if immh[i] == 0 { + zeroCount++ + } else if immh[i] == 0xffff { + negCount++ + } + } + + if zeroCount == 4 || negCount == 4 { + c.ctxt.Diag("the immediate should be MOVCON: %v", p) + } + switch { + case zeroCount == 3: + // one MOVZ + for i = 0; i < 4; i++ { + if immh[i] != 0 { + os[0] = c.opirr(p, AMOVZ) + os[0] |= MOVCONST(d, i, rt) + break + } + } + return 1 + + case negCount == 3: + // one MOVN + for i = 0; i < 4; i++ { + if immh[i] != 0xffff { + os[0] = c.opirr(p, AMOVN) + os[0] |= MOVCONST(dn, i, rt) + break + } + } + return 1 + + case zeroCount == 2: + // one MOVZ and one MOVK + for i = 0; i < 4; i++ { + if immh[i] != 0 { + os[0] = c.opirr(p, AMOVZ) + os[0] |= MOVCONST(d, i, rt) + i++ + break + } + } + for ; i < 4; i++ { + if immh[i] != 0 { + os[1] = c.opirr(p, AMOVK) + os[1] |= MOVCONST(d, i, rt) + } + } + return 2 + + case negCount == 2: + // one MOVN and one MOVK + for i = 0; i < 4; i++ { + if immh[i] != 0xffff { + os[0] = c.opirr(p, AMOVN) + os[0] |= MOVCONST(dn, i, rt) + i++ + break + } + } + for ; i < 4; i++ { + if immh[i] != 0xffff { + os[1] = c.opirr(p, AMOVK) + os[1] |= MOVCONST(d, i, rt) + } + } + return 2 + + case zeroCount == 1: + // one MOVZ and two MOVKs + for i = 0; i < 4; i++ { + if immh[i] != 0 { + os[0] = c.opirr(p, AMOVZ) + os[0] |= MOVCONST(d, i, rt) + i++ + break + } + } + + for j := 1; i < 4; i++ { + if immh[i] != 0 { + os[j] = c.opirr(p, AMOVK) + os[j] |= MOVCONST(d, i, rt) + j++ + } + } + return 3 + + case negCount == 1: + // one MOVN and two MOVKs + for i = 0; i < 4; i++ { + if immh[i] != 0xffff { + os[0] = c.opirr(p, AMOVN) + os[0] |= MOVCONST(dn, i, rt) + i++ + break + } + } + + for j := 1; i < 4; i++ { + if immh[i] != 0xffff { + os[j] = c.opirr(p, AMOVK) + os[j] |= MOVCONST(d, i, rt) + j++ + } + } + return 3 + + default: + // one MOVZ and 3 MOVKs + os[0] = c.opirr(p, AMOVZ) + os[0] |= MOVCONST(d, 0, rt) + for i = 1; i < 4; i++ { + os[i] = c.opirr(p, AMOVK) + os[i] |= MOVCONST(d, i, rt) + } + return 4 + } + default: + return 0 + } +} + func (c *ctxt7) opbfm(p *obj.Prog, a obj.As, r int, s int, rf int, rt int) uint32 { var b uint32 o := c.opirr(p, a) diff --git a/src/cmd/internal/obj/arm64/doc.go b/src/cmd/internal/obj/arm64/doc.go index 845fb22817..7fb129989b 100644 --- a/src/cmd/internal/obj/arm64/doc.go +++ b/src/cmd/internal/obj/arm64/doc.go @@ -89,7 +89,7 @@ such as str, stur, strb, sturb, strh, sturh stlr, stlrb. stlrh, st1. Examples: MOVD R29, 384(R19) <=> str x29, [x19,#384] MOVB.P R30, 30(R4) <=> strb w30, [x4],#30 - STLRH R21, (R18) <=> stlrh w21, [x18] + STLRH R21, (R19) <=> stlrh w21, [x19] (2) MADD, MADDW, MSUB, MSUBW, SMADDL, SMSUBL, UMADDL, UMSUBL , , , @@ -127,7 +127,7 @@ such as str, stur, strb, sturb, strh, sturh stlr, stlrb. stlrh, st1. Examples: CCMN VS, R13, R22, $10 <=> ccmn x13, x22, #0xa, vs - CCMPW HS, R18, R14, $11 <=> ccmp w18, w14, #0xb, cs + CCMPW HS, R19, R14, $11 <=> ccmp w19, w14, #0xb, cs (9) CSEL, CSELW, CSNEG, CSNEGW, CSINC, CSINCW , , , ; FCSELD, FCSELS , , , @@ -144,12 +144,12 @@ FCSELD, FCSELS , , , Examples: STLXR ZR, (R15), R16 <=> stlxr w16, xzr, [x15] - STXRB R9, (R21), R18 <=> stxrb w18, w9, [x21] + STXRB R9, (R21), R19 <=> stxrb w19, w9, [x21] (12) STLXP, STLXPW, STXP, STXPW (, ), (), Examples: - STLXP (R17, R18), (R4), R5 <=> stlxp w5, x17, x18, [x4] + STLXP (R17, R19), (R4), R5 <=> stlxp w5, x17, x19, [x4] STXPW (R30, R25), (R22), R13 <=> stxp w13, w30, w25, [x22] 2. Expressions for special arguments. @@ -173,7 +173,7 @@ Extended registers are written as {.{<<}}. can be UXTB, UXTH, UXTW, UXTX, SXTB, SXTH, SXTW or SXTX. Examples: - ADDS R18.UXTB<<4, R9, R26 <=> adds x26, x9, w18, uxtb #4 + ADDS R19.UXTB<<4, R9, R26 <=> adds x26, x9, w19, uxtb #4 ADDSW R14.SXTX, R14, R6 <=> adds w6, w14, w14, sxtx Memory references: [{,#0}] is written as (Rn|RSP), a base register and an immediate diff --git a/src/cmd/internal/obj/arm64/obj7.go b/src/cmd/internal/obj/arm64/obj7.go index 4476dad071..d0e354eabd 100644 --- a/src/cmd/internal/obj/arm64/obj7.go +++ b/src/cmd/internal/obj/arm64/obj7.go @@ -311,12 +311,9 @@ func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { // shared for both 32-bit and 64-bit. 32-bit ops // will zero the high 32-bit of the destination // register anyway. - switch p.As { - case AANDW, AORRW, AEORW, AANDSW, ATSTW: - if p.From.Type == obj.TYPE_CONST { - v := p.From.Offset & 0xffffffff - p.From.Offset = v | v<<32 - } + if isANDWop(p.As) && p.From.Type == obj.TYPE_CONST { + v := p.From.Offset & 0xffffffff + p.From.Offset = v | v<<32 } if c.ctxt.Flag_dynlink { diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go index f983d5277e..d924cbc214 100644 --- a/src/cmd/internal/obj/link.go +++ b/src/cmd/internal/obj/link.go @@ -403,9 +403,9 @@ type FuncInfo struct { dwarfAbsFnSym *LSym dwarfIsStmtSym *LSym - GCArgs LSym - GCLocals LSym - GCRegs LSym + GCArgs *LSym + GCLocals *LSym + GCRegs *LSym StackObjects *LSym } diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go index b6cfec3b3e..3c72f543cc 100644 --- a/src/cmd/internal/obj/objfile.go +++ b/src/cmd/internal/obj/objfile.go @@ -25,12 +25,6 @@ type objWriter struct { // Temporary buffer for zigzag int writing. varintbuf [10]uint8 - // Provide the index of a symbol reference by symbol name. - // One map for versioned symbols and one for unversioned symbols. - // Used for deduplicating the symbol reference list. - refIdx map[string]int - vrefIdx map[string]int - // Number of objects written of each type. nRefs int nData int @@ -79,10 +73,8 @@ func (w *objWriter) writeLengths() { func newObjWriter(ctxt *Link, b *bufio.Writer) *objWriter { return &objWriter{ - ctxt: ctxt, - wr: b, - vrefIdx: make(map[string]int), - refIdx: make(map[string]int), + ctxt: ctxt, + wr: b, } } @@ -157,17 +149,6 @@ func (w *objWriter) writeRef(s *LSym, isPath bool) { if s == nil || s.RefIdx != 0 { return } - var m map[string]int - if !s.Static() { - m = w.refIdx - } else { - m = w.vrefIdx - } - - if idx := m[s.Name]; idx != 0 { - s.RefIdx = idx - return - } w.wr.WriteByte(symPrefix) if isPath { w.writeString(filepath.ToSlash(s.Name)) @@ -178,7 +159,6 @@ func (w *objWriter) writeRef(s *LSym, isPath bool) { w.writeBool(s.Static()) w.nRefs++ s.RefIdx = w.nRefs - m[s.Name] = w.nRefs } func (w *objWriter) writeRefs(s *LSym) { diff --git a/src/cmd/internal/obj/plist.go b/src/cmd/internal/obj/plist.go index a8675055d9..6710b375f1 100644 --- a/src/cmd/internal/obj/plist.go +++ b/src/cmd/internal/obj/plist.go @@ -147,18 +147,6 @@ func (ctxt *Link) InitTextSym(s *LSym, flag int) { isstmt.Type = objabi.SDWARFMISC isstmt.Set(AttrDuplicateOK, s.DuplicateOK()) ctxt.Data = append(ctxt.Data, isstmt) - - // Set up the function's gcargs and gclocals. - // They will be filled in later if needed. - gcargs := &s.Func.GCArgs - gcargs.Set(AttrDuplicateOK, true) - gcargs.Type = objabi.SRODATA - gclocals := &s.Func.GCLocals - gclocals.Set(AttrDuplicateOK, true) - gclocals.Type = objabi.SRODATA - gcregs := &s.Func.GCRegs - gcregs.Set(AttrDuplicateOK, true) - gcregs.Type = objabi.SRODATA } func (ctxt *Link) Globl(s *LSym, size int64, flag int) { diff --git a/src/cmd/internal/obj/wasm/a.out.go b/src/cmd/internal/obj/wasm/a.out.go index 6f882215ff..0e8196be60 100644 --- a/src/cmd/internal/obj/wasm/a.out.go +++ b/src/cmd/internal/obj/wasm/a.out.go @@ -246,7 +246,7 @@ const ( REG_RET1 REG_RET2 REG_RET3 - REG_RUN + REG_PAUSE // locals REG_R0 diff --git a/src/cmd/internal/obj/wasm/wasmobj.go b/src/cmd/internal/obj/wasm/wasmobj.go index b1eae2882b..f271101f4b 100644 --- a/src/cmd/internal/obj/wasm/wasmobj.go +++ b/src/cmd/internal/obj/wasm/wasmobj.go @@ -16,16 +16,16 @@ import ( ) var Register = map[string]int16{ - "PC_F": REG_PC_F, - "PC_B": REG_PC_B, - "SP": REG_SP, - "CTXT": REG_CTXT, - "g": REG_g, - "RET0": REG_RET0, - "RET1": REG_RET1, - "RET2": REG_RET2, - "RET3": REG_RET3, - "RUN": REG_RUN, + "PC_F": REG_PC_F, + "PC_B": REG_PC_B, + "SP": REG_SP, + "CTXT": REG_CTXT, + "g": REG_g, + "RET0": REG_RET0, + "RET1": REG_RET1, + "RET2": REG_RET2, + "RET3": REG_RET3, + "PAUSE": REG_PAUSE, "R0": REG_R0, "R1": REG_R1, @@ -777,7 +777,7 @@ func assemble(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { } reg := p.From.Reg switch { - case reg >= REG_PC_F && reg <= REG_RUN: + case reg >= REG_PC_F && reg <= REG_PAUSE: w.WriteByte(0x23) // get_global writeUleb128(w, uint64(reg-REG_PC_F)) case reg >= REG_R0 && reg <= REG_R15: @@ -797,7 +797,7 @@ func assemble(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { } reg := p.To.Reg switch { - case reg >= REG_PC_F && reg <= REG_RUN: + case reg >= REG_PC_F && reg <= REG_PAUSE: w.WriteByte(0x24) // set_global writeUleb128(w, uint64(reg-REG_PC_F)) case reg >= REG_R0 && reg <= REG_F15: diff --git a/src/cmd/internal/obj/x86/asm6.go b/src/cmd/internal/obj/x86/asm6.go index 1d056097c3..a4507352f7 100644 --- a/src/cmd/internal/obj/x86/asm6.go +++ b/src/cmd/internal/obj/x86/asm6.go @@ -4704,7 +4704,9 @@ func (ab *AsmBuf) doasm(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog) { r = obj.Addrel(cursym) r.Off = int32(p.Pc + int64(ab.Len())) r.Sym = p.To.Sym - r.Type = objabi.R_PCREL + // Note: R_CALL instead of R_PCREL. R_CALL is more permissive in that + // it can point to a trampoline instead of the destination itself. + r.Type = objabi.R_CALL r.Siz = 4 ab.PutInt32(0) break diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go index 770590fd35..5ba038d147 100644 --- a/src/cmd/link/internal/arm64/asm.go +++ b/src/cmd/link/internal/arm64/asm.go @@ -256,7 +256,7 @@ func archreloc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) (int64, bo // (https://sourceware.org/bugzilla/show_bug.cgi?id=18270). So // we convert the adrp; ld64 + R_ARM64_GOTPCREL into adrp; // add + R_ADDRARM64. - if !(r.Sym.Version != 0 || r.Sym.Attr.VisibilityHidden() || r.Sym.Attr.Local()) && r.Sym.Type == sym.STEXT && ctxt.DynlinkingGo() { + if !(r.Sym.IsFileLocal() || r.Sym.Attr.VisibilityHidden() || r.Sym.Attr.Local()) && r.Sym.Type == sym.STEXT && ctxt.DynlinkingGo() { if o2&0xffc00000 != 0xf9400000 { ld.Errorf(s, "R_ARM64_GOTPCREL against unexpected instruction %x", o2) } diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go index 7a20650d9c..d10f4ab3c3 100644 --- a/src/cmd/link/internal/ld/dwarf.go +++ b/src/cmd/link/internal/ld/dwarf.go @@ -342,6 +342,26 @@ func lookupOrDiag(ctxt *Link, n string) *sym.Symbol { return s } +// dwarfFuncSym looks up a DWARF metadata symbol for function symbol s. +// If the symbol does not exist, it creates it if create is true, +// or returns nil otherwise. +func dwarfFuncSym(ctxt *Link, s *sym.Symbol, meta string, create bool) *sym.Symbol { + // All function ABIs use symbol version 0 for the DWARF data. + // + // TODO(austin): It may be useful to have DWARF info for ABI + // wrappers, in which case we may want these versions to + // align. Better yet, replace these name lookups with a + // general way to attach metadata to a symbol. + ver := 0 + if s.IsFileLocal() { + ver = int(s.Version) + } + if create { + return ctxt.Syms.Lookup(meta+s.Name, ver) + } + return ctxt.Syms.ROLookup(meta+s.Name, ver) +} + func dotypedef(ctxt *Link, parent *dwarf.DWDie, name string, def *dwarf.DWDie) *dwarf.DWDie { // Only emit typedefs for real names. if strings.HasPrefix(name, "map[") { @@ -843,7 +863,7 @@ func dwarfDefineGlobal(ctxt *Link, s *sym.Symbol, str string, v int64, gotype *s } dv := newdie(ctxt, ctxt.compUnitByPackage[lib].dwinfo, dwarf.DW_ABRV_VARIABLE, str, int(s.Version)) newabslocexprattr(dv, v, s) - if s.Version == 0 { + if !s.IsFileLocal() { newattr(dv, dwarf.DW_AT_external, dwarf.DW_CLS_FLAG, 1, 0) } dt := defgotype(ctxt, gotype) @@ -1146,7 +1166,7 @@ func writelines(ctxt *Link, unit *compilationUnit, ls *sym.Symbol) { // indexes (created by numberfile) to CU-local indexes. fileNums := make(map[int]int) for _, s := range unit.lib.Textp { // textp has been dead-code-eliminated already. - dsym := ctxt.Syms.Lookup(dwarf.InfoPrefix+s.Name, int(s.Version)) + dsym := dwarfFuncSym(ctxt, s, dwarf.InfoPrefix, true) for _, f := range s.FuncInfo.File { if _, ok := fileNums[int(f.Value)]; ok { continue @@ -1756,12 +1776,12 @@ func dwarfGenerateDebugInfo(ctxt *Link) { // referenced abstract functions. // Collect all debug_range symbols in unit.rangeSyms for _, s := range lib.Textp { // textp has been dead-code-eliminated already. - dsym := ctxt.Syms.ROLookup(dwarf.InfoPrefix+s.Name, int(s.Version)) + dsym := dwarfFuncSym(ctxt, s, dwarf.InfoPrefix, false) dsym.Attr |= sym.AttrNotInSymbolTable | sym.AttrReachable dsym.Type = sym.SDWARFINFO unit.funcDIEs = append(unit.funcDIEs, dsym) - rangeSym := ctxt.Syms.ROLookup(dwarf.RangePrefix+s.Name, int(s.Version)) + rangeSym := dwarfFuncSym(ctxt, s, dwarf.RangePrefix, false) if rangeSym != nil && rangeSym.Size > 0 { rangeSym.Attr |= sym.AttrReachable | sym.AttrNotInSymbolTable rangeSym.Type = sym.SDWARFRANGE diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index a70935e821..2af9c76a8b 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -2130,7 +2130,7 @@ func genasmsym(ctxt *Link, put func(*Link, *sym.Symbol, string, SymbolType, int6 if s.Attr.NotInSymbolTable() { continue } - if (s.Name == "" || s.Name[0] == '.') && s.Version == 0 && s.Name != ".rathole" && s.Name != ".TOC." { + if (s.Name == "" || s.Name[0] == '.') && !s.IsFileLocal() && s.Name != ".rathole" && s.Name != ".TOC." { continue } switch s.Type { diff --git a/src/cmd/link/internal/ld/pe.go b/src/cmd/link/internal/ld/pe.go index cf197f50b0..68251786ed 100644 --- a/src/cmd/link/internal/ld/pe.go +++ b/src/cmd/link/internal/ld/pe.go @@ -704,7 +704,7 @@ func (f *peFile) writeSymbols(ctxt *Link) { } } class := IMAGE_SYM_CLASS_EXTERNAL - if s.Version != 0 || s.Attr.VisibilityHidden() || s.Attr.Local() { + if s.IsFileLocal() || s.Attr.VisibilityHidden() || s.Attr.Local() { class = IMAGE_SYM_CLASS_STATIC } f.writeSymbol(ctxt.Out, s, value, sect, typ, uint8(class)) diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go index d2737deca5..276a3a1cbb 100644 --- a/src/cmd/link/internal/ld/symtab.go +++ b/src/cmd/link/internal/ld/symtab.go @@ -128,7 +128,7 @@ func putelfsym(ctxt *Link, x *sym.Symbol, s string, t SymbolType, addr int64, go // maybe one day STB_WEAK. bind := STB_GLOBAL - if x.Version != 0 || x.Attr.VisibilityHidden() || x.Attr.Local() { + if x.IsFileLocal() || x.Attr.VisibilityHidden() || x.Attr.Local() { bind = STB_LOCAL } @@ -224,7 +224,7 @@ func putplan9sym(ctxt *Link, x *sym.Symbol, s string, typ SymbolType, addr int64 t := int(typ) switch typ { case TextSym, DataSym, BSSSym: - if x.Version != 0 { + if x.IsFileLocal() { t += 'a' - 'A' } fallthrough diff --git a/src/cmd/link/internal/sym/symbol.go b/src/cmd/link/internal/sym/symbol.go index a6c2aaea77..4faa991463 100644 --- a/src/cmd/link/internal/sym/symbol.go +++ b/src/cmd/link/internal/sym/symbol.go @@ -51,6 +51,10 @@ type AuxSymbol struct { elftype elf.SymType } +const ( + SymVerStatic = 10 // Minimum version used by static (file-local) syms +) + func (s *Symbol) String() string { if s.Version == 0 { return s.Name @@ -58,6 +62,10 @@ func (s *Symbol) String() string { return fmt.Sprintf("%s<%d>", s.Name, s.Version) } +func (s *Symbol) IsFileLocal() bool { + return s.Version >= SymVerStatic +} + func (s *Symbol) ElfsymForReloc() int32 { // If putelfsym created a local version of this symbol, use that in all // relocations. diff --git a/src/cmd/link/internal/sym/symbols.go b/src/cmd/link/internal/sym/symbols.go index d79d1d8b1d..d7266c840b 100644 --- a/src/cmd/link/internal/sym/symbols.go +++ b/src/cmd/link/internal/sym/symbols.go @@ -40,12 +40,11 @@ type Symbols struct { } func NewSymbols() *Symbols { + hash := make([]map[string]*Symbol, SymVerStatic) + // Preallocate about 2mb for hash of non static symbols + hash[0] = make(map[string]*Symbol, 100000) return &Symbols{ - hash: []map[string]*Symbol{ - // preallocate about 2mb for hash of - // non static symbols - make(map[string]*Symbol, 100000), - }, + hash: hash, Allsym: make([]*Symbol, 0, 100000), } } diff --git a/src/cmd/link/internal/wasm/asm.go b/src/cmd/link/internal/wasm/asm.go index bffbc7c8a6..737de59928 100644 --- a/src/cmd/link/internal/wasm/asm.go +++ b/src/cmd/link/internal/wasm/asm.go @@ -54,7 +54,11 @@ type wasmFuncType struct { } var wasmFuncTypes = map[string]*wasmFuncType{ - "_rt0_wasm_js": &wasmFuncType{Params: []byte{I32, I32}}, // argc, argv + "_rt0_wasm_js": &wasmFuncType{Params: []byte{}}, // + "wasm_export_run": &wasmFuncType{Params: []byte{I32, I32}}, // argc, argv + "wasm_export_resume": &wasmFuncType{Params: []byte{}}, // + "wasm_export_getsp": &wasmFuncType{Results: []byte{I32}}, // sp + "wasm_pc_f_loop": &wasmFuncType{Params: []byte{}}, // "runtime.wasmMove": &wasmFuncType{Params: []byte{I32, I32, I32}}, // dst, src, len "runtime.wasmZero": &wasmFuncType{Params: []byte{I32, I32}}, // ptr, len "runtime.wasmDiv": &wasmFuncType{Params: []byte{I64, I64}, Results: []byte{I64}}, // x, y -> x/y @@ -162,9 +166,6 @@ func asmb(ctxt *ld.Link) { fns[i] = &wasmFunc{Name: name, Type: typ, Code: wfn.Bytes()} } - // look up program entry point - rt0 := uint32(len(hostImports)) + uint32(ctxt.Syms.ROLookup("_rt0_wasm_js", 0).Value>>16) - funcValueOffset - ctxt.Out.Write([]byte{0x00, 0x61, 0x73, 0x6d}) // magic ctxt.Out.Write([]byte{0x01, 0x00, 0x00, 0x00}) // version @@ -180,7 +181,7 @@ func asmb(ctxt *ld.Link) { writeTableSec(ctxt, fns) writeMemorySec(ctxt) writeGlobalSec(ctxt) - writeExportSec(ctxt, rt0) + writeExportSec(ctxt, len(hostImports)) writeElementSec(ctxt, uint64(len(hostImports)), uint64(len(fns))) writeCodeSec(ctxt, fns) writeDataSec(ctxt) @@ -326,7 +327,7 @@ func writeGlobalSec(ctxt *ld.Link) { I64, // 6: RET1 I64, // 7: RET2 I64, // 8: RET3 - I32, // 9: RUN + I32, // 9: PAUSE } writeUleb128(ctxt.Out, uint64(len(globalRegs))) // number of globals @@ -348,15 +349,18 @@ func writeGlobalSec(ctxt *ld.Link) { // writeExportSec writes the section that declares exports. // Exports can be accessed by the WebAssembly host, usually JavaScript. -// Currently _rt0_wasm_js (program entry point) and the linear memory get exported. -func writeExportSec(ctxt *ld.Link, rt0 uint32) { +// The wasm_export_* functions and the linear memory get exported. +func writeExportSec(ctxt *ld.Link, lenHostImports int) { sizeOffset := writeSecHeader(ctxt, sectionExport) - writeUleb128(ctxt.Out, 2) // number of exports + writeUleb128(ctxt.Out, 4) // number of exports - writeName(ctxt.Out, "run") // inst.exports.run in wasm_exec.js - ctxt.Out.WriteByte(0x00) // func export - writeUleb128(ctxt.Out, uint64(rt0)) // funcidx + for _, name := range []string{"run", "resume", "getsp"} { + idx := uint32(lenHostImports) + uint32(ctxt.Syms.ROLookup("wasm_export_"+name, 0).Value>>16) - funcValueOffset + writeName(ctxt.Out, name) // inst.exports.run/resume/getsp in wasm_exec.js + ctxt.Out.WriteByte(0x00) // func export + writeUleb128(ctxt.Out, uint64(idx)) // funcidx + } writeName(ctxt.Out, "mem") // inst.exports.mem in wasm_exec.js ctxt.Out.WriteByte(0x02) // mem export diff --git a/src/cmd/trace/annotations.go b/src/cmd/trace/annotations.go index 9905456b46..a4933b51bf 100644 --- a/src/cmd/trace/annotations.go +++ b/src/cmd/trace/annotations.go @@ -508,7 +508,7 @@ func (task *taskDesc) overlappingGCDuration(evs []*trace.Event) (overlapping tim return overlapping } -// overlappingInstant returns true if the instantaneous event, ev, occurred during +// overlappingInstant reports whether the instantaneous event, ev, occurred during // any of the task's region if ev is a goroutine-local event, or overlaps with the // task's lifetime if ev is a global event. func (task *taskDesc) overlappingInstant(ev *trace.Event) bool { diff --git a/src/cmd/trace/main.go b/src/cmd/trace/main.go index a33d2f4679..2f71a3d4bd 100644 --- a/src/cmd/trace/main.go +++ b/src/cmd/trace/main.go @@ -189,7 +189,7 @@ var templMain = template.Must(template.New("").Parse(` {{if $}} {{range $e := $}} - View trace ({{$e.Name}})
+ View trace ({{$e.Name}})
{{end}}
{{else}} @@ -202,6 +202,7 @@ var templMain = template.Must(template.New("").Parse(` Scheduler latency profile ()
User-defined tasks
User-defined regions
+Minimum mutator utilization
`)) diff --git a/src/cmd/trace/mmu.go b/src/cmd/trace/mmu.go new file mode 100644 index 0000000000..6a7d28e61d --- /dev/null +++ b/src/cmd/trace/mmu.go @@ -0,0 +1,403 @@ +// Copyright 2017 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. + +// Minimum mutator utilization (MMU) graphing. + +// TODO: +// +// In worst window list, show break-down of GC utilization sources +// (STW, assist, etc). Probably requires a different MutatorUtil +// representation. +// +// When a window size is selected, show a second plot of the mutator +// utilization distribution for that window size. +// +// Render plot progressively so rough outline is visible quickly even +// for very complex MUTs. Start by computing just a few window sizes +// and then add more window sizes. +// +// Consider using sampling to compute an approximate MUT. This would +// work by sampling the mutator utilization at randomly selected +// points in time in the trace to build an empirical distribution. We +// could potentially put confidence intervals on these estimates and +// render this progressively as we refine the distributions. + +package main + +import ( + "encoding/json" + "fmt" + trace "internal/traceparser" + "log" + "math" + "net/http" + "strconv" + "strings" + "sync" + "time" +) + +func init() { + http.HandleFunc("/mmu", httpMMU) + http.HandleFunc("/mmuPlot", httpMMUPlot) + http.HandleFunc("/mmuDetails", httpMMUDetails) +} + +var utilFlagNames = map[string]trace.UtilFlags{ + "perProc": trace.UtilPerProc, + "stw": trace.UtilSTW, + "background": trace.UtilBackground, + "assist": trace.UtilAssist, + "sweep": trace.UtilSweep, +} + +type mmuCacheEntry struct { + init sync.Once + util [][]trace.MutatorUtil + mmuCurve *trace.MMUCurve + err error +} + +var mmuCache struct { + m map[trace.UtilFlags]*mmuCacheEntry + lock sync.Mutex +} + +func init() { + mmuCache.m = make(map[trace.UtilFlags]*mmuCacheEntry) +} + +func getMMUCurve(r *http.Request) ([][]trace.MutatorUtil, *trace.MMUCurve, error) { + var flags trace.UtilFlags + for _, flagStr := range strings.Split(r.FormValue("flags"), "|") { + flags |= utilFlagNames[flagStr] + } + + mmuCache.lock.Lock() + c := mmuCache.m[flags] + if c == nil { + c = new(mmuCacheEntry) + mmuCache.m[flags] = c + } + mmuCache.lock.Unlock() + + c.init.Do(func() { + tr, err := parseTrace() + if err != nil { + c.err = err + } else { + c.util = tr.MutatorUtilization(flags) + c.mmuCurve = trace.NewMMUCurve(c.util) + } + }) + return c.util, c.mmuCurve, c.err +} + +// httpMMU serves the MMU plot page. +func httpMMU(w http.ResponseWriter, r *http.Request) { + http.ServeContent(w, r, "", time.Time{}, strings.NewReader(templMMU)) +} + +// httpMMUPlot serves the JSON data for the MMU plot. +func httpMMUPlot(w http.ResponseWriter, r *http.Request) { + mu, mmuCurve, err := getMMUCurve(r) + if err != nil { + http.Error(w, fmt.Sprintf("failed to parse events: %v", err), http.StatusInternalServerError) + return + } + + var quantiles []float64 + for _, flagStr := range strings.Split(r.FormValue("flags"), "|") { + if flagStr == "mut" { + quantiles = []float64{0, 1 - .999, 1 - .99, 1 - .95} + break + } + } + + // Find a nice starting point for the plot. + xMin := time.Second + for xMin > 1 { + if mmu := mmuCurve.MMU(xMin); mmu < 0.0001 { + break + } + xMin /= 1000 + } + // Cover six orders of magnitude. + xMax := xMin * 1e6 + // But no more than the length of the trace. + minEvent, maxEvent := mu[0][0].Time, mu[0][len(mu[0])-1].Time + for _, mu1 := range mu[1:] { + if mu1[0].Time < minEvent { + minEvent = mu1[0].Time + } + if mu1[len(mu1)-1].Time > maxEvent { + maxEvent = mu1[len(mu1)-1].Time + } + } + if maxMax := time.Duration(maxEvent - minEvent); xMax > maxMax { + xMax = maxMax + } + // Compute MMU curve. + logMin, logMax := math.Log(float64(xMin)), math.Log(float64(xMax)) + const samples = 100 + plot := make([][]float64, samples) + for i := 0; i < samples; i++ { + window := time.Duration(math.Exp(float64(i)/(samples-1)*(logMax-logMin) + logMin)) + if quantiles == nil { + plot[i] = make([]float64, 2) + plot[i][1] = mmuCurve.MMU(window) + } else { + plot[i] = make([]float64, 1+len(quantiles)) + copy(plot[i][1:], mmuCurve.MUD(window, quantiles)) + } + plot[i][0] = float64(window) + } + + // Create JSON response. + err = json.NewEncoder(w).Encode(map[string]interface{}{"xMin": int64(xMin), "xMax": int64(xMax), "quantiles": quantiles, "curve": plot}) + if err != nil { + log.Printf("failed to serialize response: %v", err) + return + } +} + +var templMMU = ` + + + + + + + + + +

+
Loading plot...
+
+

+ View
+ + ?Consider whole system utilization. For example, if one of four procs is available to the mutator, mutator utilization will be 0.25. This is the standard definition of an MMU.
+ + ?Consider per-goroutine utilization. When even one goroutine is interrupted by GC, mutator utilization is 0.
+

+

+ Include
+ + ?Stop-the-world stops all goroutines simultaneously.
+ + ?Background workers are GC-specific goroutines. 25% of the CPU is dedicated to background workers during GC.
+ + ?Mark assists are performed by allocation to prevent the mutator from outpacing GC.
+ + ?Sweep reclaims unused memory between GCs. (Enabling this may be very slow.).
+

+

+ Display
+ + ?Display percentile mutator utilization in addition to minimum. E.g., p99 MU drops the worst 1% of windows.
+

+
+
+
Select a point for details.
+ + +` + +// httpMMUDetails serves details of an MMU graph at a particular window. +func httpMMUDetails(w http.ResponseWriter, r *http.Request) { + _, mmuCurve, err := getMMUCurve(r) + if err != nil { + http.Error(w, fmt.Sprintf("failed to parse events: %v", err), http.StatusInternalServerError) + return + } + + windowStr := r.FormValue("window") + window, err := strconv.ParseUint(windowStr, 10, 64) + if err != nil { + http.Error(w, fmt.Sprintf("failed to parse window parameter %q: %v", windowStr, err), http.StatusBadRequest) + return + } + worst := mmuCurve.Examples(time.Duration(window), 10) + + // Construct a link for each window. + var links []linkedUtilWindow + for _, ui := range worst { + links = append(links, newLinkedUtilWindow(ui, time.Duration(window))) + } + + err = json.NewEncoder(w).Encode(links) + if err != nil { + log.Printf("failed to serialize trace: %v", err) + return + } +} + +type linkedUtilWindow struct { + trace.UtilWindow + URL string +} + +func newLinkedUtilWindow(ui trace.UtilWindow, window time.Duration) linkedUtilWindow { + // Find the range containing this window. + var r Range + for _, r = range ranges { + if r.EndTime > ui.Time { + break + } + } + return linkedUtilWindow{ui, fmt.Sprintf("%s#%v:%v", r.URL(), float64(ui.Time)/1e6, float64(ui.Time+int64(window))/1e6)} +} diff --git a/src/cmd/trace/trace.go b/src/cmd/trace/trace.go index d0e0acd78c..d467f371fa 100644 --- a/src/cmd/trace/trace.go +++ b/src/cmd/trace/trace.go @@ -272,9 +272,15 @@ func httpJSONTrace(w http.ResponseWriter, r *http.Request) { // Range is a named range type Range struct { - Name string - Start int - End int + Name string + Start int + End int + StartTime int64 + EndTime int64 +} + +func (r Range) URL() string { + return fmt.Sprintf("/trace?start=%d&end=%d", r.Start, r.End) } // splitTrace splits the trace into a number of ranges, @@ -345,10 +351,14 @@ func splittingTraceConsumer(max int) (*splitter, traceConsumer) { start := 0 for i, ev := range sizes { if sum+ev.Sz > max { + startTime := time.Duration(sizes[start].Time * 1000) + endTime := time.Duration(ev.Time * 1000) ranges = append(ranges, Range{ - Name: fmt.Sprintf("%v-%v", time.Duration(sizes[start].Time*1000), time.Duration(ev.Time*1000)), - Start: start, - End: i + 1, + Name: fmt.Sprintf("%v-%v", startTime, endTime), + Start: start, + End: i + 1, + StartTime: int64(startTime), + EndTime: int64(endTime), }) start = i + 1 sum = minSize @@ -363,9 +373,11 @@ func splittingTraceConsumer(max int) (*splitter, traceConsumer) { if end := len(sizes) - 1; start < end { ranges = append(ranges, Range{ - Name: fmt.Sprintf("%v-%v", time.Duration(sizes[start].Time*1000), time.Duration(sizes[end].Time*1000)), - Start: start, - End: end, + Name: fmt.Sprintf("%v-%v", time.Duration(sizes[start].Time*1000), time.Duration(sizes[end].Time*1000)), + Start: start, + End: end, + StartTime: int64(sizes[start].Time * 1000), + EndTime: int64(sizes[end].Time * 1000), }) } s.Ranges = ranges diff --git a/src/cmd/vendor/github.com/google/pprof/driver/driver.go b/src/cmd/vendor/github.com/google/pprof/driver/driver.go index 3735d6ace9..b1c745bacd 100644 --- a/src/cmd/vendor/github.com/google/pprof/driver/driver.go +++ b/src/cmd/vendor/github.com/google/pprof/driver/driver.go @@ -17,6 +17,7 @@ package driver import ( "io" + "net/http" "regexp" "time" @@ -48,13 +49,14 @@ func (o *Options) internalOptions() *plugin.Options { } } return &plugin.Options{ - Writer: o.Writer, - Flagset: o.Flagset, - Fetch: o.Fetch, - Sym: sym, - Obj: obj, - UI: o.UI, - HTTPServer: httpServer, + Writer: o.Writer, + Flagset: o.Flagset, + Fetch: o.Fetch, + Sym: sym, + Obj: obj, + UI: o.UI, + HTTPServer: httpServer, + HTTPTransport: o.HTTPTransport, } } @@ -64,13 +66,14 @@ type HTTPServerArgs plugin.HTTPServerArgs // Options groups all the optional plugins into pprof. type Options struct { - Writer Writer - Flagset FlagSet - Fetch Fetcher - Sym Symbolizer - Obj ObjTool - UI UI - HTTPServer func(*HTTPServerArgs) error + Writer Writer + Flagset FlagSet + Fetch Fetcher + Sym Symbolizer + Obj ObjTool + UI UI + HTTPServer func(*HTTPServerArgs) error + HTTPTransport http.RoundTripper } // Writer provides a mechanism to write data under a certain name, @@ -100,12 +103,16 @@ type FlagSet interface { // single flag StringList(name string, def string, usage string) *[]*string - // ExtraUsage returns any additional text that should be - // printed after the standard usage message. - // The typical use of ExtraUsage is to show any custom flags - // defined by the specific pprof plugins being used. + // ExtraUsage returns any additional text that should be printed after the + // standard usage message. The extra usage message returned includes all text + // added with AddExtraUsage(). + // The typical use of ExtraUsage is to show any custom flags defined by the + // specific pprof plugins being used. ExtraUsage() string + // AddExtraUsage appends additional text to the end of the extra usage message. + AddExtraUsage(eu string) + // Parse initializes the flags with their values for this run // and returns the non-flag command line arguments. // If an unknown flag is encountered or there are no arguments, diff --git a/src/cmd/vendor/github.com/google/pprof/internal/binutils/binutils.go b/src/cmd/vendor/github.com/google/pprof/internal/binutils/binutils.go index 12b6a5c4b2..309561112c 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/binutils/binutils.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/binutils/binutils.go @@ -18,11 +18,14 @@ package binutils import ( "debug/elf" "debug/macho" + "encoding/binary" "fmt" + "io" "os" "os/exec" "path/filepath" "regexp" + "runtime" "strings" "sync" @@ -173,12 +176,8 @@ func (bu *Binutils) Open(name string, start, limit, offset uint64) (plugin.ObjFi b := bu.get() // Make sure file is a supported executable. - // The pprof driver uses Open to sniff the difference - // between an executable and a profile. - // For now, only ELF is supported. - // Could read the first few bytes of the file and - // use a table of prefixes if we need to support other - // systems at some point. + // This uses magic numbers, mainly to provide better error messages but + // it should also help speed. if _, err := os.Stat(name); err != nil { // For testing, do not require file name to exist. @@ -188,21 +187,54 @@ func (bu *Binutils) Open(name string, start, limit, offset uint64) (plugin.ObjFi return nil, err } - if f, err := b.openELF(name, start, limit, offset); err == nil { + // Read the first 4 bytes of the file. + + f, err := os.Open(name) + if err != nil { + return nil, fmt.Errorf("error opening %s: %v", name, err) + } + defer f.Close() + + var header [4]byte + if _, err = io.ReadFull(f, header[:]); err != nil { + return nil, fmt.Errorf("error reading magic number from %s: %v", name, err) + } + + elfMagic := string(header[:]) + + // Match against supported file types. + if elfMagic == elf.ELFMAG { + f, err := b.openELF(name, start, limit, offset) + if err != nil { + return nil, fmt.Errorf("error reading ELF file %s: %v", name, err) + } return f, nil } - if f, err := b.openMachO(name, start, limit, offset); err == nil { + + // Mach-O magic numbers can be big or little endian. + machoMagicLittle := binary.LittleEndian.Uint32(header[:]) + machoMagicBig := binary.BigEndian.Uint32(header[:]) + + if machoMagicLittle == macho.Magic32 || machoMagicLittle == macho.Magic64 || + machoMagicBig == macho.Magic32 || machoMagicBig == macho.Magic64 { + f, err := b.openMachO(name, start, limit, offset) + if err != nil { + return nil, fmt.Errorf("error reading Mach-O file %s: %v", name, err) + } return f, nil } - return nil, fmt.Errorf("unrecognized binary: %s", name) + if machoMagicLittle == macho.MagicFat || machoMagicBig == macho.MagicFat { + f, err := b.openFatMachO(name, start, limit, offset) + if err != nil { + return nil, fmt.Errorf("error reading fat Mach-O file %s: %v", name, err) + } + return f, nil + } + + return nil, fmt.Errorf("unrecognized binary format: %s", name) } -func (b *binrep) openMachO(name string, start, limit, offset uint64) (plugin.ObjFile, error) { - of, err := macho.Open(name) - if err != nil { - return nil, fmt.Errorf("error parsing %s: %v", name, err) - } - defer of.Close() +func (b *binrep) openMachOCommon(name string, of *macho.File, start, limit, offset uint64) (plugin.ObjFile, error) { // Subtract the load address of the __TEXT section. Usually 0 for shared // libraries or 0x100000000 for executables. You can check this value by @@ -225,6 +257,53 @@ func (b *binrep) openMachO(name string, start, limit, offset uint64) (plugin.Obj return &fileAddr2Line{file: file{b: b, name: name, base: base}}, nil } +func (b *binrep) openFatMachO(name string, start, limit, offset uint64) (plugin.ObjFile, error) { + of, err := macho.OpenFat(name) + if err != nil { + return nil, fmt.Errorf("error parsing %s: %v", name, err) + } + defer of.Close() + + if len(of.Arches) == 0 { + return nil, fmt.Errorf("empty fat Mach-O file: %s", name) + } + + var arch macho.Cpu + // Use the host architecture. + // TODO: This is not ideal because the host architecture may not be the one + // that was profiled. E.g. an amd64 host can profile a 386 program. + switch runtime.GOARCH { + case "386": + arch = macho.Cpu386 + case "amd64", "amd64p32": + arch = macho.CpuAmd64 + case "arm", "armbe", "arm64", "arm64be": + arch = macho.CpuArm + case "ppc": + arch = macho.CpuPpc + case "ppc64", "ppc64le": + arch = macho.CpuPpc64 + default: + return nil, fmt.Errorf("unsupported host architecture for %s: %s", name, runtime.GOARCH) + } + for i := range of.Arches { + if of.Arches[i].Cpu == arch { + return b.openMachOCommon(name, of.Arches[i].File, start, limit, offset) + } + } + return nil, fmt.Errorf("architecture not found in %s: %s", name, runtime.GOARCH) +} + +func (b *binrep) openMachO(name string, start, limit, offset uint64) (plugin.ObjFile, error) { + of, err := macho.Open(name) + if err != nil { + return nil, fmt.Errorf("error parsing %s: %v", name, err) + } + defer of.Close() + + return b.openMachOCommon(name, of, start, limit, offset) +} + func (b *binrep) openELF(name string, start, limit, offset uint64) (plugin.ObjFile, error) { ef, err := elf.Open(name) if err != nil { diff --git a/src/cmd/vendor/github.com/google/pprof/internal/binutils/binutils_test.go b/src/cmd/vendor/github.com/google/pprof/internal/binutils/binutils_test.go index d844ed7e4e..17d4225a87 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/binutils/binutils_test.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/binutils/binutils_test.go @@ -22,6 +22,7 @@ import ( "reflect" "regexp" "runtime" + "strings" "testing" "github.com/google/pprof/internal/plugin" @@ -361,3 +362,31 @@ func TestLLVMSymbolizer(t *testing.T) { } } } + +func TestOpenMalformedELF(t *testing.T) { + // Test that opening a malformed ELF file will report an error containing + // the word "ELF". + bu := &Binutils{} + _, err := bu.Open(filepath.Join("testdata", "malformed_elf"), 0, 0, 0) + if err == nil { + t.Fatalf("Open: unexpected success") + } + + if !strings.Contains(err.Error(), "ELF") { + t.Errorf("Open: got %v, want error containing 'ELF'", err) + } +} + +func TestOpenMalformedMachO(t *testing.T) { + // Test that opening a malformed Mach-O file will report an error containing + // the word "Mach-O". + bu := &Binutils{} + _, err := bu.Open(filepath.Join("testdata", "malformed_macho"), 0, 0, 0) + if err == nil { + t.Fatalf("Open: unexpected success") + } + + if !strings.Contains(err.Error(), "Mach-O") { + t.Errorf("Open: got %v, want error containing 'Mach-O'", err) + } +} diff --git a/src/cmd/vendor/github.com/google/pprof/internal/binutils/testdata/malformed_elf b/src/cmd/vendor/github.com/google/pprof/internal/binutils/testdata/malformed_elf new file mode 100644 index 0000000000..f0b503b0b6 --- /dev/null +++ b/src/cmd/vendor/github.com/google/pprof/internal/binutils/testdata/malformed_elf @@ -0,0 +1 @@ +ELF \ No newline at end of file diff --git a/src/cmd/vendor/github.com/google/pprof/internal/binutils/testdata/malformed_macho b/src/cmd/vendor/github.com/google/pprof/internal/binutils/testdata/malformed_macho new file mode 100644 index 0000000000..b01ddf69a9 --- /dev/null +++ b/src/cmd/vendor/github.com/google/pprof/internal/binutils/testdata/malformed_macho @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/cli.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/cli.go index a5153e1511..dfedf9d849 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/driver/cli.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/cli.go @@ -45,8 +45,8 @@ type source struct { func parseFlags(o *plugin.Options) (*source, []string, error) { flag := o.Flagset // Comparisons. - flagBase := flag.StringList("base", "", "Source for base profile for profile subtraction") - flagDiffBase := flag.StringList("diff_base", "", "Source for diff base profile for comparison") + flagDiffBase := flag.StringList("diff_base", "", "Source of base profile for comparison") + flagBase := flag.StringList("base", "", "Source of base profile for profile subtraction") // Source options. flagSymbolize := flag.String("symbolize", "", "Options for profile symbolization") flagBuildID := flag.String("buildid", "", "Override build id for first mapping") @@ -312,7 +312,8 @@ var usageMsgSrc = "\n\n" + " -buildid Override build id for main binary\n" + " -add_comment Free-form annotation to add to the profile\n" + " Displayed on some reports or with pprof -comments\n" + - " -base source Source of profile to use as baseline\n" + + " -diff_base source Source of base profile for comparison\n" + + " -base source Source of base profile for profile subtraction\n" + " profile.pb.gz Profile in compressed protobuf format\n" + " legacy_profile Profile in legacy pprof format\n" + " http://host/profile URL for profile handler to retrieve\n" + diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/commands.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/commands.go index 91d32d1e71..ab073d878d 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/driver/commands.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/commands.go @@ -228,17 +228,17 @@ var pprofVariables = variables{ // Output granularity "functions": &variable{boolKind, "t", "granularity", helpText( "Aggregate at the function level.", - "Takes into account the filename/lineno where the function was defined.")}, + "Ignores the filename where the function was defined.")}, + "filefunctions": &variable{boolKind, "t", "granularity", helpText( + "Aggregate at the function level.", + "Takes into account the filename where the function was defined.")}, "files": &variable{boolKind, "f", "granularity", "Aggregate at the file level."}, "lines": &variable{boolKind, "f", "granularity", "Aggregate at the source code line level."}, "addresses": &variable{boolKind, "f", "granularity", helpText( - "Aggregate at the function level.", + "Aggregate at the address level.", "Includes functions' addresses in the output.")}, - "noinlines": &variable{boolKind, "f", "granularity", helpText( - "Aggregate at the function level.", - "Attributes inlined functions to their first out-of-line caller.")}, - "addressnoinlines": &variable{boolKind, "f", "granularity", helpText( - "Aggregate at the function level, including functions' addresses in the output.", + "noinlines": &variable{boolKind, "f", "", helpText( + "Ignore inlines.", "Attributes inlined functions to their first out-of-line caller.")}, } @@ -337,21 +337,27 @@ func listHelp(c string, redirect bool) string { // browsers returns a list of commands to attempt for web visualization. func browsers() []string { - cmds := []string{"chrome", "google-chrome", "firefox"} + var cmds []string + if userBrowser := os.Getenv("BROWSER"); userBrowser != "" { + cmds = append(cmds, userBrowser) + } switch runtime.GOOS { case "darwin": - return append(cmds, "/usr/bin/open") + cmds = append(cmds, "/usr/bin/open") case "windows": - return append(cmds, "cmd /c start") + cmds = append(cmds, "cmd /c start") default: - userBrowser := os.Getenv("BROWSER") - if userBrowser != "" { - cmds = append([]string{userBrowser, "sensible-browser"}, cmds...) - } else { - cmds = append([]string{"sensible-browser"}, cmds...) + // Commands opening browsers are prioritized over xdg-open, so browser() + // command can be used on linux to open the .svg file generated by the -web + // command (the .svg file includes embedded javascript so is best viewed in + // a browser). + cmds = append(cmds, []string{"chrome", "google-chrome", "chromium", "firefox", "sensible-browser"}...) + if os.Getenv("DISPLAY") != "" { + // xdg-open is only for use in a desktop environment. + cmds = append(cmds, "xdg-open") } - return append(cmds, "xdg-open") } + return cmds } var kcachegrind = []string{"kcachegrind"} diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go index 2dabc3017b..45f1846749 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go @@ -152,20 +152,33 @@ func generateReport(p *profile.Profile, cmd []string, vars variables, o *plugin. } func applyCommandOverrides(cmd string, outputFormat int, v variables) variables { - trim, tagfilter, filter := v["trim"].boolValue(), true, true + // Some report types override the trim flag to false below. This is to make + // sure the default heuristics of excluding insignificant nodes and edges + // from the call graph do not apply. One example where it is important is + // annotated source or disassembly listing. Those reports run on a specific + // function (or functions), but the trimming is applied before the function + // data is selected. So, with trimming enabled, the report could end up + // showing no data if the specified function is "uninteresting" as far as the + // trimming is concerned. + trim := v["trim"].boolValue() switch cmd { - case "callgrind", "kcachegrind": - trim = false - v.set("addresses", "t") case "disasm", "weblist": trim = false - v.set("addressnoinlines", "t") + v.set("addresses", "t") + // Force the 'noinlines' mode so that source locations for a given address + // collapse and there is only one for the given address. Without this + // cumulative metrics would be double-counted when annotating the assembly. + // This is because the merge is done by address and in case of an inlined + // stack each of the inlined entries is a separate callgraph node. + v.set("noinlines", "t") case "peek": - trim, tagfilter, filter = false, false, false + trim = false case "list": - v.set("nodecount", "0") + trim = false v.set("lines", "t") + // Do not force 'noinlines' to be false so that specifying + // "-list foo -noinlines" is supported and works as expected. case "text", "top", "topproto": if v["nodecount"].intValue() == -1 { v.set("nodecount", "0") @@ -176,9 +189,11 @@ func applyCommandOverrides(cmd string, outputFormat int, v variables) variables } } - if outputFormat == report.Proto || outputFormat == report.Raw { - trim, tagfilter, filter = false, false, false + switch outputFormat { + case report.Proto, report.Raw, report.Callgrind: + trim = false v.set("addresses", "t") + v.set("noinlines", "f") } if !trim { @@ -186,43 +201,32 @@ func applyCommandOverrides(cmd string, outputFormat int, v variables) variables v.set("nodefraction", "0") v.set("edgefraction", "0") } - if !tagfilter { - v.set("tagfocus", "") - v.set("tagignore", "") - } - if !filter { - v.set("focus", "") - v.set("ignore", "") - v.set("hide", "") - v.set("show", "") - v.set("show_from", "") - } return v } func aggregate(prof *profile.Profile, v variables) error { - var inlines, function, filename, linenumber, address bool + var function, filename, linenumber, address bool + inlines := !v["noinlines"].boolValue() switch { case v["addresses"].boolValue(): - return nil - case v["lines"].boolValue(): - inlines = true - function = true - filename = true - linenumber = true - case v["files"].boolValue(): - inlines = true - filename = true - case v["functions"].boolValue(): - inlines = true - function = true - case v["noinlines"].boolValue(): - function = true - case v["addressnoinlines"].boolValue(): + if inlines { + return nil + } function = true filename = true linenumber = true address = true + case v["lines"].boolValue(): + function = true + filename = true + linenumber = true + case v["files"].boolValue(): + filename = true + case v["functions"].boolValue(): + function = true + case v["filefunctions"].boolValue(): + function = true + filename = true default: return fmt.Errorf("unexpected granularity") } diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/driver_test.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/driver_test.go index ff6afe9cff..90f89dc7bc 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/driver/driver_test.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/driver_test.go @@ -53,6 +53,9 @@ func TestParse(t *testing.T) { flags, source string }{ {"text,functions,flat", "cpu"}, + {"text,functions,noinlines,flat", "cpu"}, + {"text,filefunctions,noinlines,flat", "cpu"}, + {"text,addresses,noinlines,flat", "cpu"}, {"tree,addresses,flat,nodecount=4", "cpusmall"}, {"text,functions,flat,nodecount=5,call_tree", "unknown"}, {"text,alloc_objects,flat", "heap_alloc"}, @@ -63,6 +66,7 @@ func TestParse(t *testing.T) { {"text,lines,cum,show=[12]00", "cpu"}, {"text,lines,cum,hide=line[X3]0,focus=[12]00", "cpu"}, {"topproto,lines,cum,hide=mangled[X3]0", "cpu"}, + {"topproto,lines", "cpu"}, {"tree,lines,cum,focus=[24]00", "heap"}, {"tree,relative_percentages,cum,focus=[24]00", "heap"}, {"tree,lines,cum,show_from=line2", "cpu"}, @@ -92,6 +96,8 @@ func TestParse(t *testing.T) { {"peek=line.*01", "cpu"}, {"weblist=line[13],addresses,flat", "cpu"}, {"tags,tagfocus=400kb:", "heap_request"}, + {"dot", "longNameFuncs"}, + {"text", "longNameFuncs"}, } baseVars := pprofVariables @@ -108,9 +114,6 @@ func TestParse(t *testing.T) { flags := strings.Split(tc.flags, ",") - // Skip the output format in the first flag, to output to a proto - addFlags(&f, flags[1:]) - // Encode profile into a protobuf and decode it again. protoTempFile, err := ioutil.TempFile("", "profile_proto") if err != nil { @@ -123,11 +126,13 @@ func TestParse(t *testing.T) { if flags[0] == "topproto" { f.bools["proto"] = false f.bools["topproto"] = true + f.bools["addresses"] = true } // First pprof invocation to save the profile into a profile.proto. - o1 := setDefaults(nil) - o1.Flagset = f + // Pass in flag set hen setting defaults, because otherwise default + // transport will try to add flags to the default flag set. + o1 := setDefaults(&plugin.Options{Flagset: f}) o1.Fetch = testFetcher{} o1.Sym = testSymbolizer{} o1.UI = testUI @@ -144,28 +149,28 @@ func TestParse(t *testing.T) { } defer os.Remove(outputTempFile.Name()) defer outputTempFile.Close() + + f = baseFlags() f.strings["output"] = outputTempFile.Name() f.args = []string{protoTempFile.Name()} - var solution string + delete(f.bools, "proto") + addFlags(&f, flags) + solution := solutionFilename(tc.source, &f) // Apply the flags for the second pprof run, and identify name of // the file containing expected results if flags[0] == "topproto" { + addFlags(&f, flags) solution = solutionFilename(tc.source, &f) delete(f.bools, "topproto") f.bools["text"] = true - } else { - delete(f.bools, "proto") - addFlags(&f, flags[:1]) - solution = solutionFilename(tc.source, &f) } - // The add_comment flag is not idempotent so only apply it on the first run. - delete(f.strings, "add_comment") // Second pprof invocation to read the profile from profile.proto // and generate a report. - o2 := setDefaults(nil) - o2.Flagset = f + // Pass in flag set hen setting defaults, because otherwise default + // transport will try to add flags to the default flag set. + o2 := setDefaults(&plugin.Options{Flagset: f}) o2.Sym = testSymbolizeDemangler{} o2.Obj = new(mockObjTool) o2.UI = testUI @@ -250,7 +255,8 @@ func testSourceURL(port int) string { func solutionFilename(source string, f *testFlags) string { name := []string{"pprof", strings.TrimPrefix(source, testSourceURL(8000))} name = addString(name, f, []string{"flat", "cum"}) - name = addString(name, f, []string{"functions", "files", "lines", "addresses"}) + name = addString(name, f, []string{"functions", "filefunctions", "files", "lines", "addresses"}) + name = addString(name, f, []string{"noinlines"}) name = addString(name, f, []string{"inuse_space", "inuse_objects", "alloc_space", "alloc_objects"}) name = addString(name, f, []string{"relative_percentages"}) name = addString(name, f, []string{"seconds"}) @@ -293,6 +299,8 @@ type testFlags struct { func (testFlags) ExtraUsage() string { return "" } +func (testFlags) AddExtraUsage(eu string) {} + func (f testFlags) Bool(s string, d bool, c string) *bool { if b, ok := f.bools[s]; ok { return &b @@ -436,6 +444,8 @@ func (testFetcher) Fetch(s string, d, t time.Duration) (*profile.Profile, string p = contentionProfile() case "symbolz": p = symzProfile() + case "longNameFuncs": + p = longNameFuncsProfile() default: return nil, "", fmt.Errorf("unexpected source: %s", s) } @@ -519,6 +529,83 @@ func fakeDemangler(name string) string { } } +// Returns a profile with function names which should be shortened in +// graph and flame views. +func longNameFuncsProfile() *profile.Profile { + var longNameFuncsM = []*profile.Mapping{ + { + ID: 1, + Start: 0x1000, + Limit: 0x4000, + File: "/path/to/testbinary", + HasFunctions: true, + HasFilenames: true, + HasLineNumbers: true, + HasInlineFrames: true, + }, + } + + var longNameFuncsF = []*profile.Function{ + {ID: 1, Name: "path/to/package1.object.function1", SystemName: "path/to/package1.object.function1", Filename: "path/to/package1.go"}, + {ID: 2, Name: "(anonymous namespace)::Bar::Foo", SystemName: "(anonymous namespace)::Bar::Foo", Filename: "a/long/path/to/package2.cc"}, + {ID: 3, Name: "java.bar.foo.FooBar.run(java.lang.Runnable)", SystemName: "java.bar.foo.FooBar.run(java.lang.Runnable)", Filename: "FooBar.java"}, + } + + var longNameFuncsL = []*profile.Location{ + { + ID: 1000, + Mapping: longNameFuncsM[0], + Address: 0x1000, + Line: []profile.Line{ + {Function: longNameFuncsF[0], Line: 1}, + }, + }, + { + ID: 2000, + Mapping: longNameFuncsM[0], + Address: 0x2000, + Line: []profile.Line{ + {Function: longNameFuncsF[1], Line: 4}, + }, + }, + { + ID: 3000, + Mapping: longNameFuncsM[0], + Address: 0x3000, + Line: []profile.Line{ + {Function: longNameFuncsF[2], Line: 9}, + }, + }, + } + + return &profile.Profile{ + PeriodType: &profile.ValueType{Type: "cpu", Unit: "milliseconds"}, + Period: 1, + DurationNanos: 10e9, + SampleType: []*profile.ValueType{ + {Type: "samples", Unit: "count"}, + {Type: "cpu", Unit: "milliseconds"}, + }, + Sample: []*profile.Sample{ + { + Location: []*profile.Location{longNameFuncsL[0], longNameFuncsL[1], longNameFuncsL[2]}, + Value: []int64{1000, 1000}, + }, + { + Location: []*profile.Location{longNameFuncsL[0], longNameFuncsL[1]}, + Value: []int64{100, 100}, + }, + { + Location: []*profile.Location{longNameFuncsL[2]}, + Value: []int64{10, 10}, + }, + }, + Location: longNameFuncsL, + Function: longNameFuncsF, + Mapping: longNameFuncsM, + } +} + func cpuProfile() *profile.Profile { var cpuM = []*profile.Mapping{ { diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/fetch.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/fetch.go index 7a7a1a20f2..b8a69e87fc 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/driver/fetch.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/fetch.go @@ -16,7 +16,6 @@ package driver import ( "bytes" - "crypto/tls" "fmt" "io" "io/ioutil" @@ -57,7 +56,7 @@ func fetchProfiles(s *source, o *plugin.Options) (*profile.Profile, error) { }) } - p, pbase, m, mbase, save, err := grabSourcesAndBases(sources, bases, o.Fetch, o.Obj, o.UI) + p, pbase, m, mbase, save, err := grabSourcesAndBases(sources, bases, o.Fetch, o.Obj, o.UI, o.HTTPTransport) if err != nil { return nil, err } @@ -123,7 +122,7 @@ func fetchProfiles(s *source, o *plugin.Options) (*profile.Profile, error) { return p, nil } -func grabSourcesAndBases(sources, bases []profileSource, fetch plugin.Fetcher, obj plugin.ObjTool, ui plugin.UI) (*profile.Profile, *profile.Profile, plugin.MappingSources, plugin.MappingSources, bool, error) { +func grabSourcesAndBases(sources, bases []profileSource, fetch plugin.Fetcher, obj plugin.ObjTool, ui plugin.UI, tr http.RoundTripper) (*profile.Profile, *profile.Profile, plugin.MappingSources, plugin.MappingSources, bool, error) { wg := sync.WaitGroup{} wg.Add(2) var psrc, pbase *profile.Profile @@ -133,11 +132,11 @@ func grabSourcesAndBases(sources, bases []profileSource, fetch plugin.Fetcher, o var countsrc, countbase int go func() { defer wg.Done() - psrc, msrc, savesrc, countsrc, errsrc = chunkedGrab(sources, fetch, obj, ui) + psrc, msrc, savesrc, countsrc, errsrc = chunkedGrab(sources, fetch, obj, ui, tr) }() go func() { defer wg.Done() - pbase, mbase, savebase, countbase, errbase = chunkedGrab(bases, fetch, obj, ui) + pbase, mbase, savebase, countbase, errbase = chunkedGrab(bases, fetch, obj, ui, tr) }() wg.Wait() save := savesrc || savebase @@ -167,7 +166,7 @@ func grabSourcesAndBases(sources, bases []profileSource, fetch plugin.Fetcher, o // chunkedGrab fetches the profiles described in source and merges them into // a single profile. It fetches a chunk of profiles concurrently, with a maximum // chunk size to limit its memory usage. -func chunkedGrab(sources []profileSource, fetch plugin.Fetcher, obj plugin.ObjTool, ui plugin.UI) (*profile.Profile, plugin.MappingSources, bool, int, error) { +func chunkedGrab(sources []profileSource, fetch plugin.Fetcher, obj plugin.ObjTool, ui plugin.UI, tr http.RoundTripper) (*profile.Profile, plugin.MappingSources, bool, int, error) { const chunkSize = 64 var p *profile.Profile @@ -180,7 +179,7 @@ func chunkedGrab(sources []profileSource, fetch plugin.Fetcher, obj plugin.ObjTo if end > len(sources) { end = len(sources) } - chunkP, chunkMsrc, chunkSave, chunkCount, chunkErr := concurrentGrab(sources[start:end], fetch, obj, ui) + chunkP, chunkMsrc, chunkSave, chunkCount, chunkErr := concurrentGrab(sources[start:end], fetch, obj, ui, tr) switch { case chunkErr != nil: return nil, nil, false, 0, chunkErr @@ -204,13 +203,13 @@ func chunkedGrab(sources []profileSource, fetch plugin.Fetcher, obj plugin.ObjTo } // concurrentGrab fetches multiple profiles concurrently -func concurrentGrab(sources []profileSource, fetch plugin.Fetcher, obj plugin.ObjTool, ui plugin.UI) (*profile.Profile, plugin.MappingSources, bool, int, error) { +func concurrentGrab(sources []profileSource, fetch plugin.Fetcher, obj plugin.ObjTool, ui plugin.UI, tr http.RoundTripper) (*profile.Profile, plugin.MappingSources, bool, int, error) { wg := sync.WaitGroup{} wg.Add(len(sources)) for i := range sources { go func(s *profileSource) { defer wg.Done() - s.p, s.msrc, s.remote, s.err = grabProfile(s.source, s.addr, fetch, obj, ui) + s.p, s.msrc, s.remote, s.err = grabProfile(s.source, s.addr, fetch, obj, ui, tr) }(&sources[i]) } wg.Wait() @@ -310,7 +309,7 @@ const testSourceAddress = "pproftest.local" // grabProfile fetches a profile. Returns the profile, sources for the // profile mappings, a bool indicating if the profile was fetched // remotely, and an error. -func grabProfile(s *source, source string, fetcher plugin.Fetcher, obj plugin.ObjTool, ui plugin.UI) (p *profile.Profile, msrc plugin.MappingSources, remote bool, err error) { +func grabProfile(s *source, source string, fetcher plugin.Fetcher, obj plugin.ObjTool, ui plugin.UI, tr http.RoundTripper) (p *profile.Profile, msrc plugin.MappingSources, remote bool, err error) { var src string duration, timeout := time.Duration(s.Seconds)*time.Second, time.Duration(s.Timeout)*time.Second if fetcher != nil { @@ -321,7 +320,7 @@ func grabProfile(s *source, source string, fetcher plugin.Fetcher, obj plugin.Ob } if err != nil || p == nil { // Fetch the profile over HTTP or from a file. - p, src, err = fetch(source, duration, timeout, ui) + p, src, err = fetch(source, duration, timeout, ui, tr) if err != nil { return } @@ -461,7 +460,7 @@ mapping: // fetch fetches a profile from source, within the timeout specified, // producing messages through the ui. It returns the profile and the // url of the actual source of the profile for remote profiles. -func fetch(source string, duration, timeout time.Duration, ui plugin.UI) (p *profile.Profile, src string, err error) { +func fetch(source string, duration, timeout time.Duration, ui plugin.UI, tr http.RoundTripper) (p *profile.Profile, src string, err error) { var f io.ReadCloser if sourceURL, timeout := adjustURL(source, duration, timeout); sourceURL != "" { @@ -469,7 +468,7 @@ func fetch(source string, duration, timeout time.Duration, ui plugin.UI) (p *pro if duration > 0 { ui.Print(fmt.Sprintf("Please wait... (%v)", duration)) } - f, err = fetchURL(sourceURL, timeout) + f, err = fetchURL(sourceURL, timeout, tr) src = sourceURL } else if isPerfFile(source) { f, err = convertPerfData(source, ui) @@ -484,8 +483,12 @@ func fetch(source string, duration, timeout time.Duration, ui plugin.UI) (p *pro } // fetchURL fetches a profile from a URL using HTTP. -func fetchURL(source string, timeout time.Duration) (io.ReadCloser, error) { - resp, err := httpGet(source, timeout) +func fetchURL(source string, timeout time.Duration, tr http.RoundTripper) (io.ReadCloser, error) { + client := &http.Client{ + Transport: tr, + Timeout: timeout + 5*time.Second, + } + resp, err := client.Get(source) if err != nil { return nil, fmt.Errorf("http fetch: %v", err) } @@ -582,30 +585,3 @@ func adjustURL(source string, duration, timeout time.Duration) (string, time.Dur u.RawQuery = values.Encode() return u.String(), timeout } - -// httpGet is a wrapper around http.Get; it is defined as a variable -// so it can be redefined during for testing. -var httpGet = func(source string, timeout time.Duration) (*http.Response, error) { - url, err := url.Parse(source) - if err != nil { - return nil, err - } - - var tlsConfig *tls.Config - if url.Scheme == "https+insecure" { - tlsConfig = &tls.Config{ - InsecureSkipVerify: true, - } - url.Scheme = "https" - source = url.String() - } - - client := &http.Client{ - Transport: &http.Transport{ - Proxy: http.ProxyFromEnvironment, - TLSClientConfig: tlsConfig, - ResponseHeaderTimeout: timeout + 5*time.Second, - }, - } - return client.Get(source) -} diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/fetch_test.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/fetch_test.go index e67b2e9f87..b9e9dfe8f4 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/driver/fetch_test.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/fetch_test.go @@ -24,8 +24,8 @@ import ( "fmt" "io/ioutil" "math/big" + "net" "net/http" - "net/url" "os" "path/filepath" "reflect" @@ -39,6 +39,7 @@ import ( "github.com/google/pprof/internal/plugin" "github.com/google/pprof/internal/proftest" "github.com/google/pprof/internal/symbolizer" + "github.com/google/pprof/internal/transport" "github.com/google/pprof/profile" ) @@ -173,12 +174,6 @@ func (testFile) Close() error { func TestFetch(t *testing.T) { const path = "testdata/" - - // Intercept http.Get calls from HTTPFetcher. - savedHTTPGet := httpGet - defer func() { httpGet = savedHTTPGet }() - httpGet = stubHTTPGet - type testcase struct { source, execName string } @@ -188,7 +183,7 @@ func TestFetch(t *testing.T) { {path + "go.nomappings.crash", "/bin/gotest.exe"}, {"http://localhost/profile?file=cppbench.cpu", ""}, } { - p, _, _, err := grabProfile(&source{ExecName: tc.execName}, tc.source, nil, testObj{}, &proftest.TestUI{T: t}) + p, _, _, err := grabProfile(&source{ExecName: tc.execName}, tc.source, nil, testObj{}, &proftest.TestUI{T: t}, &httpTransport{}) if err != nil { t.Fatalf("%s: %s", tc.source, err) } @@ -449,8 +444,9 @@ func TestFetchWithBase(t *testing.T) { f.args = tc.sources o := setDefaults(&plugin.Options{ - UI: &proftest.TestUI{T: t, AllowRx: "Local symbolization failed|Some binary filenames not available"}, - Flagset: f, + UI: &proftest.TestUI{T: t, AllowRx: "Local symbolization failed|Some binary filenames not available"}, + Flagset: f, + HTTPTransport: transport.New(nil), }) src, _, err := parseFlags(o) @@ -503,19 +499,14 @@ func mappingSources(key, source string, start uint64) plugin.MappingSources { } } -// stubHTTPGet intercepts a call to http.Get and rewrites it to use -// "file://" to get the profile directly from a file. -func stubHTTPGet(source string, _ time.Duration) (*http.Response, error) { - url, err := url.Parse(source) - if err != nil { - return nil, err - } +type httpTransport struct{} - values := url.Query() +func (tr *httpTransport) RoundTrip(req *http.Request) (*http.Response, error) { + values := req.URL.Query() file := values.Get("file") if file == "" { - return nil, fmt.Errorf("want .../file?profile, got %s", source) + return nil, fmt.Errorf("want .../file?profile, got %s", req.URL.String()) } t := &http.Transport{} @@ -532,7 +523,7 @@ func closedError() string { return "use of closed" } -func TestHttpsInsecure(t *testing.T) { +func TestHTTPSInsecure(t *testing.T) { if runtime.GOOS == "nacl" || runtime.GOOS == "js" { t.Skip("test assumes tcp available") } @@ -553,7 +544,8 @@ func TestHttpsInsecure(t *testing.T) { pprofVariables = baseVars.makeCopy() defer func() { pprofVariables = baseVars }() - tlsConfig := &tls.Config{Certificates: []tls.Certificate{selfSignedCert(t)}} + tlsCert, _, _ := selfSignedCert(t, "") + tlsConfig := &tls.Config{Certificates: []tls.Certificate{tlsCert}} l, err := tls.Listen("tcp", "localhost:0", tlsConfig) if err != nil { @@ -586,8 +578,9 @@ func TestHttpsInsecure(t *testing.T) { Symbolize: "remote", } o := &plugin.Options{ - Obj: &binutils.Binutils{}, - UI: &proftest.TestUI{T: t, AllowRx: "Saved profile in"}, + Obj: &binutils.Binutils{}, + UI: &proftest.TestUI{T: t, AllowRx: "Saved profile in"}, + HTTPTransport: transport.New(nil), } o.Sym = &symbolizer.Symbolizer{Obj: o.Obj, UI: o.UI} p, err := fetchProfiles(s, o) @@ -600,7 +593,122 @@ func TestHttpsInsecure(t *testing.T) { if len(p.Function) == 0 { t.Fatalf("fetchProfiles(%s) got non-symbolized profile: len(p.Function)==0", address) } - if err := checkProfileHasFunction(p, "TestHttpsInsecure"); err != nil { + if err := checkProfileHasFunction(p, "TestHTTPSInsecure"); err != nil { + t.Fatalf("fetchProfiles(%s) %v", address, err) + } +} + +func TestHTTPSWithServerCertFetch(t *testing.T) { + if runtime.GOOS == "nacl" || runtime.GOOS == "js" { + t.Skip("test assumes tcp available") + } + saveHome := os.Getenv(homeEnv()) + tempdir, err := ioutil.TempDir("", "home") + if err != nil { + t.Fatal("creating temp dir: ", err) + } + defer os.RemoveAll(tempdir) + + // pprof writes to $HOME/pprof by default which is not necessarily + // writeable (e.g. on a Debian buildd) so set $HOME to something we + // know we can write to for the duration of the test. + os.Setenv(homeEnv(), tempdir) + defer os.Setenv(homeEnv(), saveHome) + + baseVars := pprofVariables + pprofVariables = baseVars.makeCopy() + defer func() { pprofVariables = baseVars }() + + cert, certBytes, keyBytes := selfSignedCert(t, "localhost") + cas := x509.NewCertPool() + cas.AppendCertsFromPEM(certBytes) + + tlsConfig := &tls.Config{ + RootCAs: cas, + Certificates: []tls.Certificate{cert}, + ClientAuth: tls.RequireAndVerifyClientCert, + ClientCAs: cas, + } + + l, err := tls.Listen("tcp", "localhost:0", tlsConfig) + if err != nil { + t.Fatalf("net.Listen: got error %v, want no error", err) + } + + donec := make(chan error, 1) + go func(donec chan<- error) { + donec <- http.Serve(l, nil) + }(donec) + defer func() { + if got, want := <-donec, closedError(); !strings.Contains(got.Error(), want) { + t.Fatalf("Serve got error %v, want %q", got, want) + } + }() + defer l.Close() + + outputTempFile, err := ioutil.TempFile("", "profile_output") + if err != nil { + t.Fatalf("Failed to create tempfile: %v", err) + } + defer os.Remove(outputTempFile.Name()) + defer outputTempFile.Close() + + // Get port from the address, so request to the server can be made using + // the host name specified in certificates. + _, portStr, err := net.SplitHostPort(l.Addr().String()) + if err != nil { + t.Fatalf("cannot get port from URL: %v", err) + } + address := "https://" + "localhost:" + portStr + "/debug/pprof/goroutine" + s := &source{ + Sources: []string{address}, + Seconds: 10, + Timeout: 10, + Symbolize: "remote", + } + + certTempFile, err := ioutil.TempFile("", "cert_output") + if err != nil { + t.Errorf("cannot create cert tempfile: %v", err) + } + defer os.Remove(certTempFile.Name()) + defer certTempFile.Close() + certTempFile.Write(certBytes) + + keyTempFile, err := ioutil.TempFile("", "key_output") + if err != nil { + t.Errorf("cannot create key tempfile: %v", err) + } + defer os.Remove(keyTempFile.Name()) + defer keyTempFile.Close() + keyTempFile.Write(keyBytes) + + f := &testFlags{ + strings: map[string]string{ + "tls_cert": certTempFile.Name(), + "tls_key": keyTempFile.Name(), + "tls_ca": certTempFile.Name(), + }, + } + o := &plugin.Options{ + Obj: &binutils.Binutils{}, + UI: &proftest.TestUI{T: t, AllowRx: "Saved profile in"}, + Flagset: f, + HTTPTransport: transport.New(f), + } + + o.Sym = &symbolizer.Symbolizer{Obj: o.Obj, UI: o.UI, Transport: o.HTTPTransport} + p, err := fetchProfiles(s, o) + if err != nil { + t.Fatal(err) + } + if len(p.SampleType) == 0 { + t.Fatalf("fetchProfiles(%s) got empty profile: len(p.SampleType)==0", address) + } + if len(p.Function) == 0 { + t.Fatalf("fetchProfiles(%s) got non-symbolized profile: len(p.Function)==0", address) + } + if err := checkProfileHasFunction(p, "TestHTTPSWithServerCertFetch"); err != nil { t.Fatalf("fetchProfiles(%s) %v", address, err) } } @@ -614,7 +722,10 @@ func checkProfileHasFunction(p *profile.Profile, fname string) error { return fmt.Errorf("got %s, want function %q", p.String(), fname) } -func selfSignedCert(t *testing.T) tls.Certificate { +// selfSignedCert generates a self-signed certificate, and returns the +// generated certificate, and byte arrays containing the certificate and +// key associated with the certificate. +func selfSignedCert(t *testing.T, host string) (tls.Certificate, []byte, []byte) { privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatalf("failed to generate private key: %v", err) @@ -629,6 +740,8 @@ func selfSignedCert(t *testing.T) tls.Certificate { SerialNumber: big.NewInt(1), NotBefore: time.Now(), NotAfter: time.Now().Add(10 * time.Minute), + IsCA: true, + DNSNames: []string{host}, } b, err = x509.CreateCertificate(rand.Reader, &tmpl, &tmpl, privKey.Public(), privKey) @@ -641,5 +754,5 @@ func selfSignedCert(t *testing.T) tls.Certificate { if err != nil { t.Fatalf("failed to create TLS key pair: %v", err) } - return cert + return cert, bc, bk } diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/flags.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/flags.go new file mode 100644 index 0000000000..c48fb5cd2e --- /dev/null +++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/flags.go @@ -0,0 +1,78 @@ +package driver + +import ( + "flag" + "strings" +) + +// GoFlags implements the plugin.FlagSet interface. +type GoFlags struct { + UsageMsgs []string +} + +// Bool implements the plugin.FlagSet interface. +func (*GoFlags) Bool(o string, d bool, c string) *bool { + return flag.Bool(o, d, c) +} + +// Int implements the plugin.FlagSet interface. +func (*GoFlags) Int(o string, d int, c string) *int { + return flag.Int(o, d, c) +} + +// Float64 implements the plugin.FlagSet interface. +func (*GoFlags) Float64(o string, d float64, c string) *float64 { + return flag.Float64(o, d, c) +} + +// String implements the plugin.FlagSet interface. +func (*GoFlags) String(o, d, c string) *string { + return flag.String(o, d, c) +} + +// BoolVar implements the plugin.FlagSet interface. +func (*GoFlags) BoolVar(b *bool, o string, d bool, c string) { + flag.BoolVar(b, o, d, c) +} + +// IntVar implements the plugin.FlagSet interface. +func (*GoFlags) IntVar(i *int, o string, d int, c string) { + flag.IntVar(i, o, d, c) +} + +// Float64Var implements the plugin.FlagSet interface. +// the value of the flag. +func (*GoFlags) Float64Var(f *float64, o string, d float64, c string) { + flag.Float64Var(f, o, d, c) +} + +// StringVar implements the plugin.FlagSet interface. +func (*GoFlags) StringVar(s *string, o, d, c string) { + flag.StringVar(s, o, d, c) +} + +// StringList implements the plugin.FlagSet interface. +func (*GoFlags) StringList(o, d, c string) *[]*string { + return &[]*string{flag.String(o, d, c)} +} + +// ExtraUsage implements the plugin.FlagSet interface. +func (f *GoFlags) ExtraUsage() string { + return strings.Join(f.UsageMsgs, "\n") +} + +// AddExtraUsage implements the plugin.FlagSet interface. +func (f *GoFlags) AddExtraUsage(eu string) { + f.UsageMsgs = append(f.UsageMsgs, eu) +} + +// Parse implements the plugin.FlagSet interface. +func (*GoFlags) Parse(usage func()) []string { + flag.Usage = usage + flag.Parse() + args := flag.Args() + if len(args) == 0 { + usage() + } + return args +} diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/flamegraph.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/flamegraph.go index c9b9a5398f..13613cff86 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/driver/flamegraph.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/flamegraph.go @@ -55,7 +55,7 @@ func (ui *webInterface) flamegraph(w http.ResponseWriter, req *http.Request) { v := n.CumValue() fullName := n.Info.PrintableName() node := &treeNode{ - Name: getNodeShortName(fullName), + Name: graph.ShortenFunctionName(fullName), FullName: fullName, Cum: v, CumFormat: config.FormatValue(v), @@ -101,19 +101,3 @@ func (ui *webInterface) flamegraph(w http.ResponseWriter, req *http.Request) { Nodes: nodeArr, }) } - -// getNodeShortName builds a short node name from fullName. -func getNodeShortName(name string) string { - chunks := strings.SplitN(name, "(", 2) - head := chunks[0] - pathSep := strings.LastIndexByte(head, '/') - if pathSep == -1 || pathSep+1 >= len(head) { - return name - } - // Check if name is a stdlib package, i.e. doesn't have "." before "/" - if dot := strings.IndexByte(head, '.'); dot == -1 || dot > pathSep { - return name - } - // Trim package path prefix from node name - return name[pathSep+1:] -} diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/flamegraph_test.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/flamegraph_test.go deleted file mode 100644 index c1a887c830..0000000000 --- a/src/cmd/vendor/github.com/google/pprof/internal/driver/flamegraph_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package driver - -import "testing" - -func TestGetNodeShortName(t *testing.T) { - type testCase struct { - name string - want string - } - testcases := []testCase{ - { - "root", - "root", - }, - { - "syscall.Syscall", - "syscall.Syscall", - }, - { - "net/http.(*conn).serve", - "net/http.(*conn).serve", - }, - { - "github.com/blah/foo.Foo", - "foo.Foo", - }, - { - "github.com/blah/foo_bar.(*FooBar).Foo", - "foo_bar.(*FooBar).Foo", - }, - { - "encoding/json.(*structEncoder).(encoding/json.encode)-fm", - "encoding/json.(*structEncoder).(encoding/json.encode)-fm", - }, - { - "github.com/blah/blah/vendor/gopkg.in/redis.v3.(*baseClient).(github.com/blah/blah/vendor/gopkg.in/redis.v3.process)-fm", - "redis.v3.(*baseClient).(github.com/blah/blah/vendor/gopkg.in/redis.v3.process)-fm", - }, - } - for _, tc := range testcases { - name := getNodeShortName(tc.name) - if got, want := name, tc.want; got != want { - t.Errorf("for %s, got %q, want %q", tc.name, got, want) - } - } -} diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/interactive_test.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/interactive_test.go index 8d775e16bd..758adf9bdc 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/driver/interactive_test.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/interactive_test.go @@ -23,6 +23,7 @@ import ( "github.com/google/pprof/internal/plugin" "github.com/google/pprof/internal/proftest" "github.com/google/pprof/internal/report" + "github.com/google/pprof/internal/transport" "github.com/google/pprof/profile" ) @@ -41,7 +42,10 @@ func TestShell(t *testing.T) { // Random interleave of independent scripts pprofVariables = testVariables(savedVariables) - o := setDefaults(nil) + + // pass in HTTPTransport when setting defaults, because otherwise default + // transport will try to add flags to the default flag set. + o := setDefaults(&plugin.Options{HTTPTransport: transport.New(nil)}) o.UI = newUI(t, interleave(script, 0)) if err := interactive(p, o); err != nil { t.Error("first attempt:", err) @@ -259,12 +263,13 @@ func TestInteractiveCommands(t *testing.T) { { "weblist find -test", map[string]string{ - "functions": "false", - "addressnoinlines": "true", - "nodecount": "0", - "cum": "false", - "flat": "true", - "ignore": "test", + "functions": "false", + "addresses": "true", + "noinlines": "true", + "nodecount": "0", + "cum": "false", + "flat": "true", + "ignore": "test", }, }, { diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/options.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/options.go index 34167d4bf5..6e8f9fca25 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/driver/options.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/options.go @@ -16,7 +16,6 @@ package driver import ( "bufio" - "flag" "fmt" "io" "os" @@ -25,6 +24,7 @@ import ( "github.com/google/pprof/internal/binutils" "github.com/google/pprof/internal/plugin" "github.com/google/pprof/internal/symbolizer" + "github.com/google/pprof/internal/transport" ) // setDefaults returns a new plugin.Options with zero fields sets to @@ -38,7 +38,7 @@ func setDefaults(o *plugin.Options) *plugin.Options { d.Writer = oswriter{} } if d.Flagset == nil { - d.Flagset = goFlags{} + d.Flagset = &GoFlags{} } if d.Obj == nil { d.Obj = &binutils.Binutils{} @@ -46,67 +46,15 @@ func setDefaults(o *plugin.Options) *plugin.Options { if d.UI == nil { d.UI = &stdUI{r: bufio.NewReader(os.Stdin)} } + if d.HTTPTransport == nil { + d.HTTPTransport = transport.New(d.Flagset) + } if d.Sym == nil { - d.Sym = &symbolizer.Symbolizer{Obj: d.Obj, UI: d.UI} + d.Sym = &symbolizer.Symbolizer{Obj: d.Obj, UI: d.UI, Transport: d.HTTPTransport} } return d } -// goFlags returns a flagset implementation based on the standard flag -// package from the Go distribution. It implements the plugin.FlagSet -// interface. -type goFlags struct{} - -func (goFlags) Bool(o string, d bool, c string) *bool { - return flag.Bool(o, d, c) -} - -func (goFlags) Int(o string, d int, c string) *int { - return flag.Int(o, d, c) -} - -func (goFlags) Float64(o string, d float64, c string) *float64 { - return flag.Float64(o, d, c) -} - -func (goFlags) String(o, d, c string) *string { - return flag.String(o, d, c) -} - -func (goFlags) BoolVar(b *bool, o string, d bool, c string) { - flag.BoolVar(b, o, d, c) -} - -func (goFlags) IntVar(i *int, o string, d int, c string) { - flag.IntVar(i, o, d, c) -} - -func (goFlags) Float64Var(f *float64, o string, d float64, c string) { - flag.Float64Var(f, o, d, c) -} - -func (goFlags) StringVar(s *string, o, d, c string) { - flag.StringVar(s, o, d, c) -} - -func (goFlags) StringList(o, d, c string) *[]*string { - return &[]*string{flag.String(o, d, c)} -} - -func (goFlags) ExtraUsage() string { - return "" -} - -func (goFlags) Parse(usage func()) []string { - flag.Usage = usage - flag.Parse() - args := flag.Args() - if len(args) == 0 { - usage() - } - return args -} - type stdUI struct { r *bufio.Reader } diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.cpu.flat.addresses.noinlines.text b/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.cpu.flat.addresses.noinlines.text new file mode 100644 index 0000000000..d53c44dad9 --- /dev/null +++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.cpu.flat.addresses.noinlines.text @@ -0,0 +1,7 @@ +Showing nodes accounting for 1.12s, 100% of 1.12s total +Dropped 1 node (cum <= 0.06s) + flat flat% sum% cum cum% + 1.10s 98.21% 98.21% 1.10s 98.21% 0000000000001000 line1000 testdata/file1000.src:1 + 0.01s 0.89% 99.11% 1.01s 90.18% 0000000000002000 line2000 testdata/file2000.src:4 + 0.01s 0.89% 100% 1.01s 90.18% 0000000000003000 line3000 testdata/file3000.src:6 + 0 0% 100% 0.10s 8.93% 0000000000003001 line3000 testdata/file3000.src:9 diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.cpu.flat.filefunctions.noinlines.text b/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.cpu.flat.filefunctions.noinlines.text new file mode 100644 index 0000000000..88fb760759 --- /dev/null +++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.cpu.flat.filefunctions.noinlines.text @@ -0,0 +1,5 @@ +Showing nodes accounting for 1.12s, 100% of 1.12s total + flat flat% sum% cum cum% + 1.10s 98.21% 98.21% 1.10s 98.21% line1000 testdata/file1000.src + 0.01s 0.89% 99.11% 1.01s 90.18% line2000 testdata/file2000.src + 0.01s 0.89% 100% 1.12s 100% line3000 testdata/file3000.src diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.cpu.flat.functions.noinlines.text b/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.cpu.flat.functions.noinlines.text new file mode 100644 index 0000000000..493b4912de --- /dev/null +++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.cpu.flat.functions.noinlines.text @@ -0,0 +1,5 @@ +Showing nodes accounting for 1.12s, 100% of 1.12s total + flat flat% sum% cum cum% + 1.10s 98.21% 98.21% 1.10s 98.21% line1000 + 0.01s 0.89% 99.11% 1.01s 90.18% line2000 + 0.01s 0.89% 100% 1.12s 100% line3000 diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.cpu.lines.topproto b/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.cpu.lines.topproto new file mode 100644 index 0000000000..33bf6814a4 --- /dev/null +++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.cpu.lines.topproto @@ -0,0 +1,3 @@ +Showing nodes accounting for 1s, 100% of 1s total + flat flat% sum% cum cum% + 1s 100% 100% 1s 100% mangled1000 testdata/file1000.src:1 diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.longNameFuncs.dot b/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.longNameFuncs.dot new file mode 100644 index 0000000000..474a5108ba --- /dev/null +++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.longNameFuncs.dot @@ -0,0 +1,9 @@ +digraph "testbinary" { +node [style=filled fillcolor="#f8f8f8"] +subgraph cluster_L { "File: testbinary" [shape=box fontsize=16 label="File: testbinary\lType: cpu\lDuration: 10s, Total samples = 1.11s (11.10%)\lShowing nodes accounting for 1.11s, 100% of 1.11s total\l" tooltip="testbinary"] } +N1 [label="package1\nobject\nfunction1\n1.10s (99.10%)" id="node1" fontsize=24 shape=box tooltip="path/to/package1.object.function1 (1.10s)" color="#b20000" fillcolor="#edd5d5"] +N2 [label="FooBar\nrun\n0.01s (0.9%)\nof 1.01s (90.99%)" id="node2" fontsize=10 shape=box tooltip="java.bar.foo.FooBar.run(java.lang.Runnable) (1.01s)" color="#b20400" fillcolor="#edd6d5"] +N3 [label="Bar\nFoo\n0 of 1.10s (99.10%)" id="node3" fontsize=8 shape=box tooltip="(anonymous namespace)::Bar::Foo (1.10s)" color="#b20000" fillcolor="#edd5d5"] +N3 -> N1 [label=" 1.10s" weight=100 penwidth=5 color="#b20000" tooltip="(anonymous namespace)::Bar::Foo -> path/to/package1.object.function1 (1.10s)" labeltooltip="(anonymous namespace)::Bar::Foo -> path/to/package1.object.function1 (1.10s)"] +N2 -> N3 [label=" 1s" weight=91 penwidth=5 color="#b20500" tooltip="java.bar.foo.FooBar.run(java.lang.Runnable) -> (anonymous namespace)::Bar::Foo (1s)" labeltooltip="java.bar.foo.FooBar.run(java.lang.Runnable) -> (anonymous namespace)::Bar::Foo (1s)"] +} diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.longNameFuncs.text b/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.longNameFuncs.text new file mode 100644 index 0000000000..39cb24ed6a --- /dev/null +++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.longNameFuncs.text @@ -0,0 +1,5 @@ +Showing nodes accounting for 1.11s, 100% of 1.11s total + flat flat% sum% cum cum% + 1.10s 99.10% 99.10% 1.10s 99.10% path/to/package1.object.function1 + 0.01s 0.9% 100% 1.01s 90.99% java.bar.foo.FooBar.run(java.lang.Runnable) + 0 0% 100% 1.10s 99.10% (anonymous namespace)::Bar::Foo diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/webhtml.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/webhtml.go index c3f9c384f8..74104899ca 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/driver/webhtml.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/webhtml.go @@ -249,6 +249,21 @@ table tr td { + {{$sampleLen := len .SampleTypes}} + {{if gt $sampleLen 1}} + + {{end}} +