mirror of
https://github.com/golang/go
synced 2024-10-04 23:20:17 +00:00
Merge branch 'master' into feature/sync-atomic-and-or-s390x
This commit is contained in:
commit
20dea110c8
|
@ -21,3 +21,6 @@ warning output from the go api tool. Each file should be named
|
|||
nnnnn.txt, after the issue number for the accepted proposal.
|
||||
(The #nnnnn suffix must also appear at the end of each line in the file;
|
||||
that will be preserved when next/*.txt is concatenated into go1.XX.txt.)
|
||||
|
||||
When you add a file to the api/next directory, you must add at least one file
|
||||
under doc/next. See doc/README.md for details.
|
||||
|
|
1
api/next/42888.txt
Normal file
1
api/next/42888.txt
Normal file
|
@ -0,0 +1 @@
|
|||
pkg runtime/debug, func SetCrashOutput(*os.File) error #42888
|
1
api/next/61696.txt
Normal file
1
api/next/61696.txt
Normal file
|
@ -0,0 +1 @@
|
|||
pkg sync, method (*Map) Clear() #61696
|
1013
doc/go1.22.html
1013
doc/go1.22.html
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,6 @@
|
|||
<!--{
|
||||
"Title": "The Go Programming Language Specification",
|
||||
"Subtitle": "Version of Dec 27, 2023",
|
||||
"Subtitle": "Language version go1.22 (Feb 6, 2024)",
|
||||
"Path": "/ref/spec"
|
||||
}-->
|
||||
|
||||
|
@ -6661,7 +6661,7 @@ array or slice a [n]E, *[n]E, or []E index i int a[i] E
|
|||
string s string type index i int see below rune
|
||||
map m map[K]V key k K m[k] V
|
||||
channel c chan E, <-chan E element e E
|
||||
integer n integer type I value i I
|
||||
integer n integer type value i see below
|
||||
</pre>
|
||||
|
||||
<ol>
|
||||
|
@ -6703,26 +6703,33 @@ is <code>nil</code>, the range expression blocks forever.
|
|||
|
||||
<li>
|
||||
For an integer value <code>n</code>, the iteration values 0 through <code>n-1</code>
|
||||
are produced in increasing order, with the same type as <code>n</code>.
|
||||
are produced in increasing order.
|
||||
If <code>n</code> <= 0, the loop does not run any iterations.
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<p>
|
||||
The iteration values are assigned to the respective
|
||||
iteration variables as in an <a href="#Assignment_statements">assignment statement</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The iteration variables may be declared by the "range" clause using a form of
|
||||
<a href="#Short_variable_declarations">short variable declaration</a>
|
||||
(<code>:=</code>).
|
||||
In this case their types are set to the types of the respective iteration values
|
||||
and their <a href="#Declarations_and_scope">scope</a> is the block of the "for" statement;
|
||||
each iteration has its own separate variables [<a href="#Go_1.22">Go 1.22</a>]
|
||||
In this case their <a href="#Declarations_and_scope">scope</a> is the block of the "for" statement
|
||||
and each iteration has its own new variables [<a href="#Go_1.22">Go 1.22</a>]
|
||||
(see also <a href="#For_clause">"for" statements with a ForClause</a>).
|
||||
If the iteration variables are declared outside the “for” statement,
|
||||
after execution their values will be those of the last iteration.
|
||||
If the range expression is a (possibly untyped) integer expression <code>n</code>,
|
||||
the variable has the same type as if it was
|
||||
<a href="#Variable_declarations">declared</a> with initialization
|
||||
expression <code>n</code>.
|
||||
Otherwise, the variables have the types of their respective iteration values.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If the iteration variables are not explicitly declared by the "range" clause,
|
||||
they must be preexisting.
|
||||
In this case, the iteration values are assigned to the respective variables
|
||||
as in an <a href="#Assignment_statements">assignment statement</a>.
|
||||
If the range expression is a (possibly untyped) integer expression <code>n</code>,
|
||||
<code>n</code> too must be <a href="#Assignability">assignable</a> to the iteration variable;
|
||||
if there is no iteration variable, <code>n</code> must be assignable to <code>int</code>.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
|
@ -6765,6 +6772,11 @@ for i := range 10 {
|
|||
// type of i is int (default type for untyped constant 10)
|
||||
f(i)
|
||||
}
|
||||
|
||||
// invalid: 256 cannot be assigned to uint8
|
||||
var u uint8
|
||||
for u = range 256 {
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
|
|
8
doc/next/6-stdlib/99-minor/runtime/debug/42888.md
Normal file
8
doc/next/6-stdlib/99-minor/runtime/debug/42888.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
|
||||
The [`debug.SetCrashOutput`](/runtime#SetCrashOutput) function allows
|
||||
the user to specify an alternate file to which the runtime should
|
||||
write its fatal crash report
|
||||
([#42888](https://github.com/golang/go/issues/42888)).
|
||||
It may be used to construct an automated reporting mechanism for all
|
||||
unexpected crashes, not just those in goroutines that explicitly use
|
||||
`recover`.
|
4
doc/next/6-stdlib/99-minor/sync/61696.md
Normal file
4
doc/next/6-stdlib/99-minor/sync/61696.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
The [`(*sync.Map) Clear()`](//sync#Map.Clear) method deletes
|
||||
all the entries, resulting in an empty map
|
||||
([#61696](https://github.com/golang/go/issues/61696)).
|
||||
It is analogous to `clear`.
|
|
@ -1,2 +1,8 @@
|
|||
## Ports {#ports}
|
||||
|
||||
### Darwin {#darwin}
|
||||
|
||||
<!-- go.dev/issue/64207 -->
|
||||
As [announced](go1.22#darwin) in the Go 1.22 release notes,
|
||||
Go 1.23 requires macOS 11 Big Sur or later;
|
||||
support for previous versions has been discontinued.
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
# in the CL match the update.bash in the CL.
|
||||
|
||||
# Versions to use.
|
||||
CODE=2023d
|
||||
DATA=2023d
|
||||
CODE=2024a
|
||||
DATA=2024a
|
||||
|
||||
set -e
|
||||
|
||||
|
|
Binary file not shown.
|
@ -433,6 +433,10 @@ func writeHeader(w io.Writer, h *header) error {
|
|||
// [Writer.CreateHeader], [Writer.CreateRaw], or [Writer.Close].
|
||||
//
|
||||
// In contrast to [Writer.CreateHeader], the bytes passed to Writer are not compressed.
|
||||
//
|
||||
// CreateRaw's argument is stored in w. If the argument is a pointer to the embedded
|
||||
// [FileHeader] in a [File] obtained from a [Reader] created from in-memory data,
|
||||
// then w will refer to all of that memory.
|
||||
func (w *Writer) CreateRaw(fh *FileHeader) (io.Writer, error) {
|
||||
if err := w.prepare(fh); err != nil {
|
||||
return nil, err
|
||||
|
@ -471,7 +475,10 @@ func (w *Writer) Copy(f *File) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fw, err := w.CreateRaw(&f.FileHeader)
|
||||
// Copy the FileHeader so w doesn't store a pointer to the data
|
||||
// of f's entire archive. See #65499.
|
||||
fh := f.FileHeader
|
||||
fw, err := w.CreateRaw(&fh)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -16,8 +16,10 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"internal/testenv"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
@ -266,12 +268,28 @@ func compilerSupportsLocation() bool {
|
|||
case "gcc":
|
||||
return compiler.major >= 10
|
||||
case "clang":
|
||||
// TODO(65606): The clang toolchain on the LUCI builders is not built against
|
||||
// zlib, the ASAN runtime can't actually symbolize its own stack trace. Once
|
||||
// this is resolved, one way or another, switch this back to 'true'. We still
|
||||
// have coverage from the 'gcc' case above.
|
||||
if inLUCIBuild() {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// inLUCIBuild returns true if we're currently executing in a LUCI build.
|
||||
func inLUCIBuild() bool {
|
||||
u, err := user.Current()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return testenv.Builder() != "" && u.Username == "swarming"
|
||||
}
|
||||
|
||||
// compilerRequiredTsanVersion reports whether the compiler is the version required by Tsan.
|
||||
// Only restrictions for ppc64le are known; otherwise return true.
|
||||
func compilerRequiredTsanVersion(goos, goarch string) bool {
|
||||
|
|
|
@ -124,7 +124,7 @@ type CmdFlags struct {
|
|||
TraceProfile string "help:\"write an execution trace to `file`\""
|
||||
TrimPath string "help:\"remove `prefix` from recorded source file paths\""
|
||||
WB bool "help:\"enable write barrier\"" // TODO: remove
|
||||
PgoProfile string "help:\"read profile from `file`\""
|
||||
PgoProfile string "help:\"read profile or pre-process profile from `file`\""
|
||||
ErrorURL bool "help:\"print explanatory URL with error message if applicable\""
|
||||
|
||||
// Configuration derived from flags; not a flag itself.
|
||||
|
|
|
@ -740,7 +740,7 @@ func findHotConcreteCallee(p *pgo.Profile, caller *ir.Func, call *ir.CallExpr, e
|
|||
}
|
||||
|
||||
if base.Debug.PGODebug >= 2 {
|
||||
fmt.Printf("%v call %s:%d: hottest callee %s (weight %d)\n", ir.Line(call), callerName, callOffset, hottest.Dst.Name(), hottest.Weight)
|
||||
fmt.Printf("%v: call %s:%d: hottest callee %s (weight %d)\n", ir.Line(call), callerName, callOffset, hottest.Dst.Name(), hottest.Weight)
|
||||
}
|
||||
return hottest.Dst.AST, hottest.Weight
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ var (
|
|||
)
|
||||
|
||||
// PGOInlinePrologue records the hot callsites from ir-graph.
|
||||
func PGOInlinePrologue(p *pgo.Profile, funcs []*ir.Func) {
|
||||
func PGOInlinePrologue(p *pgo.Profile) {
|
||||
if base.Debug.PGOInlineCDFThreshold != "" {
|
||||
if s, err := strconv.ParseFloat(base.Debug.PGOInlineCDFThreshold, 64); err == nil && s >= 0 && s <= 100 {
|
||||
inlineCDFHotCallSiteThresholdPercent = s
|
||||
|
@ -119,7 +119,7 @@ func PGOInlinePrologue(p *pgo.Profile, funcs []*ir.Func) {
|
|||
// a percent, is the lower bound of weight for nodes to be considered hot
|
||||
// (currently only used in debug prints) (in case of equal weights,
|
||||
// comparing with the threshold may not accurately reflect which nodes are
|
||||
// considiered hot).
|
||||
// considered hot).
|
||||
func hotNodesFromCDF(p *pgo.Profile) (float64, []pgo.NamedCallEdge) {
|
||||
cum := int64(0)
|
||||
for i, n := range p.NamedEdgeMap.ByWeight {
|
||||
|
@ -138,24 +138,14 @@ func hotNodesFromCDF(p *pgo.Profile) (float64, []pgo.NamedCallEdge) {
|
|||
// CanInlineFuncs computes whether a batch of functions are inlinable.
|
||||
func CanInlineFuncs(funcs []*ir.Func, profile *pgo.Profile) {
|
||||
if profile != nil {
|
||||
PGOInlinePrologue(profile, funcs)
|
||||
PGOInlinePrologue(profile)
|
||||
}
|
||||
|
||||
ir.VisitFuncsBottomUp(funcs, func(list []*ir.Func, recursive bool) {
|
||||
CanInlineSCC(list, recursive, profile)
|
||||
})
|
||||
}
|
||||
|
||||
// CanInlineSCC computes the inlinability of functions within an SCC
|
||||
// (strongly connected component).
|
||||
//
|
||||
// CanInlineSCC is designed to be used by ir.VisitFuncsBottomUp
|
||||
// callbacks.
|
||||
func CanInlineSCC(funcs []*ir.Func, recursive bool, profile *pgo.Profile) {
|
||||
if base.Flag.LowerL == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
ir.VisitFuncsBottomUp(funcs, func(funcs []*ir.Func, recursive bool) {
|
||||
numfns := numNonClosures(funcs)
|
||||
|
||||
for _, fn := range funcs {
|
||||
|
@ -173,6 +163,7 @@ func CanInlineSCC(funcs []*ir.Func, recursive bool, profile *pgo.Profile) {
|
|||
analyzeFuncProps(fn, profile)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// GarbageCollectUnreferencedHiddenClosures makes a pass over all the
|
||||
|
@ -227,7 +218,7 @@ func GarbageCollectUnreferencedHiddenClosures() {
|
|||
}
|
||||
|
||||
// inlineBudget determines the max budget for function 'fn' prior to
|
||||
// analyzing the hairyness of the body of 'fn'. We pass in the pgo
|
||||
// analyzing the hairiness of the body of 'fn'. We pass in the pgo
|
||||
// profile if available (which can change the budget), also a
|
||||
// 'relaxed' flag, which expands the budget slightly to allow for the
|
||||
// possibility that a call to the function might have its score
|
||||
|
@ -239,7 +230,7 @@ func inlineBudget(fn *ir.Func, profile *pgo.Profile, relaxed bool, verbose bool)
|
|||
if profile != nil {
|
||||
if n, ok := profile.WeightedCG.IRNodes[ir.LinkFuncName(fn)]; ok {
|
||||
if _, ok := candHotCalleeMap[n]; ok {
|
||||
budget = int32(inlineHotMaxBudget)
|
||||
budget = inlineHotMaxBudget
|
||||
if verbose {
|
||||
fmt.Printf("hot-node enabled increased budget=%v for func=%v\n", budget, ir.PkgFuncName(fn))
|
||||
}
|
||||
|
@ -325,7 +316,6 @@ func CanInline(fn *ir.Func, profile *pgo.Profile) {
|
|||
Cost: budget - visitor.budget,
|
||||
Dcl: pruneUnusedAutos(n.Func.Dcl, &visitor),
|
||||
HaveDcl: true,
|
||||
|
||||
CanDelayResults: canDelayResults(fn),
|
||||
}
|
||||
if base.Flag.LowerM != 0 || logopt.Enabled() {
|
||||
|
@ -919,7 +909,7 @@ func inlineCostOK(n *ir.CallExpr, caller, callee *ir.Func, bigCaller bool) (bool
|
|||
return true, 0, metric
|
||||
}
|
||||
|
||||
// canInlineCallsite returns true if the call n from caller to callee
|
||||
// canInlineCallExpr returns true if the call n from caller to callee
|
||||
// can be inlined, plus the score computed for the call expr in
|
||||
// question. bigCaller indicates that caller is a big function. log
|
||||
// indicates that the 'cannot inline' reason should be logged.
|
||||
|
|
|
@ -95,7 +95,7 @@ func AnalyzeFunc(fn *ir.Func, canInline func(*ir.Func), budgetForFunc func(*ir.F
|
|||
// only after the closures it contains have been processed, so
|
||||
// iterate through the list in reverse order. Once a function has
|
||||
// been analyzed, revisit the question of whether it should be
|
||||
// inlinable; if it is over the default hairyness limit and it
|
||||
// inlinable; if it is over the default hairiness limit and it
|
||||
// doesn't have any interesting properties, then we don't want
|
||||
// the overhead of writing out its inline body.
|
||||
nameFinder := newNameFinder(fn)
|
||||
|
|
|
@ -45,7 +45,7 @@ func addParamsAnalyzer(fn *ir.Func, analyzers []propAnalyzer, fp *FuncProps, nf
|
|||
return analyzers
|
||||
}
|
||||
|
||||
// makeParamAnalyzer creates a new helper object to analyze parameters
|
||||
// makeParamsAnalyzer creates a new helper object to analyze parameters
|
||||
// of function fn. If the function doesn't have any interesting
|
||||
// params, a nil helper is returned along with a set of default param
|
||||
// flags for the func.
|
||||
|
|
|
@ -590,7 +590,7 @@ func GetCallSiteScore(fn *ir.Func, call *ir.CallExpr) (int, bool) {
|
|||
|
||||
// BudgetExpansion returns the amount to relax/expand the base
|
||||
// inlining budget when the new inliner is turned on; the inliner
|
||||
// will add the returned value to the hairyness budget.
|
||||
// will add the returned value to the hairiness budget.
|
||||
//
|
||||
// Background: with the new inliner, the score for a given callsite
|
||||
// can be adjusted down by some amount due to heuristics, however we
|
||||
|
@ -617,7 +617,7 @@ var allCallSites CallSiteTab
|
|||
// along with info on call site scoring and the adjustments made to a
|
||||
// given score. Here profile is the PGO profile in use (may be
|
||||
// nil), budgetCallback is a callback that can be invoked to find out
|
||||
// the original pre-adjustment hairyness limit for the function, and
|
||||
// the original pre-adjustment hairiness limit for the function, and
|
||||
// inlineHotMaxBudget is the constant of the same name used in the
|
||||
// inliner. Sample output lines:
|
||||
//
|
||||
|
@ -629,7 +629,7 @@ var allCallSites CallSiteTab
|
|||
//
|
||||
// In the dump above, "Score" is the final score calculated for the
|
||||
// callsite, "Adjustment" is the amount added to or subtracted from
|
||||
// the original hairyness estimate to form the score. "Status" shows
|
||||
// the original hairiness estimate to form the score. "Status" shows
|
||||
// whether anything changed with the site -- did the adjustment bump
|
||||
// it down just below the threshold ("PROMOTED") or instead bump it
|
||||
// above the threshold ("DEMOTED"); this will be blank ("---") if no
|
||||
|
|
|
@ -38,26 +38,15 @@ func DevirtualizeAndInlinePackage(pkg *ir.Package, profile *pgo.Profile) {
|
|||
if base.Debug.PGOInline != 0 {
|
||||
inlProfile = profile
|
||||
}
|
||||
if inlProfile != nil {
|
||||
inline.PGOInlinePrologue(inlProfile, pkg.Funcs)
|
||||
}
|
||||
|
||||
ir.VisitFuncsBottomUp(pkg.Funcs, func(funcs []*ir.Func, recursive bool) {
|
||||
// We visit functions within an SCC in fairly arbitrary order,
|
||||
// so by computing inlinability for all functions in the SCC
|
||||
// before performing any inlining, the results are less
|
||||
// sensitive to the order within the SCC (see #58905 for an
|
||||
// example).
|
||||
// First compute inlinability of all functions in the package.
|
||||
inline.CanInlineFuncs(pkg.Funcs, inlProfile)
|
||||
|
||||
// First compute inlinability for all functions in the SCC ...
|
||||
inline.CanInlineSCC(funcs, recursive, inlProfile)
|
||||
|
||||
// ... then make a second pass to do devirtualization and inlining
|
||||
// of calls.
|
||||
for _, fn := range funcs {
|
||||
// Now we make a second pass to do devirtualization and inlining of
|
||||
// calls. Order here should not matter.
|
||||
for _, fn := range pkg.Funcs {
|
||||
DevirtualizeAndInlineFunc(fn, inlProfile)
|
||||
}
|
||||
})
|
||||
|
||||
if base.Flag.LowerL != 0 {
|
||||
// Perform a garbage collection of hidden closures functions that
|
||||
|
|
|
@ -22,7 +22,7 @@ func checkStaticValueResult(n Node, newres Node) {
|
|||
}
|
||||
}
|
||||
|
||||
// checkStaticValueResult compares the result from ReassignOracle.Reassigned
|
||||
// checkReassignedResult compares the result from ReassignOracle.Reassigned
|
||||
// with the corresponding result from ir.Reassigned to make sure they agree.
|
||||
// This method is called only when turned on via build tag.
|
||||
func checkReassignedResult(n *Name, newres bool) {
|
||||
|
|
|
@ -663,9 +663,24 @@ func (pr *pkgReader) objInstIdx(info objInfo, dict *readerDict, shaped bool) ir.
|
|||
}
|
||||
|
||||
// objIdx returns the specified object, instantiated with the given
|
||||
// type arguments, if any. If shaped is true, then the shaped variant
|
||||
// of the object is returned instead.
|
||||
// type arguments, if any.
|
||||
// If shaped is true, then the shaped variant of the object is returned
|
||||
// instead.
|
||||
func (pr *pkgReader) objIdx(idx pkgbits.Index, implicits, explicits []*types.Type, shaped bool) ir.Node {
|
||||
n, err := pr.objIdxMayFail(idx, implicits, explicits, shaped)
|
||||
if err != nil {
|
||||
base.Fatalf("%v", err)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// objIdxMayFail is equivalent to objIdx, but returns an error rather than
|
||||
// failing the build if this object requires type arguments and the incorrect
|
||||
// number of type arguments were passed.
|
||||
//
|
||||
// Other sources of internal failure (such as duplicate definitions) still fail
|
||||
// the build.
|
||||
func (pr *pkgReader) objIdxMayFail(idx pkgbits.Index, implicits, explicits []*types.Type, shaped bool) (ir.Node, error) {
|
||||
rname := pr.newReader(pkgbits.RelocName, idx, pkgbits.SyncObject1)
|
||||
_, sym := rname.qualifiedIdent()
|
||||
tag := pkgbits.CodeObj(rname.Code(pkgbits.SyncCodeObj))
|
||||
|
@ -674,22 +689,25 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index, implicits, explicits []*types.Typ
|
|||
assert(!sym.IsBlank())
|
||||
switch sym.Pkg {
|
||||
case types.BuiltinPkg, types.UnsafePkg:
|
||||
return sym.Def.(ir.Node)
|
||||
return sym.Def.(ir.Node), nil
|
||||
}
|
||||
if pri, ok := objReader[sym]; ok {
|
||||
return pri.pr.objIdx(pri.idx, nil, explicits, shaped)
|
||||
return pri.pr.objIdxMayFail(pri.idx, nil, explicits, shaped)
|
||||
}
|
||||
if sym.Pkg.Path == "runtime" {
|
||||
return typecheck.LookupRuntime(sym.Name)
|
||||
return typecheck.LookupRuntime(sym.Name), nil
|
||||
}
|
||||
base.Fatalf("unresolved stub: %v", sym)
|
||||
}
|
||||
|
||||
dict := pr.objDictIdx(sym, idx, implicits, explicits, shaped)
|
||||
dict, err := pr.objDictIdx(sym, idx, implicits, explicits, shaped)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sym = dict.baseSym
|
||||
if !sym.IsBlank() && sym.Def != nil {
|
||||
return sym.Def.(*ir.Name)
|
||||
return sym.Def.(*ir.Name), nil
|
||||
}
|
||||
|
||||
r := pr.newReader(pkgbits.RelocObj, idx, pkgbits.SyncObject1)
|
||||
|
@ -725,7 +743,7 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index, implicits, explicits []*types.Typ
|
|||
name := do(ir.OTYPE, false)
|
||||
setType(name, r.typ())
|
||||
name.SetAlias(true)
|
||||
return name
|
||||
return name, nil
|
||||
|
||||
case pkgbits.ObjConst:
|
||||
name := do(ir.OLITERAL, false)
|
||||
|
@ -733,7 +751,7 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index, implicits, explicits []*types.Typ
|
|||
val := FixValue(typ, r.Value())
|
||||
setType(name, typ)
|
||||
setValue(name, val)
|
||||
return name
|
||||
return name, nil
|
||||
|
||||
case pkgbits.ObjFunc:
|
||||
if sym.Name == "init" {
|
||||
|
@ -768,7 +786,7 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index, implicits, explicits []*types.Typ
|
|||
}
|
||||
|
||||
rext.funcExt(name, nil)
|
||||
return name
|
||||
return name, nil
|
||||
|
||||
case pkgbits.ObjType:
|
||||
name := do(ir.OTYPE, true)
|
||||
|
@ -805,13 +823,13 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index, implicits, explicits []*types.Typ
|
|||
r.needWrapper(typ)
|
||||
}
|
||||
|
||||
return name
|
||||
return name, nil
|
||||
|
||||
case pkgbits.ObjVar:
|
||||
name := do(ir.ONAME, false)
|
||||
setType(name, r.typ())
|
||||
rext.varExt(name)
|
||||
return name
|
||||
return name, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -908,7 +926,7 @@ func shapify(targ *types.Type, basic bool) *types.Type {
|
|||
}
|
||||
|
||||
// objDictIdx reads and returns the specified object dictionary.
|
||||
func (pr *pkgReader) objDictIdx(sym *types.Sym, idx pkgbits.Index, implicits, explicits []*types.Type, shaped bool) *readerDict {
|
||||
func (pr *pkgReader) objDictIdx(sym *types.Sym, idx pkgbits.Index, implicits, explicits []*types.Type, shaped bool) (*readerDict, error) {
|
||||
r := pr.newReader(pkgbits.RelocObjDict, idx, pkgbits.SyncObject1)
|
||||
|
||||
dict := readerDict{
|
||||
|
@ -919,7 +937,7 @@ func (pr *pkgReader) objDictIdx(sym *types.Sym, idx pkgbits.Index, implicits, ex
|
|||
nexplicits := r.Len()
|
||||
|
||||
if nimplicits > len(implicits) || nexplicits != len(explicits) {
|
||||
base.Fatalf("%v has %v+%v params, but instantiated with %v+%v args", sym, nimplicits, nexplicits, len(implicits), len(explicits))
|
||||
return nil, fmt.Errorf("%v has %v+%v params, but instantiated with %v+%v args", sym, nimplicits, nexplicits, len(implicits), len(explicits))
|
||||
}
|
||||
|
||||
dict.targs = append(implicits[:nimplicits:nimplicits], explicits...)
|
||||
|
@ -984,7 +1002,7 @@ func (pr *pkgReader) objDictIdx(sym *types.Sym, idx pkgbits.Index, implicits, ex
|
|||
dict.itabs[i] = itabInfo{typ: r.typInfo(), iface: r.typInfo()}
|
||||
}
|
||||
|
||||
return &dict
|
||||
return &dict, nil
|
||||
}
|
||||
|
||||
func (r *reader) typeParamNames() {
|
||||
|
@ -2529,7 +2547,10 @@ func (pr *pkgReader) objDictName(idx pkgbits.Index, implicits, explicits []*type
|
|||
base.Fatalf("unresolved stub: %v", sym)
|
||||
}
|
||||
|
||||
dict := pr.objDictIdx(sym, idx, implicits, explicits, false)
|
||||
dict, err := pr.objDictIdx(sym, idx, implicits, explicits, false)
|
||||
if err != nil {
|
||||
base.Fatalf("%v", err)
|
||||
}
|
||||
|
||||
return pr.dictNameOf(dict)
|
||||
}
|
||||
|
|
|
@ -80,7 +80,11 @@ func lookupFunction(pkg *types.Pkg, symName string) (*ir.Func, error) {
|
|||
return nil, fmt.Errorf("func sym %v missing objReader", sym)
|
||||
}
|
||||
|
||||
name := pri.pr.objIdx(pri.idx, nil, nil, false).(*ir.Name)
|
||||
node, err := pri.pr.objIdxMayFail(pri.idx, nil, nil, false)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("func sym %v lookup error: %w", sym, err)
|
||||
}
|
||||
name := node.(*ir.Name)
|
||||
if name.Op() != ir.ONAME || name.Class != ir.PFUNC {
|
||||
return nil, fmt.Errorf("func sym %v refers to non-function name: %v", sym, name)
|
||||
}
|
||||
|
@ -105,7 +109,11 @@ func lookupMethod(pkg *types.Pkg, symName string) (*ir.Func, error) {
|
|||
return nil, fmt.Errorf("type sym %v missing objReader", typ)
|
||||
}
|
||||
|
||||
name := pri.pr.objIdx(pri.idx, nil, nil, false).(*ir.Name)
|
||||
node, err := pri.pr.objIdxMayFail(pri.idx, nil, nil, false)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("func sym %v lookup error: %w", typ, err)
|
||||
}
|
||||
name := node.(*ir.Name)
|
||||
if name.Op() != ir.OTYPE {
|
||||
return nil, fmt.Errorf("type sym %v refers to non-type name: %v", typ, name)
|
||||
}
|
||||
|
|
|
@ -41,16 +41,19 @@
|
|||
package pgo
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"cmd/compile/internal/base"
|
||||
"cmd/compile/internal/ir"
|
||||
"cmd/compile/internal/pgo/internal/graph"
|
||||
"cmd/compile/internal/typecheck"
|
||||
"cmd/compile/internal/types"
|
||||
"errors"
|
||||
"fmt"
|
||||
"internal/profile"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// IRGraph is a call graph with nodes pointing to IRs of functions and edges
|
||||
|
@ -139,14 +142,54 @@ type Profile struct {
|
|||
WeightedCG *IRGraph
|
||||
}
|
||||
|
||||
// New generates a profile-graph from the profile.
|
||||
var wantHdr = "GO PREPROFILE V1\n"
|
||||
|
||||
func isPreProfileFile(r *bufio.Reader) (bool, error) {
|
||||
hdr, err := r.Peek(len(wantHdr))
|
||||
if err == io.EOF {
|
||||
// Empty file.
|
||||
return false, nil
|
||||
} else if err != nil {
|
||||
return false, fmt.Errorf("error reading profile header: %w", err)
|
||||
}
|
||||
|
||||
return string(hdr) == wantHdr, nil
|
||||
}
|
||||
|
||||
// New generates a profile-graph from the profile or pre-processed profile.
|
||||
func New(profileFile string) (*Profile, error) {
|
||||
f, err := os.Open(profileFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error opening profile: %w", err)
|
||||
}
|
||||
defer f.Close()
|
||||
p, err := profile.Parse(f)
|
||||
|
||||
r := bufio.NewReader(f)
|
||||
|
||||
isPreProf, err := isPreProfileFile(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error processing profile header: %w", err)
|
||||
}
|
||||
|
||||
if isPreProf {
|
||||
profile, err := processPreprof(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error processing preprocessed PGO profile: %w", err)
|
||||
}
|
||||
return profile, nil
|
||||
}
|
||||
|
||||
profile, err := processProto(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error processing pprof PGO profile: %w", err)
|
||||
}
|
||||
return profile, nil
|
||||
|
||||
}
|
||||
|
||||
// processProto generates a profile-graph from the profile.
|
||||
func processProto(r io.Reader) (*Profile, error) {
|
||||
p, err := profile.Parse(r)
|
||||
if errors.Is(err, profile.ErrNoData) {
|
||||
// Treat a completely empty file the same as a profile with no
|
||||
// samples: nothing to do.
|
||||
|
@ -175,7 +218,7 @@ func New(profileFile string) (*Profile, error) {
|
|||
return nil, fmt.Errorf(`profile does not contain a sample index with value/type "samples/count" or cpu/nanoseconds"`)
|
||||
}
|
||||
|
||||
g := graph.NewGraph(p, &graph.Options{
|
||||
g := profile.NewGraph(p, &profile.Options{
|
||||
SampleValue: func(v []int64) int64 { return v[valueIndex] },
|
||||
})
|
||||
|
||||
|
@ -198,45 +241,31 @@ func New(profileFile string) (*Profile, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
// createNamedEdgeMap builds a map of callsite-callee edge weights from the
|
||||
// profile-graph.
|
||||
//
|
||||
// Caller should ignore the profile if totalWeight == 0.
|
||||
func createNamedEdgeMap(g *graph.Graph) (edgeMap NamedEdgeMap, totalWeight int64, err error) {
|
||||
seenStartLine := false
|
||||
|
||||
// Process graph and build various node and edge maps which will
|
||||
// be consumed by AST walk.
|
||||
weight := make(map[NamedCallEdge]int64)
|
||||
for _, n := range g.Nodes {
|
||||
seenStartLine = seenStartLine || n.Info.StartLine != 0
|
||||
|
||||
canonicalName := n.Info.Name
|
||||
// Create the key to the nodeMapKey.
|
||||
namedEdge := NamedCallEdge{
|
||||
CallerName: canonicalName,
|
||||
CallSiteOffset: n.Info.Lineno - n.Info.StartLine,
|
||||
}
|
||||
|
||||
for _, e := range n.Out {
|
||||
totalWeight += e.WeightValue()
|
||||
namedEdge.CalleeName = e.Dest.Info.Name
|
||||
// Create new entry or increment existing entry.
|
||||
weight[namedEdge] += e.WeightValue()
|
||||
}
|
||||
// processPreprof generates a profile-graph from the pre-procesed profile.
|
||||
func processPreprof(r io.Reader) (*Profile, error) {
|
||||
namedEdgeMap, totalWeight, err := createNamedEdgeMapFromPreprocess(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if totalWeight == 0 {
|
||||
return nil, nil // accept but ignore profile with no samples.
|
||||
}
|
||||
|
||||
// Create package-level call graph with weights from profile and IR.
|
||||
wg := createIRGraph(namedEdgeMap)
|
||||
|
||||
return &Profile{
|
||||
TotalWeight: totalWeight,
|
||||
NamedEdgeMap: namedEdgeMap,
|
||||
WeightedCG: wg,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func postProcessNamedEdgeMap(weight map[NamedCallEdge]int64, weightVal int64) (edgeMap NamedEdgeMap, totalWeight int64, err error) {
|
||||
if weightVal == 0 {
|
||||
return NamedEdgeMap{}, 0, nil // accept but ignore profile with no samples.
|
||||
}
|
||||
|
||||
if !seenStartLine {
|
||||
// TODO(prattmic): If Function.start_line is missing we could
|
||||
// fall back to using absolute line numbers, which is better
|
||||
// than nothing.
|
||||
return NamedEdgeMap{}, 0, fmt.Errorf("profile missing Function.start_line data (Go version of profiled application too old? Go 1.20+ automatically adds this to profiles)")
|
||||
}
|
||||
|
||||
byWeight := make([]NamedCallEdge, 0, len(weight))
|
||||
for namedEdge := range weight {
|
||||
byWeight = append(byWeight, namedEdge)
|
||||
|
@ -261,9 +290,110 @@ func createNamedEdgeMap(g *graph.Graph) (edgeMap NamedEdgeMap, totalWeight int64
|
|||
ByWeight: byWeight,
|
||||
}
|
||||
|
||||
totalWeight = weightVal
|
||||
|
||||
return edgeMap, totalWeight, nil
|
||||
}
|
||||
|
||||
// restore NodeMap information from a preprocessed profile.
|
||||
// The reader can refer to the format of preprocessed profile in cmd/preprofile/main.go.
|
||||
func createNamedEdgeMapFromPreprocess(r io.Reader) (edgeMap NamedEdgeMap, totalWeight int64, err error) {
|
||||
fileScanner := bufio.NewScanner(r)
|
||||
fileScanner.Split(bufio.ScanLines)
|
||||
weight := make(map[NamedCallEdge]int64)
|
||||
|
||||
if !fileScanner.Scan() {
|
||||
if err := fileScanner.Err(); err != nil {
|
||||
return NamedEdgeMap{}, 0, fmt.Errorf("error reading preprocessed profile: %w", err)
|
||||
}
|
||||
return NamedEdgeMap{}, 0, fmt.Errorf("preprocessed profile missing header")
|
||||
}
|
||||
if gotHdr := fileScanner.Text() + "\n"; gotHdr != wantHdr {
|
||||
return NamedEdgeMap{}, 0, fmt.Errorf("preprocessed profile malformed header; got %q want %q", gotHdr, wantHdr)
|
||||
}
|
||||
|
||||
for fileScanner.Scan() {
|
||||
readStr := fileScanner.Text()
|
||||
|
||||
callerName := readStr
|
||||
|
||||
if !fileScanner.Scan() {
|
||||
if err := fileScanner.Err(); err != nil {
|
||||
return NamedEdgeMap{}, 0, fmt.Errorf("error reading preprocessed profile: %w", err)
|
||||
}
|
||||
return NamedEdgeMap{}, 0, fmt.Errorf("preprocessed profile entry missing callee")
|
||||
}
|
||||
calleeName := fileScanner.Text()
|
||||
|
||||
if !fileScanner.Scan() {
|
||||
if err := fileScanner.Err(); err != nil {
|
||||
return NamedEdgeMap{}, 0, fmt.Errorf("error reading preprocessed profile: %w", err)
|
||||
}
|
||||
return NamedEdgeMap{}, 0, fmt.Errorf("preprocessed profile entry missing weight")
|
||||
}
|
||||
readStr = fileScanner.Text()
|
||||
|
||||
split := strings.Split(readStr, " ")
|
||||
|
||||
if len(split) != 2 {
|
||||
return NamedEdgeMap{}, 0, fmt.Errorf("preprocessed profile entry got %v want 2 fields", split)
|
||||
}
|
||||
|
||||
co, _ := strconv.Atoi(split[0])
|
||||
|
||||
namedEdge := NamedCallEdge{
|
||||
CallerName: callerName,
|
||||
CalleeName: calleeName,
|
||||
CallSiteOffset: co,
|
||||
}
|
||||
|
||||
EWeight, _ := strconv.ParseInt(split[1], 10, 64)
|
||||
|
||||
weight[namedEdge] += EWeight
|
||||
totalWeight += EWeight
|
||||
}
|
||||
|
||||
return postProcessNamedEdgeMap(weight, totalWeight)
|
||||
|
||||
}
|
||||
|
||||
// createNamedEdgeMap builds a map of callsite-callee edge weights from the
|
||||
// profile-graph.
|
||||
//
|
||||
// Caller should ignore the profile if totalWeight == 0.
|
||||
func createNamedEdgeMap(g *profile.Graph) (edgeMap NamedEdgeMap, totalWeight int64, err error) {
|
||||
seenStartLine := false
|
||||
|
||||
// Process graph and build various node and edge maps which will
|
||||
// be consumed by AST walk.
|
||||
weight := make(map[NamedCallEdge]int64)
|
||||
for _, n := range g.Nodes {
|
||||
seenStartLine = seenStartLine || n.Info.StartLine != 0
|
||||
|
||||
canonicalName := n.Info.Name
|
||||
// Create the key to the nodeMapKey.
|
||||
namedEdge := NamedCallEdge{
|
||||
CallerName: canonicalName,
|
||||
CallSiteOffset: n.Info.Lineno - n.Info.StartLine,
|
||||
}
|
||||
|
||||
for _, e := range n.Out {
|
||||
totalWeight += e.WeightValue()
|
||||
namedEdge.CalleeName = e.Dest.Info.Name
|
||||
// Create new entry or increment existing entry.
|
||||
weight[namedEdge] += e.WeightValue()
|
||||
}
|
||||
}
|
||||
|
||||
if !seenStartLine {
|
||||
// TODO(prattmic): If Function.start_line is missing we could
|
||||
// fall back to using absolute line numbers, which is better
|
||||
// than nothing.
|
||||
return NamedEdgeMap{}, 0, fmt.Errorf("profile missing Function.start_line data (Go version of profiled application too old? Go 1.20+ automatically adds this to profiles)")
|
||||
}
|
||||
return postProcessNamedEdgeMap(weight, totalWeight)
|
||||
}
|
||||
|
||||
// initializeIRGraph builds the IRGraph by visiting all the ir.Func in decl list
|
||||
// of a package.
|
||||
func createIRGraph(namedEdgeMap NamedEdgeMap) *IRGraph {
|
||||
|
|
|
@ -1331,20 +1331,25 @@ func writeITab(lsym *obj.LSym, typ, iface *types.Type, allowNonImplement bool) {
|
|||
// _ [4]byte
|
||||
// fun [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter.
|
||||
// }
|
||||
o := objw.SymPtr(lsym, 0, writeType(iface), 0)
|
||||
o = objw.SymPtr(lsym, o, writeType(typ), 0)
|
||||
o = objw.Uint32(lsym, o, types.TypeHash(typ)) // copy of type hash
|
||||
o += 4 // skip unused field
|
||||
c := rttype.NewCursor(lsym, 0, rttype.ITab)
|
||||
c.Field("Inter").WritePtr(writeType(iface))
|
||||
c.Field("Type").WritePtr(writeType(typ))
|
||||
c.Field("Hash").WriteUint32(types.TypeHash(typ)) // copy of type hash
|
||||
|
||||
var delta int64
|
||||
c = c.Field("Fun")
|
||||
if !completeItab {
|
||||
// If typ doesn't implement iface, make method entries be zero.
|
||||
o = objw.Uintptr(lsym, o, 0)
|
||||
entries = entries[:0]
|
||||
c.Elem(0).WriteUintptr(0)
|
||||
} else {
|
||||
var a rttype.ArrayCursor
|
||||
a, delta = c.ModifyArray(len(entries))
|
||||
for i, fn := range entries {
|
||||
a.Elem(i).WritePtrWeak(fn) // method pointer for each method
|
||||
}
|
||||
for _, fn := range entries {
|
||||
o = objw.SymPtrWeak(lsym, o, fn, 0) // method pointer for each method
|
||||
}
|
||||
// Nothing writes static itabs, so they are read only.
|
||||
objw.Global(lsym, int32(o), int16(obj.DUPOK|obj.RODATA))
|
||||
objw.Global(lsym, int32(rttype.ITab.Size()+delta), int16(obj.DUPOK|obj.RODATA))
|
||||
lsym.Set(obj.AttrContentAddressable, true)
|
||||
}
|
||||
|
||||
|
|
|
@ -278,7 +278,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
|
|||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = rd
|
||||
case ssa.OpRISCV64ADD, ssa.OpRISCV64SUB, ssa.OpRISCV64SUBW, ssa.OpRISCV64XOR, ssa.OpRISCV64OR, ssa.OpRISCV64AND,
|
||||
ssa.OpRISCV64SLL, ssa.OpRISCV64SRA, ssa.OpRISCV64SRAW, ssa.OpRISCV64SRL, ssa.OpRISCV64SRLW,
|
||||
ssa.OpRISCV64SLL, ssa.OpRISCV64SLLW, ssa.OpRISCV64SRA, ssa.OpRISCV64SRAW, ssa.OpRISCV64SRL, ssa.OpRISCV64SRLW,
|
||||
ssa.OpRISCV64SLT, ssa.OpRISCV64SLTU, ssa.OpRISCV64MUL, ssa.OpRISCV64MULW, ssa.OpRISCV64MULH,
|
||||
ssa.OpRISCV64MULHU, ssa.OpRISCV64DIV, ssa.OpRISCV64DIVU, ssa.OpRISCV64DIVW,
|
||||
ssa.OpRISCV64DIVUW, ssa.OpRISCV64REM, ssa.OpRISCV64REMU, ssa.OpRISCV64REMW,
|
||||
|
@ -422,8 +422,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
|
|||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = v.Reg()
|
||||
case ssa.OpRISCV64ADDI, ssa.OpRISCV64ADDIW, ssa.OpRISCV64XORI, ssa.OpRISCV64ORI, ssa.OpRISCV64ANDI,
|
||||
ssa.OpRISCV64SLLI, ssa.OpRISCV64SRAI, ssa.OpRISCV64SRAIW, ssa.OpRISCV64SRLI, ssa.OpRISCV64SRLIW, ssa.OpRISCV64SLTI,
|
||||
ssa.OpRISCV64SLTIU:
|
||||
ssa.OpRISCV64SLLI, ssa.OpRISCV64SLLIW, ssa.OpRISCV64SRAI, ssa.OpRISCV64SRAIW,
|
||||
ssa.OpRISCV64SRLI, ssa.OpRISCV64SRLIW, ssa.OpRISCV64SLTI, ssa.OpRISCV64SLTIU:
|
||||
p := s.Prog(v.Op.Asm())
|
||||
p.From.Type = obj.TYPE_CONST
|
||||
p.From.Offset = v.AuxInt
|
||||
|
|
|
@ -42,6 +42,9 @@ var UncommonType *types.Type
|
|||
var InterfaceSwitch *types.Type
|
||||
var TypeAssert *types.Type
|
||||
|
||||
// Interface tables (itabs)
|
||||
var ITab *types.Type
|
||||
|
||||
func Init() {
|
||||
// Note: this has to be called explicitly instead of being
|
||||
// an init function so it runs after the types package has
|
||||
|
@ -64,6 +67,8 @@ func Init() {
|
|||
InterfaceSwitch = fromReflect(reflect.TypeOf(abi.InterfaceSwitch{}))
|
||||
TypeAssert = fromReflect(reflect.TypeOf(abi.TypeAssert{}))
|
||||
|
||||
ITab = fromReflect(reflect.TypeOf(abi.ITab{}))
|
||||
|
||||
// Make sure abi functions are correct. These functions are used
|
||||
// by the linker which doesn't have the ability to do type layout,
|
||||
// so we check the functions it uses here.
|
||||
|
@ -80,6 +85,9 @@ func Init() {
|
|||
if got, want := int64(abi.TFlagOff(ptrSize)), Type.OffsetOf("TFlag"); got != want {
|
||||
base.Fatalf("abi.TFlagOff() == %d, want %d", got, want)
|
||||
}
|
||||
if got, want := int64(abi.ITabTypeOff(ptrSize)), ITab.OffsetOf("Type"); got != want {
|
||||
base.Fatalf("abi.ITabTypeOff() == %d, want %d", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// fromReflect translates from a host type to the equivalent target type.
|
||||
|
@ -154,6 +162,12 @@ func (c Cursor) WritePtr(target *obj.LSym) {
|
|||
objw.SymPtr(c.lsym, int(c.offset), target, 0)
|
||||
}
|
||||
}
|
||||
func (c Cursor) WritePtrWeak(target *obj.LSym) {
|
||||
if c.typ.Kind() != types.TUINTPTR {
|
||||
base.Fatalf("can't write ptr, it has kind %s", c.typ.Kind())
|
||||
}
|
||||
objw.SymPtrWeak(c.lsym, int(c.offset), target, 0)
|
||||
}
|
||||
func (c Cursor) WriteUintptr(val uint64) {
|
||||
if c.typ.Kind() != types.TUINTPTR {
|
||||
base.Fatalf("can't write uintptr, it has kind %s", c.typ.Kind())
|
||||
|
@ -250,6 +264,17 @@ func (c Cursor) Field(name string) Cursor {
|
|||
return Cursor{}
|
||||
}
|
||||
|
||||
func (c Cursor) Elem(i int64) Cursor {
|
||||
if c.typ.Kind() != types.TARRAY {
|
||||
base.Fatalf("can't call Elem on non-array %v", c.typ)
|
||||
}
|
||||
if i < 0 || i >= c.typ.NumElem() {
|
||||
base.Fatalf("element access out of bounds [%d] in [0:%d]", i, c.typ.NumElem())
|
||||
}
|
||||
elem := c.typ.Elem()
|
||||
return Cursor{lsym: c.lsym, offset: c.offset + i*elem.Size(), typ: elem}
|
||||
}
|
||||
|
||||
type ArrayCursor struct {
|
||||
c Cursor // cursor pointing at first element
|
||||
n int // number of elements
|
||||
|
|
|
@ -214,10 +214,10 @@
|
|||
(Rsh64x(64|32|16|8) x y) && shiftIsBounded(v) => (SRA x y)
|
||||
|
||||
// Rotates.
|
||||
(RotateLeft8 <t> x (MOVDconst [c])) => (Or8 (Lsh8x64 <t> x (MOVDconst [c&7])) (Rsh8Ux64 <t> x (MOVDconst [-c&7])))
|
||||
(RotateLeft16 <t> x (MOVDconst [c])) => (Or16 (Lsh16x64 <t> x (MOVDconst [c&15])) (Rsh16Ux64 <t> x (MOVDconst [-c&15])))
|
||||
(RotateLeft32 <t> x (MOVDconst [c])) => (Or32 (Lsh32x64 <t> x (MOVDconst [c&31])) (Rsh32Ux64 <t> x (MOVDconst [-c&31])))
|
||||
(RotateLeft64 <t> x (MOVDconst [c])) => (Or64 (Lsh64x64 <t> x (MOVDconst [c&63])) (Rsh64Ux64 <t> x (MOVDconst [-c&63])))
|
||||
(RotateLeft8 <t> x y) => (OR (SLL <t> x (ANDI [7] <y.Type> y)) (SRL <t> (ZeroExt8to64 x) (ANDI [7] <y.Type> (NEG <y.Type> y))))
|
||||
(RotateLeft16 <t> x y) => (OR (SLL <t> x (ANDI [15] <y.Type> y)) (SRL <t> (ZeroExt16to64 x) (ANDI [15] <y.Type> (NEG <y.Type> y))))
|
||||
(RotateLeft32 <t> x y) => (OR (SLLW <t> x y) (SRLW <t> x (NEG <y.Type> y)))
|
||||
(RotateLeft64 <t> x y) => (OR (SLL <t> x y) (SRL <t> x (NEG <y.Type> y)))
|
||||
|
||||
(Less64 ...) => (SLT ...)
|
||||
(Less32 x y) => (SLT (SignExt32to64 x) (SignExt32to64 y))
|
||||
|
@ -733,6 +733,7 @@
|
|||
(XOR (MOVDconst [val]) x) && is32Bit(val) => (XORI [val] x)
|
||||
(SLL x (MOVDconst [val])) => (SLLI [int64(val&63)] x)
|
||||
(SRL x (MOVDconst [val])) => (SRLI [int64(val&63)] x)
|
||||
(SLLW x (MOVDconst [val])) => (SLLIW [int64(val&31)] x)
|
||||
(SRLW x (MOVDconst [val])) => (SRLIW [int64(val&31)] x)
|
||||
(SRA x (MOVDconst [val])) => (SRAI [int64(val&63)] x)
|
||||
(SRAW x (MOVDconst [val])) => (SRAIW [int64(val&31)] x)
|
||||
|
|
|
@ -207,16 +207,18 @@ func init() {
|
|||
{name: "MOVDnop", argLength: 1, reg: regInfo{inputs: []regMask{gpMask}, outputs: []regMask{gpMask}}, resultInArg0: true}, // nop, return arg0 in same register
|
||||
|
||||
// Shift ops
|
||||
{name: "SLL", argLength: 2, reg: gp21, asm: "SLL"}, // arg0 << (aux1 & 63)
|
||||
{name: "SRA", argLength: 2, reg: gp21, asm: "SRA"}, // arg0 >> (aux1 & 63), signed
|
||||
{name: "SRAW", argLength: 2, reg: gp21, asm: "SRAW"}, // arg0 >> (aux1 & 31), signed
|
||||
{name: "SRL", argLength: 2, reg: gp21, asm: "SRL"}, // arg0 >> (aux1 & 63), unsigned
|
||||
{name: "SRLW", argLength: 2, reg: gp21, asm: "SRLW"}, // arg0 >> (aux1 & 31), unsigned
|
||||
{name: "SLLI", argLength: 1, reg: gp11, asm: "SLLI", aux: "Int64"}, // arg0 << auxint, shift amount 0-63
|
||||
{name: "SRAI", argLength: 1, reg: gp11, asm: "SRAI", aux: "Int64"}, // arg0 >> auxint, signed, shift amount 0-63
|
||||
{name: "SRAIW", argLength: 1, reg: gp11, asm: "SRAIW", aux: "Int64"}, // arg0 >> auxint, signed, shift amount 0-31
|
||||
{name: "SRLI", argLength: 1, reg: gp11, asm: "SRLI", aux: "Int64"}, // arg0 >> auxint, unsigned, shift amount 0-63
|
||||
{name: "SRLIW", argLength: 1, reg: gp11, asm: "SRLIW", aux: "Int64"}, // arg0 >> auxint, unsigned, shift amount 0-31
|
||||
{name: "SLL", argLength: 2, reg: gp21, asm: "SLL"}, // arg0 << (aux1 & 63), logical left shift
|
||||
{name: "SLLW", argLength: 2, reg: gp21, asm: "SLLW"}, // arg0 << (aux1 & 31), logical left shift of 32 bit value, sign extended to 64 bits
|
||||
{name: "SRA", argLength: 2, reg: gp21, asm: "SRA"}, // arg0 >> (aux1 & 63), arithmetic right shift
|
||||
{name: "SRAW", argLength: 2, reg: gp21, asm: "SRAW"}, // arg0 >> (aux1 & 31), arithmetic right shift of 32 bit value, sign extended to 64 bits
|
||||
{name: "SRL", argLength: 2, reg: gp21, asm: "SRL"}, // arg0 >> (aux1 & 63), logical right shift
|
||||
{name: "SRLW", argLength: 2, reg: gp21, asm: "SRLW"}, // arg0 >> (aux1 & 31), logical right shift of 32 bit value, sign extended to 64 bits
|
||||
{name: "SLLI", argLength: 1, reg: gp11, asm: "SLLI", aux: "Int64"}, // arg0 << auxint, shift amount 0-63, logical left shift
|
||||
{name: "SLLIW", argLength: 1, reg: gp11, asm: "SLLIW", aux: "Int64"}, // arg0 << auxint, shift amount 0-31, logical left shift of 32 bit value, sign extended to 64 bits
|
||||
{name: "SRAI", argLength: 1, reg: gp11, asm: "SRAI", aux: "Int64"}, // arg0 >> auxint, shift amount 0-63, arithmetic right shift
|
||||
{name: "SRAIW", argLength: 1, reg: gp11, asm: "SRAIW", aux: "Int64"}, // arg0 >> auxint, shift amount 0-31, arithmetic right shift of 32 bit value, sign extended to 64 bits
|
||||
{name: "SRLI", argLength: 1, reg: gp11, asm: "SRLI", aux: "Int64"}, // arg0 >> auxint, shift amount 0-63, logical right shift
|
||||
{name: "SRLIW", argLength: 1, reg: gp11, asm: "SRLIW", aux: "Int64"}, // arg0 >> auxint, shift amount 0-31, logical right shift of 32 bit value, sign extended to 64 bits
|
||||
|
||||
// Bitwise ops
|
||||
{name: "XOR", argLength: 2, reg: gp21, asm: "XOR", commutative: true}, // arg0 ^ arg1
|
||||
|
|
|
@ -2388,11 +2388,13 @@ const (
|
|||
OpRISCV64MOVWUreg
|
||||
OpRISCV64MOVDnop
|
||||
OpRISCV64SLL
|
||||
OpRISCV64SLLW
|
||||
OpRISCV64SRA
|
||||
OpRISCV64SRAW
|
||||
OpRISCV64SRL
|
||||
OpRISCV64SRLW
|
||||
OpRISCV64SLLI
|
||||
OpRISCV64SLLIW
|
||||
OpRISCV64SRAI
|
||||
OpRISCV64SRAIW
|
||||
OpRISCV64SRLI
|
||||
|
@ -32045,6 +32047,20 @@ var opcodeTable = [...]opInfo{
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "SLLW",
|
||||
argLen: 2,
|
||||
asm: riscv.ASLLW,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
{0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
|
||||
{1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
|
||||
},
|
||||
outputs: []outputInfo{
|
||||
{0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "SRA",
|
||||
argLen: 2,
|
||||
|
@ -32115,6 +32131,20 @@ var opcodeTable = [...]opInfo{
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "SLLIW",
|
||||
auxType: auxInt64,
|
||||
argLen: 1,
|
||||
asm: riscv.ASLLIW,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
{0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
|
||||
},
|
||||
outputs: []outputInfo{
|
||||
{0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "SRAI",
|
||||
auxType: auxInt64,
|
||||
|
|
|
@ -2144,7 +2144,7 @@ func canRotate(c *Config, bits int64) bool {
|
|||
return false
|
||||
}
|
||||
switch c.arch {
|
||||
case "386", "amd64", "arm64":
|
||||
case "386", "amd64", "arm64", "riscv64":
|
||||
return true
|
||||
case "arm", "s390x", "ppc64", "ppc64le", "wasm", "loong64":
|
||||
return bits >= 32
|
||||
|
|
|
@ -536,6 +536,8 @@ func rewriteValueRISCV64(v *Value) bool {
|
|||
return rewriteValueRISCV64_OpRISCV64SLL(v)
|
||||
case OpRISCV64SLLI:
|
||||
return rewriteValueRISCV64_OpRISCV64SLLI(v)
|
||||
case OpRISCV64SLLW:
|
||||
return rewriteValueRISCV64_OpRISCV64SLLW(v)
|
||||
case OpRISCV64SLT:
|
||||
return rewriteValueRISCV64_OpRISCV64SLT(v)
|
||||
case OpRISCV64SLTI:
|
||||
|
@ -6070,6 +6072,24 @@ func rewriteValueRISCV64_OpRISCV64SLLI(v *Value) bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
func rewriteValueRISCV64_OpRISCV64SLLW(v *Value) bool {
|
||||
v_1 := v.Args[1]
|
||||
v_0 := v.Args[0]
|
||||
// match: (SLLW x (MOVDconst [val]))
|
||||
// result: (SLLIW [int64(val&31)] x)
|
||||
for {
|
||||
x := v_0
|
||||
if v_1.Op != OpRISCV64MOVDconst {
|
||||
break
|
||||
}
|
||||
val := auxIntToInt64(v_1.AuxInt)
|
||||
v.reset(OpRISCV64SLLIW)
|
||||
v.AuxInt = int64ToAuxInt(int64(val & 31))
|
||||
v.AddArg(x)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
func rewriteValueRISCV64_OpRISCV64SLT(v *Value) bool {
|
||||
v_1 := v.Args[1]
|
||||
v_0 := v.Args[0]
|
||||
|
@ -6644,112 +6664,102 @@ func rewriteValueRISCV64_OpRotateLeft16(v *Value) bool {
|
|||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
typ := &b.Func.Config.Types
|
||||
// match: (RotateLeft16 <t> x (MOVDconst [c]))
|
||||
// result: (Or16 (Lsh16x64 <t> x (MOVDconst [c&15])) (Rsh16Ux64 <t> x (MOVDconst [-c&15])))
|
||||
// match: (RotateLeft16 <t> x y)
|
||||
// result: (OR (SLL <t> x (ANDI [15] <y.Type> y)) (SRL <t> (ZeroExt16to64 x) (ANDI [15] <y.Type> (NEG <y.Type> y))))
|
||||
for {
|
||||
t := v.Type
|
||||
x := v_0
|
||||
if v_1.Op != OpRISCV64MOVDconst {
|
||||
break
|
||||
}
|
||||
c := auxIntToInt64(v_1.AuxInt)
|
||||
v.reset(OpOr16)
|
||||
v0 := b.NewValue0(v.Pos, OpLsh16x64, t)
|
||||
v1 := b.NewValue0(v.Pos, OpRISCV64MOVDconst, typ.UInt64)
|
||||
v1.AuxInt = int64ToAuxInt(c & 15)
|
||||
y := v_1
|
||||
v.reset(OpRISCV64OR)
|
||||
v0 := b.NewValue0(v.Pos, OpRISCV64SLL, t)
|
||||
v1 := b.NewValue0(v.Pos, OpRISCV64ANDI, y.Type)
|
||||
v1.AuxInt = int64ToAuxInt(15)
|
||||
v1.AddArg(y)
|
||||
v0.AddArg2(x, v1)
|
||||
v2 := b.NewValue0(v.Pos, OpRsh16Ux64, t)
|
||||
v3 := b.NewValue0(v.Pos, OpRISCV64MOVDconst, typ.UInt64)
|
||||
v3.AuxInt = int64ToAuxInt(-c & 15)
|
||||
v2.AddArg2(x, v3)
|
||||
v2 := b.NewValue0(v.Pos, OpRISCV64SRL, t)
|
||||
v3 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64)
|
||||
v3.AddArg(x)
|
||||
v4 := b.NewValue0(v.Pos, OpRISCV64ANDI, y.Type)
|
||||
v4.AuxInt = int64ToAuxInt(15)
|
||||
v5 := b.NewValue0(v.Pos, OpRISCV64NEG, y.Type)
|
||||
v5.AddArg(y)
|
||||
v4.AddArg(v5)
|
||||
v2.AddArg2(v3, v4)
|
||||
v.AddArg2(v0, v2)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
func rewriteValueRISCV64_OpRotateLeft32(v *Value) bool {
|
||||
v_1 := v.Args[1]
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
typ := &b.Func.Config.Types
|
||||
// match: (RotateLeft32 <t> x (MOVDconst [c]))
|
||||
// result: (Or32 (Lsh32x64 <t> x (MOVDconst [c&31])) (Rsh32Ux64 <t> x (MOVDconst [-c&31])))
|
||||
// match: (RotateLeft32 <t> x y)
|
||||
// result: (OR (SLLW <t> x y) (SRLW <t> x (NEG <y.Type> y)))
|
||||
for {
|
||||
t := v.Type
|
||||
x := v_0
|
||||
if v_1.Op != OpRISCV64MOVDconst {
|
||||
break
|
||||
}
|
||||
c := auxIntToInt64(v_1.AuxInt)
|
||||
v.reset(OpOr32)
|
||||
v0 := b.NewValue0(v.Pos, OpLsh32x64, t)
|
||||
v1 := b.NewValue0(v.Pos, OpRISCV64MOVDconst, typ.UInt64)
|
||||
v1.AuxInt = int64ToAuxInt(c & 31)
|
||||
v0.AddArg2(x, v1)
|
||||
v2 := b.NewValue0(v.Pos, OpRsh32Ux64, t)
|
||||
v3 := b.NewValue0(v.Pos, OpRISCV64MOVDconst, typ.UInt64)
|
||||
v3.AuxInt = int64ToAuxInt(-c & 31)
|
||||
v2.AddArg2(x, v3)
|
||||
v.AddArg2(v0, v2)
|
||||
y := v_1
|
||||
v.reset(OpRISCV64OR)
|
||||
v0 := b.NewValue0(v.Pos, OpRISCV64SLLW, t)
|
||||
v0.AddArg2(x, y)
|
||||
v1 := b.NewValue0(v.Pos, OpRISCV64SRLW, t)
|
||||
v2 := b.NewValue0(v.Pos, OpRISCV64NEG, y.Type)
|
||||
v2.AddArg(y)
|
||||
v1.AddArg2(x, v2)
|
||||
v.AddArg2(v0, v1)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
func rewriteValueRISCV64_OpRotateLeft64(v *Value) bool {
|
||||
v_1 := v.Args[1]
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
typ := &b.Func.Config.Types
|
||||
// match: (RotateLeft64 <t> x (MOVDconst [c]))
|
||||
// result: (Or64 (Lsh64x64 <t> x (MOVDconst [c&63])) (Rsh64Ux64 <t> x (MOVDconst [-c&63])))
|
||||
// match: (RotateLeft64 <t> x y)
|
||||
// result: (OR (SLL <t> x y) (SRL <t> x (NEG <y.Type> y)))
|
||||
for {
|
||||
t := v.Type
|
||||
x := v_0
|
||||
if v_1.Op != OpRISCV64MOVDconst {
|
||||
break
|
||||
}
|
||||
c := auxIntToInt64(v_1.AuxInt)
|
||||
v.reset(OpOr64)
|
||||
v0 := b.NewValue0(v.Pos, OpLsh64x64, t)
|
||||
v1 := b.NewValue0(v.Pos, OpRISCV64MOVDconst, typ.UInt64)
|
||||
v1.AuxInt = int64ToAuxInt(c & 63)
|
||||
v0.AddArg2(x, v1)
|
||||
v2 := b.NewValue0(v.Pos, OpRsh64Ux64, t)
|
||||
v3 := b.NewValue0(v.Pos, OpRISCV64MOVDconst, typ.UInt64)
|
||||
v3.AuxInt = int64ToAuxInt(-c & 63)
|
||||
v2.AddArg2(x, v3)
|
||||
v.AddArg2(v0, v2)
|
||||
y := v_1
|
||||
v.reset(OpRISCV64OR)
|
||||
v0 := b.NewValue0(v.Pos, OpRISCV64SLL, t)
|
||||
v0.AddArg2(x, y)
|
||||
v1 := b.NewValue0(v.Pos, OpRISCV64SRL, t)
|
||||
v2 := b.NewValue0(v.Pos, OpRISCV64NEG, y.Type)
|
||||
v2.AddArg(y)
|
||||
v1.AddArg2(x, v2)
|
||||
v.AddArg2(v0, v1)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
func rewriteValueRISCV64_OpRotateLeft8(v *Value) bool {
|
||||
v_1 := v.Args[1]
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
typ := &b.Func.Config.Types
|
||||
// match: (RotateLeft8 <t> x (MOVDconst [c]))
|
||||
// result: (Or8 (Lsh8x64 <t> x (MOVDconst [c&7])) (Rsh8Ux64 <t> x (MOVDconst [-c&7])))
|
||||
// match: (RotateLeft8 <t> x y)
|
||||
// result: (OR (SLL <t> x (ANDI [7] <y.Type> y)) (SRL <t> (ZeroExt8to64 x) (ANDI [7] <y.Type> (NEG <y.Type> y))))
|
||||
for {
|
||||
t := v.Type
|
||||
x := v_0
|
||||
if v_1.Op != OpRISCV64MOVDconst {
|
||||
break
|
||||
}
|
||||
c := auxIntToInt64(v_1.AuxInt)
|
||||
v.reset(OpOr8)
|
||||
v0 := b.NewValue0(v.Pos, OpLsh8x64, t)
|
||||
v1 := b.NewValue0(v.Pos, OpRISCV64MOVDconst, typ.UInt64)
|
||||
v1.AuxInt = int64ToAuxInt(c & 7)
|
||||
y := v_1
|
||||
v.reset(OpRISCV64OR)
|
||||
v0 := b.NewValue0(v.Pos, OpRISCV64SLL, t)
|
||||
v1 := b.NewValue0(v.Pos, OpRISCV64ANDI, y.Type)
|
||||
v1.AuxInt = int64ToAuxInt(7)
|
||||
v1.AddArg(y)
|
||||
v0.AddArg2(x, v1)
|
||||
v2 := b.NewValue0(v.Pos, OpRsh8Ux64, t)
|
||||
v3 := b.NewValue0(v.Pos, OpRISCV64MOVDconst, typ.UInt64)
|
||||
v3.AuxInt = int64ToAuxInt(-c & 7)
|
||||
v2.AddArg2(x, v3)
|
||||
v2 := b.NewValue0(v.Pos, OpRISCV64SRL, t)
|
||||
v3 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64)
|
||||
v3.AddArg(x)
|
||||
v4 := b.NewValue0(v.Pos, OpRISCV64ANDI, y.Type)
|
||||
v4.AuxInt = int64ToAuxInt(7)
|
||||
v5 := b.NewValue0(v.Pos, OpRISCV64NEG, y.Type)
|
||||
v5.AddArg(y)
|
||||
v4.AddArg(v5)
|
||||
v2.AddArg2(v3, v4)
|
||||
v.AddArg2(v0, v2)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
func rewriteValueRISCV64_OpRsh16Ux16(v *Value) bool {
|
||||
v_1 := v.Args[1]
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"cmd/compile/internal/liveness"
|
||||
"cmd/compile/internal/objw"
|
||||
"cmd/compile/internal/reflectdata"
|
||||
"cmd/compile/internal/rttype"
|
||||
"cmd/compile/internal/ssa"
|
||||
"cmd/compile/internal/staticdata"
|
||||
"cmd/compile/internal/typecheck"
|
||||
|
@ -4894,22 +4895,22 @@ func InitTables() {
|
|||
func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
|
||||
return s.newValue2(ssa.OpRotateLeft8, types.Types[types.TUINT8], args[0], args[1])
|
||||
},
|
||||
sys.AMD64)
|
||||
sys.AMD64, sys.RISCV64)
|
||||
addF("math/bits", "RotateLeft16",
|
||||
func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
|
||||
return s.newValue2(ssa.OpRotateLeft16, types.Types[types.TUINT16], args[0], args[1])
|
||||
},
|
||||
sys.AMD64)
|
||||
sys.AMD64, sys.RISCV64)
|
||||
addF("math/bits", "RotateLeft32",
|
||||
func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
|
||||
return s.newValue2(ssa.OpRotateLeft32, types.Types[types.TUINT32], args[0], args[1])
|
||||
},
|
||||
sys.AMD64, sys.ARM, sys.ARM64, sys.S390X, sys.PPC64, sys.Wasm, sys.Loong64)
|
||||
sys.AMD64, sys.ARM, sys.ARM64, sys.Loong64, sys.PPC64, sys.RISCV64, sys.S390X, sys.Wasm)
|
||||
addF("math/bits", "RotateLeft64",
|
||||
func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
|
||||
return s.newValue2(ssa.OpRotateLeft64, types.Types[types.TUINT64], args[0], args[1])
|
||||
},
|
||||
sys.AMD64, sys.ARM64, sys.S390X, sys.PPC64, sys.Wasm, sys.Loong64)
|
||||
sys.AMD64, sys.ARM64, sys.Loong64, sys.PPC64, sys.RISCV64, sys.S390X, sys.Wasm)
|
||||
alias("math/bits", "RotateLeft", "math/bits", "RotateLeft64", p8...)
|
||||
|
||||
makeOnesCountAMD64 := func(op ssa.Op) func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
|
||||
|
@ -5537,7 +5538,7 @@ func (s *state) getClosureAndRcvr(fn *ir.SelectorExpr) (*ssa.Value, *ssa.Value)
|
|||
i := s.expr(fn.X)
|
||||
itab := s.newValue1(ssa.OpITab, types.Types[types.TUINTPTR], i)
|
||||
s.nilCheck(itab)
|
||||
itabidx := fn.Offset() + 2*int64(types.PtrSize) + 8 // offset of fun field in runtime.itab
|
||||
itabidx := fn.Offset() + rttype.ITab.OffsetOf("Fun")
|
||||
closure := s.newValue1I(ssa.OpOffPtr, s.f.Config.Types.UintptrPtr, itabidx, itab)
|
||||
rcvr := s.newValue1(ssa.OpIData, s.f.Config.Types.BytePtr, i)
|
||||
return closure, rcvr
|
||||
|
@ -6522,7 +6523,7 @@ func (s *state) dynamicDottype(n *ir.DynamicTypeAssertExpr, commaok bool) (res,
|
|||
targetItab = s.expr(n.ITab)
|
||||
// TODO(mdempsky): Investigate whether compiling n.RType could be
|
||||
// better than loading itab.typ.
|
||||
target = s.load(byteptr, s.newValue1I(ssa.OpOffPtr, byteptr, int64(types.PtrSize), targetItab)) // itab.typ
|
||||
target = s.load(byteptr, s.newValue1I(ssa.OpOffPtr, byteptr, rttype.ITab.OffsetOf("Type"), targetItab))
|
||||
} else {
|
||||
target = s.expr(n.RType)
|
||||
}
|
||||
|
@ -6580,7 +6581,7 @@ func (s *state) dottype1(pos src.XPos, src, dst *types.Type, iface, source, targ
|
|||
return
|
||||
}
|
||||
// Load type out of itab, build interface with existing idata.
|
||||
off := s.newValue1I(ssa.OpOffPtr, byteptr, int64(types.PtrSize), itab)
|
||||
off := s.newValue1I(ssa.OpOffPtr, byteptr, rttype.ITab.OffsetOf("Type"), itab)
|
||||
typ := s.load(byteptr, off)
|
||||
idata := s.newValue1(ssa.OpIData, byteptr, iface)
|
||||
res = s.newValue2(ssa.OpIMake, dst, typ, idata)
|
||||
|
@ -6590,7 +6591,7 @@ func (s *state) dottype1(pos src.XPos, src, dst *types.Type, iface, source, targ
|
|||
s.startBlock(bOk)
|
||||
// nonempty -> empty
|
||||
// Need to load type from itab
|
||||
off := s.newValue1I(ssa.OpOffPtr, byteptr, int64(types.PtrSize), itab)
|
||||
off := s.newValue1I(ssa.OpOffPtr, byteptr, rttype.ITab.OffsetOf("Type"), itab)
|
||||
s.vars[typVar] = s.load(byteptr, off)
|
||||
s.endBlock()
|
||||
|
||||
|
@ -6644,7 +6645,7 @@ func (s *state) dottype1(pos src.XPos, src, dst *types.Type, iface, source, targ
|
|||
s.startBlock(bNonNil)
|
||||
typ := itab
|
||||
if !src.IsEmptyInterface() {
|
||||
typ = s.load(byteptr, s.newValue1I(ssa.OpOffPtr, byteptr, int64(types.PtrSize), itab))
|
||||
typ = s.load(byteptr, s.newValue1I(ssa.OpOffPtr, byteptr, rttype.ITab.OffsetOf("Type"), itab))
|
||||
}
|
||||
|
||||
// Check the cache first.
|
||||
|
@ -6685,9 +6686,9 @@ func (s *state) dottype1(pos src.XPos, src, dst *types.Type, iface, source, targ
|
|||
// Load hash from type or itab.
|
||||
var hash *ssa.Value
|
||||
if src.IsEmptyInterface() {
|
||||
hash = s.newValue2(ssa.OpLoad, typs.UInt32, s.newValue1I(ssa.OpOffPtr, typs.UInt32Ptr, 2*s.config.PtrSize, typ), s.mem())
|
||||
hash = s.newValue2(ssa.OpLoad, typs.UInt32, s.newValue1I(ssa.OpOffPtr, typs.UInt32Ptr, rttype.Type.OffsetOf("Hash"), typ), s.mem())
|
||||
} else {
|
||||
hash = s.newValue2(ssa.OpLoad, typs.UInt32, s.newValue1I(ssa.OpOffPtr, typs.UInt32Ptr, 2*s.config.PtrSize, itab), s.mem())
|
||||
hash = s.newValue2(ssa.OpLoad, typs.UInt32, s.newValue1I(ssa.OpOffPtr, typs.UInt32Ptr, rttype.ITab.OffsetOf("Hash"), itab), s.mem())
|
||||
}
|
||||
hash = s.newValue1(zext, typs.Uintptr, hash)
|
||||
s.vars[hashVar] = hash
|
||||
|
@ -7380,7 +7381,7 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
|
|||
if b.Pos == src.NoXPos {
|
||||
b.Pos = p.Pos // It needs a file, otherwise a no-file non-zero line causes confusion. See #35652.
|
||||
if b.Pos == src.NoXPos {
|
||||
b.Pos = pp.Text.Pos // Sometimes p.Pos is empty. See #35695.
|
||||
b.Pos = s.pp.Text.Pos // Sometimes p.Pos is empty. See #35695.
|
||||
}
|
||||
}
|
||||
b.Pos = b.Pos.WithBogusLine() // Debuggers are not good about infinite loops, force a change in line number
|
||||
|
@ -7415,14 +7416,14 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
|
|||
// still be inside the function in question. So if
|
||||
// it ends in a call which doesn't return, add a
|
||||
// nop (which will never execute) after the call.
|
||||
Arch.Ginsnop(pp)
|
||||
Arch.Ginsnop(s.pp)
|
||||
}
|
||||
if openDeferInfo != nil {
|
||||
// When doing open-coded defers, generate a disconnected call to
|
||||
// deferreturn and a return. This will be used to during panic
|
||||
// recovery to unwind the stack and return back to the runtime.
|
||||
s.pp.NextLive = s.livenessMap.DeferReturn
|
||||
p := pp.Prog(obj.ACALL)
|
||||
p := s.pp.Prog(obj.ACALL)
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Name = obj.NAME_EXTERN
|
||||
p.To.Sym = ir.Syms.Deferreturn
|
||||
|
@ -7439,7 +7440,7 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
|
|||
}
|
||||
}
|
||||
|
||||
pp.Prog(obj.ARET)
|
||||
s.pp.Prog(obj.ARET)
|
||||
}
|
||||
|
||||
if inlMarks != nil {
|
||||
|
@ -7448,7 +7449,7 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
|
|||
// We have some inline marks. Try to find other instructions we're
|
||||
// going to emit anyway, and use those instructions instead of the
|
||||
// inline marks.
|
||||
for p := pp.Text; p != nil; p = p.Link {
|
||||
for p := s.pp.Text; p != nil; p = p.Link {
|
||||
if p.As == obj.ANOP || p.As == obj.AFUNCDATA || p.As == obj.APCDATA || p.As == obj.ATEXT || p.As == obj.APCALIGN || Arch.LinkArch.Family == sys.Wasm {
|
||||
// Don't use 0-sized instructions as inline marks, because we need
|
||||
// to identify inline mark instructions by pc offset.
|
||||
|
@ -7466,16 +7467,16 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
|
|||
hasCall = true
|
||||
}
|
||||
pos := p.Pos.AtColumn1()
|
||||
s := inlMarksByPos[pos]
|
||||
if len(s) == 0 {
|
||||
marks := inlMarksByPos[pos]
|
||||
if len(marks) == 0 {
|
||||
continue
|
||||
}
|
||||
for _, m := range s {
|
||||
for _, m := range marks {
|
||||
// We found an instruction with the same source position as
|
||||
// some of the inline marks.
|
||||
// Use this instruction instead.
|
||||
p.Pos = p.Pos.WithIsStmt() // promote position to a statement
|
||||
pp.CurFunc.LSym.Func().AddInlMark(p, inlMarks[m])
|
||||
s.pp.CurFunc.LSym.Func().AddInlMark(p, inlMarks[m])
|
||||
// Make the inline mark a real nop, so it doesn't generate any code.
|
||||
m.As = obj.ANOP
|
||||
m.Pos = src.NoXPos
|
||||
|
@ -7487,7 +7488,7 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
|
|||
// Any unmatched inline marks now need to be added to the inlining tree (and will generate a nop instruction).
|
||||
for _, p := range inlMarkList {
|
||||
if p.As != obj.ANOP {
|
||||
pp.CurFunc.LSym.Func().AddInlMark(p, inlMarks[p])
|
||||
s.pp.CurFunc.LSym.Func().AddInlMark(p, inlMarks[p])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7498,27 +7499,27 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
|
|||
// equal to the start of the function.
|
||||
// This ensures that runtime.FuncForPC(uintptr(reflect.ValueOf(fn).Pointer())).Name()
|
||||
// returns the right answer. See issue 58300.
|
||||
for p := pp.Text; p != nil; p = p.Link {
|
||||
for p := s.pp.Text; p != nil; p = p.Link {
|
||||
if p.As == obj.AFUNCDATA || p.As == obj.APCDATA || p.As == obj.ATEXT || p.As == obj.ANOP {
|
||||
continue
|
||||
}
|
||||
if base.Ctxt.PosTable.Pos(p.Pos).Base().InliningIndex() >= 0 {
|
||||
// Make a real (not 0-sized) nop.
|
||||
nop := Arch.Ginsnop(pp)
|
||||
nop := Arch.Ginsnop(s.pp)
|
||||
nop.Pos = e.curfn.Pos().WithIsStmt()
|
||||
|
||||
// Unfortunately, Ginsnop puts the instruction at the
|
||||
// end of the list. Move it up to just before p.
|
||||
|
||||
// Unlink from the current list.
|
||||
for x := pp.Text; x != nil; x = x.Link {
|
||||
for x := s.pp.Text; x != nil; x = x.Link {
|
||||
if x.Link == nop {
|
||||
x.Link = nop.Link
|
||||
break
|
||||
}
|
||||
}
|
||||
// Splice in right before p.
|
||||
for x := pp.Text; x != nil; x = x.Link {
|
||||
for x := s.pp.Text; x != nil; x = x.Link {
|
||||
if x.Link == p {
|
||||
nop.Link = p
|
||||
x.Link = nop
|
||||
|
@ -7588,13 +7589,13 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
|
|||
// Add to list of jump tables to be resolved at assembly time.
|
||||
// The assembler converts from *Prog entries to absolute addresses
|
||||
// once it knows instruction byte offsets.
|
||||
fi := pp.CurFunc.LSym.Func()
|
||||
fi := s.pp.CurFunc.LSym.Func()
|
||||
fi.JumpTables = append(fi.JumpTables, obj.JumpTable{Sym: jt.Aux.(*obj.LSym), Targets: targets})
|
||||
}
|
||||
|
||||
if e.log { // spew to stdout
|
||||
filename := ""
|
||||
for p := pp.Text; p != nil; p = p.Link {
|
||||
for p := s.pp.Text; p != nil; p = p.Link {
|
||||
if p.Pos.IsKnown() && p.InnermostFilename() != filename {
|
||||
filename = p.InnermostFilename()
|
||||
f.Logf("# %s\n", filename)
|
||||
|
@ -7616,7 +7617,7 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
|
|||
buf.WriteString("<code>")
|
||||
buf.WriteString("<dl class=\"ssa-gen\">")
|
||||
filename := ""
|
||||
for p := pp.Text; p != nil; p = p.Link {
|
||||
for p := s.pp.Text; p != nil; p = p.Link {
|
||||
// Don't spam every line with the file name, which is often huge.
|
||||
// Only print changes, and "unknown" is not a change.
|
||||
if p.Pos.IsKnown() && p.InnermostFilename() != filename {
|
||||
|
@ -7664,7 +7665,7 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
|
|||
var allPosOld []src.Pos
|
||||
var allPos []src.Pos
|
||||
|
||||
for p := pp.Text; p != nil; p = p.Link {
|
||||
for p := s.pp.Text; p != nil; p = p.Link {
|
||||
if p.Pos.IsKnown() {
|
||||
allPos = allPos[:0]
|
||||
p.Ctxt.AllPos(p.Pos, func(pos src.Pos) { allPos = append(allPos, pos) })
|
||||
|
|
|
@ -14,8 +14,16 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
type devirtualization struct {
|
||||
pos string
|
||||
callee string
|
||||
}
|
||||
|
||||
const profFileName = "devirt.pprof"
|
||||
const preProfFileName = "devirt.pprof.node_map"
|
||||
|
||||
// testPGODevirtualize tests that specific PGO devirtualize rewrites are performed.
|
||||
func testPGODevirtualize(t *testing.T, dir string) {
|
||||
func testPGODevirtualize(t *testing.T, dir string, want []devirtualization, pgoProfileName string) {
|
||||
testenv.MustHaveGoRun(t)
|
||||
t.Parallel()
|
||||
|
||||
|
@ -23,7 +31,7 @@ func testPGODevirtualize(t *testing.T, dir string) {
|
|||
|
||||
// Add a go.mod so we have a consistent symbol names in this temp dir.
|
||||
goMod := fmt.Sprintf(`module %s
|
||||
go 1.19
|
||||
go 1.21
|
||||
`, pkg)
|
||||
if err := os.WriteFile(filepath.Join(dir, "go.mod"), []byte(goMod), 0644); err != nil {
|
||||
t.Fatalf("error writing go.mod: %v", err)
|
||||
|
@ -40,7 +48,7 @@ go 1.19
|
|||
}
|
||||
|
||||
// Build the test with the profile.
|
||||
pprof := filepath.Join(dir, "devirt.pprof")
|
||||
pprof := filepath.Join(dir, pgoProfileName)
|
||||
gcflag := fmt.Sprintf("-gcflags=-m=2 -pgoprofile=%s -d=pgodebug=3", pprof)
|
||||
out := filepath.Join(dir, "test.exe")
|
||||
cmd = testenv.CleanCmdEnv(testenv.Command(t, testenv.GoToolPath(t), "test", "-o", out, gcflag, "."))
|
||||
|
@ -60,51 +68,6 @@ go 1.19
|
|||
t.Fatalf("error starting go test: %v", err)
|
||||
}
|
||||
|
||||
type devirtualization struct {
|
||||
pos string
|
||||
callee string
|
||||
}
|
||||
|
||||
want := []devirtualization{
|
||||
// ExerciseIface
|
||||
{
|
||||
pos: "./devirt.go:101:20",
|
||||
callee: "mult.Mult.Multiply",
|
||||
},
|
||||
{
|
||||
pos: "./devirt.go:101:39",
|
||||
callee: "Add.Add",
|
||||
},
|
||||
// ExerciseFuncConcrete
|
||||
{
|
||||
pos: "./devirt.go:173:36",
|
||||
callee: "AddFn",
|
||||
},
|
||||
{
|
||||
pos: "./devirt.go:173:15",
|
||||
callee: "mult.MultFn",
|
||||
},
|
||||
// ExerciseFuncField
|
||||
{
|
||||
pos: "./devirt.go:207:35",
|
||||
callee: "AddFn",
|
||||
},
|
||||
{
|
||||
pos: "./devirt.go:207:19",
|
||||
callee: "mult.MultFn",
|
||||
},
|
||||
// ExerciseFuncClosure
|
||||
// TODO(prattmic): Closure callees not implemented.
|
||||
//{
|
||||
// pos: "./devirt.go:249:27",
|
||||
// callee: "AddClosure.func1",
|
||||
//},
|
||||
//{
|
||||
// pos: "./devirt.go:249:15",
|
||||
// callee: "mult.MultClosure.func1",
|
||||
//},
|
||||
}
|
||||
|
||||
got := make(map[devirtualization]struct{})
|
||||
|
||||
devirtualizedLine := regexp.MustCompile(`(.*): PGO devirtualizing \w+ call .* to (.*)`)
|
||||
|
@ -166,11 +129,199 @@ func TestPGODevirtualize(t *testing.T) {
|
|||
if err := os.Mkdir(filepath.Join(dir, "mult.pkg"), 0755); err != nil {
|
||||
t.Fatalf("error creating dir: %v", err)
|
||||
}
|
||||
for _, file := range []string{"devirt.go", "devirt_test.go", "devirt.pprof", filepath.Join("mult.pkg", "mult.go")} {
|
||||
for _, file := range []string{"devirt.go", "devirt_test.go", profFileName, filepath.Join("mult.pkg", "mult.go")} {
|
||||
if err := copyFile(filepath.Join(dir, file), filepath.Join(srcDir, file)); err != nil {
|
||||
t.Fatalf("error copying %s: %v", file, err)
|
||||
}
|
||||
}
|
||||
|
||||
testPGODevirtualize(t, dir)
|
||||
want := []devirtualization{
|
||||
// ExerciseIface
|
||||
{
|
||||
pos: "./devirt.go:101:20",
|
||||
callee: "mult.Mult.Multiply",
|
||||
},
|
||||
{
|
||||
pos: "./devirt.go:101:39",
|
||||
callee: "Add.Add",
|
||||
},
|
||||
// ExerciseFuncConcrete
|
||||
{
|
||||
pos: "./devirt.go:173:36",
|
||||
callee: "AddFn",
|
||||
},
|
||||
{
|
||||
pos: "./devirt.go:173:15",
|
||||
callee: "mult.MultFn",
|
||||
},
|
||||
// ExerciseFuncField
|
||||
{
|
||||
pos: "./devirt.go:207:35",
|
||||
callee: "AddFn",
|
||||
},
|
||||
{
|
||||
pos: "./devirt.go:207:19",
|
||||
callee: "mult.MultFn",
|
||||
},
|
||||
// ExerciseFuncClosure
|
||||
// TODO(prattmic): Closure callees not implemented.
|
||||
//{
|
||||
// pos: "./devirt.go:249:27",
|
||||
// callee: "AddClosure.func1",
|
||||
//},
|
||||
//{
|
||||
// pos: "./devirt.go:249:15",
|
||||
// callee: "mult.MultClosure.func1",
|
||||
//},
|
||||
}
|
||||
|
||||
testPGODevirtualize(t, dir, want, profFileName)
|
||||
}
|
||||
|
||||
// TestPGOPreprocessDevirtualize tests that specific functions are devirtualized when PGO
|
||||
// is applied to the exact source that was profiled. The input profile is PGO preprocessed file.
|
||||
func TestPGOPreprocessDevirtualize(t *testing.T) {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatalf("error getting wd: %v", err)
|
||||
}
|
||||
srcDir := filepath.Join(wd, "testdata", "pgo", "devirtualize")
|
||||
|
||||
// Copy the module to a scratch location so we can add a go.mod.
|
||||
dir := t.TempDir()
|
||||
if err := os.Mkdir(filepath.Join(dir, "mult.pkg"), 0755); err != nil {
|
||||
t.Fatalf("error creating dir: %v", err)
|
||||
}
|
||||
for _, file := range []string{"devirt.go", "devirt_test.go", preProfFileName, filepath.Join("mult.pkg", "mult.go")} {
|
||||
if err := copyFile(filepath.Join(dir, file), filepath.Join(srcDir, file)); err != nil {
|
||||
t.Fatalf("error copying %s: %v", file, err)
|
||||
}
|
||||
}
|
||||
|
||||
want := []devirtualization{
|
||||
// ExerciseIface
|
||||
{
|
||||
pos: "./devirt.go:101:20",
|
||||
callee: "mult.Mult.Multiply",
|
||||
},
|
||||
{
|
||||
pos: "./devirt.go:101:39",
|
||||
callee: "Add.Add",
|
||||
},
|
||||
// ExerciseFuncConcrete
|
||||
{
|
||||
pos: "./devirt.go:173:36",
|
||||
callee: "AddFn",
|
||||
},
|
||||
{
|
||||
pos: "./devirt.go:173:15",
|
||||
callee: "mult.MultFn",
|
||||
},
|
||||
// ExerciseFuncField
|
||||
{
|
||||
pos: "./devirt.go:207:35",
|
||||
callee: "AddFn",
|
||||
},
|
||||
{
|
||||
pos: "./devirt.go:207:19",
|
||||
callee: "mult.MultFn",
|
||||
},
|
||||
// ExerciseFuncClosure
|
||||
// TODO(prattmic): Closure callees not implemented.
|
||||
//{
|
||||
// pos: "./devirt.go:249:27",
|
||||
// callee: "AddClosure.func1",
|
||||
//},
|
||||
//{
|
||||
// pos: "./devirt.go:249:15",
|
||||
// callee: "mult.MultClosure.func1",
|
||||
//},
|
||||
}
|
||||
|
||||
testPGODevirtualize(t, dir, want, preProfFileName)
|
||||
}
|
||||
|
||||
// Regression test for https://go.dev/issue/65615. If a target function changes
|
||||
// from non-generic to generic we can't devirtualize it (don't know the type
|
||||
// parameters), but the compiler should not crash.
|
||||
func TestLookupFuncGeneric(t *testing.T) {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatalf("error getting wd: %v", err)
|
||||
}
|
||||
srcDir := filepath.Join(wd, "testdata", "pgo", "devirtualize")
|
||||
|
||||
// Copy the module to a scratch location so we can add a go.mod.
|
||||
dir := t.TempDir()
|
||||
if err := os.Mkdir(filepath.Join(dir, "mult.pkg"), 0755); err != nil {
|
||||
t.Fatalf("error creating dir: %v", err)
|
||||
}
|
||||
for _, file := range []string{"devirt.go", "devirt_test.go", profFileName, filepath.Join("mult.pkg", "mult.go")} {
|
||||
if err := copyFile(filepath.Join(dir, file), filepath.Join(srcDir, file)); err != nil {
|
||||
t.Fatalf("error copying %s: %v", file, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Change MultFn from a concrete function to a parameterized function.
|
||||
if err := convertMultToGeneric(filepath.Join(dir, "mult.pkg", "mult.go")); err != nil {
|
||||
t.Fatalf("error editing mult.go: %v", err)
|
||||
}
|
||||
|
||||
// Same as TestPGODevirtualize except for MultFn, which we cannot
|
||||
// devirtualize to because it has become generic.
|
||||
//
|
||||
// Note that the important part of this test is that the build is
|
||||
// successful, not the specific devirtualizations.
|
||||
want := []devirtualization{
|
||||
// ExerciseIface
|
||||
{
|
||||
pos: "./devirt.go:101:20",
|
||||
callee: "mult.Mult.Multiply",
|
||||
},
|
||||
{
|
||||
pos: "./devirt.go:101:39",
|
||||
callee: "Add.Add",
|
||||
},
|
||||
// ExerciseFuncConcrete
|
||||
{
|
||||
pos: "./devirt.go:173:36",
|
||||
callee: "AddFn",
|
||||
},
|
||||
// ExerciseFuncField
|
||||
{
|
||||
pos: "./devirt.go:207:35",
|
||||
callee: "AddFn",
|
||||
},
|
||||
// ExerciseFuncClosure
|
||||
// TODO(prattmic): Closure callees not implemented.
|
||||
//{
|
||||
// pos: "./devirt.go:249:27",
|
||||
// callee: "AddClosure.func1",
|
||||
//},
|
||||
//{
|
||||
// pos: "./devirt.go:249:15",
|
||||
// callee: "mult.MultClosure.func1",
|
||||
//},
|
||||
}
|
||||
|
||||
testPGODevirtualize(t, dir, want, profFileName)
|
||||
}
|
||||
|
||||
var multFnRe = regexp.MustCompile(`func MultFn\(a, b int64\) int64`)
|
||||
|
||||
func convertMultToGeneric(path string) error {
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening: %w", err)
|
||||
}
|
||||
|
||||
if !multFnRe.Match(content) {
|
||||
return fmt.Errorf("MultFn not found; update regexp?")
|
||||
}
|
||||
|
||||
// Users of MultFn shouldn't need adjustment, type inference should
|
||||
// work OK.
|
||||
content = multFnRe.ReplaceAll(content, []byte(`func MultFn[T int32|int64](a, b T) T`))
|
||||
|
||||
return os.WriteFile(path, content, 0644)
|
||||
}
|
||||
|
|
|
@ -18,6 +18,9 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
const profFile = "inline_hot.pprof"
|
||||
const preProfFile = "inline_hot.pprof.node_map"
|
||||
|
||||
func buildPGOInliningTest(t *testing.T, dir string, gcflag string) []byte {
|
||||
const pkg = "example.com/pgo/inline"
|
||||
|
||||
|
@ -43,7 +46,7 @@ go 1.19
|
|||
}
|
||||
|
||||
// testPGOIntendedInlining tests that specific functions are inlined.
|
||||
func testPGOIntendedInlining(t *testing.T, dir string) {
|
||||
func testPGOIntendedInlining(t *testing.T, dir string, profFile string) {
|
||||
testenv.MustHaveGoRun(t)
|
||||
t.Parallel()
|
||||
|
||||
|
@ -86,8 +89,7 @@ func testPGOIntendedInlining(t *testing.T, dir string) {
|
|||
|
||||
// Build the test with the profile. Use a smaller threshold to test.
|
||||
// TODO: maybe adjust the test to work with default threshold.
|
||||
pprof := filepath.Join(dir, "inline_hot.pprof")
|
||||
gcflag := fmt.Sprintf("-m -m -pgoprofile=%s -d=pgoinlinebudget=160,pgoinlinecdfthreshold=90", pprof)
|
||||
gcflag := fmt.Sprintf("-m -m -pgoprofile=%s -d=pgoinlinebudget=160,pgoinlinecdfthreshold=90", profFile)
|
||||
out := buildPGOInliningTest(t, dir, gcflag)
|
||||
|
||||
scanner := bufio.NewScanner(bytes.NewReader(out))
|
||||
|
@ -155,13 +157,34 @@ func TestPGOIntendedInlining(t *testing.T) {
|
|||
// Copy the module to a scratch location so we can add a go.mod.
|
||||
dir := t.TempDir()
|
||||
|
||||
for _, file := range []string{"inline_hot.go", "inline_hot_test.go", "inline_hot.pprof"} {
|
||||
for _, file := range []string{"inline_hot.go", "inline_hot_test.go", profFile} {
|
||||
if err := copyFile(filepath.Join(dir, file), filepath.Join(srcDir, file)); err != nil {
|
||||
t.Fatalf("error copying %s: %v", file, err)
|
||||
}
|
||||
}
|
||||
|
||||
testPGOIntendedInlining(t, dir)
|
||||
testPGOIntendedInlining(t, dir, profFile)
|
||||
}
|
||||
|
||||
// TestPGOIntendedInlining tests that specific functions are inlined when PGO
|
||||
// is applied to the exact source that was profiled.
|
||||
func TestPGOPreprocessInlining(t *testing.T) {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatalf("error getting wd: %v", err)
|
||||
}
|
||||
srcDir := filepath.Join(wd, "testdata/pgo/inline")
|
||||
|
||||
// Copy the module to a scratch location so we can add a go.mod.
|
||||
dir := t.TempDir()
|
||||
|
||||
for _, file := range []string{"inline_hot.go", "inline_hot_test.go", preProfFile} {
|
||||
if err := copyFile(filepath.Join(dir, file), filepath.Join(srcDir, file)); err != nil {
|
||||
t.Fatalf("error copying %s: %v", file, err)
|
||||
}
|
||||
}
|
||||
|
||||
testPGOIntendedInlining(t, dir, preProfFile)
|
||||
}
|
||||
|
||||
// TestPGOIntendedInlining tests that specific functions are inlined when PGO
|
||||
|
@ -177,7 +200,7 @@ func TestPGOIntendedInliningShiftedLines(t *testing.T) {
|
|||
dir := t.TempDir()
|
||||
|
||||
// Copy most of the files unmodified.
|
||||
for _, file := range []string{"inline_hot_test.go", "inline_hot.pprof"} {
|
||||
for _, file := range []string{"inline_hot_test.go", profFile} {
|
||||
if err := copyFile(filepath.Join(dir, file), filepath.Join(srcDir, file)); err != nil {
|
||||
t.Fatalf("error copying %s : %v", file, err)
|
||||
}
|
||||
|
@ -209,7 +232,7 @@ func TestPGOIntendedInliningShiftedLines(t *testing.T) {
|
|||
|
||||
dst.Close()
|
||||
|
||||
testPGOIntendedInlining(t, dir)
|
||||
testPGOIntendedInlining(t, dir, profFile)
|
||||
}
|
||||
|
||||
// TestPGOSingleIndex tests that the sample index can not be 1 and compilation
|
||||
|
@ -239,15 +262,15 @@ func TestPGOSingleIndex(t *testing.T) {
|
|||
// Copy the module to a scratch location so we can add a go.mod.
|
||||
dir := t.TempDir()
|
||||
|
||||
originalPprofFile, err := os.Open(filepath.Join(srcDir, "inline_hot.pprof"))
|
||||
originalPprofFile, err := os.Open(filepath.Join(srcDir, profFile))
|
||||
if err != nil {
|
||||
t.Fatalf("error opening inline_hot.pprof: %v", err)
|
||||
t.Fatalf("error opening %v: %v", profFile, err)
|
||||
}
|
||||
defer originalPprofFile.Close()
|
||||
|
||||
p, err := profile.Parse(originalPprofFile)
|
||||
if err != nil {
|
||||
t.Fatalf("error parsing inline_hot.pprof: %v", err)
|
||||
t.Fatalf("error parsing %v: %v", profFile, err)
|
||||
}
|
||||
|
||||
// Move the samples count value-type to the 0 index.
|
||||
|
@ -258,14 +281,14 @@ func TestPGOSingleIndex(t *testing.T) {
|
|||
s.Value = []int64{s.Value[tc.originalIndex]}
|
||||
}
|
||||
|
||||
modifiedPprofFile, err := os.Create(filepath.Join(dir, "inline_hot.pprof"))
|
||||
modifiedPprofFile, err := os.Create(filepath.Join(dir, profFile))
|
||||
if err != nil {
|
||||
t.Fatalf("error creating inline_hot.pprof: %v", err)
|
||||
t.Fatalf("error creating %v: %v", profFile, err)
|
||||
}
|
||||
defer modifiedPprofFile.Close()
|
||||
|
||||
if err := p.Write(modifiedPprofFile); err != nil {
|
||||
t.Fatalf("error writing inline_hot.pprof: %v", err)
|
||||
t.Fatalf("error writing %v: %v", profFile, err)
|
||||
}
|
||||
|
||||
for _, file := range []string{"inline_hot.go", "inline_hot_test.go"} {
|
||||
|
@ -274,7 +297,7 @@ func TestPGOSingleIndex(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
testPGOIntendedInlining(t, dir)
|
||||
testPGOIntendedInlining(t, dir, profFile)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -312,13 +335,13 @@ func TestPGOHash(t *testing.T) {
|
|||
// Copy the module to a scratch location so we can add a go.mod.
|
||||
dir := t.TempDir()
|
||||
|
||||
for _, file := range []string{"inline_hot.go", "inline_hot_test.go", "inline_hot.pprof"} {
|
||||
for _, file := range []string{"inline_hot.go", "inline_hot_test.go", profFile} {
|
||||
if err := copyFile(filepath.Join(dir, file), filepath.Join(srcDir, file)); err != nil {
|
||||
t.Fatalf("error copying %s: %v", file, err)
|
||||
}
|
||||
}
|
||||
|
||||
pprof := filepath.Join(dir, "inline_hot.pprof")
|
||||
pprof := filepath.Join(dir, profFile)
|
||||
// build with -trimpath so the source location (thus the hash)
|
||||
// does not depend on the temporary directory path.
|
||||
gcflag0 := fmt.Sprintf("-pgoprofile=%s -trimpath %s=>%s -d=pgoinlinebudget=160,pgoinlinecdfthreshold=90,pgodebug=1", pprof, dir, pkg)
|
||||
|
|
52
src/cmd/compile/internal/test/testdata/pgo/devirtualize/devirt.pprof.node_map
vendored
Normal file
52
src/cmd/compile/internal/test/testdata/pgo/devirtualize/devirt.pprof.node_map
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
GO PREPROFILE V1
|
||||
example.com/pgo/devirtualize.ExerciseFuncClosure
|
||||
example.com/pgo/devirtualize/mult%2epkg.MultClosure.func1
|
||||
18 93
|
||||
example.com/pgo/devirtualize.ExerciseIface
|
||||
example.com/pgo/devirtualize/mult%2epkg.NegMult.Multiply
|
||||
49 4
|
||||
example.com/pgo/devirtualize.ExerciseFuncConcrete
|
||||
example.com/pgo/devirtualize.AddFn
|
||||
48 103
|
||||
example.com/pgo/devirtualize.ExerciseFuncField
|
||||
example.com/pgo/devirtualize/mult%2epkg.NegMultFn
|
||||
23 8
|
||||
example.com/pgo/devirtualize.ExerciseFuncField
|
||||
example.com/pgo/devirtualize/mult%2epkg.MultFn
|
||||
23 94
|
||||
example.com/pgo/devirtualize.ExerciseIface
|
||||
example.com/pgo/devirtualize/mult%2epkg.Mult.Multiply
|
||||
49 40
|
||||
example.com/pgo/devirtualize.ExerciseIface
|
||||
example.com/pgo/devirtualize.Add.Add
|
||||
49 55
|
||||
example.com/pgo/devirtualize.ExerciseFuncConcrete
|
||||
example.com/pgo/devirtualize/mult%2epkg.NegMultFn
|
||||
48 8
|
||||
example.com/pgo/devirtualize.ExerciseFuncClosure
|
||||
example.com/pgo/devirtualize/mult%2epkg.NegMultClosure.func1
|
||||
18 10
|
||||
example.com/pgo/devirtualize.ExerciseIface
|
||||
example.com/pgo/devirtualize.Sub.Add
|
||||
49 7
|
||||
example.com/pgo/devirtualize.ExerciseFuncField
|
||||
example.com/pgo/devirtualize.AddFn
|
||||
23 101
|
||||
example.com/pgo/devirtualize.ExerciseFuncField
|
||||
example.com/pgo/devirtualize.SubFn
|
||||
23 12
|
||||
example.com/pgo/devirtualize.BenchmarkDevirtFuncConcrete
|
||||
example.com/pgo/devirtualize.ExerciseFuncConcrete
|
||||
1 2
|
||||
example.com/pgo/devirtualize.ExerciseFuncConcrete
|
||||
example.com/pgo/devirtualize/mult%2epkg.MultFn
|
||||
48 91
|
||||
example.com/pgo/devirtualize.ExerciseFuncConcrete
|
||||
example.com/pgo/devirtualize.SubFn
|
||||
48 5
|
||||
example.com/pgo/devirtualize.ExerciseFuncClosure
|
||||
example.com/pgo/devirtualize.Add.Add
|
||||
18 92
|
||||
example.com/pgo/devirtualize.ExerciseFuncClosure
|
||||
example.com/pgo/devirtualize.Sub.Add
|
||||
18 14
|
13
src/cmd/compile/internal/test/testdata/pgo/inline/inline_hot.pprof.node_map
vendored
Normal file
13
src/cmd/compile/internal/test/testdata/pgo/inline/inline_hot.pprof.node_map
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
GO PREPROFILE V1
|
||||
example.com/pgo/inline.benchmarkB
|
||||
example.com/pgo/inline.A
|
||||
18 1
|
||||
example.com/pgo/inline.(*BS).NS
|
||||
example.com/pgo/inline.T
|
||||
8 3
|
||||
example.com/pgo/inline.(*BS).NS
|
||||
example.com/pgo/inline.T
|
||||
13 2
|
||||
example.com/pgo/inline.A
|
||||
example.com/pgo/inline.(*BS).NS
|
||||
7 129
|
|
@ -34,7 +34,7 @@ func AllowsGoVersion(major, minor int) bool {
|
|||
}
|
||||
|
||||
// ParseLangFlag verifies that the -lang flag holds a valid value, and
|
||||
// exits if not. It initializes data used by langSupported.
|
||||
// exits if not. It initializes data used by AllowsGoVersion.
|
||||
func ParseLangFlag() {
|
||||
if base.Flag.Lang == "" {
|
||||
return
|
||||
|
@ -59,6 +59,10 @@ func ParseLangFlag() {
|
|||
|
||||
// parseLang parses a -lang option into a langVer.
|
||||
func parseLang(s string) (lang, error) {
|
||||
if s == "go1" { // cmd/go's new spelling of "go1.0" (#65528)
|
||||
s = "go1.0"
|
||||
}
|
||||
|
||||
matches := goVersionRE.FindStringSubmatch(s)
|
||||
if matches == nil {
|
||||
return lang{}, fmt.Errorf(`should be something like "go1.12"`)
|
||||
|
|
|
@ -21,11 +21,14 @@ type Alias struct {
|
|||
// NewAlias creates a new Alias type with the given type name and rhs.
|
||||
// rhs must not be nil.
|
||||
func NewAlias(obj *TypeName, rhs Type) *Alias {
|
||||
return (*Checker)(nil).newAlias(obj, rhs)
|
||||
alias := (*Checker)(nil).newAlias(obj, rhs)
|
||||
// Ensure that alias.actual is set (#65455).
|
||||
unalias(alias)
|
||||
return alias
|
||||
}
|
||||
|
||||
func (a *Alias) Obj() *TypeName { return a.obj }
|
||||
func (a *Alias) Underlying() Type { return a.actual.Underlying() }
|
||||
func (a *Alias) Underlying() Type { return unalias(a).Underlying() }
|
||||
func (a *Alias) String() string { return TypeString(a, nil) }
|
||||
|
||||
// Type accessors
|
||||
|
@ -36,21 +39,23 @@ func (a *Alias) String() string { return TypeString(a, nil) }
|
|||
// Consequently, the result is never an alias type.
|
||||
func Unalias(t Type) Type {
|
||||
if a0, _ := t.(*Alias); a0 != nil {
|
||||
return unalias(a0)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func unalias(a0 *Alias) Type {
|
||||
if a0.actual != nil {
|
||||
return a0.actual
|
||||
}
|
||||
for a := a0; ; {
|
||||
var t Type
|
||||
for a := a0; a != nil; a, _ = t.(*Alias) {
|
||||
t = a.fromRHS
|
||||
a, _ = t.(*Alias)
|
||||
if a == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
if t == nil {
|
||||
panic(fmt.Sprintf("non-terminated alias %s", a0.obj.name))
|
||||
}
|
||||
a0.actual = t
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
|
|
|
@ -2195,6 +2195,12 @@ func TestIssue61737(t *testing.T) {
|
|||
iface.NumMethods() // unlike go/types, there is no Complete() method, so we complete implicitly
|
||||
}
|
||||
|
||||
func TestNewAlias_Issue65455(t *testing.T) {
|
||||
obj := NewTypeName(nopos, nil, "A", nil)
|
||||
alias := NewAlias(obj, Typ[Int])
|
||||
alias.Underlying() // must not panic
|
||||
}
|
||||
|
||||
func TestIssue15305(t *testing.T) {
|
||||
const src = "package p; func f() int16; var _ = f(undef)"
|
||||
f := mustParse(src)
|
||||
|
|
|
@ -22,7 +22,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
|||
|
||||
// append is the only built-in that permits the use of ... for the last argument
|
||||
bin := predeclaredFuncs[id]
|
||||
if call.HasDots && id != _Append {
|
||||
if hasDots(call) && id != _Append {
|
||||
//check.errorf(call.Ellipsis, invalidOp + "invalid use of ... with built-in %s", bin.name)
|
||||
check.errorf(call,
|
||||
InvalidDotDotDot,
|
||||
|
@ -114,7 +114,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
|||
// spec: "As a special case, append also accepts a first argument assignable
|
||||
// to type []byte with a second argument of string type followed by ... .
|
||||
// This form appends the bytes of the string.
|
||||
if nargs == 2 && call.HasDots {
|
||||
if nargs == 2 && hasDots(call) {
|
||||
if ok, _ := x.assignableTo(check, NewSlice(universeByte), nil); ok {
|
||||
y := args[1]
|
||||
if t := coreString(y.typ); t != nil && isString(t) {
|
||||
|
@ -1034,14 +1034,3 @@ func arrayPtrDeref(typ Type) Type {
|
|||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
// unparen returns e with any enclosing parentheses stripped.
|
||||
func unparen(e syntax.Expr) syntax.Expr {
|
||||
for {
|
||||
p, ok := e.(*syntax.ParenExpr)
|
||||
if !ok {
|
||||
return e
|
||||
}
|
||||
e = p.X
|
||||
}
|
||||
}
|
||||
|
|
|
@ -209,7 +209,7 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind {
|
|||
break
|
||||
}
|
||||
}
|
||||
if call.HasDots {
|
||||
if hasDots(call) {
|
||||
check.errorf(call.ArgList[0], BadDotDotDotSyntax, "invalid use of ... in conversion to %s", T)
|
||||
break
|
||||
}
|
||||
|
@ -468,7 +468,7 @@ func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, targs []T
|
|||
|
||||
nargs := len(args)
|
||||
npars := sig.params.Len()
|
||||
ddd := call.HasDots
|
||||
ddd := hasDots(call)
|
||||
|
||||
// set up parameters
|
||||
sigParams := sig.params // adjusted for variadic functions (may be nil for empty parameter lists!)
|
||||
|
@ -824,22 +824,8 @@ func (check *Checker) selector(x *operand, e *syntax.SelectorExpr, def *TypeName
|
|||
if isInterfacePtr(x.typ) {
|
||||
why = check.interfacePtrError(x.typ)
|
||||
} else {
|
||||
why = check.sprintf("type %s has no field or method %s", x.typ, sel)
|
||||
// check if there's a field or method with different capitalization
|
||||
if obj, _, _ = lookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel, true); obj != nil {
|
||||
var what string // empty or description with trailing space " " (default case, should never be reached)
|
||||
switch obj.(type) {
|
||||
case *Var:
|
||||
what = "field "
|
||||
case *Func:
|
||||
what = "method "
|
||||
}
|
||||
if samePkg(obj.Pkg(), check.pkg) || obj.Exported() {
|
||||
why = check.sprintf("%s, but does have %s%s", why, what, obj.Name())
|
||||
} else if obj.Name() == sel {
|
||||
why = check.sprintf("%s%s is not exported", what, obj.Name())
|
||||
}
|
||||
}
|
||||
alt, _, _ := lookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel, true)
|
||||
why = check.lookupError(x.typ, sel, alt, false)
|
||||
}
|
||||
check.errorf(e.Sel, MissingFieldOrMethod, "%s.%s undefined (%s)", x.expr, sel, why)
|
||||
goto Error
|
||||
|
|
113
src/cmd/compile/internal/types2/errsupport.go
Normal file
113
src/cmd/compile/internal/types2/errsupport.go
Normal file
|
@ -0,0 +1,113 @@
|
|||
// Copyright 2024 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.
|
||||
|
||||
// This file implements support functions for error messages.
|
||||
|
||||
package types2
|
||||
|
||||
// lookupError returns a case-specific error when a lookup of selector sel in the
|
||||
// given type fails but an object with alternative spelling (case folding) is found.
|
||||
// If structLit is set, the error message is specifically for struct literal fields.
|
||||
func (check *Checker) lookupError(typ Type, sel string, obj Object, structLit bool) string {
|
||||
// Provide more detail if there is an unexported object, or one with different capitalization.
|
||||
// If selector and object are in the same package (==), export doesn't matter, otherwise (!=) it does.
|
||||
// Messages depend on whether it's a general lookup or a field lookup in a struct literal.
|
||||
//
|
||||
// case sel pkg have message (examples for general lookup)
|
||||
// ---------------------------------------------------------------------------------------------------------
|
||||
// ok x.Foo == Foo
|
||||
// misspelled x.Foo == FoO type X has no field or method Foo, but does have field FoO
|
||||
// misspelled x.Foo == foo type X has no field or method Foo, but does have field foo
|
||||
// misspelled x.Foo == foO type X has no field or method Foo, but does have field foO
|
||||
//
|
||||
// misspelled x.foo == Foo type X has no field or method foo, but does have field Foo
|
||||
// misspelled x.foo == FoO type X has no field or method foo, but does have field FoO
|
||||
// ok x.foo == foo
|
||||
// misspelled x.foo == foO type X has no field or method foo, but does have field foO
|
||||
//
|
||||
// ok x.Foo != Foo
|
||||
// misspelled x.Foo != FoO type X has no field or method Foo, but does have field FoO
|
||||
// unexported x.Foo != foo type X has no field or method Foo, but does have unexported field foo
|
||||
// missing x.Foo != foO type X has no field or method Foo
|
||||
//
|
||||
// misspelled x.foo != Foo type X has no field or method foo, but does have field Foo
|
||||
// missing x.foo != FoO type X has no field or method foo
|
||||
// inaccessible x.foo != foo cannot refer to unexported field foo
|
||||
// missing x.foo != foO type X has no field or method foo
|
||||
|
||||
const (
|
||||
ok = iota
|
||||
missing // no object found
|
||||
misspelled // found object with different spelling
|
||||
unexported // found object with name differing only in first letter
|
||||
inaccessible // found object with matching name but inaccessible from the current package
|
||||
)
|
||||
|
||||
// determine case
|
||||
e := missing
|
||||
var alt string // alternative spelling of selector; if any
|
||||
if obj != nil {
|
||||
alt = obj.Name()
|
||||
if obj.Pkg() == check.pkg {
|
||||
assert(alt != sel) // otherwise there is no lookup error
|
||||
e = misspelled
|
||||
} else if isExported(sel) {
|
||||
if isExported(alt) {
|
||||
e = misspelled
|
||||
} else if tail(sel) == tail(alt) {
|
||||
e = unexported
|
||||
}
|
||||
} else if isExported(alt) {
|
||||
if tail(sel) == tail(alt) {
|
||||
e = misspelled
|
||||
}
|
||||
} else if sel == alt {
|
||||
e = inaccessible
|
||||
}
|
||||
}
|
||||
|
||||
if structLit {
|
||||
switch e {
|
||||
case missing:
|
||||
return check.sprintf("unknown field %s in struct literal of type %s", sel, typ)
|
||||
case misspelled:
|
||||
return check.sprintf("unknown field %s in struct literal of type %s, but does have %s", sel, typ, alt)
|
||||
case unexported:
|
||||
return check.sprintf("unknown field %s in struct literal of type %s, but does have unexported %s", sel, typ, alt)
|
||||
case inaccessible:
|
||||
return check.sprintf("cannot refer to unexported field %s in struct literal of type %s", alt, typ)
|
||||
}
|
||||
} else {
|
||||
what := "object"
|
||||
switch obj.(type) {
|
||||
case *Var:
|
||||
what = "field"
|
||||
case *Func:
|
||||
what = "method"
|
||||
}
|
||||
switch e {
|
||||
case missing:
|
||||
return check.sprintf("type %s has no field or method %s", typ, sel)
|
||||
case misspelled:
|
||||
return check.sprintf("type %s has no field or method %s, but does have %s %s", typ, sel, what, alt)
|
||||
case unexported:
|
||||
return check.sprintf("type %s has no field or method %s, but does have unexported %s %s", typ, sel, what, alt)
|
||||
case inaccessible:
|
||||
return check.sprintf("cannot refer to unexported %s %s", what, alt)
|
||||
}
|
||||
}
|
||||
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// tail returns the string s without its first (UTF-8) character.
|
||||
// If len(s) == 0, the result is s.
|
||||
func tail(s string) string {
|
||||
for i, _ := range s {
|
||||
if i > 0 {
|
||||
return s[i:]
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
|
@ -1184,9 +1184,14 @@ func (check *Checker) exprInternal(T *target, x *operand, e syntax.Expr, hint Ty
|
|||
check.errorf(kv, InvalidLitField, "invalid field name %s in struct literal", kv.Key)
|
||||
continue
|
||||
}
|
||||
i := fieldIndex(utyp.fields, check.pkg, key.Value, false)
|
||||
i := fieldIndex(fields, check.pkg, key.Value, false)
|
||||
if i < 0 {
|
||||
check.errorf(kv.Key, MissingLitField, "unknown field %s in struct literal of type %s", key.Value, base)
|
||||
var alt Object
|
||||
if j := fieldIndex(fields, check.pkg, key.Value, true); j >= 0 {
|
||||
alt = fields[j]
|
||||
}
|
||||
msg := check.lookupError(base, key.Value, alt, true)
|
||||
check.error(kv.Key, MissingLitField, msg)
|
||||
continue
|
||||
}
|
||||
fld := fields[i]
|
||||
|
|
|
@ -590,9 +590,9 @@ func fieldIndex(fields []*Var, pkg *Package, name string, foldCase bool) int {
|
|||
return -1
|
||||
}
|
||||
|
||||
// lookupMethod returns the index of and method with matching package and name, or (-1, nil).
|
||||
// methodIndex returns the index of and method with matching package and name, or (-1, nil).
|
||||
// See Object.sameId for the meaning of foldCase.
|
||||
func lookupMethod(methods []*Func, pkg *Package, name string, foldCase bool) (int, *Func) {
|
||||
func methodIndex(methods []*Func, pkg *Package, name string, foldCase bool) (int, *Func) {
|
||||
if name != "_" {
|
||||
for i, m := range methods {
|
||||
if m.sameId(pkg, name, foldCase) {
|
||||
|
|
|
@ -6,6 +6,7 @@ package types2
|
|||
|
||||
import (
|
||||
"cmd/compile/internal/syntax"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
@ -334,6 +335,12 @@ func (t *Named) NumMethods() int {
|
|||
// For an ordinary or instantiated type t, the receiver base type of this
|
||||
// method is the named type t. For an uninstantiated generic type t, each
|
||||
// method receiver is instantiated with its receiver type parameters.
|
||||
//
|
||||
// Methods are numbered deterministically: given the same list of source files
|
||||
// presented to the type checker, or the same sequence of NewMethod and AddMethod
|
||||
// calls, the mapping from method index to corresponding method remains the same.
|
||||
// But the specific ordering is not specified and must not be relied on as it may
|
||||
// change in the future.
|
||||
func (t *Named) Method(i int) *Func {
|
||||
t.resolve()
|
||||
|
||||
|
@ -444,15 +451,40 @@ func (t *Named) SetUnderlying(underlying Type) {
|
|||
}
|
||||
|
||||
// AddMethod adds method m unless it is already in the method list.
|
||||
// t must not have type arguments.
|
||||
// The method must be in the same package as t, and t must not have
|
||||
// type arguments.
|
||||
func (t *Named) AddMethod(m *Func) {
|
||||
assert(samePkg(t.obj.pkg, m.pkg))
|
||||
assert(t.inst == nil)
|
||||
t.resolve()
|
||||
if i, _ := lookupMethod(t.methods, m.pkg, m.name, false); i < 0 {
|
||||
if t.methodIndex(m.name, false) < 0 {
|
||||
t.methods = append(t.methods, m)
|
||||
}
|
||||
}
|
||||
|
||||
// methodIndex returns the index of the method with the given name.
|
||||
// If foldCase is set, capitalization in the name is ignored.
|
||||
// The result is negative if no such method exists.
|
||||
func (t *Named) methodIndex(name string, foldCase bool) int {
|
||||
if name == "_" {
|
||||
return -1
|
||||
}
|
||||
if foldCase {
|
||||
for i, m := range t.methods {
|
||||
if strings.EqualFold(m.name, name) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i, m := range t.methods {
|
||||
if m.name == name {
|
||||
return i
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// TODO(gri) Investigate if Unalias can be moved to where underlying is set.
|
||||
func (t *Named) Underlying() Type { return Unalias(t.resolve().underlying) }
|
||||
func (t *Named) String() string { return TypeString(t, nil) }
|
||||
|
@ -553,16 +585,17 @@ loop:
|
|||
|
||||
func (n *Named) lookupMethod(pkg *Package, name string, foldCase bool) (int, *Func) {
|
||||
n.resolve()
|
||||
if samePkg(n.obj.pkg, pkg) || isExported(name) || foldCase {
|
||||
// If n is an instance, we may not have yet instantiated all of its methods.
|
||||
// Look up the method index in orig, and only instantiate method at the
|
||||
// matching index (if any).
|
||||
i, _ := lookupMethod(n.Origin().methods, pkg, name, foldCase)
|
||||
if i < 0 {
|
||||
return -1, nil
|
||||
}
|
||||
if i := n.Origin().methodIndex(name, foldCase); i >= 0 {
|
||||
// For instances, m.Method(i) will be different from the orig method.
|
||||
return i, n.Method(i)
|
||||
}
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
// context returns the type-checker context.
|
||||
func (check *Checker) context() *Context {
|
||||
|
|
|
@ -112,3 +112,51 @@ type Inst = *Tree[int]
|
|||
t.Errorf("Duplicate instances in cycle: %s (%p) -> %s (%p) -> %s (%p)", Inst, Inst, Node, Node, Tree, Tree)
|
||||
}
|
||||
}
|
||||
|
||||
// TestMethodOrdering is a simple test verifying that the indices of methods of
|
||||
// a named type remain the same as long as the same source and AddMethod calls
|
||||
// are presented to the type checker in the same order (go.dev/issue/61298).
|
||||
func TestMethodOrdering(t *testing.T) {
|
||||
const src = `
|
||||
package p
|
||||
|
||||
type T struct{}
|
||||
|
||||
func (T) a() {}
|
||||
func (T) c() {}
|
||||
func (T) b() {}
|
||||
`
|
||||
// should get the same method order each time
|
||||
var methods []string
|
||||
for i := 0; i < 5; i++ {
|
||||
// collect T methods as provided in src
|
||||
pkg := mustTypecheck(src, nil, nil)
|
||||
T := pkg.Scope().Lookup("T").Type().(*Named)
|
||||
|
||||
// add a few more methods manually
|
||||
for _, name := range []string{"foo", "bar", "bal"} {
|
||||
m := NewFunc(nopos, pkg, name, nil /* don't care about signature */)
|
||||
T.AddMethod(m)
|
||||
}
|
||||
|
||||
// check method order
|
||||
if i == 0 {
|
||||
// first round: collect methods in given order
|
||||
methods = make([]string, T.NumMethods())
|
||||
for j := range methods {
|
||||
methods[j] = T.Method(j).Name()
|
||||
}
|
||||
} else {
|
||||
// successive rounds: methods must appear in the same order
|
||||
if got := T.NumMethods(); got != len(methods) {
|
||||
t.Errorf("got %d methods, want %d", got, len(methods))
|
||||
continue
|
||||
}
|
||||
for j, m := range methods {
|
||||
if got := T.Method(j).Name(); got != m {
|
||||
t.Errorf("got method %s, want %s", got, m)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ func (s *_TypeSet) Method(i int) *Func { return s.methods[i] }
|
|||
|
||||
// LookupMethod returns the index of and method with matching package and name, or (-1, nil).
|
||||
func (s *_TypeSet) LookupMethod(pkg *Package, name string, foldCase bool) (int, *Func) {
|
||||
return lookupMethod(s.methods, pkg, name, foldCase)
|
||||
return methodIndex(s.methods, pkg, name, foldCase)
|
||||
}
|
||||
|
||||
func (s *_TypeSet) String() string {
|
||||
|
|
|
@ -48,6 +48,20 @@ func (check *Checker) ident(x *operand, e *syntax.Name, def *TypeName, wantType
|
|||
}
|
||||
check.recordUse(e, obj)
|
||||
|
||||
// If we want a type but don't have one, stop right here and avoid potential problems
|
||||
// with missing underlying types. This also gives better error messages in some cases
|
||||
// (see go.dev/issue/65344).
|
||||
_, gotType := obj.(*TypeName)
|
||||
if !gotType && wantType {
|
||||
check.errorf(e, NotAType, "%s is not a type", obj.Name())
|
||||
// avoid "declared but not used" errors
|
||||
// (don't use Checker.use - we don't want to evaluate too much)
|
||||
if v, _ := obj.(*Var); v != nil && v.pkg == check.pkg /* see Checker.use1 */ {
|
||||
v.used = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Type-check the object.
|
||||
// Only call Checker.objDecl if the object doesn't have a type yet
|
||||
// (in which case we must actually determine it) or the object is a
|
||||
|
@ -57,7 +71,7 @@ func (check *Checker) ident(x *operand, e *syntax.Name, def *TypeName, wantType
|
|||
// informative "not a type/value" error that this function's caller
|
||||
// will issue (see go.dev/issue/25790).
|
||||
typ := obj.Type()
|
||||
if _, gotType := obj.(*TypeName); typ == nil || gotType && wantType {
|
||||
if typ == nil || gotType && wantType {
|
||||
check.objDecl(obj, def)
|
||||
typ = obj.Type() // type must have been assigned by Checker.objDecl
|
||||
}
|
||||
|
|
|
@ -20,3 +20,6 @@ import "cmd/compile/internal/syntax"
|
|||
// If p and q are in different files, p is before q if the filename
|
||||
// of p sorts lexicographically before the filename of q.
|
||||
func cmpPos(p, q syntax.Pos) int { return p.Cmp(q) }
|
||||
|
||||
// hasDots reports whether the last argument in the call is followed by ...
|
||||
func hasDots(call *syntax.CallExpr) bool { return call.HasDots }
|
||||
|
|
|
@ -700,7 +700,7 @@ func typeHashFieldOf(pos src.XPos, itab *ir.UnaryExpr) *ir.SelectorExpr {
|
|||
} else {
|
||||
// runtime.itab's hash field
|
||||
if itabHashField == nil {
|
||||
itabHashField = runtimeField("hash", int64(2*types.PtrSize), types.Types[types.TUINT32])
|
||||
itabHashField = runtimeField("hash", rttype.ITab.OffsetOf("Hash"), types.Types[types.TUINT32])
|
||||
}
|
||||
hashField = itabHashField
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"cmd/compile/internal/base"
|
||||
"cmd/compile/internal/ir"
|
||||
"cmd/compile/internal/reflectdata"
|
||||
"cmd/compile/internal/rttype"
|
||||
"cmd/compile/internal/ssagen"
|
||||
"cmd/compile/internal/typecheck"
|
||||
"cmd/compile/internal/types"
|
||||
|
@ -345,8 +346,8 @@ func mayCall(n ir.Node) bool {
|
|||
// itabType loads the _type field from a runtime.itab struct.
|
||||
func itabType(itab ir.Node) ir.Node {
|
||||
if itabTypeField == nil {
|
||||
// runtime.itab's _type field
|
||||
itabTypeField = runtimeField("_type", int64(types.PtrSize), types.NewPtr(types.Types[types.TUINT8]))
|
||||
// internal/abi.ITab's Type field
|
||||
itabTypeField = runtimeField("Type", rttype.ITab.OffsetOf("Type"), types.NewPtr(types.Types[types.TUINT8]))
|
||||
}
|
||||
return boundedDotPtr(base.Pos, itab, itabTypeField)
|
||||
}
|
||||
|
|
14
src/cmd/dist/build.go
vendored
14
src/cmd/dist/build.go
vendored
|
@ -903,6 +903,20 @@ func runInstall(pkg string, ch chan struct{}) {
|
|||
// Define GORISCV64_value from goriscv64
|
||||
asmArgs = append(asmArgs, "-D", "GORISCV64_"+goriscv64)
|
||||
}
|
||||
if goarch == "arm" {
|
||||
// Define GOARM_value from goarm, which can be either a version
|
||||
// like "6", or a version and a FP mode, like "7,hardfloat".
|
||||
switch {
|
||||
case strings.Contains(goarm, "7"):
|
||||
asmArgs = append(asmArgs, "-D", "GOARM_7")
|
||||
fallthrough
|
||||
case strings.Contains(goarm, "6"):
|
||||
asmArgs = append(asmArgs, "-D", "GOARM_6")
|
||||
fallthrough
|
||||
default:
|
||||
asmArgs = append(asmArgs, "-D", "GOARM_5")
|
||||
}
|
||||
}
|
||||
goasmh := pathf("%s/go_asm.h", workdir)
|
||||
|
||||
// Collect symabis from assembly code.
|
||||
|
|
|
@ -3,12 +3,13 @@ module cmd
|
|||
go 1.23
|
||||
|
||||
require (
|
||||
github.com/google/pprof v0.0.0-20230811205829-9131a7e9cc17
|
||||
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5
|
||||
golang.org/x/arch v0.7.0
|
||||
golang.org/x/build v0.0.0-20240122184708-c291ad69d6be
|
||||
golang.org/x/mod v0.14.0
|
||||
golang.org/x/build v0.0.0-20240201175143-3ee44a092755
|
||||
golang.org/x/mod v0.15.1-0.20240207185259-766dc5df63e3
|
||||
golang.org/x/sync v0.6.0
|
||||
golang.org/x/sys v0.16.1-0.20240110015235-f69d32aa924f
|
||||
golang.org/x/sys v0.17.0
|
||||
golang.org/x/telemetry v0.0.0-20240208185543-e9b074dd3804
|
||||
golang.org/x/term v0.16.0
|
||||
golang.org/x/tools v0.17.1-0.20240119231502-e1555a36d006
|
||||
)
|
||||
|
|
|
@ -1,21 +1,39 @@
|
|||
github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89 h1:aPflPkRFkVwbW6dmcVqfgwp1i+UWGFH6VgR1Jim5Ygc=
|
||||
github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
|
||||
github.com/chromedp/chromedp v0.9.2 h1:dKtNz4kApb06KuSXoTQIyUC2TrA0fhGDwNZf3bcgfKw=
|
||||
github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs=
|
||||
github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic=
|
||||
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
|
||||
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
||||
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.2.1 h1:F2aeBZrm2NDsc7vbovKrWSogd4wvfAxg0FQ89/iqOTk=
|
||||
github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/pprof v0.0.0-20230811205829-9131a7e9cc17 h1:0h35ESZ02+hN/MFZb7XZOXg+Rl9+Rk8fBIf5YLws9gA=
|
||||
github.com/google/pprof v0.0.0-20230811205829-9131a7e9cc17/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA=
|
||||
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 h1:E/LAvt58di64hlYjx7AsNS6C/ysHWYo+2qPCZKTQhRo=
|
||||
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab h1:BA4a7pe6ZTd9F8kXETBoijjFJ/ntaa//1wiH9BZu4zU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68=
|
||||
github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
|
||||
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/build v0.0.0-20240122184708-c291ad69d6be h1:h1qJlb1MudWuUMYotaFX+nSdSgv6zrBBDNojV68uqCA=
|
||||
golang.org/x/build v0.0.0-20240122184708-c291ad69d6be/go.mod h1:RHSzqFUzT4+buJlGik6WptO5NxLQiR/ewD2uz3fgWuA=
|
||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/build v0.0.0-20240201175143-3ee44a092755 h1:irSM9p93GT4I3+Pu/grZlkwIjrXA3GfyKwlSosVbmtU=
|
||||
golang.org/x/build v0.0.0-20240201175143-3ee44a092755/go.mod h1:RHSzqFUzT4+buJlGik6WptO5NxLQiR/ewD2uz3fgWuA=
|
||||
golang.org/x/mod v0.15.1-0.20240207185259-766dc5df63e3 h1:/p/VemLWiTsjHqHwME1Iu+xIu8s9fBtwBk8bU/ejA1A=
|
||||
golang.org/x/mod v0.15.1-0.20240207185259-766dc5df63e3/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.16.1-0.20240110015235-f69d32aa924f h1:GvGFYRZ5kIldzXQj3UmUiUTMe5spPODuLKQvP38A+Qc=
|
||||
golang.org/x/sys v0.16.1-0.20240110015235-f69d32aa924f/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/telemetry v0.0.0-20240208185543-e9b074dd3804 h1:mLYQpgq+cJOnmn3pR2U9o5rzEuOVgnmw59GHPgypGeo=
|
||||
golang.org/x/telemetry v0.0.0-20240208185543-e9b074dd3804/go.mod h1:KG1lNk5ZFNssSZLrpVb4sMXKMpGwGXOxSG3rnu2gZQQ=
|
||||
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
|
||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
|
|
|
@ -1004,6 +1004,8 @@
|
|||
// Retracted []string // retraction information, if any (with -retracted or -u)
|
||||
// Deprecated string // deprecation message, if any (with -u)
|
||||
// Error *ModuleError // error loading module
|
||||
// Sum string // checksum for path, version (as in go.sum)
|
||||
// GoModSum string // checksum for go.mod (as in go.sum)
|
||||
// Origin any // provenance of module
|
||||
// Reuse bool // reuse of old module info is safe
|
||||
// }
|
||||
|
|
|
@ -245,6 +245,8 @@ applied to a Go struct, but now a Module struct:
|
|||
Retracted []string // retraction information, if any (with -retracted or -u)
|
||||
Deprecated string // deprecation message, if any (with -u)
|
||||
Error *ModuleError // error loading module
|
||||
Sum string // checksum for path, version (as in go.sum)
|
||||
GoModSum string // checksum for go.mod (as in go.sum)
|
||||
Origin any // provenance of module
|
||||
Reuse bool // reuse of old module info is safe
|
||||
}
|
||||
|
@ -725,6 +727,9 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
|
|||
b.IsCmdList = true
|
||||
b.NeedExport = *listExport
|
||||
b.NeedCompiledGoFiles = *listCompiled
|
||||
if cfg.Experiment.CoverageRedesign && cfg.BuildCover {
|
||||
load.PrepareForCoverageBuild(pkgs)
|
||||
}
|
||||
a := &work.Action{}
|
||||
// TODO: Use pkgsFilter?
|
||||
for _, p := range pkgs {
|
||||
|
@ -732,9 +737,6 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
|
|||
a.Deps = append(a.Deps, b.AutoAction(work.ModeInstall, work.ModeInstall, p))
|
||||
}
|
||||
}
|
||||
if cfg.Experiment.CoverageRedesign && cfg.BuildCover {
|
||||
load.PrepareForCoverageBuild(pkgs)
|
||||
}
|
||||
b.Do(ctx, a)
|
||||
}
|
||||
|
||||
|
|
|
@ -2306,7 +2306,7 @@ func (p *Package) setBuildInfo(ctx context.Context, autoVCS bool) {
|
|||
}
|
||||
if mi.Replace != nil {
|
||||
dm.Replace = debugModFromModinfo(mi.Replace)
|
||||
} else if mi.Version != "" {
|
||||
} else if mi.Version != "" && cfg.BuildMod != "vendor" {
|
||||
dm.Sum = modfetch.Sum(ctx, module.Version{Path: mi.Path, Version: mi.Version})
|
||||
}
|
||||
return dm
|
||||
|
|
|
@ -569,6 +569,47 @@ func HaveSum(mod module.Version) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// RecordedSum returns the sum if the go.sum file contains an entry for mod.
|
||||
// The boolean reports true if an entry was found or
|
||||
// false if no entry found or two conflicting sums are found.
|
||||
// The entry's hash must be generated with a known hash algorithm.
|
||||
// mod.Version may have a "/go.mod" suffix to distinguish sums for
|
||||
// .mod and .zip files.
|
||||
func RecordedSum(mod module.Version) (sum string, ok bool) {
|
||||
goSum.mu.Lock()
|
||||
defer goSum.mu.Unlock()
|
||||
inited, err := initGoSum()
|
||||
foundSum := ""
|
||||
if err != nil || !inited {
|
||||
return "", false
|
||||
}
|
||||
for _, goSums := range goSum.w {
|
||||
for _, h := range goSums[mod] {
|
||||
if !strings.HasPrefix(h, "h1:") {
|
||||
continue
|
||||
}
|
||||
if !goSum.status[modSum{mod, h}].dirty {
|
||||
if foundSum != "" && foundSum != h { // conflicting sums exist
|
||||
return "", false
|
||||
}
|
||||
foundSum = h
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, h := range goSum.m[mod] {
|
||||
if !strings.HasPrefix(h, "h1:") {
|
||||
continue
|
||||
}
|
||||
if !goSum.status[modSum{mod, h}].dirty {
|
||||
if foundSum != "" && foundSum != h { // conflicting sums exist
|
||||
return "", false
|
||||
}
|
||||
foundSum = h
|
||||
}
|
||||
}
|
||||
return foundSum, true
|
||||
}
|
||||
|
||||
// checkMod checks the given module's checksum and Go version.
|
||||
func checkMod(ctx context.Context, mod module.Version) {
|
||||
// Do the file I/O before acquiring the go.sum lock.
|
||||
|
|
|
@ -124,7 +124,7 @@ var (
|
|||
errNotFromModuleCache = fmt.Errorf("%w: not from module cache", ErrNotIndexed)
|
||||
)
|
||||
|
||||
// GetPackage returns the IndexPackage for the package at the given path.
|
||||
// GetPackage returns the IndexPackage for the directory at the given path.
|
||||
// It will return ErrNotIndexed if the directory should be read without
|
||||
// using the index, for instance because the index is disabled, or the package
|
||||
// is not in a module.
|
||||
|
@ -669,11 +669,9 @@ func IsStandardPackage(goroot_, compiler, path string) bool {
|
|||
reldir = str.TrimFilePathPrefix(reldir, "cmd")
|
||||
modroot = filepath.Join(modroot, "cmd")
|
||||
}
|
||||
if _, err := GetPackage(modroot, filepath.Join(modroot, reldir)); err == nil {
|
||||
// Note that goroot.IsStandardPackage doesn't check that the directory
|
||||
// actually contains any go files-- merely that it exists. GetPackage
|
||||
// returning a nil error is enough for us to know the directory exists.
|
||||
return true
|
||||
if pkg, err := GetPackage(modroot, filepath.Join(modroot, reldir)); err == nil {
|
||||
hasGo, err := pkg.IsDirWithGoFiles()
|
||||
return err == nil && hasGo
|
||||
} else if errors.Is(err, ErrNotIndexed) {
|
||||
// Fall back because package isn't indexable. (Probably because
|
||||
// a file was modified recently)
|
||||
|
@ -786,8 +784,8 @@ func shouldBuild(sf *sourceFile, tags map[string]bool) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// IndexPackage holds the information needed to access information in the
|
||||
// index needed to load a package in a specific directory.
|
||||
// IndexPackage holds the information in the index
|
||||
// needed to load a package in a specific directory.
|
||||
type IndexPackage struct {
|
||||
error error
|
||||
dir string // directory of the package relative to the modroot
|
||||
|
|
|
@ -29,7 +29,8 @@ type ModulePublic struct {
|
|||
Retracted []string `json:",omitempty"` // retraction information, if any (with -retracted or -u)
|
||||
Deprecated string `json:",omitempty"` // deprecation message, if any (with -u)
|
||||
Error *ModuleError `json:",omitempty"` // error loading module
|
||||
|
||||
Sum string `json:",omitempty"` // checksum for path, version (as in go.sum)
|
||||
GoModSum string `json:",omitempty"` // checksum for go.mod (as in go.sum)
|
||||
Origin *codehost.Origin `json:",omitempty"` // provenance of module
|
||||
Reuse bool `json:",omitempty"` // reuse of old module info is safe
|
||||
}
|
||||
|
|
|
@ -364,12 +364,18 @@ func moduleInfo(ctx context.Context, rs *Requirements, m module.Version, mode Li
|
|||
m.GoMod = gomod
|
||||
}
|
||||
}
|
||||
if gomodsum, ok := modfetch.RecordedSum(modkey(mod)); ok {
|
||||
m.GoModSum = gomodsum
|
||||
}
|
||||
}
|
||||
if checksumOk("") {
|
||||
dir, err := modfetch.DownloadDir(ctx, mod)
|
||||
if err == nil {
|
||||
m.Dir = dir
|
||||
}
|
||||
if sum, ok := modfetch.RecordedSum(mod); ok {
|
||||
m.Sum = sum
|
||||
}
|
||||
}
|
||||
|
||||
if mode&ListRetracted != 0 {
|
||||
|
|
|
@ -367,12 +367,13 @@ func asmArgs(a *Action, p *load.Package) []any {
|
|||
}
|
||||
|
||||
if cfg.Goarch == "arm" {
|
||||
// Define GOARM_value from cfg.GOARM.
|
||||
switch cfg.GOARM {
|
||||
case "7":
|
||||
// Define GOARM_value from cfg.GOARM, which can be either a version
|
||||
// like "6", or a version and a FP mode, like "7,hardfloat".
|
||||
switch {
|
||||
case strings.Contains(cfg.GOARM, "7"):
|
||||
args = append(args, "-D", "GOARM_7")
|
||||
fallthrough
|
||||
case "6":
|
||||
case strings.Contains(cfg.GOARM, "6"):
|
||||
args = append(args, "-D", "GOARM_6")
|
||||
fallthrough
|
||||
default:
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"cmd/go/internal/toolchain"
|
||||
"cmd/go/internal/workcmd"
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
|
@ -38,10 +36,14 @@ import (
|
|||
"cmd/go/internal/run"
|
||||
"cmd/go/internal/test"
|
||||
"cmd/go/internal/tool"
|
||||
"cmd/go/internal/toolchain"
|
||||
"cmd/go/internal/trace"
|
||||
"cmd/go/internal/version"
|
||||
"cmd/go/internal/vet"
|
||||
"cmd/go/internal/work"
|
||||
"cmd/go/internal/workcmd"
|
||||
|
||||
"golang.org/x/telemetry/counter"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -89,11 +91,13 @@ var _ = go11tag
|
|||
|
||||
func main() {
|
||||
log.SetFlags(0)
|
||||
counter.Open() // Open the telemetry counter file so counters can be written to it.
|
||||
handleChdirFlag()
|
||||
toolchain.Select()
|
||||
|
||||
flag.Usage = base.Usage
|
||||
flag.Parse()
|
||||
counter.CountFlags("cmd/go:flag-", *flag.CommandLine)
|
||||
|
||||
args := flag.Args()
|
||||
if len(args) < 1 {
|
||||
|
@ -149,6 +153,7 @@ func main() {
|
|||
|
||||
cmd, used := lookupCmd(args)
|
||||
cfg.CmdName = strings.Join(args[:used], " ")
|
||||
counter.Inc("cmd/go:subcommand-" + strings.ReplaceAll(cfg.CmdName, " ", "-"))
|
||||
if len(cmd.Commands) > 0 {
|
||||
if used >= len(args) {
|
||||
help.PrintUsage(os.Stderr, cmd)
|
||||
|
@ -236,6 +241,7 @@ func invoke(cmd *base.Command, args []string) {
|
|||
} else {
|
||||
base.SetFromGOFLAGS(&cmd.Flag)
|
||||
cmd.Flag.Parse(args[1:])
|
||||
counter.CountFlags("cmd/go/"+cmd.Name()+":flag-", cmd.Flag)
|
||||
args = cmd.Flag.Args()
|
||||
}
|
||||
|
||||
|
@ -320,6 +326,7 @@ func handleChdirFlag() {
|
|||
_, dir, _ = strings.Cut(a, "=")
|
||||
os.Args = slices.Delete(os.Args, used, used+1)
|
||||
}
|
||||
counter.Inc("cmd/go:flag-C")
|
||||
|
||||
if err := os.Chdir(dir); err != nil {
|
||||
base.Fatalf("go: %v", err)
|
||||
|
|
|
@ -55,6 +55,7 @@ func scriptConditions() map[string]script.Cond {
|
|||
add("msan", sysCondition("-msan", platform.MSanSupported, true))
|
||||
add("mustlinkext", script.Condition("platform always requires external linking", mustLinkExt))
|
||||
add("net", script.PrefixCondition("can connect to external network host <suffix>", hasNet))
|
||||
add("pielinkext", script.Condition("platform requires external linking for PIE", pieLinkExt))
|
||||
add("race", sysCondition("-race", platform.RaceDetectorSupported, true))
|
||||
add("symlink", lazyBool("testenv.HasSymlink()", testenv.HasSymlink))
|
||||
add("trimpath", script.OnceCondition("test binary was built with -trimpath", isTrimpath))
|
||||
|
@ -233,3 +234,9 @@ func mustLinkExt(s *script.State) (bool, error) {
|
|||
GOARCH, _ := s.LookupEnv("GOARCH")
|
||||
return platform.MustLinkExternal(GOOS, GOARCH, false), nil
|
||||
}
|
||||
|
||||
func pieLinkExt(s *script.State) (bool, error) {
|
||||
GOOS, _ := s.LookupEnv("GOOS")
|
||||
GOARCH, _ := s.LookupEnv("GOARCH")
|
||||
return !platform.InternalLinkPIESupported(GOOS, GOARCH), nil
|
||||
}
|
||||
|
|
2
src/cmd/go/testdata/script/README
vendored
2
src/cmd/go/testdata/script/README
vendored
|
@ -410,6 +410,8 @@ The available conditions are:
|
|||
platform always requires external linking
|
||||
[net:*]
|
||||
can connect to external network host <suffix>
|
||||
[pielinkext]
|
||||
platform requires external linking for PIE
|
||||
[race]
|
||||
GOOS/GOARCH supports -race
|
||||
[root]
|
||||
|
|
9
src/cmd/go/testdata/script/build_issue_65528.txt
vendored
Normal file
9
src/cmd/go/testdata/script/build_issue_65528.txt
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
go build
|
||||
|
||||
-- go.mod --
|
||||
module test
|
||||
|
||||
go 1.0
|
||||
|
||||
-- p.go --
|
||||
package p
|
|
@ -1,5 +1,6 @@
|
|||
[!buildmode:plugin] skip
|
||||
[short] skip
|
||||
[!cgo] skip '-buildmode=plugin requires external linking'
|
||||
|
||||
go build -trimpath -buildvcs=false -buildmode=plugin -o a.so main.go
|
||||
go build -trimpath -buildvcs=false -buildmode=plugin -o b.so main.go
|
||||
|
|
4
src/cmd/go/testdata/script/cover_list.txt
vendored
4
src/cmd/go/testdata/script/cover_list.txt
vendored
|
@ -38,6 +38,10 @@ cp stdout $WORK/toolbuildid.txt
|
|||
# Build IDs should match here.
|
||||
cmp $WORK/toolbuildid.txt $WORK/listbuildid.txt
|
||||
|
||||
# Make sure that the build succeeds regardless of covermode.
|
||||
go list -export -covermode=atomic m/example
|
||||
go list -export -covermode=count m/example
|
||||
|
||||
-- go.mod --
|
||||
module m
|
||||
|
||||
|
|
|
@ -8,8 +8,7 @@ env TESTGO_VERSION=go1.21pre3
|
|||
# Compile a fake toolchain to put in the path under various names.
|
||||
env GOTOOLCHAIN=
|
||||
mkdir $WORK/bin
|
||||
go build -o $WORK/bin/ ./fakego.go # adds .exe extension implicitly on Windows
|
||||
cp $WORK/bin/fakego$GOEXE $WORK/bin/go1.50.0$GOEXE
|
||||
go build -o $WORK/bin/go1.50.0$GOEXE ./fakego.go # adds .exe extension implicitly on Windows
|
||||
|
||||
[!GOOS:plan9] env PATH=$WORK/bin
|
||||
[GOOS:plan9] env path=$WORK/bin
|
||||
|
|
11
src/cmd/go/testdata/script/list_testdata.txt
vendored
Normal file
11
src/cmd/go/testdata/script/list_testdata.txt
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
# Issue 65406. The testdata directory in GOROOT/src
|
||||
# shouldn't be treated as a standard package.
|
||||
|
||||
go list -f '{{.ImportPath}} {{.Dir}}' testdata
|
||||
! stderr 'found package testdata in multiple modules'
|
||||
stdout 'testdata '$WORK${/}'gopath'${/}'src'
|
||||
|
||||
-- go.mod --
|
||||
module testdata
|
||||
-- p.go --
|
||||
package p
|
32
src/cmd/go/testdata/script/mod_gomodcache_vendor.txt
vendored
Normal file
32
src/cmd/go/testdata/script/mod_gomodcache_vendor.txt
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
# This test verifies that GOMODCACHE does not affect whether checksums are embedded
|
||||
# with vendored files.
|
||||
# See issue #46400
|
||||
[short] skip 'builds and links a binary twice'
|
||||
go mod tidy
|
||||
go mod vendor
|
||||
|
||||
go build -mod=vendor
|
||||
go version -m example$GOEXE
|
||||
cp stdout version-m.txt
|
||||
|
||||
env GOMODCACHE=$WORK${/}modcache
|
||||
go build -mod=vendor
|
||||
go version -m example$GOEXE
|
||||
cmp stdout version-m.txt
|
||||
|
||||
-- go.mod --
|
||||
module example
|
||||
go 1.22
|
||||
require rsc.io/sampler v1.3.0
|
||||
|
||||
-- main.go --
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"rsc.io/sampler"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(sampler.Hello())
|
||||
}
|
4
src/cmd/go/testdata/script/mod_list.txt
vendored
4
src/cmd/go/testdata/script/mod_list.txt
vendored
|
@ -44,9 +44,9 @@ stderr '^go: module rsc.io/quote/buggy: not a known dependency'
|
|||
|
||||
# Module loader does not interfere with list -e (golang.org/issue/24149).
|
||||
go list -e -f '{{.Error.Err}}' database
|
||||
stdout 'no Go files in '
|
||||
stdout 'package database is not in std'
|
||||
! go list database
|
||||
stderr 'no Go files in '
|
||||
stderr 'package database is not in std'
|
||||
|
||||
-- go.mod --
|
||||
module x
|
||||
|
|
16
src/cmd/go/testdata/script/mod_list_m.txt
vendored
Normal file
16
src/cmd/go/testdata/script/mod_list_m.txt
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
go mod tidy
|
||||
|
||||
go list -m -json all
|
||||
stdout '"GoModSum":\s+"h1:.+"'
|
||||
stdout '"Sum":\s+"h1:.+"'
|
||||
|
||||
-- go.mod --
|
||||
module example
|
||||
|
||||
go 1.21
|
||||
|
||||
require rsc.io/quote v1.5.1
|
||||
-- example.go --
|
||||
package example
|
||||
|
||||
import _ "rsc.io/quote"
|
|
@ -2,6 +2,16 @@
|
|||
[short] skip
|
||||
env GOCACHE=$WORK/cache
|
||||
|
||||
# Warm up the build cache with GOMAXPROCS unrestricted.
|
||||
go test -c -o $devnull
|
||||
|
||||
# For the fuzzing phase, we reduce GOMAXPROCS to avoid consuming too many
|
||||
# resources during the test. Ideally this would just free up resources to run
|
||||
# other parallel tests more quickly, but unfortunately it is actually necessary
|
||||
# in some 32-bit environments to prevent the fuzzing engine from running out of
|
||||
# address space (see https://go.dev/issue/65434).
|
||||
env GOMAXPROCS=2
|
||||
|
||||
# The fuzz function should be able to detect whether -timeout
|
||||
# was set with T.Deadline. Note there is no F.Deadline, and
|
||||
# there is no timeout while fuzzing, even if -fuzztime is set.
|
||||
|
|
|
@ -5,6 +5,13 @@ env GOCACHE=$WORK/cache
|
|||
# There are no seed values, so 'go test' should finish quickly.
|
||||
go test
|
||||
|
||||
# For the fuzzing phase, we reduce GOMAXPROCS to avoid consuming too many
|
||||
# resources during the test. Ideally this would just free up resources to run
|
||||
# other parallel tests more quickly, but unfortunately it is actually necessary
|
||||
# in some 32-bit environments to prevent the fuzzing engine from running out of
|
||||
# address space (see https://go.dev/issue/65434).
|
||||
env GOMAXPROCS=2
|
||||
|
||||
# Fuzzing should exit 0 after fuzztime, even if timeout is short.
|
||||
go test -timeout=3s -fuzz=FuzzFast -fuzztime=5s
|
||||
|
||||
|
|
7
src/cmd/go/testdata/script/version.txt
vendored
7
src/cmd/go/testdata/script/version.txt
vendored
|
@ -57,8 +57,9 @@ stdout '^test2json.exe: .+'
|
|||
stdout '^\tpath\tcmd/test2json$'
|
||||
! stdout 'mod[^e]'
|
||||
|
||||
# Repeat the test with -buildmode=pie.
|
||||
# Repeat the test with -buildmode=pie and default linking.
|
||||
[!buildmode:pie] stop
|
||||
[pielinkext] [!cgo] stop
|
||||
go build -buildmode=pie -o external.exe rsc.io/fortune
|
||||
go version external.exe
|
||||
stdout '^external.exe: .+'
|
||||
|
@ -68,9 +69,7 @@ stdout '^\tpath\trsc.io/fortune'
|
|||
stdout '^\tmod\trsc.io/fortune\tv1.0.0'
|
||||
|
||||
# Also test PIE with internal linking.
|
||||
# currently only supported on linux/amd64, linux/arm64 and windows/amd64.
|
||||
[!GOOS:linux] [!GOOS:windows] stop
|
||||
[!GOARCH:amd64] [!GOARCH:arm64] stop
|
||||
[pielinkext] stop
|
||||
go build -buildmode=pie -ldflags=-linkmode=internal -o internal.exe rsc.io/fortune
|
||||
go version internal.exe
|
||||
stdout '^internal.exe: .+'
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[short] skip
|
||||
[!cgo] skip '-buildmode=c-shared requires external linking'
|
||||
[!buildmode:c-shared] stop
|
||||
|
||||
env GO111MODULE=on
|
||||
|
|
|
@ -422,15 +422,18 @@ const (
|
|||
C_U15CON /* 15 bit unsigned constant */
|
||||
C_S16CON /* 16 bit signed constant */
|
||||
C_U16CON /* 16 bit unsigned constant */
|
||||
C_16CON /* Any constant which fits into 16 bits. Can be signed or unsigned */
|
||||
C_U31CON /* 31 bit unsigned constant */
|
||||
C_S32CON /* 32 bit signed constant */
|
||||
C_U32CON /* 32 bit unsigned constant */
|
||||
C_32CON /* Any constant which fits into 32 bits. Can be signed or unsigned */
|
||||
C_S34CON /* 34 bit signed constant */
|
||||
C_64CON /* Any constant which fits into 64 bits. Can be signed or unsigned */
|
||||
C_SACON /* $n(REG) where n <= int16 */
|
||||
C_LACON /* $n(REG) where n <= int32 */
|
||||
C_DACON /* $n(REG) where n <= int64 */
|
||||
C_SBRA /* A short offset argument to a branching instruction */
|
||||
C_LBRA /* A long offset argument to a branching instruction */
|
||||
C_LBRAPIC /* Like C_LBRA, but requires an extra NOP for potential TOC restore by the linker. */
|
||||
C_BRA /* A short offset argument to a branching instruction */
|
||||
C_BRAPIC /* Like C_BRA, but requires an extra NOP for potential TOC restore by the linker. */
|
||||
C_ZOREG /* An $0+reg memory op */
|
||||
C_SOREG /* An $n+reg memory arg where n is a 16 bit signed offset */
|
||||
C_LOREG /* An $n+reg memory arg where n is a 32 bit signed offset */
|
||||
|
@ -446,16 +449,6 @@ const (
|
|||
C_TEXTSIZE /* An argument with Type obj.TYPE_TEXTSIZE */
|
||||
|
||||
C_NCLASS /* must be the last */
|
||||
|
||||
/* Aliased names which should be cleaned up, or integrated. */
|
||||
C_SCON = C_U15CON
|
||||
C_ADDCON = C_S16CON
|
||||
C_ANDCON = C_U16CON
|
||||
C_LCON = C_32CON
|
||||
|
||||
/* Aliased names which may be generated by ppc64map for the optab. */
|
||||
C_S32CON = C_32CON
|
||||
C_U32CON = C_32CON
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -27,15 +27,18 @@ var cnames9 = []string{
|
|||
"U15CON",
|
||||
"S16CON",
|
||||
"U16CON",
|
||||
"16CON",
|
||||
"U31CON",
|
||||
"S32CON",
|
||||
"U32CON",
|
||||
"32CON",
|
||||
"S34CON",
|
||||
"64CON",
|
||||
"SACON",
|
||||
"LACON",
|
||||
"DACON",
|
||||
"SBRA",
|
||||
"LBRA",
|
||||
"LBRAPIC",
|
||||
"BRA",
|
||||
"BRAPIC",
|
||||
"ZOREG",
|
||||
"SOREG",
|
||||
"LOREG",
|
||||
|
|
|
@ -110,60 +110,56 @@ var optab []Optab
|
|||
|
||||
var optabBase = []Optab{
|
||||
{as: obj.ATEXT, a1: C_LOREG, a6: C_TEXTSIZE, type_: 0, size: 0},
|
||||
{as: obj.ATEXT, a1: C_LOREG, a3: C_LCON, a6: C_TEXTSIZE, type_: 0, size: 0},
|
||||
{as: obj.ATEXT, a1: C_LOREG, a3: C_32CON, a6: C_TEXTSIZE, type_: 0, size: 0},
|
||||
{as: obj.ATEXT, a1: C_ADDR, a6: C_TEXTSIZE, type_: 0, size: 0},
|
||||
{as: obj.ATEXT, a1: C_ADDR, a3: C_LCON, a6: C_TEXTSIZE, type_: 0, size: 0},
|
||||
{as: obj.ATEXT, a1: C_ADDR, a3: C_32CON, a6: C_TEXTSIZE, type_: 0, size: 0},
|
||||
/* move register */
|
||||
{as: AADD, a1: C_REG, a2: C_REG, a6: C_REG, type_: 2, size: 4},
|
||||
{as: AADD, a1: C_REG, a6: C_REG, type_: 2, size: 4},
|
||||
{as: AADD, a1: C_SCON, a2: C_REG, a6: C_REG, type_: 4, size: 4},
|
||||
{as: AADD, a1: C_SCON, a6: C_REG, type_: 4, size: 4},
|
||||
{as: AADD, a1: C_ADDCON, a2: C_REG, a6: C_REG, type_: 4, size: 4},
|
||||
{as: AADD, a1: C_ADDCON, a6: C_REG, type_: 4, size: 4},
|
||||
{as: AADD, a1: C_ANDCON, a2: C_REG, a6: C_REG, type_: 22, size: 8},
|
||||
{as: AADD, a1: C_ANDCON, a6: C_REG, type_: 22, size: 8},
|
||||
{as: AADDIS, a1: C_ADDCON, a2: C_REG, a6: C_REG, type_: 20, size: 4},
|
||||
{as: AADDIS, a1: C_ADDCON, a6: C_REG, type_: 20, size: 4},
|
||||
{as: AADD, a1: C_S16CON, a2: C_REG, a6: C_REG, type_: 4, size: 4},
|
||||
{as: AADD, a1: C_S16CON, a6: C_REG, type_: 4, size: 4},
|
||||
{as: AADD, a1: C_U16CON, a2: C_REG, a6: C_REG, type_: 22, size: 8},
|
||||
{as: AADD, a1: C_U16CON, a6: C_REG, type_: 22, size: 8},
|
||||
{as: AADDIS, a1: C_S16CON, a2: C_REG, a6: C_REG, type_: 20, size: 4},
|
||||
{as: AADDIS, a1: C_S16CON, a6: C_REG, type_: 20, size: 4},
|
||||
{as: AADDC, a1: C_REG, a2: C_REG, a6: C_REG, type_: 2, size: 4},
|
||||
{as: AADDC, a1: C_REG, a6: C_REG, type_: 2, size: 4},
|
||||
{as: AADDC, a1: C_ADDCON, a2: C_REG, a6: C_REG, type_: 4, size: 4},
|
||||
{as: AADDC, a1: C_ADDCON, a6: C_REG, type_: 4, size: 4},
|
||||
{as: AADDC, a1: C_LCON, a2: C_REG, a6: C_REG, type_: 22, size: 12},
|
||||
{as: AADDC, a1: C_LCON, a6: C_REG, type_: 22, size: 12},
|
||||
{as: AADDC, a1: C_S16CON, a2: C_REG, a6: C_REG, type_: 4, size: 4},
|
||||
{as: AADDC, a1: C_S16CON, a6: C_REG, type_: 4, size: 4},
|
||||
{as: AADDC, a1: C_32CON, a2: C_REG, a6: C_REG, type_: 22, size: 12},
|
||||
{as: AADDC, a1: C_32CON, a6: C_REG, type_: 22, size: 12},
|
||||
{as: AAND, a1: C_REG, a2: C_REG, a6: C_REG, type_: 6, size: 4}, /* logical, no literal */
|
||||
{as: AAND, a1: C_REG, a6: C_REG, type_: 6, size: 4},
|
||||
{as: AANDCC, a1: C_REG, a2: C_REG, a6: C_REG, type_: 6, size: 4},
|
||||
{as: AANDCC, a1: C_REG, a6: C_REG, type_: 6, size: 4},
|
||||
{as: AANDCC, a1: C_ANDCON, a6: C_REG, type_: 58, size: 4},
|
||||
{as: AANDCC, a1: C_ANDCON, a2: C_REG, a6: C_REG, type_: 58, size: 4},
|
||||
{as: AANDCC, a1: C_ADDCON, a6: C_REG, type_: 23, size: 8},
|
||||
{as: AANDCC, a1: C_ADDCON, a2: C_REG, a6: C_REG, type_: 23, size: 8},
|
||||
{as: AANDCC, a1: C_LCON, a6: C_REG, type_: 23, size: 12},
|
||||
{as: AANDCC, a1: C_LCON, a2: C_REG, a6: C_REG, type_: 23, size: 12},
|
||||
{as: AANDISCC, a1: C_ANDCON, a6: C_REG, type_: 58, size: 4},
|
||||
{as: AANDISCC, a1: C_ANDCON, a2: C_REG, a6: C_REG, type_: 58, size: 4},
|
||||
{as: AANDCC, a1: C_U16CON, a6: C_REG, type_: 58, size: 4},
|
||||
{as: AANDCC, a1: C_U16CON, a2: C_REG, a6: C_REG, type_: 58, size: 4},
|
||||
{as: AANDCC, a1: C_S16CON, a6: C_REG, type_: 23, size: 8},
|
||||
{as: AANDCC, a1: C_S16CON, a2: C_REG, a6: C_REG, type_: 23, size: 8},
|
||||
{as: AANDCC, a1: C_32CON, a6: C_REG, type_: 23, size: 12},
|
||||
{as: AANDCC, a1: C_32CON, a2: C_REG, a6: C_REG, type_: 23, size: 12},
|
||||
{as: AANDISCC, a1: C_U16CON, a6: C_REG, type_: 58, size: 4},
|
||||
{as: AANDISCC, a1: C_U16CON, a2: C_REG, a6: C_REG, type_: 58, size: 4},
|
||||
{as: AMULLW, a1: C_REG, a2: C_REG, a6: C_REG, type_: 2, size: 4},
|
||||
{as: AMULLW, a1: C_REG, a6: C_REG, type_: 2, size: 4},
|
||||
{as: AMULLW, a1: C_ADDCON, a2: C_REG, a6: C_REG, type_: 4, size: 4},
|
||||
{as: AMULLW, a1: C_ADDCON, a6: C_REG, type_: 4, size: 4},
|
||||
{as: AMULLW, a1: C_ANDCON, a2: C_REG, a6: C_REG, type_: 4, size: 4},
|
||||
{as: AMULLW, a1: C_ANDCON, a6: C_REG, type_: 4, size: 4},
|
||||
{as: AMULLW, a1: C_LCON, a2: C_REG, a6: C_REG, type_: 22, size: 12},
|
||||
{as: AMULLW, a1: C_LCON, a6: C_REG, type_: 22, size: 12},
|
||||
{as: AMULLW, a1: C_S16CON, a2: C_REG, a6: C_REG, type_: 4, size: 4},
|
||||
{as: AMULLW, a1: C_S16CON, a6: C_REG, type_: 4, size: 4},
|
||||
{as: AMULLW, a1: C_32CON, a2: C_REG, a6: C_REG, type_: 22, size: 12},
|
||||
{as: AMULLW, a1: C_32CON, a6: C_REG, type_: 22, size: 12},
|
||||
{as: ASUBC, a1: C_REG, a2: C_REG, a6: C_REG, type_: 10, size: 4},
|
||||
{as: ASUBC, a1: C_REG, a6: C_REG, type_: 10, size: 4},
|
||||
{as: ASUBC, a1: C_REG, a3: C_ADDCON, a6: C_REG, type_: 27, size: 4},
|
||||
{as: ASUBC, a1: C_REG, a3: C_LCON, a6: C_REG, type_: 28, size: 12},
|
||||
{as: ASUBC, a1: C_REG, a3: C_S16CON, a6: C_REG, type_: 27, size: 4},
|
||||
{as: ASUBC, a1: C_REG, a3: C_32CON, a6: C_REG, type_: 28, size: 12},
|
||||
{as: AOR, a1: C_REG, a2: C_REG, a6: C_REG, type_: 6, size: 4}, /* logical, literal not cc (or/xor) */
|
||||
{as: AOR, a1: C_REG, a6: C_REG, type_: 6, size: 4},
|
||||
{as: AOR, a1: C_ANDCON, a6: C_REG, type_: 58, size: 4},
|
||||
{as: AOR, a1: C_ANDCON, a2: C_REG, a6: C_REG, type_: 58, size: 4},
|
||||
{as: AOR, a1: C_ADDCON, a6: C_REG, type_: 23, size: 8},
|
||||
{as: AOR, a1: C_ADDCON, a2: C_REG, a6: C_REG, type_: 23, size: 8},
|
||||
{as: AOR, a1: C_LCON, a6: C_REG, type_: 23, size: 12},
|
||||
{as: AOR, a1: C_LCON, a2: C_REG, a6: C_REG, type_: 23, size: 12},
|
||||
{as: AORIS, a1: C_ANDCON, a6: C_REG, type_: 58, size: 4},
|
||||
{as: AORIS, a1: C_ANDCON, a2: C_REG, a6: C_REG, type_: 58, size: 4},
|
||||
{as: AOR, a1: C_U16CON, a6: C_REG, type_: 58, size: 4},
|
||||
{as: AOR, a1: C_U16CON, a2: C_REG, a6: C_REG, type_: 58, size: 4},
|
||||
{as: AOR, a1: C_S16CON, a6: C_REG, type_: 23, size: 8},
|
||||
{as: AOR, a1: C_S16CON, a2: C_REG, a6: C_REG, type_: 23, size: 8},
|
||||
{as: AOR, a1: C_32CON, a6: C_REG, type_: 23, size: 12},
|
||||
{as: AOR, a1: C_32CON, a2: C_REG, a6: C_REG, type_: 23, size: 12},
|
||||
{as: AORIS, a1: C_U16CON, a6: C_REG, type_: 58, size: 4},
|
||||
{as: AORIS, a1: C_U16CON, a2: C_REG, a6: C_REG, type_: 58, size: 4},
|
||||
{as: ADIVW, a1: C_REG, a2: C_REG, a6: C_REG, type_: 2, size: 4}, /* op r1[,r2],r3 */
|
||||
{as: ADIVW, a1: C_REG, a6: C_REG, type_: 2, size: 4},
|
||||
{as: ASUB, a1: C_REG, a2: C_REG, a6: C_REG, type_: 10, size: 4}, /* op r2[,r1],r3 */
|
||||
|
@ -172,33 +168,33 @@ var optabBase = []Optab{
|
|||
{as: ASLW, a1: C_REG, a2: C_REG, a6: C_REG, type_: 6, size: 4},
|
||||
{as: ASLD, a1: C_REG, a6: C_REG, type_: 6, size: 4},
|
||||
{as: ASLD, a1: C_REG, a2: C_REG, a6: C_REG, type_: 6, size: 4},
|
||||
{as: ASLD, a1: C_SCON, a2: C_REG, a6: C_REG, type_: 25, size: 4},
|
||||
{as: ASLD, a1: C_SCON, a6: C_REG, type_: 25, size: 4},
|
||||
{as: AEXTSWSLI, a1: C_SCON, a6: C_REG, type_: 25, size: 4},
|
||||
{as: AEXTSWSLI, a1: C_SCON, a2: C_REG, a6: C_REG, type_: 25, size: 4},
|
||||
{as: ASLW, a1: C_SCON, a2: C_REG, a6: C_REG, type_: 57, size: 4},
|
||||
{as: ASLW, a1: C_SCON, a6: C_REG, type_: 57, size: 4},
|
||||
{as: ASLD, a1: C_U15CON, a2: C_REG, a6: C_REG, type_: 25, size: 4},
|
||||
{as: ASLD, a1: C_U15CON, a6: C_REG, type_: 25, size: 4},
|
||||
{as: AEXTSWSLI, a1: C_U15CON, a6: C_REG, type_: 25, size: 4},
|
||||
{as: AEXTSWSLI, a1: C_U15CON, a2: C_REG, a6: C_REG, type_: 25, size: 4},
|
||||
{as: ASLW, a1: C_U15CON, a2: C_REG, a6: C_REG, type_: 57, size: 4},
|
||||
{as: ASLW, a1: C_U15CON, a6: C_REG, type_: 57, size: 4},
|
||||
{as: ASRAW, a1: C_REG, a6: C_REG, type_: 6, size: 4},
|
||||
{as: ASRAW, a1: C_REG, a2: C_REG, a6: C_REG, type_: 6, size: 4},
|
||||
{as: ASRAW, a1: C_SCON, a2: C_REG, a6: C_REG, type_: 56, size: 4},
|
||||
{as: ASRAW, a1: C_SCON, a6: C_REG, type_: 56, size: 4},
|
||||
{as: ASRAW, a1: C_U15CON, a2: C_REG, a6: C_REG, type_: 56, size: 4},
|
||||
{as: ASRAW, a1: C_U15CON, a6: C_REG, type_: 56, size: 4},
|
||||
{as: ASRAD, a1: C_REG, a6: C_REG, type_: 6, size: 4},
|
||||
{as: ASRAD, a1: C_REG, a2: C_REG, a6: C_REG, type_: 6, size: 4},
|
||||
{as: ASRAD, a1: C_SCON, a2: C_REG, a6: C_REG, type_: 56, size: 4},
|
||||
{as: ASRAD, a1: C_SCON, a6: C_REG, type_: 56, size: 4},
|
||||
{as: ARLWNM, a1: C_SCON, a2: C_REG, a3: C_LCON, a6: C_REG, type_: 63, size: 4},
|
||||
{as: ARLWNM, a1: C_SCON, a2: C_REG, a3: C_SCON, a4: C_SCON, a6: C_REG, type_: 63, size: 4},
|
||||
{as: ARLWNM, a1: C_REG, a2: C_REG, a3: C_LCON, a6: C_REG, type_: 63, size: 4},
|
||||
{as: ARLWNM, a1: C_REG, a2: C_REG, a3: C_SCON, a4: C_SCON, a6: C_REG, type_: 63, size: 4},
|
||||
{as: ACLRLSLWI, a1: C_SCON, a2: C_REG, a3: C_LCON, a6: C_REG, type_: 62, size: 4},
|
||||
{as: ARLDMI, a1: C_SCON, a2: C_REG, a3: C_LCON, a6: C_REG, type_: 30, size: 4},
|
||||
{as: ARLDC, a1: C_SCON, a2: C_REG, a3: C_LCON, a6: C_REG, type_: 29, size: 4},
|
||||
{as: ASRAD, a1: C_U15CON, a2: C_REG, a6: C_REG, type_: 56, size: 4},
|
||||
{as: ASRAD, a1: C_U15CON, a6: C_REG, type_: 56, size: 4},
|
||||
{as: ARLWNM, a1: C_U15CON, a2: C_REG, a3: C_32CON, a6: C_REG, type_: 63, size: 4},
|
||||
{as: ARLWNM, a1: C_U15CON, a2: C_REG, a3: C_U15CON, a4: C_U15CON, a6: C_REG, type_: 63, size: 4},
|
||||
{as: ARLWNM, a1: C_REG, a2: C_REG, a3: C_32CON, a6: C_REG, type_: 63, size: 4},
|
||||
{as: ARLWNM, a1: C_REG, a2: C_REG, a3: C_U15CON, a4: C_U15CON, a6: C_REG, type_: 63, size: 4},
|
||||
{as: ACLRLSLWI, a1: C_U15CON, a2: C_REG, a3: C_32CON, a6: C_REG, type_: 62, size: 4},
|
||||
{as: ARLDMI, a1: C_U15CON, a2: C_REG, a3: C_32CON, a6: C_REG, type_: 30, size: 4},
|
||||
{as: ARLDC, a1: C_U15CON, a2: C_REG, a3: C_32CON, a6: C_REG, type_: 29, size: 4},
|
||||
{as: ARLDC, a1: C_REG, a3: C_U8CON, a4: C_U8CON, a6: C_REG, type_: 9, size: 4},
|
||||
{as: ARLDCL, a1: C_SCON, a2: C_REG, a3: C_LCON, a6: C_REG, type_: 29, size: 4},
|
||||
{as: ARLDCL, a1: C_REG, a2: C_REG, a3: C_LCON, a6: C_REG, type_: 14, size: 4},
|
||||
{as: ARLDICL, a1: C_REG, a2: C_REG, a3: C_LCON, a6: C_REG, type_: 14, size: 4},
|
||||
{as: ARLDICL, a1: C_SCON, a2: C_REG, a3: C_LCON, a6: C_REG, type_: 14, size: 4},
|
||||
{as: ARLDCL, a1: C_REG, a3: C_LCON, a6: C_REG, type_: 14, size: 4},
|
||||
{as: ARLDCL, a1: C_U15CON, a2: C_REG, a3: C_32CON, a6: C_REG, type_: 29, size: 4},
|
||||
{as: ARLDCL, a1: C_REG, a2: C_REG, a3: C_32CON, a6: C_REG, type_: 14, size: 4},
|
||||
{as: ARLDICL, a1: C_REG, a2: C_REG, a3: C_32CON, a6: C_REG, type_: 14, size: 4},
|
||||
{as: ARLDICL, a1: C_U15CON, a2: C_REG, a3: C_32CON, a6: C_REG, type_: 14, size: 4},
|
||||
{as: ARLDCL, a1: C_REG, a3: C_32CON, a6: C_REG, type_: 14, size: 4},
|
||||
{as: AFADD, a1: C_FREG, a6: C_FREG, type_: 2, size: 4},
|
||||
{as: AFADD, a1: C_FREG, a2: C_FREG, a6: C_FREG, type_: 2, size: 4},
|
||||
{as: AFABS, a1: C_FREG, a6: C_FREG, type_: 33, size: 4},
|
||||
|
@ -232,8 +228,7 @@ var optabBase = []Optab{
|
|||
{as: AMOVBZ, a1: C_REG, a6: C_XOREG, type_: 108, size: 4},
|
||||
{as: AMOVBZ, a1: C_REG, a6: C_REG, type_: 13, size: 4},
|
||||
|
||||
{as: AMOVD, a1: C_ADDCON, a6: C_REG, type_: 3, size: 4},
|
||||
{as: AMOVD, a1: C_ANDCON, a6: C_REG, type_: 3, size: 4},
|
||||
{as: AMOVD, a1: C_16CON, a6: C_REG, type_: 3, size: 4},
|
||||
{as: AMOVD, a1: C_SACON, a6: C_REG, type_: 3, size: 4},
|
||||
{as: AMOVD, a1: C_SOREG, a6: C_REG, type_: 8, size: 4},
|
||||
{as: AMOVD, a1: C_XOREG, a6: C_REG, type_: 109, size: 4},
|
||||
|
@ -245,8 +240,7 @@ var optabBase = []Optab{
|
|||
{as: AMOVD, a1: C_REG, a6: C_SPR, type_: 66, size: 4},
|
||||
{as: AMOVD, a1: C_REG, a6: C_REG, type_: 13, size: 4},
|
||||
|
||||
{as: AMOVW, a1: C_ADDCON, a6: C_REG, type_: 3, size: 4},
|
||||
{as: AMOVW, a1: C_ANDCON, a6: C_REG, type_: 3, size: 4},
|
||||
{as: AMOVW, a1: C_16CON, a6: C_REG, type_: 3, size: 4},
|
||||
{as: AMOVW, a1: C_SACON, a6: C_REG, type_: 3, size: 4},
|
||||
{as: AMOVW, a1: C_CREG, a6: C_REG, type_: 68, size: 4},
|
||||
{as: AMOVW, a1: C_SOREG, a6: C_REG, type_: 8, size: 4},
|
||||
|
@ -258,7 +252,7 @@ var optabBase = []Optab{
|
|||
{as: AMOVW, a1: C_REG, a6: C_SPR, type_: 66, size: 4},
|
||||
{as: AMOVW, a1: C_REG, a6: C_REG, type_: 13, size: 4},
|
||||
|
||||
{as: AFMOVD, a1: C_ADDCON, a6: C_FREG, type_: 24, size: 8},
|
||||
{as: AFMOVD, a1: C_S16CON, a6: C_FREG, type_: 24, size: 8},
|
||||
{as: AFMOVD, a1: C_SOREG, a6: C_FREG, type_: 8, size: 4},
|
||||
{as: AFMOVD, a1: C_XOREG, a6: C_FREG, type_: 109, size: 4},
|
||||
{as: AFMOVD, a1: C_ZCON, a6: C_FREG, type_: 24, size: 4},
|
||||
|
@ -275,29 +269,28 @@ var optabBase = []Optab{
|
|||
{as: AMOVFL, a1: C_CREG, a6: C_CREG, type_: 67, size: 4},
|
||||
{as: AMOVFL, a1: C_FPSCR, a6: C_CREG, type_: 73, size: 4},
|
||||
{as: AMOVFL, a1: C_FPSCR, a6: C_FREG, type_: 53, size: 4},
|
||||
{as: AMOVFL, a1: C_FREG, a3: C_LCON, a6: C_FPSCR, type_: 64, size: 4},
|
||||
{as: AMOVFL, a1: C_FREG, a3: C_32CON, a6: C_FPSCR, type_: 64, size: 4},
|
||||
{as: AMOVFL, a1: C_FREG, a6: C_FPSCR, type_: 64, size: 4},
|
||||
{as: AMOVFL, a1: C_LCON, a6: C_FPSCR, type_: 65, size: 4},
|
||||
{as: AMOVFL, a1: C_32CON, a6: C_FPSCR, type_: 65, size: 4},
|
||||
{as: AMOVFL, a1: C_REG, a6: C_CREG, type_: 69, size: 4},
|
||||
{as: AMOVFL, a1: C_REG, a6: C_LCON, type_: 69, size: 4},
|
||||
{as: AMOVFL, a1: C_REG, a6: C_32CON, type_: 69, size: 4},
|
||||
|
||||
{as: ASYSCALL, type_: 5, size: 4},
|
||||
{as: ASYSCALL, a1: C_REG, type_: 77, size: 12},
|
||||
{as: ASYSCALL, a1: C_SCON, type_: 77, size: 12},
|
||||
{as: ABEQ, a6: C_SBRA, type_: 16, size: 4},
|
||||
{as: ABEQ, a1: C_CREG, a6: C_SBRA, type_: 16, size: 4},
|
||||
{as: ABR, a6: C_LBRA, type_: 11, size: 4}, // b label
|
||||
{as: ABR, a6: C_LBRAPIC, type_: 11, size: 8}, // b label; nop
|
||||
{as: ASYSCALL, a1: C_U15CON, type_: 77, size: 12},
|
||||
{as: ABEQ, a6: C_BRA, type_: 16, size: 4},
|
||||
{as: ABEQ, a1: C_CREG, a6: C_BRA, type_: 16, size: 4},
|
||||
{as: ABR, a6: C_BRA, type_: 11, size: 4}, // b label
|
||||
{as: ABR, a6: C_BRAPIC, type_: 11, size: 8}, // b label; nop
|
||||
{as: ABR, a6: C_LR, type_: 18, size: 4}, // blr
|
||||
{as: ABR, a6: C_CTR, type_: 18, size: 4}, // bctr
|
||||
{as: ABC, a1: C_SCON, a2: C_CRBIT, a6: C_SBRA, type_: 16, size: 4}, // bc bo, bi, label
|
||||
{as: ABC, a1: C_SCON, a2: C_CRBIT, a6: C_LBRA, type_: 17, size: 4}, // bc bo, bi, label
|
||||
{as: ABC, a1: C_SCON, a2: C_CRBIT, a6: C_LR, type_: 18, size: 4}, // bclr bo, bi
|
||||
{as: ABC, a1: C_SCON, a2: C_CRBIT, a3: C_SCON, a6: C_LR, type_: 18, size: 4}, // bclr bo, bi, bh
|
||||
{as: ABC, a1: C_SCON, a2: C_CRBIT, a6: C_CTR, type_: 18, size: 4}, // bcctr bo, bi
|
||||
{as: ABDNZ, a6: C_SBRA, type_: 16, size: 4},
|
||||
{as: ABC, a1: C_U15CON, a2: C_CRBIT, a6: C_BRA, type_: 16, size: 4}, // bc bo, bi, label
|
||||
{as: ABC, a1: C_U15CON, a2: C_CRBIT, a6: C_LR, type_: 18, size: 4}, // bclr bo, bi
|
||||
{as: ABC, a1: C_U15CON, a2: C_CRBIT, a3: C_U15CON, a6: C_LR, type_: 18, size: 4}, // bclr bo, bi, bh
|
||||
{as: ABC, a1: C_U15CON, a2: C_CRBIT, a6: C_CTR, type_: 18, size: 4}, // bcctr bo, bi
|
||||
{as: ABDNZ, a6: C_BRA, type_: 16, size: 4},
|
||||
{as: ASYNC, type_: 46, size: 4},
|
||||
{as: AWORD, a1: C_LCON, type_: 40, size: 4},
|
||||
{as: AWORD, a1: C_32CON, type_: 40, size: 4},
|
||||
{as: ADWORD, a1: C_64CON, type_: 31, size: 8},
|
||||
{as: ADWORD, a1: C_LACON, type_: 31, size: 8},
|
||||
{as: AADDME, a1: C_REG, a6: C_REG, type_: 47, size: 4},
|
||||
|
@ -313,18 +306,18 @@ var optabBase = []Optab{
|
|||
{as: AREMU, a1: C_REG, a2: C_REG, a6: C_REG, type_: 50, size: 16},
|
||||
{as: AREMD, a1: C_REG, a6: C_REG, type_: 51, size: 12},
|
||||
{as: AREMD, a1: C_REG, a2: C_REG, a6: C_REG, type_: 51, size: 12},
|
||||
{as: AMTFSB0, a1: C_SCON, type_: 52, size: 4},
|
||||
{as: AMTFSB0, a1: C_U15CON, type_: 52, size: 4},
|
||||
/* Other ISA 2.05+ instructions */
|
||||
{as: APOPCNTD, a1: C_REG, a6: C_REG, type_: 93, size: 4}, /* population count, x-form */
|
||||
{as: ACMPB, a1: C_REG, a2: C_REG, a6: C_REG, type_: 92, size: 4}, /* compare byte, x-form */
|
||||
{as: ACMPEQB, a1: C_REG, a2: C_REG, a6: C_CREG, type_: 92, size: 4}, /* compare equal byte, x-form, ISA 3.0 */
|
||||
{as: ACMPEQB, a1: C_REG, a6: C_REG, type_: 70, size: 4},
|
||||
{as: AFTDIV, a1: C_FREG, a2: C_FREG, a6: C_SCON, type_: 92, size: 4}, /* floating test for sw divide, x-form */
|
||||
{as: AFTSQRT, a1: C_FREG, a6: C_SCON, type_: 93, size: 4}, /* floating test for sw square root, x-form */
|
||||
{as: AFTDIV, a1: C_FREG, a2: C_FREG, a6: C_U15CON, type_: 92, size: 4}, /* floating test for sw divide, x-form */
|
||||
{as: AFTSQRT, a1: C_FREG, a6: C_U15CON, type_: 93, size: 4}, /* floating test for sw square root, x-form */
|
||||
{as: ACOPY, a1: C_REG, a6: C_REG, type_: 92, size: 4}, /* copy/paste facility, x-form */
|
||||
{as: ADARN, a1: C_SCON, a6: C_REG, type_: 92, size: 4}, /* deliver random number, x-form */
|
||||
{as: ADARN, a1: C_U15CON, a6: C_REG, type_: 92, size: 4}, /* deliver random number, x-form */
|
||||
{as: AMADDHD, a1: C_REG, a2: C_REG, a3: C_REG, a6: C_REG, type_: 83, size: 4}, /* multiply-add high/low doubleword, va-form */
|
||||
{as: AADDEX, a1: C_REG, a2: C_REG, a3: C_SCON, a6: C_REG, type_: 94, size: 4}, /* add extended using alternate carry, z23-form */
|
||||
{as: AADDEX, a1: C_REG, a2: C_REG, a3: C_U15CON, a6: C_REG, type_: 94, size: 4}, /* add extended using alternate carry, z23-form */
|
||||
{as: ACRAND, a1: C_CRBIT, a2: C_CRBIT, a6: C_CRBIT, type_: 2, size: 4}, /* logical ops for condition register bits xl-form */
|
||||
|
||||
/* Misc ISA 3.0 instructions */
|
||||
|
@ -368,7 +361,7 @@ var optabBase = []Optab{
|
|||
/* Vector shift */
|
||||
{as: AVS, a1: C_VREG, a2: C_VREG, a6: C_VREG, type_: 82, size: 4}, /* vector shift, vx-form */
|
||||
{as: AVSA, a1: C_VREG, a2: C_VREG, a6: C_VREG, type_: 82, size: 4}, /* vector shift algebraic, vx-form */
|
||||
{as: AVSOI, a1: C_ANDCON, a2: C_VREG, a3: C_VREG, a6: C_VREG, type_: 83, size: 4}, /* vector shift by octet immediate, va-form */
|
||||
{as: AVSOI, a1: C_U16CON, a2: C_VREG, a3: C_VREG, a6: C_VREG, type_: 83, size: 4}, /* vector shift by octet immediate, va-form */
|
||||
|
||||
/* Vector count */
|
||||
{as: AVCLZ, a1: C_VREG, a6: C_VREG, type_: 85, size: 4}, /* vector count leading zeros, vx-form */
|
||||
|
@ -392,10 +385,8 @@ var optabBase = []Optab{
|
|||
{as: AVSEL, a1: C_VREG, a2: C_VREG, a3: C_VREG, a6: C_VREG, type_: 83, size: 4}, /* vector select, va-form */
|
||||
|
||||
/* Vector splat */
|
||||
{as: AVSPLTB, a1: C_SCON, a2: C_VREG, a6: C_VREG, type_: 82, size: 4}, /* vector splat, vx-form */
|
||||
{as: AVSPLTB, a1: C_ADDCON, a2: C_VREG, a6: C_VREG, type_: 82, size: 4},
|
||||
{as: AVSPLTISB, a1: C_SCON, a6: C_VREG, type_: 82, size: 4}, /* vector splat immediate, vx-form */
|
||||
{as: AVSPLTISB, a1: C_ADDCON, a6: C_VREG, type_: 82, size: 4},
|
||||
{as: AVSPLTB, a1: C_S16CON, a2: C_VREG, a6: C_VREG, type_: 82, size: 4},
|
||||
{as: AVSPLTISB, a1: C_S16CON, a6: C_VREG, type_: 82, size: 4},
|
||||
|
||||
/* Vector AES */
|
||||
{as: AVCIPH, a1: C_VREG, a2: C_VREG, a6: C_VREG, type_: 82, size: 4}, /* vector AES cipher, vx-form */
|
||||
|
@ -403,7 +394,7 @@ var optabBase = []Optab{
|
|||
{as: AVSBOX, a1: C_VREG, a6: C_VREG, type_: 82, size: 4}, /* vector AES subbytes, vx-form */
|
||||
|
||||
/* Vector SHA */
|
||||
{as: AVSHASIGMA, a1: C_ANDCON, a2: C_VREG, a3: C_ANDCON, a6: C_VREG, type_: 82, size: 4}, /* vector SHA sigma, vx-form */
|
||||
{as: AVSHASIGMA, a1: C_U16CON, a2: C_VREG, a3: C_U16CON, a6: C_VREG, type_: 82, size: 4}, /* vector SHA sigma, vx-form */
|
||||
|
||||
/* VSX vector load */
|
||||
{as: ALXVD2X, a1: C_XOREG, a6: C_VSREG, type_: 87, size: 4}, /* vsx vector load, xx1-form */
|
||||
|
@ -447,14 +438,14 @@ var optabBase = []Optab{
|
|||
{as: AXXMRGHW, a1: C_VSREG, a2: C_VSREG, a6: C_VSREG, type_: 90, size: 4}, /* vsx merge, xx3-form */
|
||||
|
||||
/* VSX splat */
|
||||
{as: AXXSPLTW, a1: C_VSREG, a3: C_SCON, a6: C_VSREG, type_: 89, size: 4}, /* vsx splat, xx2-form */
|
||||
{as: AXXSPLTIB, a1: C_SCON, a6: C_VSREG, type_: 100, size: 4}, /* vsx splat, xx2-form */
|
||||
{as: AXXSPLTW, a1: C_VSREG, a3: C_U15CON, a6: C_VSREG, type_: 89, size: 4}, /* vsx splat, xx2-form */
|
||||
{as: AXXSPLTIB, a1: C_U15CON, a6: C_VSREG, type_: 100, size: 4}, /* vsx splat, xx2-form */
|
||||
|
||||
/* VSX permute */
|
||||
{as: AXXPERM, a1: C_VSREG, a2: C_VSREG, a6: C_VSREG, type_: 90, size: 4}, /* vsx permute, xx3-form */
|
||||
|
||||
/* VSX shift */
|
||||
{as: AXXSLDWI, a1: C_VSREG, a2: C_VSREG, a3: C_SCON, a6: C_VSREG, type_: 90, size: 4}, /* vsx shift immediate, xx3-form */
|
||||
{as: AXXSLDWI, a1: C_VSREG, a2: C_VSREG, a3: C_U15CON, a6: C_VSREG, type_: 90, size: 4}, /* vsx shift immediate, xx3-form */
|
||||
|
||||
/* VSX reverse bytes */
|
||||
{as: AXXBRQ, a1: C_VSREG, a6: C_VSREG, type_: 101, size: 4}, /* vsx reverse bytes */
|
||||
|
@ -479,45 +470,45 @@ var optabBase = []Optab{
|
|||
|
||||
{as: ACMP, a1: C_REG, a6: C_REG, type_: 70, size: 4},
|
||||
{as: ACMP, a1: C_REG, a2: C_CREG, a6: C_REG, type_: 70, size: 4},
|
||||
{as: ACMP, a1: C_REG, a6: C_ADDCON, type_: 71, size: 4},
|
||||
{as: ACMP, a1: C_REG, a2: C_CREG, a6: C_ADDCON, type_: 71, size: 4},
|
||||
{as: ACMP, a1: C_REG, a6: C_S16CON, type_: 71, size: 4},
|
||||
{as: ACMP, a1: C_REG, a2: C_CREG, a6: C_S16CON, type_: 71, size: 4},
|
||||
{as: ACMPU, a1: C_REG, a6: C_REG, type_: 70, size: 4},
|
||||
{as: ACMPU, a1: C_REG, a2: C_CREG, a6: C_REG, type_: 70, size: 4},
|
||||
{as: ACMPU, a1: C_REG, a6: C_ANDCON, type_: 71, size: 4},
|
||||
{as: ACMPU, a1: C_REG, a2: C_CREG, a6: C_ANDCON, type_: 71, size: 4},
|
||||
{as: ACMPU, a1: C_REG, a6: C_U16CON, type_: 71, size: 4},
|
||||
{as: ACMPU, a1: C_REG, a2: C_CREG, a6: C_U16CON, type_: 71, size: 4},
|
||||
{as: AFCMPO, a1: C_FREG, a6: C_FREG, type_: 70, size: 4},
|
||||
{as: AFCMPO, a1: C_FREG, a2: C_CREG, a6: C_FREG, type_: 70, size: 4},
|
||||
{as: ATW, a1: C_LCON, a2: C_REG, a6: C_REG, type_: 60, size: 4},
|
||||
{as: ATW, a1: C_LCON, a2: C_REG, a6: C_ADDCON, type_: 61, size: 4},
|
||||
{as: ATW, a1: C_32CON, a2: C_REG, a6: C_REG, type_: 60, size: 4},
|
||||
{as: ATW, a1: C_32CON, a2: C_REG, a6: C_S16CON, type_: 61, size: 4},
|
||||
{as: ADCBF, a1: C_SOREG, type_: 43, size: 4},
|
||||
{as: ADCBF, a1: C_XOREG, type_: 43, size: 4},
|
||||
{as: ADCBF, a1: C_XOREG, a2: C_REG, a6: C_SCON, type_: 43, size: 4},
|
||||
{as: ADCBF, a1: C_SOREG, a6: C_SCON, type_: 43, size: 4},
|
||||
{as: ADCBF, a1: C_XOREG, a6: C_SCON, type_: 43, size: 4},
|
||||
{as: ADCBF, a1: C_XOREG, a2: C_REG, a6: C_U15CON, type_: 43, size: 4},
|
||||
{as: ADCBF, a1: C_SOREG, a6: C_U15CON, type_: 43, size: 4},
|
||||
{as: ADCBF, a1: C_XOREG, a6: C_U15CON, type_: 43, size: 4},
|
||||
{as: ASTDCCC, a1: C_REG, a2: C_REG, a6: C_XOREG, type_: 44, size: 4},
|
||||
{as: ASTDCCC, a1: C_REG, a6: C_XOREG, type_: 44, size: 4},
|
||||
{as: ALDAR, a1: C_XOREG, a6: C_REG, type_: 45, size: 4},
|
||||
{as: ALDAR, a1: C_XOREG, a3: C_ANDCON, a6: C_REG, type_: 45, size: 4},
|
||||
{as: ALDAR, a1: C_XOREG, a3: C_U16CON, a6: C_REG, type_: 45, size: 4},
|
||||
{as: AEIEIO, type_: 46, size: 4},
|
||||
{as: ATLBIE, a1: C_REG, type_: 49, size: 4},
|
||||
{as: ATLBIE, a1: C_SCON, a6: C_REG, type_: 49, size: 4},
|
||||
{as: ATLBIE, a1: C_U15CON, a6: C_REG, type_: 49, size: 4},
|
||||
{as: ASLBMFEE, a1: C_REG, a6: C_REG, type_: 55, size: 4},
|
||||
{as: ASLBMTE, a1: C_REG, a6: C_REG, type_: 55, size: 4},
|
||||
{as: ASTSW, a1: C_REG, a6: C_XOREG, type_: 44, size: 4},
|
||||
{as: ASTSW, a1: C_REG, a3: C_LCON, a6: C_ZOREG, type_: 41, size: 4},
|
||||
{as: ASTSW, a1: C_REG, a3: C_32CON, a6: C_ZOREG, type_: 41, size: 4},
|
||||
{as: ALSW, a1: C_XOREG, a6: C_REG, type_: 45, size: 4},
|
||||
{as: ALSW, a1: C_ZOREG, a3: C_LCON, a6: C_REG, type_: 42, size: 4},
|
||||
{as: ALSW, a1: C_ZOREG, a3: C_32CON, a6: C_REG, type_: 42, size: 4},
|
||||
|
||||
{as: obj.AUNDEF, type_: 78, size: 4},
|
||||
{as: obj.APCDATA, a1: C_LCON, a6: C_LCON, type_: 0, size: 0},
|
||||
{as: obj.AFUNCDATA, a1: C_SCON, a6: C_ADDR, type_: 0, size: 0},
|
||||
{as: obj.APCDATA, a1: C_32CON, a6: C_32CON, type_: 0, size: 0},
|
||||
{as: obj.AFUNCDATA, a1: C_U15CON, a6: C_ADDR, type_: 0, size: 0},
|
||||
{as: obj.ANOP, type_: 0, size: 0},
|
||||
{as: obj.ANOP, a1: C_LCON, type_: 0, size: 0}, // NOP operand variations added for #40689
|
||||
{as: obj.ANOP, a1: C_32CON, type_: 0, size: 0}, // NOP operand variations added for #40689
|
||||
{as: obj.ANOP, a1: C_REG, type_: 0, size: 0}, // to preserve previous behavior
|
||||
{as: obj.ANOP, a1: C_FREG, type_: 0, size: 0},
|
||||
{as: obj.ADUFFZERO, a6: C_LBRA, type_: 11, size: 4}, // same as ABR/ABL
|
||||
{as: obj.ADUFFCOPY, a6: C_LBRA, type_: 11, size: 4}, // same as ABR/ABL
|
||||
{as: obj.APCALIGN, a1: C_LCON, type_: 0, size: 0}, // align code
|
||||
{as: obj.ADUFFZERO, a6: C_BRA, type_: 11, size: 4}, // same as ABR/ABL
|
||||
{as: obj.ADUFFCOPY, a6: C_BRA, type_: 11, size: 4}, // same as ABR/ABL
|
||||
{as: obj.APCALIGN, a1: C_32CON, type_: 0, size: 0}, // align code
|
||||
}
|
||||
|
||||
// These are opcodes above which may generate different sequences depending on whether prefix opcode support
|
||||
|
@ -552,7 +543,7 @@ var prefixableOptab = []PrefixableOptab{
|
|||
{Optab: Optab{as: AMOVD, a1: C_REG, a6: C_LOREG, type_: 35, size: 8}, minGOPPC64: 10, pfxsize: 8},
|
||||
{Optab: Optab{as: AMOVD, a1: C_REG, a6: C_ADDR, type_: 74, size: 8}, minGOPPC64: 10, pfxsize: 8},
|
||||
|
||||
{Optab: Optab{as: AMOVW, a1: C_LCON, a6: C_REG, type_: 19, size: 8}, minGOPPC64: 10, pfxsize: 8},
|
||||
{Optab: Optab{as: AMOVW, a1: C_32CON, a6: C_REG, type_: 19, size: 8}, minGOPPC64: 10, pfxsize: 8},
|
||||
{Optab: Optab{as: AMOVW, a1: C_LACON, a6: C_REG, type_: 26, size: 8}, minGOPPC64: 10, pfxsize: 8},
|
||||
{Optab: Optab{as: AMOVW, a1: C_LOREG, a6: C_REG, type_: 36, size: 8}, minGOPPC64: 10, pfxsize: 8},
|
||||
{Optab: Optab{as: AMOVW, a1: C_ADDR, a6: C_REG, type_: 75, size: 8}, minGOPPC64: 10, pfxsize: 8},
|
||||
|
@ -574,8 +565,8 @@ var prefixableOptab = []PrefixableOptab{
|
|||
{Optab: Optab{as: AFMOVD, a1: C_FREG, a6: C_LOREG, type_: 35, size: 8}, minGOPPC64: 10, pfxsize: 8},
|
||||
{Optab: Optab{as: AFMOVD, a1: C_FREG, a6: C_ADDR, type_: 74, size: 8}, minGOPPC64: 10, pfxsize: 8},
|
||||
|
||||
{Optab: Optab{as: AADD, a1: C_LCON, a2: C_REG, a6: C_REG, type_: 22, size: 12}, minGOPPC64: 10, pfxsize: 8},
|
||||
{Optab: Optab{as: AADD, a1: C_LCON, a6: C_REG, type_: 22, size: 12}, minGOPPC64: 10, pfxsize: 8},
|
||||
{Optab: Optab{as: AADD, a1: C_32CON, a2: C_REG, a6: C_REG, type_: 22, size: 12}, minGOPPC64: 10, pfxsize: 8},
|
||||
{Optab: Optab{as: AADD, a1: C_32CON, a6: C_REG, type_: 22, size: 12}, minGOPPC64: 10, pfxsize: 8},
|
||||
{Optab: Optab{as: AADD, a1: C_S34CON, a2: C_REG, a6: C_REG, type_: 22, size: 20}, minGOPPC64: 10, pfxsize: 8},
|
||||
{Optab: Optab{as: AADD, a1: C_S34CON, a6: C_REG, type_: 22, size: 20}, minGOPPC64: 10, pfxsize: 8},
|
||||
}
|
||||
|
@ -955,7 +946,7 @@ func (c *ctxt9) aclass(a *obj.Addr) int {
|
|||
f64 := a.Val.(float64)
|
||||
if f64 == 0 {
|
||||
if math.Signbit(f64) {
|
||||
return C_ADDCON
|
||||
return C_S16CON
|
||||
}
|
||||
return C_ZCON
|
||||
}
|
||||
|
@ -1017,7 +1008,7 @@ func (c *ctxt9) aclass(a *obj.Addr) int {
|
|||
case sbits <= 16:
|
||||
return C_U16CON
|
||||
case sbits <= 31:
|
||||
return C_U32CON
|
||||
return C_U31CON
|
||||
case sbits <= 32:
|
||||
return C_U32CON
|
||||
case sbits <= 33:
|
||||
|
@ -1041,9 +1032,9 @@ func (c *ctxt9) aclass(a *obj.Addr) int {
|
|||
|
||||
case obj.TYPE_BRANCH:
|
||||
if a.Sym != nil && c.ctxt.Flag_dynlink && !pfxEnabled {
|
||||
return C_LBRAPIC
|
||||
return C_BRAPIC
|
||||
}
|
||||
return C_SBRA
|
||||
return C_BRA
|
||||
}
|
||||
|
||||
return C_GOK
|
||||
|
@ -1114,7 +1105,7 @@ func (c *ctxt9) oplook(p *obj.Prog) *Optab {
|
|||
return &ops[0]
|
||||
}
|
||||
|
||||
// Compare two operand types (ex C_REG, or C_SCON)
|
||||
// Compare two operand types (ex C_REG, or C_U15CON)
|
||||
// and return true if b is compatible with a.
|
||||
//
|
||||
// Argument comparison isn't reflexitive, so care must be taken.
|
||||
|
@ -1145,13 +1136,20 @@ func cmp(a int, b int) bool {
|
|||
return cmp(C_U5CON, b)
|
||||
case C_U15CON:
|
||||
return cmp(C_U8CON, b)
|
||||
case C_U16CON:
|
||||
return cmp(C_U15CON, b)
|
||||
|
||||
case C_S16CON:
|
||||
return cmp(C_U15CON, b)
|
||||
case C_32CON:
|
||||
case C_U16CON:
|
||||
return cmp(C_U15CON, b)
|
||||
case C_16CON:
|
||||
return cmp(C_S16CON, b) || cmp(C_U16CON, b)
|
||||
case C_U31CON:
|
||||
return cmp(C_U16CON, b)
|
||||
case C_U32CON:
|
||||
return cmp(C_U31CON, b)
|
||||
case C_S32CON:
|
||||
return cmp(C_U31CON, b) || cmp(C_S16CON, b)
|
||||
case C_32CON:
|
||||
return cmp(C_S32CON, b) || cmp(C_U32CON, b)
|
||||
case C_S34CON:
|
||||
return cmp(C_32CON, b)
|
||||
case C_64CON:
|
||||
|
@ -1160,9 +1158,6 @@ func cmp(a int, b int) bool {
|
|||
case C_LACON:
|
||||
return cmp(C_SACON, b)
|
||||
|
||||
case C_LBRA:
|
||||
return cmp(C_SBRA, b)
|
||||
|
||||
case C_SOREG:
|
||||
return cmp(C_ZOREG, b)
|
||||
|
||||
|
@ -2541,33 +2536,25 @@ func asmout(c *ctxt9, p *obj.Prog, o *Optab, out *[5]uint32) {
|
|||
}
|
||||
o1 = AOP_RRR(c.oprrr(p.As), uint32(p.To.Reg), uint32(r), uint32(p.From.Reg))
|
||||
|
||||
case 3: /* mov $soreg/addcon/andcon/ucon, r ==> addis/oris/addi/ori $i,reg',r */
|
||||
case 3: /* mov $soreg/16con, r ==> addi/ori $i,reg',r */
|
||||
d := c.vregoff(&p.From)
|
||||
|
||||
v := int32(d)
|
||||
r := int(p.From.Reg)
|
||||
// p.From may be a constant value or an offset(reg) type argument.
|
||||
isZeroOrR0 := r&0x1f == 0
|
||||
|
||||
if r0iszero != 0 /*TypeKind(100016)*/ && p.To.Reg == 0 && (r != 0 || v != 0) {
|
||||
c.ctxt.Diag("literal operation on R0\n%v", p)
|
||||
}
|
||||
a := OP_ADDI
|
||||
if int64(int16(d)) != d {
|
||||
// Operand is 16 bit value with sign bit set
|
||||
if o.a1 == C_ANDCON {
|
||||
// Needs unsigned 16 bit so use ORI
|
||||
if isZeroOrR0 {
|
||||
if int64(int16(d)) == d {
|
||||
// MOVD $int16, Ry or MOVD $offset(Rx), Ry
|
||||
o1 = AOP_IRR(uint32(OP_ADDI), uint32(p.To.Reg), uint32(r), uint32(v))
|
||||
} else {
|
||||
// MOVD $uint16, Ry
|
||||
if int64(uint16(d)) != d || (r != 0 && r != REGZERO) {
|
||||
c.ctxt.Diag("Rule expects a uint16 constant load. got:\n%v", p)
|
||||
}
|
||||
o1 = LOP_IRR(uint32(OP_ORI), uint32(p.To.Reg), uint32(0), uint32(v))
|
||||
break
|
||||
}
|
||||
// With ADDCON, needs signed 16 bit value, fall through to use ADDI
|
||||
} else if o.a1 != C_ADDCON {
|
||||
log.Fatalf("invalid handling of %v", p)
|
||||
}
|
||||
}
|
||||
|
||||
o1 = AOP_IRR(uint32(a), uint32(p.To.Reg), uint32(r), uint32(v))
|
||||
|
||||
case 4: /* add/mul $scon,[r1],r2 */
|
||||
v := c.regoff(&p.From)
|
||||
|
@ -2654,7 +2641,7 @@ func asmout(c *ctxt9, p *obj.Prog, o *Optab, out *[5]uint32) {
|
|||
}
|
||||
o1 = AOP_RRR(c.oprrr(p.As), uint32(p.To.Reg), uint32(p.From.Reg), uint32(r))
|
||||
|
||||
case 11: /* br/bl lbra */
|
||||
case 11: /* br/bl bra */
|
||||
v := int32(0)
|
||||
|
||||
if p.To.Target() != nil {
|
||||
|
@ -2688,7 +2675,7 @@ func asmout(c *ctxt9, p *obj.Prog, o *Optab, out *[5]uint32) {
|
|||
|
||||
case 13: /* mov[bhwd]{z,} r,r */
|
||||
// This needs to handle "MOV* $0, Rx". This shows up because $0 also
|
||||
// matches C_REG if r0iszero. This happens because C_REG sorts before C_ANDCON
|
||||
// matches C_REG if r0iszero. This happens because C_REG sorts before C_U16CON
|
||||
// TODO: fix the above behavior and cleanup this exception.
|
||||
if p.From.Type == obj.TYPE_CONST {
|
||||
o1 = LOP_IRR(OP_ADDI, REGZERO, uint32(p.To.Reg), 0)
|
||||
|
@ -2776,8 +2763,7 @@ func asmout(c *ctxt9, p *obj.Prog, o *Optab, out *[5]uint32) {
|
|||
c.ctxt.Diag("unexpected op in rldc case\n%v", p)
|
||||
}
|
||||
|
||||
case 17, /* bc bo,bi,lbra (same for now) */
|
||||
16: /* bc bo,bi,sbra */
|
||||
case 16: /* bc bo,bi,bra */
|
||||
a := 0
|
||||
|
||||
r := int(p.Reg)
|
||||
|
@ -2921,8 +2907,8 @@ func asmout(c *ctxt9, p *obj.Prog, o *Optab, out *[5]uint32) {
|
|||
r = int(p.To.Reg)
|
||||
}
|
||||
|
||||
// With ADDCON operand, generate 2 instructions using ADDI for signed value,
|
||||
// with LCON operand generate 3 instructions.
|
||||
// With S16CON operand, generate 2 instructions using ADDI for signed value,
|
||||
// with 32CON operand generate 3 instructions.
|
||||
if o.size == 8 {
|
||||
o1 = LOP_IRR(OP_ADDI, REGZERO, REGTMP, uint32(int32(d)))
|
||||
o2 = LOP_RRR(c.oprrr(p.As), uint32(p.To.Reg), REGTMP, uint32(r))
|
||||
|
|
|
@ -516,17 +516,19 @@ func TestAddrClassifier(t *testing.T) {
|
|||
{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 32}, C_U8CON},
|
||||
{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 << 14}, C_U15CON},
|
||||
{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 << 15}, C_U16CON},
|
||||
{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 + 1<<16}, C_U32CON},
|
||||
{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 + 1<<16}, C_U31CON},
|
||||
{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 << 31}, C_U32CON},
|
||||
{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 << 32}, C_S34CON},
|
||||
{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 << 33}, C_64CON},
|
||||
{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: -1}, C_S16CON},
|
||||
{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: -0x10001}, C_S32CON},
|
||||
{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 0x10001}, C_U31CON},
|
||||
{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: -(1 << 33)}, C_S34CON},
|
||||
{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: -(1 << 34)}, C_64CON},
|
||||
|
||||
// Branch like arguments
|
||||
{obj.Addr{Type: obj.TYPE_BRANCH, Sym: &obj.LSym{Type: objabi.SDATA}}, cmplx{C_SBRA, C_LBRAPIC, C_LBRAPIC, C_SBRA}},
|
||||
{obj.Addr{Type: obj.TYPE_BRANCH}, C_SBRA},
|
||||
{obj.Addr{Type: obj.TYPE_BRANCH, Sym: &obj.LSym{Type: objabi.SDATA}}, cmplx{C_BRA, C_BRAPIC, C_BRAPIC, C_BRA}},
|
||||
{obj.Addr{Type: obj.TYPE_BRANCH}, C_BRA},
|
||||
}
|
||||
|
||||
pic_ctxt9 := ctxt9{ctxt: &obj.Link{Flag_shared: true, Arch: &Linkppc64}, autosize: 0}
|
||||
|
|
|
@ -69,8 +69,8 @@ func (ex *Examiner) Populate(rdr *dwarf.Reader) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (e *Examiner) DIEs() []*dwarf.Entry {
|
||||
return e.dies
|
||||
func (ex *Examiner) DIEs() []*dwarf.Entry {
|
||||
return ex.dies
|
||||
}
|
||||
|
||||
func indent(ilevel int) {
|
||||
|
|
|
@ -45,6 +45,7 @@ import (
|
|||
"fmt"
|
||||
"internal/abi"
|
||||
"log"
|
||||
"math/rand"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
@ -122,10 +123,11 @@ func trampoline(ctxt *Link, s loader.Sym) {
|
|||
}
|
||||
|
||||
if ldr.SymValue(rs) == 0 && ldr.SymType(rs) != sym.SDYNIMPORT && ldr.SymType(rs) != sym.SUNDEFEXT {
|
||||
// Symbols in the same package are laid out together.
|
||||
// Symbols in the same package are laid out together (if we
|
||||
// don't randomize the function order).
|
||||
// Except that if SymPkg(s) == "", it is a host object symbol
|
||||
// which may call an external symbol via PLT.
|
||||
if ldr.SymPkg(s) != "" && ldr.SymPkg(rs) == ldr.SymPkg(s) {
|
||||
if ldr.SymPkg(s) != "" && ldr.SymPkg(rs) == ldr.SymPkg(s) && *flagRandLayout == 0 {
|
||||
// RISC-V is only able to reach +/-1MiB via a JAL instruction.
|
||||
// We need to generate a trampoline when an address is
|
||||
// currently unknown.
|
||||
|
@ -134,7 +136,7 @@ func trampoline(ctxt *Link, s loader.Sym) {
|
|||
}
|
||||
}
|
||||
// Runtime packages are laid out together.
|
||||
if isRuntimeDepPkg(ldr.SymPkg(s)) && isRuntimeDepPkg(ldr.SymPkg(rs)) {
|
||||
if isRuntimeDepPkg(ldr.SymPkg(s)) && isRuntimeDepPkg(ldr.SymPkg(rs)) && *flagRandLayout == 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
@ -2397,6 +2399,26 @@ func (ctxt *Link) textaddress() {
|
|||
|
||||
ldr := ctxt.loader
|
||||
|
||||
if *flagRandLayout != 0 {
|
||||
r := rand.New(rand.NewSource(*flagRandLayout))
|
||||
textp := ctxt.Textp
|
||||
i := 0
|
||||
// don't move the buildid symbol
|
||||
if len(textp) > 0 && ldr.SymName(textp[0]) == "go:buildid" {
|
||||
i++
|
||||
}
|
||||
// Skip over C symbols, as functions in a (C object) section must stay together.
|
||||
// TODO: maybe we can move a section as a whole.
|
||||
// Note: we load C symbols before Go symbols, so we can scan from the start.
|
||||
for i < len(textp) && (ldr.SubSym(textp[i]) != 0 || ldr.AttrSubSymbol(textp[i])) {
|
||||
i++
|
||||
}
|
||||
textp = textp[i:]
|
||||
r.Shuffle(len(textp), func(i, j int) {
|
||||
textp[i], textp[j] = textp[j], textp[i]
|
||||
})
|
||||
}
|
||||
|
||||
text := ctxt.xdefine("runtime.text", sym.STEXT, 0)
|
||||
etext := ctxt.xdefine("runtime.etext", sym.STEXT, 0)
|
||||
ldr.SetSymSect(text, sect)
|
||||
|
|
|
@ -201,7 +201,7 @@ func (d *deadcodePass) flood() {
|
|||
rs := r.Sym()
|
||||
if d.ldr.IsItab(rs) {
|
||||
// This relocation can also point at an itab, in which case it
|
||||
// means "the _type field of that itab".
|
||||
// means "the Type field of that itab".
|
||||
rs = decodeItabType(d.ldr, d.ctxt.Arch, rs)
|
||||
}
|
||||
if !d.ldr.IsGoType(rs) && !d.ctxt.linkShared {
|
||||
|
|
|
@ -301,8 +301,8 @@ func decodetypeGcprogShlib(ctxt *Link, data []byte) uint64 {
|
|||
return decodeInuxi(ctxt.Arch, data[2*int32(ctxt.Arch.PtrSize)+8+1*int32(ctxt.Arch.PtrSize):], ctxt.Arch.PtrSize)
|
||||
}
|
||||
|
||||
// decodeItabType returns the itab._type field from an itab.
|
||||
// decodeItabType returns the itab.Type field from an itab.
|
||||
func decodeItabType(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym {
|
||||
relocs := ldr.Relocs(symIdx)
|
||||
return decodeRelocSym(ldr, symIdx, &relocs, int32(arch.PtrSize))
|
||||
return decodeRelocSym(ldr, symIdx, &relocs, int32(abi.ITabTypeOff(arch.PtrSize)))
|
||||
}
|
||||
|
|
|
@ -1786,7 +1786,7 @@ func dwarfGenerateDebugInfo(ctxt *Link) {
|
|||
"type:internal/abi.SliceType",
|
||||
"type:internal/abi.StructType",
|
||||
"type:internal/abi.InterfaceType",
|
||||
"type:runtime.itab",
|
||||
"type:internal/abi.ITab",
|
||||
"type:internal/abi.Imethod"} {
|
||||
d.defgotype(d.lookupOrDiag(typ))
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ func TestRuntimeTypesPresent(t *testing.T) {
|
|||
"internal/abi.SliceType": true,
|
||||
"internal/abi.StructType": true,
|
||||
"internal/abi.InterfaceType": true,
|
||||
"runtime.itab": true,
|
||||
"internal/abi.ITab": true,
|
||||
}
|
||||
|
||||
found := findTypes(t, dwarf, want)
|
||||
|
|
|
@ -478,13 +478,11 @@ func (ctxt *Link) domacho() {
|
|||
if ctxt.LinkMode == LinkInternal && machoPlatform == PLATFORM_MACOS {
|
||||
var version uint32
|
||||
switch ctxt.Arch.Family {
|
||||
case sys.AMD64:
|
||||
case sys.ARM64, sys.AMD64:
|
||||
// This must be fairly recent for Apple signing (go.dev/issue/30488).
|
||||
// Having too old a version here was also implicated in some problems
|
||||
// calling into macOS libraries (go.dev/issue/56784).
|
||||
// In general this can be the most recent supported macOS version.
|
||||
version = 10<<16 | 13<<8 | 0<<0 // 10.13.0
|
||||
case sys.ARM64:
|
||||
version = 11<<16 | 0<<8 | 0<<0 // 11.0.0
|
||||
}
|
||||
ml := newMachoLoad(ctxt.Arch, LC_BUILD_VERSION, 4)
|
||||
|
|
|
@ -102,6 +102,7 @@ var (
|
|||
FlagTextAddr = flag.Int64("T", -1, "set the start address of text symbols")
|
||||
flagEntrySymbol = flag.String("E", "", "set `entry` symbol name")
|
||||
flagPruneWeakMap = flag.Bool("pruneweakmap", true, "prune weak mapinit refs")
|
||||
flagRandLayout = flag.Int64("randlayout", 0, "randomize function layout")
|
||||
cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
|
||||
memprofile = flag.String("memprofile", "", "write memory profile to `file`")
|
||||
memprofilerate = flag.Int64("memprofilerate", 0, "set runtime.MemProfileRate to `rate`")
|
||||
|
|
|
@ -827,9 +827,8 @@ func expandGoroot(s string) string {
|
|||
}
|
||||
|
||||
const (
|
||||
BUCKETSIZE = 256 * abi.MINFUNC
|
||||
SUBBUCKETS = 16
|
||||
SUBBUCKETSIZE = BUCKETSIZE / SUBBUCKETS
|
||||
SUBBUCKETSIZE = abi.FuncTabBucketSize / SUBBUCKETS
|
||||
NOIDX = 0x7fffffff
|
||||
)
|
||||
|
||||
|
@ -847,7 +846,7 @@ func (ctxt *Link) findfunctab(state *pclntab, container loader.Bitmap) {
|
|||
// that map to that subbucket.
|
||||
n := int32((max - min + SUBBUCKETSIZE - 1) / SUBBUCKETSIZE)
|
||||
|
||||
nbuckets := int32((max - min + BUCKETSIZE - 1) / BUCKETSIZE)
|
||||
nbuckets := int32((max - min + abi.FuncTabBucketSize - 1) / abi.FuncTabBucketSize)
|
||||
|
||||
size := 4*int64(nbuckets) + int64(n)
|
||||
|
||||
|
@ -878,7 +877,7 @@ func (ctxt *Link) findfunctab(state *pclntab, container loader.Bitmap) {
|
|||
q = ldr.SymValue(e)
|
||||
}
|
||||
|
||||
//print("%d: [%lld %lld] %s\n", idx, p, q, s->name);
|
||||
//fmt.Printf("%d: [%x %x] %s\n", idx, p, q, ldr.SymName(s))
|
||||
for ; p < q; p += SUBBUCKETSIZE {
|
||||
i = int((p - min) / SUBBUCKETSIZE)
|
||||
if indexes[i] > idx {
|
||||
|
|
|
@ -242,10 +242,6 @@ func parseArmAttributes(e binary.ByteOrder, data []byte) (found bool, ehdrFlags
|
|||
// object, and the returned ehdrFlags contains what this Load function computes.
|
||||
// TODO: find a better place for this logic.
|
||||
func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader, pkg string, length int64, pn string, initEhdrFlags uint32) (textp []loader.Sym, ehdrFlags uint32, err error) {
|
||||
newSym := func(name string, version int) loader.Sym {
|
||||
return l.CreateStaticSym(name)
|
||||
}
|
||||
lookup := l.LookupOrCreateCgoExport
|
||||
errorf := func(str string, args ...interface{}) ([]loader.Sym, uint32, error) {
|
||||
return nil, 0, fmt.Errorf("loadelf: %s: %v", pn, fmt.Sprintf(str, args...))
|
||||
}
|
||||
|
@ -515,7 +511,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader,
|
|||
}
|
||||
sectsymNames[name] = true
|
||||
|
||||
sb := l.MakeSymbolUpdater(lookup(name, localSymVersion))
|
||||
sb := l.MakeSymbolUpdater(l.LookupOrCreateCgoExport(name, localSymVersion))
|
||||
|
||||
switch sect.flags & (elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_EXECINSTR) {
|
||||
default:
|
||||
|
@ -556,7 +552,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader,
|
|||
|
||||
for i := 1; i < elfobj.nsymtab; i++ {
|
||||
var elfsym ElfSym
|
||||
if err := readelfsym(newSym, lookup, l, arch, elfobj, i, &elfsym, 1, localSymVersion); err != nil {
|
||||
if err := readelfsym(l, arch, elfobj, i, &elfsym, 1, localSymVersion); err != nil {
|
||||
return errorf("%s: malformed elf file: %v", pn, err)
|
||||
}
|
||||
symbols[i] = elfsym.sym
|
||||
|
@ -770,7 +766,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader,
|
|||
rSym = 0
|
||||
} else {
|
||||
var elfsym ElfSym
|
||||
if err := readelfsym(newSym, lookup, l, arch, elfobj, int(symIdx), &elfsym, 0, 0); err != nil {
|
||||
if err := readelfsym(l, arch, elfobj, int(symIdx), &elfsym, 0, 0); err != nil {
|
||||
return errorf("malformed elf file: %v", err)
|
||||
}
|
||||
elfsym.sym = symbols[symIdx]
|
||||
|
@ -847,7 +843,7 @@ func elfmap(elfobj *ElfObj, sect *ElfSect) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func readelfsym(newSym, lookup func(string, int) loader.Sym, l *loader.Loader, arch *sys.Arch, elfobj *ElfObj, i int, elfsym *ElfSym, needSym int, localSymVersion int) (err error) {
|
||||
func readelfsym(l *loader.Loader, arch *sys.Arch, elfobj *ElfObj, i int, elfsym *ElfSym, needSym int, localSymVersion int) (err error) {
|
||||
if i >= elfobj.nsymtab || i < 0 {
|
||||
err = fmt.Errorf("invalid elf symbol index")
|
||||
return err
|
||||
|
@ -898,7 +894,7 @@ func readelfsym(newSym, lookup func(string, int) loader.Sym, l *loader.Loader, a
|
|||
switch elfsym.bind {
|
||||
case elf.STB_GLOBAL:
|
||||
if needSym != 0 {
|
||||
s = lookup(elfsym.name, 0)
|
||||
s = l.LookupOrCreateCgoExport(elfsym.name, 0)
|
||||
|
||||
// for global scoped hidden symbols we should insert it into
|
||||
// symbol hash table, but mark them as hidden.
|
||||
|
@ -927,7 +923,7 @@ func readelfsym(newSym, lookup func(string, int) loader.Sym, l *loader.Loader, a
|
|||
// We need to be able to look this up,
|
||||
// so put it in the hash table.
|
||||
if needSym != 0 {
|
||||
s = lookup(elfsym.name, localSymVersion)
|
||||
s = l.LookupOrCreateCgoExport(elfsym.name, localSymVersion)
|
||||
l.SetAttrVisibilityHidden(s, true)
|
||||
}
|
||||
break
|
||||
|
@ -940,13 +936,13 @@ func readelfsym(newSym, lookup func(string, int) loader.Sym, l *loader.Loader, a
|
|||
// FIXME: pass empty string here for name? This would
|
||||
// reduce mem use, but also (possibly) make it harder
|
||||
// to debug problems.
|
||||
s = newSym(elfsym.name, localSymVersion)
|
||||
s = l.CreateStaticSym(elfsym.name)
|
||||
l.SetAttrVisibilityHidden(s, true)
|
||||
}
|
||||
|
||||
case elf.STB_WEAK:
|
||||
if needSym != 0 {
|
||||
s = lookup(elfsym.name, 0)
|
||||
s = l.LookupOrCreateCgoExport(elfsym.name, 0)
|
||||
if elfsym.other == 2 {
|
||||
l.SetAttrVisibilityHidden(s, true)
|
||||
}
|
||||
|
|
|
@ -348,7 +348,7 @@ func TestXFlag(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
var testMachOBuildVersionSrc = `
|
||||
var trivialSrc = `
|
||||
package main
|
||||
func main() { }
|
||||
`
|
||||
|
@ -361,7 +361,7 @@ func TestMachOBuildVersion(t *testing.T) {
|
|||
tmpdir := t.TempDir()
|
||||
|
||||
src := filepath.Join(tmpdir, "main.go")
|
||||
err := os.WriteFile(src, []byte(testMachOBuildVersionSrc), 0666)
|
||||
err := os.WriteFile(src, []byte(trivialSrc), 0666)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -388,9 +388,9 @@ func TestMachOBuildVersion(t *testing.T) {
|
|||
found := false
|
||||
const LC_BUILD_VERSION = 0x32
|
||||
checkMin := func(ver uint32) {
|
||||
major, minor := (ver>>16)&0xff, (ver>>8)&0xff
|
||||
if major != 10 || minor < 9 {
|
||||
t.Errorf("LC_BUILD_VERSION version %d.%d < 10.9", major, minor)
|
||||
major, minor, patch := (ver>>16)&0xff, (ver>>8)&0xff, (ver>>0)&0xff
|
||||
if major < 11 {
|
||||
t.Errorf("LC_BUILD_VERSION version %d.%d.%d < 11.0.0", major, minor, patch)
|
||||
}
|
||||
}
|
||||
for _, cmd := range exem.Loads {
|
||||
|
@ -1375,3 +1375,43 @@ func TestFlagS(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandLayout(t *testing.T) {
|
||||
// Test that the -randlayout flag randomizes function order and
|
||||
// generates a working binary.
|
||||
testenv.MustHaveGoBuild(t)
|
||||
|
||||
t.Parallel()
|
||||
|
||||
tmpdir := t.TempDir()
|
||||
|
||||
src := filepath.Join(tmpdir, "hello.go")
|
||||
err := os.WriteFile(src, []byte(trivialSrc), 0666)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var syms [2]string
|
||||
for i, seed := range []string{"123", "456"} {
|
||||
exe := filepath.Join(tmpdir, "hello"+seed+".exe")
|
||||
cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-randlayout="+seed, "-o", exe, src)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("build failed: %v\n%s", err, out)
|
||||
}
|
||||
cmd = testenv.Command(t, exe)
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
t.Fatalf("executable failed to run: %v\n%s", err, out)
|
||||
}
|
||||
cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe)
|
||||
out, err = cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("fail to run \"go tool nm\": %v\n%s", err, out)
|
||||
}
|
||||
syms[i] = string(out)
|
||||
}
|
||||
if syms[0] == syms[1] {
|
||||
t.Errorf("randlayout with different seeds produced same layout:\n%s\n===\n\n%s", syms[0], syms[1])
|
||||
}
|
||||
}
|
||||
|
|
187
src/cmd/preprofile/main.go
Normal file
187
src/cmd/preprofile/main.go
Normal file
|
@ -0,0 +1,187 @@
|
|||
// Copyright 2023 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.
|
||||
|
||||
// Preprofile handles pprof files.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// go tool preprofile [-v] [-o output] [-i (pprof)input]
|
||||
//
|
||||
//
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"fmt"
|
||||
"internal/profile"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// The current Go Compiler consumes significantly long compilation time when the PGO
|
||||
// is enabled. To optimize the existing flow and reduce build time of multiple Go
|
||||
// services, we create a standalone tool, PGO preprocessor, to extract information
|
||||
// from collected profiling files and to cache the WeightedCallGraph in one time
|
||||
// fashion. By adding the new tool to the Go compiler, it will reduce the time
|
||||
// of repeated profiling file parsing and avoid WeightedCallGraph reconstruction
|
||||
// in current Go Compiler.
|
||||
// The format of the pre-processed output is as follows.
|
||||
//
|
||||
// Header
|
||||
// caller_name
|
||||
// callee_name
|
||||
// "call site offset" "call edge weight"
|
||||
// ...
|
||||
// caller_name
|
||||
// callee_name
|
||||
// "call site offset" "call edge weight"
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintf(os.Stderr, "MUST have (pprof) input file \n")
|
||||
fmt.Fprintf(os.Stderr, "usage: go tool preprofile [-v] [-o output] [-i (pprof)input] \n\n")
|
||||
flag.PrintDefaults()
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
type NodeMapKey struct {
|
||||
CallerName string
|
||||
CalleeName string
|
||||
CallSiteOffset int // Line offset from function start line.
|
||||
}
|
||||
|
||||
func readPprofFile(profileFile string, outputFile string, verbose bool) bool {
|
||||
// open the pprof profile file
|
||||
f, err := os.Open(profileFile)
|
||||
if err != nil {
|
||||
log.Fatal("failed to open file " + profileFile)
|
||||
return false
|
||||
}
|
||||
defer f.Close()
|
||||
p, err := profile.Parse(f)
|
||||
if err != nil {
|
||||
log.Fatal("failed to Parse profile file.")
|
||||
return false
|
||||
}
|
||||
|
||||
if len(p.Sample) == 0 {
|
||||
// We accept empty profiles, but there is nothing to do.
|
||||
return false
|
||||
}
|
||||
|
||||
valueIndex := -1
|
||||
for i, s := range p.SampleType {
|
||||
// Samples count is the raw data collected, and CPU nanoseconds is just
|
||||
// a scaled version of it, so either one we can find is fine.
|
||||
if (s.Type == "samples" && s.Unit == "count") ||
|
||||
(s.Type == "cpu" && s.Unit == "nanoseconds") {
|
||||
valueIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if valueIndex == -1 {
|
||||
log.Fatal("failed to find CPU samples count or CPU nanoseconds value-types in profile.")
|
||||
return false
|
||||
}
|
||||
|
||||
// The processing here is equivalent to cmd/compile/internal/pgo.createNamedEdgeMap.
|
||||
g := profile.NewGraph(p, &profile.Options{
|
||||
SampleValue: func(v []int64) int64 { return v[valueIndex] },
|
||||
})
|
||||
|
||||
TotalEdgeWeight := int64(0)
|
||||
|
||||
NodeMap := make(map[NodeMapKey]int64)
|
||||
|
||||
for _, n := range g.Nodes {
|
||||
canonicalName := n.Info.Name
|
||||
// Create the key to the nodeMapKey.
|
||||
nodeinfo := NodeMapKey{
|
||||
CallerName: canonicalName,
|
||||
CallSiteOffset: n.Info.Lineno - n.Info.StartLine,
|
||||
}
|
||||
|
||||
if n.Info.StartLine == 0 {
|
||||
if verbose {
|
||||
log.Println("[PGO] warning: " + canonicalName + " relative line number is missing from the profile")
|
||||
}
|
||||
}
|
||||
|
||||
for _, e := range n.Out {
|
||||
TotalEdgeWeight += e.WeightValue()
|
||||
nodeinfo.CalleeName = e.Dest.Info.Name
|
||||
if w, ok := NodeMap[nodeinfo]; ok {
|
||||
w += e.WeightValue()
|
||||
} else {
|
||||
w = e.WeightValue()
|
||||
NodeMap[nodeinfo] = w
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var fNodeMap *os.File
|
||||
if outputFile == "" {
|
||||
fNodeMap = os.Stdout
|
||||
} else {
|
||||
dirPath := filepath.Dir(outputFile)
|
||||
_, err := os.Stat(dirPath)
|
||||
if err != nil {
|
||||
log.Fatal("Directory does not exist: ", dirPath)
|
||||
}
|
||||
base := filepath.Base(outputFile)
|
||||
outputFile = filepath.Join(dirPath, base)
|
||||
|
||||
// write out NodeMap to a file
|
||||
fNodeMap, err = os.Create(outputFile)
|
||||
if err != nil {
|
||||
log.Fatal("Error creating output file:", err)
|
||||
return false
|
||||
}
|
||||
|
||||
defer fNodeMap.Close() // Close the file when done writing
|
||||
}
|
||||
|
||||
w := bufio.NewWriter(fNodeMap)
|
||||
w.WriteString("GO PREPROFILE V1\n")
|
||||
count := 1
|
||||
separator := " "
|
||||
for key, element := range NodeMap {
|
||||
line := key.CallerName + "\n"
|
||||
w.WriteString(line)
|
||||
line = key.CalleeName + "\n"
|
||||
w.WriteString(line)
|
||||
line = strconv.Itoa(key.CallSiteOffset)
|
||||
line = line + separator + strconv.FormatInt(element, 10) + "\n"
|
||||
w.WriteString(line)
|
||||
w.Flush()
|
||||
count += 1
|
||||
}
|
||||
|
||||
if TotalEdgeWeight == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
var dumpCode = flag.String("o", "", "dump output file ")
|
||||
var input = flag.String("i", "", "input pprof file ")
|
||||
var verbose = flag.Bool("v", false, "verbose log")
|
||||
|
||||
func main() {
|
||||
log.SetFlags(0)
|
||||
log.SetPrefix("preprofile: ")
|
||||
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
if *input == "" {
|
||||
usage()
|
||||
} else {
|
||||
readPprofFile(*input, *dumpCode, *verbose)
|
||||
}
|
||||
}
|
|
@ -32,7 +32,7 @@ func TestCheckAPIFragments(t *testing.T) {
|
|||
docFS := os.DirFS(filepath.Join(root, "doc", "next"))
|
||||
// Check that each api/next file has a corresponding release note fragment.
|
||||
for _, apiFile := range files {
|
||||
if err := relnote.CheckAPIFile(rootFS, apiFile, docFS); err != nil {
|
||||
if err := relnote.CheckAPIFile(rootFS, apiFile, docFS, "doc/next"); err != nil {
|
||||
t.Errorf("%s: %v", apiFile, err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ import (
|
|||
"net/http"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
@ -25,31 +24,23 @@ import (
|
|||
// GoroutinesHandlerFunc returns a HandlerFunc that serves list of goroutine groups.
|
||||
func GoroutinesHandlerFunc(summaries map[tracev2.GoID]*trace.GoroutineSummary) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// goroutineGroup describes a group of goroutines grouped by start PC.
|
||||
// goroutineGroup describes a group of goroutines grouped by name.
|
||||
type goroutineGroup struct {
|
||||
ID uint64 // Unique identifier (PC).
|
||||
Name string // Start function.
|
||||
N int // Total number of goroutines in this group.
|
||||
ExecTime time.Duration // Total execution time of all goroutines in this group.
|
||||
}
|
||||
// Accumulate groups by PC.
|
||||
groupsByPC := make(map[uint64]goroutineGroup)
|
||||
// Accumulate groups by Name.
|
||||
groupsByName := make(map[string]goroutineGroup)
|
||||
for _, summary := range summaries {
|
||||
group := groupsByPC[summary.PC]
|
||||
group.ID = summary.PC
|
||||
group := groupsByName[summary.Name]
|
||||
group.Name = summary.Name
|
||||
group.N++
|
||||
group.ExecTime += summary.ExecTime
|
||||
groupsByPC[summary.PC] = group
|
||||
groupsByName[summary.Name] = group
|
||||
}
|
||||
var groups []goroutineGroup
|
||||
for pc, group := range groupsByPC {
|
||||
group.ID = pc
|
||||
// If goroutine didn't run during the trace (no sampled PC),
|
||||
// the v.ID and v.Name will be zero value.
|
||||
if group.ID == 0 && group.Name == "" {
|
||||
group.Name = "(Inactive, no stack trace sampled)"
|
||||
}
|
||||
for _, group := range groupsByName {
|
||||
groups = append(groups, group)
|
||||
}
|
||||
slices.SortFunc(groups, func(a, b goroutineGroup) int {
|
||||
|
@ -92,7 +83,7 @@ Click a start location to view more details about that group.<br>
|
|||
</tr>
|
||||
{{range $}}
|
||||
<tr>
|
||||
<td><code><a href="/goroutine?id={{.ID}}">{{.Name}}</a></code></td>
|
||||
<td><code><a href="/goroutine?name={{.Name}}">{{or .Name "(Inactive, no stack trace sampled)"}}</a></code></td>
|
||||
<td>{{.N}}</td>
|
||||
<td>{{.ExecTime}}</td>
|
||||
</tr>
|
||||
|
@ -106,11 +97,7 @@ Click a start location to view more details about that group.<br>
|
|||
// goroutines in a particular group.
|
||||
func GoroutineHandler(summaries map[tracev2.GoID]*trace.GoroutineSummary) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
pc, err := strconv.ParseUint(r.FormValue("id"), 10, 64)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("failed to parse id parameter '%v': %v", r.FormValue("id"), err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
goroutineName := r.FormValue("name")
|
||||
|
||||
type goroutine struct {
|
||||
*trace.GoroutineSummary
|
||||
|
@ -130,7 +117,7 @@ func GoroutineHandler(summaries map[tracev2.GoID]*trace.GoroutineSummary) http.H
|
|||
for _, summary := range summaries {
|
||||
totalExecTime += summary.ExecTime
|
||||
|
||||
if summary.PC != pc {
|
||||
if summary.Name != goroutineName {
|
||||
continue
|
||||
}
|
||||
nonOverlappingStats := summary.NonOverlappingStats()
|
||||
|
@ -198,9 +185,8 @@ func GoroutineHandler(summaries map[tracev2.GoID]*trace.GoroutineSummary) http.H
|
|||
}
|
||||
sort.Strings(allRangeStats)
|
||||
|
||||
err = templGoroutine.Execute(w, struct {
|
||||
err := templGoroutine.Execute(w, struct {
|
||||
Name string
|
||||
PC uint64
|
||||
N int
|
||||
ExecTimePercent string
|
||||
MaxTotal time.Duration
|
||||
|
@ -209,7 +195,6 @@ func GoroutineHandler(summaries map[tracev2.GoID]*trace.GoroutineSummary) http.H
|
|||
RangeStats []string
|
||||
}{
|
||||
Name: name,
|
||||
PC: pc,
|
||||
N: len(goroutines),
|
||||
ExecTimePercent: execTimePercent,
|
||||
MaxTotal: maxTotalTime,
|
||||
|
@ -339,19 +324,19 @@ Table of contents
|
|||
</tr>
|
||||
<tr>
|
||||
<td>Network wait profile:</td>
|
||||
<td> <a href="/io?id={{.PC}}">graph</a> <a href="/io?id={{.PC}}&raw=1" download="io.profile">(download)</a></td>
|
||||
<td> <a href="/io?name={{.Name}}">graph</a> <a href="/io?name={{.Name}}&raw=1" download="io.profile">(download)</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Sync block profile:</td>
|
||||
<td> <a href="/block?id={{.PC}}">graph</a> <a href="/block?id={{.PC}}&raw=1" download="block.profile">(download)</a></td>
|
||||
<td> <a href="/block?name={{.Name}}">graph</a> <a href="/block?name={{.Name}}&raw=1" download="block.profile">(download)</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Syscall profile:</td>
|
||||
<td> <a href="/syscall?id={{.PC}}">graph</a> <a href="/syscall?id={{.PC}}&raw=1" download="syscall.profile">(download)</a></td>
|
||||
<td> <a href="/syscall?name={{.Name}}">graph</a> <a href="/syscall?name={{.Name}}&raw=1" download="syscall.profile">(download)</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Scheduler wait profile:</td>
|
||||
<td> <a href="/sched?id={{.PC}}">graph</a> <a href="/sched?id={{.PC}}&raw=1" download="sched.profile">(download)</a></td>
|
||||
<td> <a href="/sched?name={{.Name}}">graph</a> <a href="/sched?name={{.Name}}&raw=1" download="sched.profile">(download)</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
|
|
@ -167,8 +167,8 @@ func checkNetworkUnblock(t *testing.T, data format.Data) {
|
|||
if netBlockEv == nil {
|
||||
t.Error("failed to find a network unblock")
|
||||
}
|
||||
if count == 0 || count > 2 {
|
||||
t.Errorf("found too many network block events: want 1 or 2, found %d", count)
|
||||
if count == 0 {
|
||||
t.Errorf("found zero network block events, want at least one")
|
||||
}
|
||||
// TODO(mknyszek): Check for the flow of this event to some slice event of a goroutine running.
|
||||
}
|
||||
|
|
|
@ -14,15 +14,14 @@ import (
|
|||
tracev2 "internal/trace/v2"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func pprofByGoroutine(compute computePprofFunc, t *parsedTrace) traceviewer.ProfileFunc {
|
||||
return func(r *http.Request) ([]traceviewer.ProfileRecord, error) {
|
||||
id := r.FormValue("id")
|
||||
gToIntervals, err := pprofMatchingGoroutines(id, t)
|
||||
name := r.FormValue("name")
|
||||
gToIntervals, err := pprofMatchingGoroutines(name, t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -44,20 +43,12 @@ func pprofByRegion(compute computePprofFunc, t *parsedTrace) traceviewer.Profile
|
|||
}
|
||||
}
|
||||
|
||||
// pprofMatchingGoroutines parses the goroutine type id string (i.e. pc)
|
||||
// and returns the ids of goroutines of the matching type and its interval.
|
||||
// pprofMatchingGoroutines returns the ids of goroutines of the matching name and its interval.
|
||||
// If the id string is empty, returns nil without an error.
|
||||
func pprofMatchingGoroutines(id string, t *parsedTrace) (map[tracev2.GoID][]interval, error) {
|
||||
if id == "" {
|
||||
return nil, nil
|
||||
}
|
||||
pc, err := strconv.ParseUint(id, 10, 64) // id is string
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid goroutine type: %v", id)
|
||||
}
|
||||
func pprofMatchingGoroutines(name string, t *parsedTrace) (map[tracev2.GoID][]interval, error) {
|
||||
res := make(map[tracev2.GoID][]interval)
|
||||
for _, g := range t.summary.Goroutines {
|
||||
if g.PC != pc {
|
||||
if g.Name != name {
|
||||
continue
|
||||
}
|
||||
endTime := g.EndTime
|
||||
|
@ -66,8 +57,8 @@ func pprofMatchingGoroutines(id string, t *parsedTrace) (map[tracev2.GoID][]inte
|
|||
}
|
||||
res[g.ID] = []interval{{start: g.StartTime, end: endTime}}
|
||||
}
|
||||
if len(res) == 0 && id != "" {
|
||||
return nil, fmt.Errorf("failed to find matching goroutines for ID: %s", id)
|
||||
if len(res) == 0 {
|
||||
return nil, fmt.Errorf("failed to find matching goroutines for name: %s", name)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
|
8819
src/cmd/trace/v2/testdata/go122.test
vendored
8819
src/cmd/trace/v2/testdata/go122.test
vendored
File diff suppressed because it is too large
Load diff
1
src/cmd/vendor/github.com/google/pprof/driver/driver.go
generated
vendored
1
src/cmd/vendor/github.com/google/pprof/driver/driver.go
generated
vendored
|
@ -189,6 +189,7 @@ type Frame struct {
|
|||
Func string // name of function
|
||||
File string // source file name
|
||||
Line int // line in file
|
||||
Column int // column in file
|
||||
}
|
||||
|
||||
// A Sym describes a single symbol in an object file.
|
||||
|
|
22
src/cmd/vendor/github.com/google/pprof/internal/binutils/addr2liner_llvm.go
generated
vendored
22
src/cmd/vendor/github.com/google/pprof/internal/binutils/addr2liner_llvm.go
generated
vendored
|
@ -129,6 +129,7 @@ func (d *llvmSymbolizer) readFrame() (plugin.Frame, bool) {
|
|||
}
|
||||
|
||||
linenumber := 0
|
||||
columnnumber := 0
|
||||
// The llvm-symbolizer outputs the <file_name>:<line_number>:<column_number>.
|
||||
// When it cannot identify the source code location, it outputs "??:0:0".
|
||||
// Older versions output just the filename and line number, so we check for
|
||||
|
@ -137,22 +138,27 @@ func (d *llvmSymbolizer) readFrame() (plugin.Frame, bool) {
|
|||
fileline = ""
|
||||
} else {
|
||||
switch split := strings.Split(fileline, ":"); len(split) {
|
||||
case 1:
|
||||
// filename
|
||||
fileline = split[0]
|
||||
case 2, 3:
|
||||
// filename:line , or
|
||||
// filename:line:disc , or
|
||||
fileline = split[0]
|
||||
case 3:
|
||||
// filename:line:column
|
||||
if col, err := strconv.Atoi(split[2]); err == nil {
|
||||
columnnumber = col
|
||||
}
|
||||
fallthrough
|
||||
case 2:
|
||||
// filename:line
|
||||
if line, err := strconv.Atoi(split[1]); err == nil {
|
||||
linenumber = line
|
||||
}
|
||||
fallthrough
|
||||
case 1:
|
||||
// filename
|
||||
fileline = split[0]
|
||||
default:
|
||||
// Unrecognized, ignore
|
||||
}
|
||||
}
|
||||
|
||||
return plugin.Frame{Func: funcname, File: fileline, Line: linenumber}, false
|
||||
return plugin.Frame{Func: funcname, File: fileline, Line: linenumber, Column: columnnumber}, false
|
||||
}
|
||||
|
||||
// addrInfo returns the stack frame information for a specific program
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue