From 142c002ee714c871a5644557f8320171c8569a1f Mon Sep 17 00:00:00 2001
From: Austin Clements
Date: Wed, 4 Sep 2019 15:14:13 -0400
Subject: [PATCH 001/199] misc/wasm: fix argv/envp layout
The wasm_exec.js wrapper tries to set up the argv and envp following
the UNIX conventions, but doesn't get it quite right, which can cause
runtime.goenv to crash if you get unlucky.
The main problem was that the envp array wasn't terminated with a nil
pointer, so the runtime didn't know when to stop reading the array.
This CL adds that nil pointer to the end of the envp array.
The other problem was harmless, but confusing. In the UNIX convention,
the argv array consists of argc pointers followed by a nil pointer,
followed by the envp array. However, wasm_exec.js put the environment
variable count between the two pointer arrays rather than a nil
pointer. The runtime never looks at this slot, so it didn't matter,
but the break from convention left Cherry and I trying to debug why it
*wasn't* losing any environment variables before we realized that that
layouts happened to be close enough to work. This CL switches to the
UNIX convention of simply terminating the argv array with a nil
pointer.
Change-Id: Ic9a4cd9eabb5dfa599a809b960f9e579b9f1f4db
Reviewed-on: https://go-review.googlesource.com/c/go/+/193417
Run-TryBot: Austin Clements
TryBot-Result: Gobot Gobot
Reviewed-by: Cherry Zhang
Reviewed-by: Richard Musiol
---
misc/wasm/wasm_exec.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/misc/wasm/wasm_exec.js b/misc/wasm/wasm_exec.js
index 1e1ba636ca..9639585693 100644
--- a/misc/wasm/wasm_exec.js
+++ b/misc/wasm/wasm_exec.js
@@ -457,12 +457,13 @@
this.argv.forEach((arg) => {
argvPtrs.push(strPtr(arg));
});
+ argvPtrs.push(0);
const keys = Object.keys(this.env).sort();
- argvPtrs.push(keys.length);
keys.forEach((key) => {
argvPtrs.push(strPtr(`${key}=${this.env[key]}`));
});
+ argvPtrs.push(0);
const argv = offset;
argvPtrs.forEach((ptr) => {
From 75da700d0ae307ebfd4a3493b53e8f361c16f481 Mon Sep 17 00:00:00 2001
From: Cuong Manh Le
Date: Sat, 14 Sep 2019 01:29:19 +0700
Subject: [PATCH 002/199] cmd/compile: consistently use strlit to access
constants string values
Passes toolstash-check.
Change-Id: Ieaef20b7649787727b69469f93ffc942022bc079
Reviewed-on: https://go-review.googlesource.com/c/go/+/195198
Run-TryBot: Cuong Manh Le
TryBot-Result: Gobot Gobot
Reviewed-by: Matthew Dempsky
---
src/cmd/compile/internal/gc/const.go | 4 ++--
src/cmd/compile/internal/gc/noder.go | 6 +++---
src/cmd/compile/internal/gc/order.go | 2 +-
src/cmd/compile/internal/gc/sinit.go | 2 +-
src/cmd/compile/internal/gc/ssa.go | 2 +-
src/cmd/compile/internal/gc/typecheck.go | 10 +++++-----
src/cmd/compile/internal/gc/walk.go | 12 ++++++------
7 files changed, 19 insertions(+), 19 deletions(-)
diff --git a/src/cmd/compile/internal/gc/const.go b/src/cmd/compile/internal/gc/const.go
index e40c23b8ef..510b1cd15d 100644
--- a/src/cmd/compile/internal/gc/const.go
+++ b/src/cmd/compile/internal/gc/const.go
@@ -612,7 +612,7 @@ func evconst(n *Node) {
var strs []string
i2 := i1
for i2 < len(s) && Isconst(s[i2], CTSTR) {
- strs = append(strs, s[i2].Val().U.(string))
+ strs = append(strs, strlit(s[i2]))
i2++
}
@@ -635,7 +635,7 @@ func evconst(n *Node) {
switch nl.Type.Etype {
case TSTRING:
if Isconst(nl, CTSTR) {
- setintconst(n, int64(len(nl.Val().U.(string))))
+ setintconst(n, int64(len(strlit(nl))))
}
case TARRAY:
if !hascallchan(nl) {
diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go
index a60b854b2c..6bbabb45dd 100644
--- a/src/cmd/compile/internal/gc/noder.go
+++ b/src/cmd/compile/internal/gc/noder.go
@@ -762,7 +762,7 @@ func (p *noder) sum(x syntax.Expr) *Node {
n := p.expr(x)
if Isconst(n, CTSTR) && n.Sym == nil {
nstr = n
- chunks = append(chunks, nstr.Val().U.(string))
+ chunks = append(chunks, strlit(nstr))
}
for i := len(adds) - 1; i >= 0; i-- {
@@ -772,12 +772,12 @@ func (p *noder) sum(x syntax.Expr) *Node {
if Isconst(r, CTSTR) && r.Sym == nil {
if nstr != nil {
// Collapse r into nstr instead of adding to n.
- chunks = append(chunks, r.Val().U.(string))
+ chunks = append(chunks, strlit(r))
continue
}
nstr = r
- chunks = append(chunks, nstr.Val().U.(string))
+ chunks = append(chunks, strlit(nstr))
} else {
if len(chunks) > 1 {
nstr.SetVal(Val{U: strings.Join(chunks, "")})
diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go
index 0ea43f114e..ee04b69a68 100644
--- a/src/cmd/compile/internal/gc/order.go
+++ b/src/cmd/compile/internal/gc/order.go
@@ -1017,7 +1017,7 @@ func (o *Order) expr(n, lhs *Node) *Node {
haslit := false
for _, n1 := range n.List.Slice() {
hasbyte = hasbyte || n1.Op == OBYTES2STR
- haslit = haslit || n1.Op == OLITERAL && len(n1.Val().U.(string)) != 0
+ haslit = haslit || n1.Op == OLITERAL && len(strlit(n1)) != 0
}
if haslit && hasbyte {
diff --git a/src/cmd/compile/internal/gc/sinit.go b/src/cmd/compile/internal/gc/sinit.go
index ae8e79d854..ae16d41b1c 100644
--- a/src/cmd/compile/internal/gc/sinit.go
+++ b/src/cmd/compile/internal/gc/sinit.go
@@ -211,7 +211,7 @@ func (s *InitSchedule) staticassign(l *Node, r *Node) bool {
case OSTR2BYTES:
if l.Class() == PEXTERN && r.Left.Op == OLITERAL {
- sval := r.Left.Val().U.(string)
+ sval := strlit(r.Left)
slicebytes(l, sval, len(sval))
return true
}
diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go
index 97d9b0f912..7b0c7e5c43 100644
--- a/src/cmd/compile/internal/gc/ssa.go
+++ b/src/cmd/compile/internal/gc/ssa.go
@@ -2336,7 +2336,7 @@ func (s *state) expr(n *Node) *ssa.Value {
// Replace "abc"[1] with 'b'.
// Delayed until now because "abc"[1] is not an ideal constant.
// See test/fixedbugs/issue11370.go.
- return s.newValue0I(ssa.OpConst8, types.Types[TUINT8], int64(int8(n.Left.Val().U.(string)[n.Right.Int64()])))
+ return s.newValue0I(ssa.OpConst8, types.Types[TUINT8], int64(int8(strlit(n.Left)[n.Right.Int64()])))
}
a := s.expr(n.Left)
i := s.expr(n.Right)
diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go
index e725c6f363..050a74b1e6 100644
--- a/src/cmd/compile/internal/gc/typecheck.go
+++ b/src/cmd/compile/internal/gc/typecheck.go
@@ -1043,8 +1043,8 @@ func typecheck1(n *Node, top int) (res *Node) {
yyerror("invalid %s index %v (index must be non-negative)", why, n.Right)
} else if t.IsArray() && x >= t.NumElem() {
yyerror("invalid array index %v (out of bounds for %d-element array)", n.Right, t.NumElem())
- } else if Isconst(n.Left, CTSTR) && x >= int64(len(n.Left.Val().U.(string))) {
- yyerror("invalid string index %v (out of bounds for %d-byte string)", n.Right, len(n.Left.Val().U.(string)))
+ } else if Isconst(n.Left, CTSTR) && x >= int64(len(strlit(n.Left))) {
+ yyerror("invalid string index %v (out of bounds for %d-byte string)", n.Right, len(strlit(n.Left)))
} else if n.Right.Val().U.(*Mpint).Cmp(maxintval[TINT]) > 0 {
yyerror("invalid %s index %v (index too large)", why, n.Right)
}
@@ -2148,8 +2148,8 @@ func checksliceindex(l *Node, r *Node, tp *types.Type) bool {
} else if tp != nil && tp.NumElem() >= 0 && r.Int64() > tp.NumElem() {
yyerror("invalid slice index %v (out of bounds for %d-element array)", r, tp.NumElem())
return false
- } else if Isconst(l, CTSTR) && r.Int64() > int64(len(l.Val().U.(string))) {
- yyerror("invalid slice index %v (out of bounds for %d-byte string)", r, len(l.Val().U.(string)))
+ } else if Isconst(l, CTSTR) && r.Int64() > int64(len(strlit(l))) {
+ yyerror("invalid slice index %v (out of bounds for %d-byte string)", r, len(strlit(l)))
return false
} else if r.Val().U.(*Mpint).Cmp(maxintval[TINT]) > 0 {
yyerror("invalid slice index %v (index too large)", r)
@@ -3409,7 +3409,7 @@ func stringtoruneslit(n *Node) *Node {
}
var l []*Node
- s := n.Left.Val().U.(string)
+ s := strlit(n.Left)
i := 0
for _, r := range s {
l = append(l, nod(OKEY, nodintconst(int64(i)), nodintconst(int64(r))))
diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go
index cb49e0f7ce..d2036b6e32 100644
--- a/src/cmd/compile/internal/gc/walk.go
+++ b/src/cmd/compile/internal/gc/walk.go
@@ -1054,7 +1054,7 @@ opswitch:
yyerror("index out of bounds")
}
} else if Isconst(n.Left, CTSTR) {
- n.SetBounded(bounded(r, int64(len(n.Left.Val().U.(string)))))
+ n.SetBounded(bounded(r, int64(len(strlit(n.Left)))))
if Debug['m'] != 0 && n.Bounded() && !Isconst(n.Right, CTINT) {
Warn("index bounds check elided")
}
@@ -1389,7 +1389,7 @@ opswitch:
case OSTR2BYTES:
s := n.Left
if Isconst(s, CTSTR) {
- sc := s.Val().U.(string)
+ sc := strlit(s)
// Allocate a [n]byte of the right size.
t := types.NewArray(types.Types[TUINT8], int64(len(sc)))
@@ -1792,7 +1792,7 @@ func walkprint(nn *Node, init *Nodes) *Node {
for i := 0; i < len(s); {
var strs []string
for i < len(s) && Isconst(s[i], CTSTR) {
- strs = append(strs, s[i].Val().U.(string))
+ strs = append(strs, strlit(s[i]))
i++
}
if len(strs) > 0 {
@@ -1861,7 +1861,7 @@ func walkprint(nn *Node, init *Nodes) *Node {
case TSTRING:
cs := ""
if Isconst(n, CTSTR) {
- cs = n.Val().U.(string)
+ cs = strlit(n)
}
switch cs {
case " ":
@@ -2510,7 +2510,7 @@ func addstr(n *Node, init *Nodes) *Node {
sz := int64(0)
for _, n1 := range n.List.Slice() {
if n1.Op == OLITERAL {
- sz += int64(len(n1.Val().U.(string)))
+ sz += int64(len(strlit(n1)))
}
}
@@ -3350,7 +3350,7 @@ func walkcompareString(n *Node, init *Nodes) *Node {
// Length-only checks are ok, though.
maxRewriteLen = 0
}
- if s := cs.Val().U.(string); len(s) <= maxRewriteLen {
+ if s := strlit(cs); len(s) <= maxRewriteLen {
if len(s) > 0 {
ncs = safeexpr(ncs, init)
}
From d9b8ffa51cf7cafe18107ec53a4ec3ceff15ce46 Mon Sep 17 00:00:00 2001
From: Cherry Zhang
Date: Wed, 28 Aug 2019 14:42:06 -0400
Subject: [PATCH 003/199] test/codegen: document -all_codegen option in README
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
It is useful to know about the -all_codegen option for running
codegen tests for all platforms. I was puzzling that some codegen
test was not failing on my local machine or on trybot, until I
found this option.
Change-Id: I062cf4d73f6a6c9ebc2258195779d2dab21bc36d
Reviewed-on: https://go-review.googlesource.com/c/go/+/192101
Reviewed-by: Daniel Martí
Run-TryBot: Daniel Martí
TryBot-Result: Gobot Gobot
---
test/codegen/README | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/test/codegen/README b/test/codegen/README
index f6877e701d..1afefd9e3c 100644
--- a/test/codegen/README
+++ b/test/codegen/README
@@ -30,6 +30,12 @@ transformation, it can be useful to first run the test harness with a
toolchain from a released Go version (and verify that the new tests
fail), and then re-runnig the tests using the devel toolchain.
+By default, only checks that are relevant to the current GOOS/GOARCH
+are run. Checks for all platforms can be enabled by specifiying the
+-all_codegen option, as
+
+ $ ../bin/go run run.go -all_codegen codegen
+
- Regexps comments syntax
From 49e7c7672d6d065435f7058df90b082cb552c7dd Mon Sep 17 00:00:00 2001
From: Lucas Bremgartner
Date: Fri, 13 Sep 2019 19:46:50 +0000
Subject: [PATCH 004/199] encoding/json: make Number with the ,string option
marshal with quotes
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add quotes when marshaling a json.Number with the string option
set via a struct tag. This ensures that the resulting json
can be unmarshaled into the source struct without error.
Fixes #34268
Change-Id: Ide167d9dec77019554870b5957b37dc258119d81
GitHub-Last-Rev: dde81b71208be01c253bb87dbb6f81ac6e0785be
GitHub-Pull-Request: golang/go#34269
Reviewed-on: https://go-review.googlesource.com/c/go/+/195043
Reviewed-by: Daniel Martí
Run-TryBot: Daniel Martí
TryBot-Result: Gobot Gobot
---
src/encoding/json/encode.go | 6 ++++++
src/encoding/json/encode_test.go | 5 ++++-
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/src/encoding/json/encode.go b/src/encoding/json/encode.go
index e5dd1b7799..b4fba476c8 100644
--- a/src/encoding/json/encode.go
+++ b/src/encoding/json/encode.go
@@ -600,7 +600,13 @@ func stringEncoder(e *encodeState, v reflect.Value, opts encOpts) {
if !isValidNumber(numStr) {
e.error(fmt.Errorf("json: invalid number literal %q", numStr))
}
+ if opts.quoted {
+ e.WriteByte('"')
+ }
e.WriteString(numStr)
+ if opts.quoted {
+ e.WriteByte('"')
+ }
return
}
if opts.quoted {
diff --git a/src/encoding/json/encode_test.go b/src/encoding/json/encode_test.go
index 18a92bae7c..8d3503b1ba 100644
--- a/src/encoding/json/encode_test.go
+++ b/src/encoding/json/encode_test.go
@@ -76,13 +76,15 @@ type StringTag struct {
IntStr int64 `json:",string"`
UintptrStr uintptr `json:",string"`
StrStr string `json:",string"`
+ NumberStr Number `json:",string"`
}
var stringTagExpected = `{
"BoolStr": "true",
"IntStr": "42",
"UintptrStr": "44",
- "StrStr": "\"xzbit\""
+ "StrStr": "\"xzbit\"",
+ "NumberStr": "46"
}`
func TestStringTag(t *testing.T) {
@@ -91,6 +93,7 @@ func TestStringTag(t *testing.T) {
s.IntStr = 42
s.UintptrStr = 44
s.StrStr = "xzbit"
+ s.NumberStr = "46"
got, err := MarshalIndent(&s, "", " ")
if err != nil {
t.Fatal(err)
From 782228a73f4c6c8019ab5763e749d5ef98905e5f Mon Sep 17 00:00:00 2001
From: Lynn Boger
Date: Tue, 25 Jun 2019 13:15:59 -0400
Subject: [PATCH 005/199] doc: update ppc64 section for asm.html
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Update the section in asm.html related to PPC64. Remove the line
that says it is in an experimental state, add a link to the
new doc.go file that has all the detail for the Go assembler for
PPC64.
Change-Id: I45d9891669e01d94e2721be576d572e02cd9d2db
Reviewed-on: https://go-review.googlesource.com/c/go/+/183840
Run-TryBot: Lynn Boger
Reviewed-by: Carlos Eduardo Seo
Reviewed-by: Daniel Martí
Reviewed-by: Cherry Zhang
TryBot-Result: Gobot Gobot
---
doc/asm.html | 17 +++--------------
1 file changed, 3 insertions(+), 14 deletions(-)
diff --git a/doc/asm.html b/doc/asm.html
index 11033fe3c5..d89072e319 100644
--- a/doc/asm.html
+++ b/doc/asm.html
@@ -832,27 +832,16 @@
-(R5)(R6*1): The location at R5 plus R6. It is a scaled
-mode as on the x86, but the only scale allowed is 1.
-
-
-
-(R5+R6): Alias for (R5)(R6*1)
-
-
IBM z/Architecture, a.k.a. s390x
From 531f1d50cc15c95822d189d039a9bfe47ff70099 Mon Sep 17 00:00:00 2001
From: Maya Rashish
Date: Sun, 21 Apr 2019 14:48:28 +0000
Subject: [PATCH 006/199] syscall: avoid zeroing unused syscall arguments
Zeroing unused registers is not required. Removing it makes the code
very slightly smaller and very slightly faster.
Change-Id: I1ec17b497db971ca8a3641e3e94c063571419f27
GitHub-Last-Rev: f721bb263637717e8ff9fd2c34148b5b2762e8c4
GitHub-Pull-Request: golang/go#31596
Reviewed-on: https://go-review.googlesource.com/c/go/+/173160
Reviewed-by: Ian Lance Taylor
---
src/syscall/asm_darwin_amd64.s | 6 ------
src/syscall/asm_linux_amd64.s | 9 ---------
src/syscall/asm_unix_amd64.s | 6 ------
3 files changed, 21 deletions(-)
diff --git a/src/syscall/asm_darwin_amd64.s b/src/syscall/asm_darwin_amd64.s
index eab4fcdc06..c863889a71 100644
--- a/src/syscall/asm_darwin_amd64.s
+++ b/src/syscall/asm_darwin_amd64.s
@@ -17,9 +17,6 @@ TEXT ·Syscall(SB),NOSPLIT,$0-56
MOVQ a1+8(FP), DI
MOVQ a2+16(FP), SI
MOVQ a3+24(FP), DX
- MOVQ $0, R10
- MOVQ $0, R8
- MOVQ $0, R9
MOVQ trap+0(FP), AX // syscall entry
ADDQ $0x2000000, AX
SYSCALL
@@ -100,9 +97,6 @@ TEXT ·RawSyscall(SB),NOSPLIT,$0-56
MOVQ a1+8(FP), DI
MOVQ a2+16(FP), SI
MOVQ a3+24(FP), DX
- MOVQ $0, R10
- MOVQ $0, R8
- MOVQ $0, R9
MOVQ trap+0(FP), AX // syscall entry
ADDQ $0x2000000, AX
SYSCALL
diff --git a/src/syscall/asm_linux_amd64.s b/src/syscall/asm_linux_amd64.s
index 364815df18..2c3374338f 100644
--- a/src/syscall/asm_linux_amd64.s
+++ b/src/syscall/asm_linux_amd64.s
@@ -19,9 +19,6 @@ TEXT ·Syscall(SB),NOSPLIT,$0-56
MOVQ a1+8(FP), DI
MOVQ a2+16(FP), SI
MOVQ a3+24(FP), DX
- MOVQ $0, R10
- MOVQ $0, R8
- MOVQ $0, R9
MOVQ trap+0(FP), AX // syscall entry
SYSCALL
CMPQ AX, $0xfffffffffffff001
@@ -70,9 +67,6 @@ TEXT ·RawSyscall(SB),NOSPLIT,$0-56
MOVQ a1+8(FP), DI
MOVQ a2+16(FP), SI
MOVQ a3+24(FP), DX
- MOVQ $0, R10
- MOVQ $0, R8
- MOVQ $0, R9
MOVQ trap+0(FP), AX // syscall entry
SYSCALL
CMPQ AX, $0xfffffffffffff001
@@ -139,9 +133,6 @@ TEXT ·rawSyscallNoError(SB),NOSPLIT,$0-48
MOVQ a1+8(FP), DI
MOVQ a2+16(FP), SI
MOVQ a3+24(FP), DX
- MOVQ $0, R10
- MOVQ $0, R8
- MOVQ $0, R9
MOVQ trap+0(FP), AX // syscall entry
SYSCALL
MOVQ AX, r1+32(FP)
diff --git a/src/syscall/asm_unix_amd64.s b/src/syscall/asm_unix_amd64.s
index 025408f9e1..9cf3fe0d35 100644
--- a/src/syscall/asm_unix_amd64.s
+++ b/src/syscall/asm_unix_amd64.s
@@ -21,9 +21,6 @@ TEXT ·Syscall(SB),NOSPLIT,$0-56
MOVQ a1+8(FP), DI
MOVQ a2+16(FP), SI
MOVQ a3+24(FP), DX
- MOVQ $0, R10
- MOVQ $0, R8
- MOVQ $0, R9
SYSCALL
JCC ok
MOVQ $-1, r1+32(FP) // r1
@@ -65,9 +62,6 @@ TEXT ·RawSyscall(SB),NOSPLIT,$0-56
MOVQ a1+8(FP), DI
MOVQ a2+16(FP), SI
MOVQ a3+24(FP), DX
- MOVQ $0, R10
- MOVQ $0, R8
- MOVQ $0, R9
MOVQ trap+0(FP), AX // syscall entry
SYSCALL
JCC ok1
From 4ae25ff1405f9d6b25f40141f42196e8f142f207 Mon Sep 17 00:00:00 2001
From: Ian Lance Taylor
Date: Wed, 11 Sep 2019 17:57:58 -0700
Subject: [PATCH 007/199] os/signal: split up sleeps waiting for signal
Try to deflake TestNohup.
The kernel will deliver a signal as a thread returns from a syscall.
If the only active thread is sleeping, and the system is busy,
the kernel may not get around to waking up a thread to catch the signal.
Try splitting up the sleep, to give the kernel another change to deliver.
I don't know if this will help, but it seems worth a try.
Fixes #33174
Change-Id: I34b3240af706501ab8538cb25c4846d1d30d7691
Reviewed-on: https://go-review.googlesource.com/c/go/+/194879
Reviewed-by: Bryan C. Mills
---
src/os/signal/signal_test.go | 34 +++++++++++++++++++++++++---------
1 file changed, 25 insertions(+), 9 deletions(-)
diff --git a/src/os/signal/signal_test.go b/src/os/signal/signal_test.go
index 6ea59f4697..d50e595d84 100644
--- a/src/os/signal/signal_test.go
+++ b/src/os/signal/signal_test.go
@@ -23,14 +23,20 @@ import (
)
func waitSig(t *testing.T, c <-chan os.Signal, sig os.Signal) {
- select {
- case s := <-c:
- if s != sig {
- t.Fatalf("signal was %v, want %v", s, sig)
+ // Sleep multiple times to give the kernel more tries to
+ // deliver the signal.
+ for i := 0; i < 10; i++ {
+ select {
+ case s := <-c:
+ if s != sig {
+ t.Fatalf("signal was %v, want %v", s, sig)
+ }
+ return
+
+ case <-time.After(100 * time.Millisecond):
}
- case <-time.After(1 * time.Second):
- t.Fatalf("timeout waiting for %v", sig)
}
+ t.Fatalf("timeout waiting for %v", sig)
}
// Test that basic signal handling works.
@@ -268,7 +274,15 @@ func TestStop(t *testing.T) {
if sig == syscall.SIGWINCH || (sig == syscall.SIGHUP && *sendUncaughtSighup == 1) {
syscall.Kill(syscall.Getpid(), sig)
}
- time.Sleep(100 * time.Millisecond)
+
+ // The kernel will deliver a signal as a thread returns
+ // from a syscall. If the only active thread is sleeping,
+ // and the system is busy, the kernel may not get around
+ // to waking up a thread to catch the signal.
+ // We try splitting up the sleep to give the kernel
+ // another chance to deliver the signal.
+ time.Sleep(50 * time.Millisecond)
+ time.Sleep(50 * time.Millisecond)
// Ask for signal
c := make(chan os.Signal, 1)
@@ -280,10 +294,11 @@ func TestStop(t *testing.T) {
waitSig(t, c, sig)
Stop(c)
+ time.Sleep(50 * time.Millisecond)
select {
case s := <-c:
t.Fatalf("unexpected signal %v", s)
- case <-time.After(100 * time.Millisecond):
+ case <-time.After(50 * time.Millisecond):
// nothing to read - good
}
@@ -294,10 +309,11 @@ func TestStop(t *testing.T) {
syscall.Kill(syscall.Getpid(), sig)
}
+ time.Sleep(50 * time.Millisecond)
select {
case s := <-c:
t.Fatalf("unexpected signal %v", s)
- case <-time.After(100 * time.Millisecond):
+ case <-time.After(50 * time.Millisecond):
// nothing to read - good
}
}
From 606019cb4b1c8fb57e5a83747ee0aff1054291d8 Mon Sep 17 00:00:00 2001
From: Matthew Dempsky
Date: Thu, 12 Sep 2019 10:18:03 -0700
Subject: [PATCH 008/199] cmd/compile: trim function name prefix from escape
diagnostics
This information is redundant with the position information already
provided. Also, no other -m diagnostics print out function name.
While here, report parameter leak diagnostics against the parameter
declaration position rather than the function, and use Warnl for
"moved to heap" messages.
Test cases updated programmatically by removing the first word from
every "no match for" error emitted by run.go:
go run run.go |& \
sed -E -n 's/^(.*):(.*): no match for `([^ ]* (.*))` in:$/\1!\2!\3!\4/p' | \
while IFS='!' read -r fn line before after; do
before=$(echo "$before" | sed 's/[.[\*^$()+?{|]/\\&/g')
after=$(echo "$after" | sed -E 's/(\&|\\)/\\&/g')
fn=$(find . -name "${fn}" | head -1)
sed -i -E -e "${line}s/\"${before}\"/\"${after}\"/" "${fn}"
done
Passes toolstash-check.
Change-Id: I6e02486b1409e4a8dbb2b9b816d22095835426b5
Reviewed-on: https://go-review.googlesource.com/c/go/+/195040
Run-TryBot: Matthew Dempsky
TryBot-Result: Gobot Gobot
Reviewed-by: Cherry Zhang
---
src/cmd/compile/internal/gc/esc.go | 22 +--
src/cmd/compile/internal/gc/escape.go | 2 +-
test/escape2.go | 238 +++++++++++++-------------
test/escape2n.go | 238 +++++++++++++-------------
test/escape_array.go | 4 +-
test/escape_calls.go | 2 +-
test/escape_closure.go | 14 +-
test/escape_field.go | 10 +-
test/escape_iface.go | 2 +-
test/escape_indir.go | 2 +-
test/escape_param.go | 20 +--
test/escape_struct_param1.go | 52 +++---
test/escape_struct_param2.go | 52 +++---
test/fixedbugs/issue12006.go | 16 +-
test/fixedbugs/issue12588.go | 2 +-
test/fixedbugs/issue13799.go | 22 +--
test/fixedbugs/issue17318.go | 10 +-
test/fixedbugs/issue21709.go | 10 +-
test/fixedbugs/issue7921.go | 14 +-
test/inline.go | 12 +-
test/inline_big.go | 6 +-
test/linkname.dir/linkname1.go | 2 +-
test/live_syscall.go | 2 +-
23 files changed, 377 insertions(+), 377 deletions(-)
diff --git a/src/cmd/compile/internal/gc/esc.go b/src/cmd/compile/internal/gc/esc.go
index c350d7c1bc..301fa7a8fc 100644
--- a/src/cmd/compile/internal/gc/esc.go
+++ b/src/cmd/compile/internal/gc/esc.go
@@ -392,7 +392,7 @@ func moveToHeap(n *Node) {
n.Name.Param.Heapaddr = heapaddr
n.Esc = EscHeap
if Debug['m'] != 0 {
- fmt.Printf("%v: moved to heap: %v\n", n.Line(), n)
+ Warnl(n.Pos, "moved to heap: %v", n)
}
}
@@ -422,7 +422,7 @@ func (e *Escape) paramTag(fn *Node, narg int, f *types.Field) string {
// argument and pass those annotations along to importing code.
if f.Type.Etype == TUINTPTR {
if Debug['m'] != 0 {
- Warnl(fn.Pos, "%v assuming %v is unsafe uintptr", funcSym(fn), name())
+ Warnl(f.Pos, "assuming %v is unsafe uintptr", name())
}
return unsafeUintptrTag
}
@@ -435,13 +435,13 @@ func (e *Escape) paramTag(fn *Node, narg int, f *types.Field) string {
// //go:noescape is given before the declaration.
if fn.Noescape() {
if Debug['m'] != 0 && f.Sym != nil {
- Warnl(fn.Pos, "%S %v does not escape", funcSym(fn), name())
+ Warnl(f.Pos, "%v does not escape", name())
}
return mktag(EscNone)
}
if Debug['m'] != 0 && f.Sym != nil {
- Warnl(fn.Pos, "leaking param: %v", name())
+ Warnl(f.Pos, "leaking param: %v", name())
}
return mktag(EscHeap)
}
@@ -449,14 +449,14 @@ func (e *Escape) paramTag(fn *Node, narg int, f *types.Field) string {
if fn.Func.Pragma&UintptrEscapes != 0 {
if f.Type.Etype == TUINTPTR {
if Debug['m'] != 0 {
- Warnl(fn.Pos, "%v marking %v as escaping uintptr", funcSym(fn), name())
+ Warnl(f.Pos, "marking %v as escaping uintptr", name())
}
return uintptrEscapesTag
}
if f.IsDDD() && f.Type.Elem().Etype == TUINTPTR {
// final argument is ...uintptr.
if Debug['m'] != 0 {
- Warnl(fn.Pos, "%v marking %v as escaping ...uintptr", funcSym(fn), name())
+ Warnl(f.Pos, "marking %v as escaping ...uintptr", name())
}
return uintptrEscapesTag
}
@@ -477,17 +477,17 @@ func (e *Escape) paramTag(fn *Node, narg int, f *types.Field) string {
if Debug['m'] != 0 && !loc.escapes {
if esc == EscNone {
- Warnl(n.Pos, "%S %S does not escape", funcSym(fn), n)
+ Warnl(f.Pos, "%v does not escape", name())
} else if esc == EscHeap {
- Warnl(n.Pos, "leaking param: %S", n)
+ Warnl(f.Pos, "leaking param: %v", name())
} else {
if esc&EscContentEscapes != 0 {
- Warnl(n.Pos, "leaking param content: %S", n)
+ Warnl(f.Pos, "leaking param content: %v", name())
}
for i := 0; i < numEscReturns; i++ {
if x := getEscReturn(esc, i); x >= 0 {
- res := n.Name.Curfn.Type.Results().Field(i).Sym
- Warnl(n.Pos, "leaking param: %S to result %v level=%d", n, res, x)
+ res := fn.Type.Results().Field(i).Sym
+ Warnl(f.Pos, "leaking param: %v to result %v level=%d", name(), res, x)
}
}
}
diff --git a/src/cmd/compile/internal/gc/escape.go b/src/cmd/compile/internal/gc/escape.go
index ff958beef3..ce0462414f 100644
--- a/src/cmd/compile/internal/gc/escape.go
+++ b/src/cmd/compile/internal/gc/escape.go
@@ -1289,7 +1289,7 @@ func (e *Escape) finish(fns []*Node) {
addrescapes(n)
} else {
if Debug['m'] != 0 && n.Op != ONAME && n.Op != OTYPESW && n.Op != ORANGE && n.Op != ODEFER {
- Warnl(n.Pos, "%S %S does not escape", funcSym(loc.curfn), n)
+ Warnl(n.Pos, "%S does not escape", n)
}
n.Esc = EscNone
if loc.transient {
diff --git a/test/escape2.go b/test/escape2.go
index 49eb835915..b7cd914c22 100644
--- a/test/escape2.go
+++ b/test/escape2.go
@@ -37,24 +37,24 @@ func foo3b(t T) { // ERROR "leaking param: t$"
}
// xx isn't going anywhere, so use of yy is ok
-func foo4(xx, yy *int) { // ERROR "foo4 xx does not escape$" "foo4 yy does not escape$"
+func foo4(xx, yy *int) { // ERROR "xx does not escape$" "yy does not escape$"
xx = yy
}
// xx isn't going anywhere, so taking address of yy is ok
-func foo5(xx **int, yy *int) { // ERROR "foo5 xx does not escape$" "foo5 yy does not escape$"
+func foo5(xx **int, yy *int) { // ERROR "xx does not escape$" "yy does not escape$"
xx = &yy
}
-func foo6(xx **int, yy *int) { // ERROR "foo6 xx does not escape$" "leaking param: yy$"
+func foo6(xx **int, yy *int) { // ERROR "xx does not escape$" "leaking param: yy$"
*xx = yy
}
-func foo7(xx **int, yy *int) { // ERROR "foo7 xx does not escape$" "foo7 yy does not escape$"
+func foo7(xx **int, yy *int) { // ERROR "xx does not escape$" "yy does not escape$"
**xx = *yy
}
-func foo8(xx, yy *int) int { // ERROR "foo8 xx does not escape$" "foo8 yy does not escape$"
+func foo8(xx, yy *int) int { // ERROR "xx does not escape$" "yy does not escape$"
xx = yy
return *xx
}
@@ -64,7 +64,7 @@ func foo9(xx, yy *int) *int { // ERROR "leaking param: xx to result ~r2 level=0$
return xx
}
-func foo10(xx, yy *int) { // ERROR "foo10 xx does not escape$" "foo10 yy does not escape$"
+func foo10(xx, yy *int) { // ERROR "xx does not escape$" "yy does not escape$"
*xx = *yy
}
@@ -88,7 +88,7 @@ func foo13(yyy **int) { // ERROR "leaking param content: yyy$"
*xxx = *yyy
}
-func foo14(yyy **int) { // ERROR "foo14 yyy does not escape$"
+func foo14(yyy **int) { // ERROR "yyy does not escape$"
**xxx = **yyy
}
@@ -100,7 +100,7 @@ func foo16(yy *int) { // ERROR "leaking param: yy$"
*xxx = yy
}
-func foo17(yy *int) { // ERROR "foo17 yy does not escape$"
+func foo17(yy *int) { // ERROR "yy does not escape$"
**xxx = *yy
}
@@ -125,11 +125,11 @@ func NewBarp(x *int) *Bar { // ERROR "leaking param: x$"
return &Bar{42, x} // ERROR "&Bar literal escapes to heap$"
}
-func NewBarp2(x *int) *Bar { // ERROR "NewBarp2 x does not escape$"
+func NewBarp2(x *int) *Bar { // ERROR "x does not escape$"
return &Bar{*x, nil} // ERROR "&Bar literal escapes to heap$"
}
-func (b *Bar) NoLeak() int { // ERROR "\(\*Bar\).NoLeak b does not escape$"
+func (b *Bar) NoLeak() int { // ERROR "b does not escape$"
return *(b.ii)
}
@@ -157,7 +157,7 @@ func (b *Bar) LeaksABit() *int { // ERROR "leaking param: b to result ~r0 level=
return b.ii
}
-func (b Bar) StillNoLeak() int { // ERROR "Bar.StillNoLeak b does not escape$"
+func (b Bar) StillNoLeak() int { // ERROR "b does not escape$"
v := 0
b.ii = &v
return b.i
@@ -176,7 +176,7 @@ func NewBar2() *Bar2 {
return &Bar2{[12]int{42}, nil} // ERROR "&Bar2 literal escapes to heap$"
}
-func (b *Bar2) NoLeak() int { // ERROR "\(\*Bar2\).NoLeak b does not escape$"
+func (b *Bar2) NoLeak() int { // ERROR "b does not escape$"
return b.i[0]
}
@@ -188,7 +188,7 @@ func (b *Bar2) AlsoNoLeak() []int { // ERROR "leaking param: b to result ~r0 lev
return b.ii[0:1]
}
-func (b Bar2) AgainNoLeak() [12]int { // ERROR "Bar2.AgainNoLeak b does not escape$"
+func (b Bar2) AgainNoLeak() [12]int { // ERROR "b does not escape$"
return b.i
}
@@ -219,7 +219,7 @@ func foo21a() func() int {
func foo22() int {
x := 42
- return func() int { // ERROR "foo22 func literal does not escape$"
+ return func() int { // ERROR "func literal does not escape$"
return x
}()
}
@@ -250,7 +250,7 @@ func foo23c(x int) func() int { // ERROR "moved to heap: x$"
}
func foo24(x int) int {
- return func() int { // ERROR "foo24 func literal does not escape$"
+ return func() int { // ERROR "func literal does not escape$"
return x
}()
}
@@ -262,7 +262,7 @@ func fooleak(xx *int) int { // ERROR "leaking param: xx$"
return *x
}
-func foonoleak(xx *int) int { // ERROR "foonoleak xx does not escape$"
+func foonoleak(xx *int) int { // ERROR "xx does not escape$"
return *x + *xx
}
@@ -286,7 +286,7 @@ func (f *Foo) fooleak() { // ERROR "leaking param: f$"
pf = f
}
-func (f *Foo) foonoleak() { // ERROR "\(\*Foo\).foonoleak f does not escape$"
+func (f *Foo) foonoleak() { // ERROR "f does not escape$"
F.x = f.x
}
@@ -294,7 +294,7 @@ func (f *Foo) Leak() { // ERROR "leaking param: f$"
f.fooleak()
}
-func (f *Foo) NoLeak() { // ERROR "\(\*Foo\).NoLeak f does not escape$"
+func (f *Foo) NoLeak() { // ERROR "f does not escape$"
f.foonoleak()
}
@@ -302,11 +302,11 @@ func foo41(x int) { // ERROR "moved to heap: x$"
F.xx = &x
}
-func (f *Foo) foo42(x int) { // ERROR "\(\*Foo\).foo42 f does not escape$" "moved to heap: x$"
+func (f *Foo) foo42(x int) { // ERROR "f does not escape$" "moved to heap: x$"
f.xx = &x
}
-func foo43(f *Foo, x int) { // ERROR "foo43 f does not escape$" "moved to heap: x$"
+func foo43(f *Foo, x int) { // ERROR "f does not escape$" "moved to heap: x$"
f.xx = &x
}
@@ -314,7 +314,7 @@ func foo44(yy *int) { // ERROR "leaking param: yy$"
F.xx = yy
}
-func (f *Foo) foo45() { // ERROR "\(\*Foo\).foo45 f does not escape$"
+func (f *Foo) foo45() { // ERROR "f does not escape$"
F.x = f.x
}
@@ -407,7 +407,7 @@ func foo60(i *int) *int { // ERROR "leaking param: i to result ~r1 level=0$"
return a[1]
}
-func foo60a(i *int) *int { // ERROR "foo60a i does not escape$"
+func foo60a(i *int) *int { // ERROR "i does not escape$"
var a [12]*int
a[0] = i
return nil
@@ -423,7 +423,7 @@ func foo61(i *int) *int { // ERROR "leaking param: i to result ~r1 level=0$"
return s.b
}
-func foo61a(i *int) *int { // ERROR "foo61a i does not escape$"
+func foo61a(i *int) *int { // ERROR "i does not escape$"
type S struct {
a, b *int
}
@@ -439,7 +439,7 @@ func foo62(i *int) *int { // ERROR "leaking param: i$"
type S struct {
a, b *int
}
- s := new(S) // ERROR "foo62 new\(S\) does not escape$"
+ s := new(S) // ERROR "new\(S\) does not escape$"
s.a = i
return nil // s.b
}
@@ -448,7 +448,7 @@ type M interface {
M()
}
-func foo63(m M) { // ERROR "foo63 m does not escape$"
+func foo63(m M) { // ERROR "m does not escape$"
}
func foo64(m M) { // ERROR "leaking param: m$"
@@ -475,7 +475,7 @@ func foo66() {
func foo67() {
var mv MV
- foo63(mv) // ERROR "foo67 mv does not escape$"
+ foo63(mv) // ERROR "mv does not escape$"
}
func foo68() {
@@ -539,7 +539,7 @@ func foo72b() [10]*int {
// issue 2145
func foo73() {
- s := []int{3, 2, 1} // ERROR "foo73 \[\]int literal does not escape$"
+ s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$"
for _, v := range s {
vv := v
// actually just escapes its scope
@@ -550,7 +550,7 @@ func foo73() {
}
func foo731() {
- s := []int{3, 2, 1} // ERROR "foo731 \[\]int literal does not escape$"
+ s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$"
for _, v := range s {
vv := v // ERROR "moved to heap: vv$"
// actually just escapes its scope
@@ -562,7 +562,7 @@ func foo731() {
}
func foo74() {
- s := []int{3, 2, 1} // ERROR "foo74 \[\]int literal does not escape$"
+ s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$"
for _, v := range s {
vv := v
// actually just escapes its scope
@@ -574,7 +574,7 @@ func foo74() {
}
func foo74a() {
- s := []int{3, 2, 1} // ERROR "foo74a \[\]int literal does not escape$"
+ s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$"
for _, v := range s {
vv := v // ERROR "moved to heap: vv$"
// actually just escapes its scope
@@ -589,7 +589,7 @@ func foo74a() {
// issue 3975
func foo74b() {
var array [3]func()
- s := []int{3, 2, 1} // ERROR "foo74b \[\]int literal does not escape$"
+ s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$"
for i, v := range s {
vv := v
// actually just escapes its scope
@@ -601,7 +601,7 @@ func foo74b() {
func foo74c() {
var array [3]func()
- s := []int{3, 2, 1} // ERROR "foo74c \[\]int literal does not escape$"
+ s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$"
for i, v := range s {
vv := v // ERROR "moved to heap: vv$"
// actually just escapes its scope
@@ -611,57 +611,57 @@ func foo74c() {
}
}
-func myprint(y *int, x ...interface{}) *int { // ERROR "leaking param: y to result ~r2 level=0$" "myprint x does not escape$"
+func myprint(y *int, x ...interface{}) *int { // ERROR "leaking param: y to result ~r2 level=0$" "x does not escape$"
return y
}
-func myprint1(y *int, x ...interface{}) *interface{} { // ERROR "leaking param: x to result ~r2 level=0$" "myprint1 y does not escape$"
+func myprint1(y *int, x ...interface{}) *interface{} { // ERROR "leaking param: x to result ~r2 level=0$" "y does not escape$"
return &x[0]
}
-func foo75(z *int) { // ERROR "foo75 z does not escape$"
- myprint(z, 1, 2, 3) // ERROR "1 does not escape" "2 does not escape" "3 does not escape" "foo75 ... argument does not escape$"
+func foo75(z *int) { // ERROR "z does not escape$"
+ myprint(z, 1, 2, 3) // ERROR "1 does not escape" "2 does not escape" "3 does not escape" "... argument does not escape$"
}
-func foo75a(z *int) { // ERROR "foo75a z does not escape$"
- myprint1(z, 1, 2, 3) // ERROR "1 does not escape" "2 does not escape" "3 does not escape" "foo75a ... argument does not escape$"
+func foo75a(z *int) { // ERROR "z does not escape$"
+ myprint1(z, 1, 2, 3) // ERROR "1 does not escape" "2 does not escape" "3 does not escape" "... argument does not escape$"
}
func foo75esc(z *int) { // ERROR "leaking param: z$"
- gxx = myprint(z, 1, 2, 3) // ERROR "1 does not escape" "2 does not escape" "3 does not escape" "foo75esc ... argument does not escape$"
+ gxx = myprint(z, 1, 2, 3) // ERROR "1 does not escape" "2 does not escape" "3 does not escape" "... argument does not escape$"
}
-func foo75aesc(z *int) { // ERROR "foo75aesc z does not escape$"
+func foo75aesc(z *int) { // ERROR "z does not escape$"
var ppi **interface{} // assignments to pointer dereferences lose track
*ppi = myprint1(z, 1, 2, 3) // ERROR "... argument escapes to heap$" "1 escapes to heap$" "2 escapes to heap$" "3 escapes to heap$"
}
-func foo75aesc1(z *int) { // ERROR "foo75aesc1 z does not escape$"
+func foo75aesc1(z *int) { // ERROR "z does not escape$"
sink = myprint1(z, 1, 2, 3) // ERROR "... argument escapes to heap$" "1 escapes to heap$" "2 escapes to heap$" "3 escapes to heap$"
}
func foo76(z *int) { // ERROR "z does not escape"
- myprint(nil, z) // ERROR "foo76 ... argument does not escape$"
+ myprint(nil, z) // ERROR "... argument does not escape$"
}
func foo76a(z *int) { // ERROR "z does not escape"
- myprint1(nil, z) // ERROR "foo76a ... argument does not escape$"
+ myprint1(nil, z) // ERROR "... argument does not escape$"
}
func foo76b() {
- myprint(nil, 1, 2, 3) // ERROR "1 does not escape" "2 does not escape" "3 does not escape" "foo76b ... argument does not escape$"
+ myprint(nil, 1, 2, 3) // ERROR "1 does not escape" "2 does not escape" "3 does not escape" "... argument does not escape$"
}
func foo76c() {
- myprint1(nil, 1, 2, 3) // ERROR "1 does not escape" "2 does not escape" "3 does not escape" "foo76c ... argument does not escape$"
+ myprint1(nil, 1, 2, 3) // ERROR "1 does not escape" "2 does not escape" "3 does not escape" "... argument does not escape$"
}
func foo76d() {
- defer myprint(nil, 1, 2, 3) // ERROR "1 does not escape" "2 does not escape" "3 does not escape" "foo76d ... argument does not escape$"
+ defer myprint(nil, 1, 2, 3) // ERROR "1 does not escape" "2 does not escape" "3 does not escape" "... argument does not escape$"
}
func foo76e() {
- defer myprint1(nil, 1, 2, 3) // ERROR "1 does not escape" "2 does not escape" "3 does not escape" "foo76e ... argument does not escape$"
+ defer myprint1(nil, 1, 2, 3) // ERROR "1 does not escape" "2 does not escape" "3 does not escape" "... argument does not escape$"
}
func foo76f() {
@@ -677,11 +677,11 @@ func foo76g() {
}
}
-func foo77(z []interface{}) { // ERROR "foo77 z does not escape$"
+func foo77(z []interface{}) { // ERROR "z does not escape$"
myprint(nil, z...) // z does not escape
}
-func foo77a(z []interface{}) { // ERROR "foo77a z does not escape$"
+func foo77a(z []interface{}) { // ERROR "z does not escape$"
myprint1(nil, z...)
}
@@ -696,10 +696,10 @@ func foo77c(z []interface{}) { // ERROR "leaking param: z$"
func dotdotdot() {
i := 0
- myprint(nil, &i) // ERROR "dotdotdot ... argument does not escape$"
+ myprint(nil, &i) // ERROR "... argument does not escape$"
j := 0
- myprint1(nil, &j) // ERROR "dotdotdot ... argument does not escape$"
+ myprint1(nil, &j) // ERROR "... argument does not escape$"
}
func foo78(z int) *int { // ERROR "moved to heap: z$"
@@ -728,7 +728,7 @@ func foo80() *int {
func foo81() *int {
for {
- z := new(int) // ERROR "foo81 new\(int\) does not escape$"
+ z := new(int) // ERROR "new\(int\) does not escape$"
_ = z
}
return nil
@@ -736,7 +736,7 @@ func foo81() *int {
func tee(p *int) (x, y *int) { return p, p } // ERROR "leaking param: p to result x level=0$" "leaking param: p to result y level=0$"
-func noop(x, y *int) {} // ERROR "noop x does not escape$" "noop y does not escape$"
+func noop(x, y *int) {} // ERROR "x does not escape$" "y does not escape$"
func foo82() {
var x, y, z int // ERROR "moved to heap: x$" "moved to heap: y$" "moved to heap: z$"
@@ -775,7 +775,7 @@ func foo92(x *int) [2]*int { // ERROR "leaking param: x to result ~r1 level=0$"
}
// does not leak c
-func foo93(c chan *int) *int { // ERROR "foo93 c does not escape$"
+func foo93(c chan *int) *int { // ERROR "c does not escape$"
for v := range c {
return v
}
@@ -794,7 +794,7 @@ func foo94(m map[*int]*int, b bool) *int { // ERROR "leaking param: m to result
}
// does leak x
-func foo95(m map[*int]*int, x *int) { // ERROR "foo95 m does not escape$" "leaking param: x$"
+func foo95(m map[*int]*int, x *int) { // ERROR "m does not escape$" "leaking param: x$"
m[x] = x
}
@@ -809,7 +809,7 @@ func foo97(m [1]*int) *int { // ERROR "leaking param: m to result ~r1 level=0$"
}
// does not leak m
-func foo98(m map[int]*int) *int { // ERROR "foo98 m does not escape$"
+func foo98(m map[int]*int) *int { // ERROR "m does not escape$"
return m[0]
}
@@ -835,7 +835,7 @@ func foo101(m [1]*int) *int { // ERROR "leaking param: m to result ~r1 level=0$"
}
// does not leak m
-func foo101a(m [1]*int) *int { // ERROR "foo101a m does not escape$"
+func foo101a(m [1]*int) *int { // ERROR "m does not escape$"
for i := range m { // ERROR "moved to heap: i$"
return &i
}
@@ -843,12 +843,12 @@ func foo101a(m [1]*int) *int { // ERROR "foo101a m does not escape$"
}
// does leak x
-func foo102(m []*int, x *int) { // ERROR "foo102 m does not escape$" "leaking param: x$"
+func foo102(m []*int, x *int) { // ERROR "m does not escape$" "leaking param: x$"
m[0] = x
}
// does not leak x
-func foo103(m [1]*int, x *int) { // ERROR "foo103 m does not escape$" "foo103 x does not escape$"
+func foo103(m [1]*int, x *int) { // ERROR "m does not escape$" "x does not escape$"
m[0] = x
}
@@ -878,7 +878,7 @@ func foo108(x *int) map[*int]*int { // ERROR "leaking param: x$"
}
func foo109(x *int) *int { // ERROR "leaking param: x$"
- m := map[*int]*int{x: nil} // ERROR "foo109 map\[\*int\]\*int literal does not escape$"
+ m := map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int literal does not escape$"
for k, _ := range m {
return k
}
@@ -886,12 +886,12 @@ func foo109(x *int) *int { // ERROR "leaking param: x$"
}
func foo110(x *int) *int { // ERROR "leaking param: x$"
- m := map[*int]*int{nil: x} // ERROR "foo110 map\[\*int\]\*int literal does not escape$"
+ m := map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int literal does not escape$"
return m[nil]
}
func foo111(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0"
- m := []*int{x} // ERROR "foo111 \[\]\*int literal does not escape$"
+ m := []*int{x} // ERROR "\[\]\*int literal does not escape$"
return m[0]
}
@@ -906,7 +906,7 @@ func foo113(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0$"
}
func foo114(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0$"
- m := &Bar{ii: x} // ERROR "foo114 &Bar literal does not escape$"
+ m := &Bar{ii: x} // ERROR "&Bar literal does not escape$"
return m.ii
}
@@ -925,12 +925,12 @@ func foo116(b bool) *int {
return nil
}
-func foo117(unknown func(interface{})) { // ERROR "foo117 unknown does not escape$"
+func foo117(unknown func(interface{})) { // ERROR "unknown does not escape$"
x := 1 // ERROR "moved to heap: x$"
unknown(&x)
}
-func foo118(unknown func(*int)) { // ERROR "foo118 unknown does not escape$"
+func foo118(unknown func(*int)) { // ERROR "unknown does not escape$"
x := 1 // ERROR "moved to heap: x$"
unknown(&x)
}
@@ -1167,7 +1167,7 @@ func foo122() {
goto L1
L1:
- i = new(int) // ERROR "foo122 new\(int\) does not escape$"
+ i = new(int) // ERROR "new\(int\) does not escape$"
_ = i
}
@@ -1182,18 +1182,18 @@ L1:
_ = i
}
-func foo124(x **int) { // ERROR "foo124 x does not escape$"
+func foo124(x **int) { // ERROR "x does not escape$"
var i int // ERROR "moved to heap: i$"
p := &i
- func() { // ERROR "foo124 func literal does not escape$"
+ func() { // ERROR "func literal does not escape$"
*x = p
}()
}
-func foo125(ch chan *int) { // ERROR "foo125 ch does not escape$"
+func foo125(ch chan *int) { // ERROR "ch does not escape$"
var i int // ERROR "moved to heap: i$"
p := &i
- func() { // ERROR "foo125 func literal does not escape$"
+ func() { // ERROR "func literal does not escape$"
ch <- p
}()
}
@@ -1203,7 +1203,7 @@ func foo126() {
for {
// loopdepth 1
var i int // ERROR "moved to heap: i$"
- func() { // ERROR "foo126 func literal does not escape$"
+ func() { // ERROR "func literal does not escape$"
px = &i
}()
}
@@ -1229,9 +1229,9 @@ func foo128() {
func foo129() {
var i int // ERROR "moved to heap: i$"
p := &i
- func() { // ERROR "foo129 func literal does not escape$"
+ func() { // ERROR "func literal does not escape$"
q := p
- func() { // ERROR "foo129.func1 func literal does not escape$"
+ func() { // ERROR "func literal does not escape$"
r := q
px = r
}()
@@ -1241,7 +1241,7 @@ func foo129() {
func foo130() {
for {
var i int // ERROR "moved to heap: i$"
- func() { // ERROR "foo130 func literal does not escape$"
+ func() { // ERROR "func literal does not escape$"
px = &i
}()
}
@@ -1249,7 +1249,7 @@ func foo130() {
func foo131() {
var i int // ERROR "moved to heap: i$"
- func() { // ERROR "foo131 func literal does not escape$"
+ func() { // ERROR "func literal does not escape$"
px = &i
}()
}
@@ -1263,7 +1263,7 @@ func foo132() {
func foo133() {
var i int // ERROR "moved to heap: i$"
- defer func() { // ERROR "foo133 func literal does not escape$"
+ defer func() { // ERROR "func literal does not escape$"
px = &i
}()
}
@@ -1271,9 +1271,9 @@ func foo133() {
func foo134() {
var i int
p := &i
- func() { // ERROR "foo134 func literal does not escape$"
+ func() { // ERROR "func literal does not escape$"
q := p
- func() { // ERROR "foo134.func1 func literal does not escape$"
+ func() { // ERROR "func literal does not escape$"
r := q
_ = r
}()
@@ -1285,7 +1285,7 @@ func foo135() {
p := &i
go func() { // ERROR "func literal escapes to heap$"
q := p
- func() { // ERROR "foo135.func1 func literal does not escape$"
+ func() { // ERROR "func literal does not escape$"
r := q
_ = r
}()
@@ -1297,7 +1297,7 @@ func foo136() {
p := &i
go func() { // ERROR "func literal escapes to heap$"
q := p
- func() { // ERROR "foo136.func1 func literal does not escape$"
+ func() { // ERROR "func literal does not escape$"
r := q
px = r
}()
@@ -1307,7 +1307,7 @@ func foo136() {
func foo137() {
var i int // ERROR "moved to heap: i$"
p := &i
- func() { // ERROR "foo137 func literal does not escape$"
+ func() { // ERROR "func literal does not escape$"
q := p
go func() { // ERROR "func literal escapes to heap$"
r := q
@@ -1358,7 +1358,7 @@ func F2([]byte)
//go:noescape
-func F3(x []byte) // ERROR "F3 x does not escape$"
+func F3(x []byte) // ERROR "x does not escape$"
func F4(x []byte) // ERROR "leaking param: x$"
@@ -1380,14 +1380,14 @@ type Tm struct {
x int
}
-func (t *Tm) M() { // ERROR "\(\*Tm\).M t does not escape$"
+func (t *Tm) M() { // ERROR "t does not escape$"
}
func foo141() {
var f func()
t := new(Tm) // ERROR "new\(Tm\) escapes to heap$"
- f = t.M // ERROR "foo141 t.M does not escape$"
+ f = t.M // ERROR "t.M does not escape$"
_ = f
}
@@ -1401,7 +1401,7 @@ func foo142() {
// issue 3888.
func foo143() {
for i := 0; i < 1000; i++ {
- func() { // ERROR "foo143 func literal does not escape$"
+ func() { // ERROR "func literal does not escape$"
for i := 0; i < 1; i++ {
var t Tm
t.M()
@@ -1435,20 +1435,20 @@ type List struct {
Next *List
}
-func foo145(l List) { // ERROR "foo145 l does not escape$"
+func foo145(l List) { // ERROR "l does not escape$"
var p *List
for p = &l; p.Next != nil; p = p.Next {
}
}
-func foo146(l List) { // ERROR "foo146 l does not escape$"
+func foo146(l List) { // ERROR "l does not escape$"
var p *List
p = &l
for ; p.Next != nil; p = p.Next {
}
}
-func foo147(l List) { // ERROR "foo147 l does not escape$"
+func foo147(l List) { // ERROR "l does not escape$"
var p *List
p = &l
for p.Next != nil {
@@ -1456,14 +1456,14 @@ func foo147(l List) { // ERROR "foo147 l does not escape$"
}
}
-func foo148(l List) { // ERROR "foo148 l does not escape$"
+func foo148(l List) { // ERROR "l does not escape$"
for p := &l; p.Next != nil; p = p.Next {
}
}
// related: address of variable should have depth of variable, not of loop
-func foo149(l List) { // ERROR "foo149 l does not escape$"
+func foo149(l List) { // ERROR "l does not escape$"
var p *List
for {
for p = &l; p.Next != nil; p = p.Next {
@@ -1542,7 +1542,7 @@ func foo152() {
// issue 8176 - &x in type switch body not marked as escaping
-func foo153(v interface{}) *int { // ERROR "foo153 v does not escape"
+func foo153(v interface{}) *int { // ERROR "v does not escape"
switch x := v.(type) {
case int: // ERROR "moved to heap: x$"
return &x
@@ -1571,14 +1571,14 @@ type Lit struct {
func ptrlitNoescape() {
// Both literal and element do not escape.
i := 0
- x := &Lit{&i} // ERROR "ptrlitNoescape &Lit literal does not escape$"
+ x := &Lit{&i} // ERROR "&Lit literal does not escape$"
_ = x
}
func ptrlitNoEscape2() {
// Literal does not escape, but element does.
i := 0 // ERROR "moved to heap: i$"
- x := &Lit{&i} // ERROR "ptrlitNoEscape2 &Lit literal does not escape$"
+ x := &Lit{&i} // ERROR "&Lit literal does not escape$"
sink = *x
}
@@ -1600,7 +1600,7 @@ type Buffer struct {
str2 string
}
-func (b *Buffer) foo() { // ERROR "\(\*Buffer\).foo b does not escape$"
+func (b *Buffer) foo() { // ERROR "b does not escape$"
b.buf1 = b.buf1[1:2] // ERROR "\(\*Buffer\).foo ignoring self-assignment in b.buf1 = b.buf1\[1:2\]$"
b.buf1 = b.buf1[1:2:3] // ERROR "\(\*Buffer\).foo ignoring self-assignment in b.buf1 = b.buf1\[1:2:3\]$"
b.buf1 = b.buf2[1:2] // ERROR "\(\*Buffer\).foo ignoring self-assignment in b.buf1 = b.buf2\[1:2\]$"
@@ -1611,12 +1611,12 @@ func (b *Buffer) bar() { // ERROR "leaking param: b$"
b.buf1 = b.arr[1:2]
}
-func (b *Buffer) arrayPtr() { // ERROR "\(\*Buffer\).arrayPtr b does not escape"
+func (b *Buffer) arrayPtr() { // ERROR "b does not escape"
b.buf1 = b.arrPtr[1:2] // ERROR "\(\*Buffer\).arrayPtr ignoring self-assignment in b.buf1 = b.arrPtr\[1:2\]$"
b.buf1 = b.arrPtr[1:2:3] // ERROR "\(\*Buffer\).arrayPtr ignoring self-assignment in b.buf1 = b.arrPtr\[1:2:3\]$"
}
-func (b *Buffer) baz() { // ERROR "\(\*Buffer\).baz b does not escape$"
+func (b *Buffer) baz() { // ERROR "b does not escape$"
b.str1 = b.str1[1:2] // ERROR "\(\*Buffer\).baz ignoring self-assignment in b.str1 = b.str1\[1:2\]$"
b.str1 = b.str2[1:2] // ERROR "\(\*Buffer\).baz ignoring self-assignment in b.str1 = b.str2\[1:2\]$"
}
@@ -1627,7 +1627,7 @@ func (b *Buffer) bat() { // ERROR "leaking param content: b$"
sink = o
}
-func quux(sp *string, bp *[]byte) { // ERROR "quux bp does not escape$" "quux sp does not escape$"
+func quux(sp *string, bp *[]byte) { // ERROR "bp does not escape$" "sp does not escape$"
*sp = (*sp)[1:2] // ERROR "quux ignoring self-assignment in \*sp = \(\*sp\)\[1:2\]$"
*bp = (*bp)[1:2] // ERROR "quux ignoring self-assignment in \*bp = \(\*bp\)\[1:2\]$"
}
@@ -1650,27 +1650,27 @@ func fieldFlowTracking() {
// String operations.
func slicebytetostring0() {
- b := make([]byte, 20) // ERROR "slicebytetostring0 make\(\[\]byte, 20\) does not escape$"
- s := string(b) // ERROR "slicebytetostring0 string\(b\) does not escape$"
+ b := make([]byte, 20) // ERROR "make\(\[\]byte, 20\) does not escape$"
+ s := string(b) // ERROR "string\(b\) does not escape$"
_ = s
}
func slicebytetostring1() {
- b := make([]byte, 20) // ERROR "slicebytetostring1 make\(\[\]byte, 20\) does not escape$"
- s := string(b) // ERROR "slicebytetostring1 string\(b\) does not escape$"
+ b := make([]byte, 20) // ERROR "make\(\[\]byte, 20\) does not escape$"
+ s := string(b) // ERROR "string\(b\) does not escape$"
s1 := s[0:1]
_ = s1
}
func slicebytetostring2() {
- b := make([]byte, 20) // ERROR "slicebytetostring2 make\(\[\]byte, 20\) does not escape$"
+ b := make([]byte, 20) // ERROR "make\(\[\]byte, 20\) does not escape$"
s := string(b) // ERROR "string\(b\) escapes to heap$"
s1 := s[0:1] // ERROR "moved to heap: s1$"
sink = &s1
}
func slicebytetostring3() {
- b := make([]byte, 20) // ERROR "slicebytetostring3 make\(\[\]byte, 20\) does not escape$"
+ b := make([]byte, 20) // ERROR "make\(\[\]byte, 20\) does not escape$"
s := string(b) // ERROR "string\(b\) escapes to heap$"
s1 := s[0:1]
sink = s1 // ERROR "s1 escapes to heap$"
@@ -1679,7 +1679,7 @@ func slicebytetostring3() {
func addstr0() {
s0 := "a"
s1 := "b"
- s := s0 + s1 // ERROR "addstr0 s0 \+ s1 does not escape$"
+ s := s0 + s1 // ERROR "s0 \+ s1 does not escape$"
_ = s
}
@@ -1687,14 +1687,14 @@ func addstr1() {
s0 := "a"
s1 := "b"
s := "c"
- s += s0 + s1 // ERROR "addstr1 s0 \+ s1 does not escape$"
+ s += s0 + s1 // ERROR "s0 \+ s1 does not escape$"
_ = s
}
func addstr2() {
- b := make([]byte, 20) // ERROR "addstr2 make\(\[\]byte, 20\) does not escape$"
+ b := make([]byte, 20) // ERROR "make\(\[\]byte, 20\) does not escape$"
s0 := "a"
- s := string(b) + s0 // ERROR "addstr2 string\(b\) \+ s0 does not escape$" "addstr2 string\(b\) does not escape$"
+ s := string(b) + s0 // ERROR "string\(b\) \+ s0 does not escape$" "string\(b\) does not escape$"
_ = s
}
@@ -1709,7 +1709,7 @@ func addstr3() {
func intstring0() bool {
// string does not escape
x := '0'
- s := string(x) // ERROR "intstring0 string\(x\) does not escape$"
+ s := string(x) // ERROR "string\(x\) does not escape$"
return s == "0"
}
@@ -1729,7 +1729,7 @@ func intstring2() {
func stringtoslicebyte0() {
s := "foo"
- x := []byte(s) // ERROR "stringtoslicebyte0 \(\[\]byte\)\(s\) does not escape$"
+ x := []byte(s) // ERROR "\(\[\]byte\)\(s\) does not escape$"
_ = x
}
@@ -1745,7 +1745,7 @@ func stringtoslicebyte2() {
func stringtoslicerune0() {
s := "foo"
- x := []rune(s) // ERROR "stringtoslicerune0 \(\[\]rune\)\(s\) does not escape$"
+ x := []rune(s) // ERROR "\(\[\]rune\)\(s\) does not escape$"
_ = x
}
@@ -1760,23 +1760,23 @@ func stringtoslicerune2() {
}
func slicerunetostring0() {
- r := []rune{1, 2, 3} // ERROR "slicerunetostring0 \[\]rune literal does not escape$"
- s := string(r) // ERROR "slicerunetostring0 string\(r\) does not escape$"
+ r := []rune{1, 2, 3} // ERROR "\[\]rune literal does not escape$"
+ s := string(r) // ERROR "string\(r\) does not escape$"
_ = s
}
func slicerunetostring1() string {
- r := []rune{1, 2, 3} // ERROR "slicerunetostring1 \[\]rune literal does not escape$"
+ r := []rune{1, 2, 3} // ERROR "\[\]rune literal does not escape$"
return string(r) // ERROR "string\(r\) escapes to heap$"
}
func slicerunetostring2() {
- r := []rune{1, 2, 3} // ERROR "slicerunetostring2 \[\]rune literal does not escape$"
+ r := []rune{1, 2, 3} // ERROR "\[\]rune literal does not escape$"
sink = string(r) // ERROR "string\(r\) escapes to heap$"
}
func makemap0() {
- m := make(map[int]int) // ERROR "makemap0 make\(map\[int\]int\) does not escape$"
+ m := make(map[int]int) // ERROR "make\(map\[int\]int\) does not escape$"
m[0] = 0
m[1]++
delete(m, 1)
@@ -1792,12 +1792,12 @@ func makemap2() {
sink = m
}
-func nonescapingEface(m map[interface{}]bool) bool { // ERROR "nonescapingEface m does not escape$"
- return m["foo"] // ERROR "nonescapingEface .foo. does not escape$"
+func nonescapingEface(m map[interface{}]bool) bool { // ERROR "m does not escape$"
+ return m["foo"] // ERROR ".foo. does not escape$"
}
-func nonescapingIface(m map[M]bool) bool { // ERROR "nonescapingIface m does not escape$"
- return m[MV(0)] // ERROR "nonescapingIface MV\(0\) does not escape$"
+func nonescapingIface(m map[M]bool) bool { // ERROR "m does not escape$"
+ return m[MV(0)] // ERROR "MV\(0\) does not escape$"
}
func issue10353() {
diff --git a/test/escape2n.go b/test/escape2n.go
index 936f0d8af6..42312fe41d 100644
--- a/test/escape2n.go
+++ b/test/escape2n.go
@@ -37,24 +37,24 @@ func foo3b(t T) { // ERROR "leaking param: t$"
}
// xx isn't going anywhere, so use of yy is ok
-func foo4(xx, yy *int) { // ERROR "foo4 xx does not escape$" "foo4 yy does not escape$"
+func foo4(xx, yy *int) { // ERROR "xx does not escape$" "yy does not escape$"
xx = yy
}
// xx isn't going anywhere, so taking address of yy is ok
-func foo5(xx **int, yy *int) { // ERROR "foo5 xx does not escape$" "foo5 yy does not escape$"
+func foo5(xx **int, yy *int) { // ERROR "xx does not escape$" "yy does not escape$"
xx = &yy
}
-func foo6(xx **int, yy *int) { // ERROR "foo6 xx does not escape$" "leaking param: yy$"
+func foo6(xx **int, yy *int) { // ERROR "xx does not escape$" "leaking param: yy$"
*xx = yy
}
-func foo7(xx **int, yy *int) { // ERROR "foo7 xx does not escape$" "foo7 yy does not escape$"
+func foo7(xx **int, yy *int) { // ERROR "xx does not escape$" "yy does not escape$"
**xx = *yy
}
-func foo8(xx, yy *int) int { // ERROR "foo8 xx does not escape$" "foo8 yy does not escape$"
+func foo8(xx, yy *int) int { // ERROR "xx does not escape$" "yy does not escape$"
xx = yy
return *xx
}
@@ -64,7 +64,7 @@ func foo9(xx, yy *int) *int { // ERROR "leaking param: xx to result ~r2 level=0$
return xx
}
-func foo10(xx, yy *int) { // ERROR "foo10 xx does not escape$" "foo10 yy does not escape$"
+func foo10(xx, yy *int) { // ERROR "xx does not escape$" "yy does not escape$"
*xx = *yy
}
@@ -88,7 +88,7 @@ func foo13(yyy **int) { // ERROR "leaking param content: yyy$"
*xxx = *yyy
}
-func foo14(yyy **int) { // ERROR "foo14 yyy does not escape$"
+func foo14(yyy **int) { // ERROR "yyy does not escape$"
**xxx = **yyy
}
@@ -100,7 +100,7 @@ func foo16(yy *int) { // ERROR "leaking param: yy$"
*xxx = yy
}
-func foo17(yy *int) { // ERROR "foo17 yy does not escape$"
+func foo17(yy *int) { // ERROR "yy does not escape$"
**xxx = *yy
}
@@ -125,11 +125,11 @@ func NewBarp(x *int) *Bar { // ERROR "leaking param: x$"
return &Bar{42, x} // ERROR "&Bar literal escapes to heap$"
}
-func NewBarp2(x *int) *Bar { // ERROR "NewBarp2 x does not escape$"
+func NewBarp2(x *int) *Bar { // ERROR "x does not escape$"
return &Bar{*x, nil} // ERROR "&Bar literal escapes to heap$"
}
-func (b *Bar) NoLeak() int { // ERROR "\(\*Bar\).NoLeak b does not escape$"
+func (b *Bar) NoLeak() int { // ERROR "b does not escape$"
return *(b.ii)
}
@@ -157,7 +157,7 @@ func (b *Bar) LeaksABit() *int { // ERROR "leaking param: b to result ~r0 level=
return b.ii
}
-func (b Bar) StillNoLeak() int { // ERROR "Bar.StillNoLeak b does not escape$"
+func (b Bar) StillNoLeak() int { // ERROR "b does not escape$"
v := 0
b.ii = &v
return b.i
@@ -176,7 +176,7 @@ func NewBar2() *Bar2 {
return &Bar2{[12]int{42}, nil} // ERROR "&Bar2 literal escapes to heap$"
}
-func (b *Bar2) NoLeak() int { // ERROR "\(\*Bar2\).NoLeak b does not escape$"
+func (b *Bar2) NoLeak() int { // ERROR "b does not escape$"
return b.i[0]
}
@@ -188,7 +188,7 @@ func (b *Bar2) AlsoNoLeak() []int { // ERROR "leaking param: b to result ~r0 lev
return b.ii[0:1]
}
-func (b Bar2) AgainNoLeak() [12]int { // ERROR "Bar2.AgainNoLeak b does not escape$"
+func (b Bar2) AgainNoLeak() [12]int { // ERROR "b does not escape$"
return b.i
}
@@ -219,7 +219,7 @@ func foo21a() func() int {
func foo22() int {
x := 42
- return func() int { // ERROR "foo22 func literal does not escape$"
+ return func() int { // ERROR "func literal does not escape$"
return x
}()
}
@@ -250,7 +250,7 @@ func foo23c(x int) func() int { // ERROR "moved to heap: x$"
}
func foo24(x int) int {
- return func() int { // ERROR "foo24 func literal does not escape$"
+ return func() int { // ERROR "func literal does not escape$"
return x
}()
}
@@ -262,7 +262,7 @@ func fooleak(xx *int) int { // ERROR "leaking param: xx$"
return *x
}
-func foonoleak(xx *int) int { // ERROR "foonoleak xx does not escape$"
+func foonoleak(xx *int) int { // ERROR "xx does not escape$"
return *x + *xx
}
@@ -286,7 +286,7 @@ func (f *Foo) fooleak() { // ERROR "leaking param: f$"
pf = f
}
-func (f *Foo) foonoleak() { // ERROR "\(\*Foo\).foonoleak f does not escape$"
+func (f *Foo) foonoleak() { // ERROR "f does not escape$"
F.x = f.x
}
@@ -294,7 +294,7 @@ func (f *Foo) Leak() { // ERROR "leaking param: f$"
f.fooleak()
}
-func (f *Foo) NoLeak() { // ERROR "\(\*Foo\).NoLeak f does not escape$"
+func (f *Foo) NoLeak() { // ERROR "f does not escape$"
f.foonoleak()
}
@@ -302,11 +302,11 @@ func foo41(x int) { // ERROR "moved to heap: x$"
F.xx = &x
}
-func (f *Foo) foo42(x int) { // ERROR "\(\*Foo\).foo42 f does not escape$" "moved to heap: x$"
+func (f *Foo) foo42(x int) { // ERROR "f does not escape$" "moved to heap: x$"
f.xx = &x
}
-func foo43(f *Foo, x int) { // ERROR "foo43 f does not escape$" "moved to heap: x$"
+func foo43(f *Foo, x int) { // ERROR "f does not escape$" "moved to heap: x$"
f.xx = &x
}
@@ -314,7 +314,7 @@ func foo44(yy *int) { // ERROR "leaking param: yy$"
F.xx = yy
}
-func (f *Foo) foo45() { // ERROR "\(\*Foo\).foo45 f does not escape$"
+func (f *Foo) foo45() { // ERROR "f does not escape$"
F.x = f.x
}
@@ -407,7 +407,7 @@ func foo60(i *int) *int { // ERROR "leaking param: i to result ~r1 level=0$"
return a[1]
}
-func foo60a(i *int) *int { // ERROR "foo60a i does not escape$"
+func foo60a(i *int) *int { // ERROR "i does not escape$"
var a [12]*int
a[0] = i
return nil
@@ -423,7 +423,7 @@ func foo61(i *int) *int { // ERROR "leaking param: i to result ~r1 level=0$"
return s.b
}
-func foo61a(i *int) *int { // ERROR "foo61a i does not escape$"
+func foo61a(i *int) *int { // ERROR "i does not escape$"
type S struct {
a, b *int
}
@@ -439,7 +439,7 @@ func foo62(i *int) *int { // ERROR "leaking param: i$"
type S struct {
a, b *int
}
- s := new(S) // ERROR "foo62 new\(S\) does not escape$"
+ s := new(S) // ERROR "new\(S\) does not escape$"
s.a = i
return nil // s.b
}
@@ -448,7 +448,7 @@ type M interface {
M()
}
-func foo63(m M) { // ERROR "foo63 m does not escape$"
+func foo63(m M) { // ERROR "m does not escape$"
}
func foo64(m M) { // ERROR "leaking param: m$"
@@ -475,7 +475,7 @@ func foo66() {
func foo67() {
var mv MV
- foo63(mv) // ERROR "foo67 mv does not escape$"
+ foo63(mv) // ERROR "mv does not escape$"
}
func foo68() {
@@ -539,7 +539,7 @@ func foo72b() [10]*int {
// issue 2145
func foo73() {
- s := []int{3, 2, 1} // ERROR "foo73 \[\]int literal does not escape$"
+ s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$"
for _, v := range s {
vv := v
// actually just escapes its scope
@@ -550,7 +550,7 @@ func foo73() {
}
func foo731() {
- s := []int{3, 2, 1} // ERROR "foo731 \[\]int literal does not escape$"
+ s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$"
for _, v := range s {
vv := v // ERROR "moved to heap: vv$"
// actually just escapes its scope
@@ -562,7 +562,7 @@ func foo731() {
}
func foo74() {
- s := []int{3, 2, 1} // ERROR "foo74 \[\]int literal does not escape$"
+ s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$"
for _, v := range s {
vv := v
// actually just escapes its scope
@@ -574,7 +574,7 @@ func foo74() {
}
func foo74a() {
- s := []int{3, 2, 1} // ERROR "foo74a \[\]int literal does not escape$"
+ s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$"
for _, v := range s {
vv := v // ERROR "moved to heap: vv$"
// actually just escapes its scope
@@ -589,7 +589,7 @@ func foo74a() {
// issue 3975
func foo74b() {
var array [3]func()
- s := []int{3, 2, 1} // ERROR "foo74b \[\]int literal does not escape$"
+ s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$"
for i, v := range s {
vv := v
// actually just escapes its scope
@@ -601,7 +601,7 @@ func foo74b() {
func foo74c() {
var array [3]func()
- s := []int{3, 2, 1} // ERROR "foo74c \[\]int literal does not escape$"
+ s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape$"
for i, v := range s {
vv := v // ERROR "moved to heap: vv$"
// actually just escapes its scope
@@ -611,57 +611,57 @@ func foo74c() {
}
}
-func myprint(y *int, x ...interface{}) *int { // ERROR "leaking param: y to result ~r2 level=0$" "myprint x does not escape$"
+func myprint(y *int, x ...interface{}) *int { // ERROR "leaking param: y to result ~r2 level=0$" "x does not escape$"
return y
}
-func myprint1(y *int, x ...interface{}) *interface{} { // ERROR "leaking param: x to result ~r2 level=0$" "myprint1 y does not escape$"
+func myprint1(y *int, x ...interface{}) *interface{} { // ERROR "leaking param: x to result ~r2 level=0$" "y does not escape$"
return &x[0]
}
-func foo75(z *int) { // ERROR "foo75 z does not escape$"
- myprint(z, 1, 2, 3) // ERROR "1 does not escape" "2 does not escape" "3 does not escape" "foo75 ... argument does not escape$"
+func foo75(z *int) { // ERROR "z does not escape$"
+ myprint(z, 1, 2, 3) // ERROR "1 does not escape" "2 does not escape" "3 does not escape" "... argument does not escape$"
}
-func foo75a(z *int) { // ERROR "foo75a z does not escape$"
- myprint1(z, 1, 2, 3) // ERROR "1 does not escape" "2 does not escape" "3 does not escape" "foo75a ... argument does not escape$"
+func foo75a(z *int) { // ERROR "z does not escape$"
+ myprint1(z, 1, 2, 3) // ERROR "1 does not escape" "2 does not escape" "3 does not escape" "... argument does not escape$"
}
func foo75esc(z *int) { // ERROR "leaking param: z$"
- gxx = myprint(z, 1, 2, 3) // ERROR "1 does not escape" "2 does not escape" "3 does not escape" "foo75esc ... argument does not escape$"
+ gxx = myprint(z, 1, 2, 3) // ERROR "1 does not escape" "2 does not escape" "3 does not escape" "... argument does not escape$"
}
-func foo75aesc(z *int) { // ERROR "foo75aesc z does not escape$"
+func foo75aesc(z *int) { // ERROR "z does not escape$"
var ppi **interface{} // assignments to pointer dereferences lose track
*ppi = myprint1(z, 1, 2, 3) // ERROR "... argument escapes to heap$" "1 escapes to heap$" "2 escapes to heap$" "3 escapes to heap$"
}
-func foo75aesc1(z *int) { // ERROR "foo75aesc1 z does not escape$"
+func foo75aesc1(z *int) { // ERROR "z does not escape$"
sink = myprint1(z, 1, 2, 3) // ERROR "... argument escapes to heap$" "1 escapes to heap$" "2 escapes to heap$" "3 escapes to heap$"
}
func foo76(z *int) { // ERROR "z does not escape"
- myprint(nil, z) // ERROR "foo76 ... argument does not escape$"
+ myprint(nil, z) // ERROR "... argument does not escape$"
}
func foo76a(z *int) { // ERROR "z does not escape"
- myprint1(nil, z) // ERROR "foo76a ... argument does not escape$"
+ myprint1(nil, z) // ERROR "... argument does not escape$"
}
func foo76b() {
- myprint(nil, 1, 2, 3) // ERROR "1 does not escape" "2 does not escape" "3 does not escape" "foo76b ... argument does not escape$"
+ myprint(nil, 1, 2, 3) // ERROR "1 does not escape" "2 does not escape" "3 does not escape" "... argument does not escape$"
}
func foo76c() {
- myprint1(nil, 1, 2, 3) // ERROR "1 does not escape" "2 does not escape" "3 does not escape" "foo76c ... argument does not escape$"
+ myprint1(nil, 1, 2, 3) // ERROR "1 does not escape" "2 does not escape" "3 does not escape" "... argument does not escape$"
}
func foo76d() {
- defer myprint(nil, 1, 2, 3) // ERROR "1 does not escape" "2 does not escape" "3 does not escape" "foo76d ... argument does not escape$"
+ defer myprint(nil, 1, 2, 3) // ERROR "1 does not escape" "2 does not escape" "3 does not escape" "... argument does not escape$"
}
func foo76e() {
- defer myprint1(nil, 1, 2, 3) // ERROR "1 does not escape" "2 does not escape" "3 does not escape" "foo76e ... argument does not escape$"
+ defer myprint1(nil, 1, 2, 3) // ERROR "1 does not escape" "2 does not escape" "3 does not escape" "... argument does not escape$"
}
func foo76f() {
@@ -677,11 +677,11 @@ func foo76g() {
}
}
-func foo77(z []interface{}) { // ERROR "foo77 z does not escape$"
+func foo77(z []interface{}) { // ERROR "z does not escape$"
myprint(nil, z...) // z does not escape
}
-func foo77a(z []interface{}) { // ERROR "foo77a z does not escape$"
+func foo77a(z []interface{}) { // ERROR "z does not escape$"
myprint1(nil, z...)
}
@@ -696,10 +696,10 @@ func foo77c(z []interface{}) { // ERROR "leaking param: z$"
func dotdotdot() {
i := 0
- myprint(nil, &i) // ERROR "dotdotdot ... argument does not escape$"
+ myprint(nil, &i) // ERROR "... argument does not escape$"
j := 0
- myprint1(nil, &j) // ERROR "dotdotdot ... argument does not escape$"
+ myprint1(nil, &j) // ERROR "... argument does not escape$"
}
func foo78(z int) *int { // ERROR "moved to heap: z$"
@@ -728,7 +728,7 @@ func foo80() *int {
func foo81() *int {
for {
- z := new(int) // ERROR "foo81 new\(int\) does not escape$"
+ z := new(int) // ERROR "new\(int\) does not escape$"
_ = z
}
return nil
@@ -736,7 +736,7 @@ func foo81() *int {
func tee(p *int) (x, y *int) { return p, p } // ERROR "leaking param: p to result x level=0$" "leaking param: p to result y level=0$"
-func noop(x, y *int) {} // ERROR "noop x does not escape$" "noop y does not escape$"
+func noop(x, y *int) {} // ERROR "x does not escape$" "y does not escape$"
func foo82() {
var x, y, z int // ERROR "moved to heap: x$" "moved to heap: y$" "moved to heap: z$"
@@ -775,7 +775,7 @@ func foo92(x *int) [2]*int { // ERROR "leaking param: x to result ~r1 level=0$"
}
// does not leak c
-func foo93(c chan *int) *int { // ERROR "foo93 c does not escape$"
+func foo93(c chan *int) *int { // ERROR "c does not escape$"
for v := range c {
return v
}
@@ -794,7 +794,7 @@ func foo94(m map[*int]*int, b bool) *int { // ERROR "leaking param: m to result
}
// does leak x
-func foo95(m map[*int]*int, x *int) { // ERROR "foo95 m does not escape$" "leaking param: x$"
+func foo95(m map[*int]*int, x *int) { // ERROR "m does not escape$" "leaking param: x$"
m[x] = x
}
@@ -809,7 +809,7 @@ func foo97(m [1]*int) *int { // ERROR "leaking param: m to result ~r1 level=0$"
}
// does not leak m
-func foo98(m map[int]*int) *int { // ERROR "foo98 m does not escape$"
+func foo98(m map[int]*int) *int { // ERROR "m does not escape$"
return m[0]
}
@@ -835,7 +835,7 @@ func foo101(m [1]*int) *int { // ERROR "leaking param: m to result ~r1 level=0$"
}
// does not leak m
-func foo101a(m [1]*int) *int { // ERROR "foo101a m does not escape$"
+func foo101a(m [1]*int) *int { // ERROR "m does not escape$"
for i := range m { // ERROR "moved to heap: i$"
return &i
}
@@ -843,12 +843,12 @@ func foo101a(m [1]*int) *int { // ERROR "foo101a m does not escape$"
}
// does leak x
-func foo102(m []*int, x *int) { // ERROR "foo102 m does not escape$" "leaking param: x$"
+func foo102(m []*int, x *int) { // ERROR "m does not escape$" "leaking param: x$"
m[0] = x
}
// does not leak x
-func foo103(m [1]*int, x *int) { // ERROR "foo103 m does not escape$" "foo103 x does not escape$"
+func foo103(m [1]*int, x *int) { // ERROR "m does not escape$" "x does not escape$"
m[0] = x
}
@@ -878,7 +878,7 @@ func foo108(x *int) map[*int]*int { // ERROR "leaking param: x$"
}
func foo109(x *int) *int { // ERROR "leaking param: x$"
- m := map[*int]*int{x: nil} // ERROR "foo109 map\[\*int\]\*int literal does not escape$"
+ m := map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int literal does not escape$"
for k, _ := range m {
return k
}
@@ -886,12 +886,12 @@ func foo109(x *int) *int { // ERROR "leaking param: x$"
}
func foo110(x *int) *int { // ERROR "leaking param: x$"
- m := map[*int]*int{nil: x} // ERROR "foo110 map\[\*int\]\*int literal does not escape$"
+ m := map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int literal does not escape$"
return m[nil]
}
func foo111(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0"
- m := []*int{x} // ERROR "foo111 \[\]\*int literal does not escape$"
+ m := []*int{x} // ERROR "\[\]\*int literal does not escape$"
return m[0]
}
@@ -906,7 +906,7 @@ func foo113(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0$"
}
func foo114(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0$"
- m := &Bar{ii: x} // ERROR "foo114 &Bar literal does not escape$"
+ m := &Bar{ii: x} // ERROR "&Bar literal does not escape$"
return m.ii
}
@@ -925,12 +925,12 @@ func foo116(b bool) *int {
return nil
}
-func foo117(unknown func(interface{})) { // ERROR "foo117 unknown does not escape$"
+func foo117(unknown func(interface{})) { // ERROR "unknown does not escape$"
x := 1 // ERROR "moved to heap: x$"
unknown(&x)
}
-func foo118(unknown func(*int)) { // ERROR "foo118 unknown does not escape$"
+func foo118(unknown func(*int)) { // ERROR "unknown does not escape$"
x := 1 // ERROR "moved to heap: x$"
unknown(&x)
}
@@ -1167,7 +1167,7 @@ func foo122() {
goto L1
L1:
- i = new(int) // ERROR "foo122 new\(int\) does not escape$"
+ i = new(int) // ERROR "new\(int\) does not escape$"
_ = i
}
@@ -1182,18 +1182,18 @@ L1:
_ = i
}
-func foo124(x **int) { // ERROR "foo124 x does not escape$"
+func foo124(x **int) { // ERROR "x does not escape$"
var i int // ERROR "moved to heap: i$"
p := &i
- func() { // ERROR "foo124 func literal does not escape$"
+ func() { // ERROR "func literal does not escape$"
*x = p
}()
}
-func foo125(ch chan *int) { // ERROR "foo125 ch does not escape$"
+func foo125(ch chan *int) { // ERROR "ch does not escape$"
var i int // ERROR "moved to heap: i$"
p := &i
- func() { // ERROR "foo125 func literal does not escape$"
+ func() { // ERROR "func literal does not escape$"
ch <- p
}()
}
@@ -1203,7 +1203,7 @@ func foo126() {
for {
// loopdepth 1
var i int // ERROR "moved to heap: i$"
- func() { // ERROR "foo126 func literal does not escape$"
+ func() { // ERROR "func literal does not escape$"
px = &i
}()
}
@@ -1229,9 +1229,9 @@ func foo128() {
func foo129() {
var i int // ERROR "moved to heap: i$"
p := &i
- func() { // ERROR "foo129 func literal does not escape$"
+ func() { // ERROR "func literal does not escape$"
q := p
- func() { // ERROR "foo129.func1 func literal does not escape$"
+ func() { // ERROR "func literal does not escape$"
r := q
px = r
}()
@@ -1241,7 +1241,7 @@ func foo129() {
func foo130() {
for {
var i int // ERROR "moved to heap: i$"
- func() { // ERROR "foo130 func literal does not escape$"
+ func() { // ERROR "func literal does not escape$"
px = &i
}()
}
@@ -1249,7 +1249,7 @@ func foo130() {
func foo131() {
var i int // ERROR "moved to heap: i$"
- func() { // ERROR "foo131 func literal does not escape$"
+ func() { // ERROR "func literal does not escape$"
px = &i
}()
}
@@ -1263,7 +1263,7 @@ func foo132() {
func foo133() {
var i int // ERROR "moved to heap: i$"
- defer func() { // ERROR "foo133 func literal does not escape$"
+ defer func() { // ERROR "func literal does not escape$"
px = &i
}()
}
@@ -1271,9 +1271,9 @@ func foo133() {
func foo134() {
var i int
p := &i
- func() { // ERROR "foo134 func literal does not escape$"
+ func() { // ERROR "func literal does not escape$"
q := p
- func() { // ERROR "foo134.func1 func literal does not escape$"
+ func() { // ERROR "func literal does not escape$"
r := q
_ = r
}()
@@ -1285,7 +1285,7 @@ func foo135() {
p := &i
go func() { // ERROR "func literal escapes to heap$"
q := p
- func() { // ERROR "foo135.func1 func literal does not escape$"
+ func() { // ERROR "func literal does not escape$"
r := q
_ = r
}()
@@ -1297,7 +1297,7 @@ func foo136() {
p := &i
go func() { // ERROR "func literal escapes to heap$"
q := p
- func() { // ERROR "foo136.func1 func literal does not escape$"
+ func() { // ERROR "func literal does not escape$"
r := q
px = r
}()
@@ -1307,7 +1307,7 @@ func foo136() {
func foo137() {
var i int // ERROR "moved to heap: i$"
p := &i
- func() { // ERROR "foo137 func literal does not escape$"
+ func() { // ERROR "func literal does not escape$"
q := p
go func() { // ERROR "func literal escapes to heap$"
r := q
@@ -1358,7 +1358,7 @@ func F2([]byte)
//go:noescape
-func F3(x []byte) // ERROR "F3 x does not escape$"
+func F3(x []byte) // ERROR "x does not escape$"
func F4(x []byte) // ERROR "leaking param: x$"
@@ -1380,14 +1380,14 @@ type Tm struct {
x int
}
-func (t *Tm) M() { // ERROR "\(\*Tm\).M t does not escape$"
+func (t *Tm) M() { // ERROR "t does not escape$"
}
func foo141() {
var f func()
t := new(Tm) // ERROR "new\(Tm\) escapes to heap$"
- f = t.M // ERROR "foo141 t.M does not escape$"
+ f = t.M // ERROR "t.M does not escape$"
_ = f
}
@@ -1401,7 +1401,7 @@ func foo142() {
// issue 3888.
func foo143() {
for i := 0; i < 1000; i++ {
- func() { // ERROR "foo143 func literal does not escape$"
+ func() { // ERROR "func literal does not escape$"
for i := 0; i < 1; i++ {
var t Tm
t.M()
@@ -1435,20 +1435,20 @@ type List struct {
Next *List
}
-func foo145(l List) { // ERROR "foo145 l does not escape$"
+func foo145(l List) { // ERROR "l does not escape$"
var p *List
for p = &l; p.Next != nil; p = p.Next {
}
}
-func foo146(l List) { // ERROR "foo146 l does not escape$"
+func foo146(l List) { // ERROR "l does not escape$"
var p *List
p = &l
for ; p.Next != nil; p = p.Next {
}
}
-func foo147(l List) { // ERROR "foo147 l does not escape$"
+func foo147(l List) { // ERROR "l does not escape$"
var p *List
p = &l
for p.Next != nil {
@@ -1456,14 +1456,14 @@ func foo147(l List) { // ERROR "foo147 l does not escape$"
}
}
-func foo148(l List) { // ERROR "foo148 l does not escape$"
+func foo148(l List) { // ERROR "l does not escape$"
for p := &l; p.Next != nil; p = p.Next {
}
}
// related: address of variable should have depth of variable, not of loop
-func foo149(l List) { // ERROR "foo149 l does not escape$"
+func foo149(l List) { // ERROR "l does not escape$"
var p *List
for {
for p = &l; p.Next != nil; p = p.Next {
@@ -1542,7 +1542,7 @@ func foo152() {
// issue 8176 - &x in type switch body not marked as escaping
-func foo153(v interface{}) *int { // ERROR "foo153 v does not escape"
+func foo153(v interface{}) *int { // ERROR "v does not escape"
switch x := v.(type) {
case int: // ERROR "moved to heap: x$"
return &x
@@ -1571,14 +1571,14 @@ type Lit struct {
func ptrlitNoescape() {
// Both literal and element do not escape.
i := 0
- x := &Lit{&i} // ERROR "ptrlitNoescape &Lit literal does not escape$"
+ x := &Lit{&i} // ERROR "&Lit literal does not escape$"
_ = x
}
func ptrlitNoEscape2() {
// Literal does not escape, but element does.
i := 0 // ERROR "moved to heap: i$"
- x := &Lit{&i} // ERROR "ptrlitNoEscape2 &Lit literal does not escape$"
+ x := &Lit{&i} // ERROR "&Lit literal does not escape$"
sink = *x
}
@@ -1600,7 +1600,7 @@ type Buffer struct {
str2 string
}
-func (b *Buffer) foo() { // ERROR "\(\*Buffer\).foo b does not escape$"
+func (b *Buffer) foo() { // ERROR "b does not escape$"
b.buf1 = b.buf1[1:2] // ERROR "\(\*Buffer\).foo ignoring self-assignment in b.buf1 = b.buf1\[1:2\]$"
b.buf1 = b.buf1[1:2:3] // ERROR "\(\*Buffer\).foo ignoring self-assignment in b.buf1 = b.buf1\[1:2:3\]$"
b.buf1 = b.buf2[1:2] // ERROR "\(\*Buffer\).foo ignoring self-assignment in b.buf1 = b.buf2\[1:2\]$"
@@ -1611,12 +1611,12 @@ func (b *Buffer) bar() { // ERROR "leaking param: b$"
b.buf1 = b.arr[1:2]
}
-func (b *Buffer) arrayPtr() { // ERROR "\(\*Buffer\).arrayPtr b does not escape"
+func (b *Buffer) arrayPtr() { // ERROR "b does not escape"
b.buf1 = b.arrPtr[1:2] // ERROR "\(\*Buffer\).arrayPtr ignoring self-assignment in b.buf1 = b.arrPtr\[1:2\]$"
b.buf1 = b.arrPtr[1:2:3] // ERROR "\(\*Buffer\).arrayPtr ignoring self-assignment in b.buf1 = b.arrPtr\[1:2:3\]$"
}
-func (b *Buffer) baz() { // ERROR "\(\*Buffer\).baz b does not escape$"
+func (b *Buffer) baz() { // ERROR "b does not escape$"
b.str1 = b.str1[1:2] // ERROR "\(\*Buffer\).baz ignoring self-assignment in b.str1 = b.str1\[1:2\]$"
b.str1 = b.str2[1:2] // ERROR "\(\*Buffer\).baz ignoring self-assignment in b.str1 = b.str2\[1:2\]$"
}
@@ -1627,7 +1627,7 @@ func (b *Buffer) bat() { // ERROR "leaking param content: b$"
sink = o
}
-func quux(sp *string, bp *[]byte) { // ERROR "quux bp does not escape$" "quux sp does not escape$"
+func quux(sp *string, bp *[]byte) { // ERROR "bp does not escape$" "sp does not escape$"
*sp = (*sp)[1:2] // ERROR "quux ignoring self-assignment in \*sp = \(\*sp\)\[1:2\]$"
*bp = (*bp)[1:2] // ERROR "quux ignoring self-assignment in \*bp = \(\*bp\)\[1:2\]$"
}
@@ -1650,27 +1650,27 @@ func fieldFlowTracking() {
// String operations.
func slicebytetostring0() {
- b := make([]byte, 20) // ERROR "slicebytetostring0 make\(\[\]byte, 20\) does not escape$"
- s := string(b) // ERROR "slicebytetostring0 string\(b\) does not escape$"
+ b := make([]byte, 20) // ERROR "make\(\[\]byte, 20\) does not escape$"
+ s := string(b) // ERROR "string\(b\) does not escape$"
_ = s
}
func slicebytetostring1() {
- b := make([]byte, 20) // ERROR "slicebytetostring1 make\(\[\]byte, 20\) does not escape$"
- s := string(b) // ERROR "slicebytetostring1 string\(b\) does not escape$"
+ b := make([]byte, 20) // ERROR "make\(\[\]byte, 20\) does not escape$"
+ s := string(b) // ERROR "string\(b\) does not escape$"
s1 := s[0:1]
_ = s1
}
func slicebytetostring2() {
- b := make([]byte, 20) // ERROR "slicebytetostring2 make\(\[\]byte, 20\) does not escape$"
+ b := make([]byte, 20) // ERROR "make\(\[\]byte, 20\) does not escape$"
s := string(b) // ERROR "string\(b\) escapes to heap$"
s1 := s[0:1] // ERROR "moved to heap: s1$"
sink = &s1
}
func slicebytetostring3() {
- b := make([]byte, 20) // ERROR "slicebytetostring3 make\(\[\]byte, 20\) does not escape$"
+ b := make([]byte, 20) // ERROR "make\(\[\]byte, 20\) does not escape$"
s := string(b) // ERROR "string\(b\) escapes to heap$"
s1 := s[0:1]
sink = s1 // ERROR "s1 escapes to heap$"
@@ -1679,7 +1679,7 @@ func slicebytetostring3() {
func addstr0() {
s0 := "a"
s1 := "b"
- s := s0 + s1 // ERROR "addstr0 s0 \+ s1 does not escape$"
+ s := s0 + s1 // ERROR "s0 \+ s1 does not escape$"
_ = s
}
@@ -1687,14 +1687,14 @@ func addstr1() {
s0 := "a"
s1 := "b"
s := "c"
- s += s0 + s1 // ERROR "addstr1 s0 \+ s1 does not escape$"
+ s += s0 + s1 // ERROR "s0 \+ s1 does not escape$"
_ = s
}
func addstr2() {
- b := make([]byte, 20) // ERROR "addstr2 make\(\[\]byte, 20\) does not escape$"
+ b := make([]byte, 20) // ERROR "make\(\[\]byte, 20\) does not escape$"
s0 := "a"
- s := string(b) + s0 // ERROR "addstr2 string\(b\) \+ s0 does not escape$" "addstr2 string\(b\) does not escape$"
+ s := string(b) + s0 // ERROR "string\(b\) \+ s0 does not escape$" "string\(b\) does not escape$"
_ = s
}
@@ -1709,7 +1709,7 @@ func addstr3() {
func intstring0() bool {
// string does not escape
x := '0'
- s := string(x) // ERROR "intstring0 string\(x\) does not escape$"
+ s := string(x) // ERROR "string\(x\) does not escape$"
return s == "0"
}
@@ -1729,7 +1729,7 @@ func intstring2() {
func stringtoslicebyte0() {
s := "foo"
- x := []byte(s) // ERROR "stringtoslicebyte0 \(\[\]byte\)\(s\) does not escape$"
+ x := []byte(s) // ERROR "\(\[\]byte\)\(s\) does not escape$"
_ = x
}
@@ -1745,7 +1745,7 @@ func stringtoslicebyte2() {
func stringtoslicerune0() {
s := "foo"
- x := []rune(s) // ERROR "stringtoslicerune0 \(\[\]rune\)\(s\) does not escape$"
+ x := []rune(s) // ERROR "\(\[\]rune\)\(s\) does not escape$"
_ = x
}
@@ -1760,23 +1760,23 @@ func stringtoslicerune2() {
}
func slicerunetostring0() {
- r := []rune{1, 2, 3} // ERROR "slicerunetostring0 \[\]rune literal does not escape$"
- s := string(r) // ERROR "slicerunetostring0 string\(r\) does not escape$"
+ r := []rune{1, 2, 3} // ERROR "\[\]rune literal does not escape$"
+ s := string(r) // ERROR "string\(r\) does not escape$"
_ = s
}
func slicerunetostring1() string {
- r := []rune{1, 2, 3} // ERROR "slicerunetostring1 \[\]rune literal does not escape$"
+ r := []rune{1, 2, 3} // ERROR "\[\]rune literal does not escape$"
return string(r) // ERROR "string\(r\) escapes to heap$"
}
func slicerunetostring2() {
- r := []rune{1, 2, 3} // ERROR "slicerunetostring2 \[\]rune literal does not escape$"
+ r := []rune{1, 2, 3} // ERROR "\[\]rune literal does not escape$"
sink = string(r) // ERROR "string\(r\) escapes to heap$"
}
func makemap0() {
- m := make(map[int]int) // ERROR "makemap0 make\(map\[int\]int\) does not escape$"
+ m := make(map[int]int) // ERROR "make\(map\[int\]int\) does not escape$"
m[0] = 0
m[1]++
delete(m, 1)
@@ -1792,12 +1792,12 @@ func makemap2() {
sink = m
}
-func nonescapingEface(m map[interface{}]bool) bool { // ERROR "nonescapingEface m does not escape$"
- return m["foo"] // ERROR "nonescapingEface .foo. does not escape$"
+func nonescapingEface(m map[interface{}]bool) bool { // ERROR "m does not escape$"
+ return m["foo"] // ERROR ".foo. does not escape$"
}
-func nonescapingIface(m map[M]bool) bool { // ERROR "nonescapingIface m does not escape$"
- return m[MV(0)] // ERROR "nonescapingIface MV\(0\) does not escape$"
+func nonescapingIface(m map[M]bool) bool { // ERROR "m does not escape$"
+ return m[MV(0)] // ERROR "MV\(0\) does not escape$"
}
func issue10353() {
diff --git a/test/escape_array.go b/test/escape_array.go
index d363b98eac..0d07fd861f 100644
--- a/test/escape_array.go
+++ b/test/escape_array.go
@@ -71,7 +71,7 @@ func fuo(x *U, y *U) *string { // ERROR "leaking param: x to result ~r2 level=1$
// pointers stored in small array literals do not escape;
// large array literals are heap allocated;
// pointers stored in large array literals escape.
-func hugeLeaks1(x **string, y **string) { // ERROR "leaking param content: x" "hugeLeaks1 y does not escape"
+func hugeLeaks1(x **string, y **string) { // ERROR "leaking param content: x" "y does not escape"
a := [10]*string{*y}
_ = a
// 4 x 4,000,000 exceeds MaxStackVarSize, therefore it must be heap allocated if pointers are 4 bytes or larger.
@@ -79,7 +79,7 @@ func hugeLeaks1(x **string, y **string) { // ERROR "leaking param content: x" "h
_ = b
}
-func hugeLeaks2(x *string, y *string) { // ERROR "leaking param: x" "hugeLeaks2 y does not escape"
+func hugeLeaks2(x *string, y *string) { // ERROR "leaking param: x" "y does not escape"
a := [10]*string{y}
_ = a
// 4 x 4,000,000 exceeds MaxStackVarSize, therefore it must be heap allocated if pointers are 4 bytes or larger.
diff --git a/test/escape_calls.go b/test/escape_calls.go
index 7b1862e014..2dbfee1558 100644
--- a/test/escape_calls.go
+++ b/test/escape_calls.go
@@ -44,7 +44,7 @@ func walk(np **Node) int { // ERROR "leaking param content: np"
}
// Test for bug where func var f used prototype's escape analysis results.
-func prototype(xyz []string) {} // ERROR "prototype xyz does not escape"
+func prototype(xyz []string) {} // ERROR "xyz does not escape"
func bar() {
var got [][]string
f := prototype
diff --git a/test/escape_closure.go b/test/escape_closure.go
index f9f3c0fbd4..3b14027fa4 100644
--- a/test/escape_closure.go
+++ b/test/escape_closure.go
@@ -139,28 +139,28 @@ func ClosureCallArgs15() {
}(&p)
}
-func ClosureLeak1(s string) string { // ERROR "ClosureLeak1 s does not escape"
+func ClosureLeak1(s string) string { // ERROR "s does not escape"
t := s + "YYYY" // ERROR "escapes to heap"
- return ClosureLeak1a(t) // ERROR "ClosureLeak1 ... argument does not escape"
+ return ClosureLeak1a(t) // ERROR "... argument does not escape"
}
// See #14409 -- returning part of captured var leaks it.
func ClosureLeak1a(a ...string) string { // ERROR "leaking param: a to result ~r1 level=1$"
- return func() string { // ERROR "ClosureLeak1a func literal does not escape"
+ return func() string { // ERROR "func literal does not escape"
return a[0]
}()
}
-func ClosureLeak2(s string) string { // ERROR "ClosureLeak2 s does not escape"
+func ClosureLeak2(s string) string { // ERROR "s does not escape"
t := s + "YYYY" // ERROR "escapes to heap"
- c := ClosureLeak2a(t) // ERROR "ClosureLeak2 ... argument does not escape"
+ c := ClosureLeak2a(t) // ERROR "... argument does not escape"
return c
}
func ClosureLeak2a(a ...string) string { // ERROR "leaking param content: a"
- return ClosureLeak2b(func() string { // ERROR "ClosureLeak2a func literal does not escape"
+ return ClosureLeak2b(func() string { // ERROR "func literal does not escape"
return a[0]
})
}
-func ClosureLeak2b(f func() string) string { // ERROR "ClosureLeak2b f does not escape"
+func ClosureLeak2b(f func() string) string { // ERROR "f does not escape"
return f()
}
diff --git a/test/escape_field.go b/test/escape_field.go
index 8bb1a99553..bf1dfb18ff 100644
--- a/test/escape_field.go
+++ b/test/escape_field.go
@@ -127,14 +127,14 @@ func field12() {
func field13() {
i := 0 // ERROR "moved to heap: i$"
- x := &X{p1: &i} // ERROR "field13 &X literal does not escape$"
+ x := &X{p1: &i} // ERROR "&X literal does not escape$"
sink = x.p1
}
func field14() {
i := 0 // ERROR "moved to heap: i$"
// BAD: &i should not escape
- x := &X{p1: &i} // ERROR "field14 &X literal does not escape$"
+ x := &X{p1: &i} // ERROR "&X literal does not escape$"
sink = x.p2
}
@@ -149,7 +149,7 @@ func field16() {
var x X
// BAD: &i should not escape
x.p1 = &i
- var iface interface{} = x // ERROR "field16 x does not escape"
+ var iface interface{} = x // ERROR "x does not escape"
x1 := iface.(X)
sink = x1.p2
}
@@ -158,7 +158,7 @@ func field17() {
i := 0 // ERROR "moved to heap: i$"
var x X
x.p1 = &i
- var iface interface{} = x // ERROR "field17 x does not escape"
+ var iface interface{} = x // ERROR "x does not escape"
x1 := iface.(X)
sink = x1.p1
}
@@ -168,7 +168,7 @@ func field18() {
var x X
// BAD: &i should not escape
x.p1 = &i
- var iface interface{} = x // ERROR "field18 x does not escape"
+ var iface interface{} = x // ERROR "x does not escape"
y, _ := iface.(Y) // Put X, but extracted Y. The cast will fail, so y is zero initialized.
sink = y // ERROR "y escapes to heap"
}
diff --git a/test/escape_iface.go b/test/escape_iface.go
index be7b6024cd..898f504b31 100644
--- a/test/escape_iface.go
+++ b/test/escape_iface.go
@@ -110,7 +110,7 @@ func efaceEscape1() {
{
i := 0 // ERROR "moved to heap: i"
v := M1{&i, 0}
- var x M = v // ERROR "efaceEscape1 v does not escape"
+ var x M = v // ERROR "v does not escape"
v1 := x.(M1)
sink = v1 // ERROR "v1 escapes to heap"
}
diff --git a/test/escape_indir.go b/test/escape_indir.go
index ccc8418d55..19889f259f 100644
--- a/test/escape_indir.go
+++ b/test/escape_indir.go
@@ -127,7 +127,7 @@ func constptr11() *ConstPtr {
return p
}
-func foo(p **int) { // ERROR "foo p does not escape"
+func foo(p **int) { // ERROR "p does not escape"
i := 0 // ERROR "moved to heap: i"
y := p
*y = &i
diff --git a/test/escape_param.go b/test/escape_param.go
index 329d6d1c7f..5e81de9f46 100644
--- a/test/escape_param.go
+++ b/test/escape_param.go
@@ -42,7 +42,7 @@ func caller1() {
}
// in -> other in
-func param2(p1 *int, p2 **int) { // ERROR "leaking param: p1$" "param2 p2 does not escape$"
+func param2(p1 *int, p2 **int) { // ERROR "leaking param: p1$" "p2 does not escape$"
*p2 = p1
}
@@ -137,7 +137,7 @@ type Pair struct {
p2 *int
}
-func param3(p *Pair) { // ERROR "param3 p does not escape"
+func param3(p *Pair) { // ERROR "p does not escape"
p.p1 = p.p2 // ERROR "param3 ignoring self-assignment in p.p1 = p.p2"
}
@@ -158,7 +158,7 @@ func caller3b() {
}
// in -> rcvr
-func (p *Pair) param4(i *int) { // ERROR "\(\*Pair\).param4 p does not escape$" "leaking param: i$"
+func (p *Pair) param4(i *int) { // ERROR "p does not escape$" "leaking param: i$"
p.p1 = i
}
@@ -211,7 +211,7 @@ func caller7() {
}
// **in -> heap
-func param8(i **int) { // ERROR "param8 i does not escape$"
+func param8(i **int) { // ERROR "i does not escape$"
sink = **i // ERROR "\* \(\*i\) escapes to heap"
}
@@ -294,7 +294,7 @@ type Indir struct {
p ***int
}
-func (r *Indir) param12(i **int) { // ERROR "\(\*Indir\).param12 r does not escape$" "moved to heap: i$"
+func (r *Indir) param12(i **int) { // ERROR "r does not escape$" "moved to heap: i$"
r.p = &i
}
@@ -309,7 +309,7 @@ func caller12a() {
func caller12b() {
i := 0 // ERROR "moved to heap: i$"
p := &i // ERROR "moved to heap: p$"
- r := &Indir{} // ERROR "caller12b &Indir literal does not escape$"
+ r := &Indir{} // ERROR "&Indir literal does not escape$"
r.param12(&p)
_ = r
}
@@ -335,7 +335,7 @@ type Val struct {
p **int
}
-func (v Val) param13(i *int) { // ERROR "Val.param13 v does not escape$" "leaking param: i$"
+func (v Val) param13(i *int) { // ERROR "v does not escape$" "leaking param: i$"
*v.p = i
}
@@ -359,7 +359,7 @@ func caller13b() {
func caller13c() {
i := 0 // ERROR "moved to heap: i$"
var p *int
- v := &Val{&p} // ERROR "caller13c &Val literal does not escape$"
+ v := &Val{&p} // ERROR "&Val literal does not escape$"
v.param13(&i)
_ = v
}
@@ -400,7 +400,7 @@ func caller13g() {
func caller13h() {
i := 0 // ERROR "moved to heap: i$"
var p *int
- v := &Val{&p} // ERROR "caller13h &Val literal does not escape$"
+ v := &Val{&p} // ERROR "&Val literal does not escape$"
v.param13(&i)
sink = **v.p // ERROR "\* \(\*v\.p\) escapes to heap"
}
@@ -420,7 +420,7 @@ func g(x *Node) *Node { // ERROR "leaking param content: x"
}
func h(x *Node) { // ERROR "leaking param: x"
- y := &Node{x} // ERROR "h &Node literal does not escape"
+ y := &Node{x} // ERROR "&Node literal does not escape"
Sink = g(y)
f(y)
}
diff --git a/test/escape_struct_param1.go b/test/escape_struct_param1.go
index 7004946e2f..70b36191ab 100644
--- a/test/escape_struct_param1.go
+++ b/test/escape_struct_param1.go
@@ -38,7 +38,7 @@ func tSPPi() {
s := "cat" // ERROR "moved to heap: s$"
ps := &s
pps := &ps
- pu := &U{ps, pps} // ERROR "tSPPi &U literal does not escape$"
+ pu := &U{ps, pps} // ERROR "&U literal does not escape$"
Ssink = pu.SPPi()
}
@@ -46,7 +46,7 @@ func tiSPP() {
s := "cat" // ERROR "moved to heap: s$"
ps := &s
pps := &ps
- pu := &U{ps, pps} // ERROR "tiSPP &U literal does not escape$"
+ pu := &U{ps, pps} // ERROR "&U literal does not escape$"
Ssink = *pu.SPP()
}
@@ -55,7 +55,7 @@ func tSP() {
s := "cat" // ERROR "moved to heap: s$"
ps := &s // ERROR "moved to heap: ps$"
pps := &ps
- pu := &U{ps, pps} // ERROR "tSP &U literal does not escape$"
+ pu := &U{ps, pps} // ERROR "&U literal does not escape$"
Ssink = pu.SP()
}
@@ -123,9 +123,9 @@ func tUPiSPa() {
ps4 := &s4 // ERROR "moved to heap: ps4$"
ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "tUPiSPa &U literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$"
- v := &V{u1, u2, &u3} // ERROR "tUPiSPa &V literal does not escape$"
+ v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
Ssink = v.UPiSPa() // Ssink = &s3 (only &s3 really escapes)
}
@@ -141,9 +141,9 @@ func tUPiSPb() {
ps4 := &s4 // ERROR "moved to heap: ps4$"
ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "tUPiSPb &U literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$"
- v := &V{u1, u2, &u3} // ERROR "tUPiSPb &V literal does not escape$"
+ v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
Ssink = v.UPiSPb() // Ssink = &s3 (only &s3 really escapes)
}
@@ -159,9 +159,9 @@ func tUPiSPc() {
ps4 := &s4 // ERROR "moved to heap: ps4$"
ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "tUPiSPc &U literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$"
- v := &V{u1, u2, &u3} // ERROR "tUPiSPc &V literal does not escape$"
+ v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
Ssink = v.UPiSPc() // Ssink = &s3 (only &s3 really escapes)
}
@@ -177,9 +177,9 @@ func tUPiSPd() {
ps4 := &s4 // ERROR "moved to heap: ps4$"
ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "tUPiSPd &U literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$"
- v := &V{u1, u2, &u3} // ERROR "tUPiSPd &V literal does not escape$"
+ v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
Ssink = v.UPiSPd() // Ssink = &s3 (only &s3 really escapes)
}
@@ -211,9 +211,9 @@ func tUPiSPPia() {
ps4 := &s4
ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "tUPiSPPia &U literal does not escape$"
- u3 := &U{&s5, &ps6} // ERROR "tUPiSPPia &U literal does not escape$"
- v := &V{u1, u2, &u3} // ERROR "tUPiSPPia &V literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
+ u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$"
+ v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
Ssink = v.UPiSPPia() // Ssink = *&ps4 = &s4 (only &s4 really escapes)
}
@@ -229,9 +229,9 @@ func tUPiSPPib() {
ps4 := &s4
ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "tUPiSPPib &U literal does not escape$"
- u3 := &U{&s5, &ps6} // ERROR "tUPiSPPib &U literal does not escape$"
- v := &V{u1, u2, &u3} // ERROR "tUPiSPPib &V literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
+ u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$"
+ v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
Ssink = v.UPiSPPib() // Ssink = *&ps4 = &s4 (only &s4 really escapes)
}
@@ -247,9 +247,9 @@ func tUPiSPPic() {
ps4 := &s4
ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "tUPiSPPic &U literal does not escape$"
- u3 := &U{&s5, &ps6} // ERROR "tUPiSPPic &U literal does not escape$"
- v := &V{u1, u2, &u3} // ERROR "tUPiSPPic &V literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
+ u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$"
+ v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
Ssink = v.UPiSPPic() // Ssink = *&ps4 = &s4 (only &s4 really escapes)
}
@@ -265,9 +265,9 @@ func tUPiSPPid() {
ps4 := &s4
ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "tUPiSPPid &U literal does not escape$"
- u3 := &U{&s5, &ps6} // ERROR "tUPiSPPid &U literal does not escape$"
- v := &V{u1, u2, &u3} // ERROR "tUPiSPPid &V literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
+ u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$"
+ v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
Ssink = v.UPiSPPid() // Ssink = *&ps4 = &s4 (only &s4 really escapes)
}
@@ -291,8 +291,8 @@ func tUPPiSPPia() {
ps4 := &s4
ps6 := &s6
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "tUPPiSPPia &U literal does not escape$"
- u3 := &U{&s5, &ps6} // ERROR "tUPPiSPPia &U literal does not escape$"
- v := &V{u1, u2, &u3} // ERROR "tUPPiSPPia &V literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
+ u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$"
+ v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
Ssink = v.UPPiSPPia() // Ssink = *&ps6 = &s6 (only &s6 really escapes)
}
diff --git a/test/escape_struct_param2.go b/test/escape_struct_param2.go
index 5a9b271958..e42be79793 100644
--- a/test/escape_struct_param2.go
+++ b/test/escape_struct_param2.go
@@ -38,7 +38,7 @@ func tSPPi() {
s := "cat" // ERROR "moved to heap: s$"
ps := &s
pps := &ps
- pu := &U{ps, pps} // ERROR "tSPPi &U literal does not escape$"
+ pu := &U{ps, pps} // ERROR "&U literal does not escape$"
Ssink = pu.SPPi()
}
@@ -46,7 +46,7 @@ func tiSPP() {
s := "cat" // ERROR "moved to heap: s$"
ps := &s
pps := &ps
- pu := &U{ps, pps} // ERROR "tiSPP &U literal does not escape$"
+ pu := &U{ps, pps} // ERROR "&U literal does not escape$"
Ssink = *pu.SPP()
}
@@ -55,7 +55,7 @@ func tSP() {
s := "cat" // ERROR "moved to heap: s$"
ps := &s // ERROR "moved to heap: ps$"
pps := &ps
- pu := &U{ps, pps} // ERROR "tSP &U literal does not escape$"
+ pu := &U{ps, pps} // ERROR "&U literal does not escape$"
Ssink = pu.SP()
}
@@ -123,9 +123,9 @@ func tUPiSPa() {
ps4 := &s4 // ERROR "moved to heap: ps4$"
ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "tUPiSPa &U literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$"
- v := &V{u1, u2, &u3} // ERROR "tUPiSPa &V literal does not escape$"
+ v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
Ssink = v.UPiSPa() // Ssink = &s3 (only &s3 really escapes)
}
@@ -141,9 +141,9 @@ func tUPiSPb() {
ps4 := &s4 // ERROR "moved to heap: ps4$"
ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "tUPiSPb &U literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$"
- v := &V{u1, u2, &u3} // ERROR "tUPiSPb &V literal does not escape$"
+ v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
Ssink = v.UPiSPb() // Ssink = &s3 (only &s3 really escapes)
}
@@ -159,9 +159,9 @@ func tUPiSPc() {
ps4 := &s4 // ERROR "moved to heap: ps4$"
ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "tUPiSPc &U literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$"
- v := &V{u1, u2, &u3} // ERROR "tUPiSPc &V literal does not escape$"
+ v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
Ssink = v.UPiSPc() // Ssink = &s3 (only &s3 really escapes)
}
@@ -177,9 +177,9 @@ func tUPiSPd() {
ps4 := &s4 // ERROR "moved to heap: ps4$"
ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "tUPiSPd &U literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
u3 := &U{&s5, &ps6} // ERROR "&U literal escapes to heap$"
- v := &V{u1, u2, &u3} // ERROR "tUPiSPd &V literal does not escape$"
+ v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
Ssink = v.UPiSPd() // Ssink = &s3 (only &s3 really escapes)
}
@@ -211,9 +211,9 @@ func tUPiSPPia() {
ps4 := &s4
ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "tUPiSPPia &U literal does not escape$"
- u3 := &U{&s5, &ps6} // ERROR "tUPiSPPia &U literal does not escape$"
- v := &V{u1, u2, &u3} // ERROR "tUPiSPPia &V literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
+ u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$"
+ v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
Ssink = v.UPiSPPia() // Ssink = *&ps4 = &s4 (only &s4 really escapes)
}
@@ -229,9 +229,9 @@ func tUPiSPPib() {
ps4 := &s4
ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "tUPiSPPib &U literal does not escape$"
- u3 := &U{&s5, &ps6} // ERROR "tUPiSPPib &U literal does not escape$"
- v := &V{u1, u2, &u3} // ERROR "tUPiSPPib &V literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
+ u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$"
+ v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
Ssink = v.UPiSPPib() // Ssink = *&ps4 = &s4 (only &s4 really escapes)
}
@@ -247,9 +247,9 @@ func tUPiSPPic() {
ps4 := &s4
ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "tUPiSPPic &U literal does not escape$"
- u3 := &U{&s5, &ps6} // ERROR "tUPiSPPic &U literal does not escape$"
- v := &V{u1, u2, &u3} // ERROR "tUPiSPPic &V literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
+ u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$"
+ v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
Ssink = v.UPiSPPic() // Ssink = *&ps4 = &s4 (only &s4 really escapes)
}
@@ -265,9 +265,9 @@ func tUPiSPPid() {
ps4 := &s4
ps6 := &s6 // ERROR "moved to heap: ps6$"
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "tUPiSPPid &U literal does not escape$"
- u3 := &U{&s5, &ps6} // ERROR "tUPiSPPid &U literal does not escape$"
- v := &V{u1, u2, &u3} // ERROR "tUPiSPPid &V literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
+ u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$"
+ v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
Ssink = v.UPiSPPid() // Ssink = *&ps4 = &s4 (only &s4 really escapes)
}
@@ -291,8 +291,8 @@ func tUPPiSPPia() { // This test is sensitive to the level cap in function summa
ps4 := &s4
ps6 := &s6
u1 := U{&s1, &ps2}
- u2 := &U{&s3, &ps4} // ERROR "tUPPiSPPia &U literal does not escape$"
- u3 := &U{&s5, &ps6} // ERROR "tUPPiSPPia &U literal does not escape$"
- v := &V{u1, u2, &u3} // ERROR "tUPPiSPPia &V literal does not escape$"
+ u2 := &U{&s3, &ps4} // ERROR "&U literal does not escape$"
+ u3 := &U{&s5, &ps6} // ERROR "&U literal does not escape$"
+ v := &V{u1, u2, &u3} // ERROR "&V literal does not escape$"
Ssink = v.UPPiSPPia() // Ssink = *&ps6 = &s6 (only &s6 really escapes)
}
diff --git a/test/fixedbugs/issue12006.go b/test/fixedbugs/issue12006.go
index 60396c93ce..c44f2e5547 100644
--- a/test/fixedbugs/issue12006.go
+++ b/test/fixedbugs/issue12006.go
@@ -8,7 +8,7 @@
package foo
-func FooN(vals ...*int) (s int) { // ERROR "FooN vals does not escape"
+func FooN(vals ...*int) (s int) { // ERROR "vals does not escape"
for _, v := range vals {
s += *v
}
@@ -37,14 +37,14 @@ func FooNz(vals ...*int) (s int) { // ERROR "leaking param: vals"
func TFooN() {
for i := 0; i < 1000; i++ {
var i, j int
- FooN(&i, &j) // ERROR "TFooN ... argument does not escape"
+ FooN(&i, &j) // ERROR "... argument does not escape"
}
}
func TFooNx() {
for i := 0; i < 1000; i++ {
var i, j, k int // ERROR "moved to heap: i" "moved to heap: j" "moved to heap: k"
- FooNx(&k, &i, &j) // ERROR "TFooNx ... argument does not escape"
+ FooNx(&k, &i, &j) // ERROR "... argument does not escape"
}
}
@@ -84,7 +84,7 @@ func TFooI() {
a := int32(1) // ERROR "moved to heap: a"
b := "cat"
c := &a
- FooI(a, b, c) // ERROR "a escapes to heap" "b escapes to heap" "TFooI ... argument does not escape"
+ FooI(a, b, c) // ERROR "a escapes to heap" "b escapes to heap" "... argument does not escape"
}
func FooJ(args ...interface{}) *int32 { // ERROR "leaking param: args to result ~r1 level=1"
@@ -108,14 +108,14 @@ func TFooJ1() {
a := int32(1)
b := "cat"
c := &a
- FooJ(a, b, c) // ERROR "TFooJ1 a does not escape" "TFooJ1 b does not escape" "TFooJ1 ... argument does not escape"
+ FooJ(a, b, c) // ERROR "a does not escape" "b does not escape" "... argument does not escape"
}
func TFooJ2() {
a := int32(1) // ERROR "moved to heap: a"
b := "cat"
c := &a
- isink = FooJ(a, b, c) // ERROR "a escapes to heap" "b escapes to heap" "TFooJ2 ... argument does not escape"
+ isink = FooJ(a, b, c) // ERROR "a escapes to heap" "b escapes to heap" "... argument does not escape"
}
type fakeSlice struct {
@@ -144,7 +144,7 @@ func TFooK2() {
a := int32(1) // ERROR "moved to heap: a"
b := "cat"
c := &a
- fs := fakeSlice{3, &[4]interface{}{a, b, c, nil}} // ERROR "a escapes to heap" "b escapes to heap" "TFooK2 &\[4\]interface {} literal does not escape"
+ fs := fakeSlice{3, &[4]interface{}{a, b, c, nil}} // ERROR "a escapes to heap" "b escapes to heap" "&\[4\]interface {} literal does not escape"
isink = FooK(fs)
}
@@ -169,6 +169,6 @@ func TFooL2() {
a := int32(1) // ERROR "moved to heap: a"
b := "cat"
c := &a
- s := []interface{}{a, b, c} // ERROR "a escapes to heap" "b escapes to heap" "TFooL2 \[\]interface {} literal does not escape"
+ s := []interface{}{a, b, c} // ERROR "a escapes to heap" "b escapes to heap" "\[\]interface {} literal does not escape"
isink = FooL(s)
}
diff --git a/test/fixedbugs/issue12588.go b/test/fixedbugs/issue12588.go
index f99807b98b..950ef36e20 100644
--- a/test/fixedbugs/issue12588.go
+++ b/test/fixedbugs/issue12588.go
@@ -26,7 +26,7 @@ func f(a A) int {
return 0
}
-func g(a *A) int { // ERROR "g a does not escape"
+func g(a *A) int { // ERROR "a does not escape"
for i, x := range &a.b {
if x != 0 {
return 64*i + int(x)
diff --git a/test/fixedbugs/issue13799.go b/test/fixedbugs/issue13799.go
index b9bf49ca42..5c57494777 100644
--- a/test/fixedbugs/issue13799.go
+++ b/test/fixedbugs/issue13799.go
@@ -20,7 +20,7 @@ func main() {
// (sometimes it takes 1 time, sometimes it takes ~4,000+).
for iter := 0; ; iter++ {
if iter%50 == 0 {
- fmt.Println(iter) // ERROR "iter escapes to heap$" "main ... argument does not escape$"
+ fmt.Println(iter) // ERROR "iter escapes to heap$" "... argument does not escape$"
}
test1(iter)
test2(iter)
@@ -60,20 +60,20 @@ func test1(iter int) {
}
if len(m) != maxI {
- panic(fmt.Sprintf("iter %d: maxI = %d, len(m) = %d", iter, maxI, len(m))) // ERROR "iter escapes to heap$" "len\(m\) escapes to heap$" "maxI escapes to heap$" "test1 ... argument does not escape$"
+ panic(fmt.Sprintf("iter %d: maxI = %d, len(m) = %d", iter, maxI, len(m))) // ERROR "iter escapes to heap$" "len\(m\) escapes to heap$" "maxI escapes to heap$" "... argument does not escape$"
}
}
func test2(iter int) {
const maxI = 500
- m := make(map[int][]int) // ERROR "test2 make\(map\[int\]\[\]int\) does not escape$"
+ m := make(map[int][]int) // ERROR "make\(map\[int\]\[\]int\) does not escape$"
// var fn func()
for i := 0; i < maxI; i++ {
var fn func() // this makes it work, because fn stays off heap
j := 0
- fn = func() { // ERROR "test2 func literal does not escape$"
+ fn = func() { // ERROR "func literal does not escape$"
m[i] = append(m[i], 0)
if j < 25 {
j++
@@ -84,7 +84,7 @@ func test2(iter int) {
}
if len(m) != maxI {
- panic(fmt.Sprintf("iter %d: maxI = %d, len(m) = %d", iter, maxI, len(m))) // ERROR "iter escapes to heap$" "len\(m\) escapes to heap$" "maxI escapes to heap$" "test2 ... argument does not escape$"
+ panic(fmt.Sprintf("iter %d: maxI = %d, len(m) = %d", iter, maxI, len(m))) // ERROR "iter escapes to heap$" "len\(m\) escapes to heap$" "maxI escapes to heap$" "... argument does not escape$"
}
}
@@ -110,7 +110,7 @@ func test3(iter int) {
}
if *m != maxI {
- panic(fmt.Sprintf("iter %d: maxI = %d, *m = %d", iter, maxI, *m)) // ERROR "\*m escapes to heap$" "iter escapes to heap$" "maxI escapes to heap$" "test3 ... argument does not escape$"
+ panic(fmt.Sprintf("iter %d: maxI = %d, *m = %d", iter, maxI, *m)) // ERROR "\*m escapes to heap$" "iter escapes to heap$" "maxI escapes to heap$" "... argument does not escape$"
}
}
@@ -124,7 +124,7 @@ func test4(iter int) {
for i := 0; i < maxI; i++ {
var fn func() // this makes it work, because fn stays off heap
j := 0
- fn = func() { // ERROR "test4 func literal does not escape$"
+ fn = func() { // ERROR "func literal does not escape$"
if j < 100 {
j++
fn()
@@ -136,7 +136,7 @@ func test4(iter int) {
}
if *m != maxI {
- panic(fmt.Sprintf("iter %d: maxI = %d, *m = %d", iter, maxI, *m)) // ERROR "\*m escapes to heap$" "iter escapes to heap$" "maxI escapes to heap$" "test4 ... argument does not escape$"
+ panic(fmt.Sprintf("iter %d: maxI = %d, *m = %d", iter, maxI, *m)) // ERROR "\*m escapes to heap$" "iter escapes to heap$" "maxI escapes to heap$" "... argument does not escape$"
}
}
@@ -144,7 +144,7 @@ type str struct {
m *int
}
-func recur1(j int, s *str) { // ERROR "recur1 s does not escape"
+func recur1(j int, s *str) { // ERROR "s does not escape"
if j < 100 {
j++
recur1(j, s)
@@ -167,7 +167,7 @@ func test5(iter int) {
}
if *m != maxI {
- panic(fmt.Sprintf("iter %d: maxI = %d, *m = %d", iter, maxI, *m)) // ERROR "\*m escapes to heap$" "iter escapes to heap$" "maxI escapes to heap$" "test5 ... argument does not escape$"
+ panic(fmt.Sprintf("iter %d: maxI = %d, *m = %d", iter, maxI, *m)) // ERROR "\*m escapes to heap$" "iter escapes to heap$" "maxI escapes to heap$" "... argument does not escape$"
}
}
@@ -185,6 +185,6 @@ func test6(iter int) {
}
if *m != maxI {
- panic(fmt.Sprintf("iter %d: maxI = %d, *m = %d", iter, maxI, *m)) // ERROR "\*m escapes to heap$" "iter escapes to heap$" "maxI escapes to heap$" "test6 ... argument does not escape$"
+ panic(fmt.Sprintf("iter %d: maxI = %d, *m = %d", iter, maxI, *m)) // ERROR "\*m escapes to heap$" "iter escapes to heap$" "maxI escapes to heap$" "... argument does not escape$"
}
}
diff --git a/test/fixedbugs/issue17318.go b/test/fixedbugs/issue17318.go
index 6932d04d25..21df729f09 100644
--- a/test/fixedbugs/issue17318.go
+++ b/test/fixedbugs/issue17318.go
@@ -22,12 +22,12 @@ type closure func(i, j int) ent
type ent int
func (e ent) String() string {
- return fmt.Sprintf("%d", int(e)) // ERROR "ent.String ... argument does not escape$" "int\(e\) escapes to heap$"
+ return fmt.Sprintf("%d", int(e)) // ERROR "... argument does not escape$" "int\(e\) escapes to heap$"
}
//go:noinline
-func foo(ops closure, j int) (err fmt.Stringer) { // ERROR "foo ops does not escape"
- enqueue := func(i int) fmt.Stringer { // ERROR "foo func literal does not escape"
+func foo(ops closure, j int) (err fmt.Stringer) { // ERROR "ops does not escape"
+ enqueue := func(i int) fmt.Stringer { // ERROR "func literal does not escape"
return ops(i, j) // ERROR "ops\(i, j\) escapes to heap$"
}
err = enqueue(4)
@@ -39,9 +39,9 @@ func foo(ops closure, j int) (err fmt.Stringer) { // ERROR "foo ops does not esc
func main() {
// 3 identical functions, to get different escape behavior.
- f := func(i, j int) ent { // ERROR "main func literal does not escape"
+ f := func(i, j int) ent { // ERROR "func literal does not escape"
return ent(i + j)
}
i := foo(f, 3).(ent)
- fmt.Printf("foo(f,3)=%d\n", int(i)) // ERROR "int\(i\) escapes to heap$" "main ... argument does not escape$"
+ fmt.Printf("foo(f,3)=%d\n", int(i)) // ERROR "int\(i\) escapes to heap$" "... argument does not escape$"
}
diff --git a/test/fixedbugs/issue21709.go b/test/fixedbugs/issue21709.go
index 6e7f1d5ba6..10f5660e1b 100644
--- a/test/fixedbugs/issue21709.go
+++ b/test/fixedbugs/issue21709.go
@@ -10,14 +10,14 @@ package p
type S struct{}
-func (s *S) Inc() {} // ERROR "\(\*S\).Inc s does not escape"
+func (s *S) Inc() {} // ERROR "s does not escape"
var N int
func F1() {
var s S // ERROR "moved to heap: s"
for i := 0; i < N; i++ {
- fs := []func(){ // ERROR "F1 \[\]func\(\) literal does not escape"
- s.Inc, // ERROR "F1 s.Inc does not escape"
+ fs := []func(){ // ERROR "\[\]func\(\) literal does not escape"
+ s.Inc, // ERROR "s.Inc does not escape"
}
for _, f := range fs {
f()
@@ -28,8 +28,8 @@ func F1() {
func F2() {
var s S // ERROR "moved to heap: s"
for i := 0; i < N; i++ {
- for _, f := range []func(){ // ERROR "F2 \[\]func\(\) literal does not escape"
- s.Inc, // ERROR "F2 s.Inc does not escape"
+ for _, f := range []func(){ // ERROR "\[\]func\(\) literal does not escape"
+ s.Inc, // ERROR "s.Inc does not escape"
} {
f()
}
diff --git a/test/fixedbugs/issue7921.go b/test/fixedbugs/issue7921.go
index e19b113062..a8efc8dd9e 100644
--- a/test/fixedbugs/issue7921.go
+++ b/test/fixedbugs/issue7921.go
@@ -18,20 +18,20 @@ func bufferNotEscape() string {
// can be stack-allocated.
var b bytes.Buffer
b.WriteString("123")
- b.Write([]byte{'4'}) // ERROR "bufferNotEscape \[\]byte literal does not escape$"
+ b.Write([]byte{'4'}) // ERROR "\[\]byte literal does not escape$"
return b.String() // ERROR "inlining call to bytes.\(\*Buffer\).String$" "string\(bytes.b.buf\[bytes.b.off:\]\) escapes to heap$"
}
-func bufferNoEscape2(xs []string) int { // ERROR "bufferNoEscape2 xs does not escape$"
- b := bytes.NewBuffer(make([]byte, 0, 64)) // ERROR "bufferNoEscape2 &bytes.Buffer literal does not escape$" "bufferNoEscape2 make\(\[\]byte, 0, 64\) does not escape$" "inlining call to bytes.NewBuffer$"
+func bufferNoEscape2(xs []string) int { // ERROR "xs does not escape$"
+ b := bytes.NewBuffer(make([]byte, 0, 64)) // ERROR "&bytes.Buffer literal does not escape$" "make\(\[\]byte, 0, 64\) does not escape$" "inlining call to bytes.NewBuffer$"
for _, x := range xs {
b.WriteString(x)
}
return b.Len() // ERROR "inlining call to bytes.\(\*Buffer\).Len$"
}
-func bufferNoEscape3(xs []string) string { // ERROR "bufferNoEscape3 xs does not escape$"
- b := bytes.NewBuffer(make([]byte, 0, 64)) // ERROR "bufferNoEscape3 &bytes.Buffer literal does not escape$" "bufferNoEscape3 make\(\[\]byte, 0, 64\) does not escape$" "inlining call to bytes.NewBuffer$"
+func bufferNoEscape3(xs []string) string { // ERROR "xs does not escape$"
+ b := bytes.NewBuffer(make([]byte, 0, 64)) // ERROR "&bytes.Buffer literal does not escape$" "make\(\[\]byte, 0, 64\) does not escape$" "inlining call to bytes.NewBuffer$"
for _, x := range xs {
b.WriteString(x)
b.WriteByte(',')
@@ -47,11 +47,11 @@ func bufferNoEscape4() []byte {
}
func bufferNoEscape5() { // ERROR "can inline bufferNoEscape5$"
- b := bytes.NewBuffer(make([]byte, 0, 128)) // ERROR "bufferNoEscape5 &bytes.Buffer literal does not escape$" "bufferNoEscape5 make\(\[\]byte, 0, 128\) does not escape$" "inlining call to bytes.NewBuffer$"
+ b := bytes.NewBuffer(make([]byte, 0, 128)) // ERROR "&bytes.Buffer literal does not escape$" "make\(\[\]byte, 0, 128\) does not escape$" "inlining call to bytes.NewBuffer$"
useBuffer(b)
}
//go:noinline
-func useBuffer(b *bytes.Buffer) { // ERROR "useBuffer b does not escape$"
+func useBuffer(b *bytes.Buffer) { // ERROR "b does not escape$"
b.WriteString("1234")
}
diff --git a/test/inline.go b/test/inline.go
index 7e0551708e..8ebffedfb7 100644
--- a/test/inline.go
+++ b/test/inline.go
@@ -95,15 +95,15 @@ func p() int {
}
func q(x int) int {
- foo := func() int { return x * 2 } // ERROR "can inline q.func1" "q func literal does not escape"
+ foo := func() int { return x * 2 } // ERROR "can inline q.func1" "func literal does not escape"
return foo() // ERROR "inlining call to q.func1"
}
func r(z int) int {
- foo := func(x int) int { // ERROR "can inline r.func1" "r func literal does not escape"
+ foo := func(x int) int { // ERROR "can inline r.func1" "func literal does not escape"
return x + z
}
- bar := func(x int) int { // ERROR "r func literal does not escape"
+ bar := func(x int) int { // ERROR "func literal does not escape"
return x + func(y int) int { // ERROR "can inline r.func2.1"
return 2*y + x*z
}(x) // ERROR "inlining call to r.func2.1"
@@ -112,7 +112,7 @@ func r(z int) int {
}
func s0(x int) int {
- foo := func() { // ERROR "can inline s0.func1" "s0 func literal does not escape"
+ foo := func() { // ERROR "can inline s0.func1" "func literal does not escape"
x = x + 1
}
foo() // ERROR "inlining call to s0.func1"
@@ -120,7 +120,7 @@ func s0(x int) int {
}
func s1(x int) int {
- foo := func() int { // ERROR "can inline s1.func1" "s1 func literal does not escape"
+ foo := func() int { // ERROR "can inline s1.func1" "func literal does not escape"
return x
}
x = x + 1
@@ -145,7 +145,7 @@ func switchBreak(x, y int) int {
}
// can't currently inline functions with a type switch
-func switchType(x interface{}) int { // ERROR "switchType x does not escape"
+func switchType(x interface{}) int { // ERROR "x does not escape"
switch x.(type) {
case int:
return x.(int)
diff --git a/test/inline_big.go b/test/inline_big.go
index c4af15b4e1..b72ceb7f42 100644
--- a/test/inline_big.go
+++ b/test/inline_big.go
@@ -9,18 +9,18 @@
package foo
-func small(a []int) int { // ERROR "can inline small as:.*" "small a does not escape"
+func small(a []int) int { // ERROR "can inline small as:.*" "a does not escape"
// Cost 16 body (need cost < 20).
// See cmd/compile/internal/gc/inl.go:inlineBigFunction*
return a[0] + a[1] + a[2] + a[3]
}
-func medium(a []int) int { // ERROR "can inline medium as:.*" "medium a does not escape"
+func medium(a []int) int { // ERROR "can inline medium as:.*" "a does not escape"
// Cost 32 body (need cost > 20 and cost < 80).
// See cmd/compile/internal/gc/inl.go:inlineBigFunction*
return a[0] + a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7]
}
-func f(a []int) int { // ERROR "cannot inline f:.*" "f a does not escape"
+func f(a []int) int { // ERROR "cannot inline f:.*" "a does not escape"
// Add lots of nodes to f's body. We need >5000.
// See cmd/compile/internal/gc/inl.go:inlineBigFunction*
a[0] = 0
diff --git a/test/linkname.dir/linkname1.go b/test/linkname.dir/linkname1.go
index 9c61522fcc..c61a0d7d95 100644
--- a/test/linkname.dir/linkname1.go
+++ b/test/linkname.dir/linkname1.go
@@ -1,6 +1,6 @@
package x
-func indexByte(xs []byte, b byte) int { // ERROR "indexByte xs does not escape"
+func indexByte(xs []byte, b byte) int { // ERROR "xs does not escape"
for i, x := range xs {
if x == b {
return i
diff --git a/test/live_syscall.go b/test/live_syscall.go
index 2d1ef14de7..b920ff68aa 100644
--- a/test/live_syscall.go
+++ b/test/live_syscall.go
@@ -15,7 +15,7 @@ import (
"unsafe"
)
-func f(uintptr) // ERROR "f assuming arg#1 is unsafe uintptr"
+func f(uintptr) // ERROR "assuming arg#1 is unsafe uintptr"
func g() { // ERROR "can inline g"
var t int
From aa680c0c49b55722a72ad3772e590cd2f9af541d Mon Sep 17 00:00:00 2001
From: Jay Conrod
Date: Fri, 13 Sep 2019 13:58:58 -0400
Subject: [PATCH 009/199] cmd/go: don't include package dir in cache key when
-trimpath is set
The '-trimpath' flag tells 'go build' to trim any paths from the
output files that are tied to the current workspace or toolchain. When
this flag is set, we do not need to include the package directory in
the text hashed to construct the action ID for each package.
Fixes #33772
Change-Id: I20b902d2f58019709b15864ca79aa0d9255ae707
Reviewed-on: https://go-review.googlesource.com/c/go/+/195318
Run-TryBot: Jay Conrod
TryBot-Result: Gobot Gobot
Reviewed-by: Bryan C. Mills
---
src/cmd/go/internal/work/exec.go | 8 ++---
src/cmd/go/script_test.go | 20 ++++++++++---
src/cmd/go/testdata/script/build_trimpath.txt | 29 +++++++++++++++++--
3 files changed, 46 insertions(+), 11 deletions(-)
diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go
index 4564e32e65..626cacfe99 100644
--- a/src/cmd/go/internal/work/exec.go
+++ b/src/cmd/go/internal/work/exec.go
@@ -201,12 +201,12 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID {
// same compiler settings and can reuse each other's results.
// If not, the reason is already recorded in buildGcflags.
fmt.Fprintf(h, "compile\n")
+ // Only include the package directory if it may affect the output.
+ // We trim workspace paths for all packages when -trimpath is set.
// The compiler hides the exact value of $GOROOT
- // when building things in GOROOT,
- // but it does not hide the exact value of $GOPATH.
- // Include the full dir in that case.
+ // when building things in GOROOT.
// Assume b.WorkDir is being trimmed properly.
- if !p.Goroot && !strings.HasPrefix(p.Dir, b.WorkDir) {
+ if !p.Goroot && !cfg.BuildTrimpath && !strings.HasPrefix(p.Dir, b.WorkDir) {
fmt.Fprintf(h, "dir %s\n", p.Dir)
}
fmt.Fprintf(h, "goos %s goarch %s\n", cfg.Goos, cfg.Goarch)
diff --git a/src/cmd/go/script_test.go b/src/cmd/go/script_test.go
index 4dcb4b4e0d..5e50dd14c7 100644
--- a/src/cmd/go/script_test.go
+++ b/src/cmd/go/script_test.go
@@ -441,10 +441,15 @@ func (ts *testScript) cmdCmp(neg bool, args []string) {
// It would be strange to say "this file can have any content except this precise byte sequence".
ts.fatalf("unsupported: ! cmp")
}
+ quiet := false
+ if len(args) > 0 && args[0] == "-q" {
+ quiet = true
+ args = args[1:]
+ }
if len(args) != 2 {
ts.fatalf("usage: cmp file1 file2")
}
- ts.doCmdCmp(args, false)
+ ts.doCmdCmp(args, false, quiet)
}
// cmpenv compares two files with environment variable substitution.
@@ -452,13 +457,18 @@ func (ts *testScript) cmdCmpenv(neg bool, args []string) {
if neg {
ts.fatalf("unsupported: ! cmpenv")
}
+ quiet := false
+ if len(args) > 0 && args[0] == "-q" {
+ quiet = true
+ args = args[1:]
+ }
if len(args) != 2 {
ts.fatalf("usage: cmpenv file1 file2")
}
- ts.doCmdCmp(args, true)
+ ts.doCmdCmp(args, true, quiet)
}
-func (ts *testScript) doCmdCmp(args []string, env bool) {
+func (ts *testScript) doCmdCmp(args []string, env, quiet bool) {
name1, name2 := args[0], args[1]
var text1, text2 string
if name1 == "stdout" {
@@ -484,7 +494,9 @@ func (ts *testScript) doCmdCmp(args []string, env bool) {
return
}
- fmt.Fprintf(&ts.log, "[diff -%s +%s]\n%s\n", name1, name2, diff(text1, text2))
+ if !quiet {
+ fmt.Fprintf(&ts.log, "[diff -%s +%s]\n%s\n", name1, name2, diff(text1, text2))
+ }
ts.fatalf("%s and %s differ", name1, name2)
}
diff --git a/src/cmd/go/testdata/script/build_trimpath.txt b/src/cmd/go/testdata/script/build_trimpath.txt
index f785b0cb9e..668f75599e 100644
--- a/src/cmd/go/testdata/script/build_trimpath.txt
+++ b/src/cmd/go/testdata/script/build_trimpath.txt
@@ -1,21 +1,44 @@
[short] skip
env -r GOROOT_REGEXP=$GOROOT
-env -r WORK_REGEXP=$WORK
+env -r WORK_REGEXP='$WORK' # don't expand $WORK; grep replaces $WORK in text before matching.
env GOROOT GOROOT_REGEXP WORK WORK_REGEXP
+# A binary built without -trimpath should contain the current workspace
+# and GOROOT for debugging and stack traces.
+cd a
+go build -o hello.exe hello.go
+grep -q $WORK_REGEXP hello.exe
+grep -q $GOROOT_REGEXP hello.exe
+
+# A binary built with -trimpath should not contain the current workspace
+# or GOROOT.
go build -trimpath -o hello.exe hello.go
! grep -q $GOROOT_REGEXP hello.exe
! grep -q $WORK_REGEXP hello.exe
+cd ..
+# A binary from an external module built with -trimpath should not contain
+# the current workspace or GOROOT.
env GO111MODULE=on
go build -trimpath -o fortune.exe rsc.io/fortune
! grep -q $GOROOT_REGEXP fortune.exe
! grep -q $WORK_REGEXP fortune.exe
--- hello.go --
+# Two binaries built from identical packages in different directories
+# should be identical.
+mkdir b
+cp a/go.mod a/hello.go b
+cd a
+go build -trimpath -o ../a.exe .
+cd ../b
+go build -trimpath -o ../b.exe .
+cd ..
+cmp -q a.exe b.exe
+
+-- a/hello.go --
package main
func main() { println("hello") }
--- go.mod --
+-- a/go.mod --
module m
From c2facbe93705530a4b4aea70da544a7d645ea288 Mon Sep 17 00:00:00 2001
From: Alberto Donizetti
Date: Mon, 16 Sep 2019 16:31:32 +0000
Subject: [PATCH 010/199] Revert "test/codegen: document -all_codegen option in
README"
This reverts CL 192101.
Reason for revert: The same paragraph was added 2 weeks ago
(look a few lines above)
Change-Id: I05efb2631d7b4966f66493f178f2a649c715a3cc
Reviewed-on: https://go-review.googlesource.com/c/go/+/195637
Reviewed-by: Cherry Zhang
---
test/codegen/README | 6 ------
1 file changed, 6 deletions(-)
diff --git a/test/codegen/README b/test/codegen/README
index 1afefd9e3c..f6877e701d 100644
--- a/test/codegen/README
+++ b/test/codegen/README
@@ -30,12 +30,6 @@ transformation, it can be useful to first run the test harness with a
toolchain from a released Go version (and verify that the new tests
fail), and then re-runnig the tests using the devel toolchain.
-By default, only checks that are relevant to the current GOOS/GOARCH
-are run. Checks for all platforms can be enabled by specifiying the
--all_codegen option, as
-
- $ ../bin/go run run.go -all_codegen codegen
-
- Regexps comments syntax
From 7f907b9ceecfecb6dfe03878490bd3ae97ba8f2f Mon Sep 17 00:00:00 2001
From: Matthew Dempsky
Date: Mon, 16 Sep 2019 11:28:05 -0700
Subject: [PATCH 011/199] cmd/compile: require -lang=go1.14 for overlapping
interfaces
Support for overlapping interfaces is a new (proposed) Go language
feature to be supported in Go 1.14, so it shouldn't be supported under
-lang=go1.13 or earlier.
Fixes #34329.
Change-Id: I5fea5716b7d135476980bc40b4f6e8c611b67735
Reviewed-on: https://go-review.googlesource.com/c/go/+/195678
Run-TryBot: Matthew Dempsky
TryBot-Result: Gobot Gobot
Reviewed-by: Robert Griesemer
---
src/cmd/compile/internal/gc/align.go | 2 +-
src/go/types/stdlib_test.go | 1 +
test/fixedbugs/issue34329.go | 14 ++++++++++++++
3 files changed, 16 insertions(+), 1 deletion(-)
create mode 100644 test/fixedbugs/issue34329.go
diff --git a/src/cmd/compile/internal/gc/align.go b/src/cmd/compile/internal/gc/align.go
index d6251adc7a..bd6176e479 100644
--- a/src/cmd/compile/internal/gc/align.go
+++ b/src/cmd/compile/internal/gc/align.go
@@ -34,7 +34,7 @@ func expandiface(t *types.Type) {
switch prev := seen[m.Sym]; {
case prev == nil:
seen[m.Sym] = m
- case !explicit && types.Identical(m.Type, prev.Type):
+ case langSupported(1, 14) && !explicit && types.Identical(m.Type, prev.Type):
return
default:
yyerrorl(m.Pos, "duplicate method %s", m.Sym.Name)
diff --git a/src/go/types/stdlib_test.go b/src/go/types/stdlib_test.go
index 771f54d3f1..a3cbe95b3a 100644
--- a/src/go/types/stdlib_test.go
+++ b/src/go/types/stdlib_test.go
@@ -181,6 +181,7 @@ func TestStdFixed(t *testing.T) {
"issue25507.go", // go/types does not have constraints on stack size
"issue20780.go", // go/types does not have constraints on stack size
"issue31747.go", // go/types does not have constraints on language level (-lang=go1.12) (see #31793)
+ "issue34329.go", // go/types does not have constraints on language level (-lang=go1.13) (see #31793)
)
}
diff --git a/test/fixedbugs/issue34329.go b/test/fixedbugs/issue34329.go
new file mode 100644
index 0000000000..790686e3dd
--- /dev/null
+++ b/test/fixedbugs/issue34329.go
@@ -0,0 +1,14 @@
+// errorcheck -lang=go1.13
+
+// Copyright 2019 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 p
+
+type I interface { M() }
+
+type _ interface {
+ I
+ I // ERROR "duplicate method M"
+}
From 4d18a7ceb2d37b148061ee2e153d56aaef4de8fc Mon Sep 17 00:00:00 2001
From: Jay Conrod
Date: Mon, 16 Sep 2019 13:45:20 -0400
Subject: [PATCH 012/199] cmd/go: don't split internal test main packages twice
Fixes #34321
Change-Id: Ia6253038c525089e20a1da64a2c5c9dcc57edd74
Reviewed-on: https://go-review.googlesource.com/c/go/+/195677
Run-TryBot: Jay Conrod
TryBot-Result: Gobot Gobot
Reviewed-by: Bryan C. Mills
---
src/cmd/go/internal/load/test.go | 2 +-
.../go/testdata/script/list_split_main.txt | 25 +++++++++++++++++++
2 files changed, 26 insertions(+), 1 deletion(-)
create mode 100644 src/cmd/go/testdata/script/list_split_main.txt
diff --git a/src/cmd/go/internal/load/test.go b/src/cmd/go/internal/load/test.go
index 2864fb5ebb..aac7d7d481 100644
--- a/src/cmd/go/internal/load/test.go
+++ b/src/cmd/go/internal/load/test.go
@@ -405,7 +405,7 @@ func recompileForTest(pmain, preal, ptest, pxtest *Package) {
// covered packages are imported by pmain. Linking multiple packages
// compiled with '-p main' causes duplicate symbol errors.
// See golang.org/issue/30907, golang.org/issue/34114.
- if p.Name == "main" && p != pmain {
+ if p.Name == "main" && p != pmain && p != ptest {
split()
}
}
diff --git a/src/cmd/go/testdata/script/list_split_main.txt b/src/cmd/go/testdata/script/list_split_main.txt
new file mode 100644
index 0000000000..74e7d5d74c
--- /dev/null
+++ b/src/cmd/go/testdata/script/list_split_main.txt
@@ -0,0 +1,25 @@
+# This test checks that a "main" package with an external test package
+# is recompiled only once.
+# Verifies golang.org/issue/34321.
+
+env GO111MODULE=off
+
+go list -e -test -deps -f '{{if not .Standard}}{{.ImportPath}}{{end}}' pkg
+cmp stdout want
+
+-- $GOPATH/src/pkg/pkg.go --
+package main
+
+func main() {}
+
+-- $GOPATH/src/pkg/pkg_test.go --
+package main
+
+import "testing"
+
+func Test(t *testing.T) {}
+
+-- want --
+pkg
+pkg [pkg.test]
+pkg.test
From 357e8f83f868bd028df7f2f1c373b4d0c57d1408 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Mart=C3=AD?=
Date: Fri, 13 Sep 2019 14:42:05 +0100
Subject: [PATCH 013/199] cmd/compile: parallelize another big chunk of rulegen
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
rulegen has a sanity check that ensures all the arch-specific opcodes
are handled by each of the gen files.
This is an expensive chunk of work, particularly since there are a lot
of opcodes in total, and each one of them compiles and runs a regular
expression.
Parallelize that for each architecture, which greatly speeds up 'go run
*.go' on my laptop with four real CPU cores.
name old time/op new time/op delta
Rulegen 3.39s ± 1% 2.53s ± 2% -25.34% (p=0.008 n=5+5)
name old user-time/op new user-time/op delta
Rulegen 10.6s ± 1% 11.2s ± 1% +6.09% (p=0.008 n=5+5)
name old sys-time/op new sys-time/op delta
Rulegen 201ms ± 7% 218ms ±17% ~ (p=0.548 n=5+5)
name old peak-RSS-bytes new peak-RSS-bytes delta
Rulegen 182MB ± 3% 184MB ± 3% ~ (p=0.690 n=5+5)
Change-Id: Iec538ed0fa7eb867eeeeaab3da1e2615ce32cbb9
Reviewed-on: https://go-review.googlesource.com/c/go/+/195218
Run-TryBot: Daniel Martí
TryBot-Result: Gobot Gobot
Reviewed-by: Keith Randall
---
src/cmd/compile/internal/ssa/gen/main.go | 31 +++++++++++++++---------
1 file changed, 19 insertions(+), 12 deletions(-)
diff --git a/src/cmd/compile/internal/ssa/gen/main.go b/src/cmd/compile/internal/ssa/gen/main.go
index ba17148fc9..783ac1bd30 100644
--- a/src/cmd/compile/internal/ssa/gen/main.go
+++ b/src/cmd/compile/internal/ssa/gen/main.go
@@ -392,27 +392,34 @@ func genOp() {
// Check that the arch genfile handles all the arch-specific opcodes.
// This is very much a hack, but it is better than nothing.
+ var wg sync.WaitGroup
for _, a := range archs {
if a.genfile == "" {
continue
}
- src, err := ioutil.ReadFile(a.genfile)
- if err != nil {
- log.Fatalf("can't read %s: %v", a.genfile, err)
- }
-
- for _, v := range a.ops {
- pattern := fmt.Sprintf("\\Wssa[.]Op%s%s\\W", a.name, v.name)
- match, err := regexp.Match(pattern, src)
+ a := a
+ wg.Add(1)
+ go func() {
+ src, err := ioutil.ReadFile(a.genfile)
if err != nil {
- log.Fatalf("bad opcode regexp %s: %v", pattern, err)
+ log.Fatalf("can't read %s: %v", a.genfile, err)
}
- if !match {
- log.Fatalf("Op%s%s has no code generation in %s", a.name, v.name, a.genfile)
+
+ for _, v := range a.ops {
+ pattern := fmt.Sprintf(`\Wssa\.Op%s%s\W`, a.name, v.name)
+ match, err := regexp.Match(pattern, src)
+ if err != nil {
+ log.Fatalf("bad opcode regexp %s: %v", pattern, err)
+ }
+ if !match {
+ log.Fatalf("Op%s%s has no code generation in %s", a.name, v.name, a.genfile)
+ }
}
- }
+ wg.Done()
+ }()
}
+ wg.Wait()
}
// Name returns the name of the architecture for use in Op* and Block* enumerations.
From 0e0bff840e3cd041aa9d103c6135862faae9c03f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Mart=C3=AD?=
Date: Fri, 13 Sep 2019 15:45:18 +0100
Subject: [PATCH 014/199] cmd/compiler: skip some go/printer work in rulegen
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
We use go/format on the final output, so don't bother with the added
tabwriter work to align comments when using go/printer.
name old time/op new time/op delta
Rulegen 2.53s ± 2% 2.48s ± 1% -2.20% (p=0.032 n=5+5)
name old user-time/op new user-time/op delta
Rulegen 11.2s ± 1% 10.8s ± 0% -3.72% (p=0.008 n=5+5)
name old sys-time/op new sys-time/op delta
Rulegen 218ms ±17% 207ms ±19% ~ (p=0.548 n=5+5)
name old peak-RSS-bytes new peak-RSS-bytes delta
Rulegen 184MB ± 3% 175MB ± 4% ~ (p=0.056 n=5+5)
Change-Id: I53bad2ab15cace67415f2171fffcd13ed596e62b
Reviewed-on: https://go-review.googlesource.com/c/go/+/195219
Run-TryBot: Daniel Martí
Reviewed-by: Brad Fitzpatrick
TryBot-Result: Gobot Gobot
---
src/cmd/compile/internal/ssa/gen/rulegen.go | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/cmd/compile/internal/ssa/gen/rulegen.go b/src/cmd/compile/internal/ssa/gen/rulegen.go
index 3a18ca252c..ed3ed75638 100644
--- a/src/cmd/compile/internal/ssa/gen/rulegen.go
+++ b/src/cmd/compile/internal/ssa/gen/rulegen.go
@@ -607,7 +607,7 @@ func fprint(w io.Writer, n Node) {
fprint(w, n.expr)
fmt.Fprintf(w, " {\nbreak\n}\n")
case ast.Node:
- printer.Fprint(w, emptyFset, n)
+ printConfig.Fprint(w, emptyFset, n)
if _, ok := n.(ast.Stmt); ok {
fmt.Fprintln(w)
}
@@ -616,6 +616,10 @@ func fprint(w io.Writer, n Node) {
}
}
+var printConfig = printer.Config{
+ Mode: printer.RawFormat, // we use go/format later, so skip work here
+}
+
var emptyFset = token.NewFileSet()
// Node can be a Statement or an ast.Expr.
From c1000c500cb4cec2991f8c1924cd5fff05279658 Mon Sep 17 00:00:00 2001
From: Lucas Bremgartner
Date: Mon, 16 Sep 2019 19:46:12 +0000
Subject: [PATCH 015/199] encoding/json: validate strings when decoding into
Number
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Unmarshaling a string into a json.Number should first check that the string is a valid Number.
If not, we should fail without decoding it.
Fixes #14702
Change-Id: I286178e93df74ad63c0a852c3f3489577072cf47
GitHub-Last-Rev: fe69bb68eed06d056639f440d2daf4bb7c99013b
GitHub-Pull-Request: golang/go#34272
Reviewed-on: https://go-review.googlesource.com/c/go/+/195045
Reviewed-by: Daniel Martí
Run-TryBot: Daniel Martí
TryBot-Result: Gobot Gobot
---
src/encoding/json/decode.go | 3 +++
src/encoding/json/decode_test.go | 31 +++++++++++++++++++++++++++++++
2 files changed, 34 insertions(+)
diff --git a/src/encoding/json/decode.go b/src/encoding/json/decode.go
index 360fc69d04..407fbcedbe 100644
--- a/src/encoding/json/decode.go
+++ b/src/encoding/json/decode.go
@@ -949,6 +949,9 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
}
v.SetBytes(b[:n])
case reflect.String:
+ if v.Type() == numberType && !isValidNumber(string(s)) {
+ return fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", item)
+ }
v.SetString(string(s))
case reflect.Interface:
if v.NumMethod() == 0 {
diff --git a/src/encoding/json/decode_test.go b/src/encoding/json/decode_test.go
index 489f8674d0..4cbd2172d0 100644
--- a/src/encoding/json/decode_test.go
+++ b/src/encoding/json/decode_test.go
@@ -949,6 +949,37 @@ var unmarshalTests = []unmarshalTest{
Offset: 29,
},
},
+ // #14702
+ {
+ in: `invalid`,
+ ptr: new(Number),
+ err: &SyntaxError{
+ msg: "invalid character 'i' looking for beginning of value",
+ Offset: 1,
+ },
+ },
+ {
+ in: `"invalid"`,
+ ptr: new(Number),
+ err: fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", `"invalid"`),
+ },
+ {
+ in: `{"A":"invalid"}`,
+ ptr: new(struct{ A Number }),
+ err: fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", `"invalid"`),
+ },
+ {
+ in: `{"A":"invalid"}`,
+ ptr: new(struct {
+ A Number `json:",string"`
+ }),
+ err: fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into json.Number", `invalid`),
+ },
+ {
+ in: `{"A":"invalid"}`,
+ ptr: new(map[string]Number),
+ err: fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", `"invalid"`),
+ },
}
func TestMarshal(t *testing.T) {
From 115e4c9c14779f2561de4299b5748b98d7dd8cf0 Mon Sep 17 00:00:00 2001
From: Matthew Dempsky
Date: Mon, 16 Sep 2019 14:59:32 -0700
Subject: [PATCH 016/199] test: add test coverage for type-switch hash
collisions
This CL expands the test for #29612 to check that type switches also
work correctly when type hashes collide.
Change-Id: Ia153743e6ea0736c1a33191acfe4d8ba890be527
Reviewed-on: https://go-review.googlesource.com/c/go/+/195782
Run-TryBot: Matthew Dempsky
Reviewed-by: Keith Randall
TryBot-Result: Gobot Gobot
---
test/fixedbugs/issue29612.dir/main.go | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/test/fixedbugs/issue29612.dir/main.go b/test/fixedbugs/issue29612.dir/main.go
index 9dbc4c4cd9..d860eaac7e 100644
--- a/test/fixedbugs/issue29612.dir/main.go
+++ b/test/fixedbugs/issue29612.dir/main.go
@@ -10,6 +10,8 @@
package main
import (
+ "fmt"
+
ssa1 "./p1/ssa"
ssa2 "./p2/ssa"
)
@@ -21,4 +23,27 @@ func main() {
v2 := &ssa2.T{}
ssa2.Works(v2)
ssa2.Panics(v2) // This call must not panic
+
+ swt(v1, 1)
+ swt(v2, 2)
+}
+
+//go:noinline
+func swt(i interface{}, want int) {
+ var got int
+ switch i.(type) {
+ case *ssa1.T:
+ got = 1
+ case *ssa2.T:
+ got = 2
+
+ case int8, int16, int32, int64:
+ got = 3
+ case uint8, uint16, uint32, uint64:
+ got = 4
+ }
+
+ if got != want {
+ panic(fmt.Sprintf("switch %v: got %d, want %d", i, got, want))
+ }
}
From 8f3d9855a10a7a77bffe635bb37f4cfe1090ea0e Mon Sep 17 00:00:00 2001
From: Matthew Dempsky
Date: Tue, 10 Sep 2019 16:03:03 -0700
Subject: [PATCH 017/199] cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky
TryBot-Result: Gobot Gobot
Reviewed-by: Keith Randall
---
src/cmd/compile/internal/gc/swt.go | 970 ++++++++++--------------
src/cmd/compile/internal/gc/swt_test.go | 50 --
2 files changed, 387 insertions(+), 633 deletions(-)
delete mode 100644 src/cmd/compile/internal/gc/swt_test.go
diff --git a/src/cmd/compile/internal/gc/swt.go b/src/cmd/compile/internal/gc/swt.go
index 5089efe08b..4a8e9bceed 100644
--- a/src/cmd/compile/internal/gc/swt.go
+++ b/src/cmd/compile/internal/gc/swt.go
@@ -10,50 +10,6 @@ import (
"sort"
)
-const (
- // expression switch
- switchKindExpr = iota // switch a {...} or switch 5 {...}
- switchKindTrue // switch true {...} or switch {...}
- switchKindFalse // switch false {...}
-)
-
-const (
- binarySearchMin = 4 // minimum number of cases for binary search
- integerRangeMin = 2 // minimum size of integer ranges
-)
-
-// An exprSwitch walks an expression switch.
-type exprSwitch struct {
- exprname *Node // node for the expression being switched on
- kind int // kind of switch statement (switchKind*)
-}
-
-// A typeSwitch walks a type switch.
-type typeSwitch struct {
- hashname *Node // node for the hash of the type of the variable being switched on
- facename *Node // node for the concrete type of the variable being switched on
- okname *Node // boolean node used for comma-ok type assertions
-}
-
-// A caseClause is a single case clause in a switch statement.
-type caseClause struct {
- node *Node // points at case statement
- ordinal int // position in switch
- hash uint32 // hash of a type switch
- // isconst indicates whether this case clause is a constant,
- // for the purposes of the switch code generation.
- // For expression switches, that's generally literals (case 5:, not case x:).
- // For type switches, that's concrete types (case time.Time:), not interfaces (case io.Reader:).
- isconst bool
-}
-
-// caseClauses are all the case clauses in a switch statement.
-type caseClauses struct {
- list []caseClause // general cases
- defjmp *Node // OGOTO for default case or OBREAK if no default case present
- niljmp *Node // OGOTO for nil type case in a type switch
-}
-
// typecheckswitch typechecks a switch statement.
func typecheckswitch(n *Node) {
typecheckslice(n.Ninit.Slice(), ctxStmt)
@@ -71,7 +27,6 @@ func typecheckTypeSwitch(n *Node) {
yyerrorl(n.Pos, "cannot type switch on non-interface value %L", n.Left.Right)
t = nil
}
- n.Type = t // TODO(mdempsky): Remove; statements aren't typed.
// We don't actually declare the type switch's guarded
// declaration itself. So if there are no cases, we won't
@@ -212,7 +167,6 @@ func typecheckExprSwitch(n *Node) {
t = nil
}
}
- n.Type = t // TODO(mdempsky): Remove; statements aren't typed.
var defCase *Node
var cs constSet
@@ -265,422 +219,267 @@ func typecheckExprSwitch(n *Node) {
// walkswitch walks a switch statement.
func walkswitch(sw *Node) {
- // convert switch {...} to switch true {...}
- if sw.Left == nil {
- sw.Left = nodbool(true)
- sw.Left = typecheck(sw.Left, ctxExpr)
- sw.Left = defaultlit(sw.Left, nil)
+ // Guard against double walk, see #25776.
+ if sw.List.Len() == 0 && sw.Nbody.Len() > 0 {
+ return // Was fatal, but eliminating every possible source of double-walking is hard
}
- if sw.Left.Op == OTYPESW {
- var s typeSwitch
- s.walk(sw)
+ if sw.Left != nil && sw.Left.Op == OTYPESW {
+ walkTypeSwitch(sw)
} else {
- var s exprSwitch
- s.walk(sw)
+ walkExprSwitch(sw)
}
}
-// walk generates an AST implementing sw.
-// sw is an expression switch.
-// The AST is generally of the form of a linear
-// search using if..goto, although binary search
-// is used with long runs of constants.
-func (s *exprSwitch) walk(sw *Node) {
- // Guard against double walk, see #25776.
- if sw.List.Len() == 0 && sw.Nbody.Len() > 0 {
- return // Was fatal, but eliminating every possible source of double-walking is hard
- }
-
- casebody(sw, nil)
+// walkExprSwitch generates an AST implementing sw. sw is an
+// expression switch.
+func walkExprSwitch(sw *Node) {
+ lno := setlineno(sw)
cond := sw.Left
sw.Left = nil
- s.kind = switchKindExpr
- if Isconst(cond, CTBOOL) {
- s.kind = switchKindTrue
- if !cond.Val().U.(bool) {
- s.kind = switchKindFalse
- }
+ // convert switch {...} to switch true {...}
+ if cond == nil {
+ cond = nodbool(true)
+ cond = typecheck(cond, ctxExpr)
+ cond = defaultlit(cond, nil)
}
// Given "switch string(byteslice)",
- // with all cases being constants (or the default case),
+ // with all cases being side-effect free,
// use a zero-cost alias of the byte slice.
- // In theory, we could be more aggressive,
- // allowing any side-effect-free expressions in cases,
- // but it's a bit tricky because some of that information
- // is unavailable due to the introduction of temporaries during order.
- // Restricting to constants is simple and probably powerful enough.
// Do this before calling walkexpr on cond,
// because walkexpr will lower the string
// conversion into a runtime call.
// See issue 24937 for more discussion.
- if cond.Op == OBYTES2STR {
- ok := true
- for _, cas := range sw.List.Slice() {
- if cas.Op != OCASE {
- Fatalf("switch string(byteslice) bad op: %v", cas.Op)
- }
- if cas.Left != nil && !Isconst(cas.Left, CTSTR) {
- ok = false
- break
- }
- }
- if ok {
- cond.Op = OBYTES2STRTMP
- }
+ if cond.Op == OBYTES2STR && allCaseExprsAreSideEffectFree(sw) {
+ cond.Op = OBYTES2STRTMP
}
cond = walkexpr(cond, &sw.Ninit)
- t := sw.Type
- if t == nil {
- return
+ if cond.Op != OLITERAL {
+ cond = copyexpr(cond, cond.Type, &sw.Nbody)
}
- // convert the switch into OIF statements
- var cas []*Node
- if s.kind == switchKindTrue || s.kind == switchKindFalse {
- s.exprname = nodbool(s.kind == switchKindTrue)
- } else if consttype(cond) > 0 {
- // leave constants to enable dead code elimination (issue 9608)
- s.exprname = cond
- } else {
- s.exprname = temp(cond.Type)
- cas = []*Node{nod(OAS, s.exprname, cond)} // This gets walk()ed again in walkstmtlist just before end of this function. See #29562.
- typecheckslice(cas, ctxStmt)
+ lineno = lno
+
+ s := exprSwitch{
+ exprname: cond,
}
- // Enumerate the cases and prepare the default case.
- clauses := s.genCaseClauses(sw.List.Slice())
- sw.List.Set(nil)
- cc := clauses.list
-
- // handle the cases in order
- for len(cc) > 0 {
- run := 1
- if okforcmp[t.Etype] && cc[0].isconst {
- // do binary search on runs of constants
- for ; run < len(cc) && cc[run].isconst; run++ {
+ br := nod(OBREAK, nil, nil)
+ var defaultGoto *Node
+ var body Nodes
+ for _, ncase := range sw.List.Slice() {
+ label := autolabel(".s")
+ jmp := npos(ncase.Pos, nodSym(OGOTO, nil, label))
+
+ // Process case dispatch.
+ if ncase.List.Len() == 0 {
+ if defaultGoto != nil {
+ Fatalf("duplicate default case not detected during typechecking")
}
- // sort and compile constants
- sort.Sort(caseClauseByConstVal(cc[:run]))
+ defaultGoto = jmp
}
- a := s.walkCases(cc[:run])
- cas = append(cas, a)
- cc = cc[run:]
+ for _, n1 := range ncase.List.Slice() {
+ s.Add(ncase.Pos, n1, jmp)
+ }
+
+ // Process body.
+ body.Append(npos(ncase.Pos, nodSym(OLABEL, nil, label)))
+ body.Append(ncase.Nbody.Slice()...)
+ if !hasFall(ncase.Nbody.Slice()) {
+ body.Append(br)
+ }
}
+ sw.List.Set(nil)
- // handle default case
- if nerrors == 0 {
- cas = append(cas, clauses.defjmp)
- sw.Nbody.Prepend(cas...)
- walkstmtlist(sw.Nbody.Slice())
+ if defaultGoto == nil {
+ defaultGoto = br
}
-}
-// walkCases generates an AST implementing the cases in cc.
-func (s *exprSwitch) walkCases(cc []caseClause) *Node {
- if len(cc) < binarySearchMin {
- // linear search
- var cas []*Node
- for _, c := range cc {
- n := c.node
- lno := setlineno(n)
+ s.Emit(&sw.Nbody)
+ sw.Nbody.Append(defaultGoto)
+ sw.Nbody.AppendNodes(&body)
+ walkstmtlist(sw.Nbody.Slice())
+}
- a := nod(OIF, nil, nil)
- if rng := n.List.Slice(); rng != nil {
- // Integer range.
- // exprname is a temp or a constant,
- // so it is safe to evaluate twice.
- // In most cases, this conjunction will be
- // rewritten by walkinrange into a single comparison.
- low := nod(OGE, s.exprname, rng[0])
- high := nod(OLE, s.exprname, rng[1])
- a.Left = nod(OANDAND, low, high)
- } else if (s.kind != switchKindTrue && s.kind != switchKindFalse) || assignop(n.Left.Type, s.exprname.Type, nil) == OCONVIFACE || assignop(s.exprname.Type, n.Left.Type, nil) == OCONVIFACE {
- a.Left = nod(OEQ, s.exprname, n.Left) // if name == val
- } else if s.kind == switchKindTrue {
- a.Left = n.Left // if val
- } else {
- // s.kind == switchKindFalse
- a.Left = nod(ONOT, n.Left, nil) // if !val
- }
- a.Left = typecheck(a.Left, ctxExpr)
- a.Left = defaultlit(a.Left, nil)
- a.Nbody.Set1(n.Right) // goto l
+// An exprSwitch walks an expression switch.
+type exprSwitch struct {
+ exprname *Node // value being switched on
- cas = append(cas, a)
- lineno = lno
- }
- return liststmt(cas)
- }
+ done Nodes
+ clauses []exprClause
+}
- // find the middle and recur
- half := len(cc) / 2
- a := nod(OIF, nil, nil)
- n := cc[half-1].node
- var mid *Node
- if rng := n.List.Slice(); rng != nil {
- mid = rng[1] // high end of range
- } else {
- mid = n.Left
- }
- le := nod(OLE, s.exprname, mid)
- if Isconst(mid, CTSTR) {
- // Search by length and then by value; see caseClauseByConstVal.
- lenlt := nod(OLT, nod(OLEN, s.exprname, nil), nod(OLEN, mid, nil))
- leneq := nod(OEQ, nod(OLEN, s.exprname, nil), nod(OLEN, mid, nil))
- a.Left = nod(OOROR, lenlt, nod(OANDAND, leneq, le))
- } else {
- a.Left = le
- }
- a.Left = typecheck(a.Left, ctxExpr)
- a.Left = defaultlit(a.Left, nil)
- a.Nbody.Set1(s.walkCases(cc[:half]))
- a.Rlist.Set1(s.walkCases(cc[half:]))
- return a
+type exprClause struct {
+ pos src.XPos
+ lo, hi *Node
+ jmp *Node
}
-// casebody builds separate lists of statements and cases.
-// It makes labels between cases and statements
-// and deals with fallthrough, break, and unreachable statements.
-func casebody(sw *Node, typeswvar *Node) {
- if sw.List.Len() == 0 {
+func (s *exprSwitch) Add(pos src.XPos, expr, jmp *Node) {
+ c := exprClause{pos: pos, lo: expr, hi: expr, jmp: jmp}
+ if okforcmp[s.exprname.Type.Etype] && expr.Op == OLITERAL {
+ s.clauses = append(s.clauses, c)
return
}
- lno := setlineno(sw)
+ s.flush()
+ s.clauses = append(s.clauses, c)
+ s.flush()
+}
- var cas []*Node // cases
- var stat []*Node // statements
- var def *Node // defaults
- br := nod(OBREAK, nil, nil)
+func (s *exprSwitch) Emit(out *Nodes) {
+ s.flush()
+ out.AppendNodes(&s.done)
+}
- for _, n := range sw.List.Slice() {
- setlineno(n)
- if n.Op != OXCASE {
- Fatalf("casebody %v", n.Op)
- }
- n.Op = OCASE
- needvar := n.List.Len() != 1 || n.List.First().Op == OLITERAL
-
- lbl := autolabel(".s")
- jmp := nodSym(OGOTO, nil, lbl)
- switch n.List.Len() {
- case 0:
- // default
- if def != nil {
- yyerrorl(n.Pos, "more than one default case")
- }
- // reuse original default case
- n.Right = jmp
- def = n
- case 1:
- // one case -- reuse OCASE node
- n.Left = n.List.First()
- n.Right = jmp
- n.List.Set(nil)
- cas = append(cas, n)
- default:
- // Expand multi-valued cases and detect ranges of integer cases.
- if typeswvar != nil || sw.Left.Type.IsInterface() || !n.List.First().Type.IsInteger() || n.List.Len() < integerRangeMin {
- // Can't use integer ranges. Expand each case into a separate node.
- for _, n1 := range n.List.Slice() {
- cas = append(cas, nod(OCASE, n1, jmp))
- }
- break
- }
- // Find integer ranges within runs of constants.
- s := n.List.Slice()
- j := 0
- for j < len(s) {
- // Find a run of constants.
- var run int
- for run = j; run < len(s) && Isconst(s[run], CTINT); run++ {
- }
- if run-j >= integerRangeMin {
- // Search for integer ranges in s[j:run].
- // Typechecking is done, so all values are already in an appropriate range.
- search := s[j:run]
- sort.Sort(constIntNodesByVal(search))
- for beg, end := 0, 1; end <= len(search); end++ {
- if end < len(search) && search[end].Int64() == search[end-1].Int64()+1 {
- continue
- }
- if end-beg >= integerRangeMin {
- // Record range in List.
- c := nod(OCASE, nil, jmp)
- c.List.Set2(search[beg], search[end-1])
- cas = append(cas, c)
- } else {
- // Not large enough for range; record separately.
- for _, n := range search[beg:end] {
- cas = append(cas, nod(OCASE, n, jmp))
- }
- }
- beg = end
- }
- j = run
- }
- // Advance to next constant, adding individual non-constant
- // or as-yet-unhandled constant cases as we go.
- for ; j < len(s) && (j < run || !Isconst(s[j], CTINT)); j++ {
- cas = append(cas, nod(OCASE, s[j], jmp))
- }
+func (s *exprSwitch) flush() {
+ cc := s.clauses
+ s.clauses = nil
+ if len(cc) == 0 {
+ return
+ }
+
+ // Caution: If len(cc) == 1, then cc[0] might not an OLITERAL.
+ // The code below is structured to implicitly handle this case
+ // (e.g., sort.Slice doesn't need to invoke the less function
+ // when there's only a single slice element).
+
+ // Sort strings by length and then by value.
+ // It is much cheaper to compare lengths than values,
+ // and all we need here is consistency.
+ // We respect this sorting below.
+ sort.Slice(cc, func(i, j int) bool {
+ vi := cc[i].lo.Val()
+ vj := cc[j].lo.Val()
+
+ if s.exprname.Type.IsString() {
+ si := vi.U.(string)
+ sj := vj.U.(string)
+ if len(si) != len(sj) {
+ return len(si) < len(sj)
}
+ return si < sj
}
- stat = append(stat, nodSym(OLABEL, nil, lbl))
- if typeswvar != nil && needvar && n.Rlist.Len() != 0 {
- l := []*Node{
- nod(ODCL, n.Rlist.First(), nil),
- nod(OAS, n.Rlist.First(), typeswvar),
+ return compareOp(vi, OLT, vj)
+ })
+
+ // Merge consecutive integer cases.
+ if s.exprname.Type.IsInteger() {
+ merged := cc[:1]
+ for _, c := range cc[1:] {
+ last := &merged[len(merged)-1]
+ if last.jmp == c.jmp && last.hi.Int64()+1 == c.lo.Int64() {
+ last.hi = c.lo
+ } else {
+ merged = append(merged, c)
}
- typecheckslice(l, ctxStmt)
- stat = append(stat, l...)
- }
- stat = append(stat, n.Nbody.Slice()...)
-
- // Search backwards for the index of the fallthrough
- // statement. Do not assume it'll be in the last
- // position, since in some cases (e.g. when the statement
- // list contains autotmp_ variables), one or more OVARKILL
- // nodes will be at the end of the list.
- fallIndex := len(stat) - 1
- for stat[fallIndex].Op == OVARKILL {
- fallIndex--
- }
- last := stat[fallIndex]
- if last.Op != OFALL {
- stat = append(stat, br)
}
+ cc = merged
}
- stat = append(stat, br)
- if def != nil {
- cas = append(cas, def)
- }
+ binarySearch(len(cc), &s.done,
+ func(i int) *Node {
+ mid := cc[i-1].hi
- sw.List.Set(cas)
- sw.Nbody.Set(stat)
- lineno = lno
+ le := nod(OLE, s.exprname, mid)
+ if s.exprname.Type.IsString() {
+ // Compare strings by length and then
+ // by value; see sort.Slice above.
+ lenlt := nod(OLT, nod(OLEN, s.exprname, nil), nod(OLEN, mid, nil))
+ leneq := nod(OEQ, nod(OLEN, s.exprname, nil), nod(OLEN, mid, nil))
+ le = nod(OOROR, lenlt, nod(OANDAND, leneq, le))
+ }
+ return le
+ },
+ func(i int, out *Nodes) {
+ c := &cc[i]
+
+ nif := nodl(c.pos, OIF, c.test(s.exprname), nil)
+ nif.Left = typecheck(nif.Left, ctxExpr)
+ nif.Left = defaultlit(nif.Left, nil)
+ nif.Nbody.Set1(c.jmp)
+ out.Append(nif)
+ },
+ )
}
-// genCaseClauses generates the caseClauses value for clauses.
-func (s *exprSwitch) genCaseClauses(clauses []*Node) caseClauses {
- var cc caseClauses
- for _, n := range clauses {
- if n.Left == nil && n.List.Len() == 0 {
- // default case
- if cc.defjmp != nil {
- Fatalf("duplicate default case not detected during typechecking")
- }
- cc.defjmp = n.Right
- continue
- }
- c := caseClause{node: n, ordinal: len(cc.list)}
- if n.List.Len() > 0 {
- c.isconst = true
- }
- switch consttype(n.Left) {
- case CTFLT, CTINT, CTRUNE, CTSTR:
- c.isconst = true
- }
- cc.list = append(cc.list, c)
+func (c *exprClause) test(exprname *Node) *Node {
+ // Integer range.
+ if c.hi != c.lo {
+ low := nodl(c.pos, OGE, exprname, c.lo)
+ high := nodl(c.pos, OLE, exprname, c.hi)
+ return nodl(c.pos, OANDAND, low, high)
}
- if cc.defjmp == nil {
- cc.defjmp = nod(OBREAK, nil, nil)
+ // Optimize "switch true { ...}" and "switch false { ... }".
+ if Isconst(exprname, CTBOOL) && !c.lo.Type.IsInterface() {
+ if exprname.Val().U.(bool) {
+ return c.lo
+ } else {
+ return nodl(c.pos, ONOT, c.lo, nil)
+ }
}
- return cc
+
+ return nodl(c.pos, OEQ, exprname, c.lo)
}
-// genCaseClauses generates the caseClauses value for clauses.
-func (s *typeSwitch) genCaseClauses(clauses []*Node) caseClauses {
- var cc caseClauses
- for _, n := range clauses {
- switch {
- case n.Left == nil:
- // default case
- if cc.defjmp != nil {
- Fatalf("duplicate default case not detected during typechecking")
- }
- cc.defjmp = n.Right
- continue
- case n.Left.Op == OLITERAL:
- // nil case in type switch
- if cc.niljmp != nil {
- Fatalf("duplicate nil case not detected during typechecking")
- }
- cc.niljmp = n.Right
- continue
+func allCaseExprsAreSideEffectFree(sw *Node) bool {
+ // In theory, we could be more aggressive, allowing any
+ // side-effect-free expressions in cases, but it's a bit
+ // tricky because some of that information is unavailable due
+ // to the introduction of temporaries during order.
+ // Restricting to constants is simple and probably powerful
+ // enough.
+
+ for _, ncase := range sw.List.Slice() {
+ if ncase.Op != OXCASE {
+ Fatalf("switch string(byteslice) bad op: %v", ncase.Op)
}
-
- // general case
- c := caseClause{
- node: n,
- ordinal: len(cc.list),
- isconst: !n.Left.Type.IsInterface(),
- hash: typehash(n.Left.Type),
+ for _, v := range ncase.List.Slice() {
+ if v.Op != OLITERAL {
+ return false
+ }
}
- cc.list = append(cc.list, c)
- }
-
- if cc.defjmp == nil {
- cc.defjmp = nod(OBREAK, nil, nil)
}
-
- return cc
+ return true
}
-// walk generates an AST that implements sw,
-// where sw is a type switch.
-// The AST is generally of the form of a linear
-// search using if..goto, although binary search
-// is used with long runs of concrete types.
-func (s *typeSwitch) walk(sw *Node) {
- cond := sw.Left
- sw.Left = nil
+// hasFall reports whether stmts ends with a "fallthrough" statement.
+func hasFall(stmts []*Node) bool {
+ // Search backwards for the index of the fallthrough
+ // statement. Do not assume it'll be in the last
+ // position, since in some cases (e.g. when the statement
+ // list contains autotmp_ variables), one or more OVARKILL
+ // nodes will be at the end of the list.
- if cond == nil {
- sw.List.Set(nil)
- return
+ i := len(stmts) - 1
+ for i >= 0 && stmts[i].Op == OVARKILL {
+ i--
}
- if cond.Right == nil {
- yyerrorl(sw.Pos, "type switch must have an assignment")
- return
- }
-
- cond.Right = walkexpr(cond.Right, &sw.Ninit)
- if !cond.Right.Type.IsInterface() {
- yyerrorl(sw.Pos, "type switch must be on an interface")
- return
- }
-
- var cas []*Node
-
- // predeclare temporary variables and the boolean var
- s.facename = temp(cond.Right.Type)
+ return i >= 0 && stmts[i].Op == OFALL
+}
- a := nod(OAS, s.facename, cond.Right)
- a = typecheck(a, ctxStmt)
- cas = append(cas, a)
+// walkTypeSwitch generates an AST that implements sw, where sw is a
+// type switch.
+func walkTypeSwitch(sw *Node) {
+ var s typeSwitch
+ s.facename = sw.Left.Right
+ sw.Left = nil
+ s.facename = walkexpr(s.facename, &sw.Ninit)
+ s.facename = copyexpr(s.facename, s.facename.Type, &sw.Nbody)
s.okname = temp(types.Types[TBOOL])
- s.okname = typecheck(s.okname, ctxExpr)
-
- s.hashname = temp(types.Types[TUINT32])
- s.hashname = typecheck(s.hashname, ctxExpr)
- // set up labels and jumps
- casebody(sw, s.facename)
-
- clauses := s.genCaseClauses(sw.List.Slice())
- sw.List.Set(nil)
- def := clauses.defjmp
+ // Get interface descriptor word.
+ // For empty interfaces this will be the type.
+ // For non-empty interfaces this will be the itab.
+ itab := nod(OITAB, s.facename, nil)
// For empty interfaces, do:
// if e._type == nil {
@@ -688,230 +487,235 @@ func (s *typeSwitch) walk(sw *Node) {
// }
// h := e._type.hash
// Use a similar strategy for non-empty interfaces.
-
- // Get interface descriptor word.
- // For empty interfaces this will be the type.
- // For non-empty interfaces this will be the itab.
- itab := nod(OITAB, s.facename, nil)
-
- // Check for nil first.
- i := nod(OIF, nil, nil)
- i.Left = nod(OEQ, itab, nodnil())
- if clauses.niljmp != nil {
- // Do explicit nil case right here.
- i.Nbody.Set1(clauses.niljmp)
- } else {
- // Jump to default case.
- lbl := autolabel(".s")
- i.Nbody.Set1(nodSym(OGOTO, nil, lbl))
- // Wrap default case with label.
- blk := nod(OBLOCK, nil, nil)
- blk.List.Set2(nodSym(OLABEL, nil, lbl), def)
- def = blk
- }
- i.Left = typecheck(i.Left, ctxExpr)
- i.Left = defaultlit(i.Left, nil)
- cas = append(cas, i)
+ ifNil := nod(OIF, nil, nil)
+ ifNil.Left = nod(OEQ, itab, nodnil())
+ ifNil.Left = typecheck(ifNil.Left, ctxExpr)
+ ifNil.Left = defaultlit(ifNil.Left, nil)
+ // ifNil.Nbody assigned at end.
+ sw.Nbody.Append(ifNil)
// Load hash from type or itab.
- h := nodSym(ODOTPTR, itab, nil)
- h.Type = types.Types[TUINT32]
- h.SetTypecheck(1)
- if cond.Right.Type.IsEmptyInterface() {
- h.Xoffset = int64(2 * Widthptr) // offset of hash in runtime._type
+ dotHash := nodSym(ODOTPTR, itab, nil)
+ dotHash.Type = types.Types[TUINT32]
+ dotHash.SetTypecheck(1)
+ if s.facename.Type.IsEmptyInterface() {
+ dotHash.Xoffset = int64(2 * Widthptr) // offset of hash in runtime._type
} else {
- h.Xoffset = int64(2 * Widthptr) // offset of hash in runtime.itab
+ dotHash.Xoffset = int64(2 * Widthptr) // offset of hash in runtime.itab
}
- h.SetBounded(true) // guaranteed not to fault
- a = nod(OAS, s.hashname, h)
- a = typecheck(a, ctxStmt)
- cas = append(cas, a)
+ dotHash.SetBounded(true) // guaranteed not to fault
+ s.hashname = copyexpr(dotHash, dotHash.Type, &sw.Nbody)
- cc := clauses.list
+ br := nod(OBREAK, nil, nil)
+ var defaultGoto, nilGoto *Node
+ var body Nodes
+ for _, ncase := range sw.List.Slice() {
+ var caseVar *Node
+ if ncase.Rlist.Len() != 0 {
+ caseVar = ncase.Rlist.First()
+ }
- // insert type equality check into each case block
- for _, c := range cc {
- c.node.Right = s.typeone(c.node)
- }
+ // For single-type cases, we initialize the case
+ // variable as part of the type assertion; but in
+ // other cases, we initialize it in the body.
+ singleType := ncase.List.Len() == 1 && ncase.List.First().Op == OTYPE
- // generate list of if statements, binary search for constant sequences
- for len(cc) > 0 {
- if !cc[0].isconst {
- n := cc[0].node
- cas = append(cas, n.Right)
- cc = cc[1:]
- continue
- }
+ label := autolabel(".s")
- // identify run of constants
- var run int
- for run = 1; run < len(cc) && cc[run].isconst; run++ {
+ jmp := npos(ncase.Pos, nodSym(OGOTO, nil, label))
+ if ncase.List.Len() == 0 { // default:
+ if defaultGoto != nil {
+ Fatalf("duplicate default case not detected during typechecking")
+ }
+ defaultGoto = jmp
}
- // sort by hash
- sort.Sort(caseClauseByType(cc[:run]))
+ for _, n1 := range ncase.List.Slice() {
+ if n1.isNil() { // case nil:
+ if nilGoto != nil {
+ Fatalf("duplicate nil case not detected during typechecking")
+ }
+ nilGoto = jmp
+ continue
+ }
- // for debugging: linear search
- if false {
- for i := 0; i < run; i++ {
- n := cc[i].node
- cas = append(cas, n.Right)
+ if singleType {
+ s.Add(n1.Type, caseVar, jmp)
+ } else {
+ s.Add(n1.Type, nil, jmp)
}
- continue
}
- // combine adjacent cases with the same hash
- var batch []caseClause
- for i, j := 0, 0; i < run; i = j {
- hash := []*Node{cc[i].node.Right}
- for j = i + 1; j < run && cc[i].hash == cc[j].hash; j++ {
- hash = append(hash, cc[j].node.Right)
+ body.Append(npos(ncase.Pos, nodSym(OLABEL, nil, label)))
+ if caseVar != nil && !singleType {
+ l := []*Node{
+ nodl(ncase.Pos, ODCL, caseVar, nil),
+ nodl(ncase.Pos, OAS, caseVar, s.facename),
}
- cc[i].node.Right = liststmt(hash)
- batch = append(batch, cc[i])
+ typecheckslice(l, ctxStmt)
+ body.Append(l...)
}
+ body.Append(ncase.Nbody.Slice()...)
+ body.Append(br)
+ }
+ sw.List.Set(nil)
- // binary search among cases to narrow by hash
- cas = append(cas, s.walkCases(batch))
- cc = cc[run:]
+ if defaultGoto == nil {
+ defaultGoto = br
}
- // handle default case
- if nerrors == 0 {
- cas = append(cas, def)
- sw.Nbody.Prepend(cas...)
- sw.List.Set(nil)
- walkstmtlist(sw.Nbody.Slice())
+ if nilGoto != nil {
+ ifNil.Nbody.Set1(nilGoto)
+ } else {
+ // TODO(mdempsky): Just use defaultGoto directly.
+
+ // Jump to default case.
+ label := autolabel(".s")
+ ifNil.Nbody.Set1(nodSym(OGOTO, nil, label))
+ // Wrap default case with label.
+ blk := nod(OBLOCK, nil, nil)
+ blk.List.Set2(nodSym(OLABEL, nil, label), defaultGoto)
+ defaultGoto = blk
}
+
+ s.Emit(&sw.Nbody)
+ sw.Nbody.Append(defaultGoto)
+ sw.Nbody.AppendNodes(&body)
+
+ walkstmtlist(sw.Nbody.Slice())
}
-// typeone generates an AST that jumps to the
-// case body if the variable is of type t.
-func (s *typeSwitch) typeone(t *Node) *Node {
- var name *Node
- var init Nodes
- if t.Rlist.Len() == 0 {
- name = nblank
- nblank = typecheck(nblank, ctxExpr|ctxAssign)
- } else {
- name = t.Rlist.First()
- init.Append(nod(ODCL, name, nil))
- a := nod(OAS, name, nil)
- a = typecheck(a, ctxStmt)
- init.Append(a)
- }
-
- a := nod(OAS2, nil, nil)
- a.List.Set2(name, s.okname) // name, ok =
- b := nod(ODOTTYPE, s.facename, nil)
- b.Type = t.Left.Type // interface.(type)
- a.Rlist.Set1(b)
- a = typecheck(a, ctxStmt)
- a = walkexpr(a, &init)
- init.Append(a)
-
- c := nod(OIF, nil, nil)
- c.Left = s.okname
- c.Nbody.Set1(t.Right) // if ok { goto l }
-
- init.Append(c)
- return init.asblock()
+// A typeSwitch walks a type switch.
+type typeSwitch struct {
+ // Temporary variables (i.e., ONAMEs) used by type switch dispatch logic:
+ facename *Node // value being type-switched on
+ hashname *Node // type hash of the value being type-switched on
+ okname *Node // boolean used for comma-ok type assertions
+
+ done Nodes
+ clauses []typeClause
}
-// walkCases generates an AST implementing the cases in cc.
-func (s *typeSwitch) walkCases(cc []caseClause) *Node {
- if len(cc) < binarySearchMin {
- var cas []*Node
- for _, c := range cc {
- n := c.node
- if !c.isconst {
- Fatalf("typeSwitch walkCases")
- }
- a := nod(OIF, nil, nil)
- a.Left = nod(OEQ, s.hashname, nodintconst(int64(c.hash)))
- a.Left = typecheck(a.Left, ctxExpr)
- a.Left = defaultlit(a.Left, nil)
- a.Nbody.Set1(n.Right)
- cas = append(cas, a)
- }
- return liststmt(cas)
- }
-
- // find the middle and recur
- half := len(cc) / 2
- a := nod(OIF, nil, nil)
- a.Left = nod(OLE, s.hashname, nodintconst(int64(cc[half-1].hash)))
- a.Left = typecheck(a.Left, ctxExpr)
- a.Left = defaultlit(a.Left, nil)
- a.Nbody.Set1(s.walkCases(cc[:half]))
- a.Rlist.Set1(s.walkCases(cc[half:]))
- return a
+type typeClause struct {
+ hash uint32
+ body Nodes
}
-// caseClauseByConstVal sorts clauses by constant value to enable binary search.
-type caseClauseByConstVal []caseClause
-
-func (x caseClauseByConstVal) Len() int { return len(x) }
-func (x caseClauseByConstVal) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
-func (x caseClauseByConstVal) Less(i, j int) bool {
- // n1 and n2 might be individual constants or integer ranges.
- // We have checked for duplicates already,
- // so ranges can be safely represented by any value in the range.
- n1 := x[i].node
- var v1 interface{}
- if s := n1.List.Slice(); s != nil {
- v1 = s[0].Val().U
+func (s *typeSwitch) Add(typ *types.Type, caseVar *Node, jmp *Node) {
+ var body Nodes
+ if caseVar != nil {
+ l := []*Node{
+ nod(ODCL, caseVar, nil),
+ nod(OAS, caseVar, nil),
+ }
+ typecheckslice(l, ctxStmt)
+ body.Append(l...)
} else {
- v1 = n1.Left.Val().U
+ caseVar = nblank
+ }
+
+ // cv, ok = iface.(type)
+ as := nod(OAS2, nil, nil)
+ as.List.Set2(caseVar, s.okname) // cv, ok =
+ dot := nod(ODOTTYPE, s.facename, nil)
+ dot.Type = typ // iface.(type)
+ as.Rlist.Set1(dot)
+ as = typecheck(as, ctxStmt)
+ as = walkexpr(as, &body)
+ body.Append(as)
+
+ // if ok { goto label }
+ nif := nod(OIF, nil, nil)
+ nif.Left = s.okname
+ nif.Nbody.Set1(jmp)
+ body.Append(nif)
+
+ if !typ.IsInterface() {
+ s.clauses = append(s.clauses, typeClause{
+ hash: typehash(typ),
+ body: body,
+ })
+ return
}
- n2 := x[j].node
- var v2 interface{}
- if s := n2.List.Slice(); s != nil {
- v2 = s[0].Val().U
- } else {
- v2 = n2.Left.Val().U
- }
-
- switch v1 := v1.(type) {
- case *Mpflt:
- return v1.Cmp(v2.(*Mpflt)) < 0
- case *Mpint:
- return v1.Cmp(v2.(*Mpint)) < 0
- case string:
- // Sort strings by length and then by value.
- // It is much cheaper to compare lengths than values,
- // and all we need here is consistency.
- // We respect this sorting in exprSwitch.walkCases.
- a := v1
- b := v2.(string)
- if len(a) != len(b) {
- return len(a) < len(b)
- }
- return a < b
- }
+ s.flush()
+ s.done.AppendNodes(&body)
+}
- Fatalf("caseClauseByConstVal passed bad clauses %v < %v", x[i].node.Left, x[j].node.Left)
- return false
+func (s *typeSwitch) Emit(out *Nodes) {
+ s.flush()
+ out.AppendNodes(&s.done)
}
-type caseClauseByType []caseClause
+func (s *typeSwitch) flush() {
+ cc := s.clauses
+ s.clauses = nil
+ if len(cc) == 0 {
+ return
+ }
+
+ sort.Slice(cc, func(i, j int) bool { return cc[i].hash < cc[j].hash })
-func (x caseClauseByType) Len() int { return len(x) }
-func (x caseClauseByType) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
-func (x caseClauseByType) Less(i, j int) bool {
- c1, c2 := x[i], x[j]
- // sort by hash code, then ordinal (for the rare case of hash collisions)
- if c1.hash != c2.hash {
- return c1.hash < c2.hash
+ // Combine adjacent cases with the same hash.
+ merged := cc[:1]
+ for _, c := range cc[1:] {
+ last := &merged[len(merged)-1]
+ if last.hash == c.hash {
+ last.body.AppendNodes(&c.body)
+ } else {
+ merged = append(merged, c)
+ }
}
- return c1.ordinal < c2.ordinal
+ cc = merged
+
+ binarySearch(len(cc), &s.done,
+ func(i int) *Node {
+ return nod(OLE, s.hashname, nodintconst(int64(cc[i-1].hash)))
+ },
+ func(i int, out *Nodes) {
+ // TODO(mdempsky): Omit hash equality check if
+ // there's only one type.
+ c := cc[i]
+ a := nod(OIF, nil, nil)
+ a.Left = nod(OEQ, s.hashname, nodintconst(int64(c.hash)))
+ a.Left = typecheck(a.Left, ctxExpr)
+ a.Left = defaultlit(a.Left, nil)
+ a.Nbody.AppendNodes(&c.body)
+ out.Append(a)
+ },
+ )
}
-type constIntNodesByVal []*Node
+// binarySearch constructs a binary search tree for handling n cases,
+// and appends it to out. It's used for efficiently implementing
+// switch statements.
+//
+// less(i) should return a boolean expression. If it evaluates true,
+// then cases [0, i) will be tested; otherwise, cases [i, n).
+//
+// base(i, out) should append statements to out to test the i'th case.
+func binarySearch(n int, out *Nodes, less func(i int) *Node, base func(i int, out *Nodes)) {
+ const binarySearchMin = 4 // minimum number of cases for binary search
+
+ var do func(lo, hi int, out *Nodes)
+ do = func(lo, hi int, out *Nodes) {
+ n := hi - lo
+ if n < binarySearchMin {
+ for i := lo; i < hi; i++ {
+ base(i, out)
+ }
+ return
+ }
+
+ half := lo + n/2
+ nif := nod(OIF, nil, nil)
+ nif.Left = less(half)
+ nif.Left = typecheck(nif.Left, ctxExpr)
+ nif.Left = defaultlit(nif.Left, nil)
+ do(lo, half, &nif.Nbody)
+ do(half, hi, &nif.Rlist)
+ out.Append(nif)
+ }
-func (x constIntNodesByVal) Len() int { return len(x) }
-func (x constIntNodesByVal) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
-func (x constIntNodesByVal) Less(i, j int) bool {
- return x[i].Val().U.(*Mpint).Cmp(x[j].Val().U.(*Mpint)) < 0
+ do(0, n, out)
}
diff --git a/src/cmd/compile/internal/gc/swt_test.go b/src/cmd/compile/internal/gc/swt_test.go
deleted file mode 100644
index 2f73ef7b99..0000000000
--- a/src/cmd/compile/internal/gc/swt_test.go
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2015 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 gc
-
-import (
- "testing"
-)
-
-func nodrune(r rune) *Node {
- v := new(Mpint)
- v.SetInt64(int64(r))
- v.Rune = true
- return nodlit(Val{v})
-}
-
-func nodflt(f float64) *Node {
- v := newMpflt()
- v.SetFloat64(f)
- return nodlit(Val{v})
-}
-
-func TestCaseClauseByConstVal(t *testing.T) {
- tests := []struct {
- a, b *Node
- }{
- // CTFLT
- {nodflt(0.1), nodflt(0.2)},
- // CTINT
- {nodintconst(0), nodintconst(1)},
- // CTRUNE
- {nodrune('a'), nodrune('b')},
- // CTSTR
- {nodlit(Val{"ab"}), nodlit(Val{"abc"})},
- {nodlit(Val{"ab"}), nodlit(Val{"xyz"})},
- {nodlit(Val{"abc"}), nodlit(Val{"xyz"})},
- }
- for i, test := range tests {
- a := caseClause{node: nod(OXXX, test.a, nil)}
- b := caseClause{node: nod(OXXX, test.b, nil)}
- s := caseClauseByConstVal{a, b}
- if less := s.Less(0, 1); !less {
- t.Errorf("%d: caseClauseByConstVal(%v, %v) = false", i, test.a, test.b)
- }
- if less := s.Less(1, 0); less {
- t.Errorf("%d: caseClauseByConstVal(%v, %v) = true", i, test.a, test.b)
- }
- }
-}
From 606fa2db7a7cd80292fca7aab6c1787fa274e52b Mon Sep 17 00:00:00 2001
From: Robert Griesemer
Date: Mon, 16 Sep 2019 16:43:51 -0700
Subject: [PATCH 018/199] go/types: remove unused pathString and rename
objPathString to pathString (cleanup)
This eliminates an old TODO.
Change-Id: I36d666905f43252f5d338b11ef9c1ed8b5f22b1f
Reviewed-on: https://go-review.googlesource.com/c/go/+/195817
Run-TryBot: Robert Griesemer
TryBot-Result: Gobot Gobot
Reviewed-by: Ian Lance Taylor
---
src/go/types/decl.go | 21 +++------------------
1 file changed, 3 insertions(+), 18 deletions(-)
diff --git a/src/go/types/decl.go b/src/go/types/decl.go
index a13442c951..11d2ee4596 100644
--- a/src/go/types/decl.go
+++ b/src/go/types/decl.go
@@ -38,22 +38,7 @@ func (check *Checker) declare(scope *Scope, id *ast.Ident, obj Object, pos token
}
// pathString returns a string of the form a->b-> ... ->g for a path [a, b, ... g].
-// TODO(gri) remove once we don't need the old cycle detection (explicitly passed
-// []*TypeName path) anymore
-func pathString(path []*TypeName) string {
- var s string
- for i, p := range path {
- if i > 0 {
- s += "->"
- }
- s += p.Name()
- }
- return s
-}
-
-// objPathString returns a string of the form a->b-> ... ->g for a path [a, b, ... g].
-// TODO(gri) s/objPathString/pathString/ once we got rid of pathString above
-func objPathString(path []Object) string {
+func pathString(path []Object) string {
var s string
for i, p := range path {
if i > 0 {
@@ -68,7 +53,7 @@ func objPathString(path []Object) string {
// For the meaning of def, see Checker.definedType, in typexpr.go.
func (check *Checker) objDecl(obj Object, def *Named) {
if trace {
- check.trace(obj.Pos(), "-- checking %s %s (objPath = %s)", obj.color(), obj, objPathString(check.objPath))
+ check.trace(obj.Pos(), "-- checking %s %s (objPath = %s)", obj.color(), obj, pathString(check.objPath))
check.indent++
defer func() {
check.indent--
@@ -291,7 +276,7 @@ func (check *Checker) typeCycle(obj Object) (isCycle bool) {
}
if trace {
- check.trace(obj.Pos(), "## cycle detected: objPath = %s->%s (len = %d)", objPathString(cycle), obj.Name(), ncycle)
+ check.trace(obj.Pos(), "## cycle detected: objPath = %s->%s (len = %d)", pathString(cycle), obj.Name(), ncycle)
check.trace(obj.Pos(), "## cycle contains: %d values, has indirection = %v, has type definition = %v", nval, hasIndir, hasTDef)
defer func() {
if isCycle {
From c3c53661ba8823ea7a051110aebbdea2650c25d0 Mon Sep 17 00:00:00 2001
From: Joel Sing
Date: Sun, 8 Sep 2019 04:11:07 +1000
Subject: [PATCH 019/199] cmd/asm,cmd/internal/obj/riscv: implement integer
computational instructions
Add support for assembling integer computational instructions.
Based on the riscv-go port.
Updates #27532
Change-Id: Ibf02649eebd65ce96002a9ca0624266d96def2cd
Reviewed-on: https://go-review.googlesource.com/c/go/+/195079
Run-TryBot: Joel Sing
TryBot-Result: Gobot Gobot
Reviewed-by: Cherry Zhang
---
src/cmd/asm/internal/asm/asm.go | 4 +-
src/cmd/asm/internal/asm/testdata/riscvenc.s | 68 ++++++
src/cmd/internal/obj/riscv/obj.go | 209 ++++++++++++++++++-
3 files changed, 278 insertions(+), 3 deletions(-)
diff --git a/src/cmd/asm/internal/asm/asm.go b/src/cmd/asm/internal/asm/asm.go
index d83cfb2284..c6f07832a7 100644
--- a/src/cmd/asm/internal/asm/asm.go
+++ b/src/cmd/asm/internal/asm/asm.go
@@ -417,7 +417,7 @@ func (p *Parser) asmJump(op obj.As, cond string, a []obj.Addr) {
prog.Reg = reg
break
}
- if p.arch.Family == sys.MIPS || p.arch.Family == sys.MIPS64 {
+ if p.arch.Family == sys.MIPS || p.arch.Family == sys.MIPS64 || p.arch.Family == sys.RISCV64 {
// 3-operand jumps.
// First two must be registers
target = &a[2]
@@ -579,7 +579,7 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
prog.To = a[1]
case 3:
switch p.arch.Family {
- case sys.MIPS, sys.MIPS64:
+ case sys.MIPS, sys.MIPS64, sys.RISCV64:
prog.From = a[0]
prog.Reg = p.getRegister(prog, op, &a[1])
prog.To = a[2]
diff --git a/src/cmd/asm/internal/asm/testdata/riscvenc.s b/src/cmd/asm/internal/asm/testdata/riscvenc.s
index eea5738f2c..c05a05ea33 100644
--- a/src/cmd/asm/internal/asm/testdata/riscvenc.s
+++ b/src/cmd/asm/internal/asm/testdata/riscvenc.s
@@ -9,3 +9,71 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
// Arbitrary bytes (entered in little-endian mode)
WORD $0x12345678 // WORD $305419896 // 78563412
WORD $0x9abcdef0 // WORD $2596069104 // f0debc9a
+
+ // Unprivileged ISA
+
+ // 2.4: Integer Computational Instructions
+
+ ADDI $2047, X5, X6 // 1383f27f
+ ADDI $-2048, X5, X6 // 13830280
+ ADDI $2047, X5 // 9382f27f
+ ADDI $-2048, X5 // 93820280
+
+ SLTI $55, X5, X7 // 93a37203
+ SLTIU $55, X5, X7 // 93b37203
+
+ ANDI $1, X5, X6 // 13f31200
+ ANDI $1, X5 // 93f21200
+ ORI $1, X5, X6 // 13e31200
+ ORI $1, X5 // 93e21200
+ XORI $1, X5, X6 // 13c31200
+ XORI $1, X5 // 93c21200
+
+ SLLI $1, X5, X6 // 13931200
+ SLLI $1, X5 // 93921200
+ SRLI $1, X5, X6 // 13d31200
+ SRLI $1, X5 // 93d21200
+ SRAI $1, X5, X6 // 13d31240
+ SRAI $1, X5 // 93d21240
+
+ ADD X6, X5, X7 // b3836200
+ ADD X5, X6 // 33035300
+ ADD $2047, X5, X6 // 1383f27f
+ ADD $-2048, X5, X6 // 13830280
+ ADD $2047, X5 // 9382f27f
+ ADD $-2048, X5 // 93820280
+
+ SLT X6, X5, X7 // b3a36200
+ SLT $55, X5, X7 // 93a37203
+ SLTU X6, X5, X7 // b3b36200
+ SLTU $55, X5, X7 // 93b37203
+
+ AND X6, X5, X7 // b3f36200
+ AND X5, X6 // 33735300
+ AND $1, X5, X6 // 13f31200
+ AND $1, X5 // 93f21200
+ OR X6, X5, X7 // b3e36200
+ OR X5, X6 // 33635300
+ OR $1, X5, X6 // 13e31200
+ OR $1, X5 // 93e21200
+ XOR X6, X5, X7 // b3c36200
+ XOR X5, X6 // 33435300
+ XOR $1, X5, X6 // 13c31200
+ XOR $1, X5 // 93c21200
+
+ SLL X6, X5, X7 // b3936200
+ SLL X5, X6 // 33135300
+ SLL $1, X5, X6 // 13931200
+ SLL $1, X5 // 93921200
+ SRL X6, X5, X7 // b3d36200
+ SRL X5, X6 // 33535300
+ SRL $1, X5, X6 // 13d31200
+ SRL $1, X5 // 93d21200
+
+ SUB X6, X5, X7 // b3836240
+ SUB X5, X6 // 33035340
+
+ SRA X6, X5, X7 // b3d36240
+ SRA X5, X6 // 33535340
+ SRA $1, X5, X6 // 13d31240
+ SRA $1, X5 // 93d21240
diff --git a/src/cmd/internal/obj/riscv/obj.go b/src/cmd/internal/obj/riscv/obj.go
index af07522cfd..3ce8cb8982 100644
--- a/src/cmd/internal/obj/riscv/obj.go
+++ b/src/cmd/internal/obj/riscv/obj.go
@@ -31,8 +31,43 @@ var RISCV64DWARFRegisters = map[int16]int16{}
func buildop(ctxt *obj.Link) {}
+// progedit is called individually for each *obj.Prog. It normalizes instruction
+// formats and eliminates as many pseudo-instructions as possible.
func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
- // TODO(jsing): Implement.
+
+ // Expand binary instructions to ternary ones.
+ if p.Reg == 0 {
+ switch p.As {
+ case AADDI, ASLTI, ASLTIU, AANDI, AORI, AXORI, ASLLI, ASRLI, ASRAI,
+ AADD, AAND, AOR, AXOR, ASLL, ASRL, ASUB, ASRA:
+ p.Reg = p.To.Reg
+ }
+ }
+
+ // Rewrite instructions with constant operands to refer to the immediate
+ // form of the instruction.
+ if p.From.Type == obj.TYPE_CONST {
+ switch p.As {
+ case AADD:
+ p.As = AADDI
+ case ASLT:
+ p.As = ASLTI
+ case ASLTU:
+ p.As = ASLTIU
+ case AAND:
+ p.As = AANDI
+ case AOR:
+ p.As = AORI
+ case AXOR:
+ p.As = AXORI
+ case ASLL:
+ p.As = ASLLI
+ case ASRL:
+ p.As = ASRLI
+ case ASRA:
+ p.As = ASRAI
+ }
+ }
}
// setPCs sets the Pc field in all instructions reachable from p.
@@ -83,6 +118,103 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
}
}
+func regVal(r, min, max int16) uint32 {
+ if r < min || r > max {
+ panic(fmt.Sprintf("register out of range, want %d < %d < %d", min, r, max))
+ }
+ return uint32(r - min)
+}
+
+// regI returns an integer register.
+func regI(r int16) uint32 {
+ return regVal(r, REG_X0, REG_X31)
+}
+
+// regAddr extracts a register from an Addr.
+func regAddr(a obj.Addr, min, max int16) uint32 {
+ if a.Type != obj.TYPE_REG {
+ panic(fmt.Sprintf("ill typed: %+v", a))
+ }
+ return regVal(a.Reg, min, max)
+}
+
+// regIAddr extracts the integer register from an Addr.
+func regIAddr(a obj.Addr) uint32 {
+ return regAddr(a, REG_X0, REG_X31)
+}
+
+// immFits reports whether immediate value x fits in nbits bits as a
+// signed integer.
+func immFits(x int64, nbits uint) bool {
+ nbits--
+ var min int64 = -1 << nbits
+ var max int64 = 1< max {
+ p.Ctxt.Diag("%v\texpected %s register in %s position but got non-%s register %s", p, descr, pos, descr, regName(int(r)))
+ }
+}
+
+// wantIntReg checks that r is an integer register.
+func wantIntReg(p *obj.Prog, pos string, r int16) {
+ wantReg(p, pos, "integer", r, REG_X0, REG_X31)
+}
+
+func wantRegAddr(p *obj.Prog, pos string, a *obj.Addr, descr string, min int16, max int16) {
+ if a == nil {
+ p.Ctxt.Diag("%v\texpected register in %s position but got nothing", p, pos)
+ return
+ }
+ if a.Type != obj.TYPE_REG {
+ p.Ctxt.Diag("%v\texpected register in %s position but got %s", p, pos, obj.Dconv(p, a))
+ return
+ }
+ if a.Reg < min || a.Reg > max {
+ p.Ctxt.Diag("%v\texpected %s register in %s position but got non-%s register %s", p, descr, pos, descr, obj.Dconv(p, a))
+ }
+}
+
+// wantIntRegAddr checks that a contains an integer register.
+func wantIntRegAddr(p *obj.Prog, pos string, a *obj.Addr) {
+ wantRegAddr(p, pos, a, "integer", REG_X0, REG_X31)
+}
+
+func validateRIII(p *obj.Prog) {
+ wantIntRegAddr(p, "from", &p.From)
+ wantIntReg(p, "reg", p.Reg)
+ wantIntRegAddr(p, "to", &p.To)
+}
+
+func validateII(p *obj.Prog) {
+ wantImm(p, "from", p.From, 12)
+ wantIntReg(p, "reg", p.Reg)
+ wantIntRegAddr(p, "to", &p.To)
+}
+
func validateRaw(p *obj.Prog) {
// Treat the raw value specially as a 32-bit unsigned integer.
// Nobody wants to enter negative machine code.
@@ -96,6 +228,42 @@ func validateRaw(p *obj.Prog) {
}
}
+// encodeR encodes an R-type RISC-V instruction.
+func encodeR(p *obj.Prog, rs1 uint32, rs2 uint32, rd uint32) uint32 {
+ ins := encode(p.As)
+ if ins == nil {
+ panic("encodeR: could not encode instruction")
+ }
+ if ins.rs2 != 0 && rs2 != 0 {
+ panic("encodeR: instruction uses rs2, but rs2 was nonzero")
+ }
+
+ // Use Scond for the floating-point rounding mode override.
+ // TODO(sorear): Is there a more appropriate way to handle opcode extension bits like this?
+ return ins.funct7<<25 | ins.rs2<<20 | rs2<<20 | rs1<<15 | ins.funct3<<12 | uint32(p.Scond)<<12 | rd<<7 | ins.opcode
+}
+
+func encodeRIII(p *obj.Prog) uint32 {
+ return encodeR(p, regI(p.Reg), regIAddr(p.From), regIAddr(p.To))
+}
+
+// encodeI encodes an I-type RISC-V instruction.
+func encodeI(p *obj.Prog, rd uint32) uint32 {
+ imm := immI(p.From, 12)
+ rs1 := regI(p.Reg)
+ ins := encode(p.As)
+ if ins == nil {
+ panic("encodeI: could not encode instruction")
+ }
+ imm |= uint32(ins.csr)
+ return imm<<20 | rs1<<15 | ins.funct3<<12 | rd<<7 | ins.opcode
+}
+
+func encodeII(p *obj.Prog) uint32 {
+ return encodeI(p, regIAddr(p.To))
+}
+
+// encodeRaw encodes a raw instruction value.
func encodeRaw(p *obj.Prog) uint32 {
// Treat the raw value specially as a 32-bit unsigned integer.
// Nobody wants to enter negative machine code.
@@ -116,6 +284,22 @@ type encoding struct {
}
var (
+ // Encodings have the following naming convention:
+ //
+ // 1. the instruction encoding (R/I/S/SB/U/UJ), in lowercase
+ // 2. zero or more register operand identifiers (I = integer
+ // register, F = float register), in uppercase
+ // 3. the word "Encoding"
+ //
+ // For example, rIIIEncoding indicates an R-type instruction with two
+ // integer register inputs and an integer register output; sFEncoding
+ // indicates an S-type instruction with rs2 being a float register.
+
+ rIIIEncoding = encoding{encode: encodeRIII, validate: validateRIII, length: 4}
+
+ iIEncoding = encoding{encode: encodeII, validate: validateII, length: 4}
+
+ // rawEncoding encodes a raw instruction byte sequence.
rawEncoding = encoding{encode: encodeRaw, validate: validateRaw, length: 4}
// pseudoOpEncoding panics if encoding is attempted, but does no validation.
@@ -131,6 +315,29 @@ var (
var encodingForAs = [ALAST & obj.AMask]encoding{
// TODO(jsing): Implement remaining instructions.
+ // Unprivileged ISA
+
+ // 2.4: Integer Computational Instructions
+ AADDI & obj.AMask: iIEncoding,
+ ASLTI & obj.AMask: iIEncoding,
+ ASLTIU & obj.AMask: iIEncoding,
+ AANDI & obj.AMask: iIEncoding,
+ AORI & obj.AMask: iIEncoding,
+ AXORI & obj.AMask: iIEncoding,
+ ASLLI & obj.AMask: iIEncoding,
+ ASRLI & obj.AMask: iIEncoding,
+ ASRAI & obj.AMask: iIEncoding,
+ AADD & obj.AMask: rIIIEncoding,
+ ASLT & obj.AMask: rIIIEncoding,
+ ASLTU & obj.AMask: rIIIEncoding,
+ AAND & obj.AMask: rIIIEncoding,
+ AOR & obj.AMask: rIIIEncoding,
+ AXOR & obj.AMask: rIIIEncoding,
+ ASLL & obj.AMask: rIIIEncoding,
+ ASRL & obj.AMask: rIIIEncoding,
+ ASUB & obj.AMask: rIIIEncoding,
+ ASRA & obj.AMask: rIIIEncoding,
+
// Escape hatch
AWORD & obj.AMask: rawEncoding,
From 38543c2813a1075e09693894625421309d8ef333 Mon Sep 17 00:00:00 2001
From: Tamir Duberstein
Date: Fri, 17 May 2019 12:55:17 -0400
Subject: [PATCH 020/199] net: avoid transiting durations through floats
This slightly simplified the code. I stumbled upon this when support was
being added to Fuchsia (and this pattern was initially cargo-culted).
Change-Id: Ica090a118a0056c5c1b51697691bc7308f0d424a
Reviewed-on: https://go-review.googlesource.com/c/go/+/177878
Reviewed-by: Brad Fitzpatrick
Run-TryBot: Brad Fitzpatrick
TryBot-Result: Gobot Gobot
---
src/net/tcpsock.go | 5 +++++
src/net/tcpsockopt_darwin.go | 3 +--
src/net/tcpsockopt_dragonfly.go | 3 +--
src/net/tcpsockopt_solaris.go | 3 +--
src/net/tcpsockopt_unix.go | 3 +--
src/net/tcpsockopt_windows.go | 3 +--
6 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/src/net/tcpsock.go b/src/net/tcpsock.go
index 0daa2f6487..b7b73d0d81 100644
--- a/src/net/tcpsock.go
+++ b/src/net/tcpsock.go
@@ -337,3 +337,8 @@ func ListenTCP(network string, laddr *TCPAddr) (*TCPListener, error) {
}
return ln, nil
}
+
+// roundDurationUp rounds d to the next multiple of to.
+func roundDurationUp(d time.Duration, to time.Duration) time.Duration {
+ return (d + to - 1) / to
+}
diff --git a/src/net/tcpsockopt_darwin.go b/src/net/tcpsockopt_darwin.go
index da0d173453..53c6756e33 100644
--- a/src/net/tcpsockopt_darwin.go
+++ b/src/net/tcpsockopt_darwin.go
@@ -15,8 +15,7 @@ const sysTCP_KEEPINTVL = 0x101
func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
// The kernel expects seconds so round to next highest second.
- d += (time.Second - time.Nanosecond)
- secs := int(d.Seconds())
+ secs := int(roundDurationUp(d, time.Second))
if err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, sysTCP_KEEPINTVL, secs); err != nil {
return wrapSyscallError("setsockopt", err)
}
diff --git a/src/net/tcpsockopt_dragonfly.go b/src/net/tcpsockopt_dragonfly.go
index 2b018f2bb2..b473c02b68 100644
--- a/src/net/tcpsockopt_dragonfly.go
+++ b/src/net/tcpsockopt_dragonfly.go
@@ -13,8 +13,7 @@ import (
func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
// The kernel expects milliseconds so round to next highest
// millisecond.
- d += (time.Millisecond - time.Nanosecond)
- msecs := int(d / time.Millisecond)
+ msecs := int(roundDurationUp(d, time.Millisecond))
if err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, msecs); err != nil {
return wrapSyscallError("setsockopt", err)
}
diff --git a/src/net/tcpsockopt_solaris.go b/src/net/tcpsockopt_solaris.go
index 019fe349eb..f15e589dc0 100644
--- a/src/net/tcpsockopt_solaris.go
+++ b/src/net/tcpsockopt_solaris.go
@@ -13,8 +13,7 @@ import (
func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
// The kernel expects milliseconds so round to next highest
// millisecond.
- d += (time.Millisecond - time.Nanosecond)
- msecs := int(d / time.Millisecond)
+ msecs := int(roundDurationUp(d, time.Millisecond))
// Normally we'd do
// syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, secs)
diff --git a/src/net/tcpsockopt_unix.go b/src/net/tcpsockopt_unix.go
index d5892588fe..fb0ecb8dc7 100644
--- a/src/net/tcpsockopt_unix.go
+++ b/src/net/tcpsockopt_unix.go
@@ -14,8 +14,7 @@ import (
func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
// The kernel expects seconds so round to next highest second.
- d += (time.Second - time.Nanosecond)
- secs := int(d.Seconds())
+ secs := int(roundDurationUp(d, time.Second))
if err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, secs); err != nil {
return wrapSyscallError("setsockopt", err)
}
diff --git a/src/net/tcpsockopt_windows.go b/src/net/tcpsockopt_windows.go
index 73dead11d0..4a0b09465e 100644
--- a/src/net/tcpsockopt_windows.go
+++ b/src/net/tcpsockopt_windows.go
@@ -15,8 +15,7 @@ import (
func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
// The kernel expects milliseconds so round to next highest
// millisecond.
- d += (time.Millisecond - time.Nanosecond)
- msecs := uint32(d / time.Millisecond)
+ msecs := uint32(roundDurationUp(d, time.Millisecond))
ka := syscall.TCPKeepalive{
OnOff: 1,
Time: msecs,
From 07ad84009865b15d68cba036610d04c66bd3f5e8 Mon Sep 17 00:00:00 2001
From: Keith Randall
Date: Mon, 16 Sep 2019 16:53:23 -0700
Subject: [PATCH 021/199] runtime: remove unneeded noinline directives
Now that mid-stack inlining reports backtraces correctly, we no
longer need to protect against inlining in a few critical areas.
Update #19348
Update #28640
Update #34276
Change-Id: Ie68487e6482c3a9509ecf7ecbbd40fe43cee8381
Reviewed-on: https://go-review.googlesource.com/c/go/+/195818
Reviewed-by: David Chase
---
src/runtime/extern.go | 1 -
src/runtime/stack_test.go | 3 ---
2 files changed, 4 deletions(-)
diff --git a/src/runtime/extern.go b/src/runtime/extern.go
index 2917efefa6..4ddf3549e6 100644
--- a/src/runtime/extern.go
+++ b/src/runtime/extern.go
@@ -200,7 +200,6 @@ func Caller(skip int) (pc uintptr, file string, line int, ok bool) {
// directly is discouraged, as is using FuncForPC on any of the
// returned PCs, since these cannot account for inlining or return
// program counter adjustment.
-//go:noinline
func Callers(skip int, pc []uintptr) int {
// runtime.callers uses pc.array==nil as a signal
// to print a stack trace. Pick off 0-length pc here
diff --git a/src/runtime/stack_test.go b/src/runtime/stack_test.go
index 143d3a99a0..adfc65384a 100644
--- a/src/runtime/stack_test.go
+++ b/src/runtime/stack_test.go
@@ -599,9 +599,6 @@ func (s structWithMethod) callers() []uintptr {
return pc[:Callers(0, pc)]
}
-// The noinline prevents this function from being inlined
-// into a wrapper. TODO: remove this when issue 28640 is fixed.
-//go:noinline
func (s structWithMethod) stack() string {
buf := make([]byte, 4<<10)
return string(buf[:Stack(buf, false)])
From ec4e8517cd17aaa2c4224815444e7d28c81ec673 Mon Sep 17 00:00:00 2001
From: LE Manh Cuong
Date: Mon, 17 Jun 2019 01:15:53 +0700
Subject: [PATCH 022/199] cmd/compile: support more length types for slice
extension optimization
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
golang.org/cl/109517 optimized the compiler to avoid the allocation for make in
append(x, make([]T, y)...). This was only implemented for the case that y has type int.
This change extends the optimization to trigger for all integer types where the value
is known at compile time to fit into an int.
name old time/op new time/op delta
ExtendInt-12 106ns ± 4% 106ns ± 0% ~ (p=0.351 n=10+6)
ExtendUint64-12 1.03µs ± 5% 0.10µs ± 4% -90.01% (p=0.000 n=9+10)
name old alloc/op new alloc/op delta
ExtendInt-12 0.00B 0.00B ~ (all equal)
ExtendUint64-12 13.6kB ± 0% 0.0kB -100.00% (p=0.000 n=10+10)
name old allocs/op new allocs/op delta
ExtendInt-12 0.00 0.00 ~ (all equal)
ExtendUint64-12 1.00 ± 0% 0.00 -100.00% (p=0.000 n=10+10)
Updates #29785
Change-Id: Ief7760097c285abd591712da98c5b02bc3961fcd
Reviewed-on: https://go-review.googlesource.com/c/go/+/182559
Run-TryBot: Cuong Manh Le
TryBot-Result: Gobot Gobot
Reviewed-by: Keith Randall
---
src/cmd/compile/internal/gc/walk.go | 19 ++++++-------
test/codegen/slices.go | 42 +++++++++++++++++++++++++++++
test/fixedbugs/issue4085b.go | 37 ++++++++++++++++++++-----
3 files changed, 83 insertions(+), 15 deletions(-)
diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go
index d2036b6e32..8dd60f4285 100644
--- a/src/cmd/compile/internal/gc/walk.go
+++ b/src/cmd/compile/internal/gc/walk.go
@@ -2700,15 +2700,14 @@ func isAppendOfMake(n *Node) bool {
return false
}
- // y must be either an integer constant or a variable of type int.
- // typecheck checks that constant arguments to make are not negative and
- // fit into an int.
- // runtime.growslice uses int as type for the newcap argument.
- // Constraining variables to be type int avoids the need for runtime checks
- // that e.g. check if an int64 value fits into an int.
- // TODO(moehrmann): support other integer types that always fit in an int
+ // y must be either an integer constant or the largest possible positive value
+ // of variable y needs to fit into an uint.
+
+ // typecheck made sure that constant arguments to make are not negative and fit into an int.
+
+ // The care of overflow of the len argument to make will be handled by an explicit check of int(len) < 0 during runtime.
y := second.Left
- if !Isconst(y, CTINT) && y.Type.Etype != TINT {
+ if !Isconst(y, CTINT) && maxintval[y.Type.Etype].Cmp(maxintval[TUINT]) > 0 {
return false
}
@@ -2742,7 +2741,9 @@ func isAppendOfMake(n *Node) bool {
// }
// s
func extendslice(n *Node, init *Nodes) *Node {
- // isAppendOfMake made sure l2 fits in an int.
+ // isAppendOfMake made sure all possible positive values of l2 fit into an uint.
+ // The case of l2 overflow when converting from e.g. uint to int is handled by an explicit
+ // check of l2 < 0 at runtime which is generated below.
l2 := conv(n.List.Second().Left, types.Types[TINT])
l2 = typecheck(l2, ctxExpr)
n.List.SetSecond(l2) // walkAppendArgs expects l2 in n.List.Second().
diff --git a/test/codegen/slices.go b/test/codegen/slices.go
index 6477c6f6c7..fccd711d71 100644
--- a/test/codegen/slices.go
+++ b/test/codegen/slices.go
@@ -44,6 +44,27 @@ func SliceExtensionConst(s []int) []int {
return append(s, make([]int, 1<<2)...)
}
+func SliceExtensionConstInt64(s []int) []int {
+ // amd64:`.*runtime\.memclrNoHeapPointers`
+ // amd64:-`.*runtime\.makeslice`
+ // amd64:-`.*runtime\.panicmakeslicelen`
+ return append(s, make([]int, int64(1<<2))...)
+}
+
+func SliceExtensionConstUint64(s []int) []int {
+ // amd64:`.*runtime\.memclrNoHeapPointers`
+ // amd64:-`.*runtime\.makeslice`
+ // amd64:-`.*runtime\.panicmakeslicelen`
+ return append(s, make([]int, uint64(1<<2))...)
+}
+
+func SliceExtensionConstUint(s []int) []int {
+ // amd64:`.*runtime\.memclrNoHeapPointers`
+ // amd64:-`.*runtime\.makeslice`
+ // amd64:-`.*runtime\.panicmakeslicelen`
+ return append(s, make([]int, uint(1<<2))...)
+}
+
func SliceExtensionPointer(s []*int, l int) []*int {
// amd64:`.*runtime\.memclrHasPointers`
// amd64:-`.*runtime\.makeslice`
@@ -56,6 +77,27 @@ func SliceExtensionVar(s []byte, l int) []byte {
return append(s, make([]byte, l)...)
}
+func SliceExtensionVarInt64(s []byte, l int64) []byte {
+ // amd64:`.*runtime\.memclrNoHeapPointers`
+ // amd64:-`.*runtime\.makeslice`
+ // amd64:`.*runtime\.panicmakeslicelen`
+ return append(s, make([]byte, l)...)
+}
+
+func SliceExtensionVarUint64(s []byte, l uint64) []byte {
+ // amd64:`.*runtime\.memclrNoHeapPointers`
+ // amd64:-`.*runtime\.makeslice`
+ // amd64:`.*runtime\.panicmakeslicelen`
+ return append(s, make([]byte, l)...)
+}
+
+func SliceExtensionVarUint(s []byte, l uint) []byte {
+ // amd64:`.*runtime\.memclrNoHeapPointers`
+ // amd64:-`.*runtime\.makeslice`
+ // amd64:`.*runtime\.panicmakeslicelen`
+ return append(s, make([]byte, l)...)
+}
+
func SliceExtensionInt64(s []int, l64 int64) []int {
// 386:`.*runtime\.makeslice`
// 386:-`.*runtime\.memclr`
diff --git a/test/fixedbugs/issue4085b.go b/test/fixedbugs/issue4085b.go
index 6304ce073a..cf27512da0 100644
--- a/test/fixedbugs/issue4085b.go
+++ b/test/fixedbugs/issue4085b.go
@@ -19,29 +19,36 @@ func main() {
shouldPanic("cap out of range", func() { _ = make(T, 0, n) })
shouldPanic("len out of range", func() { _ = make(T, int64(n)) })
shouldPanic("cap out of range", func() { _ = make(T, 0, int64(n)) })
+ testMakeInAppend(n)
+
var t *byte
if unsafe.Sizeof(t) == 8 {
// Test mem > maxAlloc
var n2 int64 = 1 << 59
shouldPanic("len out of range", func() { _ = make(T, int(n2)) })
shouldPanic("cap out of range", func() { _ = make(T, 0, int(n2)) })
+ testMakeInAppend(int(n2))
// Test elem.size*cap overflow
n2 = 1<<63 - 1
shouldPanic("len out of range", func() { _ = make(T, int(n2)) })
shouldPanic("cap out of range", func() { _ = make(T, 0, int(n2)) })
+ testMakeInAppend(int(n2))
+ var x uint64 = 1<<64 - 1
+ shouldPanic("len out of range", func() { _ = make([]byte, x) })
+ shouldPanic("cap out of range", func() { _ = make(T, 0, x) })
+ testMakeInAppend(int(x))
} else {
n = 1<<31 - 1
shouldPanic("len out of range", func() { _ = make(T, n) })
shouldPanic("cap out of range", func() { _ = make(T, 0, n) })
shouldPanic("len out of range", func() { _ = make(T, int64(n)) })
shouldPanic("cap out of range", func() { _ = make(T, 0, int64(n)) })
+ testMakeInAppend(n)
+ var x uint64 = 1<<32 - 1
+ shouldPanic("len out of range", func() { _ = make([]byte, x) })
+ shouldPanic("cap out of range", func() { _ = make(T, 0, x) })
+ testMakeInAppend(int(x))
}
-
- // Test make in append panics since the gc compiler optimizes makes in appends.
- shouldPanic("len out of range", func() { _ = append(T{}, make(T, n)...) })
- shouldPanic("cap out of range", func() { _ = append(T{}, make(T, 0, n)...) })
- shouldPanic("len out of range", func() { _ = append(T{}, make(T, int64(n))...) })
- shouldPanic("cap out of range", func() { _ = append(T{}, make(T, 0, int64(n))...) })
}
func shouldPanic(str string, f func()) {
@@ -58,3 +65,21 @@ func shouldPanic(str string, f func()) {
f()
}
+
+// Test make in append panics since the gc compiler optimizes makes in appends.
+func testMakeInAppend(n int) {
+ lengths := []int{0, 1}
+ for _, length := range lengths {
+ t := make(T, length)
+ shouldPanic("len out of range", func() { _ = append(t, make(T, n)...) })
+ shouldPanic("cap out of range", func() { _ = append(t, make(T, 0, n)...) })
+ shouldPanic("len out of range", func() { _ = append(t, make(T, int64(n))...) })
+ shouldPanic("cap out of range", func() { _ = append(t, make(T, 0, int64(n))...) })
+ shouldPanic("len out of range", func() { _ = append(t, make(T, uint64(n))...) })
+ shouldPanic("cap out of range", func() { _ = append(t, make(T, 0, uint64(n))...) })
+ shouldPanic("len out of range", func() { _ = append(t, make(T, int(n))...) })
+ shouldPanic("cap out of range", func() { _ = append(t, make(T, 0, int(n))...) })
+ shouldPanic("len out of range", func() { _ = append(t, make(T, uint(n))...) })
+ shouldPanic("cap out of range", func() { _ = append(t, make(T, 0, uint(n))...) })
+ }
+}
From 7d16e44d4f36fb37f43dc5318fbe13a1ba50425d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Mart=C3=AD?=
Date: Tue, 17 Sep 2019 14:39:54 +0100
Subject: [PATCH 023/199] cmd/compile: reduce the regexp work in rulegen
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
As correctly pointed out by Giovanni Bajo, doing a single regexp pass
should be much faster than doing hundreds per architecture. We can then
use a map to keep track of what ops are handled in each file. And the
amount of saved work is evident:
name old time/op new time/op delta
Rulegen 2.48s ± 1% 2.02s ± 1% -18.44% (p=0.008 n=5+5)
name old user-time/op new user-time/op delta
Rulegen 10.9s ± 1% 8.9s ± 0% -18.27% (p=0.008 n=5+5)
name old sys-time/op new sys-time/op delta
Rulegen 209ms ±28% 236ms ±18% ~ (p=0.310 n=5+5)
name old peak-RSS-bytes new peak-RSS-bytes delta
Rulegen 178MB ± 3% 176MB ± 3% ~ (p=0.548 n=5+5)
The speed-up is so large that we don't need to parallelize it anymore;
the numbers above are with the removed goroutines. Adding them back in
doesn't improve performance noticeably at all:
name old time/op new time/op delta
Rulegen 2.02s ± 1% 2.01s ± 1% ~ (p=0.421 n=5+5)
name old user-time/op new user-time/op delta
Rulegen 8.90s ± 0% 8.96s ± 1% ~ (p=0.095 n=5+5)
While at it, remove an unused method.
Change-Id: I328b56e63b64a9ab48147e67e7d5a385c795ec54
Reviewed-on: https://go-review.googlesource.com/c/go/+/195739
Run-TryBot: Daniel Martí
TryBot-Result: Gobot Gobot
Reviewed-by: Keith Randall
---
src/cmd/compile/internal/ssa/gen/main.go | 41 +++++++++++----------
src/cmd/compile/internal/ssa/gen/rulegen.go | 1 -
2 files changed, 21 insertions(+), 21 deletions(-)
diff --git a/src/cmd/compile/internal/ssa/gen/main.go b/src/cmd/compile/internal/ssa/gen/main.go
index 783ac1bd30..db40057743 100644
--- a/src/cmd/compile/internal/ssa/gen/main.go
+++ b/src/cmd/compile/internal/ssa/gen/main.go
@@ -392,34 +392,35 @@ func genOp() {
// Check that the arch genfile handles all the arch-specific opcodes.
// This is very much a hack, but it is better than nothing.
- var wg sync.WaitGroup
+ //
+ // Do a single regexp pass to record all ops being handled in a map, and
+ // then compare that with the ops list. This is much faster than one
+ // regexp pass per opcode.
for _, a := range archs {
if a.genfile == "" {
continue
}
- a := a
- wg.Add(1)
- go func() {
- src, err := ioutil.ReadFile(a.genfile)
- if err != nil {
- log.Fatalf("can't read %s: %v", a.genfile, err)
- }
+ pattern := fmt.Sprintf(`\Wssa\.Op%s([a-zA-Z0-9_]+)\W`, a.name)
+ rxOp, err := regexp.Compile(pattern)
+ if err != nil {
+ log.Fatalf("bad opcode regexp %s: %v", pattern, err)
+ }
- for _, v := range a.ops {
- pattern := fmt.Sprintf(`\Wssa\.Op%s%s\W`, a.name, v.name)
- match, err := regexp.Match(pattern, src)
- if err != nil {
- log.Fatalf("bad opcode regexp %s: %v", pattern, err)
- }
- if !match {
- log.Fatalf("Op%s%s has no code generation in %s", a.name, v.name, a.genfile)
- }
+ src, err := ioutil.ReadFile(a.genfile)
+ if err != nil {
+ log.Fatalf("can't read %s: %v", a.genfile, err)
+ }
+ seen := make(map[string]bool, len(a.ops))
+ for _, m := range rxOp.FindAllSubmatch(src, -1) {
+ seen[string(m[1])] = true
+ }
+ for _, op := range a.ops {
+ if !seen[op.name] {
+ log.Fatalf("Op%s%s has no code generation in %s", a.name, op.name, a.genfile)
}
- wg.Done()
- }()
+ }
}
- wg.Wait()
}
// Name returns the name of the architecture for use in Op* and Block* enumerations.
diff --git a/src/cmd/compile/internal/ssa/gen/rulegen.go b/src/cmd/compile/internal/ssa/gen/rulegen.go
index ed3ed75638..215f051370 100644
--- a/src/cmd/compile/internal/ssa/gen/rulegen.go
+++ b/src/cmd/compile/internal/ssa/gen/rulegen.go
@@ -636,7 +636,6 @@ type bodyBase struct {
canFail bool
}
-func (w *bodyBase) body() []Statement { return w.list }
func (w *bodyBase) add(nodes ...Statement) {
w.list = append(w.list, nodes...)
for _, node := range nodes {
From df855da653095e606fe69503b075e45d53d86ad7 Mon Sep 17 00:00:00 2001
From: Than McIntosh
Date: Mon, 16 Sep 2019 16:11:01 -0400
Subject: [PATCH 024/199] debug/elf: apply more relocations when reading DWARF
data sections
The elf reader's method for reading in DWARF section data has support
for applying selected relocations when the debug/dwarf readers are
being used on relocatable objects. This patch extends the set of
relocations applied slightly. In particlar, prior to this for some
architectures we were only applying relocations whose target symbol
was a section symbol; now we also include some relocations that target
other symbols. This is needed to get meaningful values for compilation
unit DIE low_pc attributes, which typically target a specific function
symbol in text.
Fixes #31363.
Change-Id: I34b02e7904cd7f2dea74197f73fa648141d15212
Reviewed-on: https://go-review.googlesource.com/c/go/+/195679
Reviewed-by: Ian Lance Taylor
---
src/cmd/link/dwarf_test.go | 2 +-
src/debug/elf/file.go | 112 +++++++++++++++++++++++--------------
2 files changed, 70 insertions(+), 44 deletions(-)
diff --git a/src/cmd/link/dwarf_test.go b/src/cmd/link/dwarf_test.go
index 897b2fc881..f33082e7f7 100644
--- a/src/cmd/link/dwarf_test.go
+++ b/src/cmd/link/dwarf_test.go
@@ -167,7 +167,7 @@ func testDWARF(t *testing.T, buildmode string, expectDWARF bool, env ...string)
func TestDWARF(t *testing.T) {
testDWARF(t, "", true)
- if runtime.GOOS == "darwin" && !testing.Short() {
+ if !testing.Short() {
t.Run("c-archive", func(t *testing.T) {
testDWARF(t, "c-archive", true)
})
diff --git a/src/debug/elf/file.go b/src/debug/elf/file.go
index 0f59fa4c32..1e863ef78e 100644
--- a/src/debug/elf/file.go
+++ b/src/debug/elf/file.go
@@ -628,6 +628,25 @@ func (f *File) applyRelocations(dst []byte, rels []byte) error {
}
}
+// relocSymbolTargetOK decides whether we should try to apply a
+// relocation to a DWARF data section, given a pointer to the symbol
+// targeted by the relocation. Most relocations in DWARF data tend to
+// be section-relative, but some target non-section symbols (for
+// example, low_PC attrs on subprogram or compilation unit DIEs that
+// target function symbols), and we need to include these as well.
+// Return value is a pair (X,Y) where X is a boolean indicating
+// whether the relocation is needed, and Y is the symbol value in the
+// case of a non-section relocation that needs to be applied.
+func relocSymbolTargetOK(sym *Symbol) (bool, uint64) {
+ if ST_TYPE(sym.Info) == STT_SECTION {
+ return true, 0
+ }
+ if sym.Section != SHN_UNDEF && sym.Section < SHN_LORESERVE {
+ return true, sym.Value
+ }
+ return false, 0
+}
+
func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) error {
// 24 is the size of Rela64.
if len(rels)%24 != 0 {
@@ -651,26 +670,28 @@ func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) error {
continue
}
sym := &symbols[symNo-1]
- if SymType(sym.Info&0xf) != STT_SECTION {
- // We don't handle non-section relocations for now.
+ needed, val := relocSymbolTargetOK(sym)
+ if !needed {
continue
}
// There are relocations, so this must be a normal
- // object file, and we only look at section symbols,
- // so we assume that the symbol value is 0.
+ // object file. The code below handles only basic relocations
+ // of the form S + A (symbol plus addend).
switch t {
case R_X86_64_64:
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
- f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], uint64(rela.Addend))
+ val64 := val + uint64(rela.Addend)
+ f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
case R_X86_64_32:
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
- f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], uint32(rela.Addend))
+ val32 := uint32(val) + uint32(rela.Addend)
+ f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
}
}
@@ -775,26 +796,28 @@ func (f *File) applyRelocationsARM64(dst []byte, rels []byte) error {
continue
}
sym := &symbols[symNo-1]
- if SymType(sym.Info&0xf) != STT_SECTION {
- // We don't handle non-section relocations for now.
+ needed, val := relocSymbolTargetOK(sym)
+ if !needed {
continue
}
// There are relocations, so this must be a normal
- // object file, and we only look at section symbols,
- // so we assume that the symbol value is 0.
+ // object file. The code below handles only basic relocations
+ // of the form S + A (symbol plus addend).
switch t {
case R_AARCH64_ABS64:
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
- f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], uint64(rela.Addend))
+ val64 := uint64(val) + uint64(rela.Addend)
+ f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
case R_AARCH64_ABS32:
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
- f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], uint32(rela.Addend))
+ val32 := uint32(val) + uint32(rela.Addend)
+ f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
}
}
@@ -824,8 +847,8 @@ func (f *File) applyRelocationsPPC(dst []byte, rels []byte) error {
continue
}
sym := &symbols[symNo-1]
- if SymType(sym.Info&0xf) != STT_SECTION {
- // We don't handle non-section relocations for now.
+ needed, val := relocSymbolTargetOK(sym)
+ if !needed {
continue
}
@@ -834,7 +857,8 @@ func (f *File) applyRelocationsPPC(dst []byte, rels []byte) error {
if rela.Off+4 >= uint32(len(dst)) || rela.Addend < 0 {
continue
}
- f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], uint32(rela.Addend))
+ val32 := uint32(val) + uint32(rela.Addend)
+ f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
}
}
@@ -864,8 +888,8 @@ func (f *File) applyRelocationsPPC64(dst []byte, rels []byte) error {
continue
}
sym := &symbols[symNo-1]
- if SymType(sym.Info&0xf) != STT_SECTION {
- // We don't handle non-section relocations for now.
+ needed, val := relocSymbolTargetOK(sym)
+ if !needed {
continue
}
@@ -874,12 +898,14 @@ func (f *File) applyRelocationsPPC64(dst []byte, rels []byte) error {
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
- f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], uint64(rela.Addend))
+ val64 := val + uint64(rela.Addend)
+ f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
case R_PPC64_ADDR32:
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
- f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], uint32(rela.Addend))
+ val32 := uint32(val) + uint32(rela.Addend)
+ f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
}
}
@@ -954,8 +980,8 @@ func (f *File) applyRelocationsMIPS64(dst []byte, rels []byte) error {
continue
}
sym := &symbols[symNo-1]
- if SymType(sym.Info&0xf) != STT_SECTION {
- // We don't handle non-section relocations for now.
+ needed, val := relocSymbolTargetOK(sym)
+ if !needed {
continue
}
@@ -964,12 +990,14 @@ func (f *File) applyRelocationsMIPS64(dst []byte, rels []byte) error {
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
- f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], uint64(rela.Addend))
+ val64 := val + uint64(rela.Addend)
+ f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
case R_MIPS_32:
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
- f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], uint32(rela.Addend))
+ val32 := uint32(val) + uint32(rela.Addend)
+ f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
}
}
@@ -999,10 +1027,8 @@ func (f *File) applyRelocationsRISCV64(dst []byte, rels []byte) error {
continue
}
sym := &symbols[symNo-1]
- switch SymType(sym.Info & 0xf) {
- case STT_SECTION, STT_NOTYPE:
- break
- default:
+ needed, val := relocSymbolTargetOK(sym)
+ if !needed {
continue
}
@@ -1011,14 +1037,14 @@ func (f *File) applyRelocationsRISCV64(dst []byte, rels []byte) error {
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
- val := sym.Value + uint64(rela.Addend)
- f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val)
+ val64 := val + uint64(rela.Addend)
+ f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
case R_RISCV_32:
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
- val := uint32(sym.Value) + uint32(rela.Addend)
- f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val)
+ val32 := uint32(val) + uint32(rela.Addend)
+ f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
}
}
@@ -1048,10 +1074,8 @@ func (f *File) applyRelocationss390x(dst []byte, rels []byte) error {
continue
}
sym := &symbols[symNo-1]
- switch SymType(sym.Info & 0xf) {
- case STT_SECTION, STT_NOTYPE:
- break
- default:
+ needed, val := relocSymbolTargetOK(sym)
+ if !needed {
continue
}
@@ -1060,14 +1084,14 @@ func (f *File) applyRelocationss390x(dst []byte, rels []byte) error {
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
- val := sym.Value + uint64(rela.Addend)
- f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val)
+ val64 := val + uint64(rela.Addend)
+ f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
case R_390_32:
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
- val := uint32(sym.Value) + uint32(rela.Addend)
- f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val)
+ val32 := uint32(val) + uint32(rela.Addend)
+ f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
}
}
@@ -1097,8 +1121,8 @@ func (f *File) applyRelocationsSPARC64(dst []byte, rels []byte) error {
continue
}
sym := &symbols[symNo-1]
- if SymType(sym.Info&0xf) != STT_SECTION {
- // We don't handle non-section relocations for now.
+ needed, val := relocSymbolTargetOK(sym)
+ if !needed {
continue
}
@@ -1107,12 +1131,14 @@ func (f *File) applyRelocationsSPARC64(dst []byte, rels []byte) error {
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
- f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], uint64(rela.Addend))
+ val64 := val + uint64(rela.Addend)
+ f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
case R_SPARC_32, R_SPARC_UA32:
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
continue
}
- f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], uint32(rela.Addend))
+ val32 := uint32(val) + uint32(rela.Addend)
+ f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
}
}
From 06e5529eceae35bb26b51f2430c2c9425149ede2 Mon Sep 17 00:00:00 2001
From: Jeremy Faller
Date: Mon, 16 Sep 2019 11:32:35 -0400
Subject: [PATCH 025/199] cmd/link: prefix syms with "_" on darwin links
RELNOTE=This change adds an underscore to all Go symbols in darwin, and
the behavior might be confusing to users of tools like "nm", etc.
Fixes #33808
Change-Id: I19ad626026ccae1e87b3bb97b6bb9fd55e95e121
Reviewed-on: https://go-review.googlesource.com/c/go/+/195619
Run-TryBot: Jeremy Faller
TryBot-Result: Gobot Gobot
Reviewed-by: Cherry Zhang
---
src/cmd/link/internal/ld/issue33808_test.go | 53 +++++++++++++++++++++
src/cmd/link/internal/ld/macho.go | 5 +-
src/debug/macho/file.go | 7 ++-
3 files changed, 62 insertions(+), 3 deletions(-)
create mode 100644 src/cmd/link/internal/ld/issue33808_test.go
diff --git a/src/cmd/link/internal/ld/issue33808_test.go b/src/cmd/link/internal/ld/issue33808_test.go
new file mode 100644
index 0000000000..df928a73d6
--- /dev/null
+++ b/src/cmd/link/internal/ld/issue33808_test.go
@@ -0,0 +1,53 @@
+// Copyright 2019 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 ld
+
+import (
+ "internal/testenv"
+ "io/ioutil"
+ "os"
+ "runtime"
+ "strings"
+ "testing"
+)
+
+const prog = `
+package main
+
+import "log"
+
+func main() {
+ log.Fatalf("HERE")
+}
+`
+
+func TestIssue33808(t *testing.T) {
+ if runtime.GOOS != "darwin" {
+ return
+ }
+ testenv.MustHaveGoBuild(t)
+
+ dir, err := ioutil.TempDir("", "TestIssue33808")
+ if err != nil {
+ t.Fatalf("could not create directory: %v", err)
+ }
+ defer os.RemoveAll(dir)
+
+ f := gobuild(t, dir, prog, "-ldflags=-linkmode=external")
+ f.Close()
+
+ syms, err := f.Symbols()
+ if err != nil {
+ t.Fatalf("Error reading symbols: %v", err)
+ }
+
+ name := "log.Fatalf"
+ for _, sym := range syms {
+ if strings.Contains(sym.Name, name) {
+ return
+ }
+ }
+ t.Fatalf("Didn't find %v", name)
+}
diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go
index 02e133e31d..7453f37c62 100644
--- a/src/cmd/link/internal/ld/macho.go
+++ b/src/cmd/link/internal/ld/macho.go
@@ -869,6 +869,7 @@ func machosymtab(ctxt *Link) {
symtab.AddUint32(ctxt.Arch, uint32(symstr.Size))
export := machoShouldExport(ctxt, s)
+ isGoSymbol := strings.Contains(s.Extname(), ".")
// In normal buildmodes, only add _ to C symbols, as
// Go symbols have dot in the name.
@@ -877,8 +878,8 @@ func machosymtab(ctxt *Link) {
// symbols like crosscall2 are in pclntab and end up
// pointing at the host binary, breaking unwinding.
// See Issue #18190.
- cexport := !strings.Contains(s.Extname(), ".") && (ctxt.BuildMode != BuildModePlugin || onlycsymbol(s))
- if cexport || export {
+ cexport := !isGoSymbol && (ctxt.BuildMode != BuildModePlugin || onlycsymbol(s))
+ if cexport || export || isGoSymbol {
symstr.AddUint8('_')
}
diff --git a/src/debug/macho/file.go b/src/debug/macho/file.go
index 16708e5247..085b0c8219 100644
--- a/src/debug/macho/file.go
+++ b/src/debug/macho/file.go
@@ -473,7 +473,12 @@ func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset
if n.Name >= uint32(len(strtab)) {
return nil, &FormatError{offset, "invalid name in symbol table", n.Name}
}
- sym.Name = cstring(strtab[n.Name:])
+ // We add "_" to Go symbols. Strip it here. See issue 33808.
+ name := cstring(strtab[n.Name:])
+ if strings.Contains(name, ".") && name[0] == '_' {
+ name = name[1:]
+ }
+ sym.Name = name
sym.Type = n.Type
sym.Sect = n.Sect
sym.Desc = n.Desc
From 575386d6324308d83f6f0782e61620ffe9f5dba3 Mon Sep 17 00:00:00 2001
From: Pantelis Sampaziotis
Date: Tue, 17 Sep 2019 19:01:36 +0000
Subject: [PATCH 026/199] time: add examples for microseconds and milliseconds
methods
This change adds testable examples for the new Microseconds and Milliseconds methods that were introduced in Go 1.13.
Fixes #34354
Change-Id: Ibdbfd770ca2192f9086f756918325f7327ce0482
GitHub-Last-Rev: 4575f48f5feb8e49742304d17776e28302647931
GitHub-Pull-Request: golang/go#34355
Reviewed-on: https://go-review.googlesource.com/c/go/+/195979
Reviewed-by: Alexander Rakoczy
Reviewed-by: Ian Lance Taylor
Run-TryBot: Alexander Rakoczy
TryBot-Result: Gobot Gobot
---
src/time/example_test.go | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/src/time/example_test.go b/src/time/example_test.go
index 25c34ebc1c..4d70471a7d 100644
--- a/src/time/example_test.go
+++ b/src/time/example_test.go
@@ -113,6 +113,20 @@ func ExampleDuration_Hours() {
// Output: I've got 4.5 hours of work left.
}
+func ExampleDuration_Microseconds() {
+ u, _ := time.ParseDuration("1s")
+ fmt.Printf("One second is %d microseconds.\n", u.Microseconds())
+ // Output:
+ // One second is 1000000 microseconds.
+}
+
+func ExampleDuration_Milliseconds() {
+ u, _ := time.ParseDuration("1s")
+ fmt.Printf("One second is %d milliseconds.\n", u.Milliseconds())
+ // Output:
+ // One second is 1000 milliseconds.
+}
+
func ExampleDuration_Minutes() {
m, _ := time.ParseDuration("1h30m")
fmt.Printf("The movie is %.0f minutes long.", m.Minutes())
From b3e2a72e6f42a924d6489b14c6881aa5cddf9418 Mon Sep 17 00:00:00 2001
From: Jeremy Faller
Date: Tue, 17 Sep 2019 21:45:28 +0000
Subject: [PATCH 027/199] Revert "cmd/link: prefix syms with "_" on darwin
links"
This reverts commit 06e5529eceae35bb26b51f2430c2c9425149ede2.
Reason for revert: darwin_386 is unhappy. (Almost as unhappy as I am.)
https://build.golang.org/log/292c90a4ef1c93597b865ab8513b66a95d93d022
Change-Id: I690566ce1d8212317fc3dc349ad0d4d5a2bb58eb
Reviewed-on: https://go-review.googlesource.com/c/go/+/196033
Reviewed-by: Cherry Zhang
Run-TryBot: Cherry Zhang
---
src/cmd/link/internal/ld/issue33808_test.go | 53 ---------------------
src/cmd/link/internal/ld/macho.go | 5 +-
src/debug/macho/file.go | 7 +--
3 files changed, 3 insertions(+), 62 deletions(-)
delete mode 100644 src/cmd/link/internal/ld/issue33808_test.go
diff --git a/src/cmd/link/internal/ld/issue33808_test.go b/src/cmd/link/internal/ld/issue33808_test.go
deleted file mode 100644
index df928a73d6..0000000000
--- a/src/cmd/link/internal/ld/issue33808_test.go
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2019 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 ld
-
-import (
- "internal/testenv"
- "io/ioutil"
- "os"
- "runtime"
- "strings"
- "testing"
-)
-
-const prog = `
-package main
-
-import "log"
-
-func main() {
- log.Fatalf("HERE")
-}
-`
-
-func TestIssue33808(t *testing.T) {
- if runtime.GOOS != "darwin" {
- return
- }
- testenv.MustHaveGoBuild(t)
-
- dir, err := ioutil.TempDir("", "TestIssue33808")
- if err != nil {
- t.Fatalf("could not create directory: %v", err)
- }
- defer os.RemoveAll(dir)
-
- f := gobuild(t, dir, prog, "-ldflags=-linkmode=external")
- f.Close()
-
- syms, err := f.Symbols()
- if err != nil {
- t.Fatalf("Error reading symbols: %v", err)
- }
-
- name := "log.Fatalf"
- for _, sym := range syms {
- if strings.Contains(sym.Name, name) {
- return
- }
- }
- t.Fatalf("Didn't find %v", name)
-}
diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go
index 7453f37c62..02e133e31d 100644
--- a/src/cmd/link/internal/ld/macho.go
+++ b/src/cmd/link/internal/ld/macho.go
@@ -869,7 +869,6 @@ func machosymtab(ctxt *Link) {
symtab.AddUint32(ctxt.Arch, uint32(symstr.Size))
export := machoShouldExport(ctxt, s)
- isGoSymbol := strings.Contains(s.Extname(), ".")
// In normal buildmodes, only add _ to C symbols, as
// Go symbols have dot in the name.
@@ -878,8 +877,8 @@ func machosymtab(ctxt *Link) {
// symbols like crosscall2 are in pclntab and end up
// pointing at the host binary, breaking unwinding.
// See Issue #18190.
- cexport := !isGoSymbol && (ctxt.BuildMode != BuildModePlugin || onlycsymbol(s))
- if cexport || export || isGoSymbol {
+ cexport := !strings.Contains(s.Extname(), ".") && (ctxt.BuildMode != BuildModePlugin || onlycsymbol(s))
+ if cexport || export {
symstr.AddUint8('_')
}
diff --git a/src/debug/macho/file.go b/src/debug/macho/file.go
index 085b0c8219..16708e5247 100644
--- a/src/debug/macho/file.go
+++ b/src/debug/macho/file.go
@@ -473,12 +473,7 @@ func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset
if n.Name >= uint32(len(strtab)) {
return nil, &FormatError{offset, "invalid name in symbol table", n.Name}
}
- // We add "_" to Go symbols. Strip it here. See issue 33808.
- name := cstring(strtab[n.Name:])
- if strings.Contains(name, ".") && name[0] == '_' {
- name = name[1:]
- }
- sym.Name = name
+ sym.Name = cstring(strtab[n.Name:])
sym.Type = n.Type
sym.Sect = n.Sect
sym.Desc = n.Desc
From 5cc64141e738b008b62d0698cdbadf2b9aead72d Mon Sep 17 00:00:00 2001
From: Javier
Date: Sun, 15 Sep 2019 12:20:58 +0800
Subject: [PATCH 028/199] math: Add examples for Copysign, Dim, Exp* and Trunc
Change-Id: I95921a8a55b243600aaec24ddca74b7040107dca
Reviewed-on: https://go-review.googlesource.com/c/go/+/195203
Reviewed-by: Robert Griesemer
---
src/math/example_test.go | 46 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
diff --git a/src/math/example_test.go b/src/math/example_test.go
index 364891324a..50c7426979 100644
--- a/src/math/example_test.go
+++ b/src/math/example_test.go
@@ -44,6 +44,11 @@ func ExampleAtanh() {
// Output: 0.00
}
+func ExampleCopysign() {
+ fmt.Printf("%.2f", math.Copysign(3.2, -1))
+ // Output: -3.20
+}
+
func ExampleCos() {
fmt.Printf("%.2f", math.Cos(math.Pi/2))
// Output: 0.00
@@ -173,3 +178,44 @@ func ExampleAbs() {
// 2.0
// 2.0
}
+func ExampleDim() {
+ fmt.Printf("%.2f\n", math.Dim(4, -2))
+ fmt.Printf("%.2f\n", math.Dim(-4, 2))
+ // Output:
+ // 6.00
+ // 0.00
+}
+
+func ExampleExp() {
+ fmt.Printf("%.2f\n", math.Exp(1))
+ fmt.Printf("%.2f\n", math.Exp(2))
+ fmt.Printf("%.2f\n", math.Exp(-1))
+ // Output:
+ // 2.72
+ // 7.39
+ // 0.37
+}
+
+func ExampleExp2() {
+ fmt.Printf("%.2f\n", math.Exp2(1))
+ fmt.Printf("%.2f\n", math.Exp2(-3))
+ // Output:
+ // 2.00
+ // 0.12
+}
+
+func ExampleExpm1() {
+ fmt.Printf("%.6f\n", math.Expm1(0.01))
+ fmt.Printf("%.6f\n", math.Expm1(-1))
+ // Output:
+ // 0.010050
+ // -0.632121
+}
+
+func ExampleTrunc() {
+ fmt.Printf("%.2f\n", math.Trunc(math.Pi))
+ fmt.Printf("%.2f\n", math.Trunc(-1.2345))
+ // Output:
+ // 3.00
+ // -1.00
+}
From 04fb929a5b7991ed0945d05ab8015c1721958d82 Mon Sep 17 00:00:00 2001
From: Robert Griesemer
Date: Mon, 16 Sep 2019 14:34:02 -0700
Subject: [PATCH 029/199] go/types: make sure interfaces are complete before
comparing them
Complete interfaces before comparing them with Checker.identical.
This requires passing through a *Checker to various functions that
didn't need this before.
Verified that none of the exported API entry points for interfaces
that rely on completed interfaces are used internally except for
Interface.Empty. Verified that interfaces are complete before
calling Empty on them, and added a dynamic check in the exported
functions.
Unfortunately, this fix exposed another problem with an esoteric
test case (#33656) which we need to reopen.
Fixes #34151.
Updates #33656.
Change-Id: I4e14bae3df74a2c21b565c24fdd07135f22e11c0
Reviewed-on: https://go-review.googlesource.com/c/go/+/195837
Run-TryBot: Robert Griesemer
TryBot-Result: Gobot Gobot
Reviewed-by: Matthew Dempsky
---
src/go/types/builtins.go | 4 +--
src/go/types/call.go | 5 ++++
src/go/types/conversions.go | 4 +--
src/go/types/expr.go | 8 ++---
src/go/types/issues_test.go | 28 ++++++++++++++++++
src/go/types/lookup.go | 16 +++++-----
src/go/types/methodset.go | 7 ++++-
src/go/types/operand.go | 7 +++--
src/go/types/predicates.go | 49 +++++++++++++++++++++++--------
src/go/types/stdlib_test.go | 1 +
src/go/types/stmt.go | 4 +--
src/go/types/testdata/cycles2.src | 4 ++-
src/go/types/type.go | 15 ++++++++--
src/go/types/typexpr.go | 10 +++----
14 files changed, 116 insertions(+), 46 deletions(-)
diff --git a/src/go/types/builtins.go b/src/go/types/builtins.go
index fb660b5cc8..af374b70c6 100644
--- a/src/go/types/builtins.go
+++ b/src/go/types/builtins.go
@@ -257,7 +257,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
}
// both argument types must be identical
- if !Identical(x.typ, y.typ) {
+ if !check.identical(x.typ, y.typ) {
check.invalidArg(x.pos(), "mismatched types %s and %s", x.typ, y.typ)
return
}
@@ -322,7 +322,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
return
}
- if !Identical(dst, src) {
+ if !check.identical(dst, src) {
check.invalidArg(x.pos(), "arguments to copy %s and %s have different element types %s and %s", x, &y, dst, src)
return
}
diff --git a/src/go/types/call.go b/src/go/types/call.go
index 1400e0f00b..31f9372644 100644
--- a/src/go/types/call.go
+++ b/src/go/types/call.go
@@ -464,6 +464,11 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
if m := mset.Lookup(check.pkg, sel); m == nil || m.obj != obj {
check.dump("%v: (%s).%v -> %s", e.Pos(), typ, obj.name, m)
check.dump("%s\n", mset)
+ // Caution: MethodSets are supposed to be used externally
+ // only (after all interface types were completed). It's
+ // now possible that we get here incorrectly. Not urgent
+ // to fix since we only run this code in debug mode.
+ // TODO(gri) fix this eventually.
panic("method sets and lookup don't agree")
}
}
diff --git a/src/go/types/conversions.go b/src/go/types/conversions.go
index fecb7b617f..7ea8fd70aa 100644
--- a/src/go/types/conversions.go
+++ b/src/go/types/conversions.go
@@ -89,7 +89,7 @@ func (x *operand) convertibleTo(check *Checker, T Type) bool {
V := x.typ
Vu := V.Underlying()
Tu := T.Underlying()
- if IdenticalIgnoreTags(Vu, Tu) {
+ if check.identicalIgnoreTags(Vu, Tu) {
return true
}
@@ -97,7 +97,7 @@ func (x *operand) convertibleTo(check *Checker, T Type) bool {
// have identical underlying types if tags are ignored"
if V, ok := V.(*Pointer); ok {
if T, ok := T.(*Pointer); ok {
- if IdenticalIgnoreTags(V.base.Underlying(), T.base.Underlying()) {
+ if check.identicalIgnoreTags(V.base.Underlying(), T.base.Underlying()) {
return true
}
}
diff --git a/src/go/types/expr.go b/src/go/types/expr.go
index 66d62d6885..0edd2789fb 100644
--- a/src/go/types/expr.go
+++ b/src/go/types/expr.go
@@ -548,9 +548,6 @@ func (check *Checker) convertUntyped(x *operand, target Type) {
}
}
case *Interface:
- if !x.isNil() && !t.Empty() /* empty interfaces are ok */ {
- goto Error
- }
// Update operand types to the default type rather then
// the target (interface) type: values must have concrete
// dynamic types. If the value is nil, keep it untyped
@@ -561,6 +558,7 @@ func (check *Checker) convertUntyped(x *operand, target Type) {
target = Typ[UntypedNil]
} else {
// cannot assign untyped values to non-empty interfaces
+ check.completeInterface(t)
if !t.Empty() {
goto Error
}
@@ -809,7 +807,7 @@ func (check *Checker) binary(x *operand, e *ast.BinaryExpr, lhs, rhs ast.Expr, o
return
}
- if !Identical(x.typ, y.typ) {
+ if !check.identical(x.typ, y.typ) {
// only report an error if we have valid types
// (otherwise we had an error reported elsewhere already)
if x.typ != Typ[Invalid] && y.typ != Typ[Invalid] {
@@ -1223,7 +1221,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
xkey := keyVal(x.val)
if _, ok := utyp.key.Underlying().(*Interface); ok {
for _, vtyp := range visited[xkey] {
- if Identical(vtyp, x.typ) {
+ if check.identical(vtyp, x.typ) {
duplicate = true
break
}
diff --git a/src/go/types/issues_test.go b/src/go/types/issues_test.go
index c9f5413920..1d0c0cb08a 100644
--- a/src/go/types/issues_test.go
+++ b/src/go/types/issues_test.go
@@ -465,3 +465,31 @@ func TestIssue29029(t *testing.T) {
t.Errorf("\ngot : %swant: %s", got, want)
}
}
+
+func TestIssue34151(t *testing.T) {
+ const asrc = `package a; type I interface{ M() }; type T struct { F interface { I } }`
+ const bsrc = `package b; import "a"; type T struct { F interface { a.I } }; var _ = a.T(T{})`
+
+ a, err := pkgFor("a", asrc, nil)
+ if err != nil {
+ t.Fatalf("package %s failed to typecheck: %v", a.Name(), err)
+ }
+
+ bast := mustParse(t, bsrc)
+ conf := Config{Importer: importHelper{a}}
+ b, err := conf.Check(bast.Name.Name, fset, []*ast.File{bast}, nil)
+ if err != nil {
+ t.Errorf("package %s failed to typecheck: %v", b.Name(), err)
+ }
+}
+
+type importHelper struct {
+ pkg *Package
+}
+
+func (h importHelper) Import(path string) (*Package, error) {
+ if path != h.pkg.Path() {
+ return nil, fmt.Errorf("got package path %q; want %q", path, h.pkg.Path())
+ }
+ return h.pkg, nil
+}
diff --git a/src/go/types/lookup.go b/src/go/types/lookup.go
index 265e30971d..648e100060 100644
--- a/src/go/types/lookup.go
+++ b/src/go/types/lookup.go
@@ -200,7 +200,7 @@ func (check *Checker) lookupFieldOrMethod(T Type, addressable bool, pkg *Package
return
}
- current = consolidateMultiples(next)
+ current = check.consolidateMultiples(next)
}
return nil, nil, false // not found
@@ -217,7 +217,7 @@ type embeddedType struct {
// consolidateMultiples collects multiple list entries with the same type
// into a single entry marked as containing multiples. The result is the
// consolidated list.
-func consolidateMultiples(list []embeddedType) []embeddedType {
+func (check *Checker) consolidateMultiples(list []embeddedType) []embeddedType {
if len(list) <= 1 {
return list // at most one entry - nothing to do
}
@@ -225,7 +225,7 @@ func consolidateMultiples(list []embeddedType) []embeddedType {
n := 0 // number of entries w/ unique type
prev := make(map[Type]int) // index at which type was previously seen
for _, e := range list {
- if i, found := lookupType(prev, e.typ); found {
+ if i, found := check.lookupType(prev, e.typ); found {
list[i].multiples = true
// ignore this entry
} else {
@@ -237,14 +237,14 @@ func consolidateMultiples(list []embeddedType) []embeddedType {
return list[:n]
}
-func lookupType(m map[Type]int, typ Type) (int, bool) {
+func (check *Checker) lookupType(m map[Type]int, typ Type) (int, bool) {
// fast path: maybe the types are equal
if i, found := m[typ]; found {
return i, true
}
for t, i := range m {
- if Identical(t, typ) {
+ if check.identical(t, typ) {
return i, true
}
}
@@ -278,8 +278,6 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method *
return
}
- // TODO(gri) Consider using method sets here. Might be more efficient.
-
if ityp, _ := V.Underlying().(*Interface); ityp != nil {
check.completeInterface(ityp)
// TODO(gri) allMethods is sorted - can do this more efficiently
@@ -290,7 +288,7 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method *
if static {
return m, false
}
- case !Identical(obj.Type(), m.typ):
+ case !check.identical(obj.Type(), m.typ):
return m, true
}
}
@@ -312,7 +310,7 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method *
check.objDecl(f, nil)
}
- if !Identical(f.typ, m.typ) {
+ if !check.identical(f.typ, m.typ) {
return m, true
}
}
diff --git a/src/go/types/methodset.go b/src/go/types/methodset.go
index 1c2208002e..a236fe2ea8 100644
--- a/src/go/types/methodset.go
+++ b/src/go/types/methodset.go
@@ -180,7 +180,12 @@ func NewMethodSet(T Type) *MethodSet {
}
}
- current = consolidateMultiples(next)
+ // It's ok to call consolidateMultiples with a nil *Checker because
+ // MethodSets are not used internally (outside debug mode). When used
+ // externally, interfaces are expected to be completed and then we do
+ // not need a *Checker to complete them when (indirectly) calling
+ // Checker.identical via consolidateMultiples.
+ current = (*Checker)(nil).consolidateMultiples(next)
}
if len(base) == 0 {
diff --git a/src/go/types/operand.go b/src/go/types/operand.go
index 97ca6c622f..1259f44300 100644
--- a/src/go/types/operand.go
+++ b/src/go/types/operand.go
@@ -211,7 +211,7 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) bool {
V := x.typ
// x's type is identical to T
- if Identical(V, T) {
+ if check.identical(V, T) {
return true
}
@@ -236,6 +236,7 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) bool {
return Vb.kind == UntypedBool && isBoolean(Tu)
}
case *Interface:
+ check.completeInterface(t)
return x.isNil() || t.Empty()
case *Pointer, *Signature, *Slice, *Map, *Chan:
return x.isNil()
@@ -245,7 +246,7 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) bool {
// x's type V and T have identical underlying types
// and at least one of V or T is not a named type
- if Identical(Vu, Tu) && (!isNamed(V) || !isNamed(T)) {
+ if check.identical(Vu, Tu) && (!isNamed(V) || !isNamed(T)) {
return true
}
@@ -268,7 +269,7 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) bool {
// type, x's type V and T have identical element types,
// and at least one of V or T is not a named type
if Vc, ok := Vu.(*Chan); ok && Vc.dir == SendRecv {
- if Tc, ok := Tu.(*Chan); ok && Identical(Vc.elem, Tc.elem) {
+ if Tc, ok := Tu.(*Chan); ok && check.identical(Vc.elem, Tc.elem) {
return !isNamed(V) || !isNamed(T)
}
}
diff --git a/src/go/types/predicates.go b/src/go/types/predicates.go
index 46ad4e2dc4..faaf753cd8 100644
--- a/src/go/types/predicates.go
+++ b/src/go/types/predicates.go
@@ -110,16 +110,31 @@ func hasNil(typ Type) bool {
return false
}
+// The functions Identical and IdenticalIgnoreTags are
+// provided for external use only, after interface types
+// were fully set up (completed). During type-checking,
+// use the methods identical and identicalIgnoreTags
+// which take a non-nil *Checker receiver.
+// TODO(gri) factor these out into api.go.
+
// Identical reports whether x and y are identical types.
// Receivers of Signature types are ignored.
func Identical(x, y Type) bool {
- return identical(x, y, true, nil)
+ return (*Checker)(nil).identical(x, y)
+}
+
+func (check *Checker) identical(x, y Type) bool {
+ return check.identical0(x, y, true, nil)
}
// IdenticalIgnoreTags reports whether x and y are identical types if tags are ignored.
// Receivers of Signature types are ignored.
func IdenticalIgnoreTags(x, y Type) bool {
- return identical(x, y, false, nil)
+ return (*Checker)(nil).identicalIgnoreTags(x, y)
+}
+
+func (check *Checker) identicalIgnoreTags(x, y Type) bool {
+ return check.identical0(x, y, false, nil)
}
// An ifacePair is a node in a stack of interface type pairs compared for identity.
@@ -132,7 +147,7 @@ func (p *ifacePair) identical(q *ifacePair) bool {
return p.x == q.x && p.y == q.y || p.x == q.y && p.y == q.x
}
-func identical(x, y Type, cmpTags bool, p *ifacePair) bool {
+func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
if x == y {
return true
}
@@ -152,13 +167,13 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool {
if y, ok := y.(*Array); ok {
// If one or both array lengths are unknown (< 0) due to some error,
// assume they are the same to avoid spurious follow-on errors.
- return (x.len < 0 || y.len < 0 || x.len == y.len) && identical(x.elem, y.elem, cmpTags, p)
+ return (x.len < 0 || y.len < 0 || x.len == y.len) && check.identical0(x.elem, y.elem, cmpTags, p)
}
case *Slice:
// Two slice types are identical if they have identical element types.
if y, ok := y.(*Slice); ok {
- return identical(x.elem, y.elem, cmpTags, p)
+ return check.identical0(x.elem, y.elem, cmpTags, p)
}
case *Struct:
@@ -173,7 +188,7 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool {
if f.embedded != g.embedded ||
cmpTags && x.Tag(i) != y.Tag(i) ||
!f.sameId(g.pkg, g.name) ||
- !identical(f.typ, g.typ, cmpTags, p) {
+ !check.identical0(f.typ, g.typ, cmpTags, p) {
return false
}
}
@@ -184,7 +199,7 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool {
case *Pointer:
// Two pointer types are identical if they have identical base types.
if y, ok := y.(*Pointer); ok {
- return identical(x.base, y.base, cmpTags, p)
+ return check.identical0(x.base, y.base, cmpTags, p)
}
case *Tuple:
@@ -195,7 +210,7 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool {
if x != nil {
for i, v := range x.vars {
w := y.vars[i]
- if !identical(v.typ, w.typ, cmpTags, p) {
+ if !check.identical0(v.typ, w.typ, cmpTags, p) {
return false
}
}
@@ -211,8 +226,8 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool {
// names are not required to match.
if y, ok := y.(*Signature); ok {
return x.variadic == y.variadic &&
- identical(x.params, y.params, cmpTags, p) &&
- identical(x.results, y.results, cmpTags, p)
+ check.identical0(x.params, y.params, cmpTags, p) &&
+ check.identical0(x.results, y.results, cmpTags, p)
}
case *Interface:
@@ -220,6 +235,14 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool {
// the same names and identical function types. Lower-case method names from
// different packages are always different. The order of the methods is irrelevant.
if y, ok := y.(*Interface); ok {
+ // If identical0 is called (indirectly) via an external API entry point
+ // (such as Identical, IdenticalIgnoreTags, etc.), check is nil. But in
+ // that case, interfaces are expected to be complete and lazy completion
+ // here is not needed.
+ if check != nil {
+ check.completeInterface(x)
+ check.completeInterface(y)
+ }
a := x.allMethods
b := y.allMethods
if len(a) == len(b) {
@@ -258,7 +281,7 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool {
}
for i, f := range a {
g := b[i]
- if f.Id() != g.Id() || !identical(f.typ, g.typ, cmpTags, q) {
+ if f.Id() != g.Id() || !check.identical0(f.typ, g.typ, cmpTags, q) {
return false
}
}
@@ -269,14 +292,14 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool {
case *Map:
// Two map types are identical if they have identical key and value types.
if y, ok := y.(*Map); ok {
- return identical(x.key, y.key, cmpTags, p) && identical(x.elem, y.elem, cmpTags, p)
+ return check.identical0(x.key, y.key, cmpTags, p) && check.identical0(x.elem, y.elem, cmpTags, p)
}
case *Chan:
// Two channel types are identical if they have identical value types
// and the same direction.
if y, ok := y.(*Chan); ok {
- return x.dir == y.dir && identical(x.elem, y.elem, cmpTags, p)
+ return x.dir == y.dir && check.identical0(x.elem, y.elem, cmpTags, p)
}
case *Named:
diff --git a/src/go/types/stdlib_test.go b/src/go/types/stdlib_test.go
index a3cbe95b3a..1b1db5d2dd 100644
--- a/src/go/types/stdlib_test.go
+++ b/src/go/types/stdlib_test.go
@@ -182,6 +182,7 @@ func TestStdFixed(t *testing.T) {
"issue20780.go", // go/types does not have constraints on stack size
"issue31747.go", // go/types does not have constraints on language level (-lang=go1.12) (see #31793)
"issue34329.go", // go/types does not have constraints on language level (-lang=go1.13) (see #31793)
+ "bug251.go", // issue #34333 which was exposed with fix for #34151
)
}
diff --git a/src/go/types/stmt.go b/src/go/types/stmt.go
index abd9d05ef2..c1593bbee9 100644
--- a/src/go/types/stmt.go
+++ b/src/go/types/stmt.go
@@ -249,7 +249,7 @@ L:
// look for duplicate types for a given value
// (quadratic algorithm, but these lists tend to be very short)
for _, vt := range seen[val] {
- if Identical(v.typ, vt.typ) {
+ if check.identical(v.typ, vt.typ) {
check.errorf(v.pos(), "duplicate case %s in expression switch", &v)
check.error(vt.pos, "\tprevious case") // secondary error, \t indented
continue L
@@ -270,7 +270,7 @@ L:
// look for duplicate types
// (quadratic algorithm, but type switches tend to be reasonably small)
for t, pos := range seen {
- if T == nil && t == nil || T != nil && t != nil && Identical(T, t) {
+ if T == nil && t == nil || T != nil && t != nil && check.identical(T, t) {
// talk about "case" rather than "type" because of nil case
Ts := "nil"
if T != nil {
diff --git a/src/go/types/testdata/cycles2.src b/src/go/types/testdata/cycles2.src
index 98ca6f4e44..5fd9e838b6 100644
--- a/src/go/types/testdata/cycles2.src
+++ b/src/go/types/testdata/cycles2.src
@@ -58,7 +58,9 @@ var y interface {
A
B
}
-var _ = x == y
+
+// TODO(gri) This should be a valid compare. See #33656.
+var _ = x /* ERROR cannot compare */ == y
// Test case for issue 6638.
diff --git a/src/go/types/type.go b/src/go/types/type.go
index 5c28a2e7ba..6263da06f2 100644
--- a/src/go/types/type.go
+++ b/src/go/types/type.go
@@ -329,14 +329,23 @@ func (t *Interface) Embedded(i int) *Named { tname, _ := t.embeddeds[i].(*Named)
func (t *Interface) EmbeddedType(i int) Type { return t.embeddeds[i] }
// NumMethods returns the total number of methods of interface t.
-func (t *Interface) NumMethods() int { return len(t.allMethods) }
+// The interface must have been completed.
+func (t *Interface) NumMethods() int { t.assertCompleteness(); return len(t.allMethods) }
+
+func (t *Interface) assertCompleteness() {
+ if t.allMethods == nil {
+ panic("interface is incomplete")
+ }
+}
// Method returns the i'th method of interface t for 0 <= i < t.NumMethods().
// The methods are ordered by their unique Id.
-func (t *Interface) Method(i int) *Func { return t.allMethods[i] }
+// The interface must have been completed.
+func (t *Interface) Method(i int) *Func { t.assertCompleteness(); return t.allMethods[i] }
// Empty reports whether t is the empty interface.
-func (t *Interface) Empty() bool { return len(t.allMethods) == 0 }
+// The interface must have been completed.
+func (t *Interface) Empty() bool { t.assertCompleteness(); return len(t.allMethods) == 0 }
// Complete computes the interface's method set. It must be called by users of
// NewInterfaceType and NewInterface after the interface's embedded types are
diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go
index 19bedae590..4948b800d1 100644
--- a/src/go/types/typexpr.go
+++ b/src/go/types/typexpr.go
@@ -538,10 +538,10 @@ func (check *Checker) completeInterface(ityp *Interface) {
return
}
- // completeInterface may be called via the LookupFieldOrMethod or
- // MissingMethod external API in which case check will be nil. In
- // this case, type-checking must be finished and all interfaces
- // should have been completed.
+ // completeInterface may be called via the LookupFieldOrMethod,
+ // MissingMethod, Identical, or IdenticalIgnoreTags external API
+ // in which case check will be nil. In this case, type-checking
+ // must be finished and all interfaces should have been completed.
if check == nil {
panic("internal error: incomplete interface")
}
@@ -569,7 +569,7 @@ func (check *Checker) completeInterface(ityp *Interface) {
default:
// check method signatures after all types are computed (issue #33656)
check.atEnd(func() {
- if !Identical(m.typ, other.Type()) {
+ if !check.identical(m.typ, other.Type()) {
check.errorf(m.pos, "duplicate method %s", m.name)
check.reportAltDecl(other)
}
From 1b2c7948963543286516f78f0b0d52956f58d82c Mon Sep 17 00:00:00 2001
From: Matthew Dempsky
Date: Fri, 13 Sep 2019 15:13:22 -0700
Subject: [PATCH 030/199] cmd/compile: tweak OIF construction for binarySearch
When emitting base cases, previously we would emit:
if c1 { s1 }
if c2 { s2 }
if c3 { s3 }
With this CL, we instead emit:
if c1 { s1 }
else if c2 { s2 }
else if c3 { s3 }
Most of the time, this doesn't make a difference, because s1/s2/s3 are
typically "goto" statements. But for type switches, we currently emit:
if hash == 271 { if _, ok := iface.(T1); ok { goto t1case } }
if hash == 314 { if _, ok := iface.(T2); ok { goto t2case } }
That is, the if bodies can fallthrough, even though it's impossible
for them to match any of the subsequent cases.
Change-Id: I453d424d0b5e40060a703738bbb374523f1c403c
Reviewed-on: https://go-review.googlesource.com/c/go/+/195339
Run-TryBot: Matthew Dempsky
TryBot-Result: Gobot Gobot
Reviewed-by: Keith Randall
---
src/cmd/compile/internal/gc/swt.go | 34 ++++++++++++++----------------
1 file changed, 16 insertions(+), 18 deletions(-)
diff --git a/src/cmd/compile/internal/gc/swt.go b/src/cmd/compile/internal/gc/swt.go
index 4a8e9bceed..004ff3c4c0 100644
--- a/src/cmd/compile/internal/gc/swt.go
+++ b/src/cmd/compile/internal/gc/swt.go
@@ -397,14 +397,10 @@ func (s *exprSwitch) flush() {
}
return le
},
- func(i int, out *Nodes) {
+ func(i int, nif *Node) {
c := &cc[i]
-
- nif := nodl(c.pos, OIF, c.test(s.exprname), nil)
- nif.Left = typecheck(nif.Left, ctxExpr)
- nif.Left = defaultlit(nif.Left, nil)
+ nif.Left = c.test(s.exprname)
nif.Nbody.Set1(c.jmp)
- out.Append(nif)
},
)
}
@@ -521,8 +517,8 @@ func walkTypeSwitch(sw *Node) {
singleType := ncase.List.Len() == 1 && ncase.List.First().Op == OTYPE
label := autolabel(".s")
-
jmp := npos(ncase.Pos, nodSym(OGOTO, nil, label))
+
if ncase.List.Len() == 0 { // default:
if defaultGoto != nil {
Fatalf("duplicate default case not detected during typechecking")
@@ -672,16 +668,12 @@ func (s *typeSwitch) flush() {
func(i int) *Node {
return nod(OLE, s.hashname, nodintconst(int64(cc[i-1].hash)))
},
- func(i int, out *Nodes) {
+ func(i int, nif *Node) {
// TODO(mdempsky): Omit hash equality check if
// there's only one type.
c := cc[i]
- a := nod(OIF, nil, nil)
- a.Left = nod(OEQ, s.hashname, nodintconst(int64(c.hash)))
- a.Left = typecheck(a.Left, ctxExpr)
- a.Left = defaultlit(a.Left, nil)
- a.Nbody.AppendNodes(&c.body)
- out.Append(a)
+ nif.Left = nod(OEQ, s.hashname, nodintconst(int64(c.hash)))
+ nif.Nbody.AppendNodes(&c.body)
},
)
}
@@ -691,10 +683,11 @@ func (s *typeSwitch) flush() {
// switch statements.
//
// less(i) should return a boolean expression. If it evaluates true,
-// then cases [0, i) will be tested; otherwise, cases [i, n).
+// then cases before i will be tested; otherwise, cases i and later.
//
-// base(i, out) should append statements to out to test the i'th case.
-func binarySearch(n int, out *Nodes, less func(i int) *Node, base func(i int, out *Nodes)) {
+// base(i, nif) should setup nif (an OIF node) to test case i. In
+// particular, it should set nif.Left and nif.Nbody.
+func binarySearch(n int, out *Nodes, less func(i int) *Node, base func(i int, nif *Node)) {
const binarySearchMin = 4 // minimum number of cases for binary search
var do func(lo, hi int, out *Nodes)
@@ -702,7 +695,12 @@ func binarySearch(n int, out *Nodes, less func(i int) *Node, base func(i int, ou
n := hi - lo
if n < binarySearchMin {
for i := lo; i < hi; i++ {
- base(i, out)
+ nif := nod(OIF, nil, nil)
+ base(i, nif)
+ nif.Left = typecheck(nif.Left, ctxExpr)
+ nif.Left = defaultlit(nif.Left, nil)
+ out.Append(nif)
+ out = &nif.Rlist
}
return
}
From 2fc6366fb56aa77efa3cecd44ce941472786a418 Mon Sep 17 00:00:00 2001
From: Matthew Dempsky
Date: Tue, 17 Sep 2019 18:32:04 -0700
Subject: [PATCH 031/199] cmd/compile: remove OCASE and rename OXCASE to OCASE
We used to use OXCASE to represent general, possibly multi-valued
cases, and then desugar these during walk into single-value cases
represented by OCASE.
In CL 194660, we switched to eliminated the desugaring step and
instead handle the multi-valued cases directly, which eliminates the
need for an OCASE Op. Instead, we can simply remove OCASE, and rename
OXCASE to just OCASE.
Passes toolstash-check.
Change-Id: I3cc184340f9081d37453927cca1c059267fdbc12
Reviewed-on: https://go-review.googlesource.com/c/go/+/196117
Reviewed-by: Keith Randall
---
src/cmd/compile/internal/gc/fmt.go | 19 +------------------
src/cmd/compile/internal/gc/iexport.go | 4 ++--
src/cmd/compile/internal/gc/iimport.go | 10 ++--------
src/cmd/compile/internal/gc/noder.go | 4 ++--
src/cmd/compile/internal/gc/op_string.go | 24 +++++++++++++++++-------
src/cmd/compile/internal/gc/order.go | 4 ++--
src/cmd/compile/internal/gc/select.go | 2 +-
src/cmd/compile/internal/gc/swt.go | 2 +-
src/cmd/compile/internal/gc/syntax.go | 8 ++++----
src/cmd/compile/internal/gc/typecheck.go | 2 +-
src/cmd/compile/internal/gc/walk.go | 7 +------
11 files changed, 34 insertions(+), 52 deletions(-)
diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go
index 53d6b9d2cc..cb6b571f83 100644
--- a/src/cmd/compile/internal/gc/fmt.go
+++ b/src/cmd/compile/internal/gc/fmt.go
@@ -1035,7 +1035,7 @@ func (n *Node) stmtfmt(s fmt.State, mode fmtMode) {
mode.Fprintf(s, " { %v }", n.List)
- case OXCASE:
+ case OCASE:
if n.List.Len() != 0 {
mode.Fprintf(s, "case %.v", n.List)
} else {
@@ -1043,22 +1043,6 @@ func (n *Node) stmtfmt(s fmt.State, mode fmtMode) {
}
mode.Fprintf(s, ": %v", n.Nbody)
- case OCASE:
- switch {
- case n.Left != nil:
- // single element
- mode.Fprintf(s, "case %v", n.Left)
- case n.List.Len() > 0:
- // range
- if n.List.Len() != 2 {
- Fatalf("bad OCASE list length %d", n.List.Len())
- }
- mode.Fprintf(s, "case %v..%v", n.List.First(), n.List.Second())
- default:
- fmt.Fprint(s, "default")
- }
- mode.Fprintf(s, ": %v", n.Nbody)
-
case OBREAK, OCONTINUE, OGOTO, OFALL:
if n.Sym != nil {
mode.Fprintf(s, "%#v %v", n.Op, n.Sym)
@@ -1192,7 +1176,6 @@ var opprec = []int{
ORETURN: -1,
OSELECT: -1,
OSWITCH: -1,
- OXCASE: -1,
OEND: 0,
}
diff --git a/src/cmd/compile/internal/gc/iexport.go b/src/cmd/compile/internal/gc/iexport.go
index 0e5a313baf..eeca0b4083 100644
--- a/src/cmd/compile/internal/gc/iexport.go
+++ b/src/cmd/compile/internal/gc/iexport.go
@@ -1079,8 +1079,8 @@ func (w *exportWriter) stmt(n *Node) {
w.exprsOrNil(n.Left, nil)
w.stmtList(n.List)
- case OCASE, OXCASE:
- w.op(OXCASE)
+ case OCASE:
+ w.op(OCASE)
w.pos(n.Pos)
w.stmtList(n.List)
w.stmtList(n.Nbody)
diff --git a/src/cmd/compile/internal/gc/iimport.go b/src/cmd/compile/internal/gc/iimport.go
index 7d134f3a5f..4862f86344 100644
--- a/src/cmd/compile/internal/gc/iimport.go
+++ b/src/cmd/compile/internal/gc/iimport.go
@@ -1003,20 +1003,14 @@ func (r *importReader) node() *Node {
n.List.Set(r.stmtList())
return n
- // case OCASE, OXCASE:
- // unreachable - mapped to OXCASE case below by exporter
-
- case OXCASE:
- n := nodl(r.pos(), OXCASE, nil, nil)
+ case OCASE:
+ n := nodl(r.pos(), OCASE, nil, nil)
n.List.Set(r.exprList())
// TODO(gri) eventually we must declare variables for type switch
// statements (type switch statements are not yet exported)
n.Nbody.Set(r.stmtList())
return n
- // case OFALL:
- // unreachable - mapped to OXFALL case below by exporter
-
case OFALL:
n := nodl(r.pos(), OFALL, nil, nil)
return n
diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go
index 6bbabb45dd..91c7fd49b1 100644
--- a/src/cmd/compile/internal/gc/noder.go
+++ b/src/cmd/compile/internal/gc/noder.go
@@ -1188,7 +1188,7 @@ func (p *noder) caseClauses(clauses []*syntax.CaseClause, tswitch *Node, rbrace
}
p.openScope(clause.Pos())
- n := p.nod(clause, OXCASE, nil, nil)
+ n := p.nod(clause, OCASE, nil, nil)
if clause.Cases != nil {
n.List.Set(p.exprList(clause.Cases))
}
@@ -1244,7 +1244,7 @@ func (p *noder) commClauses(clauses []*syntax.CommClause, rbrace syntax.Pos) []*
}
p.openScope(clause.Pos())
- n := p.nod(clause, OXCASE, nil, nil)
+ n := p.nod(clause, OCASE, nil, nil)
if clause.Comm != nil {
n.List.Set1(p.stmt(clause.Comm))
}
diff --git a/src/cmd/compile/internal/gc/op_string.go b/src/cmd/compile/internal/gc/op_string.go
index 796e13a071..1586c70f1b 100644
--- a/src/cmd/compile/internal/gc/op_string.go
+++ b/src/cmd/compile/internal/gc/op_string.go
@@ -1,4 +1,4 @@
-// Code generated by "stringer -type=Op -trimprefix=O"; DO NOT EDIT.
+// Code generated by "stringer -type Op -trimprefix O"; DO NOT EDIT.
package gc
@@ -121,8 +121,7 @@ func _() {
_ = x[OSIZEOF-110]
_ = x[OBLOCK-111]
_ = x[OBREAK-112]
- _ = x[OCASE-113]
- _ = x[OXCASE-114]
+ _ = x[OCASE-114]
_ = x[OCONTINUE-115]
_ = x[ODEFER-116]
_ = x[OEMPTY-117]
@@ -164,13 +163,24 @@ func _() {
_ = x[OEND-153]
}
-const _Op_name = "XXXNAMENONAMETYPEPACKLITERALADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCALLPARTCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVNOPCOPYDCLDCLFUNCDCLFIELDDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMULDIVMODLSHRSHANDANDNOTNEWNEWOBJNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERRECOVERRECVRUNESTRSELRECVSELRECV2IOTAREALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFBLOCKBREAKCASEXCASECONTINUEDEFEREMPTYFALLFORFORUNTILGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWTCHANTMAPTSTRUCTTINTERTFUNCTARRAYDDDDDDARGINLCALLEFACEITABIDATASPTRCLOSUREVARCFUNCCHECKNILVARDEFVARKILLVARLIVERESULTINLMARKRETJMPGETGEND"
+const (
+ _Op_name_0 = "XXXNAMENONAMETYPEPACKLITERALADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCALLPARTCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVNOPCOPYDCLDCLFUNCDCLFIELDDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMULDIVMODLSHRSHANDANDNOTNEWNEWOBJNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERRECOVERRECVRUNESTRSELRECVSELRECV2IOTAREALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFBLOCKBREAK"
+ _Op_name_1 = "CASECONTINUEDEFEREMPTYFALLFORFORUNTILGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWTCHANTMAPTSTRUCTTINTERTFUNCTARRAYDDDDDDARGINLCALLEFACEITABIDATASPTRCLOSUREVARCFUNCCHECKNILVARDEFVARKILLVARLIVERESULTINLMARKRETJMPGETGEND"
+)
-var _Op_index = [...]uint16{0, 3, 7, 13, 17, 21, 28, 31, 34, 36, 39, 45, 49, 55, 61, 70, 82, 91, 100, 112, 121, 123, 126, 136, 143, 150, 157, 161, 165, 173, 181, 190, 198, 201, 206, 213, 220, 226, 235, 243, 251, 257, 261, 270, 277, 281, 284, 291, 299, 307, 314, 320, 323, 329, 336, 344, 348, 355, 363, 365, 367, 369, 371, 373, 375, 380, 385, 393, 396, 405, 408, 412, 420, 427, 436, 439, 442, 445, 448, 451, 454, 460, 463, 469, 472, 478, 482, 485, 489, 494, 499, 505, 510, 514, 519, 527, 535, 541, 550, 561, 568, 572, 579, 586, 594, 598, 602, 606, 613, 620, 628, 634, 639, 644, 648, 653, 661, 666, 671, 675, 678, 686, 690, 692, 697, 699, 704, 710, 716, 722, 728, 733, 737, 744, 750, 755, 761, 764, 770, 777, 782, 786, 791, 795, 805, 810, 818, 824, 831, 838, 844, 851, 857, 861, 864}
+var (
+ _Op_index_0 = [...]uint16{0, 3, 7, 13, 17, 21, 28, 31, 34, 36, 39, 45, 49, 55, 61, 70, 82, 91, 100, 112, 121, 123, 126, 136, 143, 150, 157, 161, 165, 173, 181, 190, 198, 201, 206, 213, 220, 226, 235, 243, 251, 257, 261, 270, 277, 281, 284, 291, 299, 307, 314, 320, 323, 329, 336, 344, 348, 355, 363, 365, 367, 369, 371, 373, 375, 380, 385, 393, 396, 405, 408, 412, 420, 427, 436, 439, 442, 445, 448, 451, 454, 460, 463, 469, 472, 478, 482, 485, 489, 494, 499, 505, 510, 514, 519, 527, 535, 541, 550, 561, 568, 572, 579, 586, 594, 598, 602, 606, 613, 620, 628, 634, 639, 644}
+ _Op_index_1 = [...]uint8{0, 4, 12, 17, 22, 26, 29, 37, 41, 43, 48, 50, 55, 61, 67, 73, 79, 84, 88, 95, 101, 106, 112, 115, 121, 128, 133, 137, 142, 146, 156, 161, 169, 175, 182, 189, 195, 202, 208, 212, 215}
+)
func (i Op) String() string {
- if i >= Op(len(_Op_index)-1) {
+ switch {
+ case 0 <= i && i <= 112:
+ return _Op_name_0[_Op_index_0[i]:_Op_index_0[i+1]]
+ case 114 <= i && i <= 153:
+ i -= 114
+ return _Op_name_1[_Op_index_1[i]:_Op_index_1[i+1]]
+ default:
return "Op(" + strconv.FormatInt(int64(i), 10) + ")"
}
- return _Op_name[_Op_index[i]:_Op_index[i+1]]
}
diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go
index ee04b69a68..e6350ef721 100644
--- a/src/cmd/compile/internal/gc/order.go
+++ b/src/cmd/compile/internal/gc/order.go
@@ -779,7 +779,7 @@ func (o *Order) stmt(n *Node) {
t := o.markTemp()
for _, n2 := range n.List.Slice() {
- if n2.Op != OXCASE {
+ if n2.Op != OCASE {
Fatalf("order select case %v", n2.Op)
}
r := n2.Left
@@ -938,7 +938,7 @@ func (o *Order) stmt(n *Node) {
t := o.markTemp()
n.Left = o.expr(n.Left, nil)
for _, ncas := range n.List.Slice() {
- if ncas.Op != OXCASE {
+ if ncas.Op != OCASE {
Fatalf("order switch case %v", ncas.Op)
}
o.exprListInPlace(ncas.List)
diff --git a/src/cmd/compile/internal/gc/select.go b/src/cmd/compile/internal/gc/select.go
index e0ed1e2a9f..07c5c5a2a9 100644
--- a/src/cmd/compile/internal/gc/select.go
+++ b/src/cmd/compile/internal/gc/select.go
@@ -12,7 +12,7 @@ func typecheckselect(sel *Node) {
lno := setlineno(sel)
typecheckslice(sel.Ninit.Slice(), ctxStmt)
for _, ncase := range sel.List.Slice() {
- if ncase.Op != OXCASE {
+ if ncase.Op != OCASE {
setlineno(ncase)
Fatalf("typecheckselect %v", ncase.Op)
}
diff --git a/src/cmd/compile/internal/gc/swt.go b/src/cmd/compile/internal/gc/swt.go
index 004ff3c4c0..d53efefa72 100644
--- a/src/cmd/compile/internal/gc/swt.go
+++ b/src/cmd/compile/internal/gc/swt.go
@@ -434,7 +434,7 @@ func allCaseExprsAreSideEffectFree(sw *Node) bool {
// enough.
for _, ncase := range sw.List.Slice() {
- if ncase.Op != OXCASE {
+ if ncase.Op != OCASE {
Fatalf("switch string(byteslice) bad op: %v", ncase.Op)
}
for _, v := range ncase.List.Slice() {
diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go
index e8a527a8fc..e8900edd3a 100644
--- a/src/cmd/compile/internal/gc/syntax.go
+++ b/src/cmd/compile/internal/gc/syntax.go
@@ -704,8 +704,8 @@ const (
// statements
OBLOCK // { List } (block of code)
OBREAK // break [Sym]
- OCASE // case Left or List[0]..List[1]: Nbody (select case after processing; Left==nil and List==nil means default)
- OXCASE // case List: Nbody (select case before processing; List==nil means default)
+ _ // For toolstash -cmp. TODO(mdempsky): Remove.
+ OCASE // case List: Nbody (List==nil means default)
OCONTINUE // continue [Sym]
ODEFER // defer Left (Left must be call)
OEMPTY // no-op (empty statement)
@@ -727,8 +727,8 @@ const (
OGO // go Left (Left must be call)
ORANGE // for List = range Right { Nbody }
ORETURN // return List
- OSELECT // select { List } (List is list of OXCASE or OCASE)
- OSWITCH // switch Ninit; Left { List } (List is a list of OXCASE or OCASE)
+ OSELECT // select { List } (List is list of OCASE)
+ OSWITCH // switch Ninit; Left { List } (List is a list of OCASE)
OTYPESW // Left = Right.(type) (appears as .Left of OSWITCH)
// types
diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go
index 050a74b1e6..48a3e1100e 100644
--- a/src/cmd/compile/internal/gc/typecheck.go
+++ b/src/cmd/compile/internal/gc/typecheck.go
@@ -2014,7 +2014,7 @@ func typecheck1(n *Node, top int) (res *Node) {
n.Type = nil
return n
- case OXCASE:
+ case OCASE:
ok |= ctxStmt
typecheckslice(n.List.Slice(), ctxExpr)
typecheckslice(n.Nbody.Slice(), ctxStmt)
diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go
index 8dd60f4285..4c89ae639b 100644
--- a/src/cmd/compile/internal/gc/walk.go
+++ b/src/cmd/compile/internal/gc/walk.go
@@ -209,13 +209,8 @@ func walkstmt(n *Node) *Node {
case OBLOCK:
walkstmtlist(n.List.Slice())
- case OXCASE:
- yyerror("case statement out of place")
- n.Op = OCASE
- fallthrough
-
case OCASE:
- n.Right = walkstmt(n.Right)
+ yyerror("case statement out of place")
case ODEFER:
Curfn.Func.SetHasDefer(true)
From d55cf5b80cf3232bdc2793a8362f9acd8a6f8442 Mon Sep 17 00:00:00 2001
From: Matthew Dempsky
Date: Tue, 17 Sep 2019 18:37:47 -0700
Subject: [PATCH 032/199] cmd/compile: remove toolstash bandage
Change-Id: Ic33dfccf06681470bec19f66653fda67d9901095
Reviewed-on: https://go-review.googlesource.com/c/go/+/196118
Reviewed-by: Keith Randall
---
src/cmd/compile/internal/gc/op_string.go | 99 +++++++++++-------------
src/cmd/compile/internal/gc/syntax.go | 1 -
2 files changed, 44 insertions(+), 56 deletions(-)
diff --git a/src/cmd/compile/internal/gc/op_string.go b/src/cmd/compile/internal/gc/op_string.go
index 1586c70f1b..db42d9af57 100644
--- a/src/cmd/compile/internal/gc/op_string.go
+++ b/src/cmd/compile/internal/gc/op_string.go
@@ -121,66 +121,55 @@ func _() {
_ = x[OSIZEOF-110]
_ = x[OBLOCK-111]
_ = x[OBREAK-112]
- _ = x[OCASE-114]
- _ = x[OCONTINUE-115]
- _ = x[ODEFER-116]
- _ = x[OEMPTY-117]
- _ = x[OFALL-118]
- _ = x[OFOR-119]
- _ = x[OFORUNTIL-120]
- _ = x[OGOTO-121]
- _ = x[OIF-122]
- _ = x[OLABEL-123]
- _ = x[OGO-124]
- _ = x[ORANGE-125]
- _ = x[ORETURN-126]
- _ = x[OSELECT-127]
- _ = x[OSWITCH-128]
- _ = x[OTYPESW-129]
- _ = x[OTCHAN-130]
- _ = x[OTMAP-131]
- _ = x[OTSTRUCT-132]
- _ = x[OTINTER-133]
- _ = x[OTFUNC-134]
- _ = x[OTARRAY-135]
- _ = x[ODDD-136]
- _ = x[ODDDARG-137]
- _ = x[OINLCALL-138]
- _ = x[OEFACE-139]
- _ = x[OITAB-140]
- _ = x[OIDATA-141]
- _ = x[OSPTR-142]
- _ = x[OCLOSUREVAR-143]
- _ = x[OCFUNC-144]
- _ = x[OCHECKNIL-145]
- _ = x[OVARDEF-146]
- _ = x[OVARKILL-147]
- _ = x[OVARLIVE-148]
- _ = x[ORESULT-149]
- _ = x[OINLMARK-150]
- _ = x[ORETJMP-151]
- _ = x[OGETG-152]
- _ = x[OEND-153]
+ _ = x[OCASE-113]
+ _ = x[OCONTINUE-114]
+ _ = x[ODEFER-115]
+ _ = x[OEMPTY-116]
+ _ = x[OFALL-117]
+ _ = x[OFOR-118]
+ _ = x[OFORUNTIL-119]
+ _ = x[OGOTO-120]
+ _ = x[OIF-121]
+ _ = x[OLABEL-122]
+ _ = x[OGO-123]
+ _ = x[ORANGE-124]
+ _ = x[ORETURN-125]
+ _ = x[OSELECT-126]
+ _ = x[OSWITCH-127]
+ _ = x[OTYPESW-128]
+ _ = x[OTCHAN-129]
+ _ = x[OTMAP-130]
+ _ = x[OTSTRUCT-131]
+ _ = x[OTINTER-132]
+ _ = x[OTFUNC-133]
+ _ = x[OTARRAY-134]
+ _ = x[ODDD-135]
+ _ = x[ODDDARG-136]
+ _ = x[OINLCALL-137]
+ _ = x[OEFACE-138]
+ _ = x[OITAB-139]
+ _ = x[OIDATA-140]
+ _ = x[OSPTR-141]
+ _ = x[OCLOSUREVAR-142]
+ _ = x[OCFUNC-143]
+ _ = x[OCHECKNIL-144]
+ _ = x[OVARDEF-145]
+ _ = x[OVARKILL-146]
+ _ = x[OVARLIVE-147]
+ _ = x[ORESULT-148]
+ _ = x[OINLMARK-149]
+ _ = x[ORETJMP-150]
+ _ = x[OGETG-151]
+ _ = x[OEND-152]
}
-const (
- _Op_name_0 = "XXXNAMENONAMETYPEPACKLITERALADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCALLPARTCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVNOPCOPYDCLDCLFUNCDCLFIELDDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMULDIVMODLSHRSHANDANDNOTNEWNEWOBJNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERRECOVERRECVRUNESTRSELRECVSELRECV2IOTAREALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFBLOCKBREAK"
- _Op_name_1 = "CASECONTINUEDEFEREMPTYFALLFORFORUNTILGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWTCHANTMAPTSTRUCTTINTERTFUNCTARRAYDDDDDDARGINLCALLEFACEITABIDATASPTRCLOSUREVARCFUNCCHECKNILVARDEFVARKILLVARLIVERESULTINLMARKRETJMPGETGEND"
-)
+const _Op_name = "XXXNAMENONAMETYPEPACKLITERALADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCALLPARTCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVNOPCOPYDCLDCLFUNCDCLFIELDDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMULDIVMODLSHRSHANDANDNOTNEWNEWOBJNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERRECOVERRECVRUNESTRSELRECVSELRECV2IOTAREALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFBLOCKBREAKCASECONTINUEDEFEREMPTYFALLFORFORUNTILGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWTCHANTMAPTSTRUCTTINTERTFUNCTARRAYDDDDDDARGINLCALLEFACEITABIDATASPTRCLOSUREVARCFUNCCHECKNILVARDEFVARKILLVARLIVERESULTINLMARKRETJMPGETGEND"
-var (
- _Op_index_0 = [...]uint16{0, 3, 7, 13, 17, 21, 28, 31, 34, 36, 39, 45, 49, 55, 61, 70, 82, 91, 100, 112, 121, 123, 126, 136, 143, 150, 157, 161, 165, 173, 181, 190, 198, 201, 206, 213, 220, 226, 235, 243, 251, 257, 261, 270, 277, 281, 284, 291, 299, 307, 314, 320, 323, 329, 336, 344, 348, 355, 363, 365, 367, 369, 371, 373, 375, 380, 385, 393, 396, 405, 408, 412, 420, 427, 436, 439, 442, 445, 448, 451, 454, 460, 463, 469, 472, 478, 482, 485, 489, 494, 499, 505, 510, 514, 519, 527, 535, 541, 550, 561, 568, 572, 579, 586, 594, 598, 602, 606, 613, 620, 628, 634, 639, 644}
- _Op_index_1 = [...]uint8{0, 4, 12, 17, 22, 26, 29, 37, 41, 43, 48, 50, 55, 61, 67, 73, 79, 84, 88, 95, 101, 106, 112, 115, 121, 128, 133, 137, 142, 146, 156, 161, 169, 175, 182, 189, 195, 202, 208, 212, 215}
-)
+var _Op_index = [...]uint16{0, 3, 7, 13, 17, 21, 28, 31, 34, 36, 39, 45, 49, 55, 61, 70, 82, 91, 100, 112, 121, 123, 126, 136, 143, 150, 157, 161, 165, 173, 181, 190, 198, 201, 206, 213, 220, 226, 235, 243, 251, 257, 261, 270, 277, 281, 284, 291, 299, 307, 314, 320, 323, 329, 336, 344, 348, 355, 363, 365, 367, 369, 371, 373, 375, 380, 385, 393, 396, 405, 408, 412, 420, 427, 436, 439, 442, 445, 448, 451, 454, 460, 463, 469, 472, 478, 482, 485, 489, 494, 499, 505, 510, 514, 519, 527, 535, 541, 550, 561, 568, 572, 579, 586, 594, 598, 602, 606, 613, 620, 628, 634, 639, 644, 648, 656, 661, 666, 670, 673, 681, 685, 687, 692, 694, 699, 705, 711, 717, 723, 728, 732, 739, 745, 750, 756, 759, 765, 772, 777, 781, 786, 790, 800, 805, 813, 819, 826, 833, 839, 846, 852, 856, 859}
func (i Op) String() string {
- switch {
- case 0 <= i && i <= 112:
- return _Op_name_0[_Op_index_0[i]:_Op_index_0[i+1]]
- case 114 <= i && i <= 153:
- i -= 114
- return _Op_name_1[_Op_index_1[i]:_Op_index_1[i+1]]
- default:
+ if i >= Op(len(_Op_index)-1) {
return "Op(" + strconv.FormatInt(int64(i), 10) + ")"
}
+ return _Op_name[_Op_index[i]:_Op_index[i+1]]
}
diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go
index e8900edd3a..c26ed6251b 100644
--- a/src/cmd/compile/internal/gc/syntax.go
+++ b/src/cmd/compile/internal/gc/syntax.go
@@ -704,7 +704,6 @@ const (
// statements
OBLOCK // { List } (block of code)
OBREAK // break [Sym]
- _ // For toolstash -cmp. TODO(mdempsky): Remove.
OCASE // case List: Nbody (List==nil means default)
OCONTINUE // continue [Sym]
ODEFER // defer Left (Left must be call)
From 99aa56a437be4f72da2eefd2cce1c09fe8d7c201 Mon Sep 17 00:00:00 2001
From: Robert Griesemer
Date: Tue, 17 Sep 2019 16:32:11 -0700
Subject: [PATCH 033/199] go/parser: return partial result from ParseExpr in
case of error
Remove redundant code and improve documentation in the process.
Fixes #34211.
Change-Id: I9a6d1467f1a2c98a163f41f9df147fc6500c6fad
Reviewed-on: https://go-review.googlesource.com/c/go/+/196077
Reviewed-by: Ian Lance Taylor
---
src/go/parser/interface.go | 21 +++++++++++++--------
src/go/parser/parser_test.go | 9 ++++++++-
2 files changed, 21 insertions(+), 9 deletions(-)
diff --git a/src/go/parser/interface.go b/src/go/parser/interface.go
index 0c3f824c98..500c98d496 100644
--- a/src/go/parser/interface.go
+++ b/src/go/parser/interface.go
@@ -75,7 +75,7 @@ const (
// indicates the specific failure. If the source was read but syntax
// errors were found, the result is a partial AST (with ast.Bad* nodes
// representing the fragments of erroneous source code). Multiple errors
-// are returned via a scanner.ErrorList which is sorted by file position.
+// are returned via a scanner.ErrorList which is sorted by source position.
//
func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode) (f *ast.File, err error) {
if fset == nil {
@@ -173,6 +173,12 @@ func ParseDir(fset *token.FileSet, path string, filter func(os.FileInfo) bool, m
// be a valid Go (type or value) expression. Specifically, fset must not
// be nil.
//
+// If the source couldn't be read, the returned AST is nil and the error
+// indicates the specific failure. If the source was read but syntax
+// errors were found, the result is a partial AST (with ast.Bad* nodes
+// representing the fragments of erroneous source code). Multiple errors
+// are returned via a scanner.ErrorList which is sorted by source position.
+//
func ParseExprFrom(fset *token.FileSet, filename string, src interface{}, mode Mode) (expr ast.Expr, err error) {
if fset == nil {
panic("parser.ParseExprFrom: no token.FileSet provided (fset == nil)")
@@ -204,7 +210,7 @@ func ParseExprFrom(fset *token.FileSet, filename string, src interface{}, mode M
// in case of an erroneous x.
p.openScope()
p.pkgScope = p.topScope
- e := p.parseRhsOrType()
+ expr = p.parseRhsOrType()
p.closeScope()
assert(p.topScope == nil, "unbalanced scopes")
@@ -215,18 +221,17 @@ func ParseExprFrom(fset *token.FileSet, filename string, src interface{}, mode M
}
p.expect(token.EOF)
- if p.errors.Len() > 0 {
- p.errors.Sort()
- return nil, p.errors.Err()
- }
-
- return e, nil
+ return
}
// ParseExpr is a convenience function for obtaining the AST of an expression x.
// The position information recorded in the AST is undefined. The filename used
// in error messages is the empty string.
//
+// If syntax errors were found, the result is a partial AST (with ast.Bad* nodes
+// representing the fragments of erroneous source code). Multiple errors are
+// returned via a scanner.ErrorList which is sorted by source position.
+//
func ParseExpr(x string) (ast.Expr, error) {
return ParseExprFrom(token.NewFileSet(), "", []byte(x), 0)
}
diff --git a/src/go/parser/parser_test.go b/src/go/parser/parser_test.go
index 18c05bce20..25a374eeef 100644
--- a/src/go/parser/parser_test.go
+++ b/src/go/parser/parser_test.go
@@ -108,9 +108,16 @@ func TestParseExpr(t *testing.T) {
// an invalid expression
src = "a + *"
- if _, err := ParseExpr(src); err == nil {
+ x, err = ParseExpr(src)
+ if err == nil {
t.Errorf("ParseExpr(%q): got no error", src)
}
+ if x == nil {
+ t.Errorf("ParseExpr(%q): got no (partial) result", src)
+ }
+ if _, ok := x.(*ast.BinaryExpr); !ok {
+ t.Errorf("ParseExpr(%q): got %T, want *ast.BinaryExpr", src, x)
+ }
// a valid expression followed by extra tokens is invalid
src = "a[i] := x"
From 770fac4586a9c8c4647cb6ff79443ec246ae32c4 Mon Sep 17 00:00:00 2001
From: Robert Griesemer
Date: Tue, 17 Sep 2019 15:45:40 -0700
Subject: [PATCH 034/199] math/big: avoid MinExp exponent wrap-around in 'x'
Text format
Fixes #34343.
Change-Id: I74240c8f431f6596338633a86a7a5ee1fce70a65
Reviewed-on: https://go-review.googlesource.com/c/go/+/196057
Run-TryBot: Robert Griesemer
TryBot-Result: Gobot Gobot
Reviewed-by: Ian Lance Taylor
---
src/math/big/floatconv_test.go | 4 ++++
src/math/big/ftoa.go | 3 +--
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/src/math/big/floatconv_test.go b/src/math/big/floatconv_test.go
index c6c6ba63e5..3aa6834143 100644
--- a/src/math/big/floatconv_test.go
+++ b/src/math/big/floatconv_test.go
@@ -536,6 +536,10 @@ func TestFloatText(t *testing.T) {
{"-8191.53125", ToNegativeInf, 53, 'x', 4, "-0x1.fff9p+12"},
{"8191.53125", ToPositiveInf, 53, 'x', 4, "0x1.fff9p+12"},
{"-8191.53125", ToPositiveInf, 53, 'x', 4, "-0x1.fff8p+12"},
+
+ // issue 34343
+ {"0x.8p-2147483648", ToNearestEven, 4, 'p', -1, "0x.8p-2147483648"},
+ {"0x.8p-2147483648", ToNearestEven, 4, 'x', -1, "0x1p-2147483649"},
} {
f, _, err := ParseFloat(test.x, 0, test.prec, ToNearestEven)
if err != nil {
diff --git a/src/math/big/ftoa.go b/src/math/big/ftoa.go
index 6cae63ed09..5506e6e425 100644
--- a/src/math/big/ftoa.go
+++ b/src/math/big/ftoa.go
@@ -384,7 +384,7 @@ func (x *Float) fmtX(buf []byte, prec int) []byte {
case w > n:
m = nat(nil).shr(m, w-n)
}
- exp := x.exp - 1
+ exp64 := int64(x.exp) - 1 // avoid wrap-around
hm := m.utoa(16)
if debugFloat && hm[0] != '1' {
@@ -397,7 +397,6 @@ func (x *Float) fmtX(buf []byte, prec int) []byte {
}
buf = append(buf, 'p')
- exp64 := int64(exp)
if exp64 >= 0 {
buf = append(buf, '+')
} else {
From 85fc76534169b61c4797d792e2593288daa987c5 Mon Sep 17 00:00:00 2001
From: Matthew Dempsky
Date: Fri, 13 Sep 2019 16:02:23 -0700
Subject: [PATCH 035/199] cmd/compile: optimize switch on strings
When compiling expression switches, we try to optimize runs of
constants into binary searches. The ordering used isn't visible to the
application, so it's unimportant as long as we're consistent between
sorting and searching.
For strings, it's much cheaper to compare string lengths than strings
themselves, so instead of ordering strings by "si <= sj", we currently
order them by "len(si) < len(sj) || len(si) == len(sj) && si <= sj"
(i.e., the lexicographical ordering on the 2-tuple (len(s), s)).
However, it's also somewhat cheaper to compare strings for equality
(i.e., ==) than for ordering (i.e., <=). And if there were two or
three string constants of the same length in a switch statement, we
might unnecessarily emit ordering comparisons.
For example, given:
switch s {
case "", "1", "2", "3": // ordered by length then content
goto L
}
we currently compile this as:
if len(s) < 1 || len(s) == 1 && s <= "1" {
if s == "" { goto L }
else if s == "1" { goto L }
} else {
if s == "2" { goto L }
else if s == "3" { goto L }
}
This CL switches to using a 2-level binary search---first on len(s),
then on s itself---so that string ordering comparisons are only needed
when there are 4 or more strings of the same length. (4 being the
cut-off for when using binary search is actually worthwhile.)
So the above switch instead now compiles to:
if len(s) == 0 {
if s == "" { goto L }
} else if len(s) == 1 {
if s == "1" { goto L }
else if s == "2" { goto L }
else if s == "3" { goto L }
}
which is better optimized by walk and SSA. (Notably, because there are
only two distinct lengths and no more than three strings of any
particular length, this example ends up falling back to simply using
linear search.)
Test case by khr@ from CL 195138.
Fixes #33934.
Change-Id: I8eeebcaf7e26343223be5f443d6a97a0daf84f07
Reviewed-on: https://go-review.googlesource.com/c/go/+/195340
Run-TryBot: Matthew Dempsky
TryBot-Result: Gobot Gobot
Reviewed-by: Brad Fitzpatrick
Reviewed-by: Keith Randall
---
src/cmd/compile/internal/gc/swt.go | 70 ++++++++++++++++++++----------
test/codegen/switch.go | 22 ++++++++++
2 files changed, 68 insertions(+), 24 deletions(-)
create mode 100644 test/codegen/switch.go
diff --git a/src/cmd/compile/internal/gc/swt.go b/src/cmd/compile/internal/gc/swt.go
index d53efefa72..a97e9735da 100644
--- a/src/cmd/compile/internal/gc/swt.go
+++ b/src/cmd/compile/internal/gc/swt.go
@@ -349,24 +349,52 @@ func (s *exprSwitch) flush() {
// (e.g., sort.Slice doesn't need to invoke the less function
// when there's only a single slice element).
- // Sort strings by length and then by value.
- // It is much cheaper to compare lengths than values,
- // and all we need here is consistency.
- // We respect this sorting below.
- sort.Slice(cc, func(i, j int) bool {
- vi := cc[i].lo.Val()
- vj := cc[j].lo.Val()
-
- if s.exprname.Type.IsString() {
- si := vi.U.(string)
- sj := vj.U.(string)
+ if s.exprname.Type.IsString() && len(cc) >= 2 {
+ // Sort strings by length and then by value. It is
+ // much cheaper to compare lengths than values, and
+ // all we need here is consistency. We respect this
+ // sorting below.
+ sort.Slice(cc, func(i, j int) bool {
+ si := strlit(cc[i].lo)
+ sj := strlit(cc[j].lo)
if len(si) != len(sj) {
return len(si) < len(sj)
}
return si < sj
+ })
+
+ // runLen returns the string length associated with a
+ // particular run of exprClauses.
+ runLen := func(run []exprClause) int64 { return int64(len(strlit(run[0].lo))) }
+
+ // Collapse runs of consecutive strings with the same length.
+ var runs [][]exprClause
+ start := 0
+ for i := 1; i < len(cc); i++ {
+ if runLen(cc[start:]) != runLen(cc[i:]) {
+ runs = append(runs, cc[start:i])
+ start = i
+ }
}
+ runs = append(runs, cc[start:])
+
+ // Perform two-level binary search.
+ nlen := nod(OLEN, s.exprname, nil)
+ binarySearch(len(runs), &s.done,
+ func(i int) *Node {
+ return nod(OLE, nlen, nodintconst(runLen(runs[i-1])))
+ },
+ func(i int, nif *Node) {
+ run := runs[i]
+ nif.Left = nod(OEQ, nlen, nodintconst(runLen(run)))
+ s.search(run, &nif.Nbody)
+ },
+ )
+ return
+ }
- return compareOp(vi, OLT, vj)
+ sort.Slice(cc, func(i, j int) bool {
+ return compareOp(cc[i].lo.Val(), OLT, cc[j].lo.Val())
})
// Merge consecutive integer cases.
@@ -383,19 +411,13 @@ func (s *exprSwitch) flush() {
cc = merged
}
- binarySearch(len(cc), &s.done,
+ s.search(cc, &s.done)
+}
+
+func (s *exprSwitch) search(cc []exprClause, out *Nodes) {
+ binarySearch(len(cc), out,
func(i int) *Node {
- mid := cc[i-1].hi
-
- le := nod(OLE, s.exprname, mid)
- if s.exprname.Type.IsString() {
- // Compare strings by length and then
- // by value; see sort.Slice above.
- lenlt := nod(OLT, nod(OLEN, s.exprname, nil), nod(OLEN, mid, nil))
- leneq := nod(OEQ, nod(OLEN, s.exprname, nil), nod(OLEN, mid, nil))
- le = nod(OOROR, lenlt, nod(OANDAND, leneq, le))
- }
- return le
+ return nod(OLE, s.exprname, cc[i-1].hi)
},
func(i int, nif *Node) {
c := &cc[i]
diff --git a/test/codegen/switch.go b/test/codegen/switch.go
new file mode 100644
index 0000000000..2ac817d14c
--- /dev/null
+++ b/test/codegen/switch.go
@@ -0,0 +1,22 @@
+// asmcheck
+
+// Copyright 2019 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.
+
+// These tests check code generation of switch statements.
+
+package codegen
+
+// see issue 33934
+func f(x string) int {
+ // amd64:-`cmpstring`
+ switch x {
+ case "":
+ return -1
+ case "1", "2", "3":
+ return -2
+ default:
+ return -3
+ }
+}
From d3595f71712ce1b322f754ef985005e87fac6d44 Mon Sep 17 00:00:00 2001
From: Tobias Klauser
Date: Tue, 17 Sep 2019 09:05:02 +0200
Subject: [PATCH 036/199] syscall: skip TestAmbientCapsUserns if user
namespaces are not supported
Fixes #34015
Change-Id: I29798fb9c72b6f4bee8aecea96ab13b4cba2e80d
Reviewed-on: https://go-review.googlesource.com/c/go/+/195738
Run-TryBot: Tobias Klauser
TryBot-Result: Gobot Gobot
Reviewed-by: Brad Fitzpatrick
---
src/syscall/exec_linux_test.go | 23 ++++++++++++++---------
1 file changed, 14 insertions(+), 9 deletions(-)
diff --git a/src/syscall/exec_linux_test.go b/src/syscall/exec_linux_test.go
index cc2140f811..ee864ac0d4 100644
--- a/src/syscall/exec_linux_test.go
+++ b/src/syscall/exec_linux_test.go
@@ -42,6 +42,18 @@ func skipInContainer(t *testing.T) {
}
}
+func skipNoUserNamespaces(t *testing.T) {
+ if _, err := os.Stat("/proc/self/ns/user"); err != nil {
+ if os.IsNotExist(err) {
+ t.Skip("kernel doesn't support user namespaces")
+ }
+ if os.IsPermission(err) {
+ t.Skip("unable to test user namespaces due to permissions")
+ }
+ t.Fatalf("Failed to stat /proc/self/ns/user: %v", err)
+ }
+}
+
func skipUnprivilegedUserClone(t *testing.T) {
// Skip the test if the sysctl that prevents unprivileged user
// from creating user namespaces is enabled.
@@ -64,15 +76,7 @@ func isChrooted(t *testing.T) bool {
func checkUserNS(t *testing.T) {
skipInContainer(t)
- if _, err := os.Stat("/proc/self/ns/user"); err != nil {
- if os.IsNotExist(err) {
- t.Skip("kernel doesn't support user namespaces")
- }
- if os.IsPermission(err) {
- t.Skip("unable to test user namespaces due to permissions")
- }
- t.Fatalf("Failed to stat /proc/self/ns/user: %v", err)
- }
+ skipNoUserNamespaces(t)
if isChrooted(t) {
// create_user_ns in the kernel (see
// https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/kernel/user_namespace.c)
@@ -573,6 +577,7 @@ func TestAmbientCaps(t *testing.T) {
}
func TestAmbientCapsUserns(t *testing.T) {
+ skipNoUserNamespaces(t)
testAmbientCaps(t, true)
}
From eb6ce1cff479b002712b1d587edba062146ed040 Mon Sep 17 00:00:00 2001
From: "Bryan C. Mills"
Date: Tue, 17 Sep 2019 16:33:54 -0400
Subject: [PATCH 037/199] cmd/go: support -trimpath with gccgo
Fixes #32162
Change-Id: I164665108fa8ae299229054bded82cb3b027bccb
Reviewed-on: https://go-review.googlesource.com/c/go/+/196023
Run-TryBot: Bryan C. Mills
TryBot-Result: Gobot Gobot
Reviewed-by: Ian Lance Taylor
---
src/cmd/go/internal/work/gccgo.go | 9 +++-
src/cmd/go/testdata/script/build_trimpath.txt | 43 +++++++++++++------
2 files changed, 38 insertions(+), 14 deletions(-)
diff --git a/src/cmd/go/internal/work/gccgo.go b/src/cmd/go/internal/work/gccgo.go
index cf5dba189e..4c1f36dbd6 100644
--- a/src/cmd/go/internal/work/gccgo.go
+++ b/src/cmd/go/internal/work/gccgo.go
@@ -91,6 +91,10 @@ func (tools gccgoToolchain) gc(b *Builder, a *Action, archive string, importcfg
args = append(args, "-I", root)
}
}
+ if cfg.BuildTrimpath && b.gccSupportsFlag(args[:1], "-ffile-prefix-map=a=b") {
+ args = append(args, "-ffile-prefix-map="+base.Cwd+"=.")
+ args = append(args, "-ffile-prefix-map="+b.WorkDir+"=/tmp/go-build")
+ }
args = append(args, a.Package.Internal.Gccgoflags...)
for _, f := range gofiles {
args = append(args, mkAbs(p.Dir, f))
@@ -535,7 +539,10 @@ func (tools gccgoToolchain) cc(b *Builder, a *Action, ofile, cfile string) error
defs = append(defs, "-fsplit-stack")
}
defs = tools.maybePIC(defs)
- if b.gccSupportsFlag(compiler, "-fdebug-prefix-map=a=b") {
+ if b.gccSupportsFlag(compiler, "-ffile-prefix-map=a=b") {
+ defs = append(defs, "-ffile-prefix-map="+base.Cwd+"=.")
+ defs = append(defs, "-ffile-prefix-map="+b.WorkDir+"=/tmp/go-build")
+ } else if b.gccSupportsFlag(compiler, "-fdebug-prefix-map=a=b") {
defs = append(defs, "-fdebug-prefix-map="+b.WorkDir+"=/tmp/go-build")
}
if b.gccSupportsFlag(compiler, "-gno-record-gcc-switches") {
diff --git a/src/cmd/go/testdata/script/build_trimpath.txt b/src/cmd/go/testdata/script/build_trimpath.txt
index 668f75599e..ec817a5ecd 100644
--- a/src/cmd/go/testdata/script/build_trimpath.txt
+++ b/src/cmd/go/testdata/script/build_trimpath.txt
@@ -16,10 +16,10 @@ grep -q $GOROOT_REGEXP hello.exe
go build -trimpath -o hello.exe hello.go
! grep -q $GOROOT_REGEXP hello.exe
! grep -q $WORK_REGEXP hello.exe
-cd ..
# A binary from an external module built with -trimpath should not contain
# the current workspace or GOROOT.
+cd $WORK
env GO111MODULE=on
go build -trimpath -o fortune.exe rsc.io/fortune
! grep -q $GOROOT_REGEXP fortune.exe
@@ -27,18 +27,35 @@ go build -trimpath -o fortune.exe rsc.io/fortune
# Two binaries built from identical packages in different directories
# should be identical.
-mkdir b
-cp a/go.mod a/hello.go b
-cd a
-go build -trimpath -o ../a.exe .
-cd ../b
-go build -trimpath -o ../b.exe .
-cd ..
-cmp -q a.exe b.exe
+cd $GOPATH/src/a
+go build -trimpath -o $WORK/a-GOPATH.exe .
+cd $WORK/_alt/src/a
+go build -trimpath -o $WORK/a-alt.exe .
+cmp -q $WORK/a-GOPATH.exe $WORK/a-alt.exe
+
+[!exec:gccgo] stop
+
+# Binaries built using gccgo should also be identical to each other.
+env GO111MODULE=off # The current released gccgo does not support builds in module mode.
+cd $GOPATH/src/a
+go build -compiler=gccgo -trimpath -o $WORK/gccgo-GOPATH.exe .
+
+env old_gopath=$GOPATH
+env GOPATH=$WORK/_alt
+cd $WORK/_alt/src/a
+go build -compiler=gccgo -trimpath -o $WORK/gccgo-alt.exe .
+cd $WORK
+! grep -q $GOROOT_REGEXP gccgo-GOPATH.exe
+! grep -q $WORK_REGEXP gccgo-GOPATH.exe
+cmp -q gccgo-GOPATH.exe gccgo-alt.exe
--- a/hello.go --
+-- $GOPATH/src/a/hello.go --
package main
func main() { println("hello") }
-
--- a/go.mod --
-module m
+-- $GOPATH/src/a/go.mod --
+module example.com/a
+-- $WORK/_alt/src/a/hello.go --
+package main
+func main() { println("hello") }
+-- $WORK/_alt/src/a/go.mod --
+module example.com/a
From 7987238d9cfcef5f79ccd9458e59e22d8a8b3cf2 Mon Sep 17 00:00:00 2001
From: Lynn Boger
Date: Thu, 12 Sep 2019 09:22:07 -0400
Subject: [PATCH 038/199] cmd/asm,cmd/compile: clean up isel codegen on ppc64x
This cleans up the isel code generation in ssa for ppc64x.
Current there is no isel op and the isel code is only
generated from pseudo ops in ppc64/ssa.go, and only using
operands with values 0 or 1. When the isel is generated,
there is always a load of 1 into the temp register before it.
This change implements the isel op so it can be used in PPC64.rules,
and can recognize operand values other than 0 or 1. This also
eliminates the forced load of 1, so it will be loaded only if
needed.
This will make the isel code generation consistent with other ops,
and allow future rule changes that can take advantage of having
a more general purpose isel rule.
Change-Id: I363e1dbd3f7f5dfecb53187ad51cce409a8d1f8d
Reviewed-on: https://go-review.googlesource.com/c/go/+/195057
Run-TryBot: Lynn Boger
TryBot-Result: Gobot Gobot
Reviewed-by: Carlos Eduardo Seo
---
src/cmd/asm/internal/arch/ppc64.go | 2 +-
src/cmd/compile/internal/ppc64/ssa.go | 97 +-
src/cmd/compile/internal/ssa/gen/PPC64.rules | 56 +
src/cmd/compile/internal/ssa/gen/PPC64Ops.go | 8 +
src/cmd/compile/internal/ssa/opGen.go | 31 +
src/cmd/compile/internal/ssa/rewritePPC64.go | 1183 ++++++++++++++++--
6 files changed, 1222 insertions(+), 155 deletions(-)
diff --git a/src/cmd/asm/internal/arch/ppc64.go b/src/cmd/asm/internal/arch/ppc64.go
index 7e3d55b540..3139665ba5 100644
--- a/src/cmd/asm/internal/arch/ppc64.go
+++ b/src/cmd/asm/internal/arch/ppc64.go
@@ -47,7 +47,7 @@ func IsPPC64ISEL(op obj.As) bool {
// one of the CMP instructions that require special handling.
func IsPPC64CMP(op obj.As) bool {
switch op {
- case ppc64.ACMP, ppc64.ACMPU, ppc64.ACMPW, ppc64.ACMPWU:
+ case ppc64.ACMP, ppc64.ACMPU, ppc64.ACMPW, ppc64.ACMPWU, ppc64.AFCMPU:
return true
}
return false
diff --git a/src/cmd/compile/internal/ppc64/ssa.go b/src/cmd/compile/internal/ppc64/ssa.go
index cbe233f054..c45842efe6 100644
--- a/src/cmd/compile/internal/ppc64/ssa.go
+++ b/src/cmd/compile/internal/ppc64/ssa.go
@@ -15,28 +15,6 @@ import (
"strings"
)
-// iselOp encodes mapping of comparison operations onto ISEL operands
-type iselOp struct {
- cond int64
- valueIfCond int // if cond is true, the value to return (0 or 1)
-}
-
-// Input registers to ISEL used for comparison. Index 0 is zero, 1 is (will be) 1
-var iselRegs = [2]int16{ppc64.REG_R0, ppc64.REGTMP}
-
-var iselOps = map[ssa.Op]iselOp{
- ssa.OpPPC64Equal: {cond: ppc64.C_COND_EQ, valueIfCond: 1},
- ssa.OpPPC64NotEqual: {cond: ppc64.C_COND_EQ, valueIfCond: 0},
- ssa.OpPPC64LessThan: {cond: ppc64.C_COND_LT, valueIfCond: 1},
- ssa.OpPPC64GreaterEqual: {cond: ppc64.C_COND_LT, valueIfCond: 0},
- ssa.OpPPC64GreaterThan: {cond: ppc64.C_COND_GT, valueIfCond: 1},
- ssa.OpPPC64LessEqual: {cond: ppc64.C_COND_GT, valueIfCond: 0},
- ssa.OpPPC64FLessThan: {cond: ppc64.C_COND_LT, valueIfCond: 1},
- ssa.OpPPC64FGreaterThan: {cond: ppc64.C_COND_GT, valueIfCond: 1},
- ssa.OpPPC64FLessEqual: {cond: ppc64.C_COND_LT, valueIfCond: 1}, // 2 comparisons, 2nd is EQ
- ssa.OpPPC64FGreaterEqual: {cond: ppc64.C_COND_GT, valueIfCond: 1}, // 2 comparisons, 2nd is EQ
-}
-
// markMoves marks any MOVXconst ops that need to avoid clobbering flags.
func ssaMarkMoves(s *gc.SSAGenState, b *ssa.Block) {
// flive := b.FlagsLiveAtEnd
@@ -120,17 +98,6 @@ func storeByType(t *types.Type) obj.As {
panic("bad store type")
}
-func ssaGenISEL(s *gc.SSAGenState, v *ssa.Value, cr int64, r1, r2 int16) {
- r := v.Reg()
- p := s.Prog(ppc64.AISEL)
- p.To.Type = obj.TYPE_REG
- p.To.Reg = r
- p.Reg = r1
- p.SetFrom3(obj.Addr{Type: obj.TYPE_REG, Reg: r2})
- p.From.Type = obj.TYPE_CONST
- p.From.Offset = cr
-}
-
func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
switch v.Op {
case ssa.OpCopy:
@@ -843,43 +810,32 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.To.Reg = v.Args[0].Reg()
gc.AddAux(&p.To, v)
- case ssa.OpPPC64Equal,
- ssa.OpPPC64NotEqual,
- ssa.OpPPC64LessThan,
- ssa.OpPPC64FLessThan,
- ssa.OpPPC64LessEqual,
- ssa.OpPPC64GreaterThan,
- ssa.OpPPC64FGreaterThan,
- ssa.OpPPC64GreaterEqual:
-
- // On Power7 or later, can use isel instruction:
- // for a < b, a > b, a = b:
- // rtmp := 1
- // isel rt,rtmp,r0,cond // rt is target in ppc asm
-
- // for a >= b, a <= b, a != b:
- // rtmp := 1
- // isel rt,0,rtmp,!cond // rt is target in ppc asm
-
- p := s.Prog(ppc64.AMOVD)
- p.From.Type = obj.TYPE_CONST
- p.From.Offset = 1
+ case ssa.OpPPC64ISEL, ssa.OpPPC64ISELB:
+ // ISEL, ISELB
+ // AuxInt value indicates condition: 0=LT 1=GT 2=EQ 4=GE 5=LE 6=NE
+ // ISEL only accepts 0, 1, 2 condition values but the others can be
+ // achieved by swapping operand order.
+ // arg0 ? arg1 : arg2 with conditions LT, GT, EQ
+ // arg0 ? arg2 : arg1 for conditions GE, LE, NE
+ // ISELB is used when a boolean result is needed, returning 0 or 1
+ p := s.Prog(ppc64.AISEL)
p.To.Type = obj.TYPE_REG
- p.To.Reg = iselRegs[1]
- iop := iselOps[v.Op]
- ssaGenISEL(s, v, iop.cond, iselRegs[iop.valueIfCond], iselRegs[1-iop.valueIfCond])
-
- case ssa.OpPPC64FLessEqual, // These include a second branch for EQ -- dealing with NaN prevents REL= to !REL conversion
- ssa.OpPPC64FGreaterEqual:
-
- p := s.Prog(ppc64.AMOVD)
+ p.To.Reg = v.Reg()
+ // For ISELB, boolean result 0 or 1. Use R0 for 0 operand to avoid load.
+ r := obj.Addr{Type: obj.TYPE_REG, Reg: ppc64.REG_R0}
+ if v.Op == ssa.OpPPC64ISEL {
+ r.Reg = v.Args[1].Reg()
+ }
+ // AuxInt values 4,5,6 implemented with reverse operand order from 0,1,2
+ if v.AuxInt > 3 {
+ p.Reg = r.Reg
+ p.SetFrom3(obj.Addr{Type: obj.TYPE_REG, Reg: v.Args[0].Reg()})
+ } else {
+ p.Reg = v.Args[0].Reg()
+ p.SetFrom3(r)
+ }
p.From.Type = obj.TYPE_CONST
- p.From.Offset = 1
- p.To.Type = obj.TYPE_REG
- p.To.Reg = iselRegs[1]
- iop := iselOps[v.Op]
- ssaGenISEL(s, v, iop.cond, iselRegs[iop.valueIfCond], iselRegs[1-iop.valueIfCond])
- ssaGenISEL(s, v, ppc64.C_COND_EQ, iselRegs[1], v.Reg())
+ p.From.Offset = v.AuxInt & 3
case ssa.OpPPC64LoweredZero:
@@ -1265,6 +1221,11 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
gc.Warnl(v.Pos, "generated nil check")
}
+ // These should be resolved by rules and not make it here.
+ case ssa.OpPPC64Equal, ssa.OpPPC64NotEqual, ssa.OpPPC64LessThan, ssa.OpPPC64FLessThan,
+ ssa.OpPPC64LessEqual, ssa.OpPPC64GreaterThan, ssa.OpPPC64FGreaterThan, ssa.OpPPC64GreaterEqual,
+ ssa.OpPPC64FLessEqual, ssa.OpPPC64FGreaterEqual:
+ v.Fatalf("Pseudo-op should not make it to codegen: %s ###\n", v.LongString())
case ssa.OpPPC64InvertFlags:
v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
case ssa.OpPPC64FlagEQ, ssa.OpPPC64FlagLT, ssa.OpPPC64FlagGT:
diff --git a/src/cmd/compile/internal/ssa/gen/PPC64.rules b/src/cmd/compile/internal/ssa/gen/PPC64.rules
index 01656df610..b247a0b99e 100644
--- a/src/cmd/compile/internal/ssa/gen/PPC64.rules
+++ b/src/cmd/compile/internal/ssa/gen/PPC64.rules
@@ -542,6 +542,9 @@
((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)
+(CondSelect x y bool) && flagArg(bool) != nil -> (ISEL [2] x y bool)
+(CondSelect x y bool) && flagArg(bool) == nil -> (ISEL [2] x y (CMPWconst [0] bool))
+
// Lowering loads
(Load ptr mem) && (is64BitInt(t) || isPtr(t)) -> (MOVDload ptr mem)
(Load ptr mem) && is32BitInt(t) && isSigned(t) -> (MOVWload ptr mem)
@@ -1019,6 +1022,59 @@
(CMPWU x (MOVDconst [c])) && isU16Bit(c) -> (CMPWUconst x [c])
(CMPWU (MOVDconst [c]) y) && isU16Bit(c) -> (InvertFlags (CMPWUconst y [c]))
+// ISEL auxInt values 0=LT 1=GT 2=EQ arg2 ? arg0 : arg1
+// ISEL auxInt values 4=GE 5=LE 6=NE arg2 ? arg1 : arg0
+// ISELB special case where arg0, arg1 values are 0, 1
+
+(Equal cmp) -> (ISELB [2] (MOVDconst [1]) cmp)
+(NotEqual cmp) -> (ISELB [6] (MOVDconst [1]) cmp)
+(LessThan cmp) -> (ISELB [0] (MOVDconst [1]) cmp)
+(FLessThan cmp) -> (ISELB [0] (MOVDconst [1]) cmp)
+(FLessEqual cmp) -> (ISEL [2] (MOVDconst [1]) (ISELB [0] (MOVDconst [1]) cmp) cmp)
+(GreaterEqual cmp) -> (ISELB [4] (MOVDconst [1]) cmp)
+(GreaterThan cmp) -> (ISELB [1] (MOVDconst [1]) cmp)
+(FGreaterThan cmp) -> (ISELB [1] (MOVDconst [1]) cmp)
+(FGreaterEqual cmp) -> (ISEL [2] (MOVDconst [1]) (ISELB [1] (MOVDconst [1]) cmp) cmp)
+(LessEqual cmp) -> (ISELB [5] (MOVDconst [1]) cmp)
+
+(ISELB [0] _ (FlagLT)) -> (MOVDconst [1])
+(ISELB [0] _ (Flag(GT|EQ))) -> (MOVDconst [0])
+(ISELB [1] _ (FlagGT)) -> (MOVDconst [1])
+(ISELB [1] _ (Flag(LT|EQ))) -> (MOVDconst [0])
+(ISELB [2] _ (FlagEQ)) -> (MOVDconst [1])
+(ISELB [2] _ (Flag(LT|GT))) -> (MOVDconst [0])
+(ISELB [4] _ (FlagLT)) -> (MOVDconst [0])
+(ISELB [4] _ (Flag(GT|EQ))) -> (MOVDconst [1])
+(ISELB [5] _ (FlagGT)) -> (MOVDconst [0])
+(ISELB [5] _ (Flag(LT|EQ))) -> (MOVDconst [1])
+(ISELB [6] _ (FlagEQ)) -> (MOVDconst [0])
+(ISELB [6] _ (Flag(LT|GT))) -> (MOVDconst [1])
+
+(ISEL [2] x _ (FlagEQ)) -> x
+(ISEL [2] _ y (Flag(LT|GT))) -> y
+
+(ISEL [6] _ y (FlagEQ)) -> y
+(ISEL [6] x _ (Flag(LT|GT))) -> x
+
+(ISEL [0] _ y (Flag(EQ|GT))) -> y
+(ISEL [0] x _ (FlagLT)) -> x
+
+(ISEL [5] _ x (Flag(EQ|LT))) -> x
+(ISEL [5] y _ (FlagGT)) -> y
+
+(ISEL [1] _ y (Flag(EQ|LT))) -> y
+(ISEL [1] x _ (FlagGT)) -> x
+
+(ISEL [4] x _ (Flag(EQ|GT))) -> x
+(ISEL [4] _ y (FlagLT)) -> y
+
+(ISELB [n] (MOVDconst [1]) (InvertFlags bool)) && n%4 == 0 -> (ISELB [n+1] (MOVDconst [1]) bool)
+(ISELB [n] (MOVDconst [1]) (InvertFlags bool)) && n%4 == 1 -> (ISELB [n-1] (MOVDconst [1]) bool)
+(ISELB [n] (MOVDconst [1]) (InvertFlags bool)) && n%4 == 2 -> (ISELB [n] (MOVDconst [1]) bool)
+(ISEL [n] x y (InvertFlags bool)) && n%4 == 0 -> (ISEL [n+1] x y bool)
+(ISEL [n] x y (InvertFlags bool)) && n%4 == 1 -> (ISEL [n-1] x y bool)
+(ISEL [n] x y (InvertFlags bool)) && n%4 == 2 -> (ISEL [n] x y bool)
+
// A particular pattern seen in cgo code:
(AND (MOVDconst [c]) x:(MOVBZload _ _)) -> (ANDconst [c&0xFF] x)
(AND x:(MOVBZload _ _) (MOVDconst [c])) -> (ANDconst [c&0xFF] x)
diff --git a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go
index af72774765..5ed8ed41f4 100644
--- a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go
+++ b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go
@@ -140,6 +140,8 @@ func init() {
gp1cr = regInfo{inputs: []regMask{gp | sp | sb}}
gp2cr = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb}}
crgp = regInfo{inputs: nil, outputs: []regMask{gp}}
+ crgp11 = regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}
+ crgp21 = regInfo{inputs: []regMask{gp, gp}, outputs: []regMask{gp}}
gpload = regInfo{inputs: []regMask{gp | sp | sb}, outputs: []regMask{gp}}
gploadidx = regInfo{inputs: []regMask{gp | sp | sb, gp}, outputs: []regMask{gp}}
gpstore = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb}}
@@ -365,6 +367,12 @@ func init() {
{name: "CMPWconst", argLength: 1, reg: gp1cr, asm: "CMPW", aux: "Int32", typ: "Flags"},
{name: "CMPWUconst", argLength: 1, reg: gp1cr, asm: "CMPWU", aux: "Int32", typ: "Flags"},
+ // ISEL auxInt values 0=LT 1=GT 2=EQ arg2 ? arg0 : arg1
+ // ISEL auxInt values 4=GE 5=LE 6=NE arg2 ? arg1 : arg0
+ // ISELB special case where arg0, arg1 values are 0, 1 for boolean result
+ {name: "ISEL", argLength: 3, reg: crgp21, asm: "ISEL", aux: "Int32", typ: "Int32"}, // see above
+ {name: "ISELB", argLength: 2, reg: crgp11, asm: "ISEL", aux: "Int32", typ: "Int32"}, // see above
+
// pseudo-ops
{name: "Equal", argLength: 1, reg: crgp}, // bool, true flags encode x==y false otherwise.
{name: "NotEqual", argLength: 1, reg: crgp}, // bool, true flags encode x!=y false otherwise.
diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go
index ab3ffcbe19..69d33e3bd5 100644
--- a/src/cmd/compile/internal/ssa/opGen.go
+++ b/src/cmd/compile/internal/ssa/opGen.go
@@ -1811,6 +1811,8 @@ const (
OpPPC64CMPUconst
OpPPC64CMPWconst
OpPPC64CMPWUconst
+ OpPPC64ISEL
+ OpPPC64ISELB
OpPPC64Equal
OpPPC64NotEqual
OpPPC64LessThan
@@ -24212,6 +24214,35 @@ var opcodeTable = [...]opInfo{
},
},
},
+ {
+ name: "ISEL",
+ auxType: auxInt32,
+ argLen: 3,
+ asm: ppc64.AISEL,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {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
+ {1, 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
+ },
+ 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: "ISELB",
+ auxType: auxInt32,
+ argLen: 2,
+ asm: ppc64.AISEL,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {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
+ },
+ 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: "Equal",
argLen: 1,
diff --git a/src/cmd/compile/internal/ssa/rewritePPC64.go b/src/cmd/compile/internal/ssa/rewritePPC64.go
index 1bc16515d4..33b2bc057f 100644
--- a/src/cmd/compile/internal/ssa/rewritePPC64.go
+++ b/src/cmd/compile/internal/ssa/rewritePPC64.go
@@ -91,6 +91,8 @@ func rewriteValuePPC64(v *Value) bool {
return rewriteValuePPC64_OpCom64_0(v)
case OpCom8:
return rewriteValuePPC64_OpCom8_0(v)
+ case OpCondSelect:
+ return rewriteValuePPC64_OpCondSelect_0(v)
case OpConst16:
return rewriteValuePPC64_OpConst16_0(v)
case OpConst32:
@@ -429,6 +431,14 @@ func rewriteValuePPC64(v *Value) bool {
return rewriteValuePPC64_OpPPC64FCEIL_0(v)
case OpPPC64FFLOOR:
return rewriteValuePPC64_OpPPC64FFLOOR_0(v)
+ case OpPPC64FGreaterEqual:
+ return rewriteValuePPC64_OpPPC64FGreaterEqual_0(v)
+ case OpPPC64FGreaterThan:
+ return rewriteValuePPC64_OpPPC64FGreaterThan_0(v)
+ case OpPPC64FLessEqual:
+ return rewriteValuePPC64_OpPPC64FLessEqual_0(v)
+ case OpPPC64FLessThan:
+ return rewriteValuePPC64_OpPPC64FLessThan_0(v)
case OpPPC64FMOVDload:
return rewriteValuePPC64_OpPPC64FMOVDload_0(v)
case OpPPC64FMOVDstore:
@@ -451,6 +461,10 @@ func rewriteValuePPC64(v *Value) bool {
return rewriteValuePPC64_OpPPC64GreaterEqual_0(v)
case OpPPC64GreaterThan:
return rewriteValuePPC64_OpPPC64GreaterThan_0(v)
+ case OpPPC64ISEL:
+ return rewriteValuePPC64_OpPPC64ISEL_0(v) || rewriteValuePPC64_OpPPC64ISEL_10(v) || rewriteValuePPC64_OpPPC64ISEL_20(v)
+ case OpPPC64ISELB:
+ return rewriteValuePPC64_OpPPC64ISELB_0(v) || rewriteValuePPC64_OpPPC64ISELB_10(v) || rewriteValuePPC64_OpPPC64ISELB_20(v)
case OpPPC64LessEqual:
return rewriteValuePPC64_OpPPC64LessEqual_0(v)
case OpPPC64LessThan:
@@ -1296,6 +1310,47 @@ func rewriteValuePPC64_OpCom8_0(v *Value) bool {
return true
}
}
+func rewriteValuePPC64_OpCondSelect_0(v *Value) bool {
+ b := v.Block
+ // match: (CondSelect x y bool)
+ // cond: flagArg(bool) != nil
+ // result: (ISEL [2] x y bool)
+ for {
+ bool := v.Args[2]
+ x := v.Args[0]
+ y := v.Args[1]
+ if !(flagArg(bool) != nil) {
+ break
+ }
+ v.reset(OpPPC64ISEL)
+ v.AuxInt = 2
+ v.AddArg(x)
+ v.AddArg(y)
+ v.AddArg(bool)
+ return true
+ }
+ // match: (CondSelect x y bool)
+ // cond: flagArg(bool) == nil
+ // result: (ISEL [2] x y (CMPWconst [0] bool))
+ for {
+ bool := v.Args[2]
+ x := v.Args[0]
+ y := v.Args[1]
+ if !(flagArg(bool) == nil) {
+ break
+ }
+ v.reset(OpPPC64ISEL)
+ v.AuxInt = 2
+ v.AddArg(x)
+ v.AddArg(y)
+ v0 := b.NewValue0(v.Pos, OpPPC64CMPWconst, types.TypeFlags)
+ v0.AuxInt = 0
+ v0.AddArg(bool)
+ v.AddArg(v0)
+ return true
+ }
+ return false
+}
func rewriteValuePPC64_OpConst16_0(v *Value) bool {
// match: (Const16 [val])
// cond:
@@ -6441,6 +6496,8 @@ func rewriteValuePPC64_OpPPC64CMPconst_0(v *Value) bool {
return false
}
func rewriteValuePPC64_OpPPC64Equal_0(v *Value) bool {
+ b := v.Block
+ typ := &b.Func.Config.Types
// match: (Equal (FlagEQ))
// cond:
// result: (MOVDconst [1])
@@ -6490,7 +6547,19 @@ func rewriteValuePPC64_OpPPC64Equal_0(v *Value) bool {
v.AddArg(x)
return true
}
- return false
+ // match: (Equal cmp)
+ // cond:
+ // result: (ISELB [2] (MOVDconst [1]) cmp)
+ for {
+ cmp := v.Args[0]
+ v.reset(OpPPC64ISELB)
+ v.AuxInt = 2
+ v0 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64)
+ v0.AuxInt = 1
+ v.AddArg(v0)
+ v.AddArg(cmp)
+ return true
+ }
}
func rewriteValuePPC64_OpPPC64FABS_0(v *Value) bool {
// match: (FABS (FMOVDconst [x]))
@@ -6616,6 +6685,88 @@ func rewriteValuePPC64_OpPPC64FFLOOR_0(v *Value) bool {
}
return false
}
+func rewriteValuePPC64_OpPPC64FGreaterEqual_0(v *Value) bool {
+ b := v.Block
+ typ := &b.Func.Config.Types
+ // match: (FGreaterEqual cmp)
+ // cond:
+ // result: (ISEL [2] (MOVDconst [1]) (ISELB [1] (MOVDconst [1]) cmp) cmp)
+ for {
+ cmp := v.Args[0]
+ v.reset(OpPPC64ISEL)
+ v.AuxInt = 2
+ v0 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64)
+ v0.AuxInt = 1
+ v.AddArg(v0)
+ v1 := b.NewValue0(v.Pos, OpPPC64ISELB, typ.Int32)
+ v1.AuxInt = 1
+ v2 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64)
+ v2.AuxInt = 1
+ v1.AddArg(v2)
+ v1.AddArg(cmp)
+ v.AddArg(v1)
+ v.AddArg(cmp)
+ return true
+ }
+}
+func rewriteValuePPC64_OpPPC64FGreaterThan_0(v *Value) bool {
+ b := v.Block
+ typ := &b.Func.Config.Types
+ // match: (FGreaterThan cmp)
+ // cond:
+ // result: (ISELB [1] (MOVDconst [1]) cmp)
+ for {
+ cmp := v.Args[0]
+ v.reset(OpPPC64ISELB)
+ v.AuxInt = 1
+ v0 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64)
+ v0.AuxInt = 1
+ v.AddArg(v0)
+ v.AddArg(cmp)
+ return true
+ }
+}
+func rewriteValuePPC64_OpPPC64FLessEqual_0(v *Value) bool {
+ b := v.Block
+ typ := &b.Func.Config.Types
+ // match: (FLessEqual cmp)
+ // cond:
+ // result: (ISEL [2] (MOVDconst [1]) (ISELB [0] (MOVDconst [1]) cmp) cmp)
+ for {
+ cmp := v.Args[0]
+ v.reset(OpPPC64ISEL)
+ v.AuxInt = 2
+ v0 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64)
+ v0.AuxInt = 1
+ v.AddArg(v0)
+ v1 := b.NewValue0(v.Pos, OpPPC64ISELB, typ.Int32)
+ v1.AuxInt = 0
+ v2 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64)
+ v2.AuxInt = 1
+ v1.AddArg(v2)
+ v1.AddArg(cmp)
+ v.AddArg(v1)
+ v.AddArg(cmp)
+ return true
+ }
+}
+func rewriteValuePPC64_OpPPC64FLessThan_0(v *Value) bool {
+ b := v.Block
+ typ := &b.Func.Config.Types
+ // match: (FLessThan cmp)
+ // cond:
+ // result: (ISELB [0] (MOVDconst [1]) cmp)
+ for {
+ cmp := v.Args[0]
+ v.reset(OpPPC64ISELB)
+ v.AuxInt = 0
+ v0 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64)
+ v0.AuxInt = 1
+ v.AddArg(v0)
+ v.AddArg(cmp)
+ return true
+ }
+}
func rewriteValuePPC64_OpPPC64FMOVDload_0(v *Value) bool {
// match: (FMOVDload [off] {sym} ptr (MOVDstore [off] {sym} ptr x _))
// cond:
@@ -6974,6 +7125,8 @@ func rewriteValuePPC64_OpPPC64FTRUNC_0(v *Value) bool {
return false
}
func rewriteValuePPC64_OpPPC64GreaterEqual_0(v *Value) bool {
+ b := v.Block
+ typ := &b.Func.Config.Types
// match: (GreaterEqual (FlagEQ))
// cond:
// result: (MOVDconst [1])
@@ -7023,9 +7176,23 @@ func rewriteValuePPC64_OpPPC64GreaterEqual_0(v *Value) bool {
v.AddArg(x)
return true
}
- return false
+ // match: (GreaterEqual cmp)
+ // cond:
+ // result: (ISELB [4] (MOVDconst [1]) cmp)
+ for {
+ cmp := v.Args[0]
+ v.reset(OpPPC64ISELB)
+ v.AuxInt = 4
+ v0 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64)
+ v0.AuxInt = 1
+ v.AddArg(v0)
+ v.AddArg(cmp)
+ return true
+ }
}
func rewriteValuePPC64_OpPPC64GreaterThan_0(v *Value) bool {
+ b := v.Block
+ typ := &b.Func.Config.Types
// match: (GreaterThan (FlagEQ))
// cond:
// result: (MOVDconst [0])
@@ -7075,142 +7242,972 @@ func rewriteValuePPC64_OpPPC64GreaterThan_0(v *Value) bool {
v.AddArg(x)
return true
}
- return false
-}
-func rewriteValuePPC64_OpPPC64LessEqual_0(v *Value) bool {
- // match: (LessEqual (FlagEQ))
+ // match: (GreaterThan cmp)
// cond:
- // result: (MOVDconst [1])
+ // result: (ISELB [1] (MOVDconst [1]) cmp)
for {
- v_0 := v.Args[0]
- if v_0.Op != OpPPC64FlagEQ {
- break
- }
- v.reset(OpPPC64MOVDconst)
+ cmp := v.Args[0]
+ v.reset(OpPPC64ISELB)
v.AuxInt = 1
+ v0 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64)
+ v0.AuxInt = 1
+ v.AddArg(v0)
+ v.AddArg(cmp)
return true
}
- // match: (LessEqual (FlagLT))
+}
+func rewriteValuePPC64_OpPPC64ISEL_0(v *Value) bool {
+ // match: (ISEL [2] x _ (FlagEQ))
// cond:
- // result: (MOVDconst [1])
+ // result: x
for {
- v_0 := v.Args[0]
- if v_0.Op != OpPPC64FlagLT {
+ if v.AuxInt != 2 {
break
}
- v.reset(OpPPC64MOVDconst)
- v.AuxInt = 1
- return true
- }
- // match: (LessEqual (FlagGT))
- // cond:
- // result: (MOVDconst [0])
- for {
- v_0 := v.Args[0]
- if v_0.Op != OpPPC64FlagGT {
+ _ = v.Args[2]
+ x := v.Args[0]
+ v_2 := v.Args[2]
+ if v_2.Op != OpPPC64FlagEQ {
break
}
- v.reset(OpPPC64MOVDconst)
- v.AuxInt = 0
+ v.reset(OpCopy)
+ v.Type = x.Type
+ v.AddArg(x)
return true
}
- // match: (LessEqual (InvertFlags x))
+ // match: (ISEL [2] _ y (FlagLT))
// cond:
- // result: (GreaterEqual x)
+ // result: y
for {
- v_0 := v.Args[0]
- if v_0.Op != OpPPC64InvertFlags {
+ if v.AuxInt != 2 {
break
}
- x := v_0.Args[0]
- v.reset(OpPPC64GreaterEqual)
- v.AddArg(x)
+ _ = v.Args[2]
+ y := v.Args[1]
+ v_2 := v.Args[2]
+ if v_2.Op != OpPPC64FlagLT {
+ break
+ }
+ v.reset(OpCopy)
+ v.Type = y.Type
+ v.AddArg(y)
return true
}
- return false
-}
-func rewriteValuePPC64_OpPPC64LessThan_0(v *Value) bool {
- // match: (LessThan (FlagEQ))
+ // match: (ISEL [2] _ y (FlagGT))
// cond:
- // result: (MOVDconst [0])
+ // result: y
for {
- v_0 := v.Args[0]
- if v_0.Op != OpPPC64FlagEQ {
+ if v.AuxInt != 2 {
break
}
- v.reset(OpPPC64MOVDconst)
- v.AuxInt = 0
+ _ = v.Args[2]
+ y := v.Args[1]
+ v_2 := v.Args[2]
+ if v_2.Op != OpPPC64FlagGT {
+ break
+ }
+ v.reset(OpCopy)
+ v.Type = y.Type
+ v.AddArg(y)
return true
}
- // match: (LessThan (FlagLT))
+ // match: (ISEL [6] _ y (FlagEQ))
// cond:
- // result: (MOVDconst [1])
+ // result: y
for {
- v_0 := v.Args[0]
- if v_0.Op != OpPPC64FlagLT {
+ if v.AuxInt != 6 {
break
}
- v.reset(OpPPC64MOVDconst)
- v.AuxInt = 1
+ _ = v.Args[2]
+ y := v.Args[1]
+ v_2 := v.Args[2]
+ if v_2.Op != OpPPC64FlagEQ {
+ break
+ }
+ v.reset(OpCopy)
+ v.Type = y.Type
+ v.AddArg(y)
return true
}
- // match: (LessThan (FlagGT))
+ // match: (ISEL [6] x _ (FlagLT))
// cond:
- // result: (MOVDconst [0])
+ // result: x
for {
- v_0 := v.Args[0]
- if v_0.Op != OpPPC64FlagGT {
+ if v.AuxInt != 6 {
break
}
- v.reset(OpPPC64MOVDconst)
- v.AuxInt = 0
+ _ = v.Args[2]
+ x := v.Args[0]
+ v_2 := v.Args[2]
+ if v_2.Op != OpPPC64FlagLT {
+ break
+ }
+ v.reset(OpCopy)
+ v.Type = x.Type
+ v.AddArg(x)
return true
}
- // match: (LessThan (InvertFlags x))
+ // match: (ISEL [6] x _ (FlagGT))
// cond:
- // result: (GreaterThan x)
+ // result: x
for {
- v_0 := v.Args[0]
- if v_0.Op != OpPPC64InvertFlags {
+ if v.AuxInt != 6 {
break
}
- x := v_0.Args[0]
- v.reset(OpPPC64GreaterThan)
+ _ = v.Args[2]
+ x := v.Args[0]
+ v_2 := v.Args[2]
+ if v_2.Op != OpPPC64FlagGT {
+ break
+ }
+ v.reset(OpCopy)
+ v.Type = x.Type
v.AddArg(x)
return true
}
- return false
-}
-func rewriteValuePPC64_OpPPC64MFVSRD_0(v *Value) bool {
- b := v.Block
- typ := &b.Func.Config.Types
- // match: (MFVSRD (FMOVDconst [c]))
+ // match: (ISEL [0] _ y (FlagEQ))
// cond:
- // result: (MOVDconst [c])
+ // result: y
for {
- v_0 := v.Args[0]
- if v_0.Op != OpPPC64FMOVDconst {
+ if v.AuxInt != 0 {
break
}
- c := v_0.AuxInt
- v.reset(OpPPC64MOVDconst)
- v.AuxInt = c
+ _ = v.Args[2]
+ y := v.Args[1]
+ v_2 := v.Args[2]
+ if v_2.Op != OpPPC64FlagEQ {
+ break
+ }
+ v.reset(OpCopy)
+ v.Type = y.Type
+ v.AddArg(y)
return true
}
- // match: (MFVSRD x:(FMOVDload [off] {sym} ptr mem))
- // cond: x.Uses == 1 && clobber(x)
- // result: @x.Block (MOVDload [off] {sym} ptr mem)
+ // match: (ISEL [0] _ y (FlagGT))
+ // cond:
+ // result: y
for {
- x := v.Args[0]
- if x.Op != OpPPC64FMOVDload {
+ if v.AuxInt != 0 {
break
}
- off := x.AuxInt
- sym := x.Aux
- mem := x.Args[1]
- ptr := x.Args[0]
- if !(x.Uses == 1 && clobber(x)) {
- break
+ _ = v.Args[2]
+ y := v.Args[1]
+ v_2 := v.Args[2]
+ if v_2.Op != OpPPC64FlagGT {
+ break
+ }
+ v.reset(OpCopy)
+ v.Type = y.Type
+ v.AddArg(y)
+ return true
+ }
+ // match: (ISEL [0] x _ (FlagLT))
+ // cond:
+ // result: x
+ for {
+ if v.AuxInt != 0 {
+ break
+ }
+ _ = v.Args[2]
+ x := v.Args[0]
+ v_2 := v.Args[2]
+ if v_2.Op != OpPPC64FlagLT {
+ break
+ }
+ v.reset(OpCopy)
+ v.Type = x.Type
+ v.AddArg(x)
+ return true
+ }
+ // match: (ISEL [5] _ x (FlagEQ))
+ // cond:
+ // result: x
+ for {
+ if v.AuxInt != 5 {
+ break
+ }
+ _ = v.Args[2]
+ x := v.Args[1]
+ v_2 := v.Args[2]
+ if v_2.Op != OpPPC64FlagEQ {
+ break
+ }
+ v.reset(OpCopy)
+ v.Type = x.Type
+ v.AddArg(x)
+ return true
+ }
+ return false
+}
+func rewriteValuePPC64_OpPPC64ISEL_10(v *Value) bool {
+ // match: (ISEL [5] _ x (FlagLT))
+ // cond:
+ // result: x
+ for {
+ if v.AuxInt != 5 {
+ break
+ }
+ _ = v.Args[2]
+ x := v.Args[1]
+ v_2 := v.Args[2]
+ if v_2.Op != OpPPC64FlagLT {
+ break
+ }
+ v.reset(OpCopy)
+ v.Type = x.Type
+ v.AddArg(x)
+ return true
+ }
+ // match: (ISEL [5] y _ (FlagGT))
+ // cond:
+ // result: y
+ for {
+ if v.AuxInt != 5 {
+ break
+ }
+ _ = v.Args[2]
+ y := v.Args[0]
+ v_2 := v.Args[2]
+ if v_2.Op != OpPPC64FlagGT {
+ break
+ }
+ v.reset(OpCopy)
+ v.Type = y.Type
+ v.AddArg(y)
+ return true
+ }
+ // match: (ISEL [1] _ y (FlagEQ))
+ // cond:
+ // result: y
+ for {
+ if v.AuxInt != 1 {
+ break
+ }
+ _ = v.Args[2]
+ y := v.Args[1]
+ v_2 := v.Args[2]
+ if v_2.Op != OpPPC64FlagEQ {
+ break
+ }
+ v.reset(OpCopy)
+ v.Type = y.Type
+ v.AddArg(y)
+ return true
+ }
+ // match: (ISEL [1] _ y (FlagLT))
+ // cond:
+ // result: y
+ for {
+ if v.AuxInt != 1 {
+ break
+ }
+ _ = v.Args[2]
+ y := v.Args[1]
+ v_2 := v.Args[2]
+ if v_2.Op != OpPPC64FlagLT {
+ break
+ }
+ v.reset(OpCopy)
+ v.Type = y.Type
+ v.AddArg(y)
+ return true
+ }
+ // match: (ISEL [1] x _ (FlagGT))
+ // cond:
+ // result: x
+ for {
+ if v.AuxInt != 1 {
+ break
+ }
+ _ = v.Args[2]
+ x := v.Args[0]
+ v_2 := v.Args[2]
+ if v_2.Op != OpPPC64FlagGT {
+ break
+ }
+ v.reset(OpCopy)
+ v.Type = x.Type
+ v.AddArg(x)
+ return true
+ }
+ // match: (ISEL [4] x _ (FlagEQ))
+ // cond:
+ // result: x
+ for {
+ if v.AuxInt != 4 {
+ break
+ }
+ _ = v.Args[2]
+ x := v.Args[0]
+ v_2 := v.Args[2]
+ if v_2.Op != OpPPC64FlagEQ {
+ break
+ }
+ v.reset(OpCopy)
+ v.Type = x.Type
+ v.AddArg(x)
+ return true
+ }
+ // match: (ISEL [4] x _ (FlagGT))
+ // cond:
+ // result: x
+ for {
+ if v.AuxInt != 4 {
+ break
+ }
+ _ = v.Args[2]
+ x := v.Args[0]
+ v_2 := v.Args[2]
+ if v_2.Op != OpPPC64FlagGT {
+ break
+ }
+ v.reset(OpCopy)
+ v.Type = x.Type
+ v.AddArg(x)
+ return true
+ }
+ // match: (ISEL [4] _ y (FlagLT))
+ // cond:
+ // result: y
+ for {
+ if v.AuxInt != 4 {
+ break
+ }
+ _ = v.Args[2]
+ y := v.Args[1]
+ v_2 := v.Args[2]
+ if v_2.Op != OpPPC64FlagLT {
+ break
+ }
+ v.reset(OpCopy)
+ v.Type = y.Type
+ v.AddArg(y)
+ return true
+ }
+ // match: (ISEL [n] x y (InvertFlags bool))
+ // cond: n%4 == 0
+ // result: (ISEL [n+1] x y bool)
+ for {
+ n := v.AuxInt
+ _ = v.Args[2]
+ x := v.Args[0]
+ y := v.Args[1]
+ v_2 := v.Args[2]
+ if v_2.Op != OpPPC64InvertFlags {
+ break
+ }
+ bool := v_2.Args[0]
+ if !(n%4 == 0) {
+ break
+ }
+ v.reset(OpPPC64ISEL)
+ v.AuxInt = n + 1
+ v.AddArg(x)
+ v.AddArg(y)
+ v.AddArg(bool)
+ return true
+ }
+ // match: (ISEL [n] x y (InvertFlags bool))
+ // cond: n%4 == 1
+ // result: (ISEL [n-1] x y bool)
+ for {
+ n := v.AuxInt
+ _ = v.Args[2]
+ x := v.Args[0]
+ y := v.Args[1]
+ v_2 := v.Args[2]
+ if v_2.Op != OpPPC64InvertFlags {
+ break
+ }
+ bool := v_2.Args[0]
+ if !(n%4 == 1) {
+ break
+ }
+ v.reset(OpPPC64ISEL)
+ v.AuxInt = n - 1
+ v.AddArg(x)
+ v.AddArg(y)
+ v.AddArg(bool)
+ return true
+ }
+ return false
+}
+func rewriteValuePPC64_OpPPC64ISEL_20(v *Value) bool {
+ // match: (ISEL [n] x y (InvertFlags bool))
+ // cond: n%4 == 2
+ // result: (ISEL [n] x y bool)
+ for {
+ n := v.AuxInt
+ _ = v.Args[2]
+ x := v.Args[0]
+ y := v.Args[1]
+ v_2 := v.Args[2]
+ if v_2.Op != OpPPC64InvertFlags {
+ break
+ }
+ bool := v_2.Args[0]
+ if !(n%4 == 2) {
+ break
+ }
+ v.reset(OpPPC64ISEL)
+ v.AuxInt = n
+ v.AddArg(x)
+ v.AddArg(y)
+ v.AddArg(bool)
+ return true
+ }
+ return false
+}
+func rewriteValuePPC64_OpPPC64ISELB_0(v *Value) bool {
+ // match: (ISELB [0] _ (FlagLT))
+ // cond:
+ // result: (MOVDconst [1])
+ for {
+ if v.AuxInt != 0 {
+ break
+ }
+ _ = v.Args[1]
+ v_1 := v.Args[1]
+ if v_1.Op != OpPPC64FlagLT {
+ break
+ }
+ v.reset(OpPPC64MOVDconst)
+ v.AuxInt = 1
+ return true
+ }
+ // match: (ISELB [0] _ (FlagGT))
+ // cond:
+ // result: (MOVDconst [0])
+ for {
+ if v.AuxInt != 0 {
+ break
+ }
+ _ = v.Args[1]
+ v_1 := v.Args[1]
+ if v_1.Op != OpPPC64FlagGT {
+ break
+ }
+ v.reset(OpPPC64MOVDconst)
+ v.AuxInt = 0
+ return true
+ }
+ // match: (ISELB [0] _ (FlagEQ))
+ // cond:
+ // result: (MOVDconst [0])
+ for {
+ if v.AuxInt != 0 {
+ break
+ }
+ _ = v.Args[1]
+ v_1 := v.Args[1]
+ if v_1.Op != OpPPC64FlagEQ {
+ break
+ }
+ v.reset(OpPPC64MOVDconst)
+ v.AuxInt = 0
+ return true
+ }
+ // match: (ISELB [1] _ (FlagGT))
+ // cond:
+ // result: (MOVDconst [1])
+ for {
+ if v.AuxInt != 1 {
+ break
+ }
+ _ = v.Args[1]
+ v_1 := v.Args[1]
+ if v_1.Op != OpPPC64FlagGT {
+ break
+ }
+ v.reset(OpPPC64MOVDconst)
+ v.AuxInt = 1
+ return true
+ }
+ // match: (ISELB [1] _ (FlagLT))
+ // cond:
+ // result: (MOVDconst [0])
+ for {
+ if v.AuxInt != 1 {
+ break
+ }
+ _ = v.Args[1]
+ v_1 := v.Args[1]
+ if v_1.Op != OpPPC64FlagLT {
+ break
+ }
+ v.reset(OpPPC64MOVDconst)
+ v.AuxInt = 0
+ return true
+ }
+ // match: (ISELB [1] _ (FlagEQ))
+ // cond:
+ // result: (MOVDconst [0])
+ for {
+ if v.AuxInt != 1 {
+ break
+ }
+ _ = v.Args[1]
+ v_1 := v.Args[1]
+ if v_1.Op != OpPPC64FlagEQ {
+ break
+ }
+ v.reset(OpPPC64MOVDconst)
+ v.AuxInt = 0
+ return true
+ }
+ // match: (ISELB [2] _ (FlagEQ))
+ // cond:
+ // result: (MOVDconst [1])
+ for {
+ if v.AuxInt != 2 {
+ break
+ }
+ _ = v.Args[1]
+ v_1 := v.Args[1]
+ if v_1.Op != OpPPC64FlagEQ {
+ break
+ }
+ v.reset(OpPPC64MOVDconst)
+ v.AuxInt = 1
+ return true
+ }
+ // match: (ISELB [2] _ (FlagLT))
+ // cond:
+ // result: (MOVDconst [0])
+ for {
+ if v.AuxInt != 2 {
+ break
+ }
+ _ = v.Args[1]
+ v_1 := v.Args[1]
+ if v_1.Op != OpPPC64FlagLT {
+ break
+ }
+ v.reset(OpPPC64MOVDconst)
+ v.AuxInt = 0
+ return true
+ }
+ // match: (ISELB [2] _ (FlagGT))
+ // cond:
+ // result: (MOVDconst [0])
+ for {
+ if v.AuxInt != 2 {
+ break
+ }
+ _ = v.Args[1]
+ v_1 := v.Args[1]
+ if v_1.Op != OpPPC64FlagGT {
+ break
+ }
+ v.reset(OpPPC64MOVDconst)
+ v.AuxInt = 0
+ return true
+ }
+ // match: (ISELB [4] _ (FlagLT))
+ // cond:
+ // result: (MOVDconst [0])
+ for {
+ if v.AuxInt != 4 {
+ break
+ }
+ _ = v.Args[1]
+ v_1 := v.Args[1]
+ if v_1.Op != OpPPC64FlagLT {
+ break
+ }
+ v.reset(OpPPC64MOVDconst)
+ v.AuxInt = 0
+ return true
+ }
+ return false
+}
+func rewriteValuePPC64_OpPPC64ISELB_10(v *Value) bool {
+ b := v.Block
+ typ := &b.Func.Config.Types
+ // match: (ISELB [4] _ (FlagGT))
+ // cond:
+ // result: (MOVDconst [1])
+ for {
+ if v.AuxInt != 4 {
+ break
+ }
+ _ = v.Args[1]
+ v_1 := v.Args[1]
+ if v_1.Op != OpPPC64FlagGT {
+ break
+ }
+ v.reset(OpPPC64MOVDconst)
+ v.AuxInt = 1
+ return true
+ }
+ // match: (ISELB [4] _ (FlagEQ))
+ // cond:
+ // result: (MOVDconst [1])
+ for {
+ if v.AuxInt != 4 {
+ break
+ }
+ _ = v.Args[1]
+ v_1 := v.Args[1]
+ if v_1.Op != OpPPC64FlagEQ {
+ break
+ }
+ v.reset(OpPPC64MOVDconst)
+ v.AuxInt = 1
+ return true
+ }
+ // match: (ISELB [5] _ (FlagGT))
+ // cond:
+ // result: (MOVDconst [0])
+ for {
+ if v.AuxInt != 5 {
+ break
+ }
+ _ = v.Args[1]
+ v_1 := v.Args[1]
+ if v_1.Op != OpPPC64FlagGT {
+ break
+ }
+ v.reset(OpPPC64MOVDconst)
+ v.AuxInt = 0
+ return true
+ }
+ // match: (ISELB [5] _ (FlagLT))
+ // cond:
+ // result: (MOVDconst [1])
+ for {
+ if v.AuxInt != 5 {
+ break
+ }
+ _ = v.Args[1]
+ v_1 := v.Args[1]
+ if v_1.Op != OpPPC64FlagLT {
+ break
+ }
+ v.reset(OpPPC64MOVDconst)
+ v.AuxInt = 1
+ return true
+ }
+ // match: (ISELB [5] _ (FlagEQ))
+ // cond:
+ // result: (MOVDconst [1])
+ for {
+ if v.AuxInt != 5 {
+ break
+ }
+ _ = v.Args[1]
+ v_1 := v.Args[1]
+ if v_1.Op != OpPPC64FlagEQ {
+ break
+ }
+ v.reset(OpPPC64MOVDconst)
+ v.AuxInt = 1
+ return true
+ }
+ // match: (ISELB [6] _ (FlagEQ))
+ // cond:
+ // result: (MOVDconst [0])
+ for {
+ if v.AuxInt != 6 {
+ break
+ }
+ _ = v.Args[1]
+ v_1 := v.Args[1]
+ if v_1.Op != OpPPC64FlagEQ {
+ break
+ }
+ v.reset(OpPPC64MOVDconst)
+ v.AuxInt = 0
+ return true
+ }
+ // match: (ISELB [6] _ (FlagLT))
+ // cond:
+ // result: (MOVDconst [1])
+ for {
+ if v.AuxInt != 6 {
+ break
+ }
+ _ = v.Args[1]
+ v_1 := v.Args[1]
+ if v_1.Op != OpPPC64FlagLT {
+ break
+ }
+ v.reset(OpPPC64MOVDconst)
+ v.AuxInt = 1
+ return true
+ }
+ // match: (ISELB [6] _ (FlagGT))
+ // cond:
+ // result: (MOVDconst [1])
+ for {
+ if v.AuxInt != 6 {
+ break
+ }
+ _ = v.Args[1]
+ v_1 := v.Args[1]
+ if v_1.Op != OpPPC64FlagGT {
+ break
+ }
+ v.reset(OpPPC64MOVDconst)
+ v.AuxInt = 1
+ return true
+ }
+ // match: (ISELB [n] (MOVDconst [1]) (InvertFlags bool))
+ // cond: n%4 == 0
+ // result: (ISELB [n+1] (MOVDconst [1]) bool)
+ for {
+ n := v.AuxInt
+ _ = v.Args[1]
+ v_0 := v.Args[0]
+ if v_0.Op != OpPPC64MOVDconst {
+ break
+ }
+ if v_0.AuxInt != 1 {
+ break
+ }
+ v_1 := v.Args[1]
+ if v_1.Op != OpPPC64InvertFlags {
+ break
+ }
+ bool := v_1.Args[0]
+ if !(n%4 == 0) {
+ break
+ }
+ v.reset(OpPPC64ISELB)
+ v.AuxInt = n + 1
+ v0 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64)
+ v0.AuxInt = 1
+ v.AddArg(v0)
+ v.AddArg(bool)
+ return true
+ }
+ // match: (ISELB [n] (MOVDconst [1]) (InvertFlags bool))
+ // cond: n%4 == 1
+ // result: (ISELB [n-1] (MOVDconst [1]) bool)
+ for {
+ n := v.AuxInt
+ _ = v.Args[1]
+ v_0 := v.Args[0]
+ if v_0.Op != OpPPC64MOVDconst {
+ break
+ }
+ if v_0.AuxInt != 1 {
+ break
+ }
+ v_1 := v.Args[1]
+ if v_1.Op != OpPPC64InvertFlags {
+ break
+ }
+ bool := v_1.Args[0]
+ if !(n%4 == 1) {
+ break
+ }
+ v.reset(OpPPC64ISELB)
+ v.AuxInt = n - 1
+ v0 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64)
+ v0.AuxInt = 1
+ v.AddArg(v0)
+ v.AddArg(bool)
+ return true
+ }
+ return false
+}
+func rewriteValuePPC64_OpPPC64ISELB_20(v *Value) bool {
+ b := v.Block
+ typ := &b.Func.Config.Types
+ // match: (ISELB [n] (MOVDconst [1]) (InvertFlags bool))
+ // cond: n%4 == 2
+ // result: (ISELB [n] (MOVDconst [1]) bool)
+ for {
+ n := v.AuxInt
+ _ = v.Args[1]
+ v_0 := v.Args[0]
+ if v_0.Op != OpPPC64MOVDconst {
+ break
+ }
+ if v_0.AuxInt != 1 {
+ break
+ }
+ v_1 := v.Args[1]
+ if v_1.Op != OpPPC64InvertFlags {
+ break
+ }
+ bool := v_1.Args[0]
+ if !(n%4 == 2) {
+ break
+ }
+ v.reset(OpPPC64ISELB)
+ v.AuxInt = n
+ v0 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64)
+ v0.AuxInt = 1
+ v.AddArg(v0)
+ v.AddArg(bool)
+ return true
+ }
+ return false
+}
+func rewriteValuePPC64_OpPPC64LessEqual_0(v *Value) bool {
+ b := v.Block
+ typ := &b.Func.Config.Types
+ // match: (LessEqual (FlagEQ))
+ // cond:
+ // result: (MOVDconst [1])
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpPPC64FlagEQ {
+ break
+ }
+ v.reset(OpPPC64MOVDconst)
+ v.AuxInt = 1
+ return true
+ }
+ // match: (LessEqual (FlagLT))
+ // cond:
+ // result: (MOVDconst [1])
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpPPC64FlagLT {
+ break
+ }
+ v.reset(OpPPC64MOVDconst)
+ v.AuxInt = 1
+ return true
+ }
+ // match: (LessEqual (FlagGT))
+ // cond:
+ // result: (MOVDconst [0])
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpPPC64FlagGT {
+ break
+ }
+ v.reset(OpPPC64MOVDconst)
+ v.AuxInt = 0
+ return true
+ }
+ // match: (LessEqual (InvertFlags x))
+ // cond:
+ // result: (GreaterEqual x)
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpPPC64InvertFlags {
+ break
+ }
+ x := v_0.Args[0]
+ v.reset(OpPPC64GreaterEqual)
+ v.AddArg(x)
+ return true
+ }
+ // match: (LessEqual cmp)
+ // cond:
+ // result: (ISELB [5] (MOVDconst [1]) cmp)
+ for {
+ cmp := v.Args[0]
+ v.reset(OpPPC64ISELB)
+ v.AuxInt = 5
+ v0 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64)
+ v0.AuxInt = 1
+ v.AddArg(v0)
+ v.AddArg(cmp)
+ return true
+ }
+}
+func rewriteValuePPC64_OpPPC64LessThan_0(v *Value) bool {
+ b := v.Block
+ typ := &b.Func.Config.Types
+ // match: (LessThan (FlagEQ))
+ // cond:
+ // result: (MOVDconst [0])
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpPPC64FlagEQ {
+ break
+ }
+ v.reset(OpPPC64MOVDconst)
+ v.AuxInt = 0
+ return true
+ }
+ // match: (LessThan (FlagLT))
+ // cond:
+ // result: (MOVDconst [1])
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpPPC64FlagLT {
+ break
+ }
+ v.reset(OpPPC64MOVDconst)
+ v.AuxInt = 1
+ return true
+ }
+ // match: (LessThan (FlagGT))
+ // cond:
+ // result: (MOVDconst [0])
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpPPC64FlagGT {
+ break
+ }
+ v.reset(OpPPC64MOVDconst)
+ v.AuxInt = 0
+ return true
+ }
+ // match: (LessThan (InvertFlags x))
+ // cond:
+ // result: (GreaterThan x)
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpPPC64InvertFlags {
+ break
+ }
+ x := v_0.Args[0]
+ v.reset(OpPPC64GreaterThan)
+ v.AddArg(x)
+ return true
+ }
+ // match: (LessThan cmp)
+ // cond:
+ // result: (ISELB [0] (MOVDconst [1]) cmp)
+ for {
+ cmp := v.Args[0]
+ v.reset(OpPPC64ISELB)
+ v.AuxInt = 0
+ v0 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64)
+ v0.AuxInt = 1
+ v.AddArg(v0)
+ v.AddArg(cmp)
+ return true
+ }
+}
+func rewriteValuePPC64_OpPPC64MFVSRD_0(v *Value) bool {
+ b := v.Block
+ typ := &b.Func.Config.Types
+ // match: (MFVSRD (FMOVDconst [c]))
+ // cond:
+ // result: (MOVDconst [c])
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpPPC64FMOVDconst {
+ break
+ }
+ c := v_0.AuxInt
+ v.reset(OpPPC64MOVDconst)
+ v.AuxInt = c
+ return true
+ }
+ // match: (MFVSRD x:(FMOVDload [off] {sym} ptr mem))
+ // cond: x.Uses == 1 && clobber(x)
+ // result: @x.Block (MOVDload [off] {sym} ptr mem)
+ for {
+ x := v.Args[0]
+ if x.Op != OpPPC64FMOVDload {
+ break
+ }
+ off := x.AuxInt
+ sym := x.Aux
+ mem := x.Args[1]
+ ptr := x.Args[0]
+ if !(x.Uses == 1 && clobber(x)) {
+ break
}
b = x.Block
v0 := b.NewValue0(x.Pos, OpPPC64MOVDload, typ.Int64)
@@ -12030,6 +13027,8 @@ func rewriteValuePPC64_OpPPC64MaskIfNotCarry_0(v *Value) bool {
return false
}
func rewriteValuePPC64_OpPPC64NotEqual_0(v *Value) bool {
+ b := v.Block
+ typ := &b.Func.Config.Types
// match: (NotEqual (FlagEQ))
// cond:
// result: (MOVDconst [0])
@@ -12079,7 +13078,19 @@ func rewriteValuePPC64_OpPPC64NotEqual_0(v *Value) bool {
v.AddArg(x)
return true
}
- return false
+ // match: (NotEqual cmp)
+ // cond:
+ // result: (ISELB [6] (MOVDconst [1]) cmp)
+ for {
+ cmp := v.Args[0]
+ v.reset(OpPPC64ISELB)
+ v.AuxInt = 6
+ v0 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64)
+ v0.AuxInt = 1
+ v.AddArg(v0)
+ v.AddArg(cmp)
+ return true
+ }
}
func rewriteValuePPC64_OpPPC64OR_0(v *Value) bool {
b := v.Block
From baf7d95350c9eab317efe769cf113b3611a6ccd0 Mon Sep 17 00:00:00 2001
From: Than McIntosh
Date: Fri, 12 Apr 2019 09:47:43 -0400
Subject: [PATCH 039/199] cmd/go: use alternate debug_modinfo recipe for gccgo
Use a different recipe for capturing debug modinfo if we're compiling
with the gccgo toolchain, to avoid applying a go:linkname directive to
a variable (not supported by gccgo).
Fixes #30344.
Change-Id: I9ce3d42c3bbb809fd68b140f56f9bbe3406c351b
Reviewed-on: https://go-review.googlesource.com/c/go/+/171768
Reviewed-by: Bryan C. Mills
Run-TryBot: Bryan C. Mills
TryBot-Result: Gobot Gobot
---
src/cmd/go/internal/load/pkg.go | 2 +-
src/cmd/go/internal/modload/build.go | 24 +++++++++++++++++++++---
src/cmd/go/internal/work/exec.go | 2 +-
3 files changed, 23 insertions(+), 5 deletions(-)
diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go
index 27efc7c04a..daaa3ab0c1 100644
--- a/src/cmd/go/internal/load/pkg.go
+++ b/src/cmd/go/internal/load/pkg.go
@@ -40,7 +40,7 @@ var (
ModPackageModuleInfo func(path string) *modinfo.ModulePublic // return module info for Package struct
ModImportPaths func(args []string) []*search.Match // expand import paths
ModPackageBuildInfo func(main string, deps []string) string // return module info to embed in binary
- ModInfoProg func(info string) []byte // wrap module info in .go code for binary
+ ModInfoProg func(info string, isgccgo bool) []byte // wrap module info in .go code for binary
ModImportFromFiles func([]string) // update go.mod to add modules for imports in these files
ModDirImportPath func(string) string // return effective import path for directory
)
diff --git a/src/cmd/go/internal/modload/build.go b/src/cmd/go/internal/modload/build.go
index ff42516c80..f8dc0c84ff 100644
--- a/src/cmd/go/internal/modload/build.go
+++ b/src/cmd/go/internal/modload/build.go
@@ -249,16 +249,34 @@ func findModule(target, path string) module.Version {
panic("unreachable")
}
-func ModInfoProg(info string) []byte {
+func ModInfoProg(info string, isgccgo bool) []byte {
// Inject a variable with the debug information as runtime.modinfo,
// but compile it in package main so that it is specific to the binary.
// The variable must be a literal so that it will have the correct value
// before the initializer for package main runs.
//
- // The runtime startup code refers to the variable, which keeps it live in all binaries.
- return []byte(fmt.Sprintf(`package main
+ // The runtime startup code refers to the variable, which keeps it live
+ // in all binaries.
+ //
+ // Note: we use an alternate recipe below for gccgo (based on an
+ // init function) due to the fact that gccgo does not support
+ // applying a "//go:linkname" directive to a variable. This has
+ // drawbacks in that other packages may want to look at the module
+ // info in their init functions (see issue 29628), which won't
+ // work for gccgo. See also issue 30344.
+
+ if !isgccgo {
+ return []byte(fmt.Sprintf(`package main
import _ "unsafe"
//go:linkname __debug_modinfo__ runtime.modinfo
var __debug_modinfo__ = %q
`, string(infoStart)+info+string(infoEnd)))
+ } else {
+ return []byte(fmt.Sprintf(`package main
+import _ "unsafe"
+//go:linkname __set_debug_modinfo__ runtime..z2fdebug.setmodinfo
+func __set_debug_modinfo__(string)
+func init() { __set_debug_modinfo__(%q) }
+ `, string(infoStart)+info+string(infoEnd)))
+ }
}
diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go
index 626cacfe99..b75c61b6f2 100644
--- a/src/cmd/go/internal/work/exec.go
+++ b/src/cmd/go/internal/work/exec.go
@@ -663,7 +663,7 @@ func (b *Builder) build(a *Action) (err error) {
}
if p.Internal.BuildInfo != "" && cfg.ModulesEnabled {
- if err := b.writeFile(objdir+"_gomod_.go", load.ModInfoProg(p.Internal.BuildInfo)); err != nil {
+ if err := b.writeFile(objdir+"_gomod_.go", load.ModInfoProg(p.Internal.BuildInfo, cfg.BuildToolchainName == "gccgo")); err != nil {
return err
}
gofiles = append(gofiles, objdir+"_gomod_.go")
From 1aa64b55f1edc68b2e8f4b301e33f4a9588f7c1f Mon Sep 17 00:00:00 2001
From: Jeremy Faller
Date: Wed, 18 Sep 2019 13:59:52 -0400
Subject: [PATCH 040/199] cmd/link: prefix Go syms with _ on darwin
RELNOTE=This change adds an underscore to all Go symbols in darwin, and
the behavior might be confusing to users of tools like "nm", etc.
Fixes #33808
Change-Id: I1849e6618c81215cb9bfa62b678f6f389cd009d5
Reviewed-on: https://go-review.googlesource.com/c/go/+/196217
Run-TryBot: Jeremy Faller
TryBot-Result: Gobot Gobot
Reviewed-by: Cherry Zhang
---
src/cmd/link/internal/ld/issue33808_test.go | 54 +++++++++++++++++++++
src/cmd/link/internal/ld/macho.go | 5 +-
src/debug/macho/file.go | 7 ++-
3 files changed, 63 insertions(+), 3 deletions(-)
create mode 100644 src/cmd/link/internal/ld/issue33808_test.go
diff --git a/src/cmd/link/internal/ld/issue33808_test.go b/src/cmd/link/internal/ld/issue33808_test.go
new file mode 100644
index 0000000000..77eaeb4b31
--- /dev/null
+++ b/src/cmd/link/internal/ld/issue33808_test.go
@@ -0,0 +1,54 @@
+// Copyright 2019 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 ld
+
+import (
+ "internal/testenv"
+ "io/ioutil"
+ "os"
+ "runtime"
+ "strings"
+ "testing"
+)
+
+const prog = `
+package main
+
+import "log"
+
+func main() {
+ log.Fatalf("HERE")
+}
+`
+
+func TestIssue33808(t *testing.T) {
+ if runtime.GOOS != "darwin" {
+ return
+ }
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+
+ dir, err := ioutil.TempDir("", "TestIssue33808")
+ if err != nil {
+ t.Fatalf("could not create directory: %v", err)
+ }
+ defer os.RemoveAll(dir)
+
+ f := gobuild(t, dir, prog, "-ldflags=-linkmode=external")
+ f.Close()
+
+ syms, err := f.Symbols()
+ if err != nil {
+ t.Fatalf("Error reading symbols: %v", err)
+ }
+
+ name := "log.Fatalf"
+ for _, sym := range syms {
+ if strings.Contains(sym.Name, name) {
+ return
+ }
+ }
+ t.Fatalf("Didn't find %v", name)
+}
diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go
index 02e133e31d..7453f37c62 100644
--- a/src/cmd/link/internal/ld/macho.go
+++ b/src/cmd/link/internal/ld/macho.go
@@ -869,6 +869,7 @@ func machosymtab(ctxt *Link) {
symtab.AddUint32(ctxt.Arch, uint32(symstr.Size))
export := machoShouldExport(ctxt, s)
+ isGoSymbol := strings.Contains(s.Extname(), ".")
// In normal buildmodes, only add _ to C symbols, as
// Go symbols have dot in the name.
@@ -877,8 +878,8 @@ func machosymtab(ctxt *Link) {
// symbols like crosscall2 are in pclntab and end up
// pointing at the host binary, breaking unwinding.
// See Issue #18190.
- cexport := !strings.Contains(s.Extname(), ".") && (ctxt.BuildMode != BuildModePlugin || onlycsymbol(s))
- if cexport || export {
+ cexport := !isGoSymbol && (ctxt.BuildMode != BuildModePlugin || onlycsymbol(s))
+ if cexport || export || isGoSymbol {
symstr.AddUint8('_')
}
diff --git a/src/debug/macho/file.go b/src/debug/macho/file.go
index 16708e5247..085b0c8219 100644
--- a/src/debug/macho/file.go
+++ b/src/debug/macho/file.go
@@ -473,7 +473,12 @@ func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset
if n.Name >= uint32(len(strtab)) {
return nil, &FormatError{offset, "invalid name in symbol table", n.Name}
}
- sym.Name = cstring(strtab[n.Name:])
+ // We add "_" to Go symbols. Strip it here. See issue 33808.
+ name := cstring(strtab[n.Name:])
+ if strings.Contains(name, ".") && name[0] == '_' {
+ name = name[1:]
+ }
+ sym.Name = name
sym.Type = n.Type
sym.Sect = n.Sect
sym.Desc = n.Desc
From e70e0a6cbc4004f2449bdeb345dcf68278aaf8fe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Mart=C3=AD?=
Date: Wed, 18 Sep 2019 17:13:46 +0100
Subject: [PATCH 041/199] all: remove trailing whitespace from HTML files
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
I noticed lots of trailing whitespace in one of cmd/trace's HTML files.
While at it, remove a few others from still-maintained files. Leave old
documents alone, such as doc/devel/weekly.html.
Change-Id: I7de7bbb6dd3fe6403bbb1f1178a8d3640c1e537b
Reviewed-on: https://go-review.googlesource.com/c/go/+/196178
Run-TryBot: Daniel Martí
TryBot-Result: Gobot Gobot
Reviewed-by: Brad Fitzpatrick
---
doc/articles/wiki/index.html | 2 +-
doc/gccgo_contribute.html | 2 +-
doc/gccgo_install.html | 2 +-
doc/go_faq.html | 4 ++--
misc/trace/trace_viewer_full.html | 20 ++++++++++----------
5 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/doc/articles/wiki/index.html b/doc/articles/wiki/index.html
index b7ab2cc622..f8144bbac3 100644
--- a/doc/articles/wiki/index.html
+++ b/doc/articles/wiki/index.html
@@ -581,7 +581,7 @@
Validation
First, add "regexp" to the import list.
-Then we can create a global variable to store our validation
+Then we can create a global variable to store our validation
expression:
Changes to the Go frontend should follow the same process as for the
main Go repository, only for the gofrontend project and
-the gofrontend-dev@googlegroups.com mailing list
+the gofrontend-dev@googlegroups.com mailing list
rather than the go project and the
golang-dev@googlegroups.com mailing list. Those changes
will then be merged into the GCC sources.
diff --git a/doc/gccgo_install.html b/doc/gccgo_install.html
index 5b026ba57e..08415a871b 100644
--- a/doc/gccgo_install.html
+++ b/doc/gccgo_install.html
@@ -80,7 +80,7 @@
These considerations led to
-a
+a
series of discussions from which Go arose, first as a set of ideas and
desiderata, then as a language.
An overarching goal was that Go do more to help the working programmer
@@ -1282,7 +1282,7 @@
Companies often permit outgoing traffic only on the standard TCP ports 80 (HTTP)
-and 443 (HTTPS), blocking outgoing traffic on other ports, including TCP port 9418
+and 443 (HTTPS), blocking outgoing traffic on other ports, including TCP port 9418
(git) and TCP port 22 (SSH).
When using HTTPS instead of HTTP, git enforces certificate validation by
default, providing protection against man-in-the-middle, eavesdropping and tampering attacks.
diff --git a/misc/trace/trace_viewer_full.html b/misc/trace/trace_viewer_full.html
index ba9dcc6652..29360698ba 100644
--- a/misc/trace/trace_viewer_full.html
+++ b/misc/trace/trace_viewer_full.html
@@ -397,7 +397,7 @@
-
+
-
+
-
+