cmd/compile: emit itabs and itablinks

See #14874

This change tells the compiler to emit itab and itablink symbols in
situations where they could be useful; however the compiled code does
not actually make use of the new symbols yet.

Change-Id: I0db3e6ec0cb1f3b7cebd4c60229e4a48372fe586
Reviewed-on: https://go-review.googlesource.com/20888
Reviewed-by: David Crawshaw <crawshaw@golang.org>
Run-TryBot: Michel Lespinasse <walken@google.com>
diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go
index 448a0fd..5925208 100644
--- a/src/cmd/compile/internal/gc/go.go
+++ b/src/cmd/compile/internal/gc/go.go
@@ -250,6 +250,10 @@
 
 var itabpkg *Pkg // fake pkg for itab cache
 
+var itab2pkg *Pkg // fake pkg for itab entries
+
+var itablinkpkg *Pkg // fake package for runtime itab entries
+
 var Runtimepkg *Pkg // package runtime
 
 var racepkg *Pkg // package runtime/race
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index 09b433d..5c5e5ac 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -124,14 +124,21 @@
 
 	// pseudo-packages used in symbol tables
 	itabpkg = mkpkg("go.itab")
-
 	itabpkg.Name = "go.itab"
 	itabpkg.Prefix = "go.itab" // not go%2eitab
 
+	itab2pkg = mkpkg("go.itab2")
+	itab2pkg.Name = "go.itab2"
+	itab2pkg.Prefix = "go.itab2" // not go%2eitab2
+
 	typelinkpkg = mkpkg("go.typelink")
 	typelinkpkg.Name = "go.typelink"
 	typelinkpkg.Prefix = "go.typelink" // not go%2etypelink
 
+	itablinkpkg = mkpkg("go.itablink")
+	itablinkpkg.Name = "go.itablink"
+	itablinkpkg.Prefix = "go.itablink" // not go%2eitablink
+
 	trackpkg = mkpkg("go.track")
 
 	trackpkg.Name = "go.track"
diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go
index 4239d40..ecf98b9 100644
--- a/src/cmd/compile/internal/gc/reflect.go
+++ b/src/cmd/compile/internal/gc/reflect.go
@@ -13,8 +13,14 @@
 	"strings"
 )
 
+type itabEntry struct {
+	t, itype *Type
+	sym      *Sym
+}
+
 // runtime interface and reflection data structures
 var signatlist []*Node
+var itabs []itabEntry
 
 // byMethodNameAndPackagePath sorts method signatures by name, then package path.
 type byMethodNameAndPackagePath []*Sig
@@ -919,13 +925,9 @@
 	}
 	s := typesym(t)
 	if s.Def == nil {
-		n := Nod(ONAME, nil, nil)
-		n.Sym = s
+		n := newname(s)
 		n.Type = Types[TUINT8]
-		n.Addable = true
-		n.Ullman = 1
 		n.Class = PEXTERN
-		n.Xoffset = 0
 		n.Typecheck = 1
 		s.Def = n
 
@@ -945,6 +947,23 @@
 	return n
 }
 
+func itabnamesym(t, itype *Type) *Sym {
+	if t == nil || (Isptr[t.Etype] && t.Type == nil) || isideal(t) {
+		Fatalf("itabname %v", t)
+	}
+	s := Pkglookup(Tconv(t, FmtLeft)+","+Tconv(itype, FmtLeft), itab2pkg)
+	if s.Def == nil {
+		n := newname(s)
+		n.Type = Types[TUINT8]
+		n.Class = PEXTERN
+		n.Typecheck = 1
+		s.Def = n
+
+		itabs = append(itabs, itabEntry{t: t, itype: itype, sym: s})
+	}
+	return s.Def.Sym
+}
+
 // isreflexive reports whether t has a reflexive equality operator.
 // That is, if x==x for all x of type t.
 func isreflexive(t *Type) bool {
@@ -1320,6 +1339,30 @@
 		}
 	}
 
+	// process itabs
+	for _, i := range itabs {
+		// dump empty itab symbol into i.sym
+		// type itab struct {
+		//   inter  *interfacetype
+		//   _type  *_type
+		//   link   *itab
+		//   bad    int32
+		//   unused int32
+		//   fun    [1]uintptr // variable sized
+		// }
+		o := dsymptr(i.sym, 0, dtypesym(i.itype), 0)
+		o = dsymptr(i.sym, o, dtypesym(i.t), 0)
+		o += Widthptr + 8                      // skip link/bad/unused fields
+		o += len(imethods(i.itype)) * Widthptr // skip fun method pointers
+		// at runtime the itab will contain pointers to types, other itabs and
+		// method functions. None are allocated on heap, so we can use obj.NOPTR.
+		ggloblsym(i.sym, int32(o), int16(obj.DUPOK|obj.NOPTR))
+
+		ilink := Pkglookup(Tconv(i.t, FmtLeft)+","+Tconv(i.itype, FmtLeft), itablinkpkg)
+		dsymptr(ilink, 0, i.sym, 0)
+		ggloblsym(ilink, int32(Widthptr), int16(obj.DUPOK|obj.RODATA))
+	}
+
 	// generate import strings for imported packages
 	for _, p := range pkgs {
 		if p.Direct {
diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go
index 6d077d5..90ed401 100644
--- a/src/cmd/compile/internal/gc/walk.go
+++ b/src/cmd/compile/internal/gc/walk.go
@@ -996,6 +996,10 @@
 			break
 		}
 
+		if isdirectiface(n.Left.Type) {
+			itabnamesym(n.Left.Type, n.Type)
+		}
+
 		var ll []*Node
 		if !Isinter(n.Left.Type) {
 			ll = append(ll, typename(n.Left.Type))
diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go
index a54dc55..e1b1c83 100644
--- a/src/runtime/runtime2.go
+++ b/src/runtime/runtime2.go
@@ -577,6 +577,8 @@
 
 // layout of Itab known to compilers
 // allocated in non-garbage-collected memory
+// Needs to be in sync with
+// ../cmd/compile/internal/gc/reflect.go:/^func.dumptypestructs.
 type itab struct {
 	inter  *interfacetype
 	_type  *_type