]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: restructure ABI wrapper generation, export ABI
authorAustin Clements <austin@google.com>
Thu, 25 Mar 2021 23:50:08 +0000 (19:50 -0400)
committerAustin Clements <austin@google.com>
Mon, 29 Mar 2021 18:46:32 +0000 (18:46 +0000)
This CL restructures how we track function ABIs and generate ABI
wrappers in the compiler and adds import/export of ABIs across package
boundaries.

Currently, we start by tracking definition and referencing ABIs in two
global maps and eventually move some of this information into the
LSyms for functions. This complicates a lot of the existing code for
handling wrappers and makes it particularly hard to export ABI
information across packages. This change is built around instead
recording this information on the ir.Func.

First, this change replaces the global ABI def/ref maps with a type,
which makes the data flow and lifetime of this information clear in
gc.Main. These are populated during flag parsing.

Then, early in the front-end, we loop over all ir.Funcs to 1. attach
ABI def/ref information to the ir.Funcs and 2. create new ir.Funcs for
ABI wrappers. Step 1 is slightly subtle because the information is
keyed by linker symbol names, so we can't simply look things up in the
compiler's regular symbol table.

By generating ABI wrappers early in the front-end, we decouple this
step from LSym creation, which makes LSym creation much simpler (like
it was before ABI wrappers). In particular, LSyms for wrappers are now
created at the same time as all other functions instead of by
makeABIWrapper, which means we're back to the simpler, old situation
where InitLSym was the only thing responsible for constructing
function LSyms. Hence, we can restore the check that InitLSym is
called exactly once per function.

Attaching the ABI information to the ir.Func has several follow-on
benefits:

1. It's now easy to include in the export info. This enables direct
cross-package cross-ABI calls, which are important for the performance
of calling various hot assembly functions (e.g., internal/bytealg.*).
This was really the point of this whole change.

2. Since all Funcs, including wrappers, now record their definition
ABI, callTargetLSym no longer needs to distinguish wrappers from
non-wrappers, so it's now nearly trivial (it would be completely
trivial except that it has to work around a handful of cases where
ir.Name.Func is nil).

The simplification of callTargetLSym has one desirable but potentially
surprising side-effect: the compiler will now generate direct calls to
the definition ABI even when ABI wrappers are turned off. This is
almost completely unnoticeable except that cmd/internal/obj/wasm looks
for the call from runtime.deferreturn (defined in Go) to
runtime.jmpdefer (defined in assembly) to compile is specially. That
now looks like a direct call to ABI0 rather than going through the
ABIInternal alias.

While we're in here, we also set up the structures to support more
than just ABI0 and ABIInternal and add various additional consistency
checks all around.

Performance-wise, this reduces the overhead induced by wrappers from
1.24% geomean (on Sweet) to 0.52% geomean, and reduces the number of
benchmarks impacts >2% from 5 to 3. It has no impact on compiler speed.

Impact of wrappers before this change:

name                                old time/op  new time/op  delta
BiogoIgor                            15.8s ± 2%   15.8s ± 1%    ~     (p=0.863 n=25+25)
BiogoKrishna                         18.3s ± 6%   18.1s ± 7%  -1.39%  (p=0.015 n=25+25)
BleveIndexBatch100                   5.88s ± 3%   6.04s ± 6%  +2.72%  (p=0.000 n=25+25)
BleveQuery                           6.42s ± 1%   6.76s ± 1%  +5.31%  (p=0.000 n=24+24)
CompileTemplate                      245ms ± 3%   250ms ± 6%    ~     (p=0.068 n=22+25)
CompileUnicode                      93.6ms ± 2%  93.9ms ± 5%    ~     (p=0.958 n=22+25)
CompileGoTypes                       1.60s ± 2%   1.59s ± 2%    ~     (p=0.115 n=24+24)
CompileCompiler                      104ms ± 4%   104ms ± 3%    ~     (p=0.453 n=22+25)
CompileSSA                           11.0s ± 2%   11.0s ± 1%    ~     (p=0.789 n=24+25)
CompileFlate                         153ms ± 2%   153ms ± 1%    ~     (p=0.055 n=21+20)
CompileGoParser                      229ms ± 2%   230ms ± 2%    ~     (p=0.305 n=21+22)
CompileReflect                       585ms ± 5%   582ms ± 3%    ~     (p=0.365 n=25+25)
CompileTar                           211ms ± 1%   211ms ± 3%    ~     (p=0.592 n=20+22)
CompileXML                           282ms ± 3%   281ms ± 2%    ~     (p=0.937 n=22+23)
CompileStdCmd                        13.7s ± 3%   13.6s ± 2%    ~     (p=0.700 n=25+25)
FoglemanFauxGLRenderRotateBoat       8.67s ± 1%   8.78s ± 1%  +1.30%  (p=0.000 n=25+25)
FoglemanPathTraceRenderGopherIter1   20.5s ± 2%   20.9s ± 2%  +1.85%  (p=0.000 n=25+25)
GopherLuaKNucleotide                 30.1s ± 2%   31.1s ± 2%  +3.38%  (p=0.000 n=25+25)
MarkdownRenderXHTML                  246ms ± 5%   250ms ± 1%  +1.42%  (p=0.002 n=25+23)
Tile38WithinCircle100kmRequest       828µs ± 6%   885µs ± 6%  +6.85%  (p=0.000 n=23+25)
Tile38IntersectsCircle100kmRequest  1.04ms ± 5%  1.10ms ± 7%  +5.63%  (p=0.000 n=25+25)
Tile38KNearestLimit100Request        974µs ± 4%   972µs ± 4%    ~     (p=0.356 n=25+24)
[Geo mean]                           588ms        595ms       +1.24%

(https://perf.golang.org/search?q=upload:20210328.5)

And after this change:

name                                old time/op  new time/op  delta
BiogoIgor                            15.9s ± 1%   15.8s ± 1%  -0.48%  (p=0.008 n=22+25)
BiogoKrishna                         18.4s ± 6%   17.8s ± 6%  -3.55%  (p=0.008 n=25+25)
BleveIndexBatch100                   5.86s ± 3%   5.97s ± 4%  +1.88%  (p=0.001 n=25+25)
BleveQuery                           6.42s ± 1%   6.75s ± 1%  +5.14%  (p=0.000 n=25+25)
CompileTemplate                      246ms ± 5%   245ms ± 2%    ~     (p=0.472 n=23+23)
CompileUnicode                      93.7ms ± 3%  93.5ms ± 2%    ~     (p=0.813 n=22+23)
CompileGoTypes                       1.60s ± 2%   1.60s ± 2%    ~     (p=0.108 n=25+23)
CompileCompiler                      104ms ± 3%   104ms ± 2%    ~     (p=0.845 n=23+23)
CompileSSA                           11.0s ± 2%   11.0s ± 2%    ~     (p=0.525 n=25+25)
CompileFlate                         152ms ± 1%   153ms ± 2%    ~     (p=0.408 n=22+22)
CompileGoParser                      230ms ± 1%   230ms ± 1%    ~     (p=0.363 n=21+23)
CompileReflect                       582ms ± 3%   584ms ± 4%    ~     (p=0.658 n=25+25)
CompileTar                           212ms ± 2%   211ms ± 2%    ~     (p=0.315 n=23+24)
CompileXML                           282ms ± 1%   282ms ± 1%    ~     (p=0.991 n=23+22)
CompileStdCmd                        13.6s ± 2%   13.6s ± 2%    ~     (p=0.699 n=25+24)
FoglemanFauxGLRenderRotateBoat       8.66s ± 1%   8.69s ± 1%  +0.28%  (p=0.002 n=25+24)
FoglemanPathTraceRenderGopherIter1   20.5s ± 3%   20.5s ± 2%    ~     (p=0.407 n=25+25)
GopherLuaKNucleotide                 30.1s ± 2%   31.2s ± 2%  +3.82%  (p=0.000 n=25+25)
MarkdownRenderXHTML                  246ms ± 3%   245ms ± 1%    ~     (p=0.478 n=23+22)
Tile38WithinCircle100kmRequest       820µs ± 4%   856µs ± 5%  +4.39%  (p=0.000 n=24+25)
Tile38IntersectsCircle100kmRequest  1.05ms ± 6%  1.07ms ± 6%  +1.91%  (p=0.014 n=25+25)
Tile38KNearestLimit100Request        970µs ± 4%   970µs ± 3%    ~     (p=0.819 n=22+24)
[Geo mean]                           588ms        591ms       +0.52%

(https://perf.golang.org/search?q=upload:20210328.6)

For #40724.

Change-Id: I1c374e32d4bbc88efed062a1b360017d3642140d
Reviewed-on: https://go-review.googlesource.com/c/go/+/305274
Trust: Austin Clements <austin@google.com>
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Go Bot <gobot@golang.org>

12 files changed:
src/cmd/compile/internal/gc/main.go
src/cmd/compile/internal/ir/func.go
src/cmd/compile/internal/ir/sizeof_test.go
src/cmd/compile/internal/ssagen/abi.go
src/cmd/compile/internal/ssagen/nowb.go
src/cmd/compile/internal/ssagen/ssa.go
src/cmd/compile/internal/staticdata/data.go
src/cmd/compile/internal/typecheck/iexport.go
src/cmd/compile/internal/typecheck/iimport.go
src/cmd/compile/internal/types/sym.go
src/cmd/internal/obj/link.go
src/cmd/internal/obj/wasm/wasmobj.go

index c46989edb497e26c246a6429ca60117864036564..9199db830cfdc47c3b555406f8ecfae0a7d03ec8 100644 (file)
@@ -139,8 +139,9 @@ func Main(archInit func(*ssagen.ArchInfo)) {
 
        types.ParseLangFlag()
 
+       symABIs := ssagen.NewSymABIs(base.Ctxt.Pkgpath)
        if base.Flag.SymABIs != "" {
-               ssagen.ReadSymABIs(base.Flag.SymABIs, base.Ctxt.Pkgpath)
+               symABIs.ReadSymABIs(base.Flag.SymABIs)
        }
 
        if base.Compiling(base.NoInstrumentPkgs) {
@@ -187,7 +188,6 @@ func Main(archInit func(*ssagen.ArchInfo)) {
        noder.LoadPackage(flag.Args())
 
        dwarfgen.RecordPackageName()
-       ssagen.CgoSymABIs()
 
        // Build init task.
        if initTask := pkginit.Task(); initTask != nil {
@@ -233,6 +233,10 @@ func Main(archInit func(*ssagen.ArchInfo)) {
        }
        ir.CurFunc = nil
 
+       // Generate ABI wrappers. Must happen before escape analysis
+       // and doesn't benefit from dead-coding or inlining.
+       symABIs.GenABIWrappers()
+
        // Escape analysis.
        // Required for moving heap allocations onto stack,
        // which in turn is required by the closure implementation,
index c17425a4da2f72807f14be80695c26800e1af687..bcedfe138c58a8fbaaddafcc2372850f8af1d2c8 100644 (file)
@@ -93,7 +93,7 @@ type Func struct {
 
        FieldTrack map[*obj.LSym]struct{}
        DebugInfo  interface{}
-       LSym       *obj.LSym
+       LSym       *obj.LSym // Linker object in this function's native ABI (Func.ABI)
 
        Inl *Inline
 
@@ -109,7 +109,22 @@ type Func struct {
 
        Pragma PragmaFlag // go:xxx function annotations
 
-       flags      bitset16
+       flags bitset16
+
+       // ABI is a function's "definition" ABI. This is the ABI that
+       // this function's generated code is expecting to be called by.
+       //
+       // For most functions, this will be obj.ABIInternal. It may be
+       // a different ABI for functions defined in assembly or ABI wrappers.
+       //
+       // This is included in the export data and tracked across packages.
+       ABI obj.ABI
+       // ABIRefs is the set of ABIs by which this function is referenced.
+       // For ABIs other than this function's definition ABI, the
+       // compiler generates ABI wrapper functions. This is only tracked
+       // within a package.
+       ABIRefs obj.ABISet
+
        NumDefers  int32 // number of defer calls in the function
        NumReturns int32 // number of explicit returns in the function
 
@@ -124,6 +139,9 @@ func NewFunc(pos src.XPos) *Func {
        f.pos = pos
        f.op = ODCLFUNC
        f.Iota = -1
+       // Most functions are ABIInternal. The importer or symabis
+       // pass may override this.
+       f.ABI = obj.ABIInternal
        return f
 }
 
@@ -163,6 +181,7 @@ type ScopeID int32
 const (
        funcDupok         = 1 << iota // duplicate definitions ok
        funcWrapper                   // hide frame from users (elide in tracebacks, don't count as a frame for recover())
+       funcABIWrapper                // is an ABI wrapper (also set flagWrapper)
        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
@@ -184,6 +203,7 @@ type SymAndPos struct {
 
 func (f *Func) Dupok() bool                    { return f.flags&funcDupok != 0 }
 func (f *Func) Wrapper() bool                  { return f.flags&funcWrapper != 0 }
+func (f *Func) ABIWrapper() bool               { return f.flags&funcABIWrapper != 0 }
 func (f *Func) Needctxt() bool                 { return f.flags&funcNeedctxt != 0 }
 func (f *Func) ReflectMethod() bool            { return f.flags&funcReflectMethod != 0 }
 func (f *Func) IsHiddenClosure() bool          { return f.flags&funcIsHiddenClosure != 0 }
@@ -197,6 +217,7 @@ func (f *Func) ClosureCalled() bool            { return f.flags&funcClosureCalle
 
 func (f *Func) SetDupok(b bool)                    { f.flags.set(funcDupok, b) }
 func (f *Func) SetWrapper(b bool)                  { f.flags.set(funcWrapper, b) }
+func (f *Func) SetABIWrapper(b bool)               { f.flags.set(funcABIWrapper, b) }
 func (f *Func) SetNeedctxt(b bool)                 { f.flags.set(funcNeedctxt, b) }
 func (f *Func) SetReflectMethod(b bool)            { f.flags.set(funcReflectMethod, b) }
 func (f *Func) SetIsHiddenClosure(b bool)          { f.flags.set(funcIsHiddenClosure, b) }
index d8c1518b90f347658a11ce323694c1477e8bc77d..a4421fcf531503dbac132e906cc7c55f9a429d85 100644 (file)
@@ -20,7 +20,7 @@ func TestSizeof(t *testing.T) {
                _32bit uintptr     // size on 32bit platforms
                _64bit uintptr     // size on 64bit platforms
        }{
-               {Func{}, 188, 328},
+               {Func{}, 192, 328},
                {Name{}, 112, 200},
        }
 
index 2f8678060dd0dace321670b7e1af45ccff72001e..9c203838a55baeb279aed98a8eda06b6f2f75983 100644 (file)
@@ -12,7 +12,6 @@ import (
        "strings"
 
        "cmd/compile/internal/base"
-       "cmd/compile/internal/escape"
        "cmd/compile/internal/ir"
        "cmd/compile/internal/staticdata"
        "cmd/compile/internal/typecheck"
@@ -21,23 +20,40 @@ import (
        "cmd/internal/objabi"
 )
 
-// symabiDefs and symabiRefs record the defined and referenced ABIs of
-// symbols required by non-Go code. These are keyed by link symbol
-// name, where the local package prefix is always `"".`
-var symabiDefs, symabiRefs map[string]obj.ABI
+// SymABIs records information provided by the assembler about symbol
+// definition ABIs and reference ABIs.
+type SymABIs struct {
+       defs map[string]obj.ABI
+       refs map[string]obj.ABISet
 
-func CgoSymABIs() {
-       // The linker expects an ABI0 wrapper for all cgo-exported
-       // functions.
-       for _, prag := range typecheck.Target.CgoPragmas {
-               switch prag[0] {
-               case "cgo_export_static", "cgo_export_dynamic":
-                       if symabiRefs == nil {
-                               symabiRefs = make(map[string]obj.ABI)
-                       }
-                       symabiRefs[prag[1]] = obj.ABI0
-               }
+       localPrefix string
+}
+
+func NewSymABIs(myimportpath string) *SymABIs {
+       var localPrefix string
+       if myimportpath != "" {
+               localPrefix = objabi.PathToPrefix(myimportpath) + "."
        }
+
+       return &SymABIs{
+               defs:        make(map[string]obj.ABI),
+               refs:        make(map[string]obj.ABISet),
+               localPrefix: localPrefix,
+       }
+}
+
+// canonicalize returns the canonical name used for a linker symbol in
+// s's maps. Symbols in this package may be written either as "".X or
+// with the package's import path already in the symbol. This rewrites
+// both to `"".`, which matches compiler-generated linker symbol names.
+func (s *SymABIs) canonicalize(linksym string) string {
+       // If the symbol is already prefixed with localPrefix,
+       // rewrite it to start with "" so it matches the
+       // compiler's internal symbol names.
+       if s.localPrefix != "" && strings.HasPrefix(linksym, s.localPrefix) {
+               return `"".` + linksym[len(s.localPrefix):]
+       }
+       return linksym
 }
 
 // ReadSymABIs reads a symabis file that specifies definitions and
@@ -49,23 +65,12 @@ func CgoSymABIs() {
 // symbol using an ABI. For both "def" and "ref", the second field is
 // the symbol name and the third field is the ABI name, as one of the
 // named cmd/internal/obj.ABI constants.
-func ReadSymABIs(file, myimportpath string) {
+func (s *SymABIs) ReadSymABIs(file string) {
        data, err := ioutil.ReadFile(file)
        if err != nil {
                log.Fatalf("-symabis: %v", err)
        }
 
-       symabiDefs = make(map[string]obj.ABI)
-       symabiRefs = make(map[string]obj.ABI)
-
-       localPrefix := ""
-       if myimportpath != "" {
-               // Symbols in this package may be written either as
-               // "".X or with the package's import path already in
-               // the symbol.
-               localPrefix = objabi.PathToPrefix(myimportpath) + "."
-       }
-
        for lineNum, line := range strings.Split(string(data), "\n") {
                lineNum++ // 1-based
                line = strings.TrimSpace(line)
@@ -86,19 +91,13 @@ func ReadSymABIs(file, myimportpath string) {
                                log.Fatalf(`%s:%d: invalid symabi: unknown abi "%s"`, file, lineNum, abistr)
                        }
 
-                       // If the symbol is already prefixed with
-                       // myimportpath, rewrite it to start with ""
-                       // so it matches the compiler's internal
-                       // symbol names.
-                       if localPrefix != "" && strings.HasPrefix(sym, localPrefix) {
-                               sym = `"".` + sym[len(localPrefix):]
-                       }
+                       sym = s.canonicalize(sym)
 
                        // Record for later.
                        if parts[0] == "def" {
-                               symabiDefs[sym] = abi
+                               s.defs[sym] = abi
                        } else {
-                               symabiRefs[sym] = abi
+                               s.refs[sym] |= obj.ABISetOf(abi)
                        }
                default:
                        log.Fatalf(`%s:%d: invalid symabi type "%s"`, file, lineNum, parts[0])
@@ -106,6 +105,78 @@ func ReadSymABIs(file, myimportpath string) {
        }
 }
 
+// GenABIWrappers applies ABI information to Funcs and generates ABI
+// wrapper functions where necessary.
+func (s *SymABIs) GenABIWrappers() {
+       // The linker expects an ABI0 wrapper for all cgo-exported
+       // functions.
+       for _, prag := range typecheck.Target.CgoPragmas {
+               switch prag[0] {
+               case "cgo_export_static", "cgo_export_dynamic":
+                       s.refs[s.canonicalize(prag[1])] |= obj.ABISetOf(obj.ABI0)
+               }
+       }
+
+       // Apply ABI defs and refs to Funcs and generate wrappers.
+       //
+       // This may generate new decls for the wrappers, but we
+       // specifically *don't* want to visit those, lest we create
+       // wrappers for wrappers.
+       for _, fn := range typecheck.Target.Decls {
+               if fn.Op() != ir.ODCLFUNC {
+                       continue
+               }
+               fn := fn.(*ir.Func)
+               nam := fn.Nname
+               if ir.IsBlank(nam) {
+                       continue
+               }
+               sym := nam.Sym()
+               var symName string
+               if sym.Linkname != "" {
+                       symName = s.canonicalize(sym.Linkname)
+               } else {
+                       // These names will already be canonical.
+                       symName = sym.Pkg.Prefix + "." + sym.Name
+               }
+
+               // Apply definitions.
+               defABI, hasDefABI := s.defs[symName]
+               if hasDefABI {
+                       fn.ABI = defABI
+               }
+
+               // Apply references.
+               if abis, ok := s.refs[symName]; ok {
+                       fn.ABIRefs |= abis
+               }
+               // Assume all functions are referenced at least as
+               // ABIInternal, since they may be referenced from
+               // other packages.
+               fn.ABIRefs.Set(obj.ABIInternal, true)
+
+               // If a symbol is defined in this package (either in
+               // Go or assembly) and given a linkname, it may be
+               // referenced from another package, so make it
+               // callable via any ABI. It's important that we know
+               // it's defined in this package since other packages
+               // may "pull" symbols using linkname and we don't want
+               // to create duplicate ABI wrappers.
+               hasBody := len(fn.Body) != 0
+               if sym.Linkname != "" && (hasBody || hasDefABI) {
+                       fn.ABIRefs |= obj.ABISetCallable
+               }
+
+               if !objabi.Experiment.RegabiWrappers {
+                       // We'll generate ABI aliases instead of
+                       // wrappers once we have LSyms in InitLSym.
+                       continue
+               }
+
+               forEachWrapperABI(fn, makeABIWrapper)
+       }
+}
+
 // InitLSym defines f's obj.LSym and initializes it based on the
 // properties of f. This includes setting the symbol flags and ABI and
 // creating and initializing related DWARF symbols.
@@ -115,96 +186,73 @@ func ReadSymABIs(file, myimportpath string) {
 // For body-less functions, we only create the LSym; for functions
 // with bodies call a helper to setup up / populate the LSym.
 func InitLSym(f *ir.Func, hasBody bool) {
-       // FIXME: for new-style ABI wrappers, we set up the lsym at the
-       // point the wrapper is created.
-       if f.LSym != nil && objabi.Experiment.RegabiWrappers {
-               return
-       }
-       staticdata.NeedFuncSym(f)
-       selectLSym(f, hasBody)
-       if hasBody {
-               setupTextLSym(f, 0)
-       }
-}
-
-// selectLSym sets up the LSym for a given function, and
-// makes calls to helpers to create ABI wrappers if needed.
-func selectLSym(f *ir.Func, hasBody bool) {
        if f.LSym != nil {
                base.FatalfAt(f.Pos(), "InitLSym called twice on %v", f)
        }
 
        if nam := f.Nname; !ir.IsBlank(nam) {
-
-               var wrapperABI obj.ABI
-               needABIWrapper := false
-               defABI, hasDefABI := symabiDefs[nam.Linksym().Name]
-               if hasDefABI && defABI == obj.ABI0 {
-                       // Symbol is defined as ABI0. Create an
-                       // Internal -> ABI0 wrapper.
-                       f.LSym = nam.LinksymABI(obj.ABI0)
-                       needABIWrapper, wrapperABI = true, obj.ABIInternal
-               } else {
-                       f.LSym = nam.Linksym()
-                       // No ABI override. Check that the symbol is
-                       // using the expected ABI.
-                       want := obj.ABIInternal
-                       if f.LSym.ABI() != want {
-                               base.Fatalf("function symbol %s has the wrong ABI %v, expected %v", f.LSym.Name, f.LSym.ABI(), want)
-                       }
-               }
+               f.LSym = nam.LinksymABI(f.ABI)
                if f.Pragma&ir.Systemstack != 0 {
                        f.LSym.Set(obj.AttrCFunc, true)
                }
-
-               isLinknameExported := nam.Sym().Linkname != "" && (hasBody || hasDefABI)
-               if abi, ok := symabiRefs[f.LSym.Name]; (ok && abi == obj.ABI0) || isLinknameExported {
-                       // Either 1) this symbol is definitely
-                       // referenced as ABI0 from this package; or 2)
-                       // this symbol is defined in this package but
-                       // given a linkname, indicating that it may be
-                       // referenced from another package. Create an
-                       // ABI0 -> Internal wrapper so it can be
-                       // called as ABI0. In case 2, it's important
-                       // that we know it's defined in this package
-                       // since other packages may "pull" symbols
-                       // using linkname and we don't want to create
-                       // duplicate ABI wrappers.
-                       if f.LSym.ABI() != obj.ABI0 {
-                               needABIWrapper, wrapperABI = true, obj.ABI0
-                       }
+               if f.ABI == obj.ABIInternal || !objabi.Experiment.RegabiWrappers {
+                       // Function values can only point to
+                       // ABIInternal entry points. This will create
+                       // the funcsym for either the defining
+                       // function or its wrapper as appropriate.
+                       //
+                       // If we're using ABI aliases instead of
+                       // wrappers, we only InitLSym for the defining
+                       // ABI of a function, so we make the funcsym
+                       // when we see that.
+                       staticdata.NeedFuncSym(f)
+               }
+               if !objabi.Experiment.RegabiWrappers {
+                       // Create ABI aliases instead of wrappers.
+                       forEachWrapperABI(f, makeABIAlias)
                }
+       }
+       if hasBody {
+               setupTextLSym(f, 0)
+       }
+}
 
-               if needABIWrapper {
-                       if !objabi.Experiment.RegabiWrappers {
-                               // Fallback: use alias instead. FIXME.
-
-                               // These LSyms have the same name as the
-                               // native function, so we create them directly
-                               // rather than looking them up. The uniqueness
-                               // of f.lsym ensures uniqueness of asym.
-                               asym := &obj.LSym{
-                                       Name: f.LSym.Name,
-                                       Type: objabi.SABIALIAS,
-                                       R:    []obj.Reloc{{Sym: f.LSym}}, // 0 size, so "informational"
-                               }
-                               asym.SetABI(wrapperABI)
-                               asym.Set(obj.AttrDuplicateOK, true)
-                               base.Ctxt.ABIAliases = append(base.Ctxt.ABIAliases, asym)
-                       } else {
-                               if base.Debug.ABIWrap != 0 {
-                                       fmt.Fprintf(os.Stderr, "=-= %v to %v wrapper for %s.%s\n",
-                                               wrapperABI, 1-wrapperABI, types.LocalPkg.Path, f.LSym.Name)
-                               }
-                               makeABIWrapper(f, wrapperABI)
-                       }
+func forEachWrapperABI(fn *ir.Func, cb func(fn *ir.Func, wrapperABI obj.ABI)) {
+       need := fn.ABIRefs &^ obj.ABISetOf(fn.ABI)
+       if need == 0 {
+               return
+       }
+
+       for wrapperABI := obj.ABI(0); wrapperABI < obj.ABICount; wrapperABI++ {
+               if !need.Get(wrapperABI) {
+                       continue
                }
+               cb(fn, wrapperABI)
        }
 }
 
-// makeABIWrapper creates a new function that wraps a cross-ABI call
-// to "f".  The wrapper is marked as an ABIWRAPPER.
+// makeABIAlias creates a new ABI alias so calls to f via wrapperABI
+// will be resolved directly to f's ABI by the linker.
+func makeABIAlias(f *ir.Func, wrapperABI obj.ABI) {
+       // These LSyms have the same name as the native function, so
+       // we create them directly rather than looking them up.
+       // The uniqueness of f.lsym ensures uniqueness of asym.
+       asym := &obj.LSym{
+               Name: f.LSym.Name,
+               Type: objabi.SABIALIAS,
+               R:    []obj.Reloc{{Sym: f.LSym}}, // 0 size, so "informational"
+       }
+       asym.SetABI(wrapperABI)
+       asym.Set(obj.AttrDuplicateOK, true)
+       base.Ctxt.ABIAliases = append(base.Ctxt.ABIAliases, asym)
+}
+
+// makeABIWrapper creates a new function that will be called with
+// wrapperABI and calls "f" using f.ABI.
 func makeABIWrapper(f *ir.Func, wrapperABI obj.ABI) {
+       if base.Debug.ABIWrap != 0 {
+               fmt.Fprintf(os.Stderr, "=-= %v to %v wrapper for %v\n", wrapperABI, f.ABI, f)
+       }
 
        // Q: is this needed?
        savepos := base.Pos
@@ -230,16 +278,17 @@ func makeABIWrapper(f *ir.Func, wrapperABI obj.ABI) {
 
        // Reuse f's types.Sym to create a new ODCLFUNC/function.
        fn := typecheck.DeclFunc(f.Nname.Sym(), tfn)
-       fn.SetDupok(true)
-       fn.SetWrapper(true) // ignore frame for panic+recover matching
+       fn.ABI = wrapperABI
 
-       // Select LSYM now.
-       asym := base.Ctxt.LookupABI(f.LSym.Name, wrapperABI)
-       asym.Type = objabi.STEXT
-       if fn.LSym != nil {
-               panic("unexpected")
-       }
-       fn.LSym = asym
+       fn.SetABIWrapper(true)
+       fn.SetDupok(true)
+       // Set this as a wrapper so it doesn't appear in tracebacks.
+       // Having both ABIWrapper and Wrapper set suppresses obj's
+       // usual panic+recover handling for wrappers; that's okay
+       // because we're never going to defer a wrapper for a function
+       // that then recovers, so that's would just be unnecessary
+       // code in the ABI wrapper.
+       fn.SetWrapper(true)
 
        // ABI0-to-ABIInternal wrappers will be mainly loading params from
        // stack into registers (and/or storing stack locations back to
@@ -266,7 +315,7 @@ func makeABIWrapper(f *ir.Func, wrapperABI obj.ABI) {
        // into trouble here.
        // FIXME: at the moment all.bash does not pass when I leave out
        // NOSPLIT for these wrappers, so all are currently tagged with NOSPLIT.
-       setupTextLSym(fn, obj.NOSPLIT|obj.ABIWRAPPER)
+       fn.Pragma |= ir.Nosplit
 
        // Generate call. Use tail call if no params and no returns,
        // but a regular call otherwise.
@@ -314,8 +363,6 @@ func makeABIWrapper(f *ir.Func, wrapperABI obj.ABI) {
        ir.CurFunc = fn
        typecheck.Stmts(fn.Body)
 
-       escape.Batch([]*ir.Func{fn}, false)
-
        typecheck.Target.Decls = append(typecheck.Target.Decls, fn)
 
        // Restore previous context.
@@ -332,6 +379,9 @@ func setupTextLSym(f *ir.Func, flag int) {
        if f.Wrapper() {
                flag |= obj.WRAPPER
        }
+       if f.ABIWrapper() {
+               flag |= obj.ABIWRAPPER
+       }
        if f.Needctxt() {
                flag |= obj.NEEDCTXT
        }
index a2434366a022c00907950aad8c02be7956800d37..1fbc6a847d0f0712f0c399a9722660493b8c140f 100644 (file)
@@ -61,6 +61,12 @@ func newNowritebarrierrecChecker() *nowritebarrierrecChecker {
                        continue
                }
                c.curfn = n.(*ir.Func)
+               if c.curfn.ABIWrapper() {
+                       // We only want "real" calls to these
+                       // functions, not the generated ones within
+                       // their own ABI wrappers.
+                       continue
+               }
                ir.Visit(n, c.findExtraCalls)
        }
        c.curfn = nil
index 42f7887a009206f0ef47711abacd4486ea84e15f..a1f6d898149478b53b2e495c92a3dd0bdfeae356 100644 (file)
@@ -1687,7 +1687,7 @@ func (s *state) stmt(n ir.Node) {
                n := n.(*ir.TailCallStmt)
                b := s.exit()
                b.Kind = ssa.BlockRetJmp // override BlockRet
-               b.Aux = callTargetLSym(n.Target, s.curfn.LSym)
+               b.Aux = callTargetLSym(n.Target)
 
        case ir.OCONTINUE, ir.OBREAK:
                n := n.(*ir.BranchStmt)
@@ -5031,7 +5031,7 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val
                        aux := ssa.InterfaceAuxCall(params)
                        call = s.newValue1A(ssa.OpInterLECall, aux.LateExpansionResultType(), aux, codeptr)
                case callee != nil:
-                       aux := ssa.StaticAuxCall(callTargetLSym(callee, s.curfn.LSym), params)
+                       aux := ssa.StaticAuxCall(callTargetLSym(callee), params)
                        call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
                default:
                        s.Fatalf("bad call type %v %v", n.Op(), n)
@@ -7378,44 +7378,17 @@ func clobberBase(n ir.Node) ir.Node {
        return n
 }
 
-// callTargetLSym determines the correct LSym for 'callee' when called
-// from function 'caller'. There are a couple of different scenarios
-// to contend with here:
-//
-// 1. if 'caller' is an ABI wrapper, then we always want to use the
-//    LSym from the Func for the callee.
-//
-// 2. if 'caller' is not an ABI wrapper, then we looked at the callee
-//    to see if it corresponds to a "known" ABI0 symbol (e.g. assembly
-//    routine defined in the current package); if so, we want the call to
-//    directly target the ABI0 symbol (effectively bypassing the
-//    ABIInternal->ABI0 wrapper for 'callee').
-//
-// 3. in all other cases, want the regular ABIInternal linksym
-//
-func callTargetLSym(callee *ir.Name, callerLSym *obj.LSym) *obj.LSym {
-       lsym := callee.Linksym()
-       if !objabi.Experiment.RegabiWrappers {
-               return lsym
-       }
-       fn := callee.Func
-       if fn == nil {
-               return lsym
+// callTargetLSym returns the correct LSym to call 'callee' using its ABI.
+func callTargetLSym(callee *ir.Name) *obj.LSym {
+       if callee.Func == nil {
+               // TODO(austin): This happens in a few cases of
+               // compiler-generated functions. These are all
+               // ABIInternal. It would be better if callee.Func was
+               // never nil and we didn't need this case.
+               return callee.Linksym()
        }
 
-       // check for case 1 above
-       if callerLSym.ABIWrapper() {
-               if nlsym := fn.LSym; nlsym != nil {
-                       lsym = nlsym
-               }
-       } else {
-               // check for case 2 above
-               defABI, hasDefABI := symabiDefs[lsym.Name]
-               if hasDefABI && defABI == obj.ABI0 {
-                       lsym = callee.LinksymABI(obj.ABI0)
-               }
-       }
-       return lsym
+       return callee.LinksymABI(callee.Func.ABI)
 }
 
 func min8(a, b int8) int8 {
index cde4c50026e545cdb493a67c3dbd3c67fff7e801..7ca05d3bf47dd6392261a90684f47605c48d37c9 100644 (file)
@@ -269,13 +269,23 @@ func NeedFuncSym(fn *ir.Func) {
                // funcsymsmu, like in FuncSym.
                base.Fatalf("NeedFuncSym must be called in serial")
        }
+       if fn.ABI != obj.ABIInternal && objabi.Experiment.RegabiWrappers {
+               // Function values must always reference ABIInternal
+               // entry points, so it doesn't make sense to create a
+               // funcsym for other ABIs.
+               //
+               // (If we're using ABI aliases, it doesn't matter.)
+               base.Fatalf("expected ABIInternal: %v has %v", fn.Nname, fn.ABI)
+       }
+       if ir.IsBlank(fn.Nname) {
+               // Blank functions aren't unique, so we can't make a
+               // funcsym for them.
+               base.Fatalf("NeedFuncSym called for _")
+       }
        if !base.Ctxt.Flag_dynlink {
                return
        }
        s := fn.Nname.Sym()
-       if s.IsBlank() {
-               return
-       }
        if base.Flag.CompilingRuntime && (s.Name == "getg" || s.Name == "getclosureptr" || s.Name == "getcallerpc" || s.Name == "getcallersp") {
                // runtime.getg(), getclosureptr(), getcallerpc(), and
                // getcallersp() are not real functions and so do not
index fa16357066a5829e15db2b7ae93e4cca77660db4..43cc4e4a25ae0dcc183f68ddf8791bd304e4a8b8 100644 (file)
@@ -1055,7 +1055,11 @@ func (w *exportWriter) funcExt(n *ir.Name) {
        w.linkname(n.Sym())
        w.symIdx(n.Sym())
 
-       // TODO(register args) remove after register abi is working.
+       // Record definition ABI so cross-ABI calls can be direct.
+       // This is important for the performance of calling some
+       // common functions implemented in assembly (e.g., bytealg).
+       w.uint64(uint64(n.Func.ABI))
+
        w.uint64(uint64(n.Func.Pragma))
 
        // Escape analysis.
index 91bb215a290bb561142ea2d01727dc5487d58cff..35a1a0083af813b94803fb85cbf9b11ff768fffb 100644 (file)
@@ -694,7 +694,8 @@ func (r *importReader) funcExt(n *ir.Name) {
        r.linkname(n.Sym())
        r.symIdx(n.Sym())
 
-       // TODO(register args) remove after register abi is working
+       n.Func.ABI = obj.ABI(r.uint64())
+
        n.SetPragma(ir.PragmaFlag(r.uint64()))
 
        // Escape analysis.
index c689304b34f4ce99dcd7f834f0c169eab4b51c6d..9a32a01a1aceb611fc2c2c8df6d1aa935312a5eb 100644 (file)
@@ -45,7 +45,7 @@ const (
        symUniq
        symSiggen // type symbol has been generated
        symAsm    // on asmlist, for writing to -asmhdr
-       symFunc   // function symbol; uses internal ABI
+       symFunc   // function symbol
 )
 
 func (sym *Sym) OnExportList() bool { return sym.flags&symOnExportList != 0 }
index c34a769a823a59f2bb1d506a6b6640aa5eaf8c1e..75306901856de1701ff551d7eb549a006f1fd645 100644 (file)
@@ -603,6 +603,48 @@ func ParseABI(abistr string) (ABI, bool) {
        }
 }
 
+// ABISet is a bit set of ABI values.
+type ABISet uint8
+
+const (
+       // ABISetCallable is the set of all ABIs any function could
+       // potentially be called using.
+       ABISetCallable ABISet = (1 << ABI0) | (1 << ABIInternal)
+)
+
+// Ensure ABISet is big enough to hold all ABIs.
+var _ ABISet = 1 << (ABICount - 1)
+
+func ABISetOf(abi ABI) ABISet {
+       return 1 << abi
+}
+
+func (a *ABISet) Set(abi ABI, value bool) {
+       if value {
+               *a |= 1 << abi
+       } else {
+               *a &^= 1 << abi
+       }
+}
+
+func (a *ABISet) Get(abi ABI) bool {
+       return (*a>>abi)&1 != 0
+}
+
+func (a ABISet) String() string {
+       s := "{"
+       for i := ABI(0); a != 0; i++ {
+               if a&(1<<i) != 0 {
+                       if s != "{" {
+                               s += ","
+                       }
+                       s += i.String()
+                       a &^= 1 << i
+               }
+       }
+       return s + "}"
+}
+
 // Attribute is a set of symbol attributes.
 type Attribute uint32
 
index 2e9890d86c074f1b56af47b4231766f0bd6a8396..ceeae7a257cdcc33313b85c31715fe53ead79a74 100644 (file)
@@ -144,11 +144,9 @@ func instinit(ctxt *obj.Link) {
        gcWriteBarrier = ctxt.LookupABI("runtime.gcWriteBarrier", obj.ABIInternal)
        sigpanic = ctxt.LookupABI("runtime.sigpanic", obj.ABIInternal)
        deferreturn = ctxt.LookupABI("runtime.deferreturn", obj.ABIInternal)
-       // jmpdefer is defined in assembly as ABI0, but what we're
-       // looking for is the *call* to jmpdefer from the Go function
-       // deferreturn, so we're looking for the ABIInternal version
-       // of jmpdefer that's called by Go.
-       jmpdefer = ctxt.LookupABI(`"".jmpdefer`, obj.ABIInternal)
+       // jmpdefer is defined in assembly as ABI0. The compiler will
+       // generate a direct ABI0 call from Go, so look for that.
+       jmpdefer = ctxt.LookupABI(`"".jmpdefer`, obj.ABI0)
 }
 
 func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {