aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/gc
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/gc')
-rw-r--r--src/cmd/compile/internal/gc/alg.go156
-rw-r--r--src/cmd/compile/internal/gc/align.go108
-rw-r--r--src/cmd/compile/internal/gc/bench_test.go24
-rw-r--r--src/cmd/compile/internal/gc/bexport.go43
-rw-r--r--src/cmd/compile/internal/gc/builtin.go97
-rw-r--r--src/cmd/compile/internal/gc/builtin/runtime.go5
-rw-r--r--src/cmd/compile/internal/gc/closure.go12
-rw-r--r--src/cmd/compile/internal/gc/const.go191
-rw-r--r--src/cmd/compile/internal/gc/dcl.go32
-rw-r--r--src/cmd/compile/internal/gc/dwinl.go96
-rw-r--r--src/cmd/compile/internal/gc/embed.go273
-rw-r--r--src/cmd/compile/internal/gc/esc.go53
-rw-r--r--src/cmd/compile/internal/gc/escape.go55
-rw-r--r--src/cmd/compile/internal/gc/export.go12
-rw-r--r--src/cmd/compile/internal/gc/float_test.go19
-rw-r--r--src/cmd/compile/internal/gc/fmt.go80
-rw-r--r--src/cmd/compile/internal/gc/go.go22
-rw-r--r--src/cmd/compile/internal/gc/gsubr.go50
-rw-r--r--src/cmd/compile/internal/gc/iexport.go61
-rw-r--r--src/cmd/compile/internal/gc/iimport.go51
-rw-r--r--src/cmd/compile/internal/gc/init.go8
-rw-r--r--src/cmd/compile/internal/gc/inl.go482
-rw-r--r--src/cmd/compile/internal/gc/inl_test.go4
-rw-r--r--src/cmd/compile/internal/gc/lex.go7
-rw-r--r--src/cmd/compile/internal/gc/main.go140
-rw-r--r--src/cmd/compile/internal/gc/noder.go157
-rw-r--r--src/cmd/compile/internal/gc/obj.go169
-rw-r--r--src/cmd/compile/internal/gc/order.go29
-rw-r--r--src/cmd/compile/internal/gc/pgen.go26
-rw-r--r--src/cmd/compile/internal/gc/plive.go430
-rw-r--r--src/cmd/compile/internal/gc/range.go17
-rw-r--r--src/cmd/compile/internal/gc/reflect.go27
-rw-r--r--src/cmd/compile/internal/gc/scc.go15
-rw-r--r--src/cmd/compile/internal/gc/scope.go4
-rw-r--r--src/cmd/compile/internal/gc/sinit.go29
-rw-r--r--src/cmd/compile/internal/gc/ssa.go877
-rw-r--r--src/cmd/compile/internal/gc/subr.go213
-rw-r--r--src/cmd/compile/internal/gc/swt.go29
-rw-r--r--src/cmd/compile/internal/gc/syntax.go197
-rw-r--r--src/cmd/compile/internal/gc/typecheck.go195
-rw-r--r--src/cmd/compile/internal/gc/universe.go10
-rw-r--r--src/cmd/compile/internal/gc/walk.go351
42 files changed, 3012 insertions, 1844 deletions
diff --git a/src/cmd/compile/internal/gc/alg.go b/src/cmd/compile/internal/gc/alg.go
index c9d71ea00b..2f7fa27bb9 100644
--- a/src/cmd/compile/internal/gc/alg.go
+++ b/src/cmd/compile/internal/gc/alg.go
@@ -282,7 +282,7 @@ func genhash(t *types.Type) *obj.LSym {
}
sym := typesymprefix(".hash", t)
- if Debug['r'] != 0 {
+ if Debug.r != 0 {
fmt.Printf("genhash %v %v %v\n", closure, sym, t)
}
@@ -374,7 +374,7 @@ func genhash(t *types.Type) *obj.LSym {
r.List.Append(nh)
fn.Nbody.Append(r)
- if Debug['r'] != 0 {
+ if Debug.r != 0 {
dumplist("genhash body", fn.Nbody)
}
@@ -392,7 +392,7 @@ func genhash(t *types.Type) *obj.LSym {
}
fn.Func.SetNilCheckDisabled(true)
- funccompile(fn)
+ xtop = append(xtop, fn)
// Build closure. It doesn't close over any variables, so
// it contains just the function pointer.
@@ -509,7 +509,7 @@ func geneq(t *types.Type) *obj.LSym {
return closure
}
sym := typesymprefix(".eq", t)
- if Debug['r'] != 0 {
+ if Debug.r != 0 {
fmt.Printf("geneq %v\n", t)
}
@@ -529,6 +529,10 @@ func geneq(t *types.Type) *obj.LSym {
fn := dclfunc(sym, tfn)
np := asNode(tfn.Type.Params().Field(0).Nname)
nq := asNode(tfn.Type.Params().Field(1).Nname)
+ nr := asNode(tfn.Type.Results().Field(0).Nname)
+
+ // Label to jump to if an equality test fails.
+ neq := autolabel(".neq")
// We reach here only for types that have equality but
// cannot be handled by the standard algorithms,
@@ -555,13 +559,13 @@ func geneq(t *types.Type) *obj.LSym {
// for i := 0; i < nelem; i++ {
// if eq(p[i], q[i]) {
// } else {
- // return
+ // goto neq
// }
// }
//
// TODO(josharian): consider doing some loop unrolling
// for larger nelem as well, processing a few elements at a time in a loop.
- checkAll := func(unroll int64, eq func(pi, qi *Node) *Node) {
+ checkAll := func(unroll int64, last bool, eq func(pi, qi *Node) *Node) {
// checkIdx generates a node to check for equality at index i.
checkIdx := func(i *Node) *Node {
// pi := p[i]
@@ -576,37 +580,38 @@ func geneq(t *types.Type) *obj.LSym {
}
if nelem <= unroll {
+ if last {
+ // Do last comparison in a different manner.
+ nelem--
+ }
// Generate a series of checks.
- var cond *Node
for i := int64(0); i < nelem; i++ {
- c := nodintconst(i)
- check := checkIdx(c)
- if cond == nil {
- cond = check
- continue
- }
- cond = nod(OANDAND, cond, check)
+ // if check {} else { goto neq }
+ nif := nod(OIF, checkIdx(nodintconst(i)), nil)
+ nif.Rlist.Append(nodSym(OGOTO, nil, neq))
+ fn.Nbody.Append(nif)
+ }
+ if last {
+ fn.Nbody.Append(nod(OAS, nr, checkIdx(nodintconst(nelem))))
+ }
+ } else {
+ // Generate a for loop.
+ // for i := 0; i < nelem; i++
+ i := temp(types.Types[TINT])
+ init := nod(OAS, i, nodintconst(0))
+ cond := nod(OLT, i, nodintconst(nelem))
+ post := nod(OAS, i, nod(OADD, i, nodintconst(1)))
+ loop := nod(OFOR, cond, post)
+ loop.Ninit.Append(init)
+ // if eq(pi, qi) {} else { goto neq }
+ nif := nod(OIF, checkIdx(i), nil)
+ nif.Rlist.Append(nodSym(OGOTO, nil, neq))
+ loop.Nbody.Append(nif)
+ fn.Nbody.Append(loop)
+ if last {
+ fn.Nbody.Append(nod(OAS, nr, nodbool(true)))
}
- nif := nod(OIF, cond, nil)
- nif.Rlist.Append(nod(ORETURN, nil, nil))
- fn.Nbody.Append(nif)
- return
}
-
- // Generate a for loop.
- // for i := 0; i < nelem; i++
- i := temp(types.Types[TINT])
- init := nod(OAS, i, nodintconst(0))
- cond := nod(OLT, i, nodintconst(nelem))
- post := nod(OAS, i, nod(OADD, i, nodintconst(1)))
- loop := nod(OFOR, cond, post)
- loop.Ninit.Append(init)
- // if eq(pi, qi) {} else { return }
- check := checkIdx(i)
- nif := nod(OIF, check, nil)
- nif.Rlist.Append(nod(ORETURN, nil, nil))
- loop.Nbody.Append(nif)
- fn.Nbody.Append(loop)
}
switch t.Elem().Etype {
@@ -614,32 +619,28 @@ func geneq(t *types.Type) *obj.LSym {
// Do two loops. First, check that all the lengths match (cheap).
// Second, check that all the contents match (expensive).
// TODO: when the array size is small, unroll the length match checks.
- checkAll(3, func(pi, qi *Node) *Node {
+ checkAll(3, false, func(pi, qi *Node) *Node {
// Compare lengths.
eqlen, _ := eqstring(pi, qi)
return eqlen
})
- checkAll(1, func(pi, qi *Node) *Node {
+ checkAll(1, true, func(pi, qi *Node) *Node {
// Compare contents.
_, eqmem := eqstring(pi, qi)
return eqmem
})
case TFLOAT32, TFLOAT64:
- checkAll(2, func(pi, qi *Node) *Node {
+ checkAll(2, true, func(pi, qi *Node) *Node {
// p[i] == q[i]
return nod(OEQ, pi, qi)
})
// TODO: pick apart structs, do them piecemeal too
default:
- checkAll(1, func(pi, qi *Node) *Node {
+ checkAll(1, true, func(pi, qi *Node) *Node {
// p[i] == q[i]
return nod(OEQ, pi, qi)
})
}
- // return true
- ret := nod(ORETURN, nil, nil)
- ret.List.Append(nodbool(true))
- fn.Nbody.Append(ret)
case TSTRUCT:
// Build a list of conditions to satisfy.
@@ -717,22 +718,42 @@ func geneq(t *types.Type) *obj.LSym {
flatConds = append(flatConds, c...)
}
- var cond *Node
if len(flatConds) == 0 {
- cond = nodbool(true)
+ fn.Nbody.Append(nod(OAS, nr, nodbool(true)))
} else {
- cond = flatConds[0]
- for _, c := range flatConds[1:] {
- cond = nod(OANDAND, cond, c)
+ for _, c := range flatConds[:len(flatConds)-1] {
+ // if cond {} else { goto neq }
+ n := nod(OIF, c, nil)
+ n.Rlist.Append(nodSym(OGOTO, nil, neq))
+ fn.Nbody.Append(n)
}
+ fn.Nbody.Append(nod(OAS, nr, flatConds[len(flatConds)-1]))
}
+ }
- ret := nod(ORETURN, nil, nil)
- ret.List.Append(cond)
- fn.Nbody.Append(ret)
+ // ret:
+ // return
+ ret := autolabel(".ret")
+ fn.Nbody.Append(nodSym(OLABEL, nil, ret))
+ fn.Nbody.Append(nod(ORETURN, nil, nil))
+
+ // neq:
+ // r = false
+ // return (or goto ret)
+ fn.Nbody.Append(nodSym(OLABEL, nil, neq))
+ fn.Nbody.Append(nod(OAS, nr, nodbool(false)))
+ if EqCanPanic(t) || hasCall(fn) {
+ // Epilogue is large, so share it with the equal case.
+ fn.Nbody.Append(nodSym(OGOTO, nil, ret))
+ } else {
+ // Epilogue is small, so don't bother sharing.
+ fn.Nbody.Append(nod(ORETURN, nil, nil))
}
+ // TODO(khr): the epilogue size detection condition above isn't perfect.
+ // We should really do a generic CL that shares epilogues across
+ // the board. See #24936.
- if Debug['r'] != 0 {
+ if Debug.r != 0 {
dumplist("geneq body", fn.Nbody)
}
@@ -754,7 +775,7 @@ func geneq(t *types.Type) *obj.LSym {
// neither of which can be nil, and our comparisons
// are shallow.
fn.Func.SetNilCheckDisabled(true)
- funccompile(fn)
+ xtop = append(xtop, fn)
// Generate a closure which points at the function we just generated.
dsymptr(closure, 0, sym.Linksym(), 0)
@@ -762,6 +783,39 @@ func geneq(t *types.Type) *obj.LSym {
return closure
}
+func hasCall(n *Node) bool {
+ if n.Op == OCALL || n.Op == OCALLFUNC {
+ return true
+ }
+ if n.Left != nil && hasCall(n.Left) {
+ return true
+ }
+ if n.Right != nil && hasCall(n.Right) {
+ return true
+ }
+ for _, x := range n.Ninit.Slice() {
+ if hasCall(x) {
+ return true
+ }
+ }
+ for _, x := range n.Nbody.Slice() {
+ if hasCall(x) {
+ return true
+ }
+ }
+ for _, x := range n.List.Slice() {
+ if hasCall(x) {
+ return true
+ }
+ }
+ for _, x := range n.Rlist.Slice() {
+ if hasCall(x) {
+ return true
+ }
+ }
+ return false
+}
+
// eqfield returns the node
// p.field == q.field
func eqfield(p *Node, q *Node, field *types.Sym) *Node {
diff --git a/src/cmd/compile/internal/gc/align.go b/src/cmd/compile/internal/gc/align.go
index ab578ee8c7..a3a0c8fce8 100644
--- a/src/cmd/compile/internal/gc/align.go
+++ b/src/cmd/compile/internal/gc/align.go
@@ -5,7 +5,9 @@
package gc
import (
+ "bytes"
"cmd/compile/internal/types"
+ "fmt"
"sort"
)
@@ -84,7 +86,7 @@ func expandiface(t *types.Type) {
sort.Sort(methcmp(methods))
if int64(len(methods)) >= thearch.MAXWIDTH/int64(Widthptr) {
- yyerror("interface too large")
+ yyerrorl(typePos(t), "interface too large")
}
for i, m := range methods {
m.Offset = int64(i) * int64(Widthptr)
@@ -148,7 +150,7 @@ func widstruct(errtype *types.Type, t *types.Type, o int64, flag int) int64 {
maxwidth = 1<<31 - 1
}
if o >= maxwidth {
- yyerror("type %L too large", errtype)
+ yyerrorl(typePos(errtype), "type %L too large", errtype)
o = 8 // small but nonzero
}
}
@@ -173,6 +175,91 @@ func widstruct(errtype *types.Type, t *types.Type, o int64, flag int) int64 {
return o
}
+// findTypeLoop searches for an invalid type declaration loop involving
+// type t and reports whether one is found. If so, path contains the
+// loop.
+//
+// path points to a slice used for tracking the sequence of types
+// visited. Using a pointer to a slice allows the slice capacity to
+// grow and limit reallocations.
+func findTypeLoop(t *types.Type, path *[]*types.Type) bool {
+ // We implement a simple DFS loop-finding algorithm. This
+ // could be faster, but type cycles are rare.
+
+ if t.Sym != nil {
+ // Declared type. Check for loops and otherwise
+ // recurse on the type expression used in the type
+ // declaration.
+
+ for i, x := range *path {
+ if x == t {
+ *path = (*path)[i:]
+ return true
+ }
+ }
+
+ *path = append(*path, t)
+ if p := asNode(t.Nod).Name.Param; p != nil && findTypeLoop(p.Ntype.Type, path) {
+ return true
+ }
+ *path = (*path)[:len(*path)-1]
+ } else {
+ // Anonymous type. Recurse on contained types.
+
+ switch t.Etype {
+ case TARRAY:
+ if findTypeLoop(t.Elem(), path) {
+ return true
+ }
+ case TSTRUCT:
+ for _, f := range t.Fields().Slice() {
+ if findTypeLoop(f.Type, path) {
+ return true
+ }
+ }
+ case TINTER:
+ for _, m := range t.Methods().Slice() {
+ if m.Type.IsInterface() { // embedded interface
+ if findTypeLoop(m.Type, path) {
+ return true
+ }
+ }
+ }
+ }
+ }
+
+ return false
+}
+
+func reportTypeLoop(t *types.Type) {
+ if t.Broke() {
+ return
+ }
+
+ var l []*types.Type
+ if !findTypeLoop(t, &l) {
+ Fatalf("failed to find type loop for: %v", t)
+ }
+
+ // Rotate loop so that the earliest type declaration is first.
+ i := 0
+ for j, t := range l[1:] {
+ if typePos(t).Before(typePos(l[i])) {
+ i = j + 1
+ }
+ }
+ l = append(l[i:], l[:i]...)
+
+ var msg bytes.Buffer
+ fmt.Fprintf(&msg, "invalid recursive type %v\n", l[0])
+ for _, t := range l {
+ fmt.Fprintf(&msg, "\t%v: %v refers to\n", linestr(typePos(t)), t)
+ t.SetBroke(true)
+ }
+ fmt.Fprintf(&msg, "\t%v: %v", linestr(typePos(l[0])), l[0])
+ yyerrorl(typePos(l[0]), msg.String())
+}
+
// dowidth calculates and stores the size and alignment for t.
// If sizeCalculationDisabled is set, and the size/alignment
// have not already been calculated, it calls Fatal.
@@ -192,11 +279,7 @@ func dowidth(t *types.Type) {
}
if t.Width == -2 {
- if !t.Broke() {
- t.SetBroke(true)
- yyerrorl(asNode(t.Nod).Pos, "invalid recursive type %v", t)
- }
-
+ reportTypeLoop(t)
t.Width = 0
t.Align = 1
return
@@ -298,7 +381,7 @@ func dowidth(t *types.Type) {
t1 := t.ChanArgs()
dowidth(t1) // just in case
if t1.Elem().Width >= 1<<16 {
- yyerror("channel element type too large (>64kB)")
+ yyerrorl(typePos(t1), "channel element type too large (>64kB)")
}
w = 1 // anything will do
@@ -308,10 +391,7 @@ func dowidth(t *types.Type) {
checkwidth(t.Key())
case TFORW: // should have been filled in
- if !t.Broke() {
- t.SetBroke(true)
- yyerror("invalid recursive type %v", t)
- }
+ reportTypeLoop(t)
w = 1 // anything will do
case TANY:
@@ -334,7 +414,7 @@ func dowidth(t *types.Type) {
if t.Elem().Width != 0 {
cap := (uint64(thearch.MAXWIDTH) - 1) / uint64(t.Elem().Width)
if uint64(t.NumElem()) > cap {
- yyerror("type %L larger than address space", t)
+ yyerrorl(typePos(t), "type %L larger than address space", t)
}
}
w = t.NumElem() * t.Elem().Width
@@ -376,7 +456,7 @@ func dowidth(t *types.Type) {
}
if Widthptr == 4 && w != int64(int32(w)) {
- yyerror("type %v too large", t)
+ yyerrorl(typePos(t), "type %v too large", t)
}
t.Width = w
diff --git a/src/cmd/compile/internal/gc/bench_test.go b/src/cmd/compile/internal/gc/bench_test.go
index 09aaf428c3..8c4288128f 100644
--- a/src/cmd/compile/internal/gc/bench_test.go
+++ b/src/cmd/compile/internal/gc/bench_test.go
@@ -7,6 +7,7 @@ package gc
import "testing"
var globl int64
+var globl32 int32
func BenchmarkLoadAdd(b *testing.B) {
x := make([]int64, 1024)
@@ -20,6 +21,18 @@ func BenchmarkLoadAdd(b *testing.B) {
}
}
+// Added for ppc64 extswsli on power9
+func BenchmarkExtShift(b *testing.B) {
+ x := make([]int32, 1024)
+ for i := 0; i < b.N; i++ {
+ var s int64
+ for i := range x {
+ s ^= int64(x[i]+32) * 8
+ }
+ globl = s
+ }
+}
+
func BenchmarkModify(b *testing.B) {
a := make([]int64, 1024)
v := globl
@@ -30,6 +43,17 @@ func BenchmarkModify(b *testing.B) {
}
}
+func BenchmarkMullImm(b *testing.B) {
+ x := make([]int32, 1024)
+ for i := 0; i < b.N; i++ {
+ var s int32
+ for i := range x {
+ s += x[i] * 100
+ }
+ globl32 = s
+ }
+}
+
func BenchmarkConstModify(b *testing.B) {
a := make([]int64, 1024)
for i := 0; i < b.N; i++ {
diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go
index 5ced66c0da..10f21f86df 100644
--- a/src/cmd/compile/internal/gc/bexport.go
+++ b/src/cmd/compile/internal/gc/bexport.go
@@ -81,11 +81,6 @@ func (p *exporter) markType(t *types.Type) {
}
}
-// deltaNewFile is a magic line delta offset indicating a new file.
-// We use -64 because it is rare; see issue 20080 and CL 41619.
-// -64 is the smallest int that fits in a single byte as a varint.
-const deltaNewFile = -64
-
// ----------------------------------------------------------------------------
// Export format
@@ -126,30 +121,6 @@ const (
aliasTag
)
-// untype returns the "pseudo" untyped type for a Ctype (import/export use only).
-// (we can't use a pre-initialized array because we must be sure all types are
-// set up)
-func untype(ctype Ctype) *types.Type {
- switch ctype {
- case CTINT:
- return types.Idealint
- case CTRUNE:
- return types.Idealrune
- case CTFLT:
- return types.Idealfloat
- case CTCPLX:
- return types.Idealcomplex
- case CTSTR:
- return types.Idealstring
- case CTBOOL:
- return types.Idealbool
- case CTNIL:
- return types.Types[TNIL]
- }
- Fatalf("exporter: unknown Ctype")
- return nil
-}
-
var predecl []*types.Type // initialized lazily
func predeclared() []*types.Type {
@@ -184,13 +155,13 @@ func predeclared() []*types.Type {
types.Errortype,
// untyped types
- untype(CTBOOL),
- untype(CTINT),
- untype(CTRUNE),
- untype(CTFLT),
- untype(CTCPLX),
- untype(CTSTR),
- untype(CTNIL),
+ types.UntypedBool,
+ types.UntypedInt,
+ types.UntypedRune,
+ types.UntypedFloat,
+ types.UntypedComplex,
+ types.UntypedString,
+ types.Types[TNIL],
// package unsafe
types.Types[TUNSAFEPTR],
diff --git a/src/cmd/compile/internal/gc/builtin.go b/src/cmd/compile/internal/gc/builtin.go
index 861ffaaa5b..e04f23e229 100644
--- a/src/cmd/compile/internal/gc/builtin.go
+++ b/src/cmd/compile/internal/gc/builtin.go
@@ -44,6 +44,7 @@ var runtimeDecls = [...]struct {
{"printcomplex", funcTag, 27},
{"printstring", funcTag, 29},
{"printpointer", funcTag, 30},
+ {"printuintptr", funcTag, 31},
{"printiface", funcTag, 30},
{"printeface", funcTag, 30},
{"printslice", funcTag, 30},
@@ -51,20 +52,19 @@ var runtimeDecls = [...]struct {
{"printsp", funcTag, 9},
{"printlock", funcTag, 9},
{"printunlock", funcTag, 9},
- {"concatstring2", funcTag, 33},
- {"concatstring3", funcTag, 34},
- {"concatstring4", funcTag, 35},
- {"concatstring5", funcTag, 36},
- {"concatstrings", funcTag, 38},
- {"cmpstring", funcTag, 39},
- {"intstring", funcTag, 42},
- {"slicebytetostring", funcTag, 43},
- {"slicebytetostringtmp", funcTag, 44},
- {"slicerunetostring", funcTag, 47},
- {"stringtoslicebyte", funcTag, 49},
- {"stringtoslicerune", funcTag, 52},
- {"slicecopy", funcTag, 53},
- {"slicestringcopy", funcTag, 54},
+ {"concatstring2", funcTag, 34},
+ {"concatstring3", funcTag, 35},
+ {"concatstring4", funcTag, 36},
+ {"concatstring5", funcTag, 37},
+ {"concatstrings", funcTag, 39},
+ {"cmpstring", funcTag, 40},
+ {"intstring", funcTag, 43},
+ {"slicebytetostring", funcTag, 44},
+ {"slicebytetostringtmp", funcTag, 45},
+ {"slicerunetostring", funcTag, 48},
+ {"stringtoslicebyte", funcTag, 50},
+ {"stringtoslicerune", funcTag, 53},
+ {"slicecopy", funcTag, 54},
{"decoderune", funcTag, 55},
{"countrunes", funcTag, 56},
{"convI2I", funcTag, 57},
@@ -175,15 +175,16 @@ var runtimeDecls = [...]struct {
{"uint64tofloat64", funcTag, 118},
{"uint32tofloat64", funcTag, 119},
{"complex128div", funcTag, 120},
- {"racefuncenter", funcTag, 121},
+ {"racefuncenter", funcTag, 31},
{"racefuncenterfp", funcTag, 9},
{"racefuncexit", funcTag, 9},
- {"raceread", funcTag, 121},
- {"racewrite", funcTag, 121},
- {"racereadrange", funcTag, 122},
- {"racewriterange", funcTag, 122},
- {"msanread", funcTag, 122},
- {"msanwrite", funcTag, 122},
+ {"raceread", funcTag, 31},
+ {"racewrite", funcTag, 31},
+ {"racereadrange", funcTag, 121},
+ {"racewriterange", funcTag, 121},
+ {"msanread", funcTag, 121},
+ {"msanwrite", funcTag, 121},
+ {"msanmove", funcTag, 122},
{"checkptrAlignment", funcTag, 123},
{"checkptrArithmetic", funcTag, 125},
{"libfuzzerTraceCmp1", funcTag, 127},
@@ -234,31 +235,31 @@ func runtimeTypes() []*types.Type {
typs[28] = types.Types[TSTRING]
typs[29] = functype(nil, []*Node{anonfield(typs[28])}, nil)
typs[30] = functype(nil, []*Node{anonfield(typs[2])}, nil)
- typs[31] = types.NewArray(typs[0], 32)
- typs[32] = types.NewPtr(typs[31])
- typs[33] = functype(nil, []*Node{anonfield(typs[32]), anonfield(typs[28]), anonfield(typs[28])}, []*Node{anonfield(typs[28])})
- typs[34] = functype(nil, []*Node{anonfield(typs[32]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28])}, []*Node{anonfield(typs[28])})
- typs[35] = functype(nil, []*Node{anonfield(typs[32]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28])}, []*Node{anonfield(typs[28])})
- typs[36] = functype(nil, []*Node{anonfield(typs[32]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28])}, []*Node{anonfield(typs[28])})
- typs[37] = types.NewSlice(typs[28])
- typs[38] = functype(nil, []*Node{anonfield(typs[32]), anonfield(typs[37])}, []*Node{anonfield(typs[28])})
- typs[39] = functype(nil, []*Node{anonfield(typs[28]), anonfield(typs[28])}, []*Node{anonfield(typs[15])})
- typs[40] = types.NewArray(typs[0], 4)
- typs[41] = types.NewPtr(typs[40])
- typs[42] = functype(nil, []*Node{anonfield(typs[41]), anonfield(typs[22])}, []*Node{anonfield(typs[28])})
- typs[43] = functype(nil, []*Node{anonfield(typs[32]), anonfield(typs[1]), anonfield(typs[15])}, []*Node{anonfield(typs[28])})
- typs[44] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15])}, []*Node{anonfield(typs[28])})
- typs[45] = types.Runetype
- typs[46] = types.NewSlice(typs[45])
- typs[47] = functype(nil, []*Node{anonfield(typs[32]), anonfield(typs[46])}, []*Node{anonfield(typs[28])})
- typs[48] = types.NewSlice(typs[0])
- typs[49] = functype(nil, []*Node{anonfield(typs[32]), anonfield(typs[28])}, []*Node{anonfield(typs[48])})
- typs[50] = types.NewArray(typs[45], 32)
- typs[51] = types.NewPtr(typs[50])
- typs[52] = functype(nil, []*Node{anonfield(typs[51]), anonfield(typs[28])}, []*Node{anonfield(typs[46])})
- typs[53] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[15]), anonfield(typs[3]), anonfield(typs[15]), anonfield(typs[5])}, []*Node{anonfield(typs[15])})
- typs[54] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[28])}, []*Node{anonfield(typs[15])})
- typs[55] = functype(nil, []*Node{anonfield(typs[28]), anonfield(typs[15])}, []*Node{anonfield(typs[45]), anonfield(typs[15])})
+ typs[31] = functype(nil, []*Node{anonfield(typs[5])}, nil)
+ typs[32] = types.NewArray(typs[0], 32)
+ typs[33] = types.NewPtr(typs[32])
+ typs[34] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[28]), anonfield(typs[28])}, []*Node{anonfield(typs[28])})
+ typs[35] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28])}, []*Node{anonfield(typs[28])})
+ typs[36] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28])}, []*Node{anonfield(typs[28])})
+ typs[37] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28])}, []*Node{anonfield(typs[28])})
+ typs[38] = types.NewSlice(typs[28])
+ typs[39] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[38])}, []*Node{anonfield(typs[28])})
+ typs[40] = functype(nil, []*Node{anonfield(typs[28]), anonfield(typs[28])}, []*Node{anonfield(typs[15])})
+ typs[41] = types.NewArray(typs[0], 4)
+ typs[42] = types.NewPtr(typs[41])
+ typs[43] = functype(nil, []*Node{anonfield(typs[42]), anonfield(typs[22])}, []*Node{anonfield(typs[28])})
+ typs[44] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[1]), anonfield(typs[15])}, []*Node{anonfield(typs[28])})
+ typs[45] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15])}, []*Node{anonfield(typs[28])})
+ typs[46] = types.Runetype
+ typs[47] = types.NewSlice(typs[46])
+ typs[48] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[47])}, []*Node{anonfield(typs[28])})
+ typs[49] = types.NewSlice(typs[0])
+ typs[50] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[28])}, []*Node{anonfield(typs[49])})
+ typs[51] = types.NewArray(typs[46], 32)
+ typs[52] = types.NewPtr(typs[51])
+ typs[53] = functype(nil, []*Node{anonfield(typs[52]), anonfield(typs[28])}, []*Node{anonfield(typs[47])})
+ typs[54] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[15]), anonfield(typs[3]), anonfield(typs[15]), anonfield(typs[5])}, []*Node{anonfield(typs[15])})
+ typs[55] = functype(nil, []*Node{anonfield(typs[28]), anonfield(typs[15])}, []*Node{anonfield(typs[46]), anonfield(typs[15])})
typs[56] = functype(nil, []*Node{anonfield(typs[28])}, []*Node{anonfield(typs[15])})
typs[57] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2])}, []*Node{anonfield(typs[2])})
typs[58] = functype(nil, []*Node{anonfield(typs[2])}, []*Node{anonfield(typs[7])})
@@ -324,8 +325,8 @@ func runtimeTypes() []*types.Type {
typs[118] = functype(nil, []*Node{anonfield(typs[24])}, []*Node{anonfield(typs[20])})
typs[119] = functype(nil, []*Node{anonfield(typs[65])}, []*Node{anonfield(typs[20])})
typs[120] = functype(nil, []*Node{anonfield(typs[26]), anonfield(typs[26])}, []*Node{anonfield(typs[26])})
- typs[121] = functype(nil, []*Node{anonfield(typs[5])}, nil)
- typs[122] = functype(nil, []*Node{anonfield(typs[5]), anonfield(typs[5])}, nil)
+ typs[121] = functype(nil, []*Node{anonfield(typs[5]), anonfield(typs[5])}, nil)
+ typs[122] = functype(nil, []*Node{anonfield(typs[5]), anonfield(typs[5]), anonfield(typs[5])}, nil)
typs[123] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[1]), anonfield(typs[5])}, nil)
typs[124] = types.NewSlice(typs[7])
typs[125] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[124])}, nil)
diff --git a/src/cmd/compile/internal/gc/builtin/runtime.go b/src/cmd/compile/internal/gc/builtin/runtime.go
index 635da80f7c..acb69c7b28 100644
--- a/src/cmd/compile/internal/gc/builtin/runtime.go
+++ b/src/cmd/compile/internal/gc/builtin/runtime.go
@@ -54,6 +54,7 @@ func printuint(uint64)
func printcomplex(complex128)
func printstring(string)
func printpointer(any)
+func printuintptr(uintptr)
func printiface(any)
func printeface(any)
func printslice(any)
@@ -75,8 +76,7 @@ func slicebytetostringtmp(ptr *byte, n int) string
func slicerunetostring(*[32]byte, []rune) string
func stringtoslicebyte(*[32]byte, string) []byte
func stringtoslicerune(*[32]rune, string) []rune
-func slicecopy(toPtr *any, toLen int, frPtr *any, frLen int, wid uintptr) int
-func slicestringcopy(toPtr *byte, toLen int, fr string) int
+func slicecopy(toPtr *any, toLen int, fromPtr *any, fromLen int, wid uintptr) int
func decoderune(string, int) (retv rune, retk int)
func countrunes(string) int
@@ -237,6 +237,7 @@ func racewriterange(addr, size uintptr)
// memory sanitizer
func msanread(addr, size uintptr)
func msanwrite(addr, size uintptr)
+func msanmove(dst, src, size uintptr)
func checkptrAlignment(unsafe.Pointer, *byte, uintptr)
func checkptrArithmetic(unsafe.Pointer, []unsafe.Pointer)
diff --git a/src/cmd/compile/internal/gc/closure.go b/src/cmd/compile/internal/gc/closure.go
index 250be38e5b..bd350f696e 100644
--- a/src/cmd/compile/internal/gc/closure.go
+++ b/src/cmd/compile/internal/gc/closure.go
@@ -71,6 +71,10 @@ func (p *noder) funcLit(expr *syntax.FuncLit) *Node {
return clo
}
+// typecheckclosure typechecks an OCLOSURE node. It also creates the named
+// function associated with the closure.
+// TODO: This creation of the named function should probably really be done in a
+// separate pass from type-checking.
func typecheckclosure(clo *Node, top int) {
xfunc := clo.Func.Closure
// Set current associated iota value, so iota can be used inside
@@ -198,7 +202,7 @@ func capturevars(xfunc *Node) {
outer = nod(OADDR, outer, nil)
}
- if Debug['m'] > 1 {
+ if Debug.m > 1 {
var name *types.Sym
if v.Name.Curfn != nil && v.Name.Curfn.Func.Nname != nil {
name = v.Name.Curfn.Func.Nname.Sym
@@ -434,6 +438,8 @@ func typecheckpartialcall(fn *Node, sym *types.Sym) {
fn.Type = xfunc.Type
}
+// makepartialcall returns a DCLFUNC node representing the wrapper function (*-fm) needed
+// for partial calls.
func makepartialcall(fn *Node, t0 *types.Type, meth *types.Sym) *Node {
rcvrtype := fn.Left.Type
sym := methodSymSuffix(rcvrtype, meth, "-fm")
@@ -500,6 +506,10 @@ func makepartialcall(fn *Node, t0 *types.Type, meth *types.Sym) *Node {
funcbody()
xfunc = typecheck(xfunc, ctxStmt)
+ // Need to typecheck the body of the just-generated wrapper.
+ // typecheckslice() requires that Curfn is set when processing an ORETURN.
+ Curfn = xfunc
+ typecheckslice(xfunc.Nbody.Slice(), ctxStmt)
sym.Def = asTypesNode(xfunc)
xtop = append(xtop, xfunc)
Curfn = savecurfn
diff --git a/src/cmd/compile/internal/gc/const.go b/src/cmd/compile/internal/gc/const.go
index fe73df9d57..b92c8d66b5 100644
--- a/src/cmd/compile/internal/gc/const.go
+++ b/src/cmd/compile/internal/gc/const.go
@@ -44,7 +44,7 @@ func (v Val) Ctype() Ctype {
Fatalf("unexpected Ctype for %T", v.U)
panic("unreachable")
case nil:
- return 0
+ return CTxxx
case *NilVal:
return CTNIL
case bool:
@@ -114,16 +114,16 @@ func (v Val) Interface() interface{} {
type NilVal struct{}
-// Int64 returns n as an int64.
+// Int64Val returns n as an int64.
// n must be an integer or rune constant.
-func (n *Node) Int64() int64 {
+func (n *Node) Int64Val() int64 {
if !Isconst(n, CTINT) {
- Fatalf("Int64(%v)", n)
+ Fatalf("Int64Val(%v)", n)
}
return n.Val().U.(*Mpint).Int64()
}
-// CanInt64 reports whether it is safe to call Int64() on n.
+// CanInt64 reports whether it is safe to call Int64Val() on n.
func (n *Node) CanInt64() bool {
if !Isconst(n, CTINT) {
return false
@@ -131,18 +131,27 @@ func (n *Node) CanInt64() bool {
// if the value inside n cannot be represented as an int64, the
// return value of Int64 is undefined
- return n.Val().U.(*Mpint).CmpInt64(n.Int64()) == 0
+ return n.Val().U.(*Mpint).CmpInt64(n.Int64Val()) == 0
}
-// Bool returns n as a bool.
+// BoolVal returns n as a bool.
// n must be a boolean constant.
-func (n *Node) Bool() bool {
+func (n *Node) BoolVal() bool {
if !Isconst(n, CTBOOL) {
- Fatalf("Bool(%v)", n)
+ Fatalf("BoolVal(%v)", n)
}
return n.Val().U.(bool)
}
+// StringVal returns the value of a literal string Node as a string.
+// n must be a string constant.
+func (n *Node) StringVal() string {
+ if !Isconst(n, CTSTR) {
+ Fatalf("StringVal(%v)", n)
+ }
+ return n.Val().U.(string)
+}
+
// truncate float literal fv to 32-bit or 64-bit precision
// according to type; return truncated value.
func truncfltlit(oldv *Mpflt, t *types.Type) *Mpflt {
@@ -261,7 +270,7 @@ func convlit1(n *Node, t *types.Type, explicit bool, context func() string) *Nod
}
if t == nil || !okforconst[t.Etype] {
- t = defaultType(idealkind(n))
+ t = defaultType(n.Type)
}
switch n.Op {
@@ -612,7 +621,7 @@ func evconst(n *Node) {
var strs []string
i2 := i1
for i2 < len(s) && Isconst(s[i2], CTSTR) {
- strs = append(strs, strlit(s[i2]))
+ strs = append(strs, s[i2].StringVal())
i2++
}
@@ -635,7 +644,7 @@ func evconst(n *Node) {
switch nl.Type.Etype {
case TSTRING:
if Isconst(nl, CTSTR) {
- setintconst(n, int64(len(strlit(nl))))
+ setintconst(n, int64(len(nl.StringVal())))
}
case TARRAY:
if !hascallchan(nl) {
@@ -838,10 +847,6 @@ Outer:
return Val{}
}
u.Quo(y)
- case OMOD, OOR, OAND, OANDNOT, OXOR:
- // TODO(mdempsky): Move to typecheck; see #31060.
- yyerror("invalid operation: operator %v not defined on untyped float", op)
- return Val{}
default:
break Outer
}
@@ -867,10 +872,6 @@ Outer:
yyerror("complex division by zero")
return Val{}
}
- case OMOD, OOR, OAND, OANDNOT, OXOR:
- // TODO(mdempsky): Move to typecheck; see #31060.
- yyerror("invalid operation: operator %v not defined on untyped complex", op)
- return Val{}
default:
break Outer
}
@@ -932,15 +933,6 @@ func unaryOp(op Op, x Val, t *types.Type) Val {
}
u.Xor(x)
return Val{U: u}
-
- case CTFLT:
- // TODO(mdempsky): Move to typecheck; see #31060.
- yyerror("invalid operation: operator %v not defined on untyped float", op)
- return Val{}
- case CTCPLX:
- // TODO(mdempsky): Move to typecheck; see #31060.
- yyerror("invalid operation: operator %v not defined on untyped complex", op)
- return Val{}
}
case ONOT:
@@ -994,10 +986,8 @@ func setconst(n *Node, v Val) {
Xoffset: BADWIDTH,
}
n.SetVal(v)
- if n.Type.IsUntyped() {
- // TODO(mdempsky): Make typecheck responsible for setting
- // the correct untyped type.
- n.Type = idealType(v.Ctype())
+ if vt := idealType(v.Ctype()); n.Type.IsUntyped() && n.Type != vt {
+ Fatalf("untyped type mismatch, have: %v, want: %v", n.Type, vt)
}
// Check range.
@@ -1038,17 +1028,17 @@ func nodlit(v Val) *Node {
func idealType(ct Ctype) *types.Type {
switch ct {
case CTSTR:
- return types.Idealstring
+ return types.UntypedString
case CTBOOL:
- return types.Idealbool
+ return types.UntypedBool
case CTINT:
- return types.Idealint
+ return types.UntypedInt
case CTRUNE:
- return types.Idealrune
+ return types.UntypedRune
case CTFLT:
- return types.Idealfloat
+ return types.UntypedFloat
case CTCPLX:
- return types.Idealcomplex
+ return types.UntypedComplex
case CTNIL:
return types.Types[TNIL]
}
@@ -1056,67 +1046,6 @@ func idealType(ct Ctype) *types.Type {
return nil
}
-// idealkind returns a constant kind like consttype
-// but for an arbitrary "ideal" (untyped constant) expression.
-func idealkind(n *Node) Ctype {
- if n == nil || !n.Type.IsUntyped() {
- return CTxxx
- }
-
- switch n.Op {
- default:
- return CTxxx
-
- case OLITERAL:
- return n.Val().Ctype()
-
- // numeric kinds.
- case OADD,
- OAND,
- OANDNOT,
- OBITNOT,
- ODIV,
- ONEG,
- OMOD,
- OMUL,
- OSUB,
- OXOR,
- OOR,
- OPLUS:
- k1 := idealkind(n.Left)
- k2 := idealkind(n.Right)
- if k1 > k2 {
- return k1
- } else {
- return k2
- }
-
- case OREAL, OIMAG:
- return CTFLT
-
- case OCOMPLEX:
- return CTCPLX
-
- case OADDSTR:
- return CTSTR
-
- case OANDAND,
- OEQ,
- OGE,
- OGT,
- OLE,
- OLT,
- ONE,
- ONOT,
- OOROR:
- return CTBOOL
-
- // shifts (beware!).
- case OLSH, ORSH:
- return idealkind(n.Left)
- }
-}
-
// defaultlit on both nodes simultaneously;
// if they're both ideal going in they better
// get the same type going out.
@@ -1152,41 +1081,63 @@ func defaultlit2(l *Node, r *Node, force bool) (*Node, *Node) {
return l, r
}
- k := idealkind(l)
- if rk := idealkind(r); rk > k {
- k = rk
- }
- t := defaultType(k)
+ t := defaultType(mixUntyped(l.Type, r.Type))
l = convlit(l, t)
r = convlit(r, t)
return l, r
}
-func defaultType(k Ctype) *types.Type {
- switch k {
- case CTBOOL:
+func ctype(t *types.Type) Ctype {
+ switch t {
+ case types.UntypedBool:
+ return CTBOOL
+ case types.UntypedString:
+ return CTSTR
+ case types.UntypedInt:
+ return CTINT
+ case types.UntypedRune:
+ return CTRUNE
+ case types.UntypedFloat:
+ return CTFLT
+ case types.UntypedComplex:
+ return CTCPLX
+ }
+ Fatalf("bad type %v", t)
+ panic("unreachable")
+}
+
+func mixUntyped(t1, t2 *types.Type) *types.Type {
+ t := t1
+ if ctype(t2) > ctype(t1) {
+ t = t2
+ }
+ return t
+}
+
+func defaultType(t *types.Type) *types.Type {
+ if !t.IsUntyped() || t.Etype == TNIL {
+ return t
+ }
+
+ switch t {
+ case types.UntypedBool:
return types.Types[TBOOL]
- case CTSTR:
+ case types.UntypedString:
return types.Types[TSTRING]
- case CTINT:
+ case types.UntypedInt:
return types.Types[TINT]
- case CTRUNE:
+ case types.UntypedRune:
return types.Runetype
- case CTFLT:
+ case types.UntypedFloat:
return types.Types[TFLOAT64]
- case CTCPLX:
+ case types.UntypedComplex:
return types.Types[TCOMPLEX128]
}
- Fatalf("bad idealkind: %v", k)
- return nil
-}
-// strlit returns the value of a literal string Node as a string.
-func strlit(n *Node) string {
- return n.Val().U.(string)
+ Fatalf("bad type %v", t)
+ return nil
}
-// TODO(gri) smallintconst is only used in one place - can we used indexconst?
func smallintconst(n *Node) bool {
if n.Op == OLITERAL && Isconst(n, CTINT) && n.Type != nil {
switch simtype[n.Type.Etype] {
diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go
index 69eb13f607..6e90eb4d65 100644
--- a/src/cmd/compile/internal/gc/dcl.go
+++ b/src/cmd/compile/internal/gc/dcl.go
@@ -257,6 +257,8 @@ func symfield(s *types.Sym, typ *types.Type) *Node {
// oldname returns the Node that declares symbol s in the current scope.
// If no such Node currently exists, an ONONAME Node is returned instead.
+// Automatically creates a new closure variable if the referenced symbol was
+// declared in a different (containing) function.
func oldname(s *types.Sym) *Node {
n := asNode(s.Def)
if n == nil {
@@ -283,7 +285,7 @@ func oldname(s *types.Sym) *Node {
c.Name.Defn = n
// Link into list of active closure variables.
- // Popped from list in func closurebody.
+ // Popped from list in func funcLit.
c.Name.Param.Outer = n.Name.Param.Innermost
n.Name.Param.Innermost = c
@@ -382,14 +384,11 @@ func ifacedcl(n *Node) {
// returns in auto-declaration context.
func funchdr(n *Node) {
// change the declaration context from extern to auto
- if Curfn == nil && dclcontext != PEXTERN {
- Fatalf("funchdr: dclcontext = %d", dclcontext)
- }
-
+ funcStack = append(funcStack, funcStackEnt{Curfn, dclcontext})
+ Curfn = n
dclcontext = PAUTO
+
types.Markdcl()
- funcstack = append(funcstack, Curfn)
- Curfn = n
if n.Func.Nname != nil {
funcargs(n.Func.Nname.Name.Param.Ntype)
@@ -497,21 +496,22 @@ func funcarg2(f *types.Field, ctxt Class) {
declare(n, ctxt)
}
-var funcstack []*Node // stack of previous values of Curfn
+var funcStack []funcStackEnt // stack of previous values of Curfn/dclcontext
+
+type funcStackEnt struct {
+ curfn *Node
+ dclcontext Class
+}
// finish the body.
// called in auto-declaration context.
// returns in extern-declaration context.
func funcbody() {
- // change the declaration context from auto to extern
- if dclcontext != PAUTO {
- Fatalf("funcbody: unexpected dclcontext %d", dclcontext)
- }
+ // change the declaration context from auto to previous context
types.Popdcl()
- funcstack, Curfn = funcstack[:len(funcstack)-1], funcstack[len(funcstack)-1]
- if Curfn == nil {
- dclcontext = PEXTERN
- }
+ var e funcStackEnt
+ funcStack, e = funcStack[:len(funcStack)-1], funcStack[len(funcStack)-1]
+ Curfn, dclcontext = e.curfn, e.dclcontext
}
// structs, functions, and methods.
diff --git a/src/cmd/compile/internal/gc/dwinl.go b/src/cmd/compile/internal/gc/dwinl.go
index 27e2cbcd98..bb5ae61cbb 100644
--- a/src/cmd/compile/internal/gc/dwinl.go
+++ b/src/cmd/compile/internal/gc/dwinl.go
@@ -8,6 +8,7 @@ import (
"cmd/internal/dwarf"
"cmd/internal/obj"
"cmd/internal/src"
+ "fmt"
"strings"
)
@@ -34,7 +35,7 @@ func assembleInlines(fnsym *obj.LSym, dwVars []*dwarf.Var) dwarf.InlCalls {
// Walk progs to build up the InlCalls data structure
var prevpos src.XPos
- for p := fnsym.Func.Text; p != nil; p = p.Link {
+ for p := fnsym.Func().Text; p != nil; p = p.Link {
if p.Pos == prevpos {
continue
}
@@ -150,7 +151,7 @@ func assembleInlines(fnsym *obj.LSym, dwVars []*dwarf.Var) dwarf.InlCalls {
start := int64(-1)
curii := -1
var prevp *obj.Prog
- for p := fnsym.Func.Text; p != nil; prevp, p = p, p.Link {
+ for p := fnsym.Func().Text; p != nil; prevp, p = p, p.Link {
if prevp != nil && p.Pos == prevp.Pos {
continue
}
@@ -170,12 +171,32 @@ func assembleInlines(fnsym *obj.LSym, dwVars []*dwarf.Var) dwarf.InlCalls {
addRange(inlcalls.Calls, start, fnsym.Size, curii, imap)
}
+ // Issue 33188: if II foo is a child of II bar, then ensure that
+ // bar's ranges include the ranges of foo (the loop above will produce
+ // disjoint ranges).
+ for k, c := range inlcalls.Calls {
+ if c.Root {
+ unifyCallRanges(inlcalls, k)
+ }
+ }
+
// Debugging
if Debug_gendwarfinl != 0 {
dumpInlCalls(inlcalls)
dumpInlVars(dwVars)
}
+ // Perform a consistency check on inlined routine PC ranges
+ // produced by unifyCallRanges above. In particular, complain in
+ // cases where you have A -> B -> C (e.g. C is inlined into B, and
+ // B is inlined into A) and the ranges for B are not enclosed
+ // within the ranges for A, or C within B.
+ for k, c := range inlcalls.Calls {
+ if c.Root {
+ checkInlCall(fnsym.Name, inlcalls, fnsym.Size, k, -1)
+ }
+ }
+
return inlcalls
}
@@ -355,3 +376,74 @@ func dumpInlVars(dwvars []*dwarf.Var) {
Ctxt.Logf("V%d: %s CI:%d II:%d IA:%d %s\n", i, dwv.Name, dwv.ChildIndex, dwv.InlIndex-1, ia, typ)
}
}
+
+func rangesContains(par []dwarf.Range, rng dwarf.Range) (bool, string) {
+ for _, r := range par {
+ if rng.Start >= r.Start && rng.End <= r.End {
+ return true, ""
+ }
+ }
+ msg := fmt.Sprintf("range [%d,%d) not contained in {", rng.Start, rng.End)
+ for _, r := range par {
+ msg += fmt.Sprintf(" [%d,%d)", r.Start, r.End)
+ }
+ msg += " }"
+ return false, msg
+}
+
+func rangesContainsAll(parent, child []dwarf.Range) (bool, string) {
+ for _, r := range child {
+ c, m := rangesContains(parent, r)
+ if !c {
+ return false, m
+ }
+ }
+ return true, ""
+}
+
+// checkInlCall verifies that the PC ranges for inline info 'idx' are
+// enclosed/contained within the ranges of its parent inline (or if
+// this is a root/toplevel inline, checks that the ranges fall within
+// the extent of the top level function). A panic is issued if a
+// malformed range is found.
+func checkInlCall(funcName string, inlCalls dwarf.InlCalls, funcSize int64, idx, parentIdx int) {
+
+ // Callee
+ ic := inlCalls.Calls[idx]
+ callee := Ctxt.InlTree.InlinedFunction(ic.InlIndex).Name
+ calleeRanges := ic.Ranges
+
+ // Caller
+ caller := funcName
+ parentRanges := []dwarf.Range{dwarf.Range{Start: int64(0), End: funcSize}}
+ if parentIdx != -1 {
+ pic := inlCalls.Calls[parentIdx]
+ caller = Ctxt.InlTree.InlinedFunction(pic.InlIndex).Name
+ parentRanges = pic.Ranges
+ }
+
+ // Callee ranges contained in caller ranges?
+ c, m := rangesContainsAll(parentRanges, calleeRanges)
+ if !c {
+ Fatalf("** malformed inlined routine range in %s: caller %s callee %s II=%d %s\n", funcName, caller, callee, idx, m)
+ }
+
+ // Now visit kids
+ for _, k := range ic.Children {
+ checkInlCall(funcName, inlCalls, funcSize, k, idx)
+ }
+}
+
+// unifyCallRanges ensures that the ranges for a given inline
+// transitively include all of the ranges for its child inlines.
+func unifyCallRanges(inlcalls dwarf.InlCalls, idx int) {
+ ic := &inlcalls.Calls[idx]
+ for _, childIdx := range ic.Children {
+ // First make sure child ranges are unified.
+ unifyCallRanges(inlcalls, childIdx)
+
+ // Then merge child ranges into ranges for this inline.
+ cic := inlcalls.Calls[childIdx]
+ ic.Ranges = dwarf.MergeRanges(ic.Ranges, cic.Ranges)
+ }
+}
diff --git a/src/cmd/compile/internal/gc/embed.go b/src/cmd/compile/internal/gc/embed.go
new file mode 100644
index 0000000000..103949c1f9
--- /dev/null
+++ b/src/cmd/compile/internal/gc/embed.go
@@ -0,0 +1,273 @@
+// Copyright 2020 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 (
+ "cmd/compile/internal/syntax"
+ "cmd/compile/internal/types"
+ "cmd/internal/obj"
+ "encoding/json"
+ "io/ioutil"
+ "log"
+ "path"
+ "sort"
+ "strconv"
+ "strings"
+)
+
+var embedlist []*Node
+
+var embedCfg struct {
+ Patterns map[string][]string
+ Files map[string]string
+}
+
+func readEmbedCfg(file string) {
+ data, err := ioutil.ReadFile(file)
+ if err != nil {
+ log.Fatalf("-embedcfg: %v", err)
+ }
+ if err := json.Unmarshal(data, &embedCfg); err != nil {
+ log.Fatalf("%s: %v", file, err)
+ }
+ if embedCfg.Patterns == nil {
+ log.Fatalf("%s: invalid embedcfg: missing Patterns", file)
+ }
+ if embedCfg.Files == nil {
+ log.Fatalf("%s: invalid embedcfg: missing Files", file)
+ }
+}
+
+const (
+ embedUnknown = iota
+ embedBytes
+ embedString
+ embedFiles
+)
+
+var numLocalEmbed int
+
+func varEmbed(p *noder, names []*Node, typ *Node, exprs []*Node, embeds []PragmaEmbed) (newExprs []*Node) {
+ haveEmbed := false
+ for _, decl := range p.file.DeclList {
+ imp, ok := decl.(*syntax.ImportDecl)
+ if !ok {
+ // imports always come first
+ break
+ }
+ path, _ := strconv.Unquote(imp.Path.Value)
+ if path == "embed" {
+ haveEmbed = true
+ break
+ }
+ }
+
+ pos := embeds[0].Pos
+ if !haveEmbed {
+ p.yyerrorpos(pos, "invalid go:embed: missing import \"embed\"")
+ return exprs
+ }
+ if embedCfg.Patterns == nil {
+ p.yyerrorpos(pos, "invalid go:embed: build system did not supply embed configuration")
+ return exprs
+ }
+ if len(names) > 1 {
+ p.yyerrorpos(pos, "go:embed cannot apply to multiple vars")
+ return exprs
+ }
+ if len(exprs) > 0 {
+ p.yyerrorpos(pos, "go:embed cannot apply to var with initializer")
+ return exprs
+ }
+ if typ == nil {
+ // Should not happen, since len(exprs) == 0 now.
+ p.yyerrorpos(pos, "go:embed cannot apply to var without type")
+ return exprs
+ }
+
+ kind := embedKindApprox(typ)
+ if kind == embedUnknown {
+ p.yyerrorpos(pos, "go:embed cannot apply to var of type %v", typ)
+ return exprs
+ }
+
+ // Build list of files to store.
+ have := make(map[string]bool)
+ var list []string
+ for _, e := range embeds {
+ for _, pattern := range e.Patterns {
+ files, ok := embedCfg.Patterns[pattern]
+ if !ok {
+ p.yyerrorpos(e.Pos, "invalid go:embed: build system did not map pattern: %s", pattern)
+ }
+ for _, file := range files {
+ if embedCfg.Files[file] == "" {
+ p.yyerrorpos(e.Pos, "invalid go:embed: build system did not map file: %s", file)
+ continue
+ }
+ if !have[file] {
+ have[file] = true
+ list = append(list, file)
+ }
+ if kind == embedFiles {
+ for dir := path.Dir(file); dir != "." && !have[dir]; dir = path.Dir(dir) {
+ have[dir] = true
+ list = append(list, dir+"/")
+ }
+ }
+ }
+ }
+ }
+ sort.Slice(list, func(i, j int) bool {
+ return embedFileLess(list[i], list[j])
+ })
+
+ if kind == embedString || kind == embedBytes {
+ if len(list) > 1 {
+ p.yyerrorpos(pos, "invalid go:embed: multiple files for type %v", typ)
+ return exprs
+ }
+ }
+
+ v := names[0]
+ if dclcontext != PEXTERN {
+ numLocalEmbed++
+ v = newnamel(v.Pos, lookupN("embed.", numLocalEmbed))
+ v.Sym.Def = asTypesNode(v)
+ v.Name.Param.Ntype = typ
+ v.SetClass(PEXTERN)
+ externdcl = append(externdcl, v)
+ exprs = []*Node{v}
+ }
+
+ v.Name.Param.SetEmbedFiles(list)
+ embedlist = append(embedlist, v)
+ return exprs
+}
+
+// embedKindApprox determines the kind of embedding variable, approximately.
+// The match is approximate because we haven't done scope resolution yet and
+// can't tell whether "string" and "byte" really mean "string" and "byte".
+// The result must be confirmed later, after type checking, using embedKind.
+func embedKindApprox(typ *Node) int {
+ if typ.Sym != nil && typ.Sym.Name == "FS" && (typ.Sym.Pkg.Path == "embed" || (typ.Sym.Pkg == localpkg && myimportpath == "embed")) {
+ return embedFiles
+ }
+ // These are not guaranteed to match only string and []byte -
+ // maybe the local package has redefined one of those words.
+ // But it's the best we can do now during the noder.
+ // The stricter check happens later, in initEmbed calling embedKind.
+ if typ.Sym != nil && typ.Sym.Name == "string" && typ.Sym.Pkg == localpkg {
+ return embedString
+ }
+ if typ.Op == OTARRAY && typ.Left == nil && typ.Right.Sym != nil && typ.Right.Sym.Name == "byte" && typ.Right.Sym.Pkg == localpkg {
+ return embedBytes
+ }
+ return embedUnknown
+}
+
+// embedKind determines the kind of embedding variable.
+func embedKind(typ *types.Type) int {
+ if typ.Sym != nil && typ.Sym.Name == "FS" && (typ.Sym.Pkg.Path == "embed" || (typ.Sym.Pkg == localpkg && myimportpath == "embed")) {
+ return embedFiles
+ }
+ if typ == types.Types[TSTRING] {
+ return embedString
+ }
+ if typ.Sym == nil && typ.IsSlice() && typ.Elem() == types.Bytetype {
+ return embedBytes
+ }
+ return embedUnknown
+}
+
+func embedFileNameSplit(name string) (dir, elem string, isDir bool) {
+ if name[len(name)-1] == '/' {
+ isDir = true
+ name = name[:len(name)-1]
+ }
+ i := len(name) - 1
+ for i >= 0 && name[i] != '/' {
+ i--
+ }
+ if i < 0 {
+ return ".", name, isDir
+ }
+ return name[:i], name[i+1:], isDir
+}
+
+// embedFileLess implements the sort order for a list of embedded files.
+// See the comment inside ../../../../embed/embed.go's Files struct for rationale.
+func embedFileLess(x, y string) bool {
+ xdir, xelem, _ := embedFileNameSplit(x)
+ ydir, yelem, _ := embedFileNameSplit(y)
+ return xdir < ydir || xdir == ydir && xelem < yelem
+}
+
+func dumpembeds() {
+ for _, v := range embedlist {
+ initEmbed(v)
+ }
+}
+
+// initEmbed emits the init data for a //go:embed variable,
+// which is either a string, a []byte, or an embed.FS.
+func initEmbed(v *Node) {
+ files := v.Name.Param.EmbedFiles()
+ switch kind := embedKind(v.Type); kind {
+ case embedUnknown:
+ yyerrorl(v.Pos, "go:embed cannot apply to var of type %v", v.Type)
+
+ case embedString, embedBytes:
+ file := files[0]
+ fsym, size, err := fileStringSym(v.Pos, embedCfg.Files[file], kind == embedString, nil)
+ if err != nil {
+ yyerrorl(v.Pos, "embed %s: %v", file, err)
+ }
+ sym := v.Sym.Linksym()
+ off := 0
+ off = dsymptr(sym, off, fsym, 0) // data string
+ off = duintptr(sym, off, uint64(size)) // len
+ if kind == embedBytes {
+ duintptr(sym, off, uint64(size)) // cap for slice
+ }
+
+ case embedFiles:
+ slicedata := Ctxt.Lookup(`"".` + v.Sym.Name + `.files`)
+ off := 0
+ // []files pointed at by Files
+ off = dsymptr(slicedata, off, slicedata, 3*Widthptr) // []file, pointing just past slice
+ off = duintptr(slicedata, off, uint64(len(files)))
+ off = duintptr(slicedata, off, uint64(len(files)))
+
+ // embed/embed.go type file is:
+ // name string
+ // data string
+ // hash [16]byte
+ // Emit one of these per file in the set.
+ const hashSize = 16
+ hash := make([]byte, hashSize)
+ for _, file := range files {
+ off = dsymptr(slicedata, off, stringsym(v.Pos, file), 0) // file string
+ off = duintptr(slicedata, off, uint64(len(file)))
+ if strings.HasSuffix(file, "/") {
+ // entry for directory - no data
+ off = duintptr(slicedata, off, 0)
+ off = duintptr(slicedata, off, 0)
+ off += hashSize
+ } else {
+ fsym, size, err := fileStringSym(v.Pos, embedCfg.Files[file], true, hash)
+ if err != nil {
+ yyerrorl(v.Pos, "embed %s: %v", file, err)
+ }
+ off = dsymptr(slicedata, off, fsym, 0) // data string
+ off = duintptr(slicedata, off, uint64(size))
+ off = int(slicedata.WriteBytes(Ctxt, int64(off), hash))
+ }
+ }
+ ggloblsym(slicedata, int32(off), obj.RODATA|obj.LOCAL)
+ sym := v.Sym.Linksym()
+ dsymptr(sym, 0, slicedata, 0)
+ }
+}
diff --git a/src/cmd/compile/internal/gc/esc.go b/src/cmd/compile/internal/gc/esc.go
index 4b843aba35..6f328ab5ea 100644
--- a/src/cmd/compile/internal/gc/esc.go
+++ b/src/cmd/compile/internal/gc/esc.go
@@ -169,36 +169,47 @@ func mayAffectMemory(n *Node) bool {
}
}
-func mustHeapAlloc(n *Node) bool {
+// heapAllocReason returns the reason the given Node must be heap
+// allocated, or the empty string if it doesn't.
+func heapAllocReason(n *Node) string {
if n.Type == nil {
- return false
+ return ""
}
// Parameters are always passed via the stack.
if n.Op == ONAME && (n.Class() == PPARAM || n.Class() == PPARAMOUT) {
- return false
+ return ""
}
if n.Type.Width > maxStackVarSize {
- return true
+ return "too large for stack"
}
if (n.Op == ONEW || n.Op == OPTRLIT) && n.Type.Elem().Width >= maxImplicitStackVarSize {
- return true
+ return "too large for stack"
}
if n.Op == OCLOSURE && closureType(n).Size() >= maxImplicitStackVarSize {
- return true
+ return "too large for stack"
}
if n.Op == OCALLPART && partialCallType(n).Size() >= maxImplicitStackVarSize {
- return true
+ return "too large for stack"
}
- if n.Op == OMAKESLICE && !isSmallMakeSlice(n) {
- return true
+ if n.Op == OMAKESLICE {
+ r := n.Right
+ if r == nil {
+ r = n.Left
+ }
+ if !smallintconst(r) {
+ return "non-constant size"
+ }
+ if t := n.Type; t.Elem().Width != 0 && r.Int64Val() >= maxImplicitStackVarSize/t.Elem().Width {
+ return "too large for stack"
+ }
}
- return false
+ return ""
}
// addrescapes tags node n as having had its address taken
@@ -271,7 +282,7 @@ func addrescapes(n *Node) {
// moveToHeap records the parameter or local variable n as moved to the heap.
func moveToHeap(n *Node) {
- if Debug['r'] != 0 {
+ if Debug.r != 0 {
Dump("MOVE", n)
}
if compiling_runtime {
@@ -348,7 +359,7 @@ func moveToHeap(n *Node) {
n.Xoffset = 0
n.Name.Param.Heapaddr = heapaddr
n.Esc = EscHeap
- if Debug['m'] != 0 {
+ if Debug.m != 0 {
Warnl(n.Pos, "moved to heap: %v", n)
}
}
@@ -377,8 +388,8 @@ func (e *Escape) paramTag(fn *Node, narg int, f *types.Field) string {
// This really doesn't have much to do with escape analysis per se,
// but we are reusing the ability to annotate an individual function
// argument and pass those annotations along to importing code.
- if f.Type.Etype == TUINTPTR {
- if Debug['m'] != 0 {
+ if f.Type.IsUintptr() {
+ if Debug.m != 0 {
Warnl(f.Pos, "assuming %v is unsafe uintptr", name())
}
return unsafeUintptrTag
@@ -393,11 +404,11 @@ func (e *Escape) paramTag(fn *Node, narg int, f *types.Field) string {
// External functions are assumed unsafe, unless
// //go:noescape is given before the declaration.
if fn.Func.Pragma&Noescape != 0 {
- if Debug['m'] != 0 && f.Sym != nil {
+ if Debug.m != 0 && f.Sym != nil {
Warnl(f.Pos, "%v does not escape", name())
}
} else {
- if Debug['m'] != 0 && f.Sym != nil {
+ if Debug.m != 0 && f.Sym != nil {
Warnl(f.Pos, "leaking param: %v", name())
}
esc.AddHeap(0)
@@ -407,15 +418,15 @@ 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 {
+ if f.Type.IsUintptr() {
+ if Debug.m != 0 {
Warnl(f.Pos, "marking %v as escaping uintptr", name())
}
return uintptrEscapesTag
}
- if f.IsDDD() && f.Type.Elem().Etype == TUINTPTR {
+ if f.IsDDD() && f.Type.Elem().IsUintptr() {
// final argument is ...uintptr.
- if Debug['m'] != 0 {
+ if Debug.m != 0 {
Warnl(f.Pos, "marking %v as escaping ...uintptr", name())
}
return uintptrEscapesTag
@@ -437,7 +448,7 @@ func (e *Escape) paramTag(fn *Node, narg int, f *types.Field) string {
esc := loc.paramEsc
esc.Optimize()
- if Debug['m'] != 0 && !loc.escapes {
+ if Debug.m != 0 && !loc.escapes {
if esc.Empty() {
Warnl(f.Pos, "%v does not escape", name())
}
diff --git a/src/cmd/compile/internal/gc/escape.go b/src/cmd/compile/internal/gc/escape.go
index d5cca4a38b..618bdf78e2 100644
--- a/src/cmd/compile/internal/gc/escape.go
+++ b/src/cmd/compile/internal/gc/escape.go
@@ -170,7 +170,7 @@ func (e *Escape) initFunc(fn *Node) {
Fatalf("unexpected node: %v", fn)
}
fn.Esc = EscFuncPlanned
- if Debug['m'] > 3 {
+ if Debug.m > 3 {
Dump("escAnalyze", fn)
}
@@ -247,7 +247,7 @@ func (e *Escape) stmt(n *Node) {
lineno = lno
}()
- if Debug['m'] > 2 {
+ if Debug.m > 2 {
fmt.Printf("%v:[%d] %v stmt: %v\n", linestr(lineno), e.loopDepth, funcSym(e.curfn), n)
}
@@ -275,11 +275,11 @@ func (e *Escape) stmt(n *Node) {
case OLABEL:
switch asNode(n.Sym.Label) {
case &nonlooping:
- if Debug['m'] > 2 {
+ if Debug.m > 2 {
fmt.Printf("%v:%v non-looping label\n", linestr(lineno), n)
}
case &looping:
- if Debug['m'] > 2 {
+ if Debug.m > 2 {
fmt.Printf("%v: %v looping label\n", linestr(lineno), n)
}
e.loopDepth++
@@ -485,7 +485,7 @@ func (e *Escape) exprSkipInit(k EscHole, n *Node) {
e.discard(max)
case OCONV, OCONVNOP:
- if checkPtr(e.curfn, 2) && n.Type.Etype == TUNSAFEPTR && n.Left.Type.IsPtr() {
+ if checkPtr(e.curfn, 2) && n.Type.IsUnsafePtr() && n.Left.Type.IsPtr() {
// When -d=checkptr=2 is enabled, treat
// conversions to unsafe.Pointer as an
// escaping operation. This allows better
@@ -493,7 +493,7 @@ func (e *Escape) exprSkipInit(k EscHole, n *Node) {
// easily detect object boundaries on the heap
// than the stack.
e.assignHeap(n.Left, "conversion to unsafe.Pointer", n)
- } else if n.Type.Etype == TUNSAFEPTR && n.Left.Type.Etype == TUINTPTR {
+ } else if n.Type.IsUnsafePtr() && n.Left.Type.IsUintptr() {
e.unsafeValue(k, n.Left)
} else {
e.expr(k, n.Left)
@@ -625,7 +625,7 @@ func (e *Escape) unsafeValue(k EscHole, n *Node) {
switch n.Op {
case OCONV, OCONVNOP:
- if n.Left.Type.Etype == TUNSAFEPTR {
+ if n.Left.Type.IsUnsafePtr() {
e.expr(k, n.Left)
} else {
e.discard(n.Left)
@@ -717,7 +717,7 @@ func (e *Escape) addrs(l Nodes) []EscHole {
func (e *Escape) assign(dst, src *Node, why string, where *Node) {
// Filter out some no-op assignments for escape analysis.
ignore := dst != nil && src != nil && isSelfAssign(dst, src)
- if ignore && Debug['m'] != 0 {
+ if ignore && Debug.m != 0 {
Warnl(where.Pos, "%v ignoring self-assignment in %S", funcSym(e.curfn), where)
}
@@ -771,10 +771,11 @@ func (e *Escape) call(ks []EscHole, call, where *Node) {
var fn *Node
switch call.Op {
case OCALLFUNC:
- if call.Left.Op == ONAME && call.Left.Class() == PFUNC {
- fn = call.Left
- } else if call.Left.Op == OCLOSURE {
- fn = call.Left.Func.Closure.Func.Nname
+ switch v := staticValue(call.Left); {
+ case v.Op == ONAME && v.Class() == PFUNC:
+ fn = v
+ case v.Op == OCLOSURE:
+ fn = v.Func.Closure.Func.Nname
}
case OCALLMETH:
fn = asNode(call.Left.Type.FuncType().Nname)
@@ -930,7 +931,7 @@ func (k EscHole) note(where *Node, why string) EscHole {
if where == nil || why == "" {
Fatalf("note: missing where/why")
}
- if Debug['m'] >= 2 || logopt.Enabled() {
+ if Debug.m >= 2 || logopt.Enabled() {
k.notes = &EscNote{
next: k.notes,
where: where,
@@ -1030,7 +1031,7 @@ func (e *Escape) newLoc(n *Node, transient bool) *EscLocation {
Fatalf("e.curfn isn't set")
}
if n != nil && n.Type != nil && n.Type.NotInHeap() {
- yyerrorl(n.Pos, "%v is go:notinheap; stack allocation disallowed", n.Type)
+ yyerrorl(n.Pos, "%v is incomplete (or unallocatable); stack allocation disallowed", n.Type)
}
n = canonicalNode(n)
@@ -1051,11 +1052,7 @@ func (e *Escape) newLoc(n *Node, transient bool) *EscLocation {
}
n.SetOpt(loc)
- if mustHeapAlloc(n) {
- why := "too large for stack"
- if n.Op == OMAKESLICE && (!Isconst(n.Left, CTINT) || !Isconst(n.Right, CTINT)) {
- why = "non-constant size"
- }
+ if why := heapAllocReason(n); why != "" {
e.flow(e.heapHole().addr(n, why), loc)
}
}
@@ -1080,9 +1077,9 @@ func (e *Escape) flow(k EscHole, src *EscLocation) {
return
}
if dst.escapes && k.derefs < 0 { // dst = &src
- if Debug['m'] >= 2 || logopt.Enabled() {
+ if Debug.m >= 2 || logopt.Enabled() {
pos := linestr(src.n.Pos)
- if Debug['m'] >= 2 {
+ if Debug.m >= 2 {
fmt.Printf("%s: %v escapes to heap:\n", pos, src.n)
}
explanation := e.explainFlow(pos, dst, src, k.derefs, k.notes, []*logopt.LoggedOpt{})
@@ -1182,8 +1179,8 @@ func (e *Escape) walkOne(root *EscLocation, walkgen uint32, enqueue func(*EscLoc
// that value flow for tagging the function
// later.
if l.isName(PPARAM) {
- if (logopt.Enabled() || Debug['m'] >= 2) && !l.escapes {
- if Debug['m'] >= 2 {
+ if (logopt.Enabled() || Debug.m >= 2) && !l.escapes {
+ if Debug.m >= 2 {
fmt.Printf("%s: parameter %v leaks to %s with derefs=%d:\n", linestr(l.n.Pos), l.n, e.explainLoc(root), base)
}
explanation := e.explainPath(root, l)
@@ -1199,8 +1196,8 @@ func (e *Escape) walkOne(root *EscLocation, walkgen uint32, enqueue func(*EscLoc
// outlives it, then l needs to be heap
// allocated.
if addressOf && !l.escapes {
- if logopt.Enabled() || Debug['m'] >= 2 {
- if Debug['m'] >= 2 {
+ if logopt.Enabled() || Debug.m >= 2 {
+ if Debug.m >= 2 {
fmt.Printf("%s: %v escapes to heap:\n", linestr(l.n.Pos), l.n)
}
explanation := e.explainPath(root, l)
@@ -1238,7 +1235,7 @@ func (e *Escape) explainPath(root, src *EscLocation) []*logopt.LoggedOpt {
for {
// Prevent infinite loop.
if visited[src] {
- if Debug['m'] >= 2 {
+ if Debug.m >= 2 {
fmt.Printf("%s: warning: truncated explanation due to assignment cycle; see golang.org/issue/35518\n", pos)
}
break
@@ -1266,7 +1263,7 @@ func (e *Escape) explainFlow(pos string, dst, srcloc *EscLocation, derefs int, n
if derefs >= 0 {
ops = strings.Repeat("*", derefs)
}
- print := Debug['m'] >= 2
+ print := Debug.m >= 2
flow := fmt.Sprintf(" flow: %s = %s%v:", e.explainLoc(dst), ops, e.explainLoc(srcloc))
if print {
@@ -1420,7 +1417,7 @@ func (e *Escape) finish(fns []*Node) {
if loc.escapes {
if n.Op != ONAME {
- if Debug['m'] != 0 {
+ if Debug.m != 0 {
Warnl(n.Pos, "%S escapes to heap", n)
}
if logopt.Enabled() {
@@ -1430,7 +1427,7 @@ func (e *Escape) finish(fns []*Node) {
n.Esc = EscHeap
addrescapes(n)
} else {
- if Debug['m'] != 0 && n.Op != ONAME {
+ if Debug.m != 0 && n.Op != ONAME {
Warnl(n.Pos, "%S does not escape", n)
}
n.Esc = EscNone
diff --git a/src/cmd/compile/internal/gc/export.go b/src/cmd/compile/internal/gc/export.go
index 44bea2b1fd..c6917e0f81 100644
--- a/src/cmd/compile/internal/gc/export.go
+++ b/src/cmd/compile/internal/gc/export.go
@@ -31,7 +31,7 @@ func exportsym(n *Node) {
}
n.Sym.SetOnExportList(true)
- if Debug['E'] != 0 {
+ if Debug.E != 0 {
fmt.Printf("export symbol %v\n", n.Sym)
}
@@ -96,7 +96,7 @@ func importsym(ipkg *types.Pkg, s *types.Sym, op Op) *Node {
return n
}
-// pkgtype returns the named type declared by symbol s.
+// importtype returns the named type declared by symbol s.
// If no such type has been declared yet, a forward declaration is returned.
// ipkg is the package being imported
func importtype(ipkg *types.Pkg, pos src.XPos, s *types.Sym) *types.Type {
@@ -150,7 +150,7 @@ func importconst(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type, val
n.SetVal(val)
- if Debug['E'] != 0 {
+ if Debug.E != 0 {
fmt.Printf("import const %v %L = %v\n", s, t, val)
}
}
@@ -166,7 +166,7 @@ func importfunc(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type) {
n.Func = new(Func)
t.SetNname(asTypesNode(n))
- if Debug['E'] != 0 {
+ if Debug.E != 0 {
fmt.Printf("import func %v%S\n", s, t)
}
}
@@ -179,7 +179,7 @@ func importvar(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type) {
return
}
- if Debug['E'] != 0 {
+ if Debug.E != 0 {
fmt.Printf("import var %v %L\n", s, t)
}
}
@@ -192,7 +192,7 @@ func importalias(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type) {
return
}
- if Debug['E'] != 0 {
+ if Debug.E != 0 {
fmt.Printf("import type %v = %L\n", s, t)
}
}
diff --git a/src/cmd/compile/internal/gc/float_test.go b/src/cmd/compile/internal/gc/float_test.go
index 6ae363be22..c619d25705 100644
--- a/src/cmd/compile/internal/gc/float_test.go
+++ b/src/cmd/compile/internal/gc/float_test.go
@@ -6,17 +6,9 @@ package gc
import (
"math"
- "os"
- "runtime"
"testing"
)
-// For GO386=387, make sure fucomi* opcodes are not used
-// for comparison operations.
-// Note that this test will fail only on a Pentium MMX
-// processor (with GOARCH=386 GO386=387), as it just runs
-// some code and looks for an unimplemented instruction fault.
-
//go:noinline
func compare1(a, b float64) bool {
return a < b
@@ -137,9 +129,6 @@ func TestFloatCompareFolded(t *testing.T) {
}
}
-// For GO386=387, make sure fucomi* opcodes are not used
-// for float->int conversions.
-
//go:noinline
func cvt1(a float64) uint64 {
return uint64(a)
@@ -370,14 +359,6 @@ func TestFloat32StoreToLoadConstantFold(t *testing.T) {
// are not converted to quiet NaN (qNaN) values during compilation.
// See issue #27193 for more information.
- // TODO: this method for detecting 387 won't work if the compiler has been
- // built using GOARCH=386 GO386=387 and either the target is a different
- // architecture or the GO386=387 environment variable is not set when the
- // test is run.
- if runtime.GOARCH == "386" && os.Getenv("GO386") == "387" {
- t.Skip("signaling NaNs are not propagated on 387 (issue #27516)")
- }
-
// signaling NaNs
{
const nan = uint32(0x7f800001) // sNaN
diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go
index 866cd0a714..f92f5d0e88 100644
--- a/src/cmd/compile/internal/gc/fmt.go
+++ b/src/cmd/compile/internal/gc/fmt.go
@@ -419,10 +419,19 @@ func (n *Node) format(s fmt.State, verb rune, mode fmtMode) {
func (n *Node) jconv(s fmt.State, flag FmtFlag) {
c := flag & FmtShort
+ // Useful to see which nodes in a Node Dump/dumplist are actually identical
+ if Debug_dumpptrs != 0 {
+ fmt.Fprintf(s, " p(%p)", n)
+ }
if c == 0 && n.Name != nil && n.Name.Vargen != 0 {
fmt.Fprintf(s, " g(%d)", n.Name.Vargen)
}
+ if Debug_dumpptrs != 0 && c == 0 && n.Name != nil && n.Name.Defn != nil {
+ // Useful to see where Defn is set and what node it points to
+ fmt.Fprintf(s, " defn(%p)", n.Name.Defn)
+ }
+
if n.Pos.IsKnown() {
pfx := ""
switch n.Pos.IsStmt() {
@@ -492,6 +501,15 @@ func (n *Node) jconv(s fmt.State, flag FmtFlag) {
if n.Name.Assigned() {
fmt.Fprint(s, " assigned")
}
+ if n.Name.IsClosureVar() {
+ fmt.Fprint(s, " closurevar")
+ }
+ if n.Name.Captured() {
+ fmt.Fprint(s, " captured")
+ }
+ if n.Name.IsOutputParamHeapAddr() {
+ fmt.Fprint(s, " outputparamheapaddr")
+ }
}
if n.Bounded() {
fmt.Fprint(s, " bounded")
@@ -711,6 +729,17 @@ func tconv2(b *bytes.Buffer, t *types.Type, flag FmtFlag, mode fmtMode, visited
return
}
+ if t.Etype == types.TRESULTS {
+ tys := t.Extra.(*types.Results).Types
+ for i, et := range tys {
+ if i > 0 {
+ b.WriteByte(',')
+ }
+ b.WriteString(et.String())
+ }
+ return
+ }
+
flag, mode = flag.update(mode)
if mode == FTypeIdName {
flag |= FmtUnsigned
@@ -762,17 +791,17 @@ func tconv2(b *bytes.Buffer, t *types.Type, flag FmtFlag, mode fmtMode, visited
if int(t.Etype) < len(basicnames) && basicnames[t.Etype] != "" {
var name string
switch t {
- case types.Idealbool:
+ case types.UntypedBool:
name = "untyped bool"
- case types.Idealstring:
+ case types.UntypedString:
name = "untyped string"
- case types.Idealint:
+ case types.UntypedInt:
name = "untyped int"
- case types.Idealrune:
+ case types.UntypedRune:
name = "untyped rune"
- case types.Idealfloat:
+ case types.UntypedFloat:
name = "untyped float"
- case types.Idealcomplex:
+ case types.UntypedComplex:
name = "untyped complex"
default:
name = basicnames[t.Etype]
@@ -781,6 +810,13 @@ func tconv2(b *bytes.Buffer, t *types.Type, flag FmtFlag, mode fmtMode, visited
return
}
+ if mode == FDbg {
+ b.WriteString(t.Etype.String())
+ b.WriteByte('-')
+ tconv2(b, t, flag, FErr, visited)
+ return
+ }
+
// At this point, we might call tconv2 recursively. Add the current type to the visited list so we don't
// try to print it recursively.
// We record the offset in the result buffer where the type's text starts. This offset serves as a reference
@@ -794,12 +830,6 @@ func tconv2(b *bytes.Buffer, t *types.Type, flag FmtFlag, mode fmtMode, visited
visited[t] = b.Len()
defer delete(visited, t)
- if mode == FDbg {
- b.WriteString(t.Etype.String())
- b.WriteByte('-')
- tconv2(b, t, flag, FErr, visited)
- return
- }
switch t.Etype {
case TPTR:
b.WriteByte('*')
@@ -1322,7 +1352,7 @@ func (n *Node) exprfmt(s fmt.State, prec int, mode fmtMode) {
n.Orig.exprfmt(s, prec, mode)
return
}
- if n.Type != nil && n.Type.Etype != TIDEAL && n.Type.Etype != TNIL && n.Type != types.Idealbool && n.Type != types.Idealstring {
+ if n.Type != nil && !n.Type.IsUntyped() {
// Need parens when type begins with what might
// be misinterpreted as a unary operator: * or <-.
if n.Type.IsPtr() || (n.Type.IsChan() && n.Type.ChanDir() == types.Crecv) {
@@ -1407,7 +1437,7 @@ func (n *Node) exprfmt(s fmt.State, prec int, mode fmtMode) {
return
}
if n.Right != nil {
- mode.Fprintf(s, "%v literal", n.Right)
+ mode.Fprintf(s, "%v{%s}", n.Right, ellipsisIf(n.List.Len() != 0))
return
}
@@ -1421,7 +1451,7 @@ func (n *Node) exprfmt(s fmt.State, prec int, mode fmtMode) {
case OSTRUCTLIT, OARRAYLIT, OSLICELIT, OMAPLIT:
if mode == FErr {
- mode.Fprintf(s, "%v literal", n.Type)
+ mode.Fprintf(s, "%v{%s}", n.Type, ellipsisIf(n.List.Len() != 0))
return
}
mode.Fprintf(s, "(%v{ %.v })", n.Type, n.List)
@@ -1698,6 +1728,9 @@ func (n *Node) nodedump(s fmt.State, flag FmtFlag, mode fmtMode) {
}
}
+ if n.Op == OCLOSURE && n.Func.Closure != nil && n.Func.Closure.Func.Nname.Sym != nil {
+ mode.Fprintf(s, " fnName %v", n.Func.Closure.Func.Nname.Sym)
+ }
if n.Sym != nil && n.Op != ONAME {
mode.Fprintf(s, " %v", n.Sym)
}
@@ -1713,6 +1746,16 @@ func (n *Node) nodedump(s fmt.State, flag FmtFlag, mode fmtMode) {
if n.Right != nil {
mode.Fprintf(s, "%v", n.Right)
}
+ if n.Func != nil && n.Func.Closure != nil && n.Func.Closure.Nbody.Len() != 0 {
+ indent(s)
+ // The function associated with a closure
+ mode.Fprintf(s, "%v-clofunc%v", n.Op, n.Func.Closure)
+ }
+ if n.Func != nil && n.Func.Dcl != nil && len(n.Func.Dcl) != 0 {
+ indent(s)
+ // The dcls for a func or closure
+ mode.Fprintf(s, "%v-dcl%v", n.Op, asNodes(n.Func.Dcl))
+ }
if n.List.Len() != 0 {
indent(s)
mode.Fprintf(s, "%v-list%v", n.Op, n.List)
@@ -1934,3 +1977,10 @@ func indent(s fmt.State) {
fmt.Fprint(s, ". ")
}
}
+
+func ellipsisIf(b bool) string {
+ if b {
+ return "..."
+ }
+ return ""
+}
diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go
index 9079ce2afc..274930bd15 100644
--- a/src/cmd/compile/internal/gc/go.go
+++ b/src/cmd/compile/internal/gc/go.go
@@ -61,12 +61,12 @@ type Class uint8
//go:generate stringer -type=Class
const (
Pxxx Class = iota // no class; used during ssa conversion to indicate pseudo-variables
- PEXTERN // global variable
+ PEXTERN // global variables
PAUTO // local variables
- PAUTOHEAP // local variable or parameter moved to heap
+ PAUTOHEAP // local variables or parameters moved to heap
PPARAM // input arguments
PPARAMOUT // output results
- PFUNC // global function
+ PFUNC // global functions
// Careful: Class is stored in three bits in Node.flags.
_ = uint((1 << 3) - iota) // static assert for iota <= (1 << 3)
@@ -116,7 +116,15 @@ var decldepth int32
var nolocalimports bool
-var Debug [256]int
+// gc debug flags
+type DebugFlags struct {
+ P, B, C, E,
+ K, L, N, S,
+ W, e, h, j,
+ l, m, r, w int
+}
+
+var Debug DebugFlags
var debugstr string
@@ -259,7 +267,6 @@ type Arch struct {
REGSP int
MAXWIDTH int64
- Use387 bool // should 386 backend use 387 FP instructions instead of sse2.
SoftFloat bool
PadFrame func(int64) int64
@@ -302,6 +309,7 @@ var (
growslice,
msanread,
msanwrite,
+ msanmove,
newobject,
newproc,
panicdivide,
@@ -328,10 +336,6 @@ var (
BoundsCheckFunc [ssa.BoundsKindCount]*obj.LSym
ExtendCheckFunc [ssa.BoundsKindCount]*obj.LSym
- // GO386=387
- ControlWord64trunc,
- ControlWord32 *obj.LSym
-
// Wasm
WasmMove,
WasmZero,
diff --git a/src/cmd/compile/internal/gc/gsubr.go b/src/cmd/compile/internal/gc/gsubr.go
index 480d411f49..d599a383e7 100644
--- a/src/cmd/compile/internal/gc/gsubr.go
+++ b/src/cmd/compile/internal/gc/gsubr.go
@@ -70,12 +70,8 @@ func newProgs(fn *Node, worker int) *Progs {
pp.pos = fn.Pos
pp.settext(fn)
// PCDATA tables implicitly start with index -1.
- pp.prevLive = LivenessIndex{-1, -1, false}
- if go115ReduceLiveness {
- pp.nextLive = pp.prevLive
- } else {
- pp.nextLive = LivenessInvalid
- }
+ pp.prevLive = LivenessIndex{-1, false}
+ pp.nextLive = pp.prevLive
return pp
}
@@ -120,31 +116,15 @@ func (pp *Progs) Prog(as obj.As) *obj.Prog {
Addrconst(&p.From, objabi.PCDATA_StackMapIndex)
Addrconst(&p.To, int64(idx))
}
- if !go115ReduceLiveness {
+ if pp.nextLive.isUnsafePoint != pp.prevLive.isUnsafePoint {
+ // Emit unsafe-point marker.
+ pp.prevLive.isUnsafePoint = pp.nextLive.isUnsafePoint
+ p := pp.Prog(obj.APCDATA)
+ Addrconst(&p.From, objabi.PCDATA_UnsafePoint)
if pp.nextLive.isUnsafePoint {
- // Unsafe points are encoded as a special value in the
- // register map.
- pp.nextLive.regMapIndex = objabi.PCDATA_RegMapUnsafe
- }
- if pp.nextLive.regMapIndex != pp.prevLive.regMapIndex {
- // Emit register map index change.
- idx := pp.nextLive.regMapIndex
- pp.prevLive.regMapIndex = idx
- p := pp.Prog(obj.APCDATA)
- Addrconst(&p.From, objabi.PCDATA_RegMapIndex)
- Addrconst(&p.To, int64(idx))
- }
- } else {
- if pp.nextLive.isUnsafePoint != pp.prevLive.isUnsafePoint {
- // Emit unsafe-point marker.
- pp.prevLive.isUnsafePoint = pp.nextLive.isUnsafePoint
- p := pp.Prog(obj.APCDATA)
- Addrconst(&p.From, objabi.PCDATA_UnsafePoint)
- if pp.nextLive.isUnsafePoint {
- Addrconst(&p.To, objabi.PCDATA_UnsafePointUnsafe)
- } else {
- Addrconst(&p.To, objabi.PCDATA_UnsafePointSafe)
- }
+ Addrconst(&p.To, objabi.PCDATA_UnsafePointUnsafe)
+ } else {
+ Addrconst(&p.To, objabi.PCDATA_UnsafePointSafe)
}
}
@@ -153,7 +133,7 @@ func (pp *Progs) Prog(as obj.As) *obj.Prog {
pp.clearp(pp.next)
p.Link = pp.next
- if !pp.pos.IsKnown() && Debug['K'] != 0 {
+ if !pp.pos.IsKnown() && Debug.K != 0 {
Warn("prog: unknown position (line 0)")
}
@@ -199,7 +179,7 @@ func (pp *Progs) settext(fn *Node) {
ptxt := pp.Prog(obj.ATEXT)
pp.Text = ptxt
- fn.Func.lsym.Func.Text = ptxt
+ fn.Func.lsym.Func().Text = ptxt
ptxt.From.Type = obj.TYPE_MEM
ptxt.From.Name = obj.NAME_EXTERN
ptxt.From.Sym = fn.Func.lsym
@@ -322,6 +302,12 @@ func ggloblnod(nam *Node) {
if nam.Name.LibfuzzerExtraCounter() {
s.Type = objabi.SLIBFUZZER_EXTRA_COUNTER
}
+ if nam.Sym.Linkname != "" {
+ // Make sure linkname'd symbol is non-package. When a symbol is
+ // both imported and linkname'd, s.Pkg may not set to "_" in
+ // types.Sym.Linksym because LSym already exists. Set it here.
+ s.Pkg = "_"
+ }
}
func ggloblsym(s *obj.LSym, width int32, flags int16) {
diff --git a/src/cmd/compile/internal/gc/iexport.go b/src/cmd/compile/internal/gc/iexport.go
index b3f50b63af..1f53d8ca7d 100644
--- a/src/cmd/compile/internal/gc/iexport.go
+++ b/src/cmd/compile/internal/gc/iexport.go
@@ -751,11 +751,11 @@ func (w *exportWriter) param(f *types.Field) {
func constTypeOf(typ *types.Type) Ctype {
switch typ {
- case types.Idealint, types.Idealrune:
+ case types.UntypedInt, types.UntypedRune:
return CTINT
- case types.Idealfloat:
+ case types.UntypedFloat:
return CTFLT
- case types.Idealcomplex:
+ case types.UntypedComplex:
return CTCPLX
}
@@ -780,8 +780,8 @@ func constTypeOf(typ *types.Type) Ctype {
}
func (w *exportWriter) value(typ *types.Type, v Val) {
- if typ.IsUntyped() {
- typ = untype(v.Ctype())
+ if vt := idealType(v.Ctype()); typ.IsUntyped() && typ != vt {
+ Fatalf("exporter: untyped type mismatch, have: %v, want: %v", typ, vt)
}
w.typ(typ)
@@ -1017,6 +1017,8 @@ func (w *exportWriter) symIdx(s *types.Sym) {
}
func (w *exportWriter) typeExt(t *types.Type) {
+ // Export whether this type is marked notinheap.
+ w.bool(t.NotInHeap())
// For type T, export the index of type descriptor symbols of T and *T.
if i, ok := typeSymIdx[t]; ok {
w.int64(i[0])
@@ -1136,13 +1138,10 @@ func (w *exportWriter) stmt(n *Node) {
w.pos(n.Pos)
w.stmtList(n.Ninit)
w.exprsOrNil(n.Left, nil)
- w.stmtList(n.List)
+ w.caseList(n)
- case OCASE:
- w.op(OCASE)
- w.pos(n.Pos)
- w.stmtList(n.List)
- w.stmtList(n.Nbody)
+ // case OCASE:
+ // handled by caseList
case OFALL:
w.op(OFALL)
@@ -1166,6 +1165,24 @@ func (w *exportWriter) stmt(n *Node) {
}
}
+func (w *exportWriter) caseList(sw *Node) {
+ namedTypeSwitch := sw.Op == OSWITCH && sw.Left != nil && sw.Left.Op == OTYPESW && sw.Left.Left != nil
+
+ cases := sw.List.Slice()
+ w.uint64(uint64(len(cases)))
+ for _, cas := range cases {
+ if cas.Op != OCASE {
+ Fatalf("expected OCASE, got %v", cas)
+ }
+ w.pos(cas.Pos)
+ w.stmtList(cas.List)
+ if namedTypeSwitch {
+ w.localName(cas.Rlist.First())
+ }
+ w.stmtList(cas.Nbody)
+ }
+}
+
func (w *exportWriter) exprList(list Nodes) {
for _, n := range list.Slice() {
w.expr(n)
@@ -1230,6 +1247,19 @@ func (w *exportWriter) expr(n *Node) {
w.op(OTYPE)
w.typ(n.Type)
+ case OTYPESW:
+ w.op(OTYPESW)
+ w.pos(n.Pos)
+ var s *types.Sym
+ if n.Left != nil {
+ if n.Left.Op != ONONAME {
+ Fatalf("expected ONONAME, got %v", n.Left)
+ }
+ s = n.Left.Sym
+ }
+ w.localIdent(s, 0) // declared pseudo-variable, if any
+ w.exprsOrNil(n.Right, nil)
+
// case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC:
// should have been resolved by typechecking - handled by default case
@@ -1264,8 +1294,13 @@ func (w *exportWriter) expr(n *Node) {
// case OSTRUCTKEY:
// unreachable - handled in case OSTRUCTLIT by elemList
- // case OCALLPART:
- // unimplemented - handled by default case
+ case OCALLPART:
+ // An OCALLPART is an OXDOT before type checking.
+ w.op(OXDOT)
+ w.pos(n.Pos)
+ w.expr(n.Left)
+ // Right node should be ONAME
+ w.selector(n.Right.Sym)
case OXDOT, ODOT, ODOTPTR, ODOTINTER, ODOTMETH:
w.op(OXDOT)
diff --git a/src/cmd/compile/internal/gc/iimport.go b/src/cmd/compile/internal/gc/iimport.go
index 4169222c14..c0114d0e53 100644
--- a/src/cmd/compile/internal/gc/iimport.go
+++ b/src/cmd/compile/internal/gc/iimport.go
@@ -375,7 +375,7 @@ func (p *importReader) value() (typ *types.Type, v Val) {
v.U = p.string()
case CTINT:
x := new(Mpint)
- x.Rune = typ == types.Idealrune
+ x.Rune = typ == types.UntypedRune
p.mpint(&x.Val, typ)
v.U = x
case CTFLT:
@@ -596,7 +596,6 @@ func (r *importReader) typ1() *types.Type {
// Ensure we expand the interface in the frontend (#25055).
checkwidth(t)
-
return t
}
}
@@ -711,6 +710,7 @@ func (r *importReader) symIdx(s *types.Sym) {
}
func (r *importReader) typeExt(t *types.Type) {
+ t.SetNotInHeap(r.bool())
i, pi := r.int64(), r.int64()
if i != -1 && pi != -1 {
typeSymIdx[t] = [2]int64{i, pi}
@@ -742,8 +742,8 @@ func (r *importReader) doInline(n *Node) {
importlist = append(importlist, n)
- if Debug['E'] > 0 && Debug['m'] > 2 {
- if Debug['m'] > 3 {
+ if Debug.E > 0 && Debug.m > 2 {
+ if Debug.m > 3 {
fmt.Printf("inl body for %v %#v: %+v\n", n, n.Type, asNodes(n.Func.Inl.Body))
} else {
fmt.Printf("inl body for %v %#v: %v\n", n, n.Type, asNodes(n.Func.Inl.Body))
@@ -784,6 +784,28 @@ func (r *importReader) stmtList() []*Node {
return list
}
+func (r *importReader) caseList(sw *Node) []*Node {
+ namedTypeSwitch := sw.Op == OSWITCH && sw.Left != nil && sw.Left.Op == OTYPESW && sw.Left.Left != nil
+
+ cases := make([]*Node, r.uint64())
+ for i := range cases {
+ cas := nodl(r.pos(), OCASE, nil, nil)
+ cas.List.Set(r.stmtList())
+ if namedTypeSwitch {
+ // Note: per-case variables will have distinct, dotted
+ // names after import. That's okay: swt.go only needs
+ // Sym for diagnostics anyway.
+ caseVar := newnamel(cas.Pos, r.ident())
+ declare(caseVar, dclcontext)
+ cas.Rlist.Set1(caseVar)
+ caseVar.Name.Defn = sw.Left
+ }
+ cas.Nbody.Set(r.stmtList())
+ cases[i] = cas
+ }
+ return cases
+}
+
func (r *importReader) exprList() []*Node {
var list []*Node
for {
@@ -831,6 +853,14 @@ func (r *importReader) node() *Node {
case OTYPE:
return typenod(r.typ())
+ case OTYPESW:
+ n := nodl(r.pos(), OTYPESW, nil, nil)
+ if s := r.ident(); s != nil {
+ n.Left = npos(n.Pos, newnoname(s))
+ }
+ n.Right, _ = r.exprsOrNil()
+ return n
+
// case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC:
// unreachable - should have been resolved by typechecking
@@ -866,7 +896,7 @@ func (r *importReader) node() *Node {
// unreachable - handled in case OSTRUCTLIT by elemList
// case OCALLPART:
- // unimplemented
+ // unreachable - mapped to case OXDOT below by exporter
// case OXDOT, ODOT, ODOTPTR, ODOTINTER, ODOTMETH:
// unreachable - mapped to case OXDOT below by exporter
@@ -1025,16 +1055,11 @@ func (r *importReader) node() *Node {
n := nodl(r.pos(), op, nil, nil)
n.Ninit.Set(r.stmtList())
n.Left, _ = r.exprsOrNil()
- n.List.Set(r.stmtList())
+ n.List.Set(r.caseList(n))
return n
- 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 OCASE:
+ // handled by caseList
case OFALL:
n := nodl(r.pos(), OFALL, nil, nil)
diff --git a/src/cmd/compile/internal/gc/init.go b/src/cmd/compile/internal/gc/init.go
index 94cbcf9846..ec9cc4bddc 100644
--- a/src/cmd/compile/internal/gc/init.go
+++ b/src/cmd/compile/internal/gc/init.go
@@ -59,7 +59,7 @@ func fninit(n []*Node) {
Curfn = fn
typecheckslice(nf, ctxStmt)
Curfn = nil
- funccompile(fn)
+ xtop = append(xtop, fn)
fns = append(fns, initializers.Linksym())
}
if dummyInitFn.Func.Dcl != nil {
@@ -68,16 +68,14 @@ func fninit(n []*Node) {
// something's weird if we get here.
Fatalf("dummyInitFn still has declarations")
}
+ dummyInitFn = nil
// Record user init functions.
for i := 0; i < renameinitgen; i++ {
s := lookupN("init.", i)
fn := asNode(s.Def).Name.Defn
// Skip init functions with empty bodies.
- // noder.go doesn't allow external init functions, and
- // order.go has already removed any OEMPTY nodes, so
- // checking Len() == 0 is sufficient here.
- if fn.Nbody.Len() == 0 {
+ if fn.Nbody.Len() == 1 && fn.Nbody.First().Op == OEMPTY {
continue
}
fns = append(fns, s.Linksym())
diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go
index fa5b3ec698..419056985f 100644
--- a/src/cmd/compile/internal/gc/inl.go
+++ b/src/cmd/compile/internal/gc/inl.go
@@ -7,7 +7,7 @@
// saves a copy of the body. Then inlcalls walks each function body to
// expand calls to inlinable functions.
//
-// The debug['l'] flag controls the aggressiveness. Note that main() swaps level 0 and 1,
+// The Debug.l flag controls the aggressiveness. Note that main() swaps level 0 and 1,
// making 1 the default and -l disable. Additional levels (beyond -l) may be buggy and
// are not supported.
// 0: disabled
@@ -21,7 +21,7 @@
// The -d typcheckinl flag enables early typechecking of all imported bodies,
// which is useful to flush out bugs.
//
-// The debug['m'] flag enables diagnostic output. a single -m is useful for verifying
+// The Debug.m flag enables diagnostic output. a single -m is useful for verifying
// which calls get inlined or not, more is for debugging, and may go away at any point.
package gc
@@ -85,7 +85,7 @@ func typecheckinl(fn *Node) {
return // typecheckinl on local function
}
- if Debug['m'] > 2 || Debug_export != 0 {
+ if Debug.m > 2 || Debug_export != 0 {
fmt.Printf("typecheck import [%v] %L { %#v }\n", fn.Sym, fn, asNodes(fn.Func.Inl.Body))
}
@@ -94,10 +94,11 @@ func typecheckinl(fn *Node) {
typecheckslice(fn.Func.Inl.Body, ctxStmt)
Curfn = savefn
- // During typechecking, declarations are added to
- // Curfn.Func.Dcl. Move them to Inl.Dcl for consistency with
- // how local functions behave. (Append because typecheckinl
- // may be called multiple times.)
+ // During expandInline (which imports fn.Func.Inl.Body),
+ // declarations are added to fn.Func.Dcl by funcHdr(). Move them
+ // to fn.Func.Inl.Dcl for consistency with how local functions
+ // behave. (Append because typecheckinl may be called multiple
+ // times.)
fn.Func.Inl.Dcl = append(fn.Func.Inl.Dcl, fn.Func.Dcl...)
fn.Func.Dcl = nil
@@ -116,10 +117,10 @@ func caninl(fn *Node) {
}
var reason string // reason, if any, that the function was not inlined
- if Debug['m'] > 1 || logopt.Enabled() {
+ if Debug.m > 1 || logopt.Enabled() {
defer func() {
if reason != "" {
- if Debug['m'] > 1 {
+ if Debug.m > 1 {
fmt.Printf("%v: cannot inline %v: %s\n", fn.Line(), fn.Func.Nname, reason)
}
if logopt.Enabled() {
@@ -187,7 +188,7 @@ func caninl(fn *Node) {
defer n.Func.SetInlinabilityChecked(true)
cc := int32(inlineExtraCallCost)
- if Debug['l'] == 4 {
+ if Debug.l == 4 {
cc = 1 // this appears to yield better performance than 0.
}
@@ -224,9 +225,9 @@ func caninl(fn *Node) {
// this is so export can find the body of a method
fn.Type.FuncType().Nname = asTypesNode(n)
- if Debug['m'] > 1 {
+ if Debug.m > 1 {
fmt.Printf("%v: can inline %#v with cost %d as: %#v { %#v }\n", fn.Line(), n, inlineMaxBudget-visitor.budget, fn.Type, asNodes(n.Func.Inl.Body))
- } else if Debug['m'] != 0 {
+ } else if Debug.m != 0 {
fmt.Printf("%v: can inline %v\n", fn.Line(), n)
}
if logopt.Enabled() {
@@ -257,21 +258,39 @@ func inlFlood(n *Node) {
typecheckinl(n)
+ // Recursively identify all referenced functions for
+ // reexport. We want to include even non-called functions,
+ // because after inlining they might be callable.
inspectList(asNodes(n.Func.Inl.Body), func(n *Node) bool {
switch n.Op {
case ONAME:
- // Mark any referenced global variables or
- // functions for reexport. Skip methods,
- // because they're reexported alongside their
- // receiver type.
- if n.Class() == PEXTERN || n.Class() == PFUNC && !n.isMethodExpression() {
+ switch n.Class() {
+ case PFUNC:
+ if n.isMethodExpression() {
+ inlFlood(asNode(n.Type.Nname()))
+ } else {
+ inlFlood(n)
+ exportsym(n)
+ }
+ case PEXTERN:
exportsym(n)
}
- case OCALLFUNC, OCALLMETH:
- // Recursively flood any functions called by
- // this one.
- inlFlood(asNode(n.Left.Type.Nname()))
+ case ODOTMETH:
+ fn := asNode(n.Type.Nname())
+ inlFlood(fn)
+
+ case OCALLPART:
+ // Okay, because we don't yet inline indirect
+ // calls to method values.
+ case OCLOSURE:
+ // If the closure is inlinable, we'll need to
+ // flood it too. But today we don't support
+ // inlining functions that contain closures.
+ //
+ // When we do, we'll probably want:
+ // inlFlood(n.Func.Closure.Func.Nname)
+ Fatalf("unexpected closure in inlinable function")
}
return true
})
@@ -325,18 +344,10 @@ func (v *hairyVisitor) visit(n *Node) bool {
break
}
- if fn := n.Left.Func; fn != nil && fn.Inl != nil {
- v.budget -= fn.Inl.Cost
+ if fn := inlCallee(n.Left); fn != nil && fn.Func.Inl != nil {
+ v.budget -= fn.Func.Inl.Cost
break
}
- if n.Left.isMethodExpression() {
- if d := asNode(n.Left.Sym.Def); d != nil && d.Func.Inl != nil {
- v.budget -= d.Func.Inl.Cost
- break
- }
- }
- // TODO(mdempsky): Budget for OCLOSURE calls if we
- // ever allow that. See #15561 and #23093.
// Call cost for non-leaf inlining.
v.budget -= v.extraCallCost
@@ -383,16 +394,11 @@ func (v *hairyVisitor) visit(n *Node) bool {
return true
case OCLOSURE,
- OCALLPART,
ORANGE,
- OFOR,
- OFORUNTIL,
OSELECT,
- OTYPESW,
OGO,
ODEFER,
ODCLTYPE, // can't print yet
- OBREAK,
ORETJMP:
v.reason = "unhandled op " + n.Op.String()
return true
@@ -400,10 +406,23 @@ func (v *hairyVisitor) visit(n *Node) bool {
case OAPPEND:
v.budget -= inlineExtraAppendCost
- case ODCLCONST, OEMPTY, OFALL, OLABEL:
+ case ODCLCONST, OEMPTY, OFALL:
// These nodes don't produce code; omit from inlining budget.
return false
+ case OLABEL:
+ // TODO(mdempsky): Add support for inlining labeled control statements.
+ if n.labeledControl() != nil {
+ v.reason = "labeled control"
+ return true
+ }
+
+ case OBREAK, OCONTINUE:
+ if n.Sym != nil {
+ // Should have short-circuited due to labeledControl above.
+ Fatalf("unexpected labeled break/continue: %v", n)
+ }
+
case OIF:
if Isconst(n.Left, CTBOOL) {
// This if and the condition cost nothing.
@@ -421,7 +440,7 @@ func (v *hairyVisitor) visit(n *Node) bool {
v.budget--
// When debugging, don't stop early, to get full cost of inlining this function
- if v.budget < 0 && Debug['m'] < 2 && !logopt.Enabled() {
+ if v.budget < 0 && Debug.m < 2 && !logopt.Enabled() {
return true
}
@@ -430,9 +449,9 @@ func (v *hairyVisitor) visit(n *Node) bool {
v.visitList(n.Ninit) || v.visitList(n.Nbody)
}
-// Inlcopy and inlcopylist recursively copy the body of a function.
-// Any name-like node of non-local class is marked for re-export by adding it to
-// the exportlist.
+// inlcopylist (together with inlcopy) recursively copies a list of nodes, except
+// that it keeps the same ONAME, OTYPE, and OLITERAL nodes. It is used for copying
+// the body and dcls of an inlineable function.
func inlcopylist(ll []*Node) []*Node {
s := make([]*Node, 0, len(ll))
for _, n := range ll {
@@ -452,7 +471,7 @@ func inlcopy(n *Node) *Node {
}
m := n.copy()
- if m.Func != nil {
+ if n.Op != OCALLPART && m.Func != nil {
Fatalf("unexpected Func: %v", m)
}
m.Left = inlcopy(n.Left)
@@ -570,13 +589,11 @@ func inlnode(n *Node, maxCost int32, inlMap map[*Node]bool) *Node {
}
switch n.Op {
- // inhibit inlining of their argument
case ODEFER, OGO:
switch n.Left.Op {
case OCALLFUNC, OCALLMETH:
n.Left.SetNoInline(true)
}
- return n
// TODO do them here (or earlier),
// so escape analysis can avoid more heapmoves.
@@ -666,60 +683,18 @@ func inlnode(n *Node, maxCost int32, inlMap map[*Node]bool) *Node {
switch n.Op {
case OCALLFUNC:
- if Debug['m'] > 3 {
+ if Debug.m > 3 {
fmt.Printf("%v:call to func %+v\n", n.Line(), n.Left)
}
- if n.Left.Func != nil && n.Left.Func.Inl != nil && !isIntrinsicCall(n) { // normal case
- n = mkinlcall(n, n.Left, maxCost, inlMap)
- } else if n.Left.isMethodExpression() && asNode(n.Left.Sym.Def) != nil {
- n = mkinlcall(n, asNode(n.Left.Sym.Def), maxCost, inlMap)
- } else if n.Left.Op == OCLOSURE {
- if f := inlinableClosure(n.Left); f != nil {
- n = mkinlcall(n, f, maxCost, inlMap)
- }
- } else if n.Left.Op == ONAME && n.Left.Name != nil && n.Left.Name.Defn != nil {
- if d := n.Left.Name.Defn; d.Op == OAS && d.Right.Op == OCLOSURE {
- if f := inlinableClosure(d.Right); f != nil {
- // NB: this check is necessary to prevent indirect re-assignment of the variable
- // having the address taken after the invocation or only used for reads is actually fine
- // but we have no easy way to distinguish the safe cases
- if d.Left.Name.Addrtaken() {
- if Debug['m'] > 1 {
- fmt.Printf("%v: cannot inline escaping closure variable %v\n", n.Line(), n.Left)
- }
- if logopt.Enabled() {
- logopt.LogOpt(n.Pos, "cannotInlineCall", "inline", Curfn.funcname(),
- fmt.Sprintf("%v cannot be inlined (escaping closure variable)", n.Left))
- }
- break
- }
-
- // ensure the variable is never re-assigned
- if unsafe, a := reassigned(n.Left); unsafe {
- if Debug['m'] > 1 {
- if a != nil {
- fmt.Printf("%v: cannot inline re-assigned closure variable at %v: %v\n", n.Line(), a.Line(), a)
- if logopt.Enabled() {
- logopt.LogOpt(n.Pos, "cannotInlineCall", "inline", Curfn.funcname(),
- fmt.Sprintf("%v cannot be inlined (re-assigned closure variable)", a))
- }
- } else {
- fmt.Printf("%v: cannot inline global closure variable %v\n", n.Line(), n.Left)
- if logopt.Enabled() {
- logopt.LogOpt(n.Pos, "cannotInlineCall", "inline", Curfn.funcname(),
- fmt.Sprintf("%v cannot be inlined (global closure variable)", n.Left))
- }
- }
- }
- break
- }
- n = mkinlcall(n, f, maxCost, inlMap)
- }
- }
+ if isIntrinsicCall(n) {
+ break
+ }
+ if fn := inlCallee(n.Left); fn != nil && fn.Func.Inl != nil {
+ n = mkinlcall(n, fn, maxCost, inlMap)
}
case OCALLMETH:
- if Debug['m'] > 3 {
+ if Debug.m > 3 {
fmt.Printf("%v:call to meth %L\n", n.Line(), n.Left.Right)
}
@@ -739,16 +714,85 @@ func inlnode(n *Node, maxCost int32, inlMap map[*Node]bool) *Node {
return n
}
-// inlinableClosure takes an OCLOSURE node and follows linkage to the matching ONAME with
-// the inlinable body. Returns nil if the function is not inlinable.
-func inlinableClosure(n *Node) *Node {
- c := n.Func.Closure
- caninl(c)
- f := c.Func.Nname
- if f == nil || f.Func.Inl == nil {
+// inlCallee takes a function-typed expression and returns the underlying function ONAME
+// that it refers to if statically known. Otherwise, it returns nil.
+func inlCallee(fn *Node) *Node {
+ fn = staticValue(fn)
+ switch {
+ case fn.Op == ONAME && fn.Class() == PFUNC:
+ if fn.isMethodExpression() {
+ n := asNode(fn.Type.Nname())
+ // Check that receiver type matches fn.Left.
+ // TODO(mdempsky): Handle implicit dereference
+ // of pointer receiver argument?
+ if n == nil || !types.Identical(n.Type.Recv().Type, fn.Left.Type) {
+ return nil
+ }
+ return n
+ }
+ return fn
+ case fn.Op == OCLOSURE:
+ c := fn.Func.Closure
+ caninl(c)
+ return c.Func.Nname
+ }
+ return nil
+}
+
+func staticValue(n *Node) *Node {
+ for {
+ if n.Op == OCONVNOP {
+ n = n.Left
+ continue
+ }
+
+ n1 := staticValue1(n)
+ if n1 == nil {
+ return n
+ }
+ n = n1
+ }
+}
+
+// staticValue1 implements a simple SSA-like optimization. If n is a local variable
+// that is initialized and never reassigned, staticValue1 returns the initializer
+// expression. Otherwise, it returns nil.
+func staticValue1(n *Node) *Node {
+ if n.Op != ONAME || n.Class() != PAUTO || n.Name.Addrtaken() {
+ return nil
+ }
+
+ defn := n.Name.Defn
+ if defn == nil {
+ return nil
+ }
+
+ var rhs *Node
+FindRHS:
+ switch defn.Op {
+ case OAS:
+ rhs = defn.Right
+ case OAS2:
+ for i, lhs := range defn.List.Slice() {
+ if lhs == n {
+ rhs = defn.Rlist.Index(i)
+ break FindRHS
+ }
+ }
+ Fatalf("%v missing from LHS of %v", n, defn)
+ default:
return nil
}
- return f
+ if rhs == nil {
+ Fatalf("RHS is nil: %v", defn)
+ }
+
+ unsafe, _ := reassigned(n)
+ if unsafe {
+ return nil
+ }
+
+ return rhs
}
// reassigned takes an ONAME node, walks the function in which it is defined, and returns a boolean
@@ -792,14 +836,12 @@ func (v *reassignVisitor) visit(n *Node) *Node {
if n.Left == v.name && n != v.name.Name.Defn {
return n
}
- return nil
case OAS2, OAS2FUNC, OAS2MAPR, OAS2DOTTYPE:
for _, p := range n.List.Slice() {
if p == v.name && n != v.name.Name.Defn {
return n
}
}
- return nil
}
if a := v.visit(n.Left); a != nil {
return a
@@ -831,24 +873,27 @@ func (v *reassignVisitor) visitList(l Nodes) *Node {
return nil
}
-func tinlvar(t *types.Field, inlvars map[*Node]*Node) *Node {
- if n := asNode(t.Nname); n != nil && !n.isBlank() {
- inlvar := inlvars[n]
- if inlvar == nil {
- Fatalf("missing inlvar for %v\n", n)
- }
- return inlvar
+func inlParam(t *types.Field, as *Node, inlvars map[*Node]*Node) *Node {
+ n := asNode(t.Nname)
+ if n == nil || n.isBlank() {
+ return nblank
}
- return typecheck(nblank, ctxExpr|ctxAssign)
+ inlvar := inlvars[n]
+ if inlvar == nil {
+ Fatalf("missing inlvar for %v", n)
+ }
+ as.Ninit.Append(nod(ODCL, inlvar, nil))
+ inlvar.Name.Defn = as
+ return inlvar
}
var inlgen int
-// If n is a call, and fn is a function with an inlinable body,
-// return an OINLCALL.
-// On return ninit has the parameter assignments, the nbody is the
-// inlined function body and list, rlist contain the input, output
+// If n is a call node (OCALLFUNC or OCALLMETH), and fn is an ONAME node for a
+// function with an inlinable body, return an OINLCALL node that can replace n.
+// The returned node's Ninit has the parameter assignments, the Nbody is the
+// inlined function body, and (List, Rlist) contain the (input, output)
// parameters.
// The result of mkinlcall MUST be assigned back to n, e.g.
// n.Left = mkinlcall(n.Left, fn, isddd)
@@ -889,7 +934,7 @@ func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node {
}
if inlMap[fn] {
- if Debug['m'] > 1 {
+ if Debug.m > 1 {
fmt.Printf("%v: cannot inline %v into %v: repeated recursive cycle\n", n.Line(), fn, Curfn.funcname())
}
return n
@@ -903,12 +948,12 @@ func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node {
}
// We have a function node, and it has an inlineable body.
- if Debug['m'] > 1 {
+ if Debug.m > 1 {
fmt.Printf("%v: inlining call to %v %#v { %#v }\n", n.Line(), fn.Sym, fn.Type, asNodes(fn.Func.Inl.Body))
- } else if Debug['m'] != 0 {
+ } else if Debug.m != 0 {
fmt.Printf("%v: inlining call to %v\n", n.Line(), fn)
}
- if Debug['m'] > 2 {
+ if Debug.m > 2 {
fmt.Printf("%v: Before inlining: %+v\n", n.Line(), n)
}
@@ -918,6 +963,21 @@ func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node {
ninit := n.Ninit
+ // For normal function calls, the function callee expression
+ // may contain side effects (e.g., added by addinit during
+ // inlconv2expr or inlconv2list). Make sure to preserve these,
+ // if necessary (#42703).
+ if n.Op == OCALLFUNC {
+ callee := n.Left
+ for callee.Op == OCONVNOP {
+ ninit.AppendNodes(&callee.Ninit)
+ callee = callee.Left
+ }
+ if callee.Op != ONAME && callee.Op != OCLOSURE {
+ Fatalf("unexpected callee expression: %v", callee)
+ }
+ }
+
// Make temp names to use instead of the originals.
inlvars := make(map[*Node]*Node)
@@ -970,14 +1030,15 @@ func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node {
continue
}
if ln.isParamStackCopy() { // ignore the on-stack copy of a parameter that moved to the heap
- continue
- }
- inlvars[ln] = typecheck(inlvar(ln), ctxExpr)
- if ln.Class() == PPARAM || ln.Name.Param.Stackcopy != nil && ln.Name.Param.Stackcopy.Class() == PPARAM {
- ninit.Append(nod(ODCL, inlvars[ln], nil))
+ // TODO(mdempsky): Remove once I'm confident
+ // this never actually happens. We currently
+ // perform inlining before escape analysis, so
+ // nothing should have moved to the heap yet.
+ Fatalf("impossible: %v", ln)
}
+ inlf := typecheck(inlvar(ln), ctxExpr)
+ inlvars[ln] = inlf
if genDwarfInline > 0 {
- inlf := inlvars[ln]
if ln.Class() == PPARAM {
inlf.Name.SetInlFormal(true)
} else {
@@ -988,15 +1049,28 @@ func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node {
}
}
+ nreturns := 0
+ inspectList(asNodes(fn.Func.Inl.Body), func(n *Node) bool {
+ if n != nil && n.Op == ORETURN {
+ nreturns++
+ }
+ return true
+ })
+
+ // We can delay declaring+initializing result parameters if:
+ // (1) there's only one "return" statement in the inlined
+ // function, and (2) the result parameters aren't named.
+ delayretvars := nreturns == 1
+
// temporaries for return values.
var retvars []*Node
for i, t := range fn.Type.Results().Fields().Slice() {
var m *Node
- mpos := t.Pos
- if n := asNode(t.Nname); n != nil && !n.isBlank() {
+ if n := asNode(t.Nname); n != nil && !n.isBlank() && !strings.HasPrefix(n.Sym.Name, "~r") {
m = inlvar(n)
m = typecheck(m, ctxExpr)
inlvars[n] = m
+ delayretvars = false // found a named result parameter
} else {
// anonymous return values, synthesize names for use in assignment that replaces return
m = retvar(t, i)
@@ -1008,67 +1082,52 @@ func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node {
// were not part of the original callee.
if !strings.HasPrefix(m.Sym.Name, "~R") {
m.Name.SetInlFormal(true)
- m.Pos = mpos
+ m.Pos = t.Pos
inlfvars = append(inlfvars, m)
}
}
- ninit.Append(nod(ODCL, m, nil))
retvars = append(retvars, m)
}
// Assign arguments to the parameters' temp names.
as := nod(OAS2, nil, nil)
- as.Rlist.Set(n.List.Slice())
+ as.SetColas(true)
+ if n.Op == OCALLMETH {
+ if n.Left.Left == nil {
+ Fatalf("method call without receiver: %+v", n)
+ }
+ as.Rlist.Append(n.Left.Left)
+ }
+ as.Rlist.Append(n.List.Slice()...)
// For non-dotted calls to variadic functions, we assign the
// variadic parameter's temp name separately.
var vas *Node
- if fn.IsMethod() {
- rcv := fn.Type.Recv()
-
- if n.Left.Op == ODOTMETH {
- // For x.M(...), assign x directly to the
- // receiver parameter.
- if n.Left.Left == nil {
- Fatalf("method call without receiver: %+v", n)
- }
- ras := nod(OAS, tinlvar(rcv, inlvars), n.Left.Left)
- ras = typecheck(ras, ctxStmt)
- ninit.Append(ras)
- } else {
- // For T.M(...), add the receiver parameter to
- // as.List, so it's assigned by the normal
- // arguments.
- if as.Rlist.Len() == 0 {
- Fatalf("non-method call to method without first arg: %+v", n)
- }
- as.List.Append(tinlvar(rcv, inlvars))
- }
+ if recv := fn.Type.Recv(); recv != nil {
+ as.List.Append(inlParam(recv, as, inlvars))
}
-
for _, param := range fn.Type.Params().Fields().Slice() {
// For ordinary parameters or variadic parameters in
// dotted calls, just add the variable to the
// assignment list, and we're done.
if !param.IsDDD() || n.IsDDD() {
- as.List.Append(tinlvar(param, inlvars))
+ as.List.Append(inlParam(param, as, inlvars))
continue
}
// Otherwise, we need to collect the remaining values
// to pass as a slice.
- numvals := n.List.Len()
-
x := as.List.Len()
- for as.List.Len() < numvals {
+ for as.List.Len() < as.Rlist.Len() {
as.List.Append(argvar(param.Type, as.List.Len()))
}
varargs := as.List.Slice()[x:]
- vas = nod(OAS, tinlvar(param, inlvars), nil)
+ vas = nod(OAS, nil, nil)
+ vas.Left = inlParam(param, vas, inlvars)
if len(varargs) == 0 {
vas.Right = nodnil()
vas.Right.Type = param.Type
@@ -1088,11 +1147,14 @@ func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node {
ninit.Append(vas)
}
- // Zero the return parameters.
- for _, n := range retvars {
- ras := nod(OAS, n, nil)
- ras = typecheck(ras, ctxStmt)
- ninit.Append(ras)
+ if !delayretvars {
+ // Zero the return parameters.
+ for _, n := range retvars {
+ ninit.Append(nod(ODCL, n, nil))
+ ras := nod(OAS, n, nil)
+ ras = typecheck(ras, ctxStmt)
+ ninit.Append(ras)
+ }
}
retlabel := autolabel(".i")
@@ -1123,11 +1185,12 @@ func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node {
}
subst := inlsubst{
- retlabel: retlabel,
- retvars: retvars,
- inlvars: inlvars,
- bases: make(map[*src.PosBase]*src.PosBase),
- newInlIndex: newIndex,
+ retlabel: retlabel,
+ retvars: retvars,
+ delayretvars: delayretvars,
+ inlvars: inlvars,
+ bases: make(map[*src.PosBase]*src.PosBase),
+ newInlIndex: newIndex,
}
body := subst.list(asNodes(fn.Func.Inl.Body))
@@ -1165,7 +1228,7 @@ func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node {
}
}
- if Debug['m'] > 2 {
+ if Debug.m > 2 {
fmt.Printf("%v: After inlining %+v\n\n", call.Line(), call)
}
@@ -1176,7 +1239,7 @@ func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node {
// PAUTO's in the calling functions, and link them off of the
// PPARAM's, PAUTOS and PPARAMOUTs of the called function.
func inlvar(var_ *Node) *Node {
- if Debug['m'] > 3 {
+ if Debug.m > 3 {
fmt.Printf("inlvar %+v\n", var_)
}
@@ -1223,6 +1286,10 @@ type inlsubst struct {
// Temporary result variables.
retvars []*Node
+ // Whether result variables should be initialized at the
+ // "return" statement.
+ delayretvars bool
+
inlvars map[*Node]*Node
// bases maps from original PosBase to PosBase with an extra
@@ -1255,13 +1322,13 @@ func (subst *inlsubst) node(n *Node) *Node {
switch n.Op {
case ONAME:
if inlvar := subst.inlvars[n]; inlvar != nil { // These will be set during inlnode
- if Debug['m'] > 2 {
+ if Debug.m > 2 {
fmt.Printf("substituting name %+v -> %+v\n", n, inlvar)
}
return inlvar
}
- if Debug['m'] > 2 {
+ if Debug.m > 2 {
fmt.Printf("not substituting name %+v\n", n)
}
return n
@@ -1291,6 +1358,14 @@ func (subst *inlsubst) node(n *Node) *Node {
as.List.Append(n)
}
as.Rlist.Set(subst.list(n.List))
+
+ if subst.delayretvars {
+ for _, n := range as.List.Slice() {
+ as.Ninit.Append(nod(ODCL, n, nil))
+ n.Name.Defn = as
+ }
+ }
+
as = typecheck(as, ctxStmt)
m.Ninit.Append(as)
}
@@ -1353,3 +1428,68 @@ func pruneUnusedAutos(ll []*Node, vis *hairyVisitor) []*Node {
}
return s
}
+
+// devirtualize replaces interface method calls within fn with direct
+// concrete-type method calls where applicable.
+func devirtualize(fn *Node) {
+ Curfn = fn
+ inspectList(fn.Nbody, func(n *Node) bool {
+ if n.Op == OCALLINTER {
+ devirtualizeCall(n)
+ }
+ return true
+ })
+}
+
+func devirtualizeCall(call *Node) {
+ recv := staticValue(call.Left.Left)
+ if recv.Op != OCONVIFACE {
+ return
+ }
+
+ typ := recv.Left.Type
+ if typ.IsInterface() {
+ return
+ }
+
+ x := nodl(call.Left.Pos, ODOTTYPE, call.Left.Left, nil)
+ x.Type = typ
+ x = nodlSym(call.Left.Pos, OXDOT, x, call.Left.Sym)
+ x = typecheck(x, ctxExpr|ctxCallee)
+ switch x.Op {
+ case ODOTMETH:
+ if Debug.m != 0 {
+ Warnl(call.Pos, "devirtualizing %v to %v", call.Left, typ)
+ }
+ call.Op = OCALLMETH
+ call.Left = x
+ case ODOTINTER:
+ // Promoted method from embedded interface-typed field (#42279).
+ if Debug.m != 0 {
+ Warnl(call.Pos, "partially devirtualizing %v to %v", call.Left, typ)
+ }
+ call.Op = OCALLINTER
+ call.Left = x
+ default:
+ // TODO(mdempsky): Turn back into Fatalf after more testing.
+ if Debug.m != 0 {
+ Warnl(call.Pos, "failed to devirtualize %v (%v)", x, x.Op)
+ }
+ return
+ }
+
+ // Duplicated logic from typecheck for function call return
+ // value types.
+ //
+ // Receiver parameter size may have changed; need to update
+ // call.Type to get correct stack offsets for result
+ // parameters.
+ checkwidth(x.Type)
+ switch ft := x.Type; ft.NumResults() {
+ case 0:
+ case 1:
+ call.Type = ft.Results().Field(0).Type
+ default:
+ call.Type = ft.Results()
+ }
+}
diff --git a/src/cmd/compile/internal/gc/inl_test.go b/src/cmd/compile/internal/gc/inl_test.go
index 9d3b8c59fd..02735e50fb 100644
--- a/src/cmd/compile/internal/gc/inl_test.go
+++ b/src/cmd/compile/internal/gc/inl_test.go
@@ -51,6 +51,7 @@ func TestIntendedInlining(t *testing.T) {
"funcPC",
"getArgInfoFast",
"getm",
+ "getMCache",
"isDirectIface",
"itabHashFunc",
"noescape",
@@ -83,7 +84,7 @@ func TestIntendedInlining(t *testing.T) {
"puintptr.ptr",
"spanOf",
"spanOfUnchecked",
- //"(*gcWork).putFast", // TODO(austin): For debugging #27993
+ "(*gcWork).putFast",
"(*gcWork).tryGetFast",
"(*guintptr).set",
"(*markBits).advance",
@@ -115,6 +116,7 @@ func TestIntendedInlining(t *testing.T) {
"byLiteral.Len",
"byLiteral.Less",
"byLiteral.Swap",
+ "(*dictDecoder).tryWriteCopy",
},
"encoding/base64": {
"assemble32",
diff --git a/src/cmd/compile/internal/gc/lex.go b/src/cmd/compile/internal/gc/lex.go
index 1a344c6566..7cce371408 100644
--- a/src/cmd/compile/internal/gc/lex.go
+++ b/src/cmd/compile/internal/gc/lex.go
@@ -48,8 +48,11 @@ const (
Nowritebarrierrec // error on write barrier in this or recursive callees
Yeswritebarrierrec // cancels Nowritebarrierrec in this function and callees
- // Runtime-only type pragmas
+ // Runtime and cgo type pragmas
NotInHeap // values of this type must not be heap allocated
+
+ // Go command pragmas
+ GoBuildPragma
)
const (
@@ -71,6 +74,8 @@ const (
func pragmaFlag(verb string) PragmaFlag {
switch verb {
+ case "go:build":
+ return GoBuildPragma
case "go:nointerface":
if objabi.Fieldtrack_enabled != 0 {
return Nointerface
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index eedfc4bb25..a6963a3d66 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -34,8 +34,6 @@ import (
"strings"
)
-var imported_unsafe bool
-
var (
buildid string
spectre string
@@ -48,6 +46,7 @@ var (
Debug_closure int
Debug_compilelater int
debug_dclstack int
+ Debug_dumpptrs int
Debug_libfuzzer int
Debug_panic int
Debug_slice int
@@ -77,6 +76,7 @@ var debugtab = []struct {
{"compilelater", "compile functions as late as possible", &Debug_compilelater},
{"disablenil", "disable nil checks", &disable_checknil},
{"dclstack", "run internal dclstack check", &debug_dclstack},
+ {"dumpptrs", "show Node pointer values in Dump/dumplist output", &Debug_dumpptrs},
{"gcprog", "print dump of GC programs", &Debug_gcprog},
{"libfuzzer", "coverage instrumentation for libfuzzer", &Debug_libfuzzer},
{"nil", "print information about nil checks", &Debug_checknil},
@@ -91,6 +91,7 @@ var debugtab = []struct {
{"dwarfinl", "print information about DWARF inlined function creation", &Debug_gendwarfinl},
{"softfloat", "force compiler to emit soft-float code", &Debug_softfloat},
{"defer", "print information about defer compilation", &Debug_defer},
+ {"fieldtrack", "enable fieldtracking", &objabi.Fieldtrack_enabled},
}
const debugHelpHeader = `usage: -d arg[,arg]* and arg is <key>[=<value>]
@@ -132,7 +133,7 @@ func hidePanic() {
// supportsDynlink reports whether or not the code generator for the given
// architecture supports the -shared and -dynlink flags.
func supportsDynlink(arch *sys.Arch) bool {
- return arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.S390X)
+ return arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.RISCV64, sys.S390X)
}
// timing data for compiler phases
@@ -211,18 +212,27 @@ func Main(archInit func(*Arch)) {
flag.BoolVar(&compiling_runtime, "+", false, "compiling runtime")
flag.BoolVar(&compiling_std, "std", false, "compiling standard library")
- objabi.Flagcount("%", "debug non-static initializers", &Debug['%'])
- objabi.Flagcount("B", "disable bounds checking", &Debug['B'])
- objabi.Flagcount("C", "disable printing of columns in error messages", &Debug['C']) // TODO(gri) remove eventually
flag.StringVar(&localimport, "D", "", "set relative `path` for local imports")
- objabi.Flagcount("E", "debug symbol export", &Debug['E'])
+
+ objabi.Flagcount("%", "debug non-static initializers", &Debug.P)
+ objabi.Flagcount("B", "disable bounds checking", &Debug.B)
+ objabi.Flagcount("C", "disable printing of columns in error messages", &Debug.C)
+ objabi.Flagcount("E", "debug symbol export", &Debug.E)
+ objabi.Flagcount("K", "debug missing line numbers", &Debug.K)
+ objabi.Flagcount("L", "show full file names in error messages", &Debug.L)
+ objabi.Flagcount("N", "disable optimizations", &Debug.N)
+ objabi.Flagcount("S", "print assembly listing", &Debug.S)
+ objabi.Flagcount("W", "debug parse tree after type checking", &Debug.W)
+ objabi.Flagcount("e", "no limit on number of errors reported", &Debug.e)
+ objabi.Flagcount("h", "halt on error", &Debug.h)
+ objabi.Flagcount("j", "debug runtime-initialized variables", &Debug.j)
+ objabi.Flagcount("l", "disable inlining", &Debug.l)
+ objabi.Flagcount("m", "print optimization decisions", &Debug.m)
+ objabi.Flagcount("r", "debug generated wrappers", &Debug.r)
+ objabi.Flagcount("w", "debug type checking", &Debug.w)
+
objabi.Flagfn1("I", "add `directory` to import search path", addidir)
- objabi.Flagcount("K", "debug missing line numbers", &Debug['K'])
- objabi.Flagcount("L", "show full file names in error messages", &Debug['L'])
- objabi.Flagcount("N", "disable optimizations", &Debug['N'])
- objabi.Flagcount("S", "print assembly listing", &Debug['S'])
objabi.AddVersionFlag() // -V
- objabi.Flagcount("W", "debug parse tree after type checking", &Debug['W'])
flag.StringVar(&asmhdr, "asmhdr", "", "write assembly header to `file`")
flag.StringVar(&buildid, "buildid", "", "record `id` as the build id in the export metadata")
flag.IntVar(&nBackendWorkers, "c", 1, "concurrency during compilation, 1 means no concurrency")
@@ -231,17 +241,13 @@ func Main(archInit func(*Arch)) {
flag.BoolVar(&flagDWARF, "dwarf", !Wasm, "generate DWARF symbols")
flag.BoolVar(&Ctxt.Flag_locationlists, "dwarflocationlists", true, "add location lists to DWARF in optimized mode")
flag.IntVar(&genDwarfInline, "gendwarfinl", 2, "generate DWARF inline info records")
- objabi.Flagcount("e", "no limit on number of errors reported", &Debug['e'])
- objabi.Flagcount("h", "halt on error", &Debug['h'])
+ objabi.Flagfn1("embedcfg", "read go:embed configuration from `file`", readEmbedCfg)
objabi.Flagfn1("importmap", "add `definition` of the form source=actual to import map", addImportMap)
objabi.Flagfn1("importcfg", "read import configuration from `file`", readImportCfg)
flag.StringVar(&flag_installsuffix, "installsuffix", "", "set pkg directory `suffix`")
- objabi.Flagcount("j", "debug runtime-initialized variables", &Debug['j'])
- objabi.Flagcount("l", "disable inlining", &Debug['l'])
flag.StringVar(&flag_lang, "lang", "", "release to compile for")
flag.StringVar(&linkobj, "linkobj", "", "write linker-specific object to `file`")
objabi.Flagcount("live", "debug liveness analysis", &debuglive)
- objabi.Flagcount("m", "print optimization decisions", &Debug['m'])
if sys.MSanSupported(objabi.GOOS, objabi.GOARCH) {
flag.BoolVar(&flag_msan, "msan", false, "build code compatible with C/C++ memory sanitizer")
}
@@ -249,7 +255,6 @@ func Main(archInit func(*Arch)) {
flag.StringVar(&outfile, "o", "", "write output to `file`")
flag.StringVar(&myimportpath, "p", "", "set expected package import `path`")
flag.BoolVar(&writearchive, "pack", false, "write to file.a instead of file.o")
- objabi.Flagcount("r", "debug generated wrappers", &Debug['r'])
if sys.RaceDetectorSupported(objabi.GOOS, objabi.GOARCH) {
flag.BoolVar(&flag_race, "race", false, "enable race detector")
}
@@ -259,7 +264,6 @@ func Main(archInit func(*Arch)) {
}
flag.StringVar(&pathPrefix, "trimpath", "", "remove `prefix` from recorded source file paths")
flag.BoolVar(&Debug_vlog, "v", false, "increase debug verbosity")
- objabi.Flagcount("w", "debug type checking", &Debug['w'])
flag.BoolVar(&use_writebarrier, "wb", true, "enable write barrier")
var flag_shared bool
var flag_dynlink bool
@@ -325,9 +329,9 @@ func Main(archInit func(*Arch)) {
Ctxt.Flag_shared = flag_dynlink || flag_shared
Ctxt.Flag_dynlink = flag_dynlink
- Ctxt.Flag_optimize = Debug['N'] == 0
+ Ctxt.Flag_optimize = Debug.N == 0
- Ctxt.Debugasm = Debug['S']
+ Ctxt.Debugasm = Debug.S
Ctxt.Debugvlog = Debug_vlog
if flagDWARF {
Ctxt.DebugInfo = debuginfo
@@ -399,7 +403,7 @@ func Main(archInit func(*Arch)) {
instrumenting = true
}
- if compiling_runtime && Debug['N'] != 0 {
+ if compiling_runtime && Debug.N != 0 {
log.Fatal("cannot disable optimizations while compiling runtime")
}
if nBackendWorkers < 1 {
@@ -504,11 +508,11 @@ func Main(archInit func(*Arch)) {
}
// enable inlining. for now:
- // default: inlining on. (debug['l'] == 1)
- // -l: inlining off (debug['l'] == 0)
- // -l=2, -l=3: inlining on again, with extra debugging (debug['l'] > 1)
- if Debug['l'] <= 1 {
- Debug['l'] = 1 - Debug['l']
+ // default: inlining on. (Debug.l == 1)
+ // -l: inlining off (Debug.l == 0)
+ // -l=2, -l=3: inlining on again, with extra debugging (Debug.l > 1)
+ if Debug.l <= 1 {
+ Debug.l = 1 - Debug.l
}
if jsonLogOpt != "" { // parse version,destination from json logging optimization.
@@ -516,6 +520,7 @@ func Main(archInit func(*Arch)) {
}
ssaDump = os.Getenv("GOSSAFUNC")
+ ssaDir = os.Getenv("GOSSADIR")
if ssaDump != "" {
if strings.HasSuffix(ssaDump, "+") {
ssaDump = ssaDump[:len(ssaDump)-1]
@@ -594,7 +599,7 @@ func Main(archInit func(*Arch)) {
timings.Start("fe", "typecheck", "top1")
for i := 0; i < len(xtop); i++ {
n := xtop[i]
- if op := n.Op; op != ODCL && op != OAS && op != OAS2 && (op != ODCLTYPE || !n.Left.Name.Param.Alias) {
+ if op := n.Op; op != ODCL && op != OAS && op != OAS2 && (op != ODCLTYPE || !n.Left.Name.Param.Alias()) {
xtop[i] = typecheck(n, ctxStmt)
}
}
@@ -606,7 +611,7 @@ func Main(archInit func(*Arch)) {
timings.Start("fe", "typecheck", "top2")
for i := 0; i < len(xtop); i++ {
n := xtop[i]
- if op := n.Op; op == ODCL || op == OAS || op == OAS2 || op == ODCLTYPE && n.Left.Name.Param.Alias {
+ if op := n.Op; op == ODCL || op == OAS || op == OAS2 || op == ODCLTYPE && n.Left.Name.Param.Alias() {
xtop[i] = typecheck(n, ctxStmt)
}
}
@@ -617,7 +622,7 @@ func Main(archInit func(*Arch)) {
var fcount int64
for i := 0; i < len(xtop); i++ {
n := xtop[i]
- if op := n.Op; op == ODCLFUNC || op == OCLOSURE {
+ if n.Op == ODCLFUNC {
Curfn = n
decldepth = 1
saveerrors()
@@ -642,6 +647,8 @@ func Main(archInit func(*Arch)) {
errorexit()
}
+ fninit(xtop)
+
// Phase 4: Decide how to capture closed variables.
// This needs to run before escape analysis,
// because variables captured by value do not escape.
@@ -663,7 +670,7 @@ func Main(archInit func(*Arch)) {
// Phase 5: Inlining
timings.Start("fe", "inlining")
if Debug_typecheckinl != 0 {
- // Typecheck imported function bodies if debug['l'] > 1,
+ // Typecheck imported function bodies if Debug.l > 1,
// otherwise lazily when used or re-exported.
for _, n := range importlist {
if n.Func.Inl != nil {
@@ -677,7 +684,7 @@ func Main(archInit func(*Arch)) {
}
}
- if Debug['l'] != 0 {
+ if Debug.l != 0 {
// Find functions that can be inlined and clone them before walk expands them.
visitBottomUp(xtop, func(list []*Node, recursive bool) {
numfns := numNonClosures(list)
@@ -688,7 +695,7 @@ func Main(archInit func(*Arch)) {
// across more than one function.
caninl(n)
} else {
- if Debug['m'] > 1 {
+ if Debug.m > 1 {
fmt.Printf("%v: cannot inline %v: recursive\n", n.Line(), n.Func.Nname)
}
}
@@ -697,6 +704,13 @@ func Main(archInit func(*Arch)) {
})
}
+ for _, n := range xtop {
+ if n.Op == ODCLFUNC {
+ devirtualize(n)
+ }
+ }
+ Curfn = nil
+
// Phase 6: Escape analysis.
// Required for moving heap allocations onto stack,
// which in turn is required by the closure implementation,
@@ -751,10 +765,6 @@ func Main(archInit func(*Arch)) {
}
timings.AddEvent(fcount, "funcs")
- if nsavederrors+nerrors == 0 {
- fninit(xtop)
- }
-
compileFunctions()
if nowritebarrierrecCheck != nil {
@@ -809,6 +819,9 @@ func Main(archInit func(*Arch)) {
}
}
+ if len(funcStack) != 0 {
+ Fatalf("funcStack is non-empty: %v", len(funcStack))
+ }
if len(compilequeue) != 0 {
Fatalf("%d uncompiled functions", len(compilequeue))
}
@@ -966,9 +979,10 @@ func readSymABIs(file, myimportpath string) {
if len(parts) != 3 {
log.Fatalf(`%s:%d: invalid symabi: syntax is "%s sym abi"`, file, lineNum, parts[0])
}
- sym, abi := parts[1], parts[2]
- if abi != "ABI0" { // Only supported external ABI right now
- log.Fatalf(`%s:%d: invalid symabi: unknown abi "%s"`, file, lineNum, abi)
+ sym, abistr := parts[1], parts[2]
+ abi, valid := obj.ParseABI(abistr)
+ if !valid {
+ log.Fatalf(`%s:%d: invalid symabi: unknown abi "%s"`, file, lineNum, abistr)
}
// If the symbol is already prefixed with
@@ -981,9 +995,9 @@ func readSymABIs(file, myimportpath string) {
// Record for later.
if parts[0] == "def" {
- symabiDefs[sym] = obj.ABI0
+ symabiDefs[sym] = abi
} else {
- symabiRefs[sym] = obj.ABI0
+ symabiRefs[sym] = abi
}
default:
log.Fatalf(`%s:%d: invalid symabi type "%s"`, file, lineNum, parts[0])
@@ -1172,7 +1186,6 @@ func importfile(f *Val) *types.Pkg {
}
if path_ == "unsafe" {
- imported_unsafe = true
return unsafepkg
}
@@ -1405,29 +1418,34 @@ func IsAlias(sym *types.Sym) bool {
return sym.Def != nil && asNode(sym.Def).Sym != sym
}
-// By default, assume any debug flags are incompatible with concurrent compilation.
-// A few are safe and potentially in common use for normal compiles, though; mark them as such here.
-var concurrentFlagOK = [256]bool{
- 'B': true, // disabled bounds checking
- 'C': true, // disable printing of columns in error messages
- 'e': true, // no limit on errors; errors all come from non-concurrent code
- 'I': true, // add `directory` to import search path
- 'N': true, // disable optimizations
- 'l': true, // disable inlining
- 'w': true, // all printing happens before compilation
- 'W': true, // all printing happens before compilation
- 'S': true, // printing disassembly happens at the end (but see concurrentBackendAllowed below)
+// By default, assume any debug flags are incompatible with concurrent
+// compilation. A few are safe and potentially in common use for
+// normal compiles, though; return true for those.
+func concurrentFlagOk() bool {
+ // Report whether any debug flag that would prevent concurrent
+ // compilation is set, by zeroing out the allowed ones and then
+ // checking if the resulting struct is zero.
+ d := Debug
+ d.B = 0 // disable bounds checking
+ d.C = 0 // disable printing of columns in error messages
+ d.e = 0 // no limit on errors; errors all come from non-concurrent code
+ d.N = 0 // disable optimizations
+ d.l = 0 // disable inlining
+ d.w = 0 // all printing happens before compilation
+ d.W = 0 // all printing happens before compilation
+ d.S = 0 // printing disassembly happens at the end (but see concurrentBackendAllowed below)
+
+ return d == DebugFlags{}
}
func concurrentBackendAllowed() bool {
- for i, x := range &Debug {
- if x != 0 && !concurrentFlagOK[i] {
- return false
- }
+ if !concurrentFlagOk() {
+ return false
}
- // Debug['S'] by itself is ok, because all printing occurs
+
+ // Debug.S by itself is ok, because all printing occurs
// while writing the object file, and that is non-concurrent.
- // Adding Debug_vlog, however, causes Debug['S'] to also print
+ // Adding Debug_vlog, however, causes Debug.S to also print
// while flushing the plist, which happens concurrently.
if Debug_vlog || debugstr != "" || debuglive > 0 {
return false
diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go
index 5dce533e4b..67d24ef0bc 100644
--- a/src/cmd/compile/internal/gc/noder.go
+++ b/src/cmd/compile/internal/gc/noder.go
@@ -11,6 +11,7 @@ import (
"runtime"
"strconv"
"strings"
+ "unicode"
"unicode/utf8"
"cmd/compile/internal/syntax"
@@ -90,7 +91,11 @@ func (p *noder) makeSrcPosBase(b0 *syntax.PosBase) *src.PosBase {
} else {
// line directive base
p0 := b0.Pos()
- p1 := src.MakePos(p.makeSrcPosBase(p0.Base()), p0.Line(), p0.Col())
+ p0b := p0.Base()
+ if p0b == b0 {
+ panic("infinite recursion in makeSrcPosBase")
+ }
+ p1 := src.MakePos(p.makeSrcPosBase(p0b), p0.Line(), p0.Col())
b1 = src.NewLinePragmaBase(p1, fn, fileh(fn), b0.Line(), b0.Col())
}
p.basemap[b0] = b1
@@ -130,11 +135,13 @@ type noder struct {
base *src.PosBase
}
- file *syntax.File
- linknames []linkname
- pragcgobuf [][]string
- err chan syntax.Error
- scope ScopeID
+ file *syntax.File
+ linknames []linkname
+ pragcgobuf [][]string
+ err chan syntax.Error
+ scope ScopeID
+ importedUnsafe bool
+ importedEmbed bool
// scopeVars is a stack tracking the number of variables declared in the
// current function at the moment each open scope was opened.
@@ -236,19 +243,21 @@ type linkname struct {
func (p *noder) node() {
types.Block = 1
- imported_unsafe = false
+ p.importedUnsafe = false
+ p.importedEmbed = false
p.setlineno(p.file.PkgName)
mkpackage(p.file.PkgName.Value)
if pragma, ok := p.file.Pragma.(*Pragma); ok {
+ pragma.Flag &^= GoBuildPragma
p.checkUnused(pragma)
}
xtop = append(xtop, p.decls(p.file.DeclList)...)
for _, n := range p.linknames {
- if !imported_unsafe {
+ if !p.importedUnsafe {
p.yyerrorpos(n.pos, "//go:linkname only allowed in Go files that import \"unsafe\"")
continue
}
@@ -323,7 +332,6 @@ func (p *noder) importDecl(imp *syntax.ImportDecl) {
val := p.basicLit(imp.Path)
ipkg := importfile(&val)
-
if ipkg == nil {
if nerrors == 0 {
Fatalf("phase error in import")
@@ -331,6 +339,13 @@ func (p *noder) importDecl(imp *syntax.ImportDecl) {
return
}
+ if ipkg == unsafepkg {
+ p.importedUnsafe = true
+ }
+ if ipkg.Path == "embed" {
+ p.importedEmbed = true
+ }
+
ipkg.Direct = true
var my *types.Sym
@@ -372,6 +387,20 @@ func (p *noder) varDecl(decl *syntax.VarDecl) []*Node {
}
if pragma, ok := decl.Pragma.(*Pragma); ok {
+ if len(pragma.Embeds) > 0 {
+ if !p.importedEmbed {
+ // This check can't be done when building the list pragma.Embeds
+ // because that list is created before the noder starts walking over the file,
+ // so at that point it hasn't seen the imports.
+ // We're left to check now, just before applying the //go:embed lines.
+ for _, e := range pragma.Embeds {
+ p.yyerrorpos(e.Pos, "//go:embed only allowed in Go files that import \"embed\"")
+ }
+ } else {
+ exprs = varEmbed(p, names, typ, exprs, pragma.Embeds)
+ }
+ pragma.Embeds = nil
+ }
p.checkUnused(pragma)
}
@@ -454,17 +483,17 @@ func (p *noder) typeDecl(decl *syntax.TypeDecl) *Node {
param := n.Name.Param
param.Ntype = typ
- param.Alias = decl.Alias
+ param.SetAlias(decl.Alias)
if pragma, ok := decl.Pragma.(*Pragma); ok {
if !decl.Alias {
- param.Pragma = pragma.Flag & TypePragmas
+ param.SetPragma(pragma.Flag & TypePragmas)
pragma.Flag &^= TypePragmas
}
p.checkUnused(pragma)
}
nod := p.nod(decl, ODCLTYPE, n, nil)
- if param.Alias && !langSupported(1, 9, localpkg) {
+ if param.Alias() && !langSupported(1, 9, localpkg) {
yyerrorl(nod.Pos, "type aliases only supported as of -lang=go1.9")
}
return nod
@@ -773,7 +802,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, strlit(nstr))
+ chunks = append(chunks, nstr.StringVal())
}
for i := len(adds) - 1; i >= 0; i-- {
@@ -783,12 +812,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, strlit(r))
+ chunks = append(chunks, r.StringVal())
continue
}
nstr = r
- chunks = append(chunks, strlit(nstr))
+ chunks = append(chunks, nstr.StringVal())
} else {
if len(chunks) > 1 {
nstr.SetVal(Val{U: strings.Join(chunks, "")})
@@ -1437,11 +1466,6 @@ func (p *noder) mkname(name *syntax.Name) *Node {
return mkname(p.name(name))
}
-func (p *noder) newname(name *syntax.Name) *Node {
- // TODO(mdempsky): Set line number?
- return newname(p.name(name))
-}
-
func (p *noder) wrapname(n syntax.Node, x *Node) *Node {
// These nodes do not carry line numbers.
// Introduce a wrapper node to give them the correct line.
@@ -1497,13 +1521,15 @@ var allowedStdPragmas = map[string]bool{
"go:cgo_import_dynamic": true,
"go:cgo_ldflag": true,
"go:cgo_dynamic_linker": true,
+ "go:embed": true,
"go:generate": true,
}
// *Pragma is the value stored in a syntax.Pragma during parsing.
type Pragma struct {
- Flag PragmaFlag // collected bits
- Pos []PragmaPos // position of each individual flag
+ Flag PragmaFlag // collected bits
+ Pos []PragmaPos // position of each individual flag
+ Embeds []PragmaEmbed
}
type PragmaPos struct {
@@ -1511,12 +1537,22 @@ type PragmaPos struct {
Pos syntax.Pos
}
+type PragmaEmbed struct {
+ Pos syntax.Pos
+ Patterns []string
+}
+
func (p *noder) checkUnused(pragma *Pragma) {
for _, pos := range pragma.Pos {
if pos.Flag&pragma.Flag != 0 {
p.yyerrorpos(pos.Pos, "misplaced compiler directive")
}
}
+ if len(pragma.Embeds) > 0 {
+ for _, e := range pragma.Embeds {
+ p.yyerrorpos(e.Pos, "misplaced go:embed directive")
+ }
+ }
}
func (p *noder) checkUnusedDuringParse(pragma *Pragma) {
@@ -1525,6 +1561,11 @@ func (p *noder) checkUnusedDuringParse(pragma *Pragma) {
p.error(syntax.Error{Pos: pos.Pos, Msg: "misplaced compiler directive"})
}
}
+ if len(pragma.Embeds) > 0 {
+ for _, e := range pragma.Embeds {
+ p.error(syntax.Error{Pos: e.Pos, Msg: "misplaced go:embed directive"})
+ }
+ }
}
// pragma is called concurrently if files are parsed concurrently.
@@ -1569,6 +1610,17 @@ func (p *noder) pragma(pos syntax.Pos, blankLine bool, text string, old syntax.P
}
p.linknames = append(p.linknames, linkname{pos, f[1], target})
+ case text == "go:embed", strings.HasPrefix(text, "go:embed "):
+ args, err := parseGoEmbed(text[len("go:embed"):])
+ if err != nil {
+ p.error(syntax.Error{Pos: pos, Msg: err.Error()})
+ }
+ if len(args) == 0 {
+ p.error(syntax.Error{Pos: pos, Msg: "usage: //go:embed pattern..."})
+ break
+ }
+ pragma.Embeds = append(pragma.Embeds, PragmaEmbed{pos, args})
+
case strings.HasPrefix(text, "go:cgo_import_dynamic "):
// This is permitted for general use because Solaris
// code relies on it in golang.org/x/sys/unix and others.
@@ -1641,3 +1693,64 @@ func mkname(sym *types.Sym) *Node {
}
return n
}
+
+// parseGoEmbed parses the text following "//go:embed" to extract the glob patterns.
+// It accepts unquoted space-separated patterns as well as double-quoted and back-quoted Go strings.
+// go/build/read.go also processes these strings and contains similar logic.
+func parseGoEmbed(args string) ([]string, error) {
+ var list []string
+ for args = strings.TrimSpace(args); args != ""; args = strings.TrimSpace(args) {
+ var path string
+ Switch:
+ switch args[0] {
+ default:
+ i := len(args)
+ for j, c := range args {
+ if unicode.IsSpace(c) {
+ i = j
+ break
+ }
+ }
+ path = args[:i]
+ args = args[i:]
+
+ case '`':
+ i := strings.Index(args[1:], "`")
+ if i < 0 {
+ return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args)
+ }
+ path = args[1 : 1+i]
+ args = args[1+i+1:]
+
+ case '"':
+ i := 1
+ for ; i < len(args); i++ {
+ if args[i] == '\\' {
+ i++
+ continue
+ }
+ if args[i] == '"' {
+ q, err := strconv.Unquote(args[:i+1])
+ if err != nil {
+ return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args[:i+1])
+ }
+ path = q
+ args = args[i+1:]
+ break Switch
+ }
+ }
+ if i >= len(args) {
+ return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args)
+ }
+ }
+
+ if args != "" {
+ r, _ := utf8.DecodeRuneInString(args)
+ if !unicode.IsSpace(r) {
+ return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args)
+ }
+ }
+ list = append(list, path)
+ }
+ return list, nil
+}
diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go
index af5037c5a8..32aa7c5bb1 100644
--- a/src/cmd/compile/internal/gc/obj.go
+++ b/src/cmd/compile/internal/gc/obj.go
@@ -14,6 +14,8 @@ import (
"encoding/json"
"fmt"
"io"
+ "io/ioutil"
+ "os"
"sort"
"strconv"
)
@@ -113,14 +115,19 @@ func dumpCompilerObj(bout *bio.Writer) {
func dumpdata() {
externs := len(externdcl)
+ xtops := len(xtop)
dumpglobls()
addptabs()
+ exportlistLen := len(exportlist)
addsignats(externdcl)
dumpsignats()
dumptabs()
+ ptabsLen := len(ptabs)
+ itabsLen := len(itabs)
dumpimportstrings()
dumpbasictypes()
+ dumpembeds()
// Calls to dumpsignats can generate functions,
// like method wrappers and hash and equality routines.
@@ -129,9 +136,19 @@ func dumpdata() {
// number of types in a finite amount of code.
// In the typical case, we loop 0 or 1 times.
// It was not until issue 24761 that we found any code that required a loop at all.
- for len(compilequeue) > 0 {
+ for {
+ for i := xtops; i < len(xtop); i++ {
+ n := xtop[i]
+ if n.Op == ODCLFUNC {
+ funccompile(n)
+ }
+ }
+ xtops = len(xtop)
compileFunctions()
dumpsignats()
+ if xtops == len(xtop) {
+ break
+ }
}
// Dump extra globals.
@@ -149,6 +166,16 @@ func dumpdata() {
}
addGCLocals()
+
+ if exportlistLen != len(exportlist) {
+ Fatalf("exportlist changed after compile functions loop")
+ }
+ if ptabsLen != len(ptabs) {
+ Fatalf("ptabs changed after compile functions loop")
+ }
+ if itabsLen != len(itabs) {
+ Fatalf("itabs changed after compile functions loop")
+ }
}
func dumpLinkerObj(bout *bio.Writer) {
@@ -248,7 +275,7 @@ func dumpGlobalConst(n *Node) {
default:
return
}
- Ctxt.DwarfIntConst(myimportpath, n.Sym.Name, typesymname(t), n.Int64())
+ Ctxt.DwarfIntConst(myimportpath, n.Sym.Name, typesymname(t), n.Int64Val())
}
func dumpglobls() {
@@ -281,20 +308,21 @@ func dumpglobls() {
// global symbols can't be declared during parallel compilation.
func addGCLocals() {
for _, s := range Ctxt.Text {
- if s.Func == nil {
+ fn := s.Func()
+ if fn == nil {
continue
}
- for _, gcsym := range []*obj.LSym{s.Func.GCArgs, s.Func.GCLocals, s.Func.GCRegs} {
+ for _, gcsym := range []*obj.LSym{fn.GCArgs, fn.GCLocals} {
if gcsym != nil && !gcsym.OnList() {
ggloblsym(gcsym, int32(len(gcsym.P)), obj.RODATA|obj.DUPOK)
}
}
- if x := s.Func.StackObjects; x != nil {
+ if x := fn.StackObjects; x != nil {
attr := int16(obj.RODATA)
ggloblsym(x, int32(len(x.P)), attr)
x.Set(obj.AttrStatic, true)
}
- if x := s.Func.OpenCodedDeferInfo; x != nil {
+ if x := fn.OpenCodedDeferInfo; x != nil {
ggloblsym(x, int32(len(x.P)), obj.RODATA|obj.DUPOK)
}
}
@@ -333,28 +361,31 @@ func dbvec(s *obj.LSym, off int, bv bvec) int {
return off
}
+const (
+ stringSymPrefix = "go.string."
+ stringSymPattern = ".gostring.%d.%x"
+)
+
+// stringsym returns a symbol containing the string s.
+// The symbol contains the string data, not a string header.
func stringsym(pos src.XPos, s string) (data *obj.LSym) {
var symname string
if len(s) > 100 {
// Huge strings are hashed to avoid long names in object files.
// Indulge in some paranoia by writing the length of s, too,
// as protection against length extension attacks.
+ // Same pattern is known to fileStringSym below.
h := sha256.New()
io.WriteString(h, s)
- symname = fmt.Sprintf(".gostring.%d.%x", len(s), h.Sum(nil))
+ symname = fmt.Sprintf(stringSymPattern, len(s), h.Sum(nil))
} else {
// Small strings get named directly by their contents.
symname = strconv.Quote(s)
}
- const prefix = "go.string."
- symdataname := prefix + symname
-
- symdata := Ctxt.Lookup(symdataname)
-
+ symdata := Ctxt.Lookup(stringSymPrefix + symname)
if !symdata.OnList() {
- // string data
- off := dsname(symdata, 0, s, pos, "string")
+ off := dstringdata(symdata, 0, s, pos, "string")
ggloblsym(symdata, int32(off), obj.DUPOK|obj.RODATA|obj.LOCAL)
symdata.Set(obj.AttrContentAddressable, true)
}
@@ -362,26 +393,122 @@ func stringsym(pos src.XPos, s string) (data *obj.LSym) {
return symdata
}
-var slicebytes_gen int
+// fileStringSym returns a symbol for the contents and the size of file.
+// If readonly is true, the symbol shares storage with any literal string
+// or other file with the same content and is placed in a read-only section.
+// If readonly is false, the symbol is a read-write copy separate from any other,
+// for use as the backing store of a []byte.
+// The content hash of file is copied into hash. (If hash is nil, nothing is copied.)
+// The returned symbol contains the data itself, not a string header.
+func fileStringSym(pos src.XPos, file string, readonly bool, hash []byte) (*obj.LSym, int64, error) {
+ f, err := os.Open(file)
+ if err != nil {
+ return nil, 0, err
+ }
+ defer f.Close()
+ info, err := f.Stat()
+ if err != nil {
+ return nil, 0, err
+ }
+ if !info.Mode().IsRegular() {
+ return nil, 0, fmt.Errorf("not a regular file")
+ }
+ size := info.Size()
+ if size <= 1*1024 {
+ data, err := ioutil.ReadAll(f)
+ if err != nil {
+ return nil, 0, err
+ }
+ if int64(len(data)) != size {
+ return nil, 0, fmt.Errorf("file changed between reads")
+ }
+ var sym *obj.LSym
+ if readonly {
+ sym = stringsym(pos, string(data))
+ } else {
+ sym = slicedata(pos, string(data)).Sym.Linksym()
+ }
+ if len(hash) > 0 {
+ sum := sha256.Sum256(data)
+ copy(hash, sum[:])
+ }
+ return sym, size, nil
+ }
+ if size > 2e9 {
+ // ggloblsym takes an int32,
+ // and probably the rest of the toolchain
+ // can't handle such big symbols either.
+ // See golang.org/issue/9862.
+ return nil, 0, fmt.Errorf("file too large")
+ }
+
+ // File is too big to read and keep in memory.
+ // Compute hash if needed for read-only content hashing or if the caller wants it.
+ var sum []byte
+ if readonly || len(hash) > 0 {
+ h := sha256.New()
+ n, err := io.Copy(h, f)
+ if err != nil {
+ return nil, 0, err
+ }
+ if n != size {
+ return nil, 0, fmt.Errorf("file changed between reads")
+ }
+ sum = h.Sum(nil)
+ copy(hash, sum)
+ }
+
+ var symdata *obj.LSym
+ if readonly {
+ symname := fmt.Sprintf(stringSymPattern, size, sum)
+ symdata = Ctxt.Lookup(stringSymPrefix + symname)
+ if !symdata.OnList() {
+ info := symdata.NewFileInfo()
+ info.Name = file
+ info.Size = size
+ ggloblsym(symdata, int32(size), obj.DUPOK|obj.RODATA|obj.LOCAL)
+ // Note: AttrContentAddressable cannot be set here,
+ // because the content-addressable-handling code
+ // does not know about file symbols.
+ }
+ } else {
+ // Emit a zero-length data symbol
+ // and then fix up length and content to use file.
+ symdata = slicedata(pos, "").Sym.Linksym()
+ symdata.Size = size
+ symdata.Type = objabi.SNOPTRDATA
+ info := symdata.NewFileInfo()
+ info.Name = file
+ info.Size = size
+ }
+
+ return symdata, size, nil
+}
+
+var slicedataGen int
-func slicebytes(nam *Node, s string) {
- slicebytes_gen++
- symname := fmt.Sprintf(".gobytes.%d", slicebytes_gen)
+func slicedata(pos src.XPos, s string) *Node {
+ slicedataGen++
+ symname := fmt.Sprintf(".gobytes.%d", slicedataGen)
sym := localpkg.Lookup(symname)
symnode := newname(sym)
sym.Def = asTypesNode(symnode)
lsym := sym.Linksym()
- off := dsname(lsym, 0, s, nam.Pos, "slice")
+ off := dstringdata(lsym, 0, s, pos, "slice")
ggloblsym(lsym, int32(off), obj.NOPTR|obj.LOCAL)
+ return symnode
+}
+
+func slicebytes(nam *Node, s string) {
if nam.Op != ONAME {
Fatalf("slicebytes %v", nam)
}
- slicesym(nam, symnode, int64(len(s)))
+ slicesym(nam, slicedata(nam.Pos, s), int64(len(s)))
}
-func dsname(s *obj.LSym, off int, t string, pos src.XPos, what string) int {
+func dstringdata(s *obj.LSym, off int, t string, pos src.XPos, what string) int {
// Objects that are too large will cause the data section to overflow right away,
// causing a cryptic error message by the linker. Check for oversize objects here
// and provide a useful error message instead.
diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go
index aa91160e5c..30e1535c09 100644
--- a/src/cmd/compile/internal/gc/order.go
+++ b/src/cmd/compile/internal/gc/order.go
@@ -50,7 +50,7 @@ type Order struct {
// Order rewrites fn.Nbody to apply the ordering constraints
// described in the comment at the top of the file.
func order(fn *Node) {
- if Debug['W'] > 1 {
+ if Debug.W > 1 {
s := fmt.Sprintf("\nbefore order %v", fn.Func.Nname.Sym)
dumplist(s, fn.Nbody)
}
@@ -288,20 +288,13 @@ func (o *Order) popTemp(mark ordermarker) {
o.temp = o.temp[:mark]
}
-// cleanTempNoPop emits VARKILL and if needed VARLIVE instructions
-// to *out for each temporary above the mark on the temporary stack.
+// cleanTempNoPop emits VARKILL instructions to *out
+// for each temporary above the mark on the temporary stack.
// It does not pop the temporaries from the stack.
func (o *Order) cleanTempNoPop(mark ordermarker) []*Node {
var out []*Node
for i := len(o.temp) - 1; i >= int(mark); i-- {
n := o.temp[i]
- if n.Name.Keepalive() {
- n.Name.SetKeepalive(false)
- n.Name.SetAddrtaken(true) // ensure SSA keeps the n variable
- live := nod(OVARLIVE, n, nil)
- live = typecheck(live, ctxStmt)
- out = append(out, live)
- }
kill := nod(OVARKILL, n, nil)
kill = typecheck(kill, ctxStmt)
out = append(out, kill)
@@ -330,12 +323,7 @@ func (o *Order) stmtList(l Nodes) {
// and rewrites it to:
// m = OMAKESLICECOPY([]T, x, s); nil
func orderMakeSliceCopy(s []*Node) {
- const go115makeslicecopy = true
- if !go115makeslicecopy {
- return
- }
-
- if Debug['N'] != 0 || instrumenting {
+ if Debug.N != 0 || instrumenting {
return
}
@@ -500,8 +488,9 @@ func (o *Order) call(n *Node) {
// still alive when we pop the temp stack.
if arg.Op == OCONVNOP && arg.Left.Type.IsUnsafePtr() {
x := o.copyExpr(arg.Left, arg.Left.Type, false)
- x.Name.SetKeepalive(true)
arg.Left = x
+ x.Name.SetAddrtaken(true) // ensure SSA keeps the x variable
+ n.Nbody.Append(typecheck(nod(OVARLIVE, x, nil), ctxStmt))
}
}
@@ -902,7 +891,7 @@ func (o *Order) stmt(n *Node) {
// c is always evaluated; x and ok are only evaluated when assigned.
r.Right.Left = o.expr(r.Right.Left, nil)
- if r.Right.Left.Op != ONAME {
+ if !r.Right.Left.IsAutoTmp() {
r.Right.Left = o.copyExpr(r.Right.Left, r.Right.Left.Type, false)
}
@@ -1108,7 +1097,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(strlit(n1)) != 0
+ haslit = haslit || n1.Op == OLITERAL && len(n1.StringVal()) != 0
}
if haslit && hasbyte {
@@ -1280,7 +1269,7 @@ func (o *Order) expr(n, lhs *Node) *Node {
var t *types.Type
switch n.Op {
case OSLICELIT:
- t = types.NewArray(n.Type.Elem(), n.Right.Int64())
+ t = types.NewArray(n.Type.Elem(), n.Right.Int64Val())
case OCALLPART:
t = partialCallType(n)
}
diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go
index 74262595b0..353f4b08c9 100644
--- a/src/cmd/compile/internal/gc/pgen.go
+++ b/src/cmd/compile/internal/gc/pgen.go
@@ -231,6 +231,11 @@ func compile(fn *Node) {
return
}
+ // Set up the function's LSym early to avoid data races with the assemblers.
+ // Do this before walk, as walk needs the LSym to set attributes/relocations
+ // (e.g. in markTypeUsedInInterface).
+ fn.Func.initLSym(true)
+
walk(fn)
if nerrors != 0 {
return
@@ -250,9 +255,6 @@ func compile(fn *Node) {
return
}
- // Set up the function's LSym early to avoid data races with the assemblers.
- fn.Func.initLSym(true)
-
// Make sure type syms are declared for all types that might
// be types of stack objects. We need to do this here
// because symbols must be allocated before the parallel
@@ -264,8 +266,8 @@ func compile(fn *Node) {
dtypesym(n.Type)
// Also make sure we allocate a linker symbol
// for the stack object data, for the same reason.
- if fn.Func.lsym.Func.StackObjects == nil {
- fn.Func.lsym.Func.StackObjects = Ctxt.Lookup(fn.Func.lsym.Name + ".stkobj")
+ if fn.Func.lsym.Func().StackObjects == nil {
+ fn.Func.lsym.Func().StackObjects = Ctxt.Lookup(fn.Func.lsym.Name + ".stkobj")
}
}
}
@@ -413,7 +415,7 @@ func debuginfo(fnsym *obj.LSym, infosym *obj.LSym, curfn interface{}) ([]dwarf.S
case PAUTO:
if !n.Name.Used() {
// Text == nil -> generating abstract function
- if fnsym.Func.Text != nil {
+ if fnsym.Func().Text != nil {
Fatalf("debuginfo unused node (AllocFrame should truncate fn.Func.Dcl)")
}
continue
@@ -423,7 +425,7 @@ func debuginfo(fnsym *obj.LSym, infosym *obj.LSym, curfn interface{}) ([]dwarf.S
continue
}
apdecls = append(apdecls, n)
- fnsym.Func.RecordAutoType(ngotype(n).Linksym())
+ fnsym.Func().RecordAutoType(ngotype(n).Linksym())
}
decls, dwarfVars := createDwarfVars(fnsym, fn.Func, apdecls)
@@ -433,7 +435,7 @@ func debuginfo(fnsym *obj.LSym, infosym *obj.LSym, curfn interface{}) ([]dwarf.S
// the function symbol to insure that the type included in DWARF
// processing during linking.
typesyms := []*obj.LSym{}
- for t, _ := range fnsym.Func.Autot {
+ for t, _ := range fnsym.Func().Autot {
typesyms = append(typesyms, t)
}
sort.Sort(obj.BySymName(typesyms))
@@ -442,7 +444,7 @@ func debuginfo(fnsym *obj.LSym, infosym *obj.LSym, curfn interface{}) ([]dwarf.S
r.Sym = sym
r.Type = objabi.R_USETYPE
}
- fnsym.Func.Autot = nil
+ fnsym.Func().Autot = nil
var varScopes []ScopeID
for _, decl := range decls {
@@ -520,7 +522,7 @@ func createSimpleVar(fnsym *obj.LSym, n *Node) *dwarf.Var {
}
typename := dwarf.InfoPrefix + typesymname(n.Type)
- delete(fnsym.Func.Autot, ngotype(n).Linksym())
+ delete(fnsym.Func().Autot, ngotype(n).Linksym())
inlIndex := 0
if genDwarfInline > 1 {
if n.Name.InlFormal() || n.Name.InlLocal() {
@@ -665,7 +667,7 @@ func createDwarfVars(fnsym *obj.LSym, fn *Func, apDecls []*Node) ([]*Node, []*dw
ChildIndex: -1,
})
// Record go type of to insure that it gets emitted by the linker.
- fnsym.Func.RecordAutoType(ngotype(n).Linksym())
+ fnsym.Func().RecordAutoType(ngotype(n).Linksym())
}
return decls, vars
@@ -729,7 +731,7 @@ func createComplexVar(fnsym *obj.LSym, fn *Func, varID ssa.VarID) *dwarf.Var {
}
gotype := ngotype(n).Linksym()
- delete(fnsym.Func.Autot, gotype)
+ delete(fnsym.Func().Autot, gotype)
typename := dwarf.InfoPrefix + gotype.Name[len("type."):]
inlIndex := 0
if genDwarfInline > 1 {
diff --git a/src/cmd/compile/internal/gc/plive.go b/src/cmd/compile/internal/gc/plive.go
index 8976ed657a..a48173e0d6 100644
--- a/src/cmd/compile/internal/gc/plive.go
+++ b/src/cmd/compile/internal/gc/plive.go
@@ -24,16 +24,6 @@ import (
"strings"
)
-// go115ReduceLiveness disables register maps and only produces stack
-// maps at call sites.
-//
-// In Go 1.15, we changed debug call injection to use conservative
-// scanning instead of precise pointer maps, so these are no longer
-// necessary.
-//
-// Keep in sync with runtime/preempt.go:go115ReduceLiveness.
-const go115ReduceLiveness = true
-
// OpVarDef is an annotation for the liveness analysis, marking a place
// where a complete initialization (definition) of a variable begins.
// Since the liveness analysis can see initialization of single-word
@@ -96,15 +86,15 @@ type BlockEffects struct {
//
// uevar: upward exposed variables (used before set in block)
// varkill: killed variables (set in block)
- uevar varRegVec
- varkill varRegVec
+ uevar bvec
+ varkill bvec
// Computed during Liveness.solve using control flow information:
//
// livein: variables live at block entry
// liveout: variables live at block exit
- livein varRegVec
- liveout varRegVec
+ livein bvec
+ liveout bvec
}
// A collection of global state used by liveness analysis.
@@ -128,16 +118,14 @@ type Liveness struct {
// current Block during Liveness.epilogue. Indexed in Value
// order for that block. Additionally, for the entry block
// livevars[0] is the entry bitmap. Liveness.compact moves
- // these to stackMaps and regMaps.
- livevars []varRegVec
+ // these to stackMaps.
+ livevars []bvec
// livenessMap maps from safe points (i.e., CALLs) to their
// liveness map indexes.
livenessMap LivenessMap
stackMapSet bvecSet
stackMaps []bvec
- regMapSet map[liveRegMask]int
- regMaps []liveRegMask
cache progeffectscache
}
@@ -158,7 +146,7 @@ func (m *LivenessMap) reset() {
delete(m.vals, k)
}
}
- m.deferreturn = LivenessInvalid
+ m.deferreturn = LivenessDontCare
}
func (m *LivenessMap) set(v *ssa.Value, i LivenessIndex) {
@@ -166,27 +154,17 @@ func (m *LivenessMap) set(v *ssa.Value, i LivenessIndex) {
}
func (m LivenessMap) Get(v *ssa.Value) LivenessIndex {
- if !go115ReduceLiveness {
- // All safe-points are in the map, so if v isn't in
- // the map, it's an unsafe-point.
- if idx, ok := m.vals[v.ID]; ok {
- return idx
- }
- return LivenessInvalid
- }
-
// If v isn't in the map, then it's a "don't care" and not an
// unsafe-point.
if idx, ok := m.vals[v.ID]; ok {
return idx
}
- return LivenessIndex{StackMapDontCare, StackMapDontCare, false}
+ return LivenessIndex{StackMapDontCare, false}
}
// LivenessIndex stores the liveness map information for a Value.
type LivenessIndex struct {
stackMapIndex int
- regMapIndex int // only for !go115ReduceLiveness
// isUnsafePoint indicates that this is an unsafe-point.
//
@@ -197,8 +175,10 @@ type LivenessIndex struct {
isUnsafePoint bool
}
-// LivenessInvalid indicates an unsafe point with no stack map.
-var LivenessInvalid = LivenessIndex{StackMapDontCare, StackMapDontCare, true} // only for !go115ReduceLiveness
+// LivenessDontCare indicates that the liveness information doesn't
+// matter. Currently it is used in deferreturn liveness when we don't
+// actually need it. It should never be emitted to the PCDATA stream.
+var LivenessDontCare = LivenessIndex{StackMapDontCare, true}
// StackMapDontCare indicates that the stack map index at a Value
// doesn't matter.
@@ -212,46 +192,12 @@ func (idx LivenessIndex) StackMapValid() bool {
return idx.stackMapIndex != StackMapDontCare
}
-func (idx LivenessIndex) RegMapValid() bool {
- return idx.regMapIndex != StackMapDontCare
-}
-
type progeffectscache struct {
retuevar []int32
tailuevar []int32
initialized bool
}
-// varRegVec contains liveness bitmaps for variables and registers.
-type varRegVec struct {
- vars bvec
- regs liveRegMask
-}
-
-func (v *varRegVec) Eq(v2 varRegVec) bool {
- return v.vars.Eq(v2.vars) && v.regs == v2.regs
-}
-
-func (v *varRegVec) Copy(v2 varRegVec) {
- v.vars.Copy(v2.vars)
- v.regs = v2.regs
-}
-
-func (v *varRegVec) Clear() {
- v.vars.Clear()
- v.regs = 0
-}
-
-func (v *varRegVec) Or(v1, v2 varRegVec) {
- v.vars.Or(v1.vars, v2.vars)
- v.regs = v1.regs | v2.regs
-}
-
-func (v *varRegVec) AndNot(v1, v2 varRegVec) {
- v.vars.AndNot(v1.vars, v2.vars)
- v.regs = v1.regs &^ v2.regs
-}
-
// livenessShouldTrack reports whether the liveness analysis
// should track the variable n.
// We don't care about variables that have no pointers,
@@ -400,110 +346,6 @@ func affectedNode(v *ssa.Value) (*Node, ssa.SymEffect) {
}
}
-// regEffects returns the registers affected by v.
-func (lv *Liveness) regEffects(v *ssa.Value) (uevar, kill liveRegMask) {
- if go115ReduceLiveness {
- return 0, 0
- }
- if v.Op == ssa.OpPhi {
- // All phi node arguments must come from the same
- // register and the result must also go to that
- // register, so there's no overall effect.
- return 0, 0
- }
- addLocs := func(mask liveRegMask, v *ssa.Value, ptrOnly bool) liveRegMask {
- if int(v.ID) >= len(lv.f.RegAlloc) {
- // v has no allocated registers.
- return mask
- }
- loc := lv.f.RegAlloc[v.ID]
- if loc == nil {
- // v has no allocated registers.
- return mask
- }
- if v.Op == ssa.OpGetG {
- // GetG represents the G register, which is a
- // pointer, but not a valid GC register. The
- // current G is always reachable, so it's okay
- // to ignore this register.
- return mask
- }
-
- // Collect registers and types from v's location.
- var regs [2]*ssa.Register
- nreg := 0
- switch loc := loc.(type) {
- case ssa.LocalSlot:
- return mask
- case *ssa.Register:
- if ptrOnly && !v.Type.HasPointers() {
- return mask
- }
- regs[0] = loc
- nreg = 1
- case ssa.LocPair:
- // The value will have TTUPLE type, and the
- // children are nil or *ssa.Register.
- if v.Type.Etype != types.TTUPLE {
- v.Fatalf("location pair %s has non-tuple type %v", loc, v.Type)
- }
- for i, loc1 := range &loc {
- if loc1 == nil {
- continue
- }
- if ptrOnly && !v.Type.FieldType(i).HasPointers() {
- continue
- }
- regs[nreg] = loc1.(*ssa.Register)
- nreg++
- }
- default:
- v.Fatalf("weird RegAlloc location: %s (%T)", loc, loc)
- }
-
- // Add register locations to vars.
- for _, reg := range regs[:nreg] {
- if reg.GCNum() == -1 {
- if ptrOnly {
- v.Fatalf("pointer in non-pointer register %v", reg)
- } else {
- continue
- }
- }
- mask |= 1 << uint(reg.GCNum())
- }
- return mask
- }
-
- // v clobbers all registers it writes to (whether or not the
- // write is pointer-typed).
- kill = addLocs(0, v, false)
- for _, arg := range v.Args {
- // v uses all registers is reads from, but we only
- // care about marking those containing pointers.
- uevar = addLocs(uevar, arg, true)
- }
- return uevar, kill
-}
-
-type liveRegMask uint32 // only if !go115ReduceLiveness
-
-func (m liveRegMask) niceString(config *ssa.Config) string {
- if m == 0 {
- return "<none>"
- }
- str := ""
- for i, reg := range config.GCRegMap {
- if m&(1<<uint(i)) != 0 {
- if str != "" {
- str += ","
- }
- str += reg.String()
- }
- }
- return str
-}
-
type livenessFuncCache struct {
be []BlockEffects
livenessMap LivenessMap
@@ -519,8 +361,6 @@ func newliveness(fn *Node, f *ssa.Func, vars []*Node, idx map[*Node]int32, stkpt
vars: vars,
idx: idx,
stkptrsize: stkptrsize,
-
- regMapSet: make(map[liveRegMask]int),
}
// Significant sources of allocation are kept in the ssa.Cache
@@ -533,7 +373,7 @@ func newliveness(fn *Node, f *ssa.Func, vars []*Node, idx map[*Node]int32, stkpt
if cap(lc.be) >= f.NumBlocks() {
lv.be = lc.be[:f.NumBlocks()]
}
- lv.livenessMap = LivenessMap{vals: lc.livenessMap.vals, deferreturn: LivenessInvalid}
+ lv.livenessMap = LivenessMap{vals: lc.livenessMap.vals, deferreturn: LivenessDontCare}
lc.livenessMap.vals = nil
}
if lv.be == nil {
@@ -546,10 +386,10 @@ func newliveness(fn *Node, f *ssa.Func, vars []*Node, idx map[*Node]int32, stkpt
for _, b := range f.Blocks {
be := lv.blockEffects(b)
- be.uevar = varRegVec{vars: bulk.next()}
- be.varkill = varRegVec{vars: bulk.next()}
- be.livein = varRegVec{vars: bulk.next()}
- be.liveout = varRegVec{vars: bulk.next()}
+ be.uevar = bulk.next()
+ be.varkill = bulk.next()
+ be.livein = bulk.next()
+ be.liveout = bulk.next()
}
lv.livenessMap.reset()
@@ -637,20 +477,6 @@ func onebitwalktype1(t *types.Type, off int64, bv bvec) {
}
}
-// usedRegs returns the maximum width of the live register map.
-func (lv *Liveness) usedRegs() int32 {
- var any liveRegMask
- for _, live := range lv.regMaps {
- any |= live
- }
- i := int32(0)
- for any != 0 {
- any >>= 1
- i++
- }
- return i
-}
-
// Generates live pointer value maps for arguments and local variables. The
// this argument and the in arguments are always assumed live. The vars
// argument is a slice of *Nodes.
@@ -851,31 +677,16 @@ func (lv *Liveness) markUnsafePoints() {
// particular, call Values can have a stack map in case the callee
// grows the stack, but not themselves be a safe-point.
func (lv *Liveness) hasStackMap(v *ssa.Value) bool {
- // The runtime only has safe-points in function prologues, so
- // we only need stack maps at call sites. go:nosplit functions
- // are similar.
- if go115ReduceLiveness || compiling_runtime || lv.f.NoSplit {
- if !v.Op.IsCall() {
- return false
- }
- // typedmemclr and typedmemmove are write barriers and
- // deeply non-preemptible. They are unsafe points and
- // hence should not have liveness maps.
- if sym, _ := v.Aux.(*obj.LSym); sym == typedmemclr || sym == typedmemmove {
- return false
- }
- return true
+ if !v.Op.IsCall() {
+ return false
}
-
- switch v.Op {
- case ssa.OpInitMem, ssa.OpArg, ssa.OpSP, ssa.OpSB,
- ssa.OpSelect0, ssa.OpSelect1, ssa.OpGetG,
- ssa.OpVarDef, ssa.OpVarLive, ssa.OpKeepAlive,
- ssa.OpPhi:
- // These don't produce code (see genssa).
+ // typedmemclr and typedmemmove are write barriers and
+ // deeply non-preemptible. They are unsafe points and
+ // hence should not have liveness maps.
+ if sym, ok := v.Aux.(*ssa.AuxCall); ok && (sym.Fn == typedmemclr || sym.Fn == typedmemmove) {
return false
}
- return !lv.unsafePoints.Get(int32(v.ID))
+ return true
}
// Initializes the sets for solving the live variables. Visits all the
@@ -891,17 +702,13 @@ func (lv *Liveness) prologue() {
// effects with the each prog effects.
for j := len(b.Values) - 1; j >= 0; j-- {
pos, e := lv.valueEffects(b.Values[j])
- regUevar, regKill := lv.regEffects(b.Values[j])
if e&varkill != 0 {
- be.varkill.vars.Set(pos)
- be.uevar.vars.Unset(pos)
+ be.varkill.Set(pos)
+ be.uevar.Unset(pos)
}
- be.varkill.regs |= regKill
- be.uevar.regs &^= regKill
if e&uevar != 0 {
- be.uevar.vars.Set(pos)
+ be.uevar.Set(pos)
}
- be.uevar.regs |= regUevar
}
}
}
@@ -911,8 +718,8 @@ func (lv *Liveness) solve() {
// These temporary bitvectors exist to avoid successive allocations and
// frees within the loop.
nvars := int32(len(lv.vars))
- newlivein := varRegVec{vars: bvalloc(nvars)}
- newliveout := varRegVec{vars: bvalloc(nvars)}
+ newlivein := bvalloc(nvars)
+ newliveout := bvalloc(nvars)
// Walk blocks in postorder ordering. This improves convergence.
po := lv.f.Postorder()
@@ -930,11 +737,11 @@ func (lv *Liveness) solve() {
switch b.Kind {
case ssa.BlockRet:
for _, pos := range lv.cache.retuevar {
- newliveout.vars.Set(pos)
+ newliveout.Set(pos)
}
case ssa.BlockRetJmp:
for _, pos := range lv.cache.tailuevar {
- newliveout.vars.Set(pos)
+ newliveout.Set(pos)
}
case ssa.BlockExit:
// panic exit - nothing to do
@@ -969,7 +776,7 @@ func (lv *Liveness) solve() {
// variables at each safe point locations.
func (lv *Liveness) epilogue() {
nvars := int32(len(lv.vars))
- liveout := varRegVec{vars: bvalloc(nvars)}
+ liveout := bvalloc(nvars)
livedefer := bvalloc(nvars) // always-live variables
// If there is a defer (that could recover), then all output
@@ -1025,12 +832,11 @@ func (lv *Liveness) epilogue() {
{
// Reserve an entry for function entry.
live := bvalloc(nvars)
- lv.livevars = append(lv.livevars, varRegVec{vars: live})
+ lv.livevars = append(lv.livevars, live)
}
for _, b := range lv.f.Blocks {
be := lv.blockEffects(b)
- firstBitmapIndex := len(lv.livevars)
// Walk forward through the basic block instructions and
// allocate liveness maps for those instructions that need them.
@@ -1040,7 +846,7 @@ func (lv *Liveness) epilogue() {
}
live := bvalloc(nvars)
- lv.livevars = append(lv.livevars, varRegVec{vars: live})
+ lv.livevars = append(lv.livevars, live)
}
// walk backward, construct maps at each safe point
@@ -1056,21 +862,18 @@ func (lv *Liveness) epilogue() {
live := &lv.livevars[index]
live.Or(*live, liveout)
- live.vars.Or(live.vars, livedefer) // only for non-entry safe points
+ live.Or(*live, livedefer) // only for non-entry safe points
index--
}
// Update liveness information.
pos, e := lv.valueEffects(v)
- regUevar, regKill := lv.regEffects(v)
if e&varkill != 0 {
- liveout.vars.Unset(pos)
+ liveout.Unset(pos)
}
- liveout.regs &^= regKill
if e&uevar != 0 {
- liveout.vars.Set(pos)
+ liveout.Set(pos)
}
- liveout.regs |= regUevar
}
if b == lv.f.Entry {
@@ -1080,7 +883,7 @@ func (lv *Liveness) epilogue() {
// Check to make sure only input variables are live.
for i, n := range lv.vars {
- if !liveout.vars.Get(int32(i)) {
+ if !liveout.Get(int32(i)) {
continue
}
if n.Class() == PPARAM {
@@ -1094,32 +897,16 @@ func (lv *Liveness) epilogue() {
live.Or(*live, liveout)
}
- // Check that no registers are live across calls.
- // For closure calls, the CALLclosure is the last use
- // of the context register, so it's dead after the call.
- index = int32(firstBitmapIndex)
- for _, v := range b.Values {
- if lv.hasStackMap(v) {
- live := lv.livevars[index]
- if v.Op.IsCall() && live.regs != 0 {
- lv.printDebug()
- v.Fatalf("%v register %s recorded as live at call", lv.fn.Func.Nname, live.regs.niceString(lv.f.Config))
- }
- index++
- }
- }
-
// The liveness maps for this block are now complete. Compact them.
lv.compact(b)
}
// If we have an open-coded deferreturn call, make a liveness map for it.
if lv.fn.Func.OpenCodedDeferDisallowed() {
- lv.livenessMap.deferreturn = LivenessInvalid
+ lv.livenessMap.deferreturn = LivenessDontCare
} else {
lv.livenessMap.deferreturn = LivenessIndex{
stackMapIndex: lv.stackMapSet.add(livedefer),
- regMapIndex: 0, // entry regMap, containing no live registers
isUnsafePoint: false,
}
}
@@ -1136,20 +923,10 @@ func (lv *Liveness) epilogue() {
lv.f.Fatalf("%v %L recorded as live on entry", lv.fn.Func.Nname, n)
}
}
- if !go115ReduceLiveness {
- // Check that no registers are live at function entry.
- // The context register, if any, comes from a
- // LoweredGetClosurePtr operation first thing in the function,
- // so it doesn't appear live at entry.
- if regs := lv.regMaps[0]; regs != 0 {
- lv.printDebug()
- lv.f.Fatalf("%v register %s recorded as live on entry", lv.fn.Func.Nname, regs.niceString(lv.f.Config))
- }
- }
}
// Compact coalesces identical bitmaps from lv.livevars into the sets
-// lv.stackMapSet and lv.regMaps.
+// lv.stackMapSet.
//
// Compact clears lv.livevars.
//
@@ -1165,45 +942,23 @@ func (lv *Liveness) epilogue() {
// PCDATA tables cost about 100k. So for now we keep using a single index for
// both bitmap lists.
func (lv *Liveness) compact(b *ssa.Block) {
- add := func(live varRegVec, isUnsafePoint bool) LivenessIndex { // only if !go115ReduceLiveness
- // Deduplicate the stack map.
- stackIndex := lv.stackMapSet.add(live.vars)
- // Deduplicate the register map.
- regIndex, ok := lv.regMapSet[live.regs]
- if !ok {
- regIndex = len(lv.regMapSet)
- lv.regMapSet[live.regs] = regIndex
- lv.regMaps = append(lv.regMaps, live.regs)
- }
- return LivenessIndex{stackIndex, regIndex, isUnsafePoint}
- }
pos := 0
if b == lv.f.Entry {
// Handle entry stack map.
- if !go115ReduceLiveness {
- add(lv.livevars[0], false)
- } else {
- lv.stackMapSet.add(lv.livevars[0].vars)
- }
+ lv.stackMapSet.add(lv.livevars[0])
pos++
}
for _, v := range b.Values {
- if go115ReduceLiveness {
- hasStackMap := lv.hasStackMap(v)
- isUnsafePoint := lv.allUnsafe || lv.unsafePoints.Get(int32(v.ID))
- idx := LivenessIndex{StackMapDontCare, StackMapDontCare, isUnsafePoint}
- if hasStackMap {
- idx.stackMapIndex = lv.stackMapSet.add(lv.livevars[pos].vars)
- pos++
- }
- if hasStackMap || isUnsafePoint {
- lv.livenessMap.set(v, idx)
- }
- } else if lv.hasStackMap(v) {
- isUnsafePoint := lv.allUnsafe || lv.unsafePoints.Get(int32(v.ID))
- lv.livenessMap.set(v, add(lv.livevars[pos], isUnsafePoint))
+ hasStackMap := lv.hasStackMap(v)
+ isUnsafePoint := lv.allUnsafe || lv.unsafePoints.Get(int32(v.ID))
+ idx := LivenessIndex{StackMapDontCare, isUnsafePoint}
+ if hasStackMap {
+ idx.stackMapIndex = lv.stackMapSet.add(lv.livevars[pos])
pos++
}
+ if hasStackMap || isUnsafePoint {
+ lv.livenessMap.set(v, idx)
+ }
}
// Reset livevars.
@@ -1231,8 +986,8 @@ func (lv *Liveness) showlive(v *ssa.Value, live bvec) {
s := "live at "
if v == nil {
s += fmt.Sprintf("entry to %s:", lv.fn.funcname())
- } else if sym, ok := v.Aux.(*obj.LSym); ok {
- fn := sym.Name
+ } else if sym, ok := v.Aux.(*ssa.AuxCall); ok && sym.Fn != nil {
+ fn := sym.Fn.Name
if pos := strings.Index(fn, "."); pos >= 0 {
fn = fn[pos+1:]
}
@@ -1250,8 +1005,8 @@ func (lv *Liveness) showlive(v *ssa.Value, live bvec) {
Warnl(pos, s)
}
-func (lv *Liveness) printbvec(printed bool, name string, live varRegVec) bool {
- if live.vars.IsEmpty() && live.regs == 0 {
+func (lv *Liveness) printbvec(printed bool, name string, live bvec) bool {
+ if live.IsEmpty() {
return printed
}
@@ -1264,19 +1019,18 @@ func (lv *Liveness) printbvec(printed bool, name string, live varRegVec) bool {
comma := ""
for i, n := range lv.vars {
- if !live.vars.Get(int32(i)) {
+ if !live.Get(int32(i)) {
continue
}
fmt.Printf("%s%s", comma, n.Sym.Name)
comma = ","
}
- fmt.Printf("%s%s", comma, live.regs.niceString(lv.f.Config))
return true
}
-// printeffect is like printbvec, but for valueEffects and regEffects.
-func (lv *Liveness) printeffect(printed bool, name string, pos int32, x bool, regMask liveRegMask) bool {
- if !x && regMask == 0 {
+// printeffect is like printbvec, but for valueEffects.
+func (lv *Liveness) printeffect(printed bool, name string, pos int32, x bool) bool {
+ if !x {
return printed
}
if !printed {
@@ -1288,15 +1042,7 @@ func (lv *Liveness) printeffect(printed bool, name string, pos int32, x bool, re
if x {
fmt.Printf("%s", lv.vars[pos].Sym.Name)
}
- for j, reg := range lv.f.Config.GCRegMap {
- if regMask&(1<<uint(j)) != 0 {
- if x {
- fmt.Printf(",")
- }
- x = true
- fmt.Printf("%v", reg)
- }
- }
+
return true
}
@@ -1364,15 +1110,14 @@ func (lv *Liveness) printDebug() {
pcdata := lv.livenessMap.Get(v)
pos, effect := lv.valueEffects(v)
- regUevar, regKill := lv.regEffects(v)
printed = false
- printed = lv.printeffect(printed, "uevar", pos, effect&uevar != 0, regUevar)
- printed = lv.printeffect(printed, "varkill", pos, effect&varkill != 0, regKill)
+ printed = lv.printeffect(printed, "uevar", pos, effect&uevar != 0)
+ printed = lv.printeffect(printed, "varkill", pos, effect&varkill != 0)
if printed {
fmt.Printf("\n")
}
- if pcdata.StackMapValid() || pcdata.RegMapValid() {
+ if pcdata.StackMapValid() {
fmt.Printf("\tlive=")
printed = false
if pcdata.StackMapValid() {
@@ -1388,16 +1133,6 @@ func (lv *Liveness) printDebug() {
printed = true
}
}
- if pcdata.RegMapValid() { // only if !go115ReduceLiveness
- regLive := lv.regMaps[pcdata.regMapIndex]
- if regLive != 0 {
- if printed {
- fmt.Printf(",")
- }
- fmt.Printf("%s", regLive.niceString(lv.f.Config))
- printed = true
- }
- }
fmt.Printf("\n")
}
@@ -1423,7 +1158,7 @@ func (lv *Liveness) printDebug() {
// first word dumped is the total number of bitmaps. The second word is the
// length of the bitmaps. All bitmaps are assumed to be of equal length. The
// remaining bytes are the raw bitmaps.
-func (lv *Liveness) emit() (argsSym, liveSym, regsSym *obj.LSym) {
+func (lv *Liveness) emit() (argsSym, liveSym *obj.LSym) {
// Size args bitmaps to be just large enough to hold the largest pointer.
// First, find the largest Xoffset node we care about.
// (Nodes without pointers aren't in lv.vars; see livenessShouldTrack.)
@@ -1452,7 +1187,7 @@ func (lv *Liveness) emit() (argsSym, liveSym, regsSym *obj.LSym) {
maxLocals := lv.stkptrsize
// Temporary symbols for encoding bitmaps.
- var argsSymTmp, liveSymTmp, regsSymTmp obj.LSym
+ var argsSymTmp, liveSymTmp obj.LSym
args := bvalloc(int32(maxArgs / int64(Widthptr)))
aoff := duint32(&argsSymTmp, 0, uint32(len(lv.stackMaps))) // number of bitmaps
@@ -1472,24 +1207,6 @@ func (lv *Liveness) emit() (argsSym, liveSym, regsSym *obj.LSym) {
loff = dbvec(&liveSymTmp, loff, locals)
}
- if !go115ReduceLiveness {
- regs := bvalloc(lv.usedRegs())
- roff := duint32(&regsSymTmp, 0, uint32(len(lv.regMaps))) // number of bitmaps
- roff = duint32(&regsSymTmp, roff, uint32(regs.n)) // number of bits in each bitmap
- if regs.n > 32 {
- // Our uint32 conversion below won't work.
- Fatalf("GP registers overflow uint32")
- }
-
- if regs.n > 0 {
- for _, live := range lv.regMaps {
- regs.Clear()
- regs.b[0] = uint32(live)
- roff = dbvec(&regsSymTmp, roff, regs)
- }
- }
- }
-
// Give these LSyms content-addressable names,
// so that they can be de-duplicated.
// This provides significant binary size savings.
@@ -1502,11 +1219,7 @@ func (lv *Liveness) emit() (argsSym, liveSym, regsSym *obj.LSym) {
lsym.Set(obj.AttrContentAddressable, true)
})
}
- if !go115ReduceLiveness {
- return makeSym(&argsSymTmp), makeSym(&liveSymTmp), makeSym(&regsSymTmp)
- }
- // TODO(go115ReduceLiveness): Remove regsSym result
- return makeSym(&argsSymTmp), makeSym(&liveSymTmp), nil
+ return makeSym(&argsSymTmp), makeSym(&liveSymTmp)
}
// Entry pointer for liveness analysis. Solves for the liveness of
@@ -1552,27 +1265,20 @@ func liveness(e *ssafn, f *ssa.Func, pp *Progs) LivenessMap {
// Emit the live pointer map data structures
ls := e.curfn.Func.lsym
- ls.Func.GCArgs, ls.Func.GCLocals, ls.Func.GCRegs = lv.emit()
+ fninfo := ls.Func()
+ fninfo.GCArgs, fninfo.GCLocals = lv.emit()
p := pp.Prog(obj.AFUNCDATA)
Addrconst(&p.From, objabi.FUNCDATA_ArgsPointerMaps)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = ls.Func.GCArgs
+ p.To.Sym = fninfo.GCArgs
p = pp.Prog(obj.AFUNCDATA)
Addrconst(&p.From, objabi.FUNCDATA_LocalsPointerMaps)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = ls.Func.GCLocals
-
- if !go115ReduceLiveness {
- p = pp.Prog(obj.AFUNCDATA)
- Addrconst(&p.From, objabi.FUNCDATA_RegPointerMaps)
- p.To.Type = obj.TYPE_MEM
- p.To.Name = obj.NAME_EXTERN
- p.To.Sym = ls.Func.GCRegs
- }
+ p.To.Sym = fninfo.GCLocals
return lv.livenessMap
}
diff --git a/src/cmd/compile/internal/gc/range.go b/src/cmd/compile/internal/gc/range.go
index 5434b0167a..1b4d765d42 100644
--- a/src/cmd/compile/internal/gc/range.go
+++ b/src/cmd/compile/internal/gc/range.go
@@ -112,12 +112,13 @@ func typecheckrangeExpr(n *Node) {
v2 = nil
}
- var why string
if v1 != nil {
if v1.Name != nil && v1.Name.Defn == n {
v1.Type = t1
- } else if v1.Type != nil && assignop(t1, v1.Type, &why) == 0 {
- yyerrorl(n.Pos, "cannot assign type %v to %L in range%s", t1, v1, why)
+ } else if v1.Type != nil {
+ if op, why := assignop(t1, v1.Type); op == OXXX {
+ yyerrorl(n.Pos, "cannot assign type %v to %L in range%s", t1, v1, why)
+ }
}
checkassign(n, v1)
}
@@ -125,8 +126,10 @@ func typecheckrangeExpr(n *Node) {
if v2 != nil {
if v2.Name != nil && v2.Name.Defn == n {
v2.Type = t2
- } else if v2.Type != nil && assignop(t2, v2.Type, &why) == 0 {
- yyerrorl(n.Pos, "cannot assign type %v to %L in range%s", t2, v2, why)
+ } else if v2.Type != nil {
+ if op, why := assignop(t2, v2.Type); op == OXXX {
+ yyerrorl(n.Pos, "cannot assign type %v to %L in range%s", t2, v2, why)
+ }
}
checkassign(n, v2)
}
@@ -463,7 +466,7 @@ func walkrange(n *Node) *Node {
//
// where == for keys of map m is reflexive.
func isMapClear(n *Node) bool {
- if Debug['N'] != 0 || instrumenting {
+ if Debug.N != 0 || instrumenting {
return false
}
@@ -530,7 +533,7 @@ func mapClear(m *Node) *Node {
//
// Parameters are as in walkrange: "for v1, v2 = range a".
func arrayClear(n, v1, v2, a *Node) bool {
- if Debug['N'] != 0 || instrumenting {
+ if Debug.N != 0 || instrumenting {
return false
}
diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go
index 49b2a0ed49..9401eba7a5 100644
--- a/src/cmd/compile/internal/gc/reflect.go
+++ b/src/cmd/compile/internal/gc/reflect.go
@@ -61,8 +61,9 @@ const (
MAXELEMSIZE = 128
)
-func structfieldSize() int { return 3 * Widthptr } // Sizeof(runtime.structfield{})
-func imethodSize() int { return 4 + 4 } // Sizeof(runtime.imethod{})
+func structfieldSize() int { return 3 * Widthptr } // Sizeof(runtime.structfield{})
+func imethodSize() int { return 4 + 4 } // Sizeof(runtime.imethod{})
+func commonSize() int { return 4*Widthptr + 8 + 8 } // Sizeof(runtime._type{})
func uncommonSize(t *types.Type) int { // Sizeof(runtime.uncommontype{})
if t.Sym == nil && len(methods(t)) == 0 {
@@ -510,6 +511,7 @@ func dimportpath(p *types.Pkg) {
s := Ctxt.Lookup("type..importpath." + p.Prefix + ".")
ot := dnameData(s, 0, str, "", nil, false)
ggloblsym(s, int32(ot), obj.DUPOK|obj.RODATA)
+ s.Set(obj.AttrContentAddressable, true)
p.Pathsym = s
}
@@ -637,6 +639,7 @@ func dname(name, tag string, pkg *types.Pkg, exported bool) *obj.LSym {
}
ot := dnameData(s, 0, name, tag, pkg, exported)
ggloblsym(s, int32(ot), obj.DUPOK|obj.RODATA)
+ s.Set(obj.AttrContentAddressable, true)
return s
}
@@ -1422,6 +1425,20 @@ func dtypesym(t *types.Type) *obj.LSym {
return lsym
}
+// ifaceMethodOffset returns the offset of the i-th method in the interface
+// type descriptor, ityp.
+func ifaceMethodOffset(ityp *types.Type, i int64) int64 {
+ // interface type descriptor layout is struct {
+ // _type // commonSize
+ // pkgpath // 1 word
+ // []imethod // 3 words (pointing to [...]imethod below)
+ // uncommontype // uncommonSize
+ // [...]imethod
+ // }
+ // The size of imethod is 8.
+ return int64(commonSize()+4*Widthptr+uncommonSize(ityp)) + i*8
+}
+
// for each itabEntry, gather the methods on
// the concrete type that implement the interface
func peekitabs() {
@@ -1574,8 +1591,12 @@ func dumptabs() {
// typ typeOff // pointer to symbol
// }
nsym := dname(p.s.Name, "", nil, true)
+ tsym := dtypesym(p.t)
ot = dsymptrOff(s, ot, nsym)
- ot = dsymptrOff(s, ot, dtypesym(p.t))
+ ot = dsymptrOff(s, ot, tsym)
+ // Plugin exports symbols as interfaces. Mark their types
+ // as UsedInIface.
+ tsym.Set(obj.AttrUsedInIface, true)
}
ggloblsym(s, int32(ot), int16(obj.RODATA))
diff --git a/src/cmd/compile/internal/gc/scc.go b/src/cmd/compile/internal/gc/scc.go
index 60e0a9b8b5..5c7935aa87 100644
--- a/src/cmd/compile/internal/gc/scc.go
+++ b/src/cmd/compile/internal/gc/scc.go
@@ -75,8 +75,19 @@ func (v *bottomUpVisitor) visit(n *Node) uint32 {
inspectList(n.Nbody, func(n *Node) bool {
switch n.Op {
- case OCALLFUNC, OCALLMETH:
- fn := asNode(n.Left.Type.Nname())
+ case ONAME:
+ if n.Class() == PFUNC {
+ if n.isMethodExpression() {
+ n = asNode(n.Type.Nname())
+ }
+ if n != nil && n.Name.Defn != nil {
+ if m := v.visit(n.Name.Defn); m < min {
+ min = m
+ }
+ }
+ }
+ case ODOTMETH:
+ fn := asNode(n.Type.Nname())
if fn != nil && fn.Op == ONAME && fn.Class() == PFUNC && fn.Name.Defn != nil {
if m := v.visit(fn.Name.Defn); m < min {
min = m
diff --git a/src/cmd/compile/internal/gc/scope.go b/src/cmd/compile/internal/gc/scope.go
index d7239d5693..e66b859e10 100644
--- a/src/cmd/compile/internal/gc/scope.go
+++ b/src/cmd/compile/internal/gc/scope.go
@@ -62,9 +62,9 @@ func scopePCs(fnsym *obj.LSym, marks []Mark, dwarfScopes []dwarf.Scope) {
if len(marks) == 0 {
return
}
- p0 := fnsym.Func.Text
+ p0 := fnsym.Func().Text
scope := findScope(marks, p0.Pos)
- for p := fnsym.Func.Text; p != nil; p = p.Link {
+ for p := p0; p != nil; p = p.Link {
if p.Pos == p0.Pos {
continue
}
diff --git a/src/cmd/compile/internal/gc/sinit.go b/src/cmd/compile/internal/gc/sinit.go
index 71ed558461..212fcc022d 100644
--- a/src/cmd/compile/internal/gc/sinit.go
+++ b/src/cmd/compile/internal/gc/sinit.go
@@ -39,7 +39,7 @@ func (s *InitSchedule) append(n *Node) {
// staticInit adds an initialization statement n to the schedule.
func (s *InitSchedule) staticInit(n *Node) {
if !s.tryStaticInit(n) {
- if Debug['%'] != 0 {
+ if Debug.P != 0 {
Dump("nonstatic", n)
}
s.append(n)
@@ -128,7 +128,7 @@ func (s *InitSchedule) staticcopy(l *Node, r *Node) bool {
case OSLICELIT:
// copy slice
a := s.inittemps[r]
- slicesym(l, a, r.Right.Int64())
+ slicesym(l, a, r.Right.Int64Val())
return true
case OARRAYLIT, OSTRUCTLIT:
@@ -205,7 +205,7 @@ func (s *InitSchedule) staticassign(l *Node, r *Node) bool {
case OSTR2BYTES:
if l.Class() == PEXTERN && r.Left.Op == OLITERAL {
- sval := strlit(r.Left)
+ sval := r.Left.StringVal()
slicebytes(l, sval)
return true
}
@@ -213,7 +213,7 @@ func (s *InitSchedule) staticassign(l *Node, r *Node) bool {
case OSLICELIT:
s.initplan(r)
// Init slice.
- bound := r.Right.Int64()
+ bound := r.Right.Int64Val()
ta := types.NewArray(r.Type.Elem(), bound)
ta.SetNoalg(true)
a := staticname(ta)
@@ -278,7 +278,7 @@ func (s *InitSchedule) staticassign(l *Node, r *Node) bool {
return Isconst(val, CTNIL)
}
- markTypeUsedInInterface(val.Type)
+ markTypeUsedInInterface(val.Type, l.Sym.Linksym())
var itab *Node
if l.Type.IsEmptyInterface() {
@@ -375,11 +375,6 @@ func readonlystaticname(t *types.Type) *Node {
return n
}
-func isLiteral(n *Node) bool {
- // Treat nils as zeros rather than literals.
- return n.Op == OLITERAL && n.Val().Ctype() != CTNIL
-}
-
func (n *Node) isSimpleName() bool {
return n.Op == ONAME && n.Class() != PAUTOHEAP && n.Class() != PEXTERN
}
@@ -404,7 +399,7 @@ const (
func getdyn(n *Node, top bool) initGenType {
switch n.Op {
default:
- if isLiteral(n) {
+ if n.isGoConst() {
return initConst
}
return initDynamic
@@ -413,7 +408,7 @@ func getdyn(n *Node, top bool) initGenType {
if !top {
return initDynamic
}
- if n.Right.Int64()/4 > int64(n.List.Len()) {
+ if n.Right.Int64Val()/4 > int64(n.List.Len()) {
// <25% of entries have explicit values.
// Very rough estimation, it takes 4 bytes of instructions
// to initialize 1 byte of result. So don't use a static
@@ -559,7 +554,7 @@ func fixedlit(ctxt initContext, kind initKind, n *Node, var_ *Node, init *Nodes)
continue
}
- islit := isLiteral(value)
+ islit := value.isGoConst()
if (kind == initKindStatic && !islit) || (kind == initKindDynamic && islit) {
continue
}
@@ -589,12 +584,12 @@ func isSmallSliceLit(n *Node) bool {
r := n.Right
- return smallintconst(r) && (n.Type.Elem().Width == 0 || r.Int64() <= smallArrayBytes/n.Type.Elem().Width)
+ return smallintconst(r) && (n.Type.Elem().Width == 0 || r.Int64Val() <= smallArrayBytes/n.Type.Elem().Width)
}
func slicelit(ctxt initContext, n *Node, var_ *Node, init *Nodes) {
// make an array type corresponding the number of elements we have
- t := types.NewArray(n.Type.Elem(), n.Right.Int64())
+ t := types.NewArray(n.Type.Elem(), n.Right.Int64Val())
dowidth(t)
if ctxt == inNonInitFunction {
@@ -732,7 +727,7 @@ func slicelit(ctxt initContext, n *Node, var_ *Node, init *Nodes) {
continue
}
- if vstat != nil && isLiteral(value) { // already set by copy from static value
+ if vstat != nil && value.isGoConst() { // already set by copy from static value
continue
}
@@ -993,7 +988,7 @@ func oaslit(n *Node, init *Nodes) bool {
func getlit(lit *Node) int {
if smallintconst(lit) {
- return int(lit.Int64())
+ return int(lit.Int64Val())
}
return -1
}
diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go
index 52083d999e..5b74754b53 100644
--- a/src/cmd/compile/internal/gc/ssa.go
+++ b/src/cmd/compile/internal/gc/ssa.go
@@ -9,6 +9,7 @@ import (
"fmt"
"html"
"os"
+ "path/filepath"
"sort"
"bufio"
@@ -26,6 +27,7 @@ var ssaConfig *ssa.Config
var ssaCaches []ssa.Cache
var ssaDump string // early copy of $GOSSAFUNC; the func name to dump output for
+var ssaDir string // optional destination for ssa dump file
var ssaDumpStdout bool // whether to dump to stdout
var ssaDumpCFG string // generate CFGs for these phases
const ssaDumpFile = "ssa.html"
@@ -48,21 +50,16 @@ func initssaconfig() {
// Caching is disabled in the backend, so generating these here avoids allocations.
_ = types.NewPtr(types.Types[TINTER]) // *interface{}
_ = types.NewPtr(types.NewPtr(types.Types[TSTRING])) // **string
- _ = types.NewPtr(types.NewPtr(types.Idealstring)) // **string
_ = types.NewPtr(types.NewSlice(types.Types[TINTER])) // *[]interface{}
_ = types.NewPtr(types.NewPtr(types.Bytetype)) // **byte
_ = types.NewPtr(types.NewSlice(types.Bytetype)) // *[]byte
_ = types.NewPtr(types.NewSlice(types.Types[TSTRING])) // *[]string
- _ = types.NewPtr(types.NewSlice(types.Idealstring)) // *[]string
_ = types.NewPtr(types.NewPtr(types.NewPtr(types.Types[TUINT8]))) // ***uint8
_ = types.NewPtr(types.Types[TINT16]) // *int16
_ = types.NewPtr(types.Types[TINT64]) // *int64
_ = types.NewPtr(types.Errortype) // *error
types.NewPtrCacheEnabled = false
- ssaConfig = ssa.NewConfig(thearch.LinkArch.Name, *types_, Ctxt, Debug['N'] == 0)
- if thearch.LinkArch.Name == "386" {
- ssaConfig.Set387(thearch.Use387)
- }
+ ssaConfig = ssa.NewConfig(thearch.LinkArch.Name, *types_, Ctxt, Debug.N == 0)
ssaConfig.SoftFloat = thearch.SoftFloat
ssaConfig.Race = flag_race
ssaCaches = make([]ssa.Cache, nBackendWorkers)
@@ -75,13 +72,14 @@ func initssaconfig() {
deferproc = sysfunc("deferproc")
deferprocStack = sysfunc("deferprocStack")
Deferreturn = sysfunc("deferreturn")
- Duffcopy = sysvar("duffcopy") // asm func with special ABI
- Duffzero = sysvar("duffzero") // asm func with special ABI
- gcWriteBarrier = sysvar("gcWriteBarrier") // asm func with special ABI
+ Duffcopy = sysfunc("duffcopy")
+ Duffzero = sysfunc("duffzero")
+ gcWriteBarrier = sysfunc("gcWriteBarrier")
goschedguarded = sysfunc("goschedguarded")
growslice = sysfunc("growslice")
msanread = sysfunc("msanread")
msanwrite = sysfunc("msanwrite")
+ msanmove = sysfunc("msanmove")
newobject = sysfunc("newobject")
newproc = sysfunc("newproc")
panicdivide = sysfunc("panicdivide")
@@ -108,51 +106,51 @@ func initssaconfig() {
// asm funcs with special ABI
if thearch.LinkArch.Name == "amd64" {
GCWriteBarrierReg = map[int16]*obj.LSym{
- x86.REG_AX: sysvar("gcWriteBarrier"),
- x86.REG_CX: sysvar("gcWriteBarrierCX"),
- x86.REG_DX: sysvar("gcWriteBarrierDX"),
- x86.REG_BX: sysvar("gcWriteBarrierBX"),
- x86.REG_BP: sysvar("gcWriteBarrierBP"),
- x86.REG_SI: sysvar("gcWriteBarrierSI"),
- x86.REG_R8: sysvar("gcWriteBarrierR8"),
- x86.REG_R9: sysvar("gcWriteBarrierR9"),
+ x86.REG_AX: sysfunc("gcWriteBarrier"),
+ x86.REG_CX: sysfunc("gcWriteBarrierCX"),
+ x86.REG_DX: sysfunc("gcWriteBarrierDX"),
+ x86.REG_BX: sysfunc("gcWriteBarrierBX"),
+ x86.REG_BP: sysfunc("gcWriteBarrierBP"),
+ x86.REG_SI: sysfunc("gcWriteBarrierSI"),
+ x86.REG_R8: sysfunc("gcWriteBarrierR8"),
+ x86.REG_R9: sysfunc("gcWriteBarrierR9"),
}
}
if thearch.LinkArch.Family == sys.Wasm {
- BoundsCheckFunc[ssa.BoundsIndex] = sysvar("goPanicIndex")
- BoundsCheckFunc[ssa.BoundsIndexU] = sysvar("goPanicIndexU")
- BoundsCheckFunc[ssa.BoundsSliceAlen] = sysvar("goPanicSliceAlen")
- BoundsCheckFunc[ssa.BoundsSliceAlenU] = sysvar("goPanicSliceAlenU")
- BoundsCheckFunc[ssa.BoundsSliceAcap] = sysvar("goPanicSliceAcap")
- BoundsCheckFunc[ssa.BoundsSliceAcapU] = sysvar("goPanicSliceAcapU")
- BoundsCheckFunc[ssa.BoundsSliceB] = sysvar("goPanicSliceB")
- BoundsCheckFunc[ssa.BoundsSliceBU] = sysvar("goPanicSliceBU")
- BoundsCheckFunc[ssa.BoundsSlice3Alen] = sysvar("goPanicSlice3Alen")
- BoundsCheckFunc[ssa.BoundsSlice3AlenU] = sysvar("goPanicSlice3AlenU")
- BoundsCheckFunc[ssa.BoundsSlice3Acap] = sysvar("goPanicSlice3Acap")
- BoundsCheckFunc[ssa.BoundsSlice3AcapU] = sysvar("goPanicSlice3AcapU")
- BoundsCheckFunc[ssa.BoundsSlice3B] = sysvar("goPanicSlice3B")
- BoundsCheckFunc[ssa.BoundsSlice3BU] = sysvar("goPanicSlice3BU")
- BoundsCheckFunc[ssa.BoundsSlice3C] = sysvar("goPanicSlice3C")
- BoundsCheckFunc[ssa.BoundsSlice3CU] = sysvar("goPanicSlice3CU")
+ BoundsCheckFunc[ssa.BoundsIndex] = sysfunc("goPanicIndex")
+ BoundsCheckFunc[ssa.BoundsIndexU] = sysfunc("goPanicIndexU")
+ BoundsCheckFunc[ssa.BoundsSliceAlen] = sysfunc("goPanicSliceAlen")
+ BoundsCheckFunc[ssa.BoundsSliceAlenU] = sysfunc("goPanicSliceAlenU")
+ BoundsCheckFunc[ssa.BoundsSliceAcap] = sysfunc("goPanicSliceAcap")
+ BoundsCheckFunc[ssa.BoundsSliceAcapU] = sysfunc("goPanicSliceAcapU")
+ BoundsCheckFunc[ssa.BoundsSliceB] = sysfunc("goPanicSliceB")
+ BoundsCheckFunc[ssa.BoundsSliceBU] = sysfunc("goPanicSliceBU")
+ BoundsCheckFunc[ssa.BoundsSlice3Alen] = sysfunc("goPanicSlice3Alen")
+ BoundsCheckFunc[ssa.BoundsSlice3AlenU] = sysfunc("goPanicSlice3AlenU")
+ BoundsCheckFunc[ssa.BoundsSlice3Acap] = sysfunc("goPanicSlice3Acap")
+ BoundsCheckFunc[ssa.BoundsSlice3AcapU] = sysfunc("goPanicSlice3AcapU")
+ BoundsCheckFunc[ssa.BoundsSlice3B] = sysfunc("goPanicSlice3B")
+ BoundsCheckFunc[ssa.BoundsSlice3BU] = sysfunc("goPanicSlice3BU")
+ BoundsCheckFunc[ssa.BoundsSlice3C] = sysfunc("goPanicSlice3C")
+ BoundsCheckFunc[ssa.BoundsSlice3CU] = sysfunc("goPanicSlice3CU")
} else {
- BoundsCheckFunc[ssa.BoundsIndex] = sysvar("panicIndex")
- BoundsCheckFunc[ssa.BoundsIndexU] = sysvar("panicIndexU")
- BoundsCheckFunc[ssa.BoundsSliceAlen] = sysvar("panicSliceAlen")
- BoundsCheckFunc[ssa.BoundsSliceAlenU] = sysvar("panicSliceAlenU")
- BoundsCheckFunc[ssa.BoundsSliceAcap] = sysvar("panicSliceAcap")
- BoundsCheckFunc[ssa.BoundsSliceAcapU] = sysvar("panicSliceAcapU")
- BoundsCheckFunc[ssa.BoundsSliceB] = sysvar("panicSliceB")
- BoundsCheckFunc[ssa.BoundsSliceBU] = sysvar("panicSliceBU")
- BoundsCheckFunc[ssa.BoundsSlice3Alen] = sysvar("panicSlice3Alen")
- BoundsCheckFunc[ssa.BoundsSlice3AlenU] = sysvar("panicSlice3AlenU")
- BoundsCheckFunc[ssa.BoundsSlice3Acap] = sysvar("panicSlice3Acap")
- BoundsCheckFunc[ssa.BoundsSlice3AcapU] = sysvar("panicSlice3AcapU")
- BoundsCheckFunc[ssa.BoundsSlice3B] = sysvar("panicSlice3B")
- BoundsCheckFunc[ssa.BoundsSlice3BU] = sysvar("panicSlice3BU")
- BoundsCheckFunc[ssa.BoundsSlice3C] = sysvar("panicSlice3C")
- BoundsCheckFunc[ssa.BoundsSlice3CU] = sysvar("panicSlice3CU")
+ BoundsCheckFunc[ssa.BoundsIndex] = sysfunc("panicIndex")
+ BoundsCheckFunc[ssa.BoundsIndexU] = sysfunc("panicIndexU")
+ BoundsCheckFunc[ssa.BoundsSliceAlen] = sysfunc("panicSliceAlen")
+ BoundsCheckFunc[ssa.BoundsSliceAlenU] = sysfunc("panicSliceAlenU")
+ BoundsCheckFunc[ssa.BoundsSliceAcap] = sysfunc("panicSliceAcap")
+ BoundsCheckFunc[ssa.BoundsSliceAcapU] = sysfunc("panicSliceAcapU")
+ BoundsCheckFunc[ssa.BoundsSliceB] = sysfunc("panicSliceB")
+ BoundsCheckFunc[ssa.BoundsSliceBU] = sysfunc("panicSliceBU")
+ BoundsCheckFunc[ssa.BoundsSlice3Alen] = sysfunc("panicSlice3Alen")
+ BoundsCheckFunc[ssa.BoundsSlice3AlenU] = sysfunc("panicSlice3AlenU")
+ BoundsCheckFunc[ssa.BoundsSlice3Acap] = sysfunc("panicSlice3Acap")
+ BoundsCheckFunc[ssa.BoundsSlice3AcapU] = sysfunc("panicSlice3AcapU")
+ BoundsCheckFunc[ssa.BoundsSlice3B] = sysfunc("panicSlice3B")
+ BoundsCheckFunc[ssa.BoundsSlice3BU] = sysfunc("panicSlice3BU")
+ BoundsCheckFunc[ssa.BoundsSlice3C] = sysfunc("panicSlice3C")
+ BoundsCheckFunc[ssa.BoundsSlice3CU] = sysfunc("panicSlice3CU")
}
if thearch.LinkArch.PtrSize == 4 {
ExtendCheckFunc[ssa.BoundsIndex] = sysvar("panicExtendIndex")
@@ -173,10 +171,6 @@ func initssaconfig() {
ExtendCheckFunc[ssa.BoundsSlice3CU] = sysvar("panicExtendSlice3CU")
}
- // GO386=387 runtime definitions
- ControlWord64trunc = sysvar("controlWord64trunc") // uint16
- ControlWord32 = sysvar("controlWord32") // uint16
-
// Wasm (all asm funcs with special ABIs)
WasmMove = sysvar("wasmMove")
WasmZero = sysvar("wasmZero")
@@ -247,7 +241,7 @@ func dvarint(x *obj.LSym, off int, v int64) int {
// - Offset of where argument should be placed in the args frame when making call
func (s *state) emitOpenDeferInfo() {
x := Ctxt.Lookup(s.curfn.Func.lsym.Name + ".opendefer")
- s.curfn.Func.lsym.Func.OpenCodedDeferInfo = x
+ s.curfn.Func.lsym.Func().OpenCodedDeferInfo = x
off := 0
// Compute maxargsize (max size of arguments for all defers)
@@ -346,7 +340,13 @@ func buildssa(fn *Node, worker int) *ssa.Func {
s.f.Entry.Pos = fn.Pos
if printssa {
- s.f.HTMLWriter = ssa.NewHTMLWriter(ssaDumpFile, s.f, ssaDumpCFG)
+ ssaDF := ssaDumpFile
+ if ssaDir != "" {
+ ssaDF = filepath.Join(ssaDir, myimportpath+"."+name+".html")
+ ssaD := filepath.Dir(ssaDF)
+ os.MkdirAll(ssaD, 0755)
+ }
+ s.f.HTMLWriter = ssa.NewHTMLWriter(ssaDF, s.f, ssaDumpCFG)
// TODO: generate and print a mapping from nodes to values and blocks
dumpSourcesColumn(s.f.HTMLWriter, fn)
s.f.HTMLWriter.WriteAST("AST", astBuf)
@@ -358,7 +358,7 @@ func buildssa(fn *Node, worker int) *ssa.Func {
s.fwdVars = map[*Node]*ssa.Value{}
s.startmem = s.entryNewValue0(ssa.OpInitMem, types.TypeMem)
- s.hasOpenDefers = Debug['N'] == 0 && s.hasdefer && !s.curfn.Func.OpenCodedDeferDisallowed()
+ s.hasOpenDefers = Debug.N == 0 && s.hasdefer && !s.curfn.Func.OpenCodedDeferDisallowed()
switch {
case s.hasOpenDefers && (Ctxt.Flag_shared || Ctxt.Flag_dynlink) && thearch.LinkArch.Name == "386":
// Don't support open-coded defers for 386 ONLY when using shared
@@ -410,11 +410,17 @@ func buildssa(fn *Node, worker int) *ssa.Func {
// Generate addresses of local declarations
s.decladdrs = map[*Node]*ssa.Value{}
+ var args []ssa.Param
+ var results []ssa.Param
for _, n := range fn.Func.Dcl {
switch n.Class() {
- case PPARAM, PPARAMOUT:
+ case PPARAM:
s.decladdrs[n] = s.entryNewValue2A(ssa.OpLocalAddr, types.NewPtr(n.Type), n, s.sp, s.startmem)
- if n.Class() == PPARAMOUT && s.canSSA(n) {
+ args = append(args, ssa.Param{Type: n.Type, Offset: int32(n.Xoffset)})
+ case PPARAMOUT:
+ s.decladdrs[n] = s.entryNewValue2A(ssa.OpLocalAddr, types.NewPtr(n.Type), n, s.sp, s.startmem)
+ results = append(results, ssa.Param{Type: n.Type, Offset: int32(n.Xoffset)})
+ if s.canSSA(n) {
// Save ssa-able PPARAMOUT variables so we can
// store them back to the stack at the end of
// the function.
@@ -651,6 +657,8 @@ type state struct {
lastDeferExit *ssa.Block // Entry block of last defer exit code we generated
lastDeferFinalBlock *ssa.Block // Final block of last defer exit code we generated
lastDeferCount int // Number of defers encountered at that point
+
+ prevCall *ssa.Value // the previous call; use this to tie results to the call op.
}
type funcLine struct {
@@ -740,7 +748,7 @@ func (s *state) pushLine(line src.XPos) {
// the frontend may emit node with line number missing,
// use the parent line number in this case.
line = s.peekPos()
- if Debug['K'] != 0 {
+ if Debug.K != 0 {
Warn("buildssa: unknown position (line 0)")
}
} else {
@@ -805,6 +813,11 @@ func (s *state) newValue2(op ssa.Op, t *types.Type, arg0, arg1 *ssa.Value) *ssa.
return s.curBlock.NewValue2(s.peekPos(), op, t, arg0, arg1)
}
+// newValue2A adds a new value with two arguments and an aux value to the current block.
+func (s *state) newValue2A(op ssa.Op, t *types.Type, aux interface{}, arg0, arg1 *ssa.Value) *ssa.Value {
+ return s.curBlock.NewValue2A(s.peekPos(), op, t, aux, arg0, arg1)
+}
+
// newValue2Apos adds a new value with two arguments and an aux value to the current block.
// isStmt determines whether the created values may be a statement or not
// (i.e., false means never, yes means maybe).
@@ -954,7 +967,45 @@ func (s *state) newValueOrSfCall2(op ssa.Op, t *types.Type, arg0, arg1 *ssa.Valu
return s.newValue2(op, t, arg0, arg1)
}
-func (s *state) instrument(t *types.Type, addr *ssa.Value, wr bool) {
+type instrumentKind uint8
+
+const (
+ instrumentRead = iota
+ instrumentWrite
+ instrumentMove
+)
+
+func (s *state) instrument(t *types.Type, addr *ssa.Value, kind instrumentKind) {
+ s.instrument2(t, addr, nil, kind)
+}
+
+// instrumentFields instruments a read/write operation on addr.
+// If it is instrumenting for MSAN and t is a struct type, it instruments
+// operation for each field, instead of for the whole struct.
+func (s *state) instrumentFields(t *types.Type, addr *ssa.Value, kind instrumentKind) {
+ if !flag_msan || !t.IsStruct() {
+ s.instrument(t, addr, kind)
+ return
+ }
+ for _, f := range t.Fields().Slice() {
+ if f.Sym.IsBlank() {
+ continue
+ }
+ offptr := s.newValue1I(ssa.OpOffPtr, types.NewPtr(f.Type), f.Offset, addr)
+ s.instrumentFields(f.Type, offptr, kind)
+ }
+}
+
+func (s *state) instrumentMove(t *types.Type, dst, src *ssa.Value) {
+ if flag_msan {
+ s.instrument2(t, dst, src, instrumentMove)
+ } else {
+ s.instrument(t, src, instrumentRead)
+ s.instrument(t, dst, instrumentWrite)
+ }
+}
+
+func (s *state) instrument2(t *types.Type, addr, addr2 *ssa.Value, kind instrumentKind) {
if !s.curfn.Func.InstrumentBody() {
return
}
@@ -971,33 +1022,54 @@ func (s *state) instrument(t *types.Type, addr *ssa.Value, wr bool) {
var fn *obj.LSym
needWidth := false
+ if addr2 != nil && kind != instrumentMove {
+ panic("instrument2: non-nil addr2 for non-move instrumentation")
+ }
+
if flag_msan {
- fn = msanread
- if wr {
+ switch kind {
+ case instrumentRead:
+ fn = msanread
+ case instrumentWrite:
fn = msanwrite
+ case instrumentMove:
+ fn = msanmove
+ default:
+ panic("unreachable")
}
needWidth = true
} else if flag_race && t.NumComponents(types.CountBlankFields) > 1 {
// for composite objects we have to write every address
// because a write might happen to any subobject.
// composites with only one element don't have subobjects, though.
- fn = racereadrange
- if wr {
+ switch kind {
+ case instrumentRead:
+ fn = racereadrange
+ case instrumentWrite:
fn = racewriterange
+ default:
+ panic("unreachable")
}
needWidth = true
} else if flag_race {
// for non-composite objects we can write just the start
// address, as any write must write the first byte.
- fn = raceread
- if wr {
+ switch kind {
+ case instrumentRead:
+ fn = raceread
+ case instrumentWrite:
fn = racewrite
+ default:
+ panic("unreachable")
}
} else {
panic("unreachable")
}
args := []*ssa.Value{addr}
+ if addr2 != nil {
+ args = append(args, addr2)
+ }
if needWidth {
args = append(args, s.constInt(types.Types[TUINTPTR], w))
}
@@ -1005,7 +1077,7 @@ func (s *state) instrument(t *types.Type, addr *ssa.Value, wr bool) {
}
func (s *state) load(t *types.Type, src *ssa.Value) *ssa.Value {
- s.instrument(t, src, false)
+ s.instrumentFields(t, src, instrumentRead)
return s.rawLoad(t, src)
}
@@ -1018,15 +1090,14 @@ func (s *state) store(t *types.Type, dst, val *ssa.Value) {
}
func (s *state) zero(t *types.Type, dst *ssa.Value) {
- s.instrument(t, dst, true)
+ s.instrument(t, dst, instrumentWrite)
store := s.newValue2I(ssa.OpZero, types.TypeMem, t.Size(), dst, s.mem())
store.Aux = t
s.vars[&memVar] = store
}
func (s *state) move(t *types.Type, dst, src *ssa.Value) {
- s.instrument(t, src, false)
- s.instrument(t, dst, true)
+ s.instrumentMove(t, dst, src)
store := s.newValue3I(ssa.OpMove, types.TypeMem, t.Size(), dst, src, s.mem())
store.Aux = t
s.vars[&memVar] = store
@@ -1071,7 +1142,7 @@ func (s *state) stmt(n *Node) {
fallthrough
case OCALLMETH, OCALLINTER:
- s.call(n, callNormal)
+ s.callResult(n, callNormal)
if n.Op == OCALLFUNC && n.Left.Op == ONAME && n.Left.Class() == PFUNC {
if fn := n.Left.Sym.Name; compiling_runtime && fn == "throw" ||
n.Left.Sym.Pkg == Runtimepkg && (fn == "throwinit" || fn == "gopanic" || fn == "panicwrap" || fn == "block" || fn == "panicmakeslicelen" || fn == "panicmakeslicecap") {
@@ -1103,10 +1174,10 @@ func (s *state) stmt(n *Node) {
if n.Esc == EscNever {
d = callDeferStack
}
- s.call(n.Left, d)
+ s.callResult(n.Left, d)
}
case OGO:
- s.call(n.Left, callGo)
+ s.callResult(n.Left, callGo)
case OAS2DOTTYPE:
res, resok := s.dottype(n.Right, true)
@@ -1208,7 +1279,7 @@ func (s *state) stmt(n *Node) {
// Check whether we're writing the result of an append back to the same slice.
// If so, we handle it specially to avoid write barriers on the fast
// (non-growth) path.
- if !samesafeexpr(n.Left, rhs.List.First()) || Debug['N'] != 0 {
+ if !samesafeexpr(n.Left, rhs.List.First()) || Debug.N != 0 {
break
}
// If the slice can be SSA'd, it'll be on the stack,
@@ -1265,7 +1336,7 @@ func (s *state) stmt(n *Node) {
// We're assigning a slicing operation back to its source.
// Don't write back fields we aren't changing. See issue #14855.
i, j, k := rhs.SliceBounds()
- if i != nil && (i.Op == OLITERAL && i.Val().Ctype() == CTINT && i.Int64() == 0) {
+ if i != nil && (i.Op == OLITERAL && i.Val().Ctype() == CTINT && i.Int64Val() == 0) {
// [0:...] is the same as [:...]
i = nil
}
@@ -1295,7 +1366,7 @@ func (s *state) stmt(n *Node) {
case OIF:
if Isconst(n.Left, CTBOOL) {
s.stmtList(n.Left.Ninit)
- if n.Left.Bool() {
+ if n.Left.BoolVal() {
s.stmtList(n.Nbody)
} else {
s.stmtList(n.Rlist)
@@ -2113,7 +2184,7 @@ func (s *state) expr(n *Node) *ssa.Value {
}
// unsafe.Pointer <--> *T
- if to.Etype == TUNSAFEPTR && from.IsPtrShaped() || from.Etype == TUNSAFEPTR && to.IsPtrShaped() {
+ if to.IsUnsafePtr() && from.IsPtrShaped() || from.IsUnsafePtr() && to.IsPtrShaped() {
return v
}
@@ -2466,6 +2537,11 @@ func (s *state) expr(n *Node) *ssa.Value {
a := s.expr(n.Left)
b := s.expr(n.Right)
return s.newValue2(s.ssaOp(n.Op, n.Type), a.Type, a, b)
+ case OANDNOT:
+ a := s.expr(n.Left)
+ b := s.expr(n.Right)
+ b = s.newValue1(s.ssaOp(OBITNOT, b.Type), b.Type, b)
+ return s.newValue2(s.ssaOp(OAND, n.Type), a.Type, a, b)
case OLSH, ORSH:
a := s.expr(n.Left)
b := s.expr(n.Right)
@@ -2549,8 +2625,23 @@ func (s *state) expr(n *Node) *ssa.Value {
return s.addr(n.Left)
case ORESULT:
- addr := s.constOffPtrSP(types.NewPtr(n.Type), n.Xoffset)
- return s.load(n.Type, addr)
+ if s.prevCall == nil || s.prevCall.Op != ssa.OpStaticLECall && s.prevCall.Op != ssa.OpInterLECall && s.prevCall.Op != ssa.OpClosureLECall {
+ // Do the old thing
+ addr := s.constOffPtrSP(types.NewPtr(n.Type), n.Xoffset)
+ return s.rawLoad(n.Type, addr)
+ }
+ which := s.prevCall.Aux.(*ssa.AuxCall).ResultForOffset(n.Xoffset)
+ if which == -1 {
+ // Do the old thing // TODO: Panic instead.
+ addr := s.constOffPtrSP(types.NewPtr(n.Type), n.Xoffset)
+ return s.rawLoad(n.Type, addr)
+ }
+ if canSSAType(n.Type) {
+ return s.newValue1I(ssa.OpSelectN, n.Type, which, s.prevCall)
+ } else {
+ addr := s.newValue1I(ssa.OpSelectNAddr, types.NewPtr(n.Type), which, s.prevCall)
+ return s.rawLoad(n.Type, addr)
+ }
case ODEREF:
p := s.exprPtr(n.Left, n.Bounded(), n.Pos)
@@ -2589,7 +2680,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(strlit(n.Left)[n.Right.Int64()])))
+ return s.newValue0I(ssa.OpConst8, types.Types[TUINT8], int64(int8(n.Left.StringVal()[n.Right.Int64Val()])))
}
a := s.expr(n.Left)
i := s.expr(n.Right)
@@ -2598,7 +2689,7 @@ func (s *state) expr(n *Node) *ssa.Value {
ptrtyp := s.f.Config.Types.BytePtr
ptr := s.newValue1(ssa.OpStringPtr, ptrtyp, a)
if Isconst(n.Right, CTINT) {
- ptr = s.newValue1I(ssa.OpOffPtr, ptrtyp, n.Right.Int64(), ptr)
+ ptr = s.newValue1I(ssa.OpOffPtr, ptrtyp, n.Right.Int64Val(), ptr)
} else {
ptr = s.newValue2(ssa.OpAddPtr, ptrtyp, ptr, i)
}
@@ -2710,8 +2801,7 @@ func (s *state) expr(n *Node) *ssa.Value {
fallthrough
case OCALLINTER, OCALLMETH:
- a := s.call(n, callNormal)
- return s.load(n.Type, a)
+ return s.callResult(n, callNormal)
case OGETG:
return s.newValue1(ssa.OpGetG, n.Type, s.mem())
@@ -3369,6 +3459,13 @@ func init() {
return s.newValue1(ssa.OpSelect0, types.Types[TUINT32], v)
},
sys.PPC64, sys.S390X)
+ addF("runtime/internal/atomic", "LoadAcq64",
+ func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+ v := s.newValue2(ssa.OpAtomicLoadAcq64, types.NewTuple(types.Types[TUINT64], types.TypeMem), args[0], s.mem())
+ s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
+ return s.newValue1(ssa.OpSelect0, types.Types[TUINT64], v)
+ },
+ sys.PPC64)
addF("runtime/internal/atomic", "Loadp",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
v := s.newValue2(ssa.OpAtomicLoadPtr, types.NewTuple(s.f.Config.Types.BytePtr, types.TypeMem), args[0], s.mem())
@@ -3407,6 +3504,12 @@ func init() {
return nil
},
sys.PPC64, sys.S390X)
+ addF("runtime/internal/atomic", "StoreRel64",
+ func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+ s.vars[&memVar] = s.newValue3(ssa.OpAtomicStoreRel64, types.TypeMem, args[0], args[1], s.mem())
+ return nil
+ },
+ sys.PPC64)
addF("runtime/internal/atomic", "Xchg",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
@@ -3414,31 +3517,19 @@ func init() {
s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
return s.newValue1(ssa.OpSelect0, types.Types[TUINT32], v)
},
- sys.AMD64, sys.ARM64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
+ sys.AMD64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
addF("runtime/internal/atomic", "Xchg64",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
v := s.newValue3(ssa.OpAtomicExchange64, types.NewTuple(types.Types[TUINT64], types.TypeMem), args[0], args[1], s.mem())
s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
return s.newValue1(ssa.OpSelect0, types.Types[TUINT64], v)
},
- sys.AMD64, sys.ARM64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
-
- addF("runtime/internal/atomic", "Xadd",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- v := s.newValue3(ssa.OpAtomicAdd32, types.NewTuple(types.Types[TUINT32], types.TypeMem), args[0], args[1], s.mem())
- s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
- return s.newValue1(ssa.OpSelect0, types.Types[TUINT32], v)
- },
- sys.AMD64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
- addF("runtime/internal/atomic", "Xadd64",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- v := s.newValue3(ssa.OpAtomicAdd64, types.NewTuple(types.Types[TUINT64], types.TypeMem), args[0], args[1], s.mem())
- s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
- return s.newValue1(ssa.OpSelect0, types.Types[TUINT64], v)
- },
sys.AMD64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
- makeXaddARM64 := func(op0 ssa.Op, op1 ssa.Op, ty types.EType) func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+ type atomicOpEmitter func(s *state, n *Node, args []*ssa.Value, op ssa.Op, typ types.EType)
+
+ makeAtomicGuardedIntrinsicARM64 := func(op0, op1 ssa.Op, typ, rtyp types.EType, emit atomicOpEmitter) intrinsicBuilder {
+
return func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
// Target Atomic feature is identified by dynamic detection
addr := s.entryNewValue1A(ssa.OpAddr, types.Types[TBOOL].PtrTo(), arm64HasATOMICS, s.sb)
@@ -3451,33 +3542,60 @@ func init() {
bEnd := s.f.NewBlock(ssa.BlockPlain)
b.AddEdgeTo(bTrue)
b.AddEdgeTo(bFalse)
- b.Likely = ssa.BranchUnlikely // most machines don't have Atomics nowadays
+ b.Likely = ssa.BranchLikely
// We have atomic instructions - use it directly.
s.startBlock(bTrue)
- v0 := s.newValue3(op1, types.NewTuple(types.Types[ty], types.TypeMem), args[0], args[1], s.mem())
- s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v0)
- s.vars[n] = s.newValue1(ssa.OpSelect0, types.Types[ty], v0)
+ emit(s, n, args, op1, typ)
s.endBlock().AddEdgeTo(bEnd)
// Use original instruction sequence.
s.startBlock(bFalse)
- v1 := s.newValue3(op0, types.NewTuple(types.Types[ty], types.TypeMem), args[0], args[1], s.mem())
- s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v1)
- s.vars[n] = s.newValue1(ssa.OpSelect0, types.Types[ty], v1)
+ emit(s, n, args, op0, typ)
s.endBlock().AddEdgeTo(bEnd)
// Merge results.
s.startBlock(bEnd)
- return s.variable(n, types.Types[ty])
+ if rtyp == TNIL {
+ return nil
+ } else {
+ return s.variable(n, types.Types[rtyp])
+ }
}
}
+ atomicXchgXaddEmitterARM64 := func(s *state, n *Node, args []*ssa.Value, op ssa.Op, typ types.EType) {
+ v := s.newValue3(op, types.NewTuple(types.Types[typ], types.TypeMem), args[0], args[1], s.mem())
+ s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
+ s.vars[n] = s.newValue1(ssa.OpSelect0, types.Types[typ], v)
+ }
+ addF("runtime/internal/atomic", "Xchg",
+ makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicExchange32, ssa.OpAtomicExchange32Variant, TUINT32, TUINT32, atomicXchgXaddEmitterARM64),
+ sys.ARM64)
+ addF("runtime/internal/atomic", "Xchg64",
+ makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicExchange64, ssa.OpAtomicExchange64Variant, TUINT64, TUINT64, atomicXchgXaddEmitterARM64),
+ sys.ARM64)
+
addF("runtime/internal/atomic", "Xadd",
- makeXaddARM64(ssa.OpAtomicAdd32, ssa.OpAtomicAdd32Variant, TUINT32),
+ func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+ v := s.newValue3(ssa.OpAtomicAdd32, types.NewTuple(types.Types[TUINT32], types.TypeMem), args[0], args[1], s.mem())
+ s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
+ return s.newValue1(ssa.OpSelect0, types.Types[TUINT32], v)
+ },
+ sys.AMD64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
+ addF("runtime/internal/atomic", "Xadd64",
+ func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+ v := s.newValue3(ssa.OpAtomicAdd64, types.NewTuple(types.Types[TUINT64], types.TypeMem), args[0], args[1], s.mem())
+ s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
+ return s.newValue1(ssa.OpSelect0, types.Types[TUINT64], v)
+ },
+ sys.AMD64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
+
+ addF("runtime/internal/atomic", "Xadd",
+ makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicAdd32, ssa.OpAtomicAdd32Variant, TUINT32, TUINT32, atomicXchgXaddEmitterARM64),
sys.ARM64)
addF("runtime/internal/atomic", "Xadd64",
- makeXaddARM64(ssa.OpAtomicAdd64, ssa.OpAtomicAdd64Variant, TUINT64),
+ makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicAdd64, ssa.OpAtomicAdd64Variant, TUINT64, TUINT64, atomicXchgXaddEmitterARM64),
sys.ARM64)
addF("runtime/internal/atomic", "Cas",
@@ -3486,14 +3604,14 @@ func init() {
s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
return s.newValue1(ssa.OpSelect0, types.Types[TBOOL], v)
},
- sys.AMD64, sys.ARM64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
+ sys.AMD64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
addF("runtime/internal/atomic", "Cas64",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
v := s.newValue4(ssa.OpAtomicCompareAndSwap64, types.NewTuple(types.Types[TBOOL], types.TypeMem), args[0], args[1], args[2], s.mem())
s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
return s.newValue1(ssa.OpSelect0, types.Types[TBOOL], v)
},
- sys.AMD64, sys.ARM64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
+ sys.AMD64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
addF("runtime/internal/atomic", "CasRel",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
v := s.newValue4(ssa.OpAtomicCompareAndSwap32, types.NewTuple(types.Types[TBOOL], types.TypeMem), args[0], args[1], args[2], s.mem())
@@ -3502,18 +3620,60 @@ func init() {
},
sys.PPC64)
+ atomicCasEmitterARM64 := func(s *state, n *Node, args []*ssa.Value, op ssa.Op, typ types.EType) {
+ v := s.newValue4(op, types.NewTuple(types.Types[TBOOL], types.TypeMem), args[0], args[1], args[2], s.mem())
+ s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
+ s.vars[n] = s.newValue1(ssa.OpSelect0, types.Types[typ], v)
+ }
+
+ addF("runtime/internal/atomic", "Cas",
+ makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicCompareAndSwap32, ssa.OpAtomicCompareAndSwap32Variant, TUINT32, TBOOL, atomicCasEmitterARM64),
+ sys.ARM64)
+ addF("runtime/internal/atomic", "Cas64",
+ makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicCompareAndSwap64, ssa.OpAtomicCompareAndSwap64Variant, TUINT64, TBOOL, atomicCasEmitterARM64),
+ sys.ARM64)
+
addF("runtime/internal/atomic", "And8",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
s.vars[&memVar] = s.newValue3(ssa.OpAtomicAnd8, types.TypeMem, args[0], args[1], s.mem())
return nil
},
- sys.AMD64, sys.ARM64, sys.MIPS, sys.PPC64, sys.S390X)
+ sys.AMD64, sys.MIPS, sys.PPC64, sys.S390X)
+ addF("runtime/internal/atomic", "And",
+ func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+ s.vars[&memVar] = s.newValue3(ssa.OpAtomicAnd32, types.TypeMem, args[0], args[1], s.mem())
+ return nil
+ },
+ sys.AMD64, sys.MIPS, sys.PPC64, sys.S390X)
addF("runtime/internal/atomic", "Or8",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
s.vars[&memVar] = s.newValue3(ssa.OpAtomicOr8, types.TypeMem, args[0], args[1], s.mem())
return nil
},
sys.AMD64, sys.ARM64, sys.MIPS, sys.PPC64, sys.S390X)
+ addF("runtime/internal/atomic", "Or",
+ func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+ s.vars[&memVar] = s.newValue3(ssa.OpAtomicOr32, types.TypeMem, args[0], args[1], s.mem())
+ return nil
+ },
+ sys.AMD64, sys.MIPS, sys.PPC64, sys.S390X)
+
+ atomicAndOrEmitterARM64 := func(s *state, n *Node, args []*ssa.Value, op ssa.Op, typ types.EType) {
+ s.vars[&memVar] = s.newValue3(op, types.TypeMem, args[0], args[1], s.mem())
+ }
+
+ addF("runtime/internal/atomic", "And8",
+ makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicAnd8, ssa.OpAtomicAnd8Variant, TNIL, TNIL, atomicAndOrEmitterARM64),
+ sys.ARM64)
+ addF("runtime/internal/atomic", "And",
+ makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicAnd32, ssa.OpAtomicAnd32Variant, TNIL, TNIL, atomicAndOrEmitterARM64),
+ sys.ARM64)
+ addF("runtime/internal/atomic", "Or8",
+ makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicOr8, ssa.OpAtomicOr8Variant, TNIL, TNIL, atomicAndOrEmitterARM64),
+ sys.ARM64)
+ addF("runtime/internal/atomic", "Or",
+ makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicOr32, ssa.OpAtomicOr32Variant, TNIL, TNIL, atomicAndOrEmitterARM64),
+ sys.ARM64)
alias("runtime/internal/atomic", "Loadint64", "runtime/internal/atomic", "Load64", all...)
alias("runtime/internal/atomic", "Xaddint64", "runtime/internal/atomic", "Xadd64", all...)
@@ -3522,9 +3682,19 @@ func init() {
alias("runtime/internal/atomic", "Loaduintptr", "runtime/internal/atomic", "Load", p4...)
alias("runtime/internal/atomic", "Loaduintptr", "runtime/internal/atomic", "Load64", p8...)
alias("runtime/internal/atomic", "LoadAcq", "runtime/internal/atomic", "Load", lwatomics...)
+ alias("runtime/internal/atomic", "LoadAcq64", "runtime/internal/atomic", "Load64", lwatomics...)
+ alias("runtime/internal/atomic", "LoadAcquintptr", "runtime/internal/atomic", "LoadAcq", p4...)
+ alias("sync", "runtime_LoadAcquintptr", "runtime/internal/atomic", "LoadAcq", p4...) // linknamed
+ alias("runtime/internal/atomic", "LoadAcquintptr", "runtime/internal/atomic", "LoadAcq64", p8...)
+ alias("sync", "runtime_LoadAcquintptr", "runtime/internal/atomic", "LoadAcq64", p8...) // linknamed
alias("runtime/internal/atomic", "Storeuintptr", "runtime/internal/atomic", "Store", p4...)
alias("runtime/internal/atomic", "Storeuintptr", "runtime/internal/atomic", "Store64", p8...)
alias("runtime/internal/atomic", "StoreRel", "runtime/internal/atomic", "Store", lwatomics...)
+ alias("runtime/internal/atomic", "StoreRel64", "runtime/internal/atomic", "Store64", lwatomics...)
+ alias("runtime/internal/atomic", "StoreReluintptr", "runtime/internal/atomic", "StoreRel", p4...)
+ alias("sync", "runtime_StoreReluintptr", "runtime/internal/atomic", "StoreRel", p4...) // linknamed
+ alias("runtime/internal/atomic", "StoreReluintptr", "runtime/internal/atomic", "StoreRel64", p8...)
+ alias("sync", "runtime_StoreReluintptr", "runtime/internal/atomic", "StoreRel64", p8...) // linknamed
alias("runtime/internal/atomic", "Xchguintptr", "runtime/internal/atomic", "Xchg", p4...)
alias("runtime/internal/atomic", "Xchguintptr", "runtime/internal/atomic", "Xchg64", p8...)
alias("runtime/internal/atomic", "Xadduintptr", "runtime/internal/atomic", "Xadd", p4...)
@@ -3584,8 +3754,7 @@ func init() {
addF("math", "FMA",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
if !s.config.UseFMA {
- a := s.call(n, callNormal)
- s.vars[n] = s.load(types.Types[TFLOAT64], a)
+ s.vars[n] = s.callResult(n, callNormal) // types.Types[TFLOAT64]
return s.variable(n, types.Types[TFLOAT64])
}
v := s.entryNewValue0A(ssa.OpHasCPUFeature, types.Types[TBOOL], x86HasFMA)
@@ -3606,8 +3775,7 @@ func init() {
// Call the pure Go version.
s.startBlock(bFalse)
- a := s.call(n, callNormal)
- s.vars[n] = s.load(types.Types[TFLOAT64], a)
+ s.vars[n] = s.callResult(n, callNormal) // types.Types[TFLOAT64]
s.endBlock().AddEdgeTo(bEnd)
// Merge results.
@@ -3618,8 +3786,7 @@ func init() {
addF("math", "FMA",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
if !s.config.UseFMA {
- a := s.call(n, callNormal)
- s.vars[n] = s.load(types.Types[TFLOAT64], a)
+ s.vars[n] = s.callResult(n, callNormal) // types.Types[TFLOAT64]
return s.variable(n, types.Types[TFLOAT64])
}
addr := s.entryNewValue1A(ssa.OpAddr, types.Types[TBOOL].PtrTo(), armHasVFPv4, s.sb)
@@ -3641,8 +3808,7 @@ func init() {
// Call the pure Go version.
s.startBlock(bFalse)
- a := s.call(n, callNormal)
- s.vars[n] = s.load(types.Types[TFLOAT64], a)
+ s.vars[n] = s.callResult(n, callNormal) // types.Types[TFLOAT64]
s.endBlock().AddEdgeTo(bEnd)
// Merge results.
@@ -3671,8 +3837,7 @@ func init() {
// Call the pure Go version.
s.startBlock(bFalse)
- a := s.call(n, callNormal)
- s.vars[n] = s.load(types.Types[TFLOAT64], a)
+ s.vars[n] = s.callResult(n, callNormal) // types.Types[TFLOAT64]
s.endBlock().AddEdgeTo(bEnd)
// Merge results.
@@ -3882,8 +4047,7 @@ func init() {
// Call the pure Go version.
s.startBlock(bFalse)
- a := s.call(n, callNormal)
- s.vars[n] = s.load(types.Types[TINT], a)
+ s.vars[n] = s.callResult(n, callNormal) // types.Types[TINT]
s.endBlock().AddEdgeTo(bEnd)
// Merge results.
@@ -4006,11 +4170,6 @@ func init() {
return s.newValue2(ssa.OpMul64uhilo, types.NewTuple(types.Types[TUINT64], types.Types[TUINT64]), args[0], args[1])
},
sys.ArchAMD64, sys.ArchARM64, sys.ArchPPC64LE, sys.ArchPPC64, sys.ArchS390X)
- add("math/big", "divWW",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue3(ssa.OpDiv128u, types.NewTuple(types.Types[TUINT64], types.Types[TUINT64]), args[0], args[1], args[2])
- },
- sys.ArchAMD64)
}
// findIntrinsic returns a function which builds the SSA equivalent of the
@@ -4240,6 +4399,7 @@ func (s *state) openDeferExit() {
s.lastDeferExit = deferExit
s.lastDeferCount = len(s.openDefers)
zeroval := s.constInt8(types.Types[TUINT8], 0)
+ testLateExpansion := ssa.LateCallExpansionEnabledWithin(s.f)
// Test for and run defers in reverse order
for i := len(s.openDefers) - 1; i >= 0; i-- {
r := s.openDefers[i]
@@ -4275,21 +4435,40 @@ func (s *state) openDeferExit() {
argStart := Ctxt.FixedFrameSize()
fn := r.n.Left
stksize := fn.Type.ArgWidth()
+ var ACArgs []ssa.Param
+ var ACResults []ssa.Param
+ var callArgs []*ssa.Value
if r.rcvr != nil {
// rcvr in case of OCALLINTER
v := s.load(r.rcvr.Type.Elem(), r.rcvr)
addr := s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart)
- s.store(types.Types[TUINTPTR], addr, v)
+ ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(argStart)})
+ if testLateExpansion {
+ callArgs = append(callArgs, v)
+ } else {
+ s.store(types.Types[TUINTPTR], addr, v)
+ }
}
for j, argAddrVal := range r.argVals {
f := getParam(r.n, j)
pt := types.NewPtr(f.Type)
- addr := s.constOffPtrSP(pt, argStart+f.Offset)
- if !canSSAType(f.Type) {
- s.move(f.Type, addr, argAddrVal)
+ ACArgs = append(ACArgs, ssa.Param{Type: f.Type, Offset: int32(argStart + f.Offset)})
+ if testLateExpansion {
+ var a *ssa.Value
+ if !canSSAType(f.Type) {
+ a = s.newValue2(ssa.OpDereference, f.Type, argAddrVal, s.mem())
+ } else {
+ a = s.load(f.Type, argAddrVal)
+ }
+ callArgs = append(callArgs, a)
} else {
- argVal := s.load(f.Type, argAddrVal)
- s.storeType(f.Type, addr, argVal, 0, false)
+ addr := s.constOffPtrSP(pt, argStart+f.Offset)
+ if !canSSAType(f.Type) {
+ s.move(f.Type, addr, argAddrVal)
+ } else {
+ argVal := s.load(f.Type, argAddrVal)
+ s.storeType(f.Type, addr, argVal, 0, false)
+ }
}
}
var call *ssa.Value
@@ -4297,13 +4476,31 @@ func (s *state) openDeferExit() {
v := s.load(r.closure.Type.Elem(), r.closure)
s.maybeNilCheckClosure(v, callDefer)
codeptr := s.rawLoad(types.Types[TUINTPTR], v)
- call = s.newValue3(ssa.OpClosureCall, types.TypeMem, codeptr, v, s.mem())
+ aux := ssa.ClosureAuxCall(ACArgs, ACResults)
+ if testLateExpansion {
+ callArgs = append(callArgs, s.mem())
+ call = s.newValue2A(ssa.OpClosureLECall, aux.LateExpansionResultType(), aux, codeptr, v)
+ call.AddArgs(callArgs...)
+ } else {
+ call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, aux, codeptr, v, s.mem())
+ }
} else {
- // Do a static call if the original call was a static function or method
- call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, fn.Sym.Linksym(), s.mem())
+ aux := ssa.StaticAuxCall(fn.Sym.Linksym(), ACArgs, ACResults)
+ if testLateExpansion {
+ callArgs = append(callArgs, s.mem())
+ call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
+ call.AddArgs(callArgs...)
+ } else {
+ // Do a static call if the original call was a static function or method
+ call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, aux, s.mem())
+ }
}
call.AuxInt = stksize
- s.vars[&memVar] = call
+ if testLateExpansion {
+ s.vars[&memVar] = s.newValue1I(ssa.OpSelectN, types.TypeMem, int64(len(ACResults)), call)
+ } else {
+ s.vars[&memVar] = call
+ }
// Make sure that the stack slots with pointers are kept live
// through the call (which is a pre-emption point). Also, we will
// use the first call of the last defer exit to compute liveness
@@ -4327,16 +4524,40 @@ func (s *state) openDeferExit() {
}
}
+func (s *state) callResult(n *Node, k callKind) *ssa.Value {
+ return s.call(n, k, false)
+}
+
+func (s *state) callAddr(n *Node, k callKind) *ssa.Value {
+ return s.call(n, k, true)
+}
+
// Calls the function n using the specified call type.
// Returns the address of the return value (or nil if none).
-func (s *state) call(n *Node, k callKind) *ssa.Value {
+func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value {
+ s.prevCall = nil
var sym *types.Sym // target symbol (if static)
var closure *ssa.Value // ptr to closure to run (if dynamic)
var codeptr *ssa.Value // ptr to target code (if dynamic)
var rcvr *ssa.Value // receiver to set
fn := n.Left
+ var ACArgs []ssa.Param
+ var ACResults []ssa.Param
+ var callArgs []*ssa.Value
+ res := n.Left.Type.Results()
+ if k == callNormal {
+ nf := res.NumFields()
+ for i := 0; i < nf; i++ {
+ fp := res.Field(i)
+ ACResults = append(ACResults, ssa.Param{Type: fp.Type, Offset: int32(fp.Offset + Ctxt.FixedFrameSize())})
+ }
+ }
+
+ testLateExpansion := false
+
switch n.Op {
case OCALLFUNC:
+ testLateExpansion = k != callDeferStack && ssa.LateCallExpansionEnabledWithin(s.f)
if k == callNormal && fn.Op == ONAME && fn.Class() == PFUNC {
sym = fn.Sym
break
@@ -4351,6 +4572,7 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
if fn.Op != ODOTMETH {
s.Fatalf("OCALLMETH: n.Left not an ODOTMETH: %v", fn)
}
+ testLateExpansion = k != callDeferStack && ssa.LateCallExpansionEnabledWithin(s.f)
if k == callNormal {
sym = fn.Sym
break
@@ -4362,6 +4584,7 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
if fn.Op != ODOTINTER {
s.Fatalf("OCALLINTER: n.Left not an ODOTINTER: %v", fn.Op)
}
+ testLateExpansion = k != callDeferStack && ssa.LateCallExpansionEnabledWithin(s.f)
var iclosure *ssa.Value
iclosure, rcvr = s.getClosureAndRcvr(fn)
if k == callNormal {
@@ -4380,6 +4603,7 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
var call *ssa.Value
if k == callDeferStack {
+ testLateExpansion = ssa.LateCallExpansionEnabledWithin(s.f)
// Make a defer struct d on the stack.
t := deferstruct(stksize)
d := tempAt(n.Pos, s.curfn, t)
@@ -4430,12 +4654,21 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
}
// Call runtime.deferprocStack with pointer to _defer record.
- arg0 := s.constOffPtrSP(types.Types[TUINTPTR], Ctxt.FixedFrameSize())
- s.store(types.Types[TUINTPTR], arg0, addr)
- call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, deferprocStack, s.mem())
+ ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(Ctxt.FixedFrameSize())})
+ aux := ssa.StaticAuxCall(deferprocStack, ACArgs, ACResults)
+ if testLateExpansion {
+ callArgs = append(callArgs, addr, s.mem())
+ call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
+ call.AddArgs(callArgs...)
+ } else {
+ arg0 := s.constOffPtrSP(types.Types[TUINTPTR], Ctxt.FixedFrameSize())
+ s.store(types.Types[TUINTPTR], arg0, addr)
+ call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, aux, s.mem())
+ }
if stksize < int64(Widthptr) {
// We need room for both the call to deferprocStack and the call to
// the deferred function.
+ // TODO Revisit this if/when we pass args in registers.
stksize = int64(Widthptr)
}
call.AuxInt = stksize
@@ -4447,10 +4680,20 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
if k != callNormal {
// Write argsize and closure (args to newproc/deferproc).
argsize := s.constInt32(types.Types[TUINT32], int32(stksize))
- addr := s.constOffPtrSP(s.f.Config.Types.UInt32Ptr, argStart)
- s.store(types.Types[TUINT32], addr, argsize)
- addr = s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart+int64(Widthptr))
- s.store(types.Types[TUINTPTR], addr, closure)
+ ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINT32], Offset: int32(argStart)})
+ if testLateExpansion {
+ callArgs = append(callArgs, argsize)
+ } else {
+ addr := s.constOffPtrSP(s.f.Config.Types.UInt32Ptr, argStart)
+ s.store(types.Types[TUINT32], addr, argsize)
+ }
+ ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(argStart) + int32(Widthptr)})
+ if testLateExpansion {
+ callArgs = append(callArgs, closure)
+ } else {
+ addr := s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart+int64(Widthptr))
+ s.store(types.Types[TUINTPTR], addr, closure)
+ }
stksize += 2 * int64(Widthptr)
argStart += 2 * int64(Widthptr)
}
@@ -4458,7 +4701,12 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
// Set receiver (for interface calls).
if rcvr != nil {
addr := s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart)
- s.store(types.Types[TUINTPTR], addr, rcvr)
+ ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(argStart)})
+ if testLateExpansion {
+ callArgs = append(callArgs, rcvr)
+ } else {
+ s.store(types.Types[TUINTPTR], addr, rcvr)
+ }
}
// Write args.
@@ -4466,20 +4714,38 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
args := n.Rlist.Slice()
if n.Op == OCALLMETH {
f := t.Recv()
- s.storeArg(args[0], f.Type, argStart+f.Offset)
+ ACArg, arg := s.putArg(args[0], f.Type, argStart+f.Offset, testLateExpansion)
+ ACArgs = append(ACArgs, ACArg)
+ callArgs = append(callArgs, arg)
args = args[1:]
}
for i, n := range args {
f := t.Params().Field(i)
- s.storeArg(n, f.Type, argStart+f.Offset)
+ ACArg, arg := s.putArg(n, f.Type, argStart+f.Offset, testLateExpansion)
+ ACArgs = append(ACArgs, ACArg)
+ callArgs = append(callArgs, arg)
}
+ callArgs = append(callArgs, s.mem())
+
// call target
switch {
case k == callDefer:
- call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, deferproc, s.mem())
+ aux := ssa.StaticAuxCall(deferproc, ACArgs, ACResults)
+ if testLateExpansion {
+ call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
+ call.AddArgs(callArgs...)
+ } else {
+ call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, aux, s.mem())
+ }
case k == callGo:
- call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, newproc, s.mem())
+ aux := ssa.StaticAuxCall(newproc, ACArgs, ACResults)
+ if testLateExpansion {
+ call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
+ call.AddArgs(callArgs...)
+ } else {
+ call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, aux, s.mem())
+ }
case closure != nil:
// rawLoad because loading the code pointer from a
// closure is always safe, but IsSanitizerSafeAddr
@@ -4487,17 +4753,42 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
// critical that we not clobber any arguments already
// stored onto the stack.
codeptr = s.rawLoad(types.Types[TUINTPTR], closure)
- call = s.newValue3(ssa.OpClosureCall, types.TypeMem, codeptr, closure, s.mem())
+ if testLateExpansion {
+ aux := ssa.ClosureAuxCall(ACArgs, ACResults)
+ call = s.newValue2A(ssa.OpClosureLECall, aux.LateExpansionResultType(), aux, codeptr, closure)
+ call.AddArgs(callArgs...)
+ } else {
+ call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, ssa.ClosureAuxCall(ACArgs, ACResults), codeptr, closure, s.mem())
+ }
case codeptr != nil:
- call = s.newValue2(ssa.OpInterCall, types.TypeMem, codeptr, s.mem())
+ if testLateExpansion {
+ aux := ssa.InterfaceAuxCall(ACArgs, ACResults)
+ call = s.newValue1A(ssa.OpInterLECall, aux.LateExpansionResultType(), aux, codeptr)
+ call.AddArgs(callArgs...)
+ } else {
+ call = s.newValue2A(ssa.OpInterCall, types.TypeMem, ssa.InterfaceAuxCall(ACArgs, ACResults), codeptr, s.mem())
+ }
case sym != nil:
- call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, sym.Linksym(), s.mem())
+ if testLateExpansion {
+ aux := ssa.StaticAuxCall(sym.Linksym(), ACArgs, ACResults)
+ call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
+ call.AddArgs(callArgs...)
+ } else {
+ call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(sym.Linksym(), ACArgs, ACResults), s.mem())
+ }
default:
s.Fatalf("bad call type %v %v", n.Op, n)
}
call.AuxInt = stksize // Call operations carry the argsize of the callee along with them
}
- s.vars[&memVar] = call
+ if testLateExpansion {
+ s.prevCall = call
+ s.vars[&memVar] = s.newValue1I(ssa.OpSelectN, types.TypeMem, int64(len(ACResults)), call)
+ } else {
+ s.vars[&memVar] = call
+ }
+ // Insert OVARLIVE nodes
+ s.stmtList(n.Nbody)
// Finish block for defers
if k == callDefer || k == callDeferStack {
@@ -4515,13 +4806,23 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
s.startBlock(bNext)
}
- res := n.Left.Type.Results()
if res.NumFields() == 0 || k != callNormal {
// call has no return value. Continue with the next statement.
return nil
}
fp := res.Field(0)
- return s.constOffPtrSP(types.NewPtr(fp.Type), fp.Offset+Ctxt.FixedFrameSize())
+ if returnResultAddr {
+ pt := types.NewPtr(fp.Type)
+ if testLateExpansion {
+ return s.newValue1I(ssa.OpSelectNAddr, pt, 0, call)
+ }
+ return s.constOffPtrSP(pt, fp.Offset+Ctxt.FixedFrameSize())
+ }
+
+ if testLateExpansion {
+ return s.newValue1I(ssa.OpSelectN, fp.Type, 0, call)
+ }
+ return s.load(n.Type, s.constOffPtrSP(types.NewPtr(fp.Type), fp.Offset+Ctxt.FixedFrameSize()))
}
// maybeNilCheckClosure checks if a nil check of a closure is needed in some
@@ -4558,7 +4859,7 @@ func (s *state) getClosureAndRcvr(fn *Node) (*ssa.Value, *ssa.Value) {
s.nilCheck(itab)
itabidx := fn.Xoffset + 2*int64(Widthptr) + 8 // offset of fun field in runtime.itab
closure := s.newValue1I(ssa.OpOffPtr, s.f.Config.Types.UintptrPtr, itabidx, itab)
- rcvr := s.newValue1(ssa.OpIData, types.Types[TUINTPTR], i)
+ rcvr := s.newValue1(ssa.OpIData, s.f.Config.Types.BytePtr, i)
return closure, rcvr
}
@@ -4619,7 +4920,17 @@ func (s *state) addr(n *Node) *ssa.Value {
}
case ORESULT:
// load return from callee
- return s.constOffPtrSP(t, n.Xoffset)
+ if s.prevCall == nil || s.prevCall.Op != ssa.OpStaticLECall && s.prevCall.Op != ssa.OpInterLECall && s.prevCall.Op != ssa.OpClosureLECall {
+ return s.constOffPtrSP(t, n.Xoffset)
+ }
+ which := s.prevCall.Aux.(*ssa.AuxCall).ResultForOffset(n.Xoffset)
+ if which == -1 {
+ // Do the old thing // TODO: Panic instead.
+ return s.constOffPtrSP(t, n.Xoffset)
+ }
+ x := s.newValue1I(ssa.OpSelectNAddr, t, which, s.prevCall)
+ return x
+
case OINDEX:
if n.Left.Type.IsSlice() {
a := s.expr(n.Left)
@@ -4650,7 +4961,7 @@ func (s *state) addr(n *Node) *ssa.Value {
addr := s.addr(n.Left)
return s.newValue1(ssa.OpCopy, t, addr) // ensure that addr has the right type
case OCALLFUNC, OCALLINTER, OCALLMETH:
- return s.call(n, callNormal)
+ return s.callAddr(n, callNormal)
case ODOTTYPE:
v, _ := s.dottype(n, false)
if v.Op != ssa.OpLoad {
@@ -4669,7 +4980,7 @@ func (s *state) addr(n *Node) *ssa.Value {
// canSSA reports whether n is SSA-able.
// n must be an ONAME (or an ODOT sequence with an ONAME base).
func (s *state) canSSA(n *Node) bool {
- if Debug['N'] != 0 {
+ if Debug.N != 0 {
return false
}
for n.Op == ODOT || (n.Op == OINDEX && n.Left.Type.IsArray()) {
@@ -4708,7 +5019,7 @@ func (s *state) canSSA(n *Node) bool {
if n.Class() == PPARAM && n.Sym != nil && n.Sym.Name == ".this" {
// wrappers generated by genwrapper need to update
// the .this pointer in place.
- // TODO: treat as a PPARMOUT?
+ // TODO: treat as a PPARAMOUT?
return false
}
return canSSAType(n.Type)
@@ -4780,7 +5091,7 @@ func (s *state) nilCheck(ptr *ssa.Value) {
func (s *state) boundsCheck(idx, len *ssa.Value, kind ssa.BoundsKind, bounded bool) *ssa.Value {
idx = s.extendIndex(idx, len, kind, bounded)
- if bounded || Debug['B'] != 0 {
+ if bounded || Debug.B != 0 {
// If bounded or bounds checking is flag-disabled, then no check necessary,
// just return the extended index.
//
@@ -4909,21 +5220,49 @@ func (s *state) intDivide(n *Node, a, b *ssa.Value) *ssa.Value {
// The call is added to the end of the current block.
// If returns is false, the block is marked as an exit block.
func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args ...*ssa.Value) []*ssa.Value {
+ s.prevCall = nil
// Write args to the stack
off := Ctxt.FixedFrameSize()
+ testLateExpansion := ssa.LateCallExpansionEnabledWithin(s.f)
+ var ACArgs []ssa.Param
+ var ACResults []ssa.Param
+ var callArgs []*ssa.Value
+
for _, arg := range args {
t := arg.Type
off = Rnd(off, t.Alignment())
- ptr := s.constOffPtrSP(t.PtrTo(), off)
size := t.Size()
- s.store(t, ptr, arg)
+ ACArgs = append(ACArgs, ssa.Param{Type: t, Offset: int32(off)})
+ if testLateExpansion {
+ callArgs = append(callArgs, arg)
+ } else {
+ ptr := s.constOffPtrSP(t.PtrTo(), off)
+ s.store(t, ptr, arg)
+ }
off += size
}
off = Rnd(off, int64(Widthreg))
+ // Accumulate results types and offsets
+ offR := off
+ for _, t := range results {
+ offR = Rnd(offR, t.Alignment())
+ ACResults = append(ACResults, ssa.Param{Type: t, Offset: int32(offR)})
+ offR += t.Size()
+ }
+
// Issue call
- call := s.newValue1A(ssa.OpStaticCall, types.TypeMem, fn, s.mem())
- s.vars[&memVar] = call
+ var call *ssa.Value
+ aux := ssa.StaticAuxCall(fn, ACArgs, ACResults)
+ if testLateExpansion {
+ callArgs = append(callArgs, s.mem())
+ call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
+ call.AddArgs(callArgs...)
+ s.vars[&memVar] = s.newValue1I(ssa.OpSelectN, types.TypeMem, int64(len(ACResults)), call)
+ } else {
+ call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, aux, s.mem())
+ s.vars[&memVar] = call
+ }
if !returns {
// Finish block
@@ -4939,11 +5278,24 @@ func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args .
// Load results
res := make([]*ssa.Value, len(results))
- for i, t := range results {
- off = Rnd(off, t.Alignment())
- ptr := s.constOffPtrSP(types.NewPtr(t), off)
- res[i] = s.load(t, ptr)
- off += t.Size()
+ if testLateExpansion {
+ for i, t := range results {
+ off = Rnd(off, t.Alignment())
+ if canSSAType(t) {
+ res[i] = s.newValue1I(ssa.OpSelectN, t, int64(i), call)
+ } else {
+ addr := s.newValue1I(ssa.OpSelectNAddr, types.NewPtr(t), int64(i), call)
+ res[i] = s.rawLoad(t, addr)
+ }
+ off += t.Size()
+ }
+ } else {
+ for i, t := range results {
+ off = Rnd(off, t.Alignment())
+ ptr := s.constOffPtrSP(types.NewPtr(t), off)
+ res[i] = s.load(t, ptr)
+ off += t.Size()
+ }
}
off = Rnd(off, int64(Widthptr))
@@ -4955,7 +5307,7 @@ func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args .
// do *left = right for type t.
func (s *state) storeType(t *types.Type, left, right *ssa.Value, skip skipMask, leftIsStmt bool) {
- s.instrument(t, left, true)
+ s.instrument(t, left, instrumentWrite)
if skip == 0 && (!t.HasPointers() || ssa.IsStackAddr(left)) {
// Known to not have write barrier. Store the whole type.
@@ -4980,7 +5332,10 @@ func (s *state) storeTypeScalars(t *types.Type, left, right *ssa.Value, skip ski
case t.IsBoolean() || t.IsInteger() || t.IsFloat() || t.IsComplex():
s.store(t, left, right)
case t.IsPtrShaped():
- // no scalar fields.
+ if t.IsPtr() && t.Elem().NotInHeap() {
+ s.store(t, left, right) // see issue 42032
+ }
+ // otherwise, no scalar fields.
case t.IsString():
if skip&skipLen != 0 {
return
@@ -5024,6 +5379,9 @@ func (s *state) storeTypeScalars(t *types.Type, left, right *ssa.Value, skip ski
func (s *state) storeTypePtrs(t *types.Type, left, right *ssa.Value) {
switch {
case t.IsPtrShaped():
+ if t.IsPtr() && t.Elem().NotInHeap() {
+ break // see issue 42032
+ }
s.store(t, left, right)
case t.IsString():
ptr := s.newValue1(ssa.OpStringPtr, s.f.Config.Types.BytePtr, right)
@@ -5057,8 +5415,21 @@ func (s *state) storeTypePtrs(t *types.Type, left, right *ssa.Value) {
}
}
-func (s *state) storeArg(n *Node, t *types.Type, off int64) {
- s.storeArgWithBase(n, t, s.sp, off)
+// putArg evaluates n for the purpose of passing it as an argument to a function and returns the corresponding Param for the call.
+// If forLateExpandedCall is true, it returns the argument value to pass to the call operation.
+// If forLateExpandedCall is false, then the value is stored at the specified stack offset, and the returned value is nil.
+func (s *state) putArg(n *Node, t *types.Type, off int64, forLateExpandedCall bool) (ssa.Param, *ssa.Value) {
+ var a *ssa.Value
+ if forLateExpandedCall {
+ if !canSSAType(t) {
+ a = s.newValue2(ssa.OpDereference, t, s.addr(n), s.mem())
+ } else {
+ a = s.expr(n)
+ }
+ } else {
+ s.storeArgWithBase(n, t, s.sp, off)
+ }
+ return ssa.Param{Type: t, Offset: int32(off)}, a
}
func (s *state) storeArgWithBase(n *Node, t *types.Type, base *ssa.Value, off int64) {
@@ -5554,7 +5925,7 @@ func (s *state) dottype(n *Node, commaok bool) (res, resok *ssa.Value) {
// Load type out of itab, build interface with existing idata.
off := s.newValue1I(ssa.OpOffPtr, byteptr, int64(Widthptr), itab)
typ := s.load(byteptr, off)
- idata := s.newValue1(ssa.OpIData, n.Type, iface)
+ idata := s.newValue1(ssa.OpIData, byteptr, iface)
res = s.newValue2(ssa.OpIMake, n.Type, typ, idata)
return
}
@@ -5576,7 +5947,7 @@ func (s *state) dottype(n *Node, commaok bool) (res, resok *ssa.Value) {
bOk.AddEdgeTo(bEnd)
bFail.AddEdgeTo(bEnd)
s.startBlock(bEnd)
- idata := s.newValue1(ssa.OpIData, n.Type, iface)
+ idata := s.newValue1(ssa.OpIData, byteptr, iface)
res = s.newValue2(ssa.OpIMake, n.Type, s.variable(&typVar, byteptr), idata)
resok = cond
delete(s.vars, &typVar)
@@ -5787,9 +6158,7 @@ type SSAGenState struct {
// bstart remembers where each block starts (indexed by block ID)
bstart []*obj.Prog
- // 387 port: maps from SSE registers (REG_X?) to 387 registers (REG_F?)
- SSEto387 map[int16]int16
- // Some architectures require a 64-bit temporary for FP-related register shuffling. Examples include x86-387, PPC, and Sparc V8.
+ // Some architectures require a 64-bit temporary for FP-related register shuffling. Examples include PPC and Sparc V8.
ScratchFpMem *Node
maxarg int64 // largest frame size for arguments to calls made by the function
@@ -5895,7 +6264,7 @@ func emitStackObjects(e *ssafn, pp *Progs) {
// Populate the stack object data.
// Format must match runtime/stack.go:stackObjectRecord.
- x := e.curfn.Func.lsym.Func.StackObjects
+ x := e.curfn.Func.lsym.Func().StackObjects
off := 0
off = duintptr(x, off, uint64(len(vars)))
for _, v := range vars {
@@ -5932,7 +6301,7 @@ func genssa(f *ssa.Func, pp *Progs) {
s.livenessMap = liveness(e, f, pp)
emitStackObjects(e, pp)
- openDeferInfo := e.curfn.Func.lsym.Func.OpenCodedDeferInfo
+ openDeferInfo := e.curfn.Func.lsym.Func().OpenCodedDeferInfo
if openDeferInfo != nil {
// This function uses open-coded defers -- write out the funcdata
// info that we computed at the end of genssa.
@@ -5956,10 +6325,6 @@ func genssa(f *ssa.Func, pp *Progs) {
progToBlock[s.pp.next] = f.Blocks[0]
}
- if thearch.Use387 {
- s.SSEto387 = map[int16]int16{}
- }
-
s.ScratchFpMem = e.scratchFpMem
if Ctxt.Flag_locationlists {
@@ -6004,7 +6369,7 @@ func genssa(f *ssa.Func, pp *Progs) {
// instruction. We won't use the actual liveness map on a
// control instruction. Just mark it something that is
// preemptible, unless this function is "all unsafe".
- s.pp.nextLive = LivenessIndex{-1, -1, allUnsafe(f)}
+ s.pp.nextLive = LivenessIndex{-1, allUnsafe(f)}
// Emit values in block
thearch.SSAMarkMoves(&s, b)
@@ -6082,7 +6447,7 @@ func genssa(f *ssa.Func, pp *Progs) {
}
// Emit control flow instructions for block
var next *ssa.Block
- if i < len(f.Blocks)-1 && Debug['N'] == 0 {
+ if i < len(f.Blocks)-1 && Debug.N == 0 {
// If -N, leave next==nil so every block with successors
// ends in a JMP (except call blocks - plive doesn't like
// select{send,recv} followed by a JMP call). Helps keep
@@ -6141,7 +6506,7 @@ func genssa(f *ssa.Func, pp *Progs) {
// some of the inline marks.
// Use this instruction instead.
p.Pos = p.Pos.WithIsStmt() // promote position to a statement
- pp.curfn.Func.lsym.Func.AddInlMark(p, inlMarks[m])
+ pp.curfn.Func.lsym.Func().AddInlMark(p, inlMarks[m])
// Make the inline mark a real nop, so it doesn't generate any code.
m.As = obj.ANOP
m.Pos = src.NoXPos
@@ -6153,7 +6518,7 @@ func genssa(f *ssa.Func, pp *Progs) {
// Any unmatched inline marks now need to be added to the inlining tree (and will generate a nop instruction).
for _, p := range inlMarkList {
if p.As != obj.ANOP {
- pp.curfn.Func.lsym.Func.AddInlMark(p, inlMarks[p])
+ pp.curfn.Func.lsym.Func().AddInlMark(p, inlMarks[p])
}
}
}
@@ -6353,6 +6718,9 @@ func AddAux2(a *obj.Addr, v *ssa.Value, offset int64) {
}
// Add symbol's offset from its base register.
switch n := v.Aux.(type) {
+ case *ssa.AuxCall:
+ a.Name = obj.NAME_EXTERN
+ a.Sym = n.Fn
case *obj.LSym:
a.Name = obj.NAME_EXTERN
a.Sym = n
@@ -6387,7 +6755,7 @@ func (s *state) extendIndex(idx, len *ssa.Value, kind ssa.BoundsKind, bounded bo
} else {
lo = s.newValue1(ssa.OpInt64Lo, types.Types[TUINT], idx)
}
- if bounded || Debug['B'] != 0 {
+ if bounded || Debug.B != 0 {
return lo
}
bNext := s.f.NewBlock(ssa.BlockPlain)
@@ -6539,10 +6907,10 @@ func (s *SSAGenState) Call(v *ssa.Value) *obj.Prog {
} else {
p.Pos = v.Pos.WithNotStmt()
}
- if sym, ok := v.Aux.(*obj.LSym); ok {
+ if sym, ok := v.Aux.(*ssa.AuxCall); ok && sym.Fn != nil {
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = sym
+ p.To.Sym = sym.Fn
} else {
// TODO(mdempsky): Can these differences be eliminated?
switch thearch.LinkArch.Family {
@@ -6565,12 +6933,14 @@ func (s *SSAGenState) PrepareCall(v *ssa.Value) {
idx := s.livenessMap.Get(v)
if !idx.StackMapValid() {
// See Liveness.hasStackMap.
- if sym, _ := v.Aux.(*obj.LSym); !(sym == typedmemclr || sym == typedmemmove) {
+ if sym, ok := v.Aux.(*ssa.AuxCall); !ok || !(sym.Fn == typedmemclr || sym.Fn == typedmemmove) {
Fatalf("missing stack map index for %v", v.LongString())
}
}
- if sym, _ := v.Aux.(*obj.LSym); sym == Deferreturn {
+ call, ok := v.Aux.(*ssa.AuxCall)
+
+ if ok && call.Fn == Deferreturn {
// Deferred calls will appear to be returning to
// the CALL deferreturn(SB) that we are about to emit.
// However, the stack trace code will show the line
@@ -6582,11 +6952,11 @@ func (s *SSAGenState) PrepareCall(v *ssa.Value) {
thearch.Ginsnopdefer(s.pp)
}
- if sym, ok := v.Aux.(*obj.LSym); ok {
+ if ok {
// Record call graph information for nowritebarrierrec
// analysis.
if nowritebarrierrecCheck != nil {
- nowritebarrierrecCheck.recordCall(s.pp.curfn, sym, v.Pos)
+ nowritebarrierrecCheck.recordCall(s.pp.curfn, call.Fn, v.Pos)
}
}
@@ -6659,56 +7029,38 @@ func (e *ssafn) Auto(pos src.XPos, t *types.Type) ssa.GCNode {
}
func (e *ssafn) SplitString(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
- n := name.N.(*Node)
ptrType := types.NewPtr(types.Types[TUINT8])
lenType := types.Types[TINT]
- if n.Class() == PAUTO && !n.Name.Addrtaken() {
- // Split this string up into two separate variables.
- p := e.splitSlot(&name, ".ptr", 0, ptrType)
- l := e.splitSlot(&name, ".len", ptrType.Size(), lenType)
- return p, l
- }
- // Return the two parts of the larger variable.
- return ssa.LocalSlot{N: n, Type: ptrType, Off: name.Off}, ssa.LocalSlot{N: n, Type: lenType, Off: name.Off + int64(Widthptr)}
+ // Split this string up into two separate variables.
+ p := e.SplitSlot(&name, ".ptr", 0, ptrType)
+ l := e.SplitSlot(&name, ".len", ptrType.Size(), lenType)
+ return p, l
}
func (e *ssafn) SplitInterface(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
n := name.N.(*Node)
u := types.Types[TUINTPTR]
t := types.NewPtr(types.Types[TUINT8])
- if n.Class() == PAUTO && !n.Name.Addrtaken() {
- // Split this interface up into two separate variables.
- f := ".itab"
- if n.Type.IsEmptyInterface() {
- f = ".type"
- }
- c := e.splitSlot(&name, f, 0, u) // see comment in plive.go:onebitwalktype1.
- d := e.splitSlot(&name, ".data", u.Size(), t)
- return c, d
+ // Split this interface up into two separate variables.
+ f := ".itab"
+ if n.Type.IsEmptyInterface() {
+ f = ".type"
}
- // Return the two parts of the larger variable.
- return ssa.LocalSlot{N: n, Type: u, Off: name.Off}, ssa.LocalSlot{N: n, Type: t, Off: name.Off + int64(Widthptr)}
+ c := e.SplitSlot(&name, f, 0, u) // see comment in plive.go:onebitwalktype1.
+ d := e.SplitSlot(&name, ".data", u.Size(), t)
+ return c, d
}
func (e *ssafn) SplitSlice(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot, ssa.LocalSlot) {
- n := name.N.(*Node)
ptrType := types.NewPtr(name.Type.Elem())
lenType := types.Types[TINT]
- if n.Class() == PAUTO && !n.Name.Addrtaken() {
- // Split this slice up into three separate variables.
- p := e.splitSlot(&name, ".ptr", 0, ptrType)
- l := e.splitSlot(&name, ".len", ptrType.Size(), lenType)
- c := e.splitSlot(&name, ".cap", ptrType.Size()+lenType.Size(), lenType)
- return p, l, c
- }
- // Return the three parts of the larger variable.
- return ssa.LocalSlot{N: n, Type: ptrType, Off: name.Off},
- ssa.LocalSlot{N: n, Type: lenType, Off: name.Off + int64(Widthptr)},
- ssa.LocalSlot{N: n, Type: lenType, Off: name.Off + int64(2*Widthptr)}
+ p := e.SplitSlot(&name, ".ptr", 0, ptrType)
+ l := e.SplitSlot(&name, ".len", ptrType.Size(), lenType)
+ c := e.SplitSlot(&name, ".cap", ptrType.Size()+lenType.Size(), lenType)
+ return p, l, c
}
func (e *ssafn) SplitComplex(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
- n := name.N.(*Node)
s := name.Type.Size() / 2
var t *types.Type
if s == 8 {
@@ -6716,53 +7068,30 @@ func (e *ssafn) SplitComplex(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot)
} else {
t = types.Types[TFLOAT32]
}
- if n.Class() == PAUTO && !n.Name.Addrtaken() {
- // Split this complex up into two separate variables.
- r := e.splitSlot(&name, ".real", 0, t)
- i := e.splitSlot(&name, ".imag", t.Size(), t)
- return r, i
- }
- // Return the two parts of the larger variable.
- return ssa.LocalSlot{N: n, Type: t, Off: name.Off}, ssa.LocalSlot{N: n, Type: t, Off: name.Off + s}
+ r := e.SplitSlot(&name, ".real", 0, t)
+ i := e.SplitSlot(&name, ".imag", t.Size(), t)
+ return r, i
}
func (e *ssafn) SplitInt64(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
- n := name.N.(*Node)
var t *types.Type
if name.Type.IsSigned() {
t = types.Types[TINT32]
} else {
t = types.Types[TUINT32]
}
- if n.Class() == PAUTO && !n.Name.Addrtaken() {
- // Split this int64 up into two separate variables.
- if thearch.LinkArch.ByteOrder == binary.BigEndian {
- return e.splitSlot(&name, ".hi", 0, t), e.splitSlot(&name, ".lo", t.Size(), types.Types[TUINT32])
- }
- return e.splitSlot(&name, ".hi", t.Size(), t), e.splitSlot(&name, ".lo", 0, types.Types[TUINT32])
- }
- // Return the two parts of the larger variable.
if thearch.LinkArch.ByteOrder == binary.BigEndian {
- return ssa.LocalSlot{N: n, Type: t, Off: name.Off}, ssa.LocalSlot{N: n, Type: types.Types[TUINT32], Off: name.Off + 4}
+ return e.SplitSlot(&name, ".hi", 0, t), e.SplitSlot(&name, ".lo", t.Size(), types.Types[TUINT32])
}
- return ssa.LocalSlot{N: n, Type: t, Off: name.Off + 4}, ssa.LocalSlot{N: n, Type: types.Types[TUINT32], Off: name.Off}
+ return e.SplitSlot(&name, ".hi", t.Size(), t), e.SplitSlot(&name, ".lo", 0, types.Types[TUINT32])
}
func (e *ssafn) SplitStruct(name ssa.LocalSlot, i int) ssa.LocalSlot {
- n := name.N.(*Node)
st := name.Type
- ft := st.FieldType(i)
- var offset int64
- for f := 0; f < i; f++ {
- offset += st.FieldType(f).Size()
- }
- if n.Class() == PAUTO && !n.Name.Addrtaken() {
- // Note: the _ field may appear several times. But
- // have no fear, identically-named but distinct Autos are
- // ok, albeit maybe confusing for a debugger.
- return e.splitSlot(&name, "."+st.FieldName(i), offset, ft)
- }
- return ssa.LocalSlot{N: n, Type: ft, Off: name.Off + st.FieldOff(i)}
+ // Note: the _ field may appear several times. But
+ // have no fear, identically-named but distinct Autos are
+ // ok, albeit maybe confusing for a debugger.
+ return e.SplitSlot(&name, "."+st.FieldName(i), st.FieldOff(i), st.FieldType(i))
}
func (e *ssafn) SplitArray(name ssa.LocalSlot) ssa.LocalSlot {
@@ -6772,19 +7101,23 @@ func (e *ssafn) SplitArray(name ssa.LocalSlot) ssa.LocalSlot {
e.Fatalf(n.Pos, "bad array size")
}
et := at.Elem()
- if n.Class() == PAUTO && !n.Name.Addrtaken() {
- return e.splitSlot(&name, "[0]", 0, et)
- }
- return ssa.LocalSlot{N: n, Type: et, Off: name.Off}
+ return e.SplitSlot(&name, "[0]", 0, et)
}
func (e *ssafn) DerefItab(it *obj.LSym, offset int64) *obj.LSym {
return itabsym(it, offset)
}
-// splitSlot returns a slot representing the data of parent starting at offset.
-func (e *ssafn) splitSlot(parent *ssa.LocalSlot, suffix string, offset int64, t *types.Type) ssa.LocalSlot {
- s := &types.Sym{Name: parent.N.(*Node).Sym.Name + suffix, Pkg: localpkg}
+// SplitSlot returns a slot representing the data of parent starting at offset.
+func (e *ssafn) SplitSlot(parent *ssa.LocalSlot, suffix string, offset int64, t *types.Type) ssa.LocalSlot {
+ node := parent.N.(*Node)
+
+ if node.Class() != PAUTO || node.Name.Addrtaken() {
+ // addressed things and non-autos retain their parents (i.e., cannot truly be split)
+ return ssa.LocalSlot{N: node, Type: t, Off: parent.Off + offset}
+ }
+
+ s := &types.Sym{Name: node.Sym.Name + suffix, Pkg: localpkg}
n := &Node{
Name: new(Name),
diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go
index 8fa3fca50f..defefd76b3 100644
--- a/src/cmd/compile/internal/gc/subr.go
+++ b/src/cmd/compile/internal/gc/subr.go
@@ -96,7 +96,7 @@ func flusherrors() {
}
func hcrash() {
- if Debug['h'] != 0 {
+ if Debug.h != 0 {
flusherrors()
if outfile != "" {
os.Remove(outfile)
@@ -107,7 +107,7 @@ func hcrash() {
}
func linestr(pos src.XPos) string {
- return Ctxt.OutermostPos(pos).Format(Debug['C'] == 0, Debug['L'] == 1)
+ return Ctxt.OutermostPos(pos).Format(Debug.C == 0, Debug.L == 1)
}
// lasterror keeps track of the most recently issued error.
@@ -153,7 +153,7 @@ func yyerrorl(pos src.XPos, format string, args ...interface{}) {
hcrash()
nerrors++
- if nsavederrors+nerrors >= 10 && Debug['e'] == 0 {
+ if nsavederrors+nerrors >= 10 && Debug.e == 0 {
flusherrors()
fmt.Printf("%v: too many errors\n", linestr(pos))
errorexit()
@@ -175,7 +175,7 @@ func Warn(fmt_ string, args ...interface{}) {
func Warnl(line src.XPos, fmt_ string, args ...interface{}) {
adderr(line, fmt_, args...)
- if Debug['m'] != 0 {
+ if Debug.m != 0 {
flusherrors()
}
}
@@ -222,7 +222,7 @@ func hasUniquePos(n *Node) bool {
}
if !n.Pos.IsKnown() {
- if Debug['K'] != 0 {
+ if Debug.K != 0 {
Warn("setlineno: unknown position (line 0)")
}
return false
@@ -348,7 +348,7 @@ func newname(s *types.Sym) *Node {
return n
}
-// newname returns a new ONAME Node associated with symbol s at position pos.
+// newnamel returns a new ONAME Node associated with symbol s at position pos.
// The caller is responsible for setting n.Name.Curfn.
func newnamel(pos src.XPos, s *types.Sym) *Node {
if s == nil {
@@ -546,22 +546,19 @@ func methtype(t *types.Type) *types.Type {
// Is type src assignment compatible to type dst?
// If so, return op code to use in conversion.
-// If not, return OXXX.
-func assignop(src, dst *types.Type, why *string) Op {
- if why != nil {
- *why = ""
- }
-
+// If not, return OXXX. In this case, the string return parameter may
+// hold a reason why. In all other cases, it'll be the empty string.
+func assignop(src, dst *types.Type) (Op, string) {
if src == dst {
- return OCONVNOP
+ return OCONVNOP, ""
}
if src == nil || dst == nil || src.Etype == TFORW || dst.Etype == TFORW || src.Orig == nil || dst.Orig == nil {
- return OXXX
+ return OXXX, ""
}
// 1. src type is identical to dst.
if types.Identical(src, dst) {
- return OCONVNOP
+ return OCONVNOP, ""
}
// 2. src and dst have identical underlying types
@@ -575,13 +572,13 @@ func assignop(src, dst *types.Type, why *string) Op {
if src.IsEmptyInterface() {
// Conversion between two empty interfaces
// requires no code.
- return OCONVNOP
+ return OCONVNOP, ""
}
if (src.Sym == nil || dst.Sym == nil) && !src.IsInterface() {
// Conversion between two types, at least one unnamed,
// needs no conversion. The exception is nonempty interfaces
// which need to have their itab updated.
- return OCONVNOP
+ return OCONVNOP, ""
}
}
@@ -590,49 +587,47 @@ func assignop(src, dst *types.Type, why *string) Op {
var missing, have *types.Field
var ptr int
if implements(src, dst, &missing, &have, &ptr) {
- return OCONVIFACE
+ return OCONVIFACE, ""
}
// we'll have complained about this method anyway, suppress spurious messages.
if have != nil && have.Sym == missing.Sym && (have.Type.Broke() || missing.Type.Broke()) {
- return OCONVIFACE
+ return OCONVIFACE, ""
}
- if why != nil {
- if isptrto(src, TINTER) {
- *why = fmt.Sprintf(":\n\t%v is pointer to interface, not interface", src)
- } else if have != nil && have.Sym == missing.Sym && have.Nointerface() {
- *why = fmt.Sprintf(":\n\t%v does not implement %v (%v method is marked 'nointerface')", src, dst, missing.Sym)
- } else if have != nil && have.Sym == missing.Sym {
- *why = fmt.Sprintf(":\n\t%v does not implement %v (wrong type for %v method)\n"+
- "\t\thave %v%0S\n\t\twant %v%0S", src, dst, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type)
- } else if ptr != 0 {
- *why = fmt.Sprintf(":\n\t%v does not implement %v (%v method has pointer receiver)", src, dst, missing.Sym)
- } else if have != nil {
- *why = fmt.Sprintf(":\n\t%v does not implement %v (missing %v method)\n"+
- "\t\thave %v%0S\n\t\twant %v%0S", src, dst, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type)
- } else {
- *why = fmt.Sprintf(":\n\t%v does not implement %v (missing %v method)", src, dst, missing.Sym)
- }
+ var why string
+ if isptrto(src, TINTER) {
+ why = fmt.Sprintf(":\n\t%v is pointer to interface, not interface", src)
+ } else if have != nil && have.Sym == missing.Sym && have.Nointerface() {
+ why = fmt.Sprintf(":\n\t%v does not implement %v (%v method is marked 'nointerface')", src, dst, missing.Sym)
+ } else if have != nil && have.Sym == missing.Sym {
+ why = fmt.Sprintf(":\n\t%v does not implement %v (wrong type for %v method)\n"+
+ "\t\thave %v%0S\n\t\twant %v%0S", src, dst, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type)
+ } else if ptr != 0 {
+ why = fmt.Sprintf(":\n\t%v does not implement %v (%v method has pointer receiver)", src, dst, missing.Sym)
+ } else if have != nil {
+ why = fmt.Sprintf(":\n\t%v does not implement %v (missing %v method)\n"+
+ "\t\thave %v%0S\n\t\twant %v%0S", src, dst, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type)
+ } else {
+ why = fmt.Sprintf(":\n\t%v does not implement %v (missing %v method)", src, dst, missing.Sym)
}
- return OXXX
+ return OXXX, why
}
if isptrto(dst, TINTER) {
- if why != nil {
- *why = fmt.Sprintf(":\n\t%v is pointer to interface, not interface", dst)
- }
- return OXXX
+ why := fmt.Sprintf(":\n\t%v is pointer to interface, not interface", dst)
+ return OXXX, why
}
if src.IsInterface() && dst.Etype != TBLANK {
var missing, have *types.Field
var ptr int
- if why != nil && implements(dst, src, &missing, &have, &ptr) {
- *why = ": need type assertion"
+ var why string
+ if implements(dst, src, &missing, &have, &ptr) {
+ why = ": need type assertion"
}
- return OXXX
+ return OXXX, why
}
// 4. src is a bidirectional channel value, dst is a channel type,
@@ -640,7 +635,7 @@ func assignop(src, dst *types.Type, why *string) Op {
// either src or dst is not a named type.
if src.IsChan() && src.ChanDir() == types.Cboth && dst.IsChan() {
if types.Identical(src.Elem(), dst.Elem()) && (src.Sym == nil || dst.Sym == nil) {
- return OCONVNOP
+ return OCONVNOP, ""
}
}
@@ -653,7 +648,7 @@ func assignop(src, dst *types.Type, why *string) Op {
TCHAN,
TINTER,
TSLICE:
- return OCONVNOP
+ return OCONVNOP, ""
}
}
@@ -661,26 +656,23 @@ func assignop(src, dst *types.Type, why *string) Op {
// 7. Any typed value can be assigned to the blank identifier.
if dst.Etype == TBLANK {
- return OCONVNOP
+ return OCONVNOP, ""
}
- return OXXX
+ return OXXX, ""
}
// Can we convert a value of type src to a value of type dst?
// If so, return op code to use in conversion (maybe OCONVNOP).
-// If not, return OXXX.
+// If not, return OXXX. In this case, the string return parameter may
+// hold a reason why. In all other cases, it'll be the empty string.
// srcConstant indicates whether the value of type src is a constant.
-func convertop(srcConstant bool, src, dst *types.Type, why *string) Op {
- if why != nil {
- *why = ""
- }
-
+func convertop(srcConstant bool, src, dst *types.Type) (Op, string) {
if src == dst {
- return OCONVNOP
+ return OCONVNOP, ""
}
if src == nil || dst == nil {
- return OXXX
+ return OXXX, ""
}
// Conversions from regular to go:notinheap are not allowed
@@ -688,23 +680,19 @@ func convertop(srcConstant bool, src, dst *types.Type, why *string) Op {
// rules.
// (a) Disallow (*T) to (*U) where T is go:notinheap but U isn't.
if src.IsPtr() && dst.IsPtr() && dst.Elem().NotInHeap() && !src.Elem().NotInHeap() {
- if why != nil {
- *why = fmt.Sprintf(":\n\t%v is go:notinheap, but %v is not", dst.Elem(), src.Elem())
- }
- return OXXX
+ why := fmt.Sprintf(":\n\t%v is incomplete (or unallocatable), but %v is not", dst.Elem(), src.Elem())
+ return OXXX, why
}
// (b) Disallow string to []T where T is go:notinheap.
if src.IsString() && dst.IsSlice() && dst.Elem().NotInHeap() && (dst.Elem().Etype == types.Bytetype.Etype || dst.Elem().Etype == types.Runetype.Etype) {
- if why != nil {
- *why = fmt.Sprintf(":\n\t%v is go:notinheap", dst.Elem())
- }
- return OXXX
+ why := fmt.Sprintf(":\n\t%v is incomplete (or unallocatable)", dst.Elem())
+ return OXXX, why
}
// 1. src can be assigned to dst.
- op := assignop(src, dst, why)
+ op, why := assignop(src, dst)
if op != OXXX {
- return op
+ return op, why
}
// The rules for interfaces are no different in conversions
@@ -712,60 +700,57 @@ func convertop(srcConstant bool, src, dst *types.Type, why *string) Op {
// with the good message from assignop.
// Otherwise clear the error.
if src.IsInterface() || dst.IsInterface() {
- return OXXX
- }
- if why != nil {
- *why = ""
+ return OXXX, why
}
// 2. Ignoring struct tags, src and dst have identical underlying types.
if types.IdenticalIgnoreTags(src.Orig, dst.Orig) {
- return OCONVNOP
+ return OCONVNOP, ""
}
// 3. src and dst are unnamed pointer types and, ignoring struct tags,
// their base types have identical underlying types.
if src.IsPtr() && dst.IsPtr() && src.Sym == nil && dst.Sym == nil {
if types.IdenticalIgnoreTags(src.Elem().Orig, dst.Elem().Orig) {
- return OCONVNOP
+ return OCONVNOP, ""
}
}
// 4. src and dst are both integer or floating point types.
if (src.IsInteger() || src.IsFloat()) && (dst.IsInteger() || dst.IsFloat()) {
if simtype[src.Etype] == simtype[dst.Etype] {
- return OCONVNOP
+ return OCONVNOP, ""
}
- return OCONV
+ return OCONV, ""
}
// 5. src and dst are both complex types.
if src.IsComplex() && dst.IsComplex() {
if simtype[src.Etype] == simtype[dst.Etype] {
- return OCONVNOP
+ return OCONVNOP, ""
}
- return OCONV
+ return OCONV, ""
}
// Special case for constant conversions: any numeric
// conversion is potentially okay. We'll validate further
// within evconst. See #38117.
if srcConstant && (src.IsInteger() || src.IsFloat() || src.IsComplex()) && (dst.IsInteger() || dst.IsFloat() || dst.IsComplex()) {
- return OCONV
+ return OCONV, ""
}
// 6. src is an integer or has type []byte or []rune
// and dst is a string type.
if src.IsInteger() && dst.IsString() {
- return ORUNESTR
+ return ORUNESTR, ""
}
if src.IsSlice() && dst.IsString() {
if src.Elem().Etype == types.Bytetype.Etype {
- return OBYTES2STR
+ return OBYTES2STR, ""
}
if src.Elem().Etype == types.Runetype.Etype {
- return ORUNES2STR
+ return ORUNES2STR, ""
}
}
@@ -773,21 +758,21 @@ func convertop(srcConstant bool, src, dst *types.Type, why *string) Op {
// String to slice.
if src.IsString() && dst.IsSlice() {
if dst.Elem().Etype == types.Bytetype.Etype {
- return OSTR2BYTES
+ return OSTR2BYTES, ""
}
if dst.Elem().Etype == types.Runetype.Etype {
- return OSTR2RUNES
+ return OSTR2RUNES, ""
}
}
// 8. src is a pointer or uintptr and dst is unsafe.Pointer.
- if (src.IsPtr() || src.Etype == TUINTPTR) && dst.Etype == TUNSAFEPTR {
- return OCONVNOP
+ if (src.IsPtr() || src.IsUintptr()) && dst.IsUnsafePtr() {
+ return OCONVNOP, ""
}
// 9. src is unsafe.Pointer and dst is a pointer or uintptr.
- if src.Etype == TUNSAFEPTR && (dst.IsPtr() || dst.Etype == TUINTPTR) {
- return OCONVNOP
+ if src.IsUnsafePtr() && (dst.IsPtr() || dst.IsUintptr()) {
+ return OCONVNOP, ""
}
// src is map and dst is a pointer to corresponding hmap.
@@ -795,10 +780,10 @@ func convertop(srcConstant bool, src, dst *types.Type, why *string) Op {
// go gc maps are implemented as a pointer to a hmap struct.
if src.Etype == TMAP && dst.IsPtr() &&
src.MapType().Hmap == dst.Elem() {
- return OCONVNOP
+ return OCONVNOP, ""
}
- return OXXX
+ return OXXX, ""
}
func assignconv(n *Node, t *types.Type, context string) *Node {
@@ -825,7 +810,7 @@ func assignconvfn(n *Node, t *types.Type, context func() string) *Node {
// Convert ideal bool from comparison to plain bool
// if the next step is non-bool (like interface{}).
- if n.Type == types.Idealbool && !t.IsBoolean() {
+ if n.Type == types.UntypedBool && !t.IsBoolean() {
if n.Op == ONAME || n.Op == OLITERAL {
r := nod(OCONVNOP, n, nil)
r.Type = types.Types[TBOOL]
@@ -839,8 +824,7 @@ func assignconvfn(n *Node, t *types.Type, context func() string) *Node {
return n
}
- var why string
- op := assignop(n.Type, t, &why)
+ op, why := assignop(n.Type, t)
if op == OXXX {
yyerror("cannot use %L as type %v in %s%s", n, t, context(), why)
op = OCONV
@@ -928,16 +912,20 @@ func (o Op) IsSlice3() bool {
return false
}
-// slicePtrLen extracts the pointer and length from a slice.
+// backingArrayPtrLen extracts the pointer and length from a slice or string.
// This constructs two nodes referring to n, so n must be a cheapexpr.
-func (n *Node) slicePtrLen() (ptr, len *Node) {
+func (n *Node) backingArrayPtrLen() (ptr, len *Node) {
var init Nodes
c := cheapexpr(n, &init)
if c != n || init.Len() != 0 {
- Fatalf("slicePtrLen not cheap: %v", n)
+ Fatalf("backingArrayPtrLen not cheap: %v", n)
}
ptr = nod(OSPTR, n, nil)
- ptr.Type = n.Type.Elem().PtrTo()
+ if n.Type.IsString() {
+ ptr.Type = types.Types[TUINT8].PtrTo()
+ } else {
+ ptr.Type = n.Type.Elem().PtrTo()
+ }
len = nod(OLEN, n, nil)
len.Type = types.Types[TINT]
return ptr, len
@@ -1036,25 +1024,24 @@ func calcHasCall(n *Node) bool {
return false
}
-func badtype(op Op, tl *types.Type, tr *types.Type) {
- fmt_ := ""
+func badtype(op Op, tl, tr *types.Type) {
+ var s string
if tl != nil {
- fmt_ += fmt.Sprintf("\n\t%v", tl)
+ s += fmt.Sprintf("\n\t%v", tl)
}
if tr != nil {
- fmt_ += fmt.Sprintf("\n\t%v", tr)
+ s += fmt.Sprintf("\n\t%v", tr)
}
// common mistake: *struct and *interface.
if tl != nil && tr != nil && tl.IsPtr() && tr.IsPtr() {
if tl.Elem().IsStruct() && tr.Elem().IsInterface() {
- fmt_ += "\n\t(*struct vs *interface)"
+ s += "\n\t(*struct vs *interface)"
} else if tl.Elem().IsInterface() && tr.Elem().IsStruct() {
- fmt_ += "\n\t(*interface vs *struct)"
+ s += "\n\t(*interface vs *struct)"
}
}
- s := fmt_
yyerror("illegal types for operand: %v%s", op, s)
}
@@ -1519,7 +1506,7 @@ func structargs(tl *types.Type, mustname bool) []*Node {
// method - M func (t T)(), a TFIELD type struct
// newnam - the eventual mangled name of this function
func genwrapper(rcvr *types.Type, method *types.Field, newnam *types.Sym) {
- if false && Debug['r'] != 0 {
+ if false && Debug.r != 0 {
fmt.Printf("genwrapper rcvrtype=%v method=%v newnam=%v\n", rcvr, method, newnam)
}
@@ -1592,7 +1579,7 @@ func genwrapper(rcvr *types.Type, method *types.Field, newnam *types.Sym) {
fn.Nbody.Append(call)
}
- if false && Debug['r'] != 0 {
+ if false && Debug.r != 0 {
dumplist("genwrapper body", fn.Nbody)
}
@@ -1615,7 +1602,7 @@ func genwrapper(rcvr *types.Type, method *types.Field, newnam *types.Sym) {
escapeFuncs([]*Node{fn}, false)
Curfn = nil
- funccompile(fn)
+ xtop = append(xtop, fn)
}
func paramNnames(ft *types.Type) []*Node {
@@ -1733,7 +1720,7 @@ func implements(t, iface *types.Type, m, samename **types.Field, ptr *int) bool
// the method does not exist for value types.
rcvr := tm.Type.Recv().Type
if rcvr.IsPtr() && !t0.IsPtr() && !followptr && !isifacemethod(tm.Type) {
- if false && Debug['r'] != 0 {
+ if false && Debug.r != 0 {
yyerror("interface pointer mismatch")
}
@@ -1867,8 +1854,10 @@ func isdirectiface(t *types.Type) bool {
}
switch t.Etype {
- case TPTR,
- TCHAN,
+ case TPTR:
+ // Pointers to notinheap types must be stored indirectly. See issue 42076.
+ return !t.Elem().NotInHeap()
+ case TCHAN,
TMAP,
TFUNC,
TUNSAFEPTR:
@@ -1917,3 +1906,13 @@ func ifaceData(pos src.XPos, n *Node, t *types.Type) *Node {
ind.SetBounded(true)
return ind
}
+
+// typePos returns the position associated with t.
+// This is where t was declared or where it appeared as a type expression.
+func typePos(t *types.Type) src.XPos {
+ n := asNode(t.Nod)
+ if n == nil || !n.Pos.IsKnown() {
+ Fatalf("bad type: %v", t)
+ }
+ return n.Pos
+}
diff --git a/src/cmd/compile/internal/gc/swt.go b/src/cmd/compile/internal/gc/swt.go
index 138b0acc53..8d9fbe300e 100644
--- a/src/cmd/compile/internal/gc/swt.go
+++ b/src/cmd/compile/internal/gc/swt.go
@@ -189,16 +189,19 @@ func typecheckExprSwitch(n *Node) {
continue
}
- switch {
- case nilonly != "" && !n1.isNil():
+ if nilonly != "" && !n1.isNil() {
yyerrorl(ncase.Pos, "invalid case %v in switch (can only compare %s %v to nil)", n1, nilonly, n.Left)
- case t.IsInterface() && !n1.Type.IsInterface() && !IsComparable(n1.Type):
+ } else if t.IsInterface() && !n1.Type.IsInterface() && !IsComparable(n1.Type) {
yyerrorl(ncase.Pos, "invalid case %L in switch (incomparable type)", n1)
- case assignop(n1.Type, t, nil) == 0 && assignop(t, n1.Type, nil) == 0:
- if n.Left != nil {
- yyerrorl(ncase.Pos, "invalid case %v in switch on %v (mismatched types %v and %v)", n1, n.Left, n1.Type, t)
- } else {
- yyerrorl(ncase.Pos, "invalid case %v in switch (mismatched types %v and bool)", n1, n1.Type)
+ } else {
+ op1, _ := assignop(n1.Type, t)
+ op2, _ := assignop(t, n1.Type)
+ if op1 == OXXX && op2 == OXXX {
+ if n.Left != nil {
+ yyerrorl(ncase.Pos, "invalid case %v in switch on %v (mismatched types %v and %v)", n1, n.Left, n1.Type, t)
+ } else {
+ yyerrorl(ncase.Pos, "invalid case %v in switch (mismatched types %v and bool)", n1, n1.Type)
+ }
}
}
@@ -358,8 +361,8 @@ func (s *exprSwitch) flush() {
// 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)
+ si := cc[i].lo.StringVal()
+ sj := cc[j].lo.StringVal()
if len(si) != len(sj) {
return len(si) < len(sj)
}
@@ -368,7 +371,7 @@ func (s *exprSwitch) flush() {
// runLen returns the string length associated with a
// particular run of exprClauses.
- runLen := func(run []exprClause) int64 { return int64(len(strlit(run[0].lo))) }
+ runLen := func(run []exprClause) int64 { return int64(len(run[0].lo.StringVal())) }
// Collapse runs of consecutive strings with the same length.
var runs [][]exprClause
@@ -405,7 +408,7 @@ func (s *exprSwitch) flush() {
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() {
+ if last.jmp == c.jmp && last.hi.Int64Val()+1 == c.lo.Int64Val() {
last.hi = c.lo
} else {
merged = append(merged, c)
@@ -440,7 +443,7 @@ func (c *exprClause) test(exprname *Node) *Node {
// Optimize "switch true { ...}" and "switch false { ... }".
if Isconst(exprname, CTBOOL) && !c.lo.Type.IsInterface() {
- if exprname.Val().U.(bool) {
+ if exprname.BoolVal() {
return c.lo
} else {
return nodl(c.pos, ONOT, c.lo, nil)
diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go
index 47e5e59156..43358333b8 100644
--- a/src/cmd/compile/internal/gc/syntax.go
+++ b/src/cmd/compile/internal/gc/syntax.go
@@ -142,7 +142,7 @@ const (
_, _ // second nodeInitorder bit
_, nodeHasBreak
_, nodeNoInline // used internally by inliner to indicate that a function call should not be inlined; set for OCALLFUNC and OCALLMETH only
- _, nodeImplicit // implicit OADDR or ODEREF; ++/-- statement represented as OASOP; or ANDNOT lowered to OAND
+ _, nodeImplicit // implicit OADDR or ODEREF; ++/-- statement represented as OASOP
_, nodeIsDDD // is the argument variadic
_, nodeDiag // already printed error about this
_, nodeColas // OAS resulting from :=
@@ -247,7 +247,7 @@ func (n *Node) Val() Val {
// SetVal sets the Val for the node, which must not have been used with SetOpt.
func (n *Node) SetVal(v Val) {
if n.HasOpt() {
- Debug['h'] = 1
+ Debug.h = 1
Dump("have Opt", n)
Fatalf("have Opt")
}
@@ -270,7 +270,7 @@ func (n *Node) SetOpt(x interface{}) {
return
}
if n.HasVal() {
- Debug['h'] = 1
+ Debug.h = 1
Dump("have Val", n)
Fatalf("have Val")
}
@@ -344,14 +344,22 @@ func (n *Node) CanBeAnSSASym() {
// Name holds Node fields used only by named nodes (ONAME, OTYPE, OPACK, OLABEL, some OLITERAL).
type Name struct {
- Pack *Node // real package for import . names
- Pkg *types.Pkg // pkg for OPACK nodes
- Defn *Node // initializing assignment
- Curfn *Node // function for local variables
- Param *Param // additional fields for ONAME, OTYPE
- Decldepth int32 // declaration loop depth, increased for every loop or label
- Vargen int32 // unique name for ONAME within a function. Function outputs are numbered starting at one.
- flags bitset16
+ Pack *Node // real package for import . names
+ Pkg *types.Pkg // pkg for OPACK nodes
+ // For a local variable (not param) or extern, the initializing assignment (OAS or OAS2).
+ // For a closure var, the ONAME node of the outer captured variable
+ Defn *Node
+ // The ODCLFUNC node (for a static function/method or a closure) in which
+ // local variable or param is declared.
+ Curfn *Node
+ Param *Param // additional fields for ONAME, OTYPE
+ Decldepth int32 // declaration loop depth, increased for every loop or label
+ // Unique number for ONAME nodes within a function. Function outputs
+ // (results) are numbered starting at one, followed by function inputs
+ // (parameters), and then local variables. Vargen is used to distinguish
+ // local variables/params with the same name.
+ Vargen int32
+ flags bitset16
}
const (
@@ -359,7 +367,6 @@ const (
nameReadonly
nameByval // is the variable captured by value or by reference
nameNeedzero // if it contains pointers, needs to be zeroed on function entry
- nameKeepalive // mark value live across unknown assembly call
nameAutoTemp // is the variable a temporary (implies no dwarf info. reset if escapes to heap)
nameUsed // for variable declared and not used error
nameIsClosureVar // PAUTOHEAP closure pseudo-variable; original at n.Name.Defn
@@ -376,7 +383,6 @@ func (n *Name) Captured() bool { return n.flags&nameCaptured != 0 }
func (n *Name) Readonly() bool { return n.flags&nameReadonly != 0 }
func (n *Name) Byval() bool { return n.flags&nameByval != 0 }
func (n *Name) Needzero() bool { return n.flags&nameNeedzero != 0 }
-func (n *Name) Keepalive() bool { return n.flags&nameKeepalive != 0 }
func (n *Name) AutoTemp() bool { return n.flags&nameAutoTemp != 0 }
func (n *Name) Used() bool { return n.flags&nameUsed != 0 }
func (n *Name) IsClosureVar() bool { return n.flags&nameIsClosureVar != 0 }
@@ -392,7 +398,6 @@ func (n *Name) SetCaptured(b bool) { n.flags.set(nameCaptured, b) }
func (n *Name) SetReadonly(b bool) { n.flags.set(nameReadonly, b) }
func (n *Name) SetByval(b bool) { n.flags.set(nameByval, b) }
func (n *Name) SetNeedzero(b bool) { n.flags.set(nameNeedzero, b) }
-func (n *Name) SetKeepalive(b bool) { n.flags.set(nameKeepalive, b) }
func (n *Name) SetAutoTemp(b bool) { n.flags.set(nameAutoTemp, b) }
func (n *Name) SetUsed(b bool) { n.flags.set(nameUsed, b) }
func (n *Name) SetIsClosureVar(b bool) { n.flags.set(nameIsClosureVar, b) }
@@ -463,14 +468,14 @@ type Param struct {
// x1 := xN.Defn
// x1.Innermost = xN.Outer
//
- // We leave xN.Innermost set so that we can still get to the original
+ // We leave x1.Innermost set so that we can still get to the original
// variable quickly. Not shown here, but once we're
// done parsing a function and no longer need xN.Outer for the
- // lexical x reference links as described above, closurebody
+ // lexical x reference links as described above, funcLit
// recomputes xN.Outer as the semantic x reference link tree,
// even filling in x in intermediate closures that might not
// have mentioned it along the way to inner closures that did.
- // See closurebody for details.
+ // See funcLit for details.
//
// During the eventual compilation, then, for closure variables we have:
//
@@ -483,11 +488,87 @@ type Param struct {
Innermost *Node
Outer *Node
- // OTYPE
- //
- // TODO: Should Func pragmas also be stored on the Name?
- Pragma PragmaFlag
- Alias bool // node is alias for Ntype (only used when type-checking ODCLTYPE)
+ // OTYPE & ONAME //go:embed info,
+ // sharing storage to reduce gc.Param size.
+ // Extra is nil, or else *Extra is a *paramType or an *embedFileList.
+ Extra *interface{}
+}
+
+type paramType struct {
+ flag PragmaFlag
+ alias bool
+}
+
+type embedFileList []string
+
+// Pragma returns the PragmaFlag for p, which must be for an OTYPE.
+func (p *Param) Pragma() PragmaFlag {
+ if p.Extra == nil {
+ return 0
+ }
+ return (*p.Extra).(*paramType).flag
+}
+
+// SetPragma sets the PragmaFlag for p, which must be for an OTYPE.
+func (p *Param) SetPragma(flag PragmaFlag) {
+ if p.Extra == nil {
+ if flag == 0 {
+ return
+ }
+ p.Extra = new(interface{})
+ *p.Extra = &paramType{flag: flag}
+ return
+ }
+ (*p.Extra).(*paramType).flag = flag
+}
+
+// Alias reports whether p, which must be for an OTYPE, is a type alias.
+func (p *Param) Alias() bool {
+ if p.Extra == nil {
+ return false
+ }
+ t, ok := (*p.Extra).(*paramType)
+ if !ok {
+ return false
+ }
+ return t.alias
+}
+
+// SetAlias sets whether p, which must be for an OTYPE, is a type alias.
+func (p *Param) SetAlias(alias bool) {
+ if p.Extra == nil {
+ if !alias {
+ return
+ }
+ p.Extra = new(interface{})
+ *p.Extra = &paramType{alias: alias}
+ return
+ }
+ (*p.Extra).(*paramType).alias = alias
+}
+
+// EmbedFiles returns the list of embedded files for p,
+// which must be for an ONAME var.
+func (p *Param) EmbedFiles() []string {
+ if p.Extra == nil {
+ return nil
+ }
+ return *(*p.Extra).(*embedFileList)
+}
+
+// SetEmbedFiles sets the list of embedded files for p,
+// which must be for an ONAME var.
+func (p *Param) SetEmbedFiles(list []string) {
+ if p.Extra == nil {
+ if len(list) == 0 {
+ return
+ }
+ f := embedFileList(list)
+ p.Extra = new(interface{})
+ *p.Extra = &f
+ return
+ }
+ *(*p.Extra).(*embedFileList) = list
}
// Functions
@@ -535,10 +616,16 @@ type Param struct {
// Func holds Node fields used only with function-like nodes.
type Func struct {
Shortname *types.Sym
- Enter Nodes // for example, allocate and initialize memory for escaping parameters
- Exit Nodes
- Cvars Nodes // closure params
- Dcl []*Node // autodcl for this func/closure
+ // Extra entry code for the function. For example, allocate and initialize
+ // memory for escaping parameters. However, just for OCLOSURE, Enter is a
+ // list of ONAME nodes of captured variables
+ Enter Nodes
+ Exit Nodes
+ // ONAME nodes for closure params, each should have closurevar set
+ Cvars Nodes
+ // ONAME nodes for all params/locals for this func/closure, does NOT
+ // include closurevars until transformclosure runs.
+ Dcl []*Node
// Parents records the parent scope of each scope within a
// function. The root scope (0) has no parent, so the i'th
@@ -557,8 +644,8 @@ type Func struct {
DebugInfo *ssa.FuncDebug
Ntype *Node // signature
Top int // top context (ctxCallee, etc)
- Closure *Node // OCLOSURE <-> ODCLFUNC
- Nname *Node
+ Closure *Node // OCLOSURE <-> ODCLFUNC (see header comment above)
+ Nname *Node // The ONAME node associated with an ODCLFUNC (both have same Type)
lsym *obj.LSym
Inl *Inline
@@ -607,6 +694,8 @@ const (
funcWrapper // is method wrapper
funcNeedctxt // function uses context register (has closure variables)
funcReflectMethod // function calls reflect.Type.Method or MethodByName
+ // true if closure inside a function; false if a simple function or a
+ // closure in a global variable initialization
funcIsHiddenClosure
funcHasDefer // contains a defer statement
funcNilCheckDisabled // disable nil checks when compiling this function
@@ -658,8 +747,10 @@ const (
OXXX Op = iota
// names
- ONAME // var or func name
- ONONAME // unnamed arg or return value: f(int, string) (int, error) { etc }
+ ONAME // var or func name
+ // Unnamed arg or return value: f(int, string) (int, error) { etc }
+ // Also used for a qualified package identifier that hasn't been resolved yet.
+ ONONAME
OTYPE // type name
OPACK // import
OLITERAL // literal
@@ -679,19 +770,24 @@ const (
OSTR2BYTES // Type(Left) (Type is []byte, Left is a string)
OSTR2BYTESTMP // Type(Left) (Type is []byte, Left is a string, ephemeral)
OSTR2RUNES // Type(Left) (Type is []rune, Left is a string)
- OAS // Left = Right or (if Colas=true) Left := Right
- OAS2 // List = Rlist (x, y, z = a, b, c)
- OAS2DOTTYPE // List = Right (x, ok = I.(int))
- OAS2FUNC // List = Right (x, y = f())
- OAS2MAPR // List = Right (x, ok = m["foo"])
- OAS2RECV // List = Right (x, ok = <-c)
- OASOP // Left Etype= Right (x += y)
- OCALL // Left(List) (function call, method call or type conversion)
+ // Left = Right or (if Colas=true) Left := Right
+ // If Colas, then Ninit includes a DCL node for Left.
+ OAS
+ // List = Rlist (x, y, z = a, b, c) or (if Colas=true) List := Rlist
+ // If Colas, then Ninit includes DCL nodes for List
+ OAS2
+ OAS2DOTTYPE // List = Right (x, ok = I.(int))
+ OAS2FUNC // List = Right (x, y = f())
+ OAS2MAPR // List = Right (x, ok = m["foo"])
+ OAS2RECV // List = Right (x, ok = <-c)
+ OASOP // Left Etype= Right (x += y)
+ OCALL // Left(List) (function call, method call or type conversion)
// OCALLFUNC, OCALLMETH, and OCALLINTER have the same structure.
// Prior to walk, they are: Left(List), where List is all regular arguments.
// After walk, List is a series of assignments to temporaries,
// and Rlist is an updated set of arguments.
+ // Nbody is all OVARLIVE nodes that are attached to OCALLxxx.
// TODO(josharian/khr): Use Ninit instead of List for the assignments to temporaries. See CL 114797.
OCALLFUNC // Left(List/Rlist) (function call f(args))
OCALLMETH // Left(List/Rlist) (direct method call x.Method(args))
@@ -699,7 +795,7 @@ const (
OCALLPART // Left.Right (method expression x.Method, not called)
OCAP // cap(Left)
OCLOSE // close(Left)
- OCLOSURE // func Type { Body } (func literal)
+ OCLOSURE // func Type { Func.Closure.Nbody } (func literal)
OCOMPLIT // Right{List} (composite literal, not yet lowered to specific form)
OMAPLIT // Type{List} (composite literal, Type is map)
OSTRUCTLIT // Type{List} (composite literal, Type is struct)
@@ -718,7 +814,7 @@ const (
ODCLCONST // const pi = 3.14
ODCLTYPE // type Int int or type Int = int
- ODELETE // delete(Left, Right)
+ ODELETE // delete(List)
ODOT // Left.Sym (Left is of struct type)
ODOTPTR // Left.Sym (Left is of pointer to struct type)
ODOTMETH // Left.Sym (Left is non-interface, Right is method name)
@@ -789,9 +885,14 @@ const (
OSIZEOF // unsafe.Sizeof(Left)
// statements
- OBLOCK // { List } (block of code)
- OBREAK // break [Sym]
- OCASE // case List: Nbody (List==nil means default)
+ OBLOCK // { List } (block of code)
+ OBREAK // break [Sym]
+ // OCASE: case List: Nbody (List==nil means default)
+ // For OTYPESW, List is a OTYPE node for the specified type (or OLITERAL
+ // for nil), and, if a type-switch variable is specified, Rlist is an
+ // ONAME for the version of the type-switch variable with the specified
+ // type.
+ OCASE
OCONTINUE // continue [Sym]
ODEFER // defer Left (Left must be call)
OEMPTY // no-op (empty statement)
@@ -815,15 +916,19 @@ const (
ORETURN // return List
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)
+ // OTYPESW: Left := Right.(type) (appears as .Left of OSWITCH)
+ // Left is nil if there is no type-switch variable
+ OTYPESW
// types
OTCHAN // chan int
OTMAP // map[string]int
OTSTRUCT // struct{}
OTINTER // interface{}
- OTFUNC // func()
- OTARRAY // []int, [8]int, [N]int or [...]int
+ // OTFUNC: func() - Left is receiver field, List is list of param fields, Rlist is
+ // list of result fields.
+ OTFUNC
+ OTARRAY // []int, [8]int, [N]int or [...]int
// misc
ODDD // func f(args ...int) or f(l...) or var a = [...]int{0, 1, 2}.
diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go
index dec4b96fc4..c0b05035f0 100644
--- a/src/cmd/compile/internal/gc/typecheck.go
+++ b/src/cmd/compile/internal/gc/typecheck.go
@@ -6,7 +6,6 @@ package gc
import (
"cmd/compile/internal/types"
- "cmd/internal/objabi"
"fmt"
"strings"
)
@@ -151,8 +150,8 @@ var _typekind = []string{
}
func typekind(t *types.Type) string {
- if t.IsSlice() {
- return "slice"
+ if t.IsUntyped() {
+ return fmt.Sprintf("%v", t)
}
et := t.Etype
if int(et) < len(_typekind) {
@@ -257,12 +256,12 @@ func typecheck(n *Node, top int) (res *Node) {
// are substituted.
cycle := cycleFor(n)
for _, n1 := range cycle {
- if n1.Name != nil && !n1.Name.Param.Alias {
+ if n1.Name != nil && !n1.Name.Param.Alias() {
// Cycle is ok. But if n is an alias type and doesn't
// have a type yet, we have a recursive type declaration
// with aliases that we can't handle properly yet.
// Report an error rather than crashing later.
- if n.Name != nil && n.Name.Param.Alias && n.Type == nil {
+ if n.Name != nil && n.Name.Param.Alias() && n.Type == nil {
lineno = n.Pos
Fatalf("cannot handle alias type declaration (issue #25838): %v", n)
}
@@ -361,7 +360,7 @@ func typecheck1(n *Node, top int) (res *Node) {
ok |= ctxExpr
if n.Type == nil && n.Val().Ctype() == CTSTR {
- n.Type = types.Idealstring
+ n.Type = types.UntypedString
}
case ONONAME:
@@ -471,10 +470,10 @@ func typecheck1(n *Node, top int) (res *Node) {
return n
}
if l.Type.NotInHeap() {
- yyerror("go:notinheap map key not allowed")
+ yyerror("incomplete (or unallocatable) map key not allowed")
}
if r.Type.NotInHeap() {
- yyerror("go:notinheap map value not allowed")
+ yyerror("incomplete (or unallocatable) map value not allowed")
}
setTypeNode(n, types.NewMap(l.Type, r.Type))
@@ -491,7 +490,7 @@ func typecheck1(n *Node, top int) (res *Node) {
return n
}
if l.Type.NotInHeap() {
- yyerror("chan of go:notinheap type not allowed")
+ yyerror("chan of incomplete (or unallocatable) type not allowed")
}
setTypeNode(n, types.NewChan(l.Type, n.TChanDir()))
@@ -623,10 +622,29 @@ func typecheck1(n *Node, top int) (res *Node) {
// no defaultlit for left
// the outer context gives the type
n.Type = l.Type
+ if (l.Type == types.UntypedFloat || l.Type == types.UntypedComplex) && r.Op == OLITERAL {
+ n.Type = types.UntypedInt
+ }
break
}
+ // For "x == x && len(s)", it's better to report that "len(s)" (type int)
+ // can't be used with "&&" than to report that "x == x" (type untyped bool)
+ // can't be converted to int (see issue #41500).
+ if n.Op == OANDAND || n.Op == OOROR {
+ if !n.Left.Type.IsBoolean() {
+ yyerror("invalid operation: %v (operator %v not defined on %s)", n, n.Op, typekind(n.Left.Type))
+ n.Type = nil
+ return n
+ }
+ if !n.Right.Type.IsBoolean() {
+ yyerror("invalid operation: %v (operator %v not defined on %s)", n, n.Op, typekind(n.Right.Type))
+ n.Type = nil
+ return n
+ }
+ }
+
// ideal mixed with non-ideal
l, r = defaultlit2(l, r, false)
@@ -655,8 +673,8 @@ func typecheck1(n *Node, top int) (res *Node) {
// The conversion allocates, so only do it if the concrete type is huge.
converted := false
if r.Type.Etype != TBLANK {
- aop = assignop(l.Type, r.Type, nil)
- if aop != 0 {
+ aop, _ = assignop(l.Type, r.Type)
+ if aop != OXXX {
if r.Type.IsInterface() && !l.Type.IsInterface() && !IsComparable(l.Type) {
yyerror("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(l.Type))
n.Type = nil
@@ -677,8 +695,8 @@ func typecheck1(n *Node, top int) (res *Node) {
}
if !converted && l.Type.Etype != TBLANK {
- aop = assignop(r.Type, l.Type, nil)
- if aop != 0 {
+ aop, _ = assignop(r.Type, l.Type)
+ if aop != OXXX {
if l.Type.IsInterface() && !r.Type.IsInterface() && !IsComparable(r.Type) {
yyerror("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(r.Type))
n.Type = nil
@@ -713,7 +731,10 @@ func typecheck1(n *Node, top int) (res *Node) {
}
}
- if !okfor[op][et] {
+ if t.Etype == TIDEAL {
+ t = mixUntyped(l.Type, r.Type)
+ }
+ if dt := defaultType(t); !okfor[op][dt.Etype] {
yyerror("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(t))
n.Type = nil
return n
@@ -753,17 +774,9 @@ func typecheck1(n *Node, top int) (res *Node) {
}
}
- t = l.Type
if iscmp[n.Op] {
- // TIDEAL includes complex constant, but only OEQ and ONE are defined for complex,
- // so check that the n.op is available for complex here before doing evconst.
- if !okfor[n.Op][TCOMPLEX128] && (Isconst(l, CTCPLX) || Isconst(r, CTCPLX)) {
- yyerror("invalid operation: %v (operator %v not defined on untyped complex)", n, n.Op)
- n.Type = nil
- return n
- }
evconst(n)
- t = types.Idealbool
+ t = types.UntypedBool
if n.Op != OLITERAL {
l, r = defaultlit2(l, r, true)
n.Left = l
@@ -808,8 +821,8 @@ func typecheck1(n *Node, top int) (res *Node) {
n.Type = nil
return n
}
- if !okfor[n.Op][t.Etype] {
- yyerror("invalid operation: %v %v", n.Op, t)
+ if !okfor[n.Op][defaultType(t).Etype] {
+ yyerror("invalid operation: %v (operator %v not defined on %s)", n, n.Op, typekind(t))
n.Type = nil
return n
}
@@ -1032,13 +1045,13 @@ func typecheck1(n *Node, top int) (res *Node) {
}
if !n.Bounded() && Isconst(n.Right, CTINT) {
- x := n.Right.Int64()
+ x := n.Right.Int64Val()
if x < 0 {
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(strlit(n.Left))) {
- yyerror("invalid string index %v (out of bounds for %d-byte string)", n.Right, len(strlit(n.Left)))
+ } else if Isconst(n.Left, CTSTR) && x >= int64(len(n.Left.StringVal())) {
+ yyerror("invalid string index %v (out of bounds for %d-byte string)", n.Right, len(n.Left.StringVal()))
} else if n.Right.Val().U.(*Mpint).Cmp(maxintval[TINT]) > 0 {
yyerror("invalid %s index %v (index too large)", why, n.Right)
}
@@ -1134,11 +1147,11 @@ func typecheck1(n *Node, top int) (res *Node) {
l = defaultlit(l, types.Types[TINT])
c = defaultlit(c, types.Types[TINT])
- if Isconst(l, CTINT) && l.Int64() < 0 {
+ if Isconst(l, CTINT) && l.Int64Val() < 0 {
Fatalf("len for OSLICEHEADER must be non-negative")
}
- if Isconst(c, CTINT) && c.Int64() < 0 {
+ if Isconst(c, CTINT) && c.Int64Val() < 0 {
Fatalf("cap for OSLICEHEADER must be non-negative")
}
@@ -1187,7 +1200,7 @@ func typecheck1(n *Node, top int) (res *Node) {
if n.Left.Val().U.(*Mpint).Cmp(maxintval[TINT]) > 0 {
Fatalf("len for OMAKESLICECOPY too large")
}
- if n.Left.Int64() < 0 {
+ if n.Left.Int64Val() < 0 {
Fatalf("len for OMAKESLICECOPY must be non-negative")
}
}
@@ -1444,7 +1457,7 @@ func typecheck1(n *Node, top int) (res *Node) {
// Determine result type.
switch t.Etype {
case TIDEAL:
- n.Type = types.Idealfloat
+ n.Type = types.UntypedFloat
case TCOMPLEX64:
n.Type = types.Types[TFLOAT32]
case TCOMPLEX128:
@@ -1490,7 +1503,7 @@ func typecheck1(n *Node, top int) (res *Node) {
return n
case TIDEAL:
- t = types.Idealcomplex
+ t = types.UntypedComplex
case TFLOAT32:
t = types.Types[TCOMPLEX64]
@@ -1677,8 +1690,8 @@ func typecheck1(n *Node, top int) (res *Node) {
return n
}
var why string
- n.Op = convertop(n.Left.Op == OLITERAL, t, n.Type, &why)
- if n.Op == 0 {
+ n.Op, why = convertop(n.Left.Op == OLITERAL, t, n.Type)
+ if n.Op == OXXX {
if !n.Diag() && !n.Type.Broke() && !n.Left.Diag() {
yyerror("cannot convert %L to type %v%s", n.Left, n.Type, why)
n.SetDiag(true)
@@ -1756,7 +1769,7 @@ func typecheck1(n *Node, top int) (res *Node) {
n.Type = nil
return n
}
- if !checkmake(t, "len", l) || r != nil && !checkmake(t, "cap", r) {
+ if !checkmake(t, "len", &l) || r != nil && !checkmake(t, "cap", &r) {
n.Type = nil
return n
}
@@ -1780,7 +1793,7 @@ func typecheck1(n *Node, top int) (res *Node) {
n.Type = nil
return n
}
- if !checkmake(t, "size", l) {
+ if !checkmake(t, "size", &l) {
n.Type = nil
return n
}
@@ -1801,7 +1814,7 @@ func typecheck1(n *Node, top int) (res *Node) {
n.Type = nil
return n
}
- if !checkmake(t, "buffer", l) {
+ if !checkmake(t, "buffer", &l) {
n.Type = nil
return n
}
@@ -2051,11 +2064,6 @@ func typecheck1(n *Node, top int) (res *Node) {
n.Type = nil
return n
- case OCASE:
- ok |= ctxStmt
- typecheckslice(n.List.Slice(), ctxExpr)
- typecheckslice(n.Nbody.Slice(), ctxStmt)
-
case ODCLFUNC:
ok |= ctxStmt
typecheckfunc(n)
@@ -2068,12 +2076,6 @@ func typecheck1(n *Node, top int) (res *Node) {
ok |= ctxStmt
n.Left = typecheck(n.Left, ctxType)
checkwidth(n.Left.Type)
- if n.Left.Type != nil && n.Left.Type.NotInHeap() && n.Left.Name.Param.Pragma&NotInHeap == 0 {
- // The type contains go:notinheap types, so it
- // must be marked as such (alternatively, we
- // could silently propagate go:notinheap).
- yyerror("type %v must be go:notinheap", n.Left.Type)
- }
}
t := n.Type
@@ -2179,14 +2181,14 @@ func checksliceindex(l *Node, r *Node, tp *types.Type) bool {
}
if r.Op == OLITERAL {
- if r.Int64() < 0 {
+ if r.Int64Val() < 0 {
yyerror("invalid slice index %v (index must be non-negative)", r)
return false
- } else if tp != nil && tp.NumElem() >= 0 && r.Int64() > tp.NumElem() {
+ } else if tp != nil && tp.NumElem() >= 0 && r.Int64Val() > 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(strlit(l))) {
- yyerror("invalid slice index %v (out of bounds for %d-byte string)", r, len(strlit(l)))
+ } else if Isconst(l, CTSTR) && r.Int64Val() > int64(len(l.StringVal())) {
+ yyerror("invalid slice index %v (out of bounds for %d-byte string)", r, len(l.StringVal()))
return false
} else if r.Val().U.(*Mpint).Cmp(maxintval[TINT]) > 0 {
yyerror("invalid slice index %v (index too large)", r)
@@ -2439,15 +2441,6 @@ func derefall(t *types.Type) *types.Type {
return t
}
-type typeSymKey struct {
- t *types.Type
- s *types.Sym
-}
-
-// dotField maps (*types.Type, *types.Sym) pairs to the corresponding struct field (*types.Type with Etype==TFIELD).
-// It is a cache for use during usefield in walk.go, only enabled when field tracking.
-var dotField = map[typeSymKey]*types.Field{}
-
func lookdot(n *Node, t *types.Type, dostrcmp int) *types.Field {
s := n.Sym
@@ -2478,9 +2471,6 @@ func lookdot(n *Node, t *types.Type, dostrcmp int) *types.Field {
}
n.Xoffset = f1.Offset
n.Type = f1.Type
- if objabi.Fieldtrack_enabled > 0 {
- dotField[typeSymKey{t.Orig, s}] = f1
- }
if t.IsInterface() {
if n.Left.Type.IsPtr() {
n.Left = nod(ODEREF, n.Left, nil) // implicitstar
@@ -2489,6 +2479,8 @@ func lookdot(n *Node, t *types.Type, dostrcmp int) *types.Field {
}
n.Op = ODOTINTER
+ } else {
+ n.SetOpt(f1)
}
return f1
@@ -2508,7 +2500,7 @@ func lookdot(n *Node, t *types.Type, dostrcmp int) *types.Field {
n.Left = nod(OADDR, n.Left, nil)
n.Left.SetImplicit(true)
n.Left = typecheck(n.Left, ctxType|ctxExpr)
- } else if tt.IsPtr() && !rcvr.IsPtr() && types.Identical(tt.Elem(), rcvr) {
+ } else if tt.IsPtr() && (!rcvr.IsPtr() || rcvr.IsPtr() && rcvr.Elem().NotInHeap()) && types.Identical(tt.Elem(), rcvr) {
n.Left = nod(ODEREF, n.Left, nil)
n.Left.SetImplicit(true)
n.Left = typecheck(n.Left, ctxType|ctxExpr)
@@ -2667,7 +2659,7 @@ func typecheckaste(op Op, call *Node, isddd bool, tstruct *types.Type, nl Nodes,
return
notenough:
- if n == nil || !n.Diag() {
+ if n == nil || (!n.Diag() && n.Type != nil) {
details := errorDetails(nl, tstruct, isddd)
if call != nil {
// call is the expression being called, not the overall call.
@@ -2708,17 +2700,17 @@ func errorDetails(nl Nodes, tstruct *types.Type, isddd bool) string {
return ""
}
}
- return fmt.Sprintf("\n\thave %s\n\twant %v", nl.retsigerr(isddd), tstruct)
+ return fmt.Sprintf("\n\thave %s\n\twant %v", nl.sigerr(isddd), tstruct)
}
// sigrepr is a type's representation to the outside world,
// in string representations of return signatures
// e.g in error messages about wrong arguments to return.
-func sigrepr(t *types.Type) string {
+func sigrepr(t *types.Type, isddd bool) string {
switch t {
- case types.Idealstring:
+ case types.UntypedString:
return "string"
- case types.Idealbool:
+ case types.UntypedBool:
return "bool"
}
@@ -2729,26 +2721,29 @@ func sigrepr(t *types.Type) string {
return "number"
}
+ // Turn []T... argument to ...T for clearer error message.
+ if isddd {
+ if !t.IsSlice() {
+ Fatalf("bad type for ... argument: %v", t)
+ }
+ return "..." + t.Elem().String()
+ }
return t.String()
}
-// retsigerr returns the signature of the types
-// at the respective return call site of a function.
-func (nl Nodes) retsigerr(isddd bool) string {
+// sigerr returns the signature of the types at the call or return.
+func (nl Nodes) sigerr(isddd bool) string {
if nl.Len() < 1 {
return "()"
}
var typeStrings []string
- for _, n := range nl.Slice() {
- typeStrings = append(typeStrings, sigrepr(n.Type))
+ for i, n := range nl.Slice() {
+ isdddArg := isddd && i == nl.Len()-1
+ typeStrings = append(typeStrings, sigrepr(n.Type, isdddArg))
}
- ddd := ""
- if isddd {
- ddd = "..."
- }
- return fmt.Sprintf("(%s%s)", strings.Join(typeStrings, ", "), ddd)
+ return fmt.Sprintf("(%s)", strings.Join(typeStrings, ", "))
}
// type check composite
@@ -3135,9 +3130,14 @@ func checkassign(stmt *Node, n *Node) {
return
}
- if n.Op == ODOT && n.Left.Op == OINDEXMAP {
+ switch {
+ case n.Op == ODOT && n.Left.Op == OINDEXMAP:
yyerror("cannot assign to struct field %v in map", n)
- } else {
+ case (n.Op == OINDEX && n.Left.Type.IsString()) || n.Op == OSLICESTR:
+ yyerror("cannot assign to %v (strings are immutable)", n)
+ case n.Op == OLITERAL && n.Sym != nil && n.isGoConst():
+ yyerror("cannot assign to %v (declared const)", n)
+ default:
yyerror("cannot assign to %v", n)
}
n.Type = nil
@@ -3251,9 +3251,7 @@ func typecheckas(n *Node) {
}
func checkassignto(src *types.Type, dst *Node) {
- var why string
-
- if assignop(src, dst.Type, &why) == 0 {
+ if op, why := assignop(src, dst.Type); op == OXXX {
yyerror("cannot assign %v to %L in multiple assignment%s", src, dst, why)
return
}
@@ -3434,9 +3432,8 @@ func stringtoruneslit(n *Node) *Node {
}
var l []*Node
- s := strlit(n.Left)
i := 0
- for _, r := range s {
+ for _, r := range n.Left.StringVal() {
l = append(l, nod(OKEY, nodintconst(int64(i)), nodintconst(int64(r))))
i++
}
@@ -3491,7 +3488,7 @@ func setUnderlying(t, underlying *types.Type) {
}
// Propagate go:notinheap pragma from the Name to the Type.
- if n.Name != nil && n.Name.Param != nil && n.Name.Param.Pragma&NotInHeap != 0 {
+ if n.Name != nil && n.Name.Param != nil && n.Name.Param.Pragma()&NotInHeap != 0 {
t.SetNotInHeap(true)
}
@@ -3663,7 +3660,7 @@ func typecheckdef(n *Node) {
n.Name.Defn = typecheck(n.Name.Defn, ctxStmt) // fills in n.Type
case OTYPE:
- if p := n.Name.Param; p.Alias {
+ if p := n.Name.Param; p.Alias() {
// Type alias declaration: Simply use the rhs type - no need
// to create a new type.
// If we have a syntax error, p.Ntype may be nil.
@@ -3713,7 +3710,8 @@ ret:
n.SetWalkdef(1)
}
-func checkmake(t *types.Type, arg string, n *Node) bool {
+func checkmake(t *types.Type, arg string, np **Node) bool {
+ n := *np
if !n.Type.IsInteger() && n.Type.Etype != TIDEAL {
yyerror("non-integer %s argument in make(%v) - %v", arg, t, n.Type)
return false
@@ -3723,12 +3721,12 @@ func checkmake(t *types.Type, arg string, n *Node) bool {
// to avoid redundant "constant NNN overflows int" errors.
switch consttype(n) {
case CTINT, CTRUNE, CTFLT, CTCPLX:
- n.SetVal(toint(n.Val()))
- if n.Val().U.(*Mpint).CmpInt64(0) < 0 {
+ v := toint(n.Val()).U.(*Mpint)
+ if v.CmpInt64(0) < 0 {
yyerror("negative %s argument in make(%v)", arg, t)
return false
}
- if n.Val().U.(*Mpint).Cmp(maxintval[TINT]) > 0 {
+ if v.Cmp(maxintval[TINT]) > 0 {
yyerror("%s argument too large in make(%v)", arg, t)
return false
}
@@ -3740,6 +3738,7 @@ func checkmake(t *types.Type, arg string, n *Node) bool {
// for instance, indexlit might be called here and incorporate some
// of the bounds checks done for make.
n = defaultlit(n, types.Types[TINT])
+ *np = n
return true
}
@@ -3886,7 +3885,7 @@ func deadcodefn(fn *Node) {
return
}
case OFOR:
- if !Isconst(n.Left, CTBOOL) || n.Left.Bool() {
+ if !Isconst(n.Left, CTBOOL) || n.Left.BoolVal() {
return
}
default:
@@ -3916,7 +3915,7 @@ func deadcodeslice(nn Nodes) {
n.Left = deadcodeexpr(n.Left)
if Isconst(n.Left, CTBOOL) {
var body Nodes
- if n.Left.Bool() {
+ if n.Left.BoolVal() {
n.Rlist = Nodes{}
body = n.Nbody
} else {
@@ -3959,7 +3958,7 @@ func deadcodeexpr(n *Node) *Node {
n.Left = deadcodeexpr(n.Left)
n.Right = deadcodeexpr(n.Right)
if Isconst(n.Left, CTBOOL) {
- if n.Left.Bool() {
+ if n.Left.BoolVal() {
return n.Right // true && x => x
} else {
return n.Left // false && x => false
@@ -3969,7 +3968,7 @@ func deadcodeexpr(n *Node) *Node {
n.Left = deadcodeexpr(n.Left)
n.Right = deadcodeexpr(n.Right)
if Isconst(n.Left, CTBOOL) {
- if n.Left.Bool() {
+ if n.Left.BoolVal() {
return n.Left // true || x => true
} else {
return n.Right // false || x => x
diff --git a/src/cmd/compile/internal/gc/universe.go b/src/cmd/compile/internal/gc/universe.go
index 04861c8dd4..ff8cabd8e3 100644
--- a/src/cmd/compile/internal/gc/universe.go
+++ b/src/cmd/compile/internal/gc/universe.go
@@ -123,21 +123,21 @@ func lexinit() {
asNode(s2.Def).SetSubOp(s.op)
}
- types.Idealstring = types.New(TSTRING)
- types.Idealbool = types.New(TBOOL)
+ types.UntypedString = types.New(TSTRING)
+ types.UntypedBool = types.New(TBOOL)
types.Types[TANY] = types.New(TANY)
s := builtinpkg.Lookup("true")
s.Def = asTypesNode(nodbool(true))
asNode(s.Def).Sym = lookup("true")
asNode(s.Def).Name = new(Name)
- asNode(s.Def).Type = types.Idealbool
+ asNode(s.Def).Type = types.UntypedBool
s = builtinpkg.Lookup("false")
s.Def = asTypesNode(nodbool(false))
asNode(s.Def).Sym = lookup("false")
asNode(s.Def).Name = new(Name)
- asNode(s.Def).Type = types.Idealbool
+ asNode(s.Def).Type = types.UntypedBool
s = lookup("_")
s.Block = -100
@@ -351,7 +351,7 @@ func typeinit() {
sizeofString = Rnd(sliceLenOffset+int64(Widthptr), int64(Widthptr))
dowidth(types.Types[TSTRING])
- dowidth(types.Idealstring)
+ dowidth(types.UntypedString)
}
func makeErrorInterface() *types.Type {
diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go
index 0158af8700..a7b6e7fcb3 100644
--- a/src/cmd/compile/internal/gc/walk.go
+++ b/src/cmd/compile/internal/gc/walk.go
@@ -21,7 +21,7 @@ const zeroValSize = 1024 // must match value of runtime/map.go:maxZero
func walk(fn *Node) {
Curfn = fn
- if Debug['W'] != 0 {
+ if Debug.W != 0 {
s := fmt.Sprintf("\nbefore walk %v", Curfn.Func.Nname.Sym)
dumplist(s, Curfn.Nbody)
}
@@ -63,14 +63,14 @@ func walk(fn *Node) {
return
}
walkstmtlist(Curfn.Nbody.Slice())
- if Debug['W'] != 0 {
+ if Debug.W != 0 {
s := fmt.Sprintf("after walk %v", Curfn.Func.Nname.Sym)
dumplist(s, Curfn.Nbody)
}
zeroResults()
heapmoves()
- if Debug['W'] != 0 && Curfn.Func.Enter.Len() > 0 {
+ if Debug.W != 0 && Curfn.Func.Enter.Len() > 0 {
s := fmt.Sprintf("enter %v", Curfn.Func.Nname.Sym)
dumplist(s, Curfn.Func.Enter)
}
@@ -231,6 +231,13 @@ func walkstmt(n *Node) *Node {
case OCOPY:
n.Left = copyany(n.Left, &n.Ninit, true)
+ case OCALLFUNC, OCALLMETH, OCALLINTER:
+ if n.Left.Nbody.Len() > 0 {
+ n.Left = wrapCall(n.Left, &n.Ninit)
+ } else {
+ n.Left = walkexpr(n.Left, &n.Ninit)
+ }
+
default:
n.Left = walkexpr(n.Left, &n.Ninit)
}
@@ -329,19 +336,6 @@ func walkstmt(n *Node) *Node {
return n
}
-func isSmallMakeSlice(n *Node) bool {
- if n.Op != OMAKESLICE {
- return false
- }
- r := n.Right
- if r == nil {
- r = n.Left
- }
- t := n.Type
-
- return smallintconst(r) && (t.Elem().Width == 0 || r.Int64() < maxImplicitStackVarSize/t.Elem().Width)
-}
-
// walk the whole tree of the body of an
// expression or simple statement.
// the types expressions are calculated.
@@ -442,7 +436,7 @@ func walkexpr(n *Node, init *Nodes) *Node {
lno := setlineno(n)
- if Debug['w'] > 1 {
+ if Debug.w > 1 {
Dump("before walk expr", n)
}
@@ -480,7 +474,7 @@ opswitch:
ODEREF, OSPTR, OITAB, OIDATA, OADDR:
n.Left = walkexpr(n.Left, init)
- case OEFACE, OAND, OSUB, OMUL, OADD, OOR, OXOR, OLSH, ORSH:
+ case OEFACE, OAND, OANDNOT, OSUB, OMUL, OADD, OOR, OXOR, OLSH, ORSH:
n.Left = walkexpr(n.Left, init)
n.Right = walkexpr(n.Right, init)
@@ -558,6 +552,7 @@ opswitch:
case OCALLINTER, OCALLFUNC, OCALLMETH:
if n.Op == OCALLINTER {
usemethod(n)
+ markUsedIfaceMethod(n)
}
if n.Op == OCALLFUNC && n.Left.Op == OCLOSURE {
@@ -641,7 +636,7 @@ opswitch:
// x = append(...)
r := n.Right
if r.Type.Elem().NotInHeap() {
- yyerror("%v is go:notinheap; heap allocation disallowed", r.Type.Elem())
+ yyerror("%v can't be allocated in Go; it is incomplete (or unallocatable)", r.Type.Elem())
}
switch {
case isAppendOfMake(r):
@@ -798,8 +793,8 @@ opswitch:
fromType := n.Left.Type
toType := n.Type
- if !fromType.IsInterface() {
- markTypeUsedInInterface(fromType)
+ if !fromType.IsInterface() && !Curfn.Func.Nname.isBlank() { // skip unnamed functions (func _())
+ markTypeUsedInInterface(fromType, Curfn.Func.lsym)
}
// typeword generates the type word of the interface value.
@@ -954,11 +949,11 @@ opswitch:
case OCONV, OCONVNOP:
n.Left = walkexpr(n.Left, init)
if n.Op == OCONVNOP && checkPtr(Curfn, 1) {
- if n.Type.IsPtr() && n.Left.Type.Etype == TUNSAFEPTR { // unsafe.Pointer to *T
+ if n.Type.IsPtr() && n.Left.Type.IsUnsafePtr() { // unsafe.Pointer to *T
n = walkCheckPtrAlignment(n, init, nil)
break
}
- if n.Type.Etype == TUNSAFEPTR && n.Left.Type.Etype == TUINTPTR { // uintptr to unsafe.Pointer
+ if n.Type.IsUnsafePtr() && n.Left.Type.IsUintptr() { // uintptr to unsafe.Pointer
n = walkCheckPtrArithmetic(n, init)
break
}
@@ -970,14 +965,6 @@ opswitch:
fn := basicnames[param] + "to" + basicnames[result]
n = conv(mkcall(fn, types.Types[result], init, conv(n.Left, types.Types[param])), n.Type)
- case OANDNOT:
- n.Left = walkexpr(n.Left, init)
- n.Op = OAND
- n.SetImplicit(true) // for walkCheckPtrArithmetic
- n.Right = nod(OBITNOT, n.Right, nil)
- n.Right = typecheck(n.Right, ctxExpr)
- n.Right = walkexpr(n.Right, init)
-
case ODIV, OMOD:
n.Left = walkexpr(n.Left, init)
n.Right = walkexpr(n.Right, init)
@@ -1002,11 +989,11 @@ opswitch:
// runtime calls late in SSA processing.
if Widthreg < 8 && (et == TINT64 || et == TUINT64) {
if n.Right.Op == OLITERAL {
- // Leave div/mod by constant powers of 2.
+ // Leave div/mod by constant powers of 2 or small 16-bit constants.
// The SSA backend will handle those.
switch et {
case TINT64:
- c := n.Right.Int64()
+ c := n.Right.Int64Val()
if c < 0 {
c = -c
}
@@ -1014,7 +1001,10 @@ opswitch:
break opswitch
}
case TUINT64:
- c := uint64(n.Right.Int64())
+ c := uint64(n.Right.Int64Val())
+ if c < 1<<16 {
+ break opswitch
+ }
if c != 0 && c&(c-1) == 0 {
break opswitch
}
@@ -1054,15 +1044,15 @@ opswitch:
}
if t.IsArray() {
n.SetBounded(bounded(r, t.NumElem()))
- if Debug['m'] != 0 && n.Bounded() && !Isconst(n.Right, CTINT) {
+ if Debug.m != 0 && n.Bounded() && !Isconst(n.Right, CTINT) {
Warn("index bounds check elided")
}
if smallintconst(n.Right) && !n.Bounded() {
yyerror("index out of bounds")
}
} else if Isconst(n.Left, CTSTR) {
- n.SetBounded(bounded(r, int64(len(strlit(n.Left)))))
- if Debug['m'] != 0 && n.Bounded() && !Isconst(n.Right, CTINT) {
+ n.SetBounded(bounded(r, int64(len(n.Left.StringVal()))))
+ if Debug.m != 0 && n.Bounded() && !Isconst(n.Right, CTINT) {
Warn("index bounds check elided")
}
if smallintconst(n.Right) && !n.Bounded() {
@@ -1123,7 +1113,7 @@ opswitch:
n.List.SetSecond(walkexpr(n.List.Second(), init))
case OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR:
- checkSlice := checkPtr(Curfn, 1) && n.Op == OSLICE3ARR && n.Left.Op == OCONVNOP && n.Left.Left.Type.Etype == TUNSAFEPTR
+ checkSlice := checkPtr(Curfn, 1) && n.Op == OSLICE3ARR && n.Left.Op == OCONVNOP && n.Left.Left.Type.IsUnsafePtr()
if checkSlice {
n.Left.Left = walkexpr(n.Left.Left, init)
} else {
@@ -1157,7 +1147,7 @@ opswitch:
case ONEW:
if n.Type.Elem().NotInHeap() {
- yyerror("%v is go:notinheap; heap allocation disallowed", n.Type.Elem())
+ yyerror("%v can't be allocated in Go; it is incomplete (or unallocatable)", n.Type.Elem())
}
if n.Esc == EscNone {
if n.Type.Elem().Width >= maxImplicitStackVarSize {
@@ -1328,11 +1318,11 @@ opswitch:
}
t := n.Type
if t.Elem().NotInHeap() {
- yyerror("%v is go:notinheap; heap allocation disallowed", t.Elem())
+ yyerror("%v can't be allocated in Go; it is incomplete (or unallocatable)", t.Elem())
}
if n.Esc == EscNone {
- if !isSmallMakeSlice(n) {
- Fatalf("non-small OMAKESLICE with EscNone: %v", n)
+ if why := heapAllocReason(n); why != "" {
+ Fatalf("%v has EscNone, but %v", n, why)
}
// var arr [r]T
// n = arr[:l]
@@ -1405,7 +1395,7 @@ opswitch:
t := n.Type
if t.Elem().NotInHeap() {
- yyerror("%v is go:notinheap; heap allocation disallowed", t.Elem())
+ yyerror("%v can't be allocated in Go; it is incomplete (or unallocatable)", t.Elem())
}
length := conv(n.Left, types.Types[TINT])
@@ -1477,7 +1467,7 @@ opswitch:
} else {
// slicebytetostring(*[32]byte, ptr *byte, n int) string
n.Left = cheapexpr(n.Left, init)
- ptr, len := n.Left.slicePtrLen()
+ ptr, len := n.Left.backingArrayPtrLen()
n = mkcall("slicebytetostring", n.Type, init, a, ptr, len)
}
@@ -1490,13 +1480,13 @@ opswitch:
}
// slicebytetostringtmp(ptr *byte, n int) string
n.Left = cheapexpr(n.Left, init)
- ptr, len := n.Left.slicePtrLen()
+ ptr, len := n.Left.backingArrayPtrLen()
n = mkcall("slicebytetostringtmp", n.Type, init, ptr, len)
case OSTR2BYTES:
s := n.Left
if Isconst(s, CTSTR) {
- sc := strlit(s)
+ sc := s.StringVal()
// Allocate a [n]byte of the right size.
t := types.NewArray(types.Types[TUINT8], int64(len(sc)))
@@ -1604,7 +1594,7 @@ opswitch:
updateHasCall(n)
- if Debug['w'] != 0 && n != nil {
+ if Debug.w != 0 && n != nil {
Dump("after walk expr", n)
}
@@ -1614,8 +1604,27 @@ opswitch:
// markTypeUsedInInterface marks that type t is converted to an interface.
// This information is used in the linker in dead method elimination.
-func markTypeUsedInInterface(t *types.Type) {
- typenamesym(t).Linksym().Set(obj.AttrUsedInIface, true)
+func markTypeUsedInInterface(t *types.Type, from *obj.LSym) {
+ tsym := typenamesym(t).Linksym()
+ // Emit a marker relocation. The linker will know the type is converted
+ // to an interface if "from" is reachable.
+ r := obj.Addrel(from)
+ r.Sym = tsym
+ r.Type = objabi.R_USEIFACE
+}
+
+// markUsedIfaceMethod marks that an interface method is used in the current
+// function. n is OCALLINTER node.
+func markUsedIfaceMethod(n *Node) {
+ ityp := n.Left.Left.Type
+ tsym := typenamesym(ityp).Linksym()
+ r := obj.Addrel(Curfn.Func.lsym)
+ r.Sym = tsym
+ // n.Left.Xoffset is the method index * Widthptr (the offset of code pointer
+ // in itab).
+ midx := n.Left.Xoffset / int64(Widthptr)
+ r.Add = ifaceMethodOffset(ityp, midx)
+ r.Type = objabi.R_USEIFACEMETHOD
}
// rtconvfn returns the parameter and result types that will be used by a
@@ -1905,7 +1914,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, strlit(s[i]))
+ strs = append(strs, s[i].StringVal())
i++
}
if len(strs) > 0 {
@@ -1951,7 +1960,17 @@ func walkprint(nn *Node, init *Nodes) *Node {
on = syslook("printiface")
}
on = substArgTypes(on, n.Type) // any-1
- case TPTR, TCHAN, TMAP, TFUNC, TUNSAFEPTR:
+ case TPTR:
+ if n.Type.Elem().NotInHeap() {
+ on = syslook("printuintptr")
+ n = nod(OCONV, n, nil)
+ n.Type = types.Types[TUNSAFEPTR]
+ n = nod(OCONV, n, nil)
+ n.Type = types.Types[TUINTPTR]
+ break
+ }
+ fallthrough
+ case TCHAN, TMAP, TFUNC, TUNSAFEPTR:
on = syslook("printpointer")
on = substArgTypes(on, n.Type) // any-1
case TSLICE:
@@ -1974,7 +1993,7 @@ func walkprint(nn *Node, init *Nodes) *Node {
case TSTRING:
cs := ""
if Isconst(n, CTSTR) {
- cs = strlit(n)
+ cs = n.StringVal()
}
switch cs {
case " ":
@@ -2143,7 +2162,7 @@ func reorder3(all []*Node) []*Node {
// The result of reorder3save MUST be assigned back to n, e.g.
// n.Left = reorder3save(n.Left, all, i, early)
func reorder3save(n *Node, all []*Node, i int, early *[]*Node) *Node {
- if !aliased(n, all, i) {
+ if !aliased(n, all[:i]) {
return n
}
@@ -2175,73 +2194,75 @@ func outervalue(n *Node) *Node {
}
}
-// Is it possible that the computation of n might be
-// affected by writes in as up to but not including the ith element?
-func aliased(n *Node, all []*Node, i int) bool {
- if n == nil {
+// Is it possible that the computation of r might be
+// affected by assignments in all?
+func aliased(r *Node, all []*Node) bool {
+ if r == nil {
return false
}
// Treat all fields of a struct as referring to the whole struct.
// We could do better but we would have to keep track of the fields.
- for n.Op == ODOT {
- n = n.Left
+ for r.Op == ODOT {
+ r = r.Left
}
// Look for obvious aliasing: a variable being assigned
// during the all list and appearing in n.
- // Also record whether there are any writes to main memory.
- // Also record whether there are any writes to variables
- // whose addresses have been taken.
+ // Also record whether there are any writes to addressable
+ // memory (either main memory or variables whose addresses
+ // have been taken).
memwrite := false
- varwrite := false
- for _, an := range all[:i] {
- a := outervalue(an.Left)
-
- for a.Op == ODOT {
- a = a.Left
+ for _, as := range all {
+ // We can ignore assignments to blank.
+ if as.Left.isBlank() {
+ continue
}
- if a.Op != ONAME {
+ l := outervalue(as.Left)
+ if l.Op != ONAME {
memwrite = true
continue
}
- switch n.Class() {
+ switch l.Class() {
default:
- varwrite = true
+ Fatalf("unexpected class: %v, %v", l, l.Class())
+
+ case PAUTOHEAP, PEXTERN:
+ memwrite = true
continue
case PAUTO, PPARAM, PPARAMOUT:
- if n.Name.Addrtaken() {
- varwrite = true
+ if l.Name.Addrtaken() {
+ memwrite = true
continue
}
- if vmatch2(a, n) {
- // Direct hit.
+ if vmatch2(l, r) {
+ // Direct hit: l appears in r.
return true
}
}
}
- // The variables being written do not appear in n.
- // However, n might refer to computed addresses
+ // The variables being written do not appear in r.
+ // However, r might refer to computed addresses
// that are being written.
// If no computed addresses are affected by the writes, no aliasing.
- if !memwrite && !varwrite {
+ if !memwrite {
return false
}
- // If n does not refer to computed addresses
- // (that is, if n only refers to variables whose addresses
+ // If r does not refer to computed addresses
+ // (that is, if r only refers to variables whose addresses
// have not been taken), no aliasing.
- if varexpr(n) {
+ if varexpr(r) {
return false
}
- // Otherwise, both the writes and n refer to computed memory addresses.
+ // Otherwise, both the writes and r refer to computed memory addresses.
// Assume that they might conflict.
return true
}
@@ -2629,7 +2650,7 @@ func addstr(n *Node, init *Nodes) *Node {
sz := int64(0)
for _, n1 := range n.List.Slice() {
if n1.Op == OLITERAL {
- sz += int64(len(strlit(n1)))
+ sz += int64(len(n1.StringVal()))
}
}
@@ -2757,36 +2778,25 @@ func appendslice(n *Node, init *Nodes) *Node {
// instantiate typedslicecopy(typ *type, dstPtr *any, dstLen int, srcPtr *any, srcLen int) int
fn := syslook("typedslicecopy")
fn = substArgTypes(fn, l1.Type.Elem(), l2.Type.Elem())
- ptr1, len1 := nptr1.slicePtrLen()
- ptr2, len2 := nptr2.slicePtrLen()
+ ptr1, len1 := nptr1.backingArrayPtrLen()
+ ptr2, len2 := nptr2.backingArrayPtrLen()
ncopy = mkcall1(fn, types.Types[TINT], &nodes, typename(elemtype), ptr1, len1, ptr2, len2)
-
} else if instrumenting && !compiling_runtime {
- // rely on runtime to instrument copy.
- // copy(s[len(l1):], l2)
+ // rely on runtime to instrument:
+ // copy(s[len(l1):], l2)
+ // l2 can be a slice or string.
nptr1 := nod(OSLICE, s, nil)
nptr1.Type = s.Type
nptr1.SetSliceBounds(nod(OLEN, l1, nil), nil, nil)
nptr1 = cheapexpr(nptr1, &nodes)
-
nptr2 := l2
- if l2.Type.IsString() {
- // instantiate func slicestringcopy(toPtr *byte, toLen int, fr string) int
- fn := syslook("slicestringcopy")
- ptr, len := nptr1.slicePtrLen()
- str := nod(OCONVNOP, nptr2, nil)
- str.Type = types.Types[TSTRING]
- ncopy = mkcall1(fn, types.Types[TINT], &nodes, ptr, len, str)
- } else {
- // instantiate func slicecopy(to any, fr any, wid uintptr) int
- fn := syslook("slicecopy")
- fn = substArgTypes(fn, l1.Type.Elem(), l2.Type.Elem())
- ptr1, len1 := nptr1.slicePtrLen()
- ptr2, len2 := nptr2.slicePtrLen()
- ncopy = mkcall1(fn, types.Types[TINT], &nodes, ptr1, len1, ptr2, len2, nodintconst(elemtype.Width))
- }
+ ptr1, len1 := nptr1.backingArrayPtrLen()
+ ptr2, len2 := nptr2.backingArrayPtrLen()
+ fn := syslook("slicecopy")
+ fn = substArgTypes(fn, ptr1.Type.Elem(), ptr2.Type.Elem())
+ ncopy = mkcall1(fn, types.Types[TINT], &nodes, ptr1, len1, ptr2, len2, nodintconst(elemtype.Width))
} else {
// memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T))
nptr1 := nod(OINDEX, s, nod(OLEN, l1, nil))
@@ -2814,7 +2824,7 @@ func appendslice(n *Node, init *Nodes) *Node {
// isAppendOfMake reports whether n is of the form append(x , make([]T, y)...).
// isAppendOfMake assumes n has already been typechecked.
func isAppendOfMake(n *Node) bool {
- if Debug['N'] != 0 || instrumenting {
+ if Debug.N != 0 || instrumenting {
return false
}
@@ -3085,28 +3095,25 @@ func copyany(n *Node, init *Nodes, runtimecall bool) *Node {
Curfn.Func.setWBPos(n.Pos)
fn := writebarrierfn("typedslicecopy", n.Left.Type.Elem(), n.Right.Type.Elem())
n.Left = cheapexpr(n.Left, init)
- ptrL, lenL := n.Left.slicePtrLen()
+ ptrL, lenL := n.Left.backingArrayPtrLen()
n.Right = cheapexpr(n.Right, init)
- ptrR, lenR := n.Right.slicePtrLen()
+ ptrR, lenR := n.Right.backingArrayPtrLen()
return mkcall1(fn, n.Type, init, typename(n.Left.Type.Elem()), ptrL, lenL, ptrR, lenR)
}
if runtimecall {
- if n.Right.Type.IsString() {
- fn := syslook("slicestringcopy")
- n.Left = cheapexpr(n.Left, init)
- ptr, len := n.Left.slicePtrLen()
- str := nod(OCONVNOP, n.Right, nil)
- str.Type = types.Types[TSTRING]
- return mkcall1(fn, n.Type, init, ptr, len, str)
- }
+ // rely on runtime to instrument:
+ // copy(n.Left, n.Right)
+ // n.Right can be a slice or string.
- fn := syslook("slicecopy")
- fn = substArgTypes(fn, n.Left.Type.Elem(), n.Right.Type.Elem())
n.Left = cheapexpr(n.Left, init)
- ptrL, lenL := n.Left.slicePtrLen()
+ ptrL, lenL := n.Left.backingArrayPtrLen()
n.Right = cheapexpr(n.Right, init)
- ptrR, lenR := n.Right.slicePtrLen()
+ ptrR, lenR := n.Right.backingArrayPtrLen()
+
+ fn := syslook("slicecopy")
+ fn = substArgTypes(fn, ptrL.Type.Elem(), ptrR.Type.Elem())
+
return mkcall1(fn, n.Type, init, ptrL, lenL, ptrR, lenR, nodintconst(n.Left.Type.Elem().Width))
}
@@ -3437,7 +3444,7 @@ func walkcompare(n *Node, init *Nodes) *Node {
func tracecmpArg(n *Node, t *types.Type, init *Nodes) *Node {
// Ugly hack to avoid "constant -1 overflows uintptr" errors, etc.
- if n.Op == OLITERAL && n.Type.IsSigned() && n.Int64() < 0 {
+ if n.Op == OLITERAL && n.Type.IsSigned() && n.Int64Val() < 0 {
n = copyexpr(n, n.Type, init)
}
@@ -3507,7 +3514,7 @@ func walkcompareString(n *Node, init *Nodes) *Node {
// Length-only checks are ok, though.
maxRewriteLen = 0
}
- if s := strlit(cs); len(s) <= maxRewriteLen {
+ if s := cs.StringVal(); len(s) <= maxRewriteLen {
if len(s) > 0 {
ncs = safeexpr(ncs, init)
}
@@ -3602,26 +3609,32 @@ func bounded(n *Node, max int64) bool {
bits := int32(8 * n.Type.Width)
if smallintconst(n) {
- v := n.Int64()
+ v := n.Int64Val()
return 0 <= v && v < max
}
switch n.Op {
- case OAND:
+ case OAND, OANDNOT:
v := int64(-1)
- if smallintconst(n.Left) {
- v = n.Left.Int64()
- } else if smallintconst(n.Right) {
- v = n.Right.Int64()
+ switch {
+ case smallintconst(n.Left):
+ v = n.Left.Int64Val()
+ case smallintconst(n.Right):
+ v = n.Right.Int64Val()
+ if n.Op == OANDNOT {
+ v = ^v
+ if !sign {
+ v &= 1<<uint(bits) - 1
+ }
+ }
}
-
if 0 <= v && v < max {
return true
}
case OMOD:
if !sign && smallintconst(n.Right) {
- v := n.Right.Int64()
+ v := n.Right.Int64Val()
if 0 <= v && v <= max {
return true
}
@@ -3629,7 +3642,7 @@ func bounded(n *Node, max int64) bool {
case ODIV:
if !sign && smallintconst(n.Right) {
- v := n.Right.Int64()
+ v := n.Right.Int64Val()
for bits > 0 && v >= 2 {
bits--
v >>= 1
@@ -3638,7 +3651,7 @@ func bounded(n *Node, max int64) bool {
case ORSH:
if !sign && smallintconst(n.Right) {
- v := n.Right.Int64()
+ v := n.Right.Int64Val()
if v > int64(bits) {
return true
}
@@ -3694,6 +3707,8 @@ func usemethod(n *Node) {
// Also need to check for reflect package itself (see Issue #38515).
if s := res0.Type.Sym; s != nil && s.Name == "Method" && isReflectPkg(s.Pkg) {
Curfn.Func.SetReflectMethod(true)
+ // The LSym is initialized at this point. We need to set the attribute on the LSym.
+ Curfn.Func.lsym.Set(obj.AttrReflectMethod, true)
}
}
@@ -3719,10 +3734,13 @@ func usefield(n *Node) {
if t.IsPtr() {
t = t.Elem()
}
- field := dotField[typeSymKey{t.Orig, n.Sym}]
+ field := n.Opt().(*types.Field)
if field == nil {
Fatalf("usefield %v %v without paramfld", n.Left.Type, n.Sym)
}
+ if field.Sym != n.Sym || field.Offset != n.Xoffset {
+ Fatalf("field inconsistency: %v,%v != %v,%v", field.Sym, field.Offset, n.Sym, n.Xoffset)
+ }
if !strings.Contains(field.Note, "go:\"track\"") {
return
}
@@ -3857,6 +3875,14 @@ func candiscard(n *Node) bool {
// builtin(a1, a2, a3)
// }(x, y, z)
// for print, println, and delete.
+//
+// Rewrite
+// go f(x, y, uintptr(unsafe.Pointer(z)))
+// into
+// go func(a1, a2, a3) {
+// builtin(a1, a2, uintptr(a3))
+// }(x, y, unsafe.Pointer(z))
+// for function contains unsafe-uintptr arguments.
var wrapCall_prgen int
@@ -3868,9 +3894,27 @@ func wrapCall(n *Node, init *Nodes) *Node {
init.AppendNodes(&n.Ninit)
}
+ isBuiltinCall := n.Op != OCALLFUNC && n.Op != OCALLMETH && n.Op != OCALLINTER
+
+ // Turn f(a, b, []T{c, d, e}...) back into f(a, b, c, d, e).
+ if !isBuiltinCall && n.IsDDD() {
+ last := n.List.Len() - 1
+ if va := n.List.Index(last); va.Op == OSLICELIT {
+ n.List.Set(append(n.List.Slice()[:last], va.List.Slice()...))
+ n.SetIsDDD(false)
+ }
+ }
+
+ // origArgs keeps track of what argument is uintptr-unsafe/unsafe-uintptr conversion.
+ origArgs := make([]*Node, n.List.Len())
t := nod(OTFUNC, nil, nil)
for i, arg := range n.List.Slice() {
s := lookupN("a", i)
+ if !isBuiltinCall && arg.Op == OCONVNOP && arg.Type.IsUintptr() && arg.Left.Type.IsUnsafePtr() {
+ origArgs[i] = arg
+ arg = arg.Left
+ n.List.SetIndex(i, arg)
+ }
t.List.Append(symfield(s, arg.Type))
}
@@ -3878,10 +3922,23 @@ func wrapCall(n *Node, init *Nodes) *Node {
sym := lookupN("wrap·", wrapCall_prgen)
fn := dclfunc(sym, t)
- a := nod(n.Op, nil, nil)
- a.List.Set(paramNnames(t.Type))
- a = typecheck(a, ctxStmt)
- fn.Nbody.Set1(a)
+ args := paramNnames(t.Type)
+ for i, origArg := range origArgs {
+ if origArg == nil {
+ continue
+ }
+ arg := nod(origArg.Op, args[i], nil)
+ arg.Type = origArg.Type
+ args[i] = arg
+ }
+ call := nod(n.Op, nil, nil)
+ if !isBuiltinCall {
+ call.Op = OCALL
+ call.Left = n.Left
+ call.SetIsDDD(n.IsDDD())
+ }
+ call.List.Set(args)
+ fn.Nbody.Set1(call)
funcbody()
@@ -3889,12 +3946,12 @@ func wrapCall(n *Node, init *Nodes) *Node {
typecheckslice(fn.Nbody.Slice(), ctxStmt)
xtop = append(xtop, fn)
- a = nod(OCALL, nil, nil)
- a.Left = fn.Func.Nname
- a.List.Set(n.List.Slice())
- a = typecheck(a, ctxStmt)
- a = walkexpr(a, init)
- return a
+ call = nod(OCALL, nil, nil)
+ call.Left = fn.Func.Nname
+ call.List.Set(n.List.Slice())
+ call = typecheck(call, ctxStmt)
+ call = walkexpr(call, init)
+ return call
}
// substArgTypes substitutes the given list of types for
@@ -3933,7 +3990,7 @@ func canMergeLoads() bool {
// isRuneCount reports whether n is of the form len([]rune(string)).
// These are optimized into a call to runtime.countrunes.
func isRuneCount(n *Node) bool {
- return Debug['N'] == 0 && !instrumenting && n.Op == OLEN && n.Left.Op == OSTR2RUNES
+ return Debug.N == 0 && !instrumenting && n.Op == OLEN && n.Left.Op == OSTR2RUNES
}
func walkCheckPtrAlignment(n *Node, init *Nodes, count *Node) *Node {
@@ -4002,14 +4059,10 @@ func walkCheckPtrArithmetic(n *Node, init *Nodes) *Node {
case OADD:
walk(n.Left)
walk(n.Right)
- case OSUB:
+ case OSUB, OANDNOT:
walk(n.Left)
- case OAND:
- if n.Implicit() { // was OANDNOT
- walk(n.Left)
- }
case OCONVNOP:
- if n.Left.Type.Etype == TUNSAFEPTR {
+ if n.Left.Type.IsUnsafePtr() {
n.Left = cheapexpr(n.Left, init)
originals = append(originals, convnop(n.Left, types.Types[TUNSAFEPTR]))
}