2020-11-28 07:13:54 -05:00
|
|
|
// 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 ir
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"cmd/compile/internal/base"
|
|
|
|
|
"cmd/compile/internal/types"
|
|
|
|
|
"cmd/internal/obj"
|
2023-04-28 15:52:16 -04:00
|
|
|
"cmd/internal/objabi"
|
2020-11-28 07:13:54 -05:00
|
|
|
"cmd/internal/src"
|
[dev.typeparams] cmd/compile: refactor closure construction
typecheck.tcClosure is complicated with many code flows because all of
its callers setup the closure funcs in slightly different ways. E.g.,
it's non-obvious who's responsible for setting the underlying func's
Sym or adding it to target.Decls, or how to write new code that
constructs a closure without interfering with existing code.
This CL refactors everything to use three common functions in package
ir: NewClosureFunc (which handle creating the Func, Name, and
ClosureExpr and wiring them together), NameClosure (which generates
and assigns its unique Sym), and UseClosure (which handles adding the
Func to target.Decls).
Most IR builders can actually name the closure right away, but the
legacy noder+typecheck path may not yet know the name of the enclosing
function. In particular, for methods declared with aliased receiver
parameters, we need to wait until after typechecking top-level
declarations to know the method's true name. So they're left anonymous
until typecheck.
UseClosure does relatively little work today, but it serves as a
useful spot to check that the code setting up closures got it right.
It may also eventually serve as an optimization point for early
lifting of trivial closures, which may or may not ultimately be
beneficial.
Change-Id: I7da1e93c70d268f575b12d6aaeb2336eb910a6f1
Reviewed-on: https://go-review.googlesource.com/c/go/+/327051
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-06-11 03:09:26 -07:00
|
|
|
"fmt"
|
cmd/compile: incorporate inlined function names into closure naming
In Go 1.17, cmd/compile gained the ability to inline calls to
functions that contain function literals (aka "closures"). This was
implemented by duplicating the function literal body and emitting a
second LSym, because in general it might be optimized better than the
original function literal.
However, the second LSym was named simply as any other function
literal appearing literally in the enclosing function would be named.
E.g., if f has a closure "f.funcX", and f is inlined into g, we would
create "g.funcY" (N.B., X and Y need not be the same.). Users then
have no idea this function originally came from f.
With this CL, the inlined call stack is incorporated into the clone
LSym's name: instead of "g.funcY", it's named "g.f.funcY".
In the future, it seems desirable to arrange for the clone's name to
appear exactly as the original name, so stack traces remain the same
as when -l or -d=inlfuncswithclosures are used. But it's unclear
whether the linker supports that today, or whether any downstream
tooling would be confused by this.
Updates #60324.
Change-Id: Ifad0ccef7e959e72005beeecdfffd872f63982f8
Reviewed-on: https://go-review.googlesource.com/c/go/+/497137
Reviewed-by: Michael Pratt <mpratt@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
2023-05-22 13:25:15 -07:00
|
|
|
"strings"
|
2023-05-15 18:00:57 -04:00
|
|
|
"unicode/utf8"
|
2020-11-28 07:13:54 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// A Func corresponds to a single function in a Go program
|
|
|
|
|
// (and vice versa: each function is denoted by exactly one *Func).
|
|
|
|
|
//
|
|
|
|
|
// There are multiple nodes that represent a Func in the IR.
|
|
|
|
|
//
|
2020-12-01 20:51:18 -08:00
|
|
|
// The ONAME node (Func.Nname) is used for plain references to it.
|
|
|
|
|
// The ODCLFUNC node (the Func itself) is used for its declaration code.
|
|
|
|
|
// The OCLOSURE node (Func.OClosure) is used for a reference to a
|
2020-11-28 07:13:54 -05:00
|
|
|
// function literal.
|
|
|
|
|
//
|
2020-12-01 20:51:18 -08:00
|
|
|
// An imported function will have an ONAME node which points to a Func
|
|
|
|
|
// with an empty body.
|
|
|
|
|
// A declared function or method has an ODCLFUNC (the Func itself) and an ONAME.
|
2020-11-28 07:13:54 -05:00
|
|
|
// A function literal is represented directly by an OCLOSURE, but it also
|
|
|
|
|
// has an ODCLFUNC (and a matching ONAME) representing the compiled
|
|
|
|
|
// underlying form of the closure, which accesses the captured variables
|
|
|
|
|
// using a special data structure passed in a register.
|
|
|
|
|
//
|
|
|
|
|
// A method declaration is represented like functions, except f.Sym
|
2022-02-28 15:07:56 -08:00
|
|
|
// will be the qualified method name (e.g., "T.m").
|
2020-11-28 07:13:54 -05:00
|
|
|
//
|
|
|
|
|
// A method expression (T.M) is represented as an OMETHEXPR node,
|
|
|
|
|
// in which n.Left and n.Right point to the type and method, respectively.
|
|
|
|
|
// Each distinct mention of a method expression in the source code
|
|
|
|
|
// constructs a fresh node.
|
|
|
|
|
//
|
|
|
|
|
// A method value (t.M) is represented by ODOTMETH/ODOTINTER
|
2021-06-27 01:28:38 +07:00
|
|
|
// when it is called directly and by OMETHVALUE otherwise.
|
2020-11-28 07:13:54 -05:00
|
|
|
// These are like method expressions, except that for ODOTMETH/ODOTINTER,
|
|
|
|
|
// the method name is stored in Sym instead of Right.
|
2021-06-27 01:28:38 +07:00
|
|
|
// Each OMETHVALUE ends up being implemented as a new
|
2020-11-28 07:13:54 -05:00
|
|
|
// function, a bit like a closure, with its own ODCLFUNC.
|
2021-06-27 01:28:38 +07:00
|
|
|
// The OMETHVALUE uses n.Func to record the linkage to
|
2020-12-01 20:51:18 -08:00
|
|
|
// the generated ODCLFUNC, but there is no
|
2021-06-27 01:28:38 +07:00
|
|
|
// pointer from the Func back to the OMETHVALUE.
|
2020-11-28 07:13:54 -05:00
|
|
|
type Func struct {
|
2020-11-28 07:23:50 -05:00
|
|
|
miniNode
|
2020-12-23 00:02:08 -05:00
|
|
|
Body Nodes
|
2020-11-28 07:23:50 -05:00
|
|
|
|
2020-11-26 00:47:44 -05:00
|
|
|
Nname *Name // ONAME node
|
|
|
|
|
OClosure *ClosureExpr // OCLOSURE node
|
2020-11-28 07:13:54 -05:00
|
|
|
|
|
|
|
|
// ONAME nodes for all params/locals for this func/closure, does NOT
|
2021-01-15 14:12:35 -08:00
|
|
|
// include closurevars until transforming closures during walk.
|
2021-01-11 15:07:09 -08:00
|
|
|
// Names must be listed PPARAMs, PPARAMOUTs, then PAUTOs,
|
|
|
|
|
// with PPARAMs and PPARAMOUTs in order corresponding to the function signature.
|
2023-09-13 19:26:32 -07:00
|
|
|
// Anonymous and blank params are declared as ~pNN (for PPARAMs) and ~rNN (for PPARAMOUTs).
|
2020-11-28 07:31:18 -05:00
|
|
|
Dcl []*Name
|
2020-11-28 07:13:54 -05:00
|
|
|
|
2021-01-01 01:46:55 -08:00
|
|
|
// ClosureVars lists the free variables that are used within a
|
|
|
|
|
// function literal, but formally declared in an enclosing
|
|
|
|
|
// function. The variables in this slice are the closure function's
|
|
|
|
|
// own copy of the variables, which are used within its function
|
|
|
|
|
// body. They will also each have IsClosureVar set, and will have
|
|
|
|
|
// Byval set if they're captured by value.
|
|
|
|
|
ClosureVars []*Name
|
|
|
|
|
|
2021-01-12 12:00:58 -08:00
|
|
|
// Enclosed functions that need to be compiled.
|
|
|
|
|
// Populated during walk.
|
|
|
|
|
Closures []*Func
|
|
|
|
|
|
2020-11-28 07:13:54 -05:00
|
|
|
// Parents records the parent scope of each scope within a
|
|
|
|
|
// function. The root scope (0) has no parent, so the i'th
|
|
|
|
|
// scope's parent is stored at Parents[i-1].
|
|
|
|
|
Parents []ScopeID
|
|
|
|
|
|
|
|
|
|
// Marks records scope boundary changes.
|
|
|
|
|
Marks []Mark
|
|
|
|
|
|
2020-12-28 19:14:39 -08:00
|
|
|
FieldTrack map[*obj.LSym]struct{}
|
2020-11-28 07:13:54 -05:00
|
|
|
DebugInfo interface{}
|
cmd/compile: restructure ABI wrapper generation, export ABI
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>
2021-03-25 19:50:08 -04:00
|
|
|
LSym *obj.LSym // Linker object in this function's native ABI (Func.ABI)
|
2020-11-28 07:13:54 -05:00
|
|
|
|
|
|
|
|
Inl *Inline
|
|
|
|
|
|
2024-01-26 17:49:33 -05:00
|
|
|
// RangeParent, if non-nil, is the first non-range body function containing
|
|
|
|
|
// the closure for the body of a range function.
|
|
|
|
|
RangeParent *Func
|
|
|
|
|
|
|
|
|
|
// funcLitGen, rangeLitGen and goDeferGen track how many closures have been
|
|
|
|
|
// created in this function for function literals, range-over-func loops,
|
|
|
|
|
// and go/defer wrappers, respectively. Used by closureName for creating
|
|
|
|
|
// unique function names.
|
2023-08-16 14:39:47 -07:00
|
|
|
// Tracking goDeferGen separately avoids wrappers throwing off
|
|
|
|
|
// function literal numbering (e.g., runtime/trace_test.TestTraceSymbolize.func11).
|
2024-01-26 17:49:33 -05:00
|
|
|
funcLitGen int32
|
|
|
|
|
rangeLitGen int32
|
|
|
|
|
goDeferGen int32
|
2020-11-28 07:31:18 -05:00
|
|
|
|
2020-11-28 07:13:54 -05:00
|
|
|
Label int32 // largest auto-generated label in this function
|
|
|
|
|
|
|
|
|
|
Endlineno src.XPos
|
|
|
|
|
WBPos src.XPos // position of first write barrier; see SetWBPos
|
|
|
|
|
|
|
|
|
|
Pragma PragmaFlag // go:xxx function annotations
|
|
|
|
|
|
cmd/compile: restructure ABI wrapper generation, export ABI
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>
2021-03-25 19:50:08 -04:00
|
|
|
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
|
|
|
|
|
|
2020-11-28 07:31:18 -05:00
|
|
|
NumDefers int32 // number of defer calls in the function
|
|
|
|
|
NumReturns int32 // number of explicit returns in the function
|
2020-11-28 07:13:54 -05:00
|
|
|
|
2023-11-14 16:17:05 +08:00
|
|
|
// NWBRCalls records the LSyms of functions called by this
|
2020-11-28 07:13:54 -05:00
|
|
|
// function for go:nowritebarrierrec analysis. Only filled in
|
|
|
|
|
// if nowritebarrierrecCheck != nil.
|
|
|
|
|
NWBRCalls *[]SymAndPos
|
2022-02-07 12:00:44 -05:00
|
|
|
|
|
|
|
|
// For wrapper functions, WrappedFunc point to the original Func.
|
|
|
|
|
// Currently only used for go/defer wrappers.
|
|
|
|
|
WrappedFunc *Func
|
2023-01-22 15:30:59 -08:00
|
|
|
|
|
|
|
|
// WasmImport is used by the //go:wasmimport directive to store info about
|
|
|
|
|
// a WebAssembly function import.
|
|
|
|
|
WasmImport *WasmImport
|
cmd/compile: add basic wasmexport support
This CL adds a compiler directive go:wasmexport, which applies to
a Go function and makes it an exported function of the Wasm module
being built, so it can be called directly from the host. As
proposed in #65199, parameter and result types are limited to
32-bit and 64-bit integers and floats, and there can be at most
one result.
As the Go and Wasm calling conventions are different, for a
wasmexport function we generate a wrapper function does the ABI
conversion at compile time.
Currently this CL only adds basic support. In particular,
- it only supports executable mode, i.e. the Go wasm module calls
into the host via wasmimport, which then calls back to Go via
wasmexport. Library (c-shared) mode is not implemented yet.
- only supports wasip1, not js.
- if the exported function unwinds stacks (goroutine switch, stack
growth, etc.), it probably doesn't work.
TODO: support stack unwinding, c-shared mode, js.
For #65199.
Change-Id: Id1777c2d44f7d51942c1caed3173c0a82f120cc4
Reviewed-on: https://go-review.googlesource.com/c/go/+/603055
Reviewed-by: Than McIntosh <thanm@golang.org>
Reviewed-by: Randy Reddig <randy.reddig@fastly.com>
Reviewed-by: David Chase <drchase@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2024-08-03 14:20:58 -04:00
|
|
|
// WasmExport is used by the //go:wasmexport directive to store info about
|
|
|
|
|
// a WebAssembly function import.
|
|
|
|
|
WasmExport *WasmExport
|
2023-01-22 15:30:59 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// WasmImport stores metadata associated with the //go:wasmimport pragma.
|
|
|
|
|
type WasmImport struct {
|
|
|
|
|
Module string
|
|
|
|
|
Name string
|
2020-11-28 07:13:54 -05:00
|
|
|
}
|
|
|
|
|
|
cmd/compile: add basic wasmexport support
This CL adds a compiler directive go:wasmexport, which applies to
a Go function and makes it an exported function of the Wasm module
being built, so it can be called directly from the host. As
proposed in #65199, parameter and result types are limited to
32-bit and 64-bit integers and floats, and there can be at most
one result.
As the Go and Wasm calling conventions are different, for a
wasmexport function we generate a wrapper function does the ABI
conversion at compile time.
Currently this CL only adds basic support. In particular,
- it only supports executable mode, i.e. the Go wasm module calls
into the host via wasmimport, which then calls back to Go via
wasmexport. Library (c-shared) mode is not implemented yet.
- only supports wasip1, not js.
- if the exported function unwinds stacks (goroutine switch, stack
growth, etc.), it probably doesn't work.
TODO: support stack unwinding, c-shared mode, js.
For #65199.
Change-Id: Id1777c2d44f7d51942c1caed3173c0a82f120cc4
Reviewed-on: https://go-review.googlesource.com/c/go/+/603055
Reviewed-by: Than McIntosh <thanm@golang.org>
Reviewed-by: Randy Reddig <randy.reddig@fastly.com>
Reviewed-by: David Chase <drchase@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2024-08-03 14:20:58 -04:00
|
|
|
// WasmExport stores metadata associated with the //go:wasmexport pragma.
|
|
|
|
|
type WasmExport struct {
|
|
|
|
|
Name string
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-16 19:45:12 -07:00
|
|
|
// NewFunc returns a new Func with the given name and type.
|
|
|
|
|
//
|
|
|
|
|
// fpos is the position of the "func" token, and npos is the position
|
|
|
|
|
// of the name identifier.
|
|
|
|
|
//
|
|
|
|
|
// TODO(mdempsky): I suspect there's no need for separate fpos and
|
|
|
|
|
// npos.
|
|
|
|
|
func NewFunc(fpos, npos src.XPos, sym *types.Sym, typ *types.Type) *Func {
|
|
|
|
|
name := NewNameAt(npos, sym, typ)
|
|
|
|
|
name.Class = PFUNC
|
|
|
|
|
sym.SetFunc(true)
|
|
|
|
|
|
|
|
|
|
fn := &Func{Nname: name}
|
|
|
|
|
fn.pos = fpos
|
|
|
|
|
fn.op = ODCLFUNC
|
cmd/compile: restructure ABI wrapper generation, export ABI
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>
2021-03-25 19:50:08 -04:00
|
|
|
// Most functions are ABIInternal. The importer or symabis
|
|
|
|
|
// pass may override this.
|
2023-08-16 19:45:12 -07:00
|
|
|
fn.ABI = obj.ABIInternal
|
|
|
|
|
fn.SetTypecheck(1)
|
|
|
|
|
|
|
|
|
|
name.Func = fn
|
|
|
|
|
|
|
|
|
|
return fn
|
2020-11-28 07:23:50 -05:00
|
|
|
}
|
|
|
|
|
|
2020-12-04 11:37:54 -05:00
|
|
|
func (f *Func) isStmt() {}
|
|
|
|
|
|
2024-05-22 15:00:20 -07:00
|
|
|
func (n *Func) copy() Node { panic(n.no("copy")) }
|
|
|
|
|
func (n *Func) doChildren(do func(Node) bool) bool { return doNodes(n.Body, do) }
|
|
|
|
|
func (n *Func) doChildrenWithHidden(do func(Node) bool) bool { return doNodes(n.Body, do) }
|
|
|
|
|
func (n *Func) editChildren(edit func(Node) Node) { editNodes(n.Body, edit) }
|
|
|
|
|
func (n *Func) editChildrenWithHidden(edit func(Node) Node) { editNodes(n.Body, edit) }
|
2020-12-29 15:36:48 -08:00
|
|
|
|
2021-01-17 02:38:41 -08:00
|
|
|
func (f *Func) Type() *types.Type { return f.Nname.Type() }
|
|
|
|
|
func (f *Func) Sym() *types.Sym { return f.Nname.Sym() }
|
|
|
|
|
func (f *Func) Linksym() *obj.LSym { return f.Nname.Linksym() }
|
|
|
|
|
func (f *Func) LinksymABI(abi obj.ABI) *obj.LSym { return f.Nname.LinksymABI(abi) }
|
2020-11-28 07:23:50 -05:00
|
|
|
|
2020-11-28 07:13:54 -05:00
|
|
|
// An Inline holds fields used for function bodies that can be inlined.
|
|
|
|
|
type Inline struct {
|
|
|
|
|
Cost int32 // heuristic cost of inlining this function
|
|
|
|
|
|
2023-08-26 16:37:24 -07:00
|
|
|
// Copy of Func.Dcl for use during inlining. This copy is needed
|
|
|
|
|
// because the function's Dcl may change from later compiler
|
|
|
|
|
// transformations. This field is also populated when a function
|
|
|
|
|
// from another package is imported and inlined.
|
|
|
|
|
Dcl []*Name
|
|
|
|
|
HaveDcl bool // whether we've loaded Dcl
|
2021-05-27 02:47:04 -07:00
|
|
|
|
2023-06-29 13:22:26 -04:00
|
|
|
// Function properties, encoded as a string (these are used for
|
|
|
|
|
// making inlining decisions). See cmd/compile/internal/inline/inlheur.
|
|
|
|
|
Properties string
|
|
|
|
|
|
2021-05-27 02:47:04 -07:00
|
|
|
// CanDelayResults reports whether it's safe for the inliner to delay
|
|
|
|
|
// initializing the result parameters until immediately before the
|
|
|
|
|
// "return" statement.
|
|
|
|
|
CanDelayResults bool
|
2020-11-28 07:13:54 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A Mark represents a scope boundary.
|
|
|
|
|
type Mark struct {
|
|
|
|
|
// Pos is the position of the token that marks the scope
|
|
|
|
|
// change.
|
|
|
|
|
Pos src.XPos
|
|
|
|
|
|
|
|
|
|
// Scope identifies the innermost scope to the right of Pos.
|
|
|
|
|
Scope ScopeID
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A ScopeID represents a lexical scope within a function.
|
|
|
|
|
type ScopeID int32
|
|
|
|
|
|
|
|
|
|
const (
|
2023-08-27 00:01:37 +07:00
|
|
|
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)
|
|
|
|
|
funcHasDefer // contains a defer statement
|
|
|
|
|
funcNilCheckDisabled // disable nil checks when compiling this function
|
|
|
|
|
funcInlinabilityChecked // inliner has already determined whether the function is inlinable
|
|
|
|
|
funcNeverReturns // function never returns (in most cases calls panic(), os.Exit(), or equivalent)
|
|
|
|
|
funcOpenCodedDeferDisallowed // can't do open-coded defers
|
|
|
|
|
funcClosureResultsLost // closure is called indirectly and we lost track of its results; used by escape analysis
|
|
|
|
|
funcPackageInit // compiler emitted .init func for package
|
2020-11-28 07:13:54 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type SymAndPos struct {
|
|
|
|
|
Sym *obj.LSym // LSym of callee
|
|
|
|
|
Pos src.XPos // line of call
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (f *Func) Dupok() bool { return f.flags&funcDupok != 0 }
|
|
|
|
|
func (f *Func) Wrapper() bool { return f.flags&funcWrapper != 0 }
|
cmd/compile: restructure ABI wrapper generation, export ABI
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>
2021-03-25 19:50:08 -04:00
|
|
|
func (f *Func) ABIWrapper() bool { return f.flags&funcABIWrapper != 0 }
|
2020-11-28 07:13:54 -05:00
|
|
|
func (f *Func) Needctxt() bool { return f.flags&funcNeedctxt != 0 }
|
|
|
|
|
func (f *Func) HasDefer() bool { return f.flags&funcHasDefer != 0 }
|
|
|
|
|
func (f *Func) NilCheckDisabled() bool { return f.flags&funcNilCheckDisabled != 0 }
|
|
|
|
|
func (f *Func) InlinabilityChecked() bool { return f.flags&funcInlinabilityChecked != 0 }
|
2023-08-01 12:00:57 -04:00
|
|
|
func (f *Func) NeverReturns() bool { return f.flags&funcNeverReturns != 0 }
|
2020-11-28 07:13:54 -05:00
|
|
|
func (f *Func) OpenCodedDeferDisallowed() bool { return f.flags&funcOpenCodedDeferDisallowed != 0 }
|
2023-08-15 16:45:52 -07:00
|
|
|
func (f *Func) ClosureResultsLost() bool { return f.flags&funcClosureResultsLost != 0 }
|
2023-01-26 15:12:00 -05:00
|
|
|
func (f *Func) IsPackageInit() bool { return f.flags&funcPackageInit != 0 }
|
2020-11-28 07:13:54 -05:00
|
|
|
|
|
|
|
|
func (f *Func) SetDupok(b bool) { f.flags.set(funcDupok, b) }
|
|
|
|
|
func (f *Func) SetWrapper(b bool) { f.flags.set(funcWrapper, b) }
|
cmd/compile: restructure ABI wrapper generation, export ABI
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>
2021-03-25 19:50:08 -04:00
|
|
|
func (f *Func) SetABIWrapper(b bool) { f.flags.set(funcABIWrapper, b) }
|
2020-11-28 07:13:54 -05:00
|
|
|
func (f *Func) SetNeedctxt(b bool) { f.flags.set(funcNeedctxt, b) }
|
|
|
|
|
func (f *Func) SetHasDefer(b bool) { f.flags.set(funcHasDefer, b) }
|
|
|
|
|
func (f *Func) SetNilCheckDisabled(b bool) { f.flags.set(funcNilCheckDisabled, b) }
|
|
|
|
|
func (f *Func) SetInlinabilityChecked(b bool) { f.flags.set(funcInlinabilityChecked, b) }
|
2023-08-01 12:00:57 -04:00
|
|
|
func (f *Func) SetNeverReturns(b bool) { f.flags.set(funcNeverReturns, b) }
|
2020-11-28 07:13:54 -05:00
|
|
|
func (f *Func) SetOpenCodedDeferDisallowed(b bool) { f.flags.set(funcOpenCodedDeferDisallowed, b) }
|
2023-08-15 16:45:52 -07:00
|
|
|
func (f *Func) SetClosureResultsLost(b bool) { f.flags.set(funcClosureResultsLost, b) }
|
2023-01-26 15:12:00 -05:00
|
|
|
func (f *Func) SetIsPackageInit(b bool) { f.flags.set(funcPackageInit, b) }
|
2020-11-28 07:13:54 -05:00
|
|
|
|
|
|
|
|
func (f *Func) SetWBPos(pos src.XPos) {
|
|
|
|
|
if base.Debug.WB != 0 {
|
|
|
|
|
base.WarnfAt(pos, "write barrier")
|
|
|
|
|
}
|
|
|
|
|
if !f.WBPos.IsKnown() {
|
|
|
|
|
f.WBPos = pos
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-27 00:01:37 +07:00
|
|
|
func (f *Func) IsClosure() bool {
|
|
|
|
|
if f.OClosure == nil {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
// Trivial closure will be converted to global.
|
|
|
|
|
return !IsTrivialClosure(f.OClosure)
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-28 15:52:16 -04:00
|
|
|
// FuncName returns the name (without the package) of the function f.
|
2020-12-26 21:43:30 -08:00
|
|
|
func FuncName(f *Func) string {
|
2020-12-21 15:10:26 -05:00
|
|
|
if f == nil || f.Nname == nil {
|
2020-11-28 07:13:54 -05:00
|
|
|
return "<nil>"
|
|
|
|
|
}
|
2020-12-26 21:43:30 -08:00
|
|
|
return f.Sym().Name
|
2020-11-28 07:13:54 -05:00
|
|
|
}
|
|
|
|
|
|
2023-04-28 15:52:16 -04:00
|
|
|
// PkgFuncName returns the name of the function referenced by f, with package
|
|
|
|
|
// prepended.
|
|
|
|
|
//
|
|
|
|
|
// This differs from the compiler's internal convention where local functions
|
|
|
|
|
// lack a package. This is primarily useful when the ultimate consumer of this
|
|
|
|
|
// is a human looking at message.
|
2020-12-26 21:43:30 -08:00
|
|
|
func PkgFuncName(f *Func) string {
|
|
|
|
|
if f == nil || f.Nname == nil {
|
2020-11-28 07:13:54 -05:00
|
|
|
return "<nil>"
|
|
|
|
|
}
|
2020-12-26 21:43:30 -08:00
|
|
|
s := f.Sym()
|
2020-11-28 07:13:54 -05:00
|
|
|
pkg := s.Pkg
|
|
|
|
|
|
2022-05-12 15:47:21 -07:00
|
|
|
return pkg.Path + "." + s.Name
|
2020-11-28 07:13:54 -05:00
|
|
|
}
|
[dev.regabi] cmd/compile: move helpers into package ir [generated]
[git-generate]
cd src/cmd/compile/internal/gc
sed -i '' 's/TestBuiltin.*/& t.Skip("mkbuiltin needs fixing")/' builtin_test.go
gofmt -w builtin_test.go
rf '
# Inline a few little-used constructors to avoid bringing them.
ex {
import "cmd/compile/internal/base"
import "cmd/compile/internal/ir"
import "cmd/compile/internal/types"
import "cmd/internal/src"
var typ *types.Type
var sym *types.Sym
var str string
symfield(sym, typ) -> ir.NewField(base.Pos, sym, nil, typ)
anonfield(typ) -> ir.NewField(base.Pos, nil, nil, typ)
namedfield(str, typ) -> ir.NewField(base.Pos, lookup(str), nil, typ)
var cp *ir.CallPartExpr
callpartMethod(cp) -> cp.Method
var n ir.Node
callpartMethod(n) -> n.(*ir.CallPartExpr).Method
var ns []ir.Node
liststmt(ns) -> ir.NewBlockStmt(src.NoXPos, ns)
}
rm symfield anonfield namedfield liststmt callpartMethod
mv maxStackVarSize MaxStackVarSize
mv maxImplicitStackVarSize MaxImplicitStackVarSize
mv smallArrayBytes MaxSmallArraySize
mv MaxStackVarSize cfg.go
mv nodbool NewBool
mv nodintconst NewInt
mv nodstr NewString
mv NewBool NewInt NewString const.go
mv Mpprec ConstPrec
mv bigFloatVal BigFloat
mv doesoverflow ConstOverflow
mv isGoConst IsConstNode
mv smallintconst IsSmallIntConst
mv isZero IsZero
mv islvalue IsAssignable
mv staticValue StaticValue
mv samesafeexpr SameSafeExpr
mv checkPtr ShouldCheckPtr
mv isReflectHeaderDataField IsReflectHeaderDataField
mv paramNnames ParamNames
mv methodSym MethodSym
mv methodSymSuffix MethodSymSuffix
mv methodExprFunc MethodExprFunc
mv methodExprName MethodExprName
mv IsZero IsAssignable StaticValue staticValue1 reassigned \
IsIntrinsicCall \
SameSafeExpr ShouldCheckPtr IsReflectHeaderDataField \
ParamNames MethodSym MethodSymSuffix \
MethodExprName MethodExprFunc \
expr.go
mv Curfn CurFunc
mv funcsymname FuncSymName
mv newFuncNameAt NewFuncNameAt
mv setNodeNameFunc MarkFunc
mv CurFunc FuncSymName NewFuncNameAt MarkFunc func.go
mv isParamStackCopy IsParamStackCopy
mv isParamHeapCopy IsParamHeapCopy
mv nodfp RegFP
mv IsParamStackCopy IsParamHeapCopy RegFP name.go
mv hasUniquePos HasUniquePos
mv setlineno SetPos
mv initExpr InitExpr
mv hasNamedResults HasNamedResults
mv outervalue OuterValue
mv HasNamedResults HasUniquePos SetPos InitExpr OuterValue EscNever node.go
mv visitBottomUp VisitFuncsBottomUp # scc.go
mv cfg.go \
NewBool NewInt NewString \ # parts of const.go
ConstPrec BigFloat ConstOverflow IsConstNode IsSmallIntConst \
expr.go func.go name.go node.go scc.go \
cmd/compile/internal/ir
'
Change-Id: I13402c5a2cedbf78d993a1eae2940718f23ac166
Reviewed-on: https://go-review.googlesource.com/c/go/+/279421
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 00:38:15 -05:00
|
|
|
|
2023-04-28 15:52:16 -04:00
|
|
|
// LinkFuncName returns the name of the function f, as it will appear in the
|
|
|
|
|
// symbol table of the final linked binary.
|
|
|
|
|
func LinkFuncName(f *Func) string {
|
|
|
|
|
if f == nil || f.Nname == nil {
|
|
|
|
|
return "<nil>"
|
|
|
|
|
}
|
|
|
|
|
s := f.Sym()
|
|
|
|
|
pkg := s.Pkg
|
|
|
|
|
|
|
|
|
|
return objabi.PathToPrefix(pkg.Path) + "." + s.Name
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-15 18:00:57 -04:00
|
|
|
// ParseLinkFuncName parsers a symbol name (as returned from LinkFuncName) back
|
|
|
|
|
// to the package path and local symbol name.
|
|
|
|
|
func ParseLinkFuncName(name string) (pkg, sym string, err error) {
|
|
|
|
|
pkg, sym = splitPkg(name)
|
|
|
|
|
if pkg == "" {
|
|
|
|
|
return "", "", fmt.Errorf("no package path in name")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pkg, err = objabi.PrefixToPath(pkg) // unescape
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", "", fmt.Errorf("malformed package path: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return pkg, sym, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Borrowed from x/mod.
|
|
|
|
|
func modPathOK(r rune) bool {
|
|
|
|
|
if r < utf8.RuneSelf {
|
|
|
|
|
return r == '-' || r == '.' || r == '_' || r == '~' ||
|
|
|
|
|
'0' <= r && r <= '9' ||
|
|
|
|
|
'A' <= r && r <= 'Z' ||
|
|
|
|
|
'a' <= r && r <= 'z'
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func escapedImportPathOK(r rune) bool {
|
|
|
|
|
return modPathOK(r) || r == '+' || r == '/' || r == '%'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// splitPkg splits the full linker symbol name into package and local symbol
|
|
|
|
|
// name.
|
|
|
|
|
func splitPkg(name string) (pkgpath, sym string) {
|
|
|
|
|
// package-sym split is at first dot after last the / that comes before
|
|
|
|
|
// any characters illegal in a package path.
|
|
|
|
|
|
|
|
|
|
lastSlashIdx := 0
|
|
|
|
|
for i, r := range name {
|
|
|
|
|
// Catches cases like:
|
|
|
|
|
// * example.foo[sync/atomic.Uint64].
|
|
|
|
|
// * example%2ecom.foo[sync/atomic.Uint64].
|
|
|
|
|
//
|
|
|
|
|
// Note that name is still escaped; unescape occurs after splitPkg.
|
|
|
|
|
if !escapedImportPathOK(r) {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
if r == '/' {
|
|
|
|
|
lastSlashIdx = i
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for i := lastSlashIdx; i < len(name); i++ {
|
|
|
|
|
r := name[i]
|
|
|
|
|
if r == '.' {
|
|
|
|
|
return name[:i], name[i+1:]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return "", name
|
|
|
|
|
}
|
|
|
|
|
|
[dev.regabi] cmd/compile: move helpers into package ir [generated]
[git-generate]
cd src/cmd/compile/internal/gc
sed -i '' 's/TestBuiltin.*/& t.Skip("mkbuiltin needs fixing")/' builtin_test.go
gofmt -w builtin_test.go
rf '
# Inline a few little-used constructors to avoid bringing them.
ex {
import "cmd/compile/internal/base"
import "cmd/compile/internal/ir"
import "cmd/compile/internal/types"
import "cmd/internal/src"
var typ *types.Type
var sym *types.Sym
var str string
symfield(sym, typ) -> ir.NewField(base.Pos, sym, nil, typ)
anonfield(typ) -> ir.NewField(base.Pos, nil, nil, typ)
namedfield(str, typ) -> ir.NewField(base.Pos, lookup(str), nil, typ)
var cp *ir.CallPartExpr
callpartMethod(cp) -> cp.Method
var n ir.Node
callpartMethod(n) -> n.(*ir.CallPartExpr).Method
var ns []ir.Node
liststmt(ns) -> ir.NewBlockStmt(src.NoXPos, ns)
}
rm symfield anonfield namedfield liststmt callpartMethod
mv maxStackVarSize MaxStackVarSize
mv maxImplicitStackVarSize MaxImplicitStackVarSize
mv smallArrayBytes MaxSmallArraySize
mv MaxStackVarSize cfg.go
mv nodbool NewBool
mv nodintconst NewInt
mv nodstr NewString
mv NewBool NewInt NewString const.go
mv Mpprec ConstPrec
mv bigFloatVal BigFloat
mv doesoverflow ConstOverflow
mv isGoConst IsConstNode
mv smallintconst IsSmallIntConst
mv isZero IsZero
mv islvalue IsAssignable
mv staticValue StaticValue
mv samesafeexpr SameSafeExpr
mv checkPtr ShouldCheckPtr
mv isReflectHeaderDataField IsReflectHeaderDataField
mv paramNnames ParamNames
mv methodSym MethodSym
mv methodSymSuffix MethodSymSuffix
mv methodExprFunc MethodExprFunc
mv methodExprName MethodExprName
mv IsZero IsAssignable StaticValue staticValue1 reassigned \
IsIntrinsicCall \
SameSafeExpr ShouldCheckPtr IsReflectHeaderDataField \
ParamNames MethodSym MethodSymSuffix \
MethodExprName MethodExprFunc \
expr.go
mv Curfn CurFunc
mv funcsymname FuncSymName
mv newFuncNameAt NewFuncNameAt
mv setNodeNameFunc MarkFunc
mv CurFunc FuncSymName NewFuncNameAt MarkFunc func.go
mv isParamStackCopy IsParamStackCopy
mv isParamHeapCopy IsParamHeapCopy
mv nodfp RegFP
mv IsParamStackCopy IsParamHeapCopy RegFP name.go
mv hasUniquePos HasUniquePos
mv setlineno SetPos
mv initExpr InitExpr
mv hasNamedResults HasNamedResults
mv outervalue OuterValue
mv HasNamedResults HasUniquePos SetPos InitExpr OuterValue EscNever node.go
mv visitBottomUp VisitFuncsBottomUp # scc.go
mv cfg.go \
NewBool NewInt NewString \ # parts of const.go
ConstPrec BigFloat ConstOverflow IsConstNode IsSmallIntConst \
expr.go func.go name.go node.go scc.go \
cmd/compile/internal/ir
'
Change-Id: I13402c5a2cedbf78d993a1eae2940718f23ac166
Reviewed-on: https://go-review.googlesource.com/c/go/+/279421
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 00:38:15 -05:00
|
|
|
var CurFunc *Func
|
|
|
|
|
|
2021-06-21 22:35:01 -07:00
|
|
|
// WithFunc invokes do with CurFunc and base.Pos set to curfn and
|
|
|
|
|
// curfn.Pos(), respectively, and then restores their previous values
|
|
|
|
|
// before returning.
|
|
|
|
|
func WithFunc(curfn *Func, do func()) {
|
|
|
|
|
oldfn, oldpos := CurFunc, base.Pos
|
|
|
|
|
defer func() { CurFunc, base.Pos = oldfn, oldpos }()
|
|
|
|
|
|
|
|
|
|
CurFunc, base.Pos = curfn, curfn.Pos()
|
|
|
|
|
do()
|
|
|
|
|
}
|
|
|
|
|
|
[dev.regabi] cmd/compile: move helpers into package ir [generated]
[git-generate]
cd src/cmd/compile/internal/gc
sed -i '' 's/TestBuiltin.*/& t.Skip("mkbuiltin needs fixing")/' builtin_test.go
gofmt -w builtin_test.go
rf '
# Inline a few little-used constructors to avoid bringing them.
ex {
import "cmd/compile/internal/base"
import "cmd/compile/internal/ir"
import "cmd/compile/internal/types"
import "cmd/internal/src"
var typ *types.Type
var sym *types.Sym
var str string
symfield(sym, typ) -> ir.NewField(base.Pos, sym, nil, typ)
anonfield(typ) -> ir.NewField(base.Pos, nil, nil, typ)
namedfield(str, typ) -> ir.NewField(base.Pos, lookup(str), nil, typ)
var cp *ir.CallPartExpr
callpartMethod(cp) -> cp.Method
var n ir.Node
callpartMethod(n) -> n.(*ir.CallPartExpr).Method
var ns []ir.Node
liststmt(ns) -> ir.NewBlockStmt(src.NoXPos, ns)
}
rm symfield anonfield namedfield liststmt callpartMethod
mv maxStackVarSize MaxStackVarSize
mv maxImplicitStackVarSize MaxImplicitStackVarSize
mv smallArrayBytes MaxSmallArraySize
mv MaxStackVarSize cfg.go
mv nodbool NewBool
mv nodintconst NewInt
mv nodstr NewString
mv NewBool NewInt NewString const.go
mv Mpprec ConstPrec
mv bigFloatVal BigFloat
mv doesoverflow ConstOverflow
mv isGoConst IsConstNode
mv smallintconst IsSmallIntConst
mv isZero IsZero
mv islvalue IsAssignable
mv staticValue StaticValue
mv samesafeexpr SameSafeExpr
mv checkPtr ShouldCheckPtr
mv isReflectHeaderDataField IsReflectHeaderDataField
mv paramNnames ParamNames
mv methodSym MethodSym
mv methodSymSuffix MethodSymSuffix
mv methodExprFunc MethodExprFunc
mv methodExprName MethodExprName
mv IsZero IsAssignable StaticValue staticValue1 reassigned \
IsIntrinsicCall \
SameSafeExpr ShouldCheckPtr IsReflectHeaderDataField \
ParamNames MethodSym MethodSymSuffix \
MethodExprName MethodExprFunc \
expr.go
mv Curfn CurFunc
mv funcsymname FuncSymName
mv newFuncNameAt NewFuncNameAt
mv setNodeNameFunc MarkFunc
mv CurFunc FuncSymName NewFuncNameAt MarkFunc func.go
mv isParamStackCopy IsParamStackCopy
mv isParamHeapCopy IsParamHeapCopy
mv nodfp RegFP
mv IsParamStackCopy IsParamHeapCopy RegFP name.go
mv hasUniquePos HasUniquePos
mv setlineno SetPos
mv initExpr InitExpr
mv hasNamedResults HasNamedResults
mv outervalue OuterValue
mv HasNamedResults HasUniquePos SetPos InitExpr OuterValue EscNever node.go
mv visitBottomUp VisitFuncsBottomUp # scc.go
mv cfg.go \
NewBool NewInt NewString \ # parts of const.go
ConstPrec BigFloat ConstOverflow IsConstNode IsSmallIntConst \
expr.go func.go name.go node.go scc.go \
cmd/compile/internal/ir
'
Change-Id: I13402c5a2cedbf78d993a1eae2940718f23ac166
Reviewed-on: https://go-review.googlesource.com/c/go/+/279421
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 00:38:15 -05:00
|
|
|
func FuncSymName(s *types.Sym) string {
|
|
|
|
|
return s.Name + "·f"
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-23 01:05:16 -05:00
|
|
|
// ClosureDebugRuntimeCheck applies boilerplate checks for debug flags
|
2022-11-11 19:22:35 +08:00
|
|
|
// and compiling runtime.
|
2020-12-23 01:05:16 -05:00
|
|
|
func ClosureDebugRuntimeCheck(clo *ClosureExpr) {
|
|
|
|
|
if base.Debug.Closure > 0 {
|
|
|
|
|
if clo.Esc() == EscHeap {
|
|
|
|
|
base.WarnfAt(clo.Pos(), "heap closure, captured vars = %v", clo.Func.ClosureVars)
|
|
|
|
|
} else {
|
|
|
|
|
base.WarnfAt(clo.Pos(), "stack closure, captured vars = %v", clo.Func.ClosureVars)
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-06-03 18:25:47 -04:00
|
|
|
if base.Flag.CompilingRuntime && clo.Esc() == EscHeap && !clo.IsGoWrap {
|
2023-03-09 16:21:22 -08:00
|
|
|
base.ErrorfAt(clo.Pos(), 0, "heap-allocated closure %s, not allowed in runtime", FuncName(clo.Func))
|
2020-12-23 01:05:16 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// IsTrivialClosure reports whether closure clo has an
|
|
|
|
|
// empty list of captured vars.
|
|
|
|
|
func IsTrivialClosure(clo *ClosureExpr) bool {
|
|
|
|
|
return len(clo.Func.ClosureVars) == 0
|
|
|
|
|
}
|
[dev.typeparams] cmd/compile: refactor closure construction
typecheck.tcClosure is complicated with many code flows because all of
its callers setup the closure funcs in slightly different ways. E.g.,
it's non-obvious who's responsible for setting the underlying func's
Sym or adding it to target.Decls, or how to write new code that
constructs a closure without interfering with existing code.
This CL refactors everything to use three common functions in package
ir: NewClosureFunc (which handle creating the Func, Name, and
ClosureExpr and wiring them together), NameClosure (which generates
and assigns its unique Sym), and UseClosure (which handles adding the
Func to target.Decls).
Most IR builders can actually name the closure right away, but the
legacy noder+typecheck path may not yet know the name of the enclosing
function. In particular, for methods declared with aliased receiver
parameters, we need to wait until after typechecking top-level
declarations to know the method's true name. So they're left anonymous
until typecheck.
UseClosure does relatively little work today, but it serves as a
useful spot to check that the code setting up closures got it right.
It may also eventually serve as an optimization point for early
lifting of trivial closures, which may or may not ultimately be
beneficial.
Change-Id: I7da1e93c70d268f575b12d6aaeb2336eb910a6f1
Reviewed-on: https://go-review.googlesource.com/c/go/+/327051
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-06-11 03:09:26 -07:00
|
|
|
|
|
|
|
|
// globClosgen is like Func.Closgen, but for the global scope.
|
|
|
|
|
var globClosgen int32
|
|
|
|
|
|
cmd/compile: incorporate inlined function names into closure naming
In Go 1.17, cmd/compile gained the ability to inline calls to
functions that contain function literals (aka "closures"). This was
implemented by duplicating the function literal body and emitting a
second LSym, because in general it might be optimized better than the
original function literal.
However, the second LSym was named simply as any other function
literal appearing literally in the enclosing function would be named.
E.g., if f has a closure "f.funcX", and f is inlined into g, we would
create "g.funcY" (N.B., X and Y need not be the same.). Users then
have no idea this function originally came from f.
With this CL, the inlined call stack is incorporated into the clone
LSym's name: instead of "g.funcY", it's named "g.f.funcY".
In the future, it seems desirable to arrange for the clone's name to
appear exactly as the original name, so stack traces remain the same
as when -l or -d=inlfuncswithclosures are used. But it's unclear
whether the linker supports that today, or whether any downstream
tooling would be confused by this.
Updates #60324.
Change-Id: Ifad0ccef7e959e72005beeecdfffd872f63982f8
Reviewed-on: https://go-review.googlesource.com/c/go/+/497137
Reviewed-by: Michael Pratt <mpratt@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
2023-05-22 13:25:15 -07:00
|
|
|
// closureName generates a new unique name for a closure within outerfn at pos.
|
2023-08-16 14:39:47 -07:00
|
|
|
func closureName(outerfn *Func, pos src.XPos, why Op) *types.Sym {
|
2024-07-17 00:55:11 +07:00
|
|
|
if outerfn.OClosure != nil && outerfn.OClosure.Func.RangeParent != nil {
|
2024-01-26 17:49:33 -05:00
|
|
|
outerfn = outerfn.OClosure.Func.RangeParent
|
|
|
|
|
}
|
[dev.typeparams] cmd/compile: refactor closure construction
typecheck.tcClosure is complicated with many code flows because all of
its callers setup the closure funcs in slightly different ways. E.g.,
it's non-obvious who's responsible for setting the underlying func's
Sym or adding it to target.Decls, or how to write new code that
constructs a closure without interfering with existing code.
This CL refactors everything to use three common functions in package
ir: NewClosureFunc (which handle creating the Func, Name, and
ClosureExpr and wiring them together), NameClosure (which generates
and assigns its unique Sym), and UseClosure (which handles adding the
Func to target.Decls).
Most IR builders can actually name the closure right away, but the
legacy noder+typecheck path may not yet know the name of the enclosing
function. In particular, for methods declared with aliased receiver
parameters, we need to wait until after typechecking top-level
declarations to know the method's true name. So they're left anonymous
until typecheck.
UseClosure does relatively little work today, but it serves as a
useful spot to check that the code setting up closures got it right.
It may also eventually serve as an optimization point for early
lifting of trivial closures, which may or may not ultimately be
beneficial.
Change-Id: I7da1e93c70d268f575b12d6aaeb2336eb910a6f1
Reviewed-on: https://go-review.googlesource.com/c/go/+/327051
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-06-11 03:09:26 -07:00
|
|
|
pkg := types.LocalPkg
|
|
|
|
|
outer := "glob."
|
2024-05-22 16:15:43 -04:00
|
|
|
var suffix string = "."
|
2023-08-16 14:39:47 -07:00
|
|
|
switch why {
|
|
|
|
|
default:
|
|
|
|
|
base.FatalfAt(pos, "closureName: bad Op: %v", why)
|
|
|
|
|
case OCLOSURE:
|
2024-07-17 00:55:11 +07:00
|
|
|
if outerfn.OClosure == nil {
|
2024-05-22 16:15:43 -04:00
|
|
|
suffix = ".func"
|
[dev.typeparams] cmd/compile: refactor closure construction
typecheck.tcClosure is complicated with many code flows because all of
its callers setup the closure funcs in slightly different ways. E.g.,
it's non-obvious who's responsible for setting the underlying func's
Sym or adding it to target.Decls, or how to write new code that
constructs a closure without interfering with existing code.
This CL refactors everything to use three common functions in package
ir: NewClosureFunc (which handle creating the Func, Name, and
ClosureExpr and wiring them together), NameClosure (which generates
and assigns its unique Sym), and UseClosure (which handles adding the
Func to target.Decls).
Most IR builders can actually name the closure right away, but the
legacy noder+typecheck path may not yet know the name of the enclosing
function. In particular, for methods declared with aliased receiver
parameters, we need to wait until after typechecking top-level
declarations to know the method's true name. So they're left anonymous
until typecheck.
UseClosure does relatively little work today, but it serves as a
useful spot to check that the code setting up closures got it right.
It may also eventually serve as an optimization point for early
lifting of trivial closures, which may or may not ultimately be
beneficial.
Change-Id: I7da1e93c70d268f575b12d6aaeb2336eb910a6f1
Reviewed-on: https://go-review.googlesource.com/c/go/+/327051
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-06-11 03:09:26 -07:00
|
|
|
}
|
2024-01-26 17:49:33 -05:00
|
|
|
case ORANGE:
|
2024-05-22 16:15:43 -04:00
|
|
|
suffix = "-range"
|
2023-08-16 14:39:47 -07:00
|
|
|
case OGO:
|
2024-05-22 16:15:43 -04:00
|
|
|
suffix = ".gowrap"
|
2023-08-16 14:39:47 -07:00
|
|
|
case ODEFER:
|
2024-05-22 16:15:43 -04:00
|
|
|
suffix = ".deferwrap"
|
2023-08-16 14:39:47 -07:00
|
|
|
}
|
|
|
|
|
gen := &globClosgen
|
[dev.typeparams] cmd/compile: refactor closure construction
typecheck.tcClosure is complicated with many code flows because all of
its callers setup the closure funcs in slightly different ways. E.g.,
it's non-obvious who's responsible for setting the underlying func's
Sym or adding it to target.Decls, or how to write new code that
constructs a closure without interfering with existing code.
This CL refactors everything to use three common functions in package
ir: NewClosureFunc (which handle creating the Func, Name, and
ClosureExpr and wiring them together), NameClosure (which generates
and assigns its unique Sym), and UseClosure (which handles adding the
Func to target.Decls).
Most IR builders can actually name the closure right away, but the
legacy noder+typecheck path may not yet know the name of the enclosing
function. In particular, for methods declared with aliased receiver
parameters, we need to wait until after typechecking top-level
declarations to know the method's true name. So they're left anonymous
until typecheck.
UseClosure does relatively little work today, but it serves as a
useful spot to check that the code setting up closures got it right.
It may also eventually serve as an optimization point for early
lifting of trivial closures, which may or may not ultimately be
beneficial.
Change-Id: I7da1e93c70d268f575b12d6aaeb2336eb910a6f1
Reviewed-on: https://go-review.googlesource.com/c/go/+/327051
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-06-11 03:09:26 -07:00
|
|
|
|
2023-08-16 14:39:47 -07:00
|
|
|
// There may be multiple functions named "_". In those
|
|
|
|
|
// cases, we can't use their individual Closgens as it
|
|
|
|
|
// would lead to name clashes.
|
2024-07-17 00:55:11 +07:00
|
|
|
if !IsBlank(outerfn.Nname) {
|
[dev.typeparams] cmd/compile: refactor closure construction
typecheck.tcClosure is complicated with many code flows because all of
its callers setup the closure funcs in slightly different ways. E.g.,
it's non-obvious who's responsible for setting the underlying func's
Sym or adding it to target.Decls, or how to write new code that
constructs a closure without interfering with existing code.
This CL refactors everything to use three common functions in package
ir: NewClosureFunc (which handle creating the Func, Name, and
ClosureExpr and wiring them together), NameClosure (which generates
and assigns its unique Sym), and UseClosure (which handles adding the
Func to target.Decls).
Most IR builders can actually name the closure right away, but the
legacy noder+typecheck path may not yet know the name of the enclosing
function. In particular, for methods declared with aliased receiver
parameters, we need to wait until after typechecking top-level
declarations to know the method's true name. So they're left anonymous
until typecheck.
UseClosure does relatively little work today, but it serves as a
useful spot to check that the code setting up closures got it right.
It may also eventually serve as an optimization point for early
lifting of trivial closures, which may or may not ultimately be
beneficial.
Change-Id: I7da1e93c70d268f575b12d6aaeb2336eb910a6f1
Reviewed-on: https://go-review.googlesource.com/c/go/+/327051
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-06-11 03:09:26 -07:00
|
|
|
pkg = outerfn.Sym().Pkg
|
|
|
|
|
outer = FuncName(outerfn)
|
|
|
|
|
|
2024-01-26 17:49:33 -05:00
|
|
|
switch why {
|
|
|
|
|
case OCLOSURE:
|
2023-08-16 14:39:47 -07:00
|
|
|
gen = &outerfn.funcLitGen
|
2024-01-26 17:49:33 -05:00
|
|
|
case ORANGE:
|
|
|
|
|
gen = &outerfn.rangeLitGen
|
|
|
|
|
default:
|
2023-08-16 14:39:47 -07:00
|
|
|
gen = &outerfn.goDeferGen
|
[dev.typeparams] cmd/compile: refactor closure construction
typecheck.tcClosure is complicated with many code flows because all of
its callers setup the closure funcs in slightly different ways. E.g.,
it's non-obvious who's responsible for setting the underlying func's
Sym or adding it to target.Decls, or how to write new code that
constructs a closure without interfering with existing code.
This CL refactors everything to use three common functions in package
ir: NewClosureFunc (which handle creating the Func, Name, and
ClosureExpr and wiring them together), NameClosure (which generates
and assigns its unique Sym), and UseClosure (which handles adding the
Func to target.Decls).
Most IR builders can actually name the closure right away, but the
legacy noder+typecheck path may not yet know the name of the enclosing
function. In particular, for methods declared with aliased receiver
parameters, we need to wait until after typechecking top-level
declarations to know the method's true name. So they're left anonymous
until typecheck.
UseClosure does relatively little work today, but it serves as a
useful spot to check that the code setting up closures got it right.
It may also eventually serve as an optimization point for early
lifting of trivial closures, which may or may not ultimately be
beneficial.
Change-Id: I7da1e93c70d268f575b12d6aaeb2336eb910a6f1
Reviewed-on: https://go-review.googlesource.com/c/go/+/327051
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-06-11 03:09:26 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
cmd/compile: incorporate inlined function names into closure naming
In Go 1.17, cmd/compile gained the ability to inline calls to
functions that contain function literals (aka "closures"). This was
implemented by duplicating the function literal body and emitting a
second LSym, because in general it might be optimized better than the
original function literal.
However, the second LSym was named simply as any other function
literal appearing literally in the enclosing function would be named.
E.g., if f has a closure "f.funcX", and f is inlined into g, we would
create "g.funcY" (N.B., X and Y need not be the same.). Users then
have no idea this function originally came from f.
With this CL, the inlined call stack is incorporated into the clone
LSym's name: instead of "g.funcY", it's named "g.f.funcY".
In the future, it seems desirable to arrange for the clone's name to
appear exactly as the original name, so stack traces remain the same
as when -l or -d=inlfuncswithclosures are used. But it's unclear
whether the linker supports that today, or whether any downstream
tooling would be confused by this.
Updates #60324.
Change-Id: Ifad0ccef7e959e72005beeecdfffd872f63982f8
Reviewed-on: https://go-review.googlesource.com/c/go/+/497137
Reviewed-by: Michael Pratt <mpratt@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
2023-05-22 13:25:15 -07:00
|
|
|
// If this closure was created due to inlining, then incorporate any
|
|
|
|
|
// inlined functions' names into the closure's linker symbol name
|
|
|
|
|
// too (#60324).
|
|
|
|
|
if inlIndex := base.Ctxt.InnermostPos(pos).Base().InliningIndex(); inlIndex >= 0 {
|
|
|
|
|
names := []string{outer}
|
|
|
|
|
base.Ctxt.InlTree.AllParents(inlIndex, func(call obj.InlinedCall) {
|
|
|
|
|
names = append(names, call.Name)
|
|
|
|
|
})
|
|
|
|
|
outer = strings.Join(names, ".")
|
|
|
|
|
}
|
|
|
|
|
|
[dev.typeparams] cmd/compile: refactor closure construction
typecheck.tcClosure is complicated with many code flows because all of
its callers setup the closure funcs in slightly different ways. E.g.,
it's non-obvious who's responsible for setting the underlying func's
Sym or adding it to target.Decls, or how to write new code that
constructs a closure without interfering with existing code.
This CL refactors everything to use three common functions in package
ir: NewClosureFunc (which handle creating the Func, Name, and
ClosureExpr and wiring them together), NameClosure (which generates
and assigns its unique Sym), and UseClosure (which handles adding the
Func to target.Decls).
Most IR builders can actually name the closure right away, but the
legacy noder+typecheck path may not yet know the name of the enclosing
function. In particular, for methods declared with aliased receiver
parameters, we need to wait until after typechecking top-level
declarations to know the method's true name. So they're left anonymous
until typecheck.
UseClosure does relatively little work today, but it serves as a
useful spot to check that the code setting up closures got it right.
It may also eventually serve as an optimization point for early
lifting of trivial closures, which may or may not ultimately be
beneficial.
Change-Id: I7da1e93c70d268f575b12d6aaeb2336eb910a6f1
Reviewed-on: https://go-review.googlesource.com/c/go/+/327051
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-06-11 03:09:26 -07:00
|
|
|
*gen++
|
2024-05-22 16:15:43 -04:00
|
|
|
return pkg.Lookup(fmt.Sprintf("%s%s%d", outer, suffix, *gen))
|
[dev.typeparams] cmd/compile: refactor closure construction
typecheck.tcClosure is complicated with many code flows because all of
its callers setup the closure funcs in slightly different ways. E.g.,
it's non-obvious who's responsible for setting the underlying func's
Sym or adding it to target.Decls, or how to write new code that
constructs a closure without interfering with existing code.
This CL refactors everything to use three common functions in package
ir: NewClosureFunc (which handle creating the Func, Name, and
ClosureExpr and wiring them together), NameClosure (which generates
and assigns its unique Sym), and UseClosure (which handles adding the
Func to target.Decls).
Most IR builders can actually name the closure right away, but the
legacy noder+typecheck path may not yet know the name of the enclosing
function. In particular, for methods declared with aliased receiver
parameters, we need to wait until after typechecking top-level
declarations to know the method's true name. So they're left anonymous
until typecheck.
UseClosure does relatively little work today, but it serves as a
useful spot to check that the code setting up closures got it right.
It may also eventually serve as an optimization point for early
lifting of trivial closures, which may or may not ultimately be
beneficial.
Change-Id: I7da1e93c70d268f575b12d6aaeb2336eb910a6f1
Reviewed-on: https://go-review.googlesource.com/c/go/+/327051
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-06-11 03:09:26 -07:00
|
|
|
}
|
|
|
|
|
|
2023-08-16 18:56:41 -07:00
|
|
|
// NewClosureFunc creates a new Func to represent a function literal
|
|
|
|
|
// with the given type.
|
|
|
|
|
//
|
|
|
|
|
// fpos the position used for the underlying ODCLFUNC and ONAME,
|
|
|
|
|
// whereas cpos is the position used for the OCLOSURE. They're
|
|
|
|
|
// separate because in the presence of inlining, the OCLOSURE node
|
|
|
|
|
// should have an inline-adjusted position, whereas the ODCLFUNC and
|
|
|
|
|
// ONAME must not.
|
|
|
|
|
//
|
2023-08-27 00:01:37 +07:00
|
|
|
// outerfn is the enclosing function. The returned function is
|
2023-08-16 18:56:41 -07:00
|
|
|
// appending to pkg.Funcs.
|
2023-08-16 14:39:47 -07:00
|
|
|
//
|
|
|
|
|
// why is the reason we're generating this Func. It can be OCLOSURE
|
|
|
|
|
// (for a normal function literal) or OGO or ODEFER (for wrapping a
|
|
|
|
|
// call expression that has parameters or results).
|
|
|
|
|
func NewClosureFunc(fpos, cpos src.XPos, why Op, typ *types.Type, outerfn *Func, pkg *Package) *Func {
|
2023-08-27 00:01:37 +07:00
|
|
|
if outerfn == nil {
|
|
|
|
|
base.FatalfAt(fpos, "outerfn is nil")
|
2024-04-22 15:58:29 -04:00
|
|
|
}
|
[dev.typeparams] cmd/compile: refactor closure construction
typecheck.tcClosure is complicated with many code flows because all of
its callers setup the closure funcs in slightly different ways. E.g.,
it's non-obvious who's responsible for setting the underlying func's
Sym or adding it to target.Decls, or how to write new code that
constructs a closure without interfering with existing code.
This CL refactors everything to use three common functions in package
ir: NewClosureFunc (which handle creating the Func, Name, and
ClosureExpr and wiring them together), NameClosure (which generates
and assigns its unique Sym), and UseClosure (which handles adding the
Func to target.Decls).
Most IR builders can actually name the closure right away, but the
legacy noder+typecheck path may not yet know the name of the enclosing
function. In particular, for methods declared with aliased receiver
parameters, we need to wait until after typechecking top-level
declarations to know the method's true name. So they're left anonymous
until typecheck.
UseClosure does relatively little work today, but it serves as a
useful spot to check that the code setting up closures got it right.
It may also eventually serve as an optimization point for early
lifting of trivial closures, which may or may not ultimately be
beneficial.
Change-Id: I7da1e93c70d268f575b12d6aaeb2336eb910a6f1
Reviewed-on: https://go-review.googlesource.com/c/go/+/327051
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-06-11 03:09:26 -07:00
|
|
|
|
2023-08-27 00:01:37 +07:00
|
|
|
fn := NewFunc(fpos, fpos, closureName(outerfn, cpos, why), typ)
|
|
|
|
|
fn.SetDupok(outerfn.Dupok()) // if the outer function is dupok, so is the closure
|
|
|
|
|
|
2023-08-16 18:56:41 -07:00
|
|
|
clo := &ClosureExpr{Func: fn}
|
|
|
|
|
clo.op = OCLOSURE
|
|
|
|
|
clo.pos = cpos
|
|
|
|
|
clo.SetType(typ)
|
|
|
|
|
clo.SetTypecheck(1)
|
2024-01-26 17:49:33 -05:00
|
|
|
if why == ORANGE {
|
|
|
|
|
clo.Func.RangeParent = outerfn
|
|
|
|
|
if outerfn.OClosure != nil && outerfn.OClosure.Func.RangeParent != nil {
|
|
|
|
|
clo.Func.RangeParent = outerfn.OClosure.Func.RangeParent
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-08-16 19:45:12 -07:00
|
|
|
fn.OClosure = clo
|
[dev.typeparams] cmd/compile: refactor closure construction
typecheck.tcClosure is complicated with many code flows because all of
its callers setup the closure funcs in slightly different ways. E.g.,
it's non-obvious who's responsible for setting the underlying func's
Sym or adding it to target.Decls, or how to write new code that
constructs a closure without interfering with existing code.
This CL refactors everything to use three common functions in package
ir: NewClosureFunc (which handle creating the Func, Name, and
ClosureExpr and wiring them together), NameClosure (which generates
and assigns its unique Sym), and UseClosure (which handles adding the
Func to target.Decls).
Most IR builders can actually name the closure right away, but the
legacy noder+typecheck path may not yet know the name of the enclosing
function. In particular, for methods declared with aliased receiver
parameters, we need to wait until after typechecking top-level
declarations to know the method's true name. So they're left anonymous
until typecheck.
UseClosure does relatively little work today, but it serves as a
useful spot to check that the code setting up closures got it right.
It may also eventually serve as an optimization point for early
lifting of trivial closures, which may or may not ultimately be
beneficial.
Change-Id: I7da1e93c70d268f575b12d6aaeb2336eb910a6f1
Reviewed-on: https://go-review.googlesource.com/c/go/+/327051
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-06-11 03:09:26 -07:00
|
|
|
|
2023-08-16 19:45:12 -07:00
|
|
|
fn.Nname.Defn = fn
|
2023-08-16 18:56:41 -07:00
|
|
|
pkg.Funcs = append(pkg.Funcs, fn)
|
[dev.typeparams] cmd/compile: refactor closure construction
typecheck.tcClosure is complicated with many code flows because all of
its callers setup the closure funcs in slightly different ways. E.g.,
it's non-obvious who's responsible for setting the underlying func's
Sym or adding it to target.Decls, or how to write new code that
constructs a closure without interfering with existing code.
This CL refactors everything to use three common functions in package
ir: NewClosureFunc (which handle creating the Func, Name, and
ClosureExpr and wiring them together), NameClosure (which generates
and assigns its unique Sym), and UseClosure (which handles adding the
Func to target.Decls).
Most IR builders can actually name the closure right away, but the
legacy noder+typecheck path may not yet know the name of the enclosing
function. In particular, for methods declared with aliased receiver
parameters, we need to wait until after typechecking top-level
declarations to know the method's true name. So they're left anonymous
until typecheck.
UseClosure does relatively little work today, but it serves as a
useful spot to check that the code setting up closures got it right.
It may also eventually serve as an optimization point for early
lifting of trivial closures, which may or may not ultimately be
beneficial.
Change-Id: I7da1e93c70d268f575b12d6aaeb2336eb910a6f1
Reviewed-on: https://go-review.googlesource.com/c/go/+/327051
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-06-11 03:09:26 -07:00
|
|
|
|
2023-08-16 18:56:41 -07:00
|
|
|
return fn
|
[dev.typeparams] cmd/compile: refactor closure construction
typecheck.tcClosure is complicated with many code flows because all of
its callers setup the closure funcs in slightly different ways. E.g.,
it's non-obvious who's responsible for setting the underlying func's
Sym or adding it to target.Decls, or how to write new code that
constructs a closure without interfering with existing code.
This CL refactors everything to use three common functions in package
ir: NewClosureFunc (which handle creating the Func, Name, and
ClosureExpr and wiring them together), NameClosure (which generates
and assigns its unique Sym), and UseClosure (which handles adding the
Func to target.Decls).
Most IR builders can actually name the closure right away, but the
legacy noder+typecheck path may not yet know the name of the enclosing
function. In particular, for methods declared with aliased receiver
parameters, we need to wait until after typechecking top-level
declarations to know the method's true name. So they're left anonymous
until typecheck.
UseClosure does relatively little work today, but it serves as a
useful spot to check that the code setting up closures got it right.
It may also eventually serve as an optimization point for early
lifting of trivial closures, which may or may not ultimately be
beneficial.
Change-Id: I7da1e93c70d268f575b12d6aaeb2336eb910a6f1
Reviewed-on: https://go-review.googlesource.com/c/go/+/327051
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-06-11 03:09:26 -07:00
|
|
|
}
|
2022-03-24 12:27:39 -07:00
|
|
|
|
|
|
|
|
// IsFuncPCIntrinsic returns whether n is a direct call of internal/abi.FuncPCABIxxx functions.
|
|
|
|
|
func IsFuncPCIntrinsic(n *CallExpr) bool {
|
2023-10-04 15:22:49 -07:00
|
|
|
if n.Op() != OCALLFUNC || n.Fun.Op() != ONAME {
|
2022-03-24 12:27:39 -07:00
|
|
|
return false
|
|
|
|
|
}
|
2023-10-04 15:22:49 -07:00
|
|
|
fn := n.Fun.(*Name).Sym()
|
2022-03-24 12:27:39 -07:00
|
|
|
return (fn.Name == "FuncPCABI0" || fn.Name == "FuncPCABIInternal") &&
|
|
|
|
|
fn.Pkg.Path == "internal/abi"
|
|
|
|
|
}
|
2023-09-13 19:26:32 -07:00
|
|
|
|
2023-11-03 16:00:40 -04:00
|
|
|
// IsIfaceOfFunc inspects whether n is an interface conversion from a direct
|
|
|
|
|
// reference of a func. If so, it returns referenced Func; otherwise nil.
|
|
|
|
|
//
|
|
|
|
|
// This is only usable before walk.walkConvertInterface, which converts to an
|
|
|
|
|
// OMAKEFACE.
|
|
|
|
|
func IsIfaceOfFunc(n Node) *Func {
|
|
|
|
|
if n, ok := n.(*ConvExpr); ok && n.Op() == OCONVIFACE {
|
|
|
|
|
if name, ok := n.X.(*Name); ok && name.Op() == ONAME && name.Class == PFUNC {
|
|
|
|
|
return name.Func
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// FuncPC returns a uintptr-typed expression that evaluates to the PC of a
|
|
|
|
|
// function as uintptr, as returned by internal/abi.FuncPC{ABI0,ABIInternal}.
|
|
|
|
|
//
|
|
|
|
|
// n should be a Node of an interface type, as is passed to
|
|
|
|
|
// internal/abi.FuncPC{ABI0,ABIInternal}.
|
|
|
|
|
//
|
|
|
|
|
// TODO(prattmic): Since n is simply an interface{} there is no assertion that
|
|
|
|
|
// it is actually a function at all. Perhaps we should emit a runtime type
|
|
|
|
|
// assertion?
|
|
|
|
|
func FuncPC(pos src.XPos, n Node, wantABI obj.ABI) Node {
|
|
|
|
|
if !n.Type().IsInterface() {
|
|
|
|
|
base.ErrorfAt(pos, 0, "internal/abi.FuncPC%s expects an interface value, got %v", wantABI, n.Type())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if fn := IsIfaceOfFunc(n); fn != nil {
|
|
|
|
|
name := fn.Nname
|
|
|
|
|
abi := fn.ABI
|
|
|
|
|
if abi != wantABI {
|
|
|
|
|
base.ErrorfAt(pos, 0, "internal/abi.FuncPC%s expects an %v function, %s is defined as %v", wantABI, wantABI, name.Sym().Name, abi)
|
|
|
|
|
}
|
2024-03-04 16:44:43 +00:00
|
|
|
var e Node = NewLinksymExpr(pos, name.LinksymABI(abi), types.Types[types.TUINTPTR])
|
2023-11-03 16:00:40 -04:00
|
|
|
e = NewAddrExpr(pos, e)
|
|
|
|
|
e.SetType(types.Types[types.TUINTPTR].PtrTo())
|
|
|
|
|
e = NewConvExpr(pos, OCONVNOP, types.Types[types.TUINTPTR], e)
|
|
|
|
|
e.SetTypecheck(1)
|
|
|
|
|
return e
|
|
|
|
|
}
|
|
|
|
|
// fn is not a defined function. It must be ABIInternal.
|
|
|
|
|
// Read the address from func value, i.e. *(*uintptr)(idata(fn)).
|
|
|
|
|
if wantABI != obj.ABIInternal {
|
|
|
|
|
base.ErrorfAt(pos, 0, "internal/abi.FuncPC%s does not accept func expression, which is ABIInternal", wantABI)
|
|
|
|
|
}
|
|
|
|
|
var e Node = NewUnaryExpr(pos, OIDATA, n)
|
|
|
|
|
e.SetType(types.Types[types.TUINTPTR].PtrTo())
|
|
|
|
|
e.SetTypecheck(1)
|
|
|
|
|
e = NewStarExpr(pos, e)
|
|
|
|
|
e.SetType(types.Types[types.TUINTPTR])
|
|
|
|
|
e.SetTypecheck(1)
|
|
|
|
|
return e
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-13 19:26:32 -07:00
|
|
|
// DeclareParams creates Names for all of the parameters in fn's
|
|
|
|
|
// signature and adds them to fn.Dcl.
|
|
|
|
|
//
|
|
|
|
|
// If setNname is true, then it also sets types.Field.Nname for each
|
|
|
|
|
// parameter.
|
|
|
|
|
func (fn *Func) DeclareParams(setNname bool) {
|
|
|
|
|
if fn.Dcl != nil {
|
|
|
|
|
base.FatalfAt(fn.Pos(), "%v already has Dcl", fn)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
declareParams := func(params []*types.Field, ctxt Class, prefix string, offset int) {
|
|
|
|
|
for i, param := range params {
|
|
|
|
|
sym := param.Sym
|
|
|
|
|
if sym == nil || sym.IsBlank() {
|
|
|
|
|
sym = fn.Sym().Pkg.LookupNum(prefix, i)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
name := NewNameAt(param.Pos, sym, param.Type)
|
|
|
|
|
name.Class = ctxt
|
|
|
|
|
name.Curfn = fn
|
|
|
|
|
fn.Dcl[offset+i] = name
|
|
|
|
|
|
|
|
|
|
if setNname {
|
|
|
|
|
param.Nname = name
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sig := fn.Type()
|
|
|
|
|
params := sig.RecvParams()
|
|
|
|
|
results := sig.Results()
|
|
|
|
|
|
|
|
|
|
fn.Dcl = make([]*Name, len(params)+len(results))
|
|
|
|
|
declareParams(params, PPARAM, "~p", 0)
|
|
|
|
|
declareParams(results, PPARAMOUT, "~r", len(params))
|
|
|
|
|
}
|