Commit d0b59deb authored by Russ Cox's avatar Russ Cox

cmd/internal/gc: replace hash tables with Go maps

The C version of the compiler had just one hash table,
indexed by a (name string, pkg *Pkg) pair.
Because we always know the pkg during a lookup,
replace the one table with a per-Pkg map[string]*Sym.
This also lets us do non-allocating []byte key lookups.

This CL *does* change the generated object files.
In the old code, export data and init calls were emitted
in "hash table order". Now they are emitted in the order
in which they were added to the table.

Change-Id: I5a48d5c9add996dc43ad04a905641d901522de0b
Reviewed-on: https://go-review.googlesource.com/6600Reviewed-by: default avatarRob Pike <r@golang.org>
parent bed1f90d
...@@ -372,12 +372,9 @@ func dumpexport() { ...@@ -372,12 +372,9 @@ func dumpexport() {
} }
fmt.Fprintf(bout, "\n") fmt.Fprintf(bout, "\n")
var p *Pkg for _, p := range pkgs {
for i := int32(0); i < int32(len(phash)); i++ { if p.Direct != 0 {
for p = phash[i]; p != nil; p = p.Link { dumppkg(p)
if p.Direct != 0 {
dumppkg(p)
}
} }
} }
...@@ -432,6 +429,8 @@ func pkgtype(s *Sym) *Type { ...@@ -432,6 +429,8 @@ func pkgtype(s *Sym) *Type {
return s.Def.Type return s.Def.Type
} }
var numImport = make(map[string]int)
func importimport(s *Sym, path string) { func importimport(s *Sym, path string) {
// Informational: record package name // Informational: record package name
// associated with import path, for use in // associated with import path, for use in
...@@ -443,7 +442,7 @@ func importimport(s *Sym, path string) { ...@@ -443,7 +442,7 @@ func importimport(s *Sym, path string) {
p := mkpkg(path) p := mkpkg(path)
if p.Name == "" { if p.Name == "" {
p.Name = s.Name p.Name = s.Name
Pkglookup(s.Name, nil).Npkg++ numImport[s.Name]++
} else if p.Name != s.Name { } else if p.Name != s.Name {
Yyerror("conflicting names %s and %s for package %q", p.Name, s.Name, p.Path) Yyerror("conflicting names %s and %s for package %q", p.Name, s.Name, p.Path)
} }
......
...@@ -426,7 +426,7 @@ func symfmt(s *Sym, flag int) string { ...@@ -426,7 +426,7 @@ func symfmt(s *Sym, flag int) string {
} }
// If the name was used by multiple packages, display the full path, // If the name was used by multiple packages, display the full path,
if s.Pkg.Name != "" && Pkglookup(s.Pkg.Name, nil).Npkg > 1 { if s.Pkg.Name != "" && numImport[s.Pkg.Name] > 1 {
return fmt.Sprintf("%q.%s", s.Pkg.Path, s.Name) return fmt.Sprintf("%q.%s", s.Pkg.Path, s.Name)
} }
var fp string var fp string
......
...@@ -110,11 +110,11 @@ type Pkg struct { ...@@ -110,11 +110,11 @@ type Pkg struct {
Path string Path string
Pathsym *Sym Pathsym *Sym
Prefix string Prefix string
Link *Pkg
Imported uint8 Imported uint8
Exported int8 Exported int8
Direct int8 Direct int8
Safe bool Safe bool
Syms map[string]*Sym
} }
type Sym struct { type Sym struct {
...@@ -122,7 +122,6 @@ type Sym struct { ...@@ -122,7 +122,6 @@ type Sym struct {
Flags uint8 Flags uint8
Sym uint8 Sym uint8
Link *Sym Link *Sym
Npkg int32
Uniqgen uint32 Uniqgen uint32
Importdef *Pkg Importdef *Pkg
Linkname string Linkname string
...@@ -753,8 +752,6 @@ var debugstr string ...@@ -753,8 +752,6 @@ var debugstr string
var Debug_checknil int var Debug_checknil int
var hash [NHASH]*Sym
var importmyname *Sym // my name for package var importmyname *Sym // my name for package
var localpkg *Pkg // package being compiled var localpkg *Pkg // package being compiled
...@@ -787,8 +784,6 @@ var trackpkg *Pkg // fake package for field tracking ...@@ -787,8 +784,6 @@ var trackpkg *Pkg // fake package for field tracking
var rawpkg *Pkg // fake package for raw symbol names var rawpkg *Pkg // fake package for raw symbol names
var phash [128]*Pkg
var Tptr int // either TPTR32 or TPTR64 var Tptr int // either TPTR32 or TPTR64
var myimportpath string var myimportpath string
......
...@@ -249,7 +249,7 @@ import_package: ...@@ -249,7 +249,7 @@ import_package:
{ {
if importpkg.Name == "" { if importpkg.Name == "" {
importpkg.Name = $2.Name; importpkg.Name = $2.Name;
Pkglookup($2.Name, nil).Npkg++; numImport[$2.Name]++
} else if importpkg.Name != $2.Name { } else if importpkg.Name != $2.Name {
Yyerror("conflicting names %s and %s for package %q", importpkg.Name, $2.Name, importpkg.Path); Yyerror("conflicting names %s and %s for package %q", importpkg.Name, $2.Name, importpkg.Path);
} }
......
...@@ -88,14 +88,8 @@ func anyinit(n *NodeList) bool { ...@@ -88,14 +88,8 @@ func anyinit(n *NodeList) bool {
} }
// are there any imported init functions // are there any imported init functions
for h := uint32(0); h < NHASH; h++ { for _, s := range initSyms {
for s = hash[h]; s != nil; s = s.Link { if s.Def != nil {
if s.Name[0] != 'i' || s.Name != "init" {
continue
}
if s.Def == nil {
continue
}
return true return true
} }
} }
...@@ -161,22 +155,10 @@ func fninit(n *NodeList) { ...@@ -161,22 +155,10 @@ func fninit(n *NodeList) {
r = list(r, a) r = list(r, a)
// (7) // (7)
var s *Sym for _, s := range initSyms {
for h := uint32(0); h < NHASH; h++ { if s.Def != nil && s != initsym {
for s = hash[h]; s != nil; s = s.Link {
if s.Name[0] != 'i' || s.Name != "init" {
continue
}
if s.Def == nil {
continue
}
if s == initsym {
continue
}
// could check that it is fn of no args/returns // could check that it is fn of no args/returns
a = Nod(OCALL, s.Def, nil) a = Nod(OCALL, s.Def, nil)
r = list(r, a) r = list(r, a)
} }
} }
...@@ -188,7 +170,7 @@ func fninit(n *NodeList) { ...@@ -188,7 +170,7 @@ func fninit(n *NodeList) {
// could check that it is fn of no args/returns // could check that it is fn of no args/returns
for i := 1; ; i++ { for i := 1; ; i++ {
namebuf = fmt.Sprintf("init.%d", i) namebuf = fmt.Sprintf("init.%d", i)
s = Lookup(namebuf) s := Lookup(namebuf)
if s.Def == nil { if s.Def == nil {
break break
} }
......
...@@ -1377,7 +1377,7 @@ talph: ...@@ -1377,7 +1377,7 @@ talph:
cp = nil cp = nil
ungetc(c) ungetc(c)
s = Lookup(lexbuf.String()) s = LookupBytes(lexbuf.Bytes())
switch s.Lexical { switch s.Lexical {
case LIGNORE: case LIGNORE:
goto l0 goto l0
...@@ -3120,36 +3120,33 @@ func mkpackage(pkgname string) { ...@@ -3120,36 +3120,33 @@ func mkpackage(pkgname string) {
if pkgname != localpkg.Name { if pkgname != localpkg.Name {
Yyerror("package %s; expected %s", pkgname, localpkg.Name) Yyerror("package %s; expected %s", pkgname, localpkg.Name)
} }
var s *Sym for _, s := range localpkg.Syms {
for h := int32(0); h < NHASH; h++ { if s.Def == nil {
for s = hash[h]; s != nil; s = s.Link { continue
if s.Def == nil || s.Pkg != localpkg { }
continue if s.Def.Op == OPACK {
} // throw away top-level package name leftover
if s.Def.Op == OPACK { // from previous file.
// throw away top-level package name leftover // leave s->block set to cause redeclaration
// from previous file. // errors if a conflicting top-level name is
// leave s->block set to cause redeclaration // introduced by a different file.
// errors if a conflicting top-level name is if s.Def.Used == 0 && nsyntaxerrors == 0 {
// introduced by a different file. pkgnotused(int(s.Def.Lineno), s.Def.Pkg.Path, s.Name)
if s.Def.Used == 0 && nsyntaxerrors == 0 {
pkgnotused(int(s.Def.Lineno), s.Def.Pkg.Path, s.Name)
}
s.Def = nil
continue
} }
s.Def = nil
continue
}
if s.Def.Sym != s { if s.Def.Sym != s {
// throw away top-level name left over // throw away top-level name left over
// from previous import . "x" // from previous import . "x"
if s.Def.Pack != nil && s.Def.Pack.Used == 0 && nsyntaxerrors == 0 { if s.Def.Pack != nil && s.Def.Pack.Used == 0 && nsyntaxerrors == 0 {
pkgnotused(int(s.Def.Pack.Lineno), s.Def.Pack.Pkg.Path, "") pkgnotused(int(s.Def.Pack.Lineno), s.Def.Pack.Pkg.Path, "")
s.Def.Pack.Used = 1 s.Def.Pack.Used = 1
}
s.Def = nil
continue
} }
s.Def = nil
continue
} }
} }
} }
......
...@@ -1246,12 +1246,9 @@ func dumptypestructs() { ...@@ -1246,12 +1246,9 @@ func dumptypestructs() {
} }
// generate import strings for imported packages // generate import strings for imported packages
var p *Pkg for _, p := range pkgs {
for i := 0; i < len(phash); i++ { if p.Direct != 0 {
for p = phash[i]; p != nil; p = p.Link { dimportpath(p)
if p.Direct != 0 {
dimportpath(p)
}
} }
} }
......
...@@ -275,54 +275,54 @@ func setlineno(n *Node) int32 { ...@@ -275,54 +275,54 @@ func setlineno(n *Node) int32 {
return lno return lno
} }
func stringhash(p string) uint32 { func Lookup(name string) *Sym {
var c int return localpkg.Lookup(name)
}
h := uint32(0) func LookupBytes(name []byte) *Sym {
for { return localpkg.LookupBytes(name)
c, p = intstarstringplusplus(p) }
if c == 0 {
break
}
h = h*PRIME1 + uint32(c)
}
if int32(h) < 0 { var initSyms []*Sym
h = -h
if int32(h) < 0 { var nopkg = new(Pkg)
h = 0
} func (pkg *Pkg) Lookup(name string) *Sym {
if pkg == nil {
pkg = nopkg
}
if s := pkg.Syms[name]; s != nil {
return s
} }
return h s := &Sym{
Name: name,
Pkg: pkg,
Lexical: LNAME,
}
if s.Name == "init" {
initSyms = append(initSyms, s)
}
if pkg.Syms == nil {
pkg.Syms = make(map[string]*Sym)
}
pkg.Syms[name] = s
return s
} }
func Lookup(name string) *Sym { func (pkg *Pkg) LookupBytes(name []byte) *Sym {
return Pkglookup(name, localpkg) if pkg == nil {
pkg = nopkg
}
if s := pkg.Syms[string(name)]; s != nil {
return s
}
str := internString(name)
return pkg.Lookup(str)
} }
func Pkglookup(name string, pkg *Pkg) *Sym { func Pkglookup(name string, pkg *Pkg) *Sym {
h := stringhash(name) % NHASH return pkg.Lookup(name)
c := int(name[0])
for s := hash[h]; s != nil; s = s.Link {
if int(s.Name[0]) != c || s.Pkg != pkg {
continue
}
if s.Name == name {
return s
}
}
s := new(Sym)
s.Name = name
s.Pkg = pkg
s.Link = hash[h]
hash[h] = s
s.Lexical = LNAME
return s
} }
func restrictlookup(name string, pkg *Pkg) *Sym { func restrictlookup(name string, pkg *Pkg) *Sym {
...@@ -335,35 +335,29 @@ func restrictlookup(name string, pkg *Pkg) *Sym { ...@@ -335,35 +335,29 @@ func restrictlookup(name string, pkg *Pkg) *Sym {
// find all the exported symbols in package opkg // find all the exported symbols in package opkg
// and make them available in the current package // and make them available in the current package
func importdot(opkg *Pkg, pack *Node) { func importdot(opkg *Pkg, pack *Node) {
var s *Sym
var s1 *Sym var s1 *Sym
var pkgerror string var pkgerror string
n := 0 n := 0
for h := uint32(0); h < NHASH; h++ { for _, s := range opkg.Syms {
for s = hash[h]; s != nil; s = s.Link { if s.Def == nil {
if s.Pkg != opkg { continue
continue
}
if s.Def == nil {
continue
}
if !exportname(s.Name) || strings.ContainsRune(s.Name, 0xb7) { // 0xb7 = center dot
continue
}
s1 = Lookup(s.Name)
if s1.Def != nil {
pkgerror = fmt.Sprintf("during import %q", opkg.Path)
redeclare(s1, pkgerror)
continue
}
s1.Def = s.Def
s1.Block = s.Block
s1.Def.Pack = pack
s1.Origpkg = opkg
n++
} }
if !exportname(s.Name) || strings.ContainsRune(s.Name, 0xb7) { // 0xb7 = center dot
continue
}
s1 = Lookup(s.Name)
if s1.Def != nil {
pkgerror = fmt.Sprintf("during import %q", opkg.Path)
redeclare(s1, pkgerror)
continue
}
s1.Def = s.Def
s1.Block = s.Block
s1.Def.Pack = pack
s1.Origpkg = opkg
n++
} }
if n == 0 { if n == 0 {
...@@ -3583,19 +3577,19 @@ func pathtoprefix(s string) string { ...@@ -3583,19 +3577,19 @@ func pathtoprefix(s string) string {
return s return s
} }
var pkgMap = make(map[string]*Pkg)
var pkgs []*Pkg
func mkpkg(path string) *Pkg { func mkpkg(path string) *Pkg {
h := int(stringhash(path) & uint32(len(phash)-1)) if p := pkgMap[path]; p != nil {
for p := phash[h]; p != nil; p = p.Link { return p
if p.Path == path {
return p
}
} }
p := new(Pkg) p := new(Pkg)
p.Path = path p.Path = path
p.Prefix = pathtoprefix(path) p.Prefix = pathtoprefix(path)
p.Link = phash[h] pkgMap[path] = p
phash[h] = p pkgs = append(pkgs, p)
return p return p
} }
......
...@@ -2659,21 +2659,16 @@ toomany: ...@@ -2659,21 +2659,16 @@ toomany:
/* /*
* type check composite * type check composite
*/ */
func fielddup(n *Node, hash []*Node) { func fielddup(n *Node, hash map[string]bool) {
if n.Op != ONAME { if n.Op != ONAME {
Fatal("fielddup: not ONAME") Fatal("fielddup: not ONAME")
} }
s := n.Sym.Name name := n.Sym.Name
h := uint(stringhash(s) % uint32(len(hash))) if hash[name] {
for a := hash[h]; a != nil; a = a.Ntest { Yyerror("duplicate field name in struct literal: %s", name)
if a.Sym.Name == s { return
Yyerror("duplicate field name in struct literal: %s", s)
return
}
} }
hash[name] = true
n.Ntest = hash[h]
hash[h] = n
} }
func keydup(n *Node, hash []*Node) { func keydup(n *Node, hash []*Node) {
...@@ -3019,8 +3014,7 @@ func typecheckcomplit(np **Node) { ...@@ -3019,8 +3014,7 @@ func typecheckcomplit(np **Node) {
Yyerror("too few values in struct initializer") Yyerror("too few values in struct initializer")
} }
} else { } else {
var autohash [101]*Node hash := make(map[string]bool)
hash := inithash(n, autohash[:])
// keyed list // keyed list
var s *Sym var s *Sym
......
...@@ -1226,7 +1226,7 @@ yydefault: ...@@ -1226,7 +1226,7 @@ yydefault:
{ {
if importpkg.Name == "" { if importpkg.Name == "" {
importpkg.Name = yyDollar[2].sym.Name importpkg.Name = yyDollar[2].sym.Name
Pkglookup(yyDollar[2].sym.Name, nil).Npkg++ numImport[yyDollar[2].sym.Name]++
} else if importpkg.Name != yyDollar[2].sym.Name { } else if importpkg.Name != yyDollar[2].sym.Name {
Yyerror("conflicting names %s and %s for package %q", importpkg.Name, yyDollar[2].sym.Name, importpkg.Path) Yyerror("conflicting names %s and %s for package %q", importpkg.Name, yyDollar[2].sym.Name, importpkg.Path)
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment