cmd/compile: make funccompile non-reentrant

Currently, there's awkward reentrancy issue with funccompile:

    funccompile -> compile -> dtypesym -> geneq/genhash/genwrapper -> funccompile

Though it's not a problem at this moment, some attempts by @mdempsky to
move order/walk/instrument into buildssa was failed, due to SSA cache
corruption.

This commit fixes that reentrancy issue, by making generated functions
to be pumped through the same compile workqueue that normal functions
are compiled. We do this by adding them to xtop, instead of calling
funccompile directly in geneq/genhash/genwrapper. In dumpdata, we look
for uncompiled functions in xtop instead of compilequeue, then finish
compiling them.

Updates #38463
Fixes #33485

Change-Id: Ic9f0ce45b56ae2ff3862f17fd979253ddc144bb5
Reviewed-on: https://go-review.googlesource.com/c/go/+/254617
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Keith Randall <khr@golang.org>
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
diff --git a/src/cmd/compile/internal/gc/alg.go b/src/cmd/compile/internal/gc/alg.go
index c9d71ea..6302b88 100644
--- a/src/cmd/compile/internal/gc/alg.go
+++ b/src/cmd/compile/internal/gc/alg.go
@@ -392,7 +392,7 @@
 	}
 
 	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.
@@ -754,7 +754,7 @@
 	// 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)
diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go
index af5037c..b55331a 100644
--- a/src/cmd/compile/internal/gc/obj.go
+++ b/src/cmd/compile/internal/gc/obj.go
@@ -113,12 +113,16 @@
 
 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()
 
@@ -129,9 +133,19 @@
 	// 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 +163,16 @@
 	}
 
 	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) {
diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go
index d3ba53f..5a5833d 100644
--- a/src/cmd/compile/internal/gc/subr.go
+++ b/src/cmd/compile/internal/gc/subr.go
@@ -1615,7 +1615,7 @@
 	escapeFuncs([]*Node{fn}, false)
 
 	Curfn = nil
-	funccompile(fn)
+	xtop = append(xtop, fn)
 }
 
 func paramNnames(ft *types.Type) []*Node {