mirror of https://github.com/golang/go.git
Merge remote-tracking branch 'origin' into hotfix/atomic-and-or-wasm-dup
Change-Id: I256e2c89ffe8dc5ee5a7fe16f25a1cbd41ffd592
This commit is contained in:
commit
92736a6e34
|
|
@ -159,6 +159,12 @@ Go 1.22 changed the default TLS cipher suites used by clients and servers when
|
|||
not explicitly configured, removing the cipher suites which used RSA based key
|
||||
exchange. The default can be revert using the [`tlsrsakex` setting](/pkg/crypto/tls/#Config).
|
||||
|
||||
Go 1.22 disabled
|
||||
[`ConnectionState.ExportKeyingMaterial`](/pkg/crypto/tls/#ConnectionState.ExportKeyingMaterial)
|
||||
when the connection supports neither TLS 1.3 nor Extended Master Secret
|
||||
(implemented in Go 1.21). It can be reenabled with the [`tlsunsafeekm`
|
||||
setting](/pkg/crypto/tls/#ConnectionState.ExportKeyingMaterial).
|
||||
|
||||
### Go 1.21
|
||||
|
||||
Go 1.21 made it a run-time error to call `panic` with a nil interface value,
|
||||
|
|
|
|||
|
|
@ -633,6 +633,56 @@ modifying or saving the FPCR.
|
|||
Functions are allowed to modify it between calls (as long as they
|
||||
restore it), but as of this writing Go code never does.
|
||||
|
||||
### loong64 architecture
|
||||
|
||||
The loong64 architecture uses R4 – R19 for integer arguments and integer results.
|
||||
|
||||
It uses F0 – F15 for floating-point arguments and results.
|
||||
|
||||
Registers R20 - R21, R23 – R28, R30 - R31, F16 – F31 are permanent scratch registers.
|
||||
|
||||
Register R2 is reserved and never used.
|
||||
|
||||
Register R20, R21 is Used by runtime.duffcopy, runtime.duffzero.
|
||||
|
||||
Special-purpose registers used within Go generated code and Go assembly code
|
||||
are as follows:
|
||||
|
||||
| Register | Call meaning | Return meaning | Body meaning |
|
||||
| --- | --- | --- | --- |
|
||||
| R0 | Zero value | Same | Same |
|
||||
| R1 | Link register | Link register | Scratch |
|
||||
| R3 | Stack pointer | Same | Same |
|
||||
| R20,R21 | Scratch | Scratch | Used by duffcopy, duffzero |
|
||||
| R22 | Current goroutine | Same | Same |
|
||||
| R29 | Closure context pointer | Same | Same |
|
||||
| R30, R31 | used by the assembler | Same | Same |
|
||||
|
||||
*Rationale*: These register meanings are compatible with Go’s stack-based
|
||||
calling convention.
|
||||
|
||||
#### Stack layout
|
||||
|
||||
The stack pointer, R3, grows down and is aligned to 8 bytes.
|
||||
|
||||
A function's stack frame, after the frame is created, is laid out as
|
||||
follows:
|
||||
|
||||
+------------------------------+
|
||||
| ... locals ... |
|
||||
| ... outgoing arguments ... |
|
||||
| return PC | ← R3 points to
|
||||
+------------------------------+ ↓ lower addresses
|
||||
|
||||
This stack layout is used by both register-based (ABIInternal) and
|
||||
stack-based (ABI0) calling conventions.
|
||||
|
||||
The "return PC" is loaded to the link register, R1, as part of the
|
||||
loong64 `JAL` operation.
|
||||
|
||||
#### Flags
|
||||
All bits in CSR are system flags and are not modified by Go.
|
||||
|
||||
### ppc64 architecture
|
||||
|
||||
The ppc64 architecture uses R3 – R10 and R14 – R17 for integer arguments
|
||||
|
|
|
|||
|
|
@ -98,12 +98,13 @@ func AnalyzeFunc(fn *ir.Func, canInline func(*ir.Func), budgetForFunc func(*ir.F
|
|||
// inlinable; if it is over the default hairyness 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)
|
||||
for i := len(funcs) - 1; i >= 0; i-- {
|
||||
f := funcs[i]
|
||||
if f.OClosure != nil && !f.InlinabilityChecked() {
|
||||
canInline(f)
|
||||
}
|
||||
funcProps := analyzeFunc(f, inlineMaxBudget)
|
||||
funcProps := analyzeFunc(f, inlineMaxBudget, nameFinder)
|
||||
revisitInlinability(f, funcProps, budgetForFunc)
|
||||
if f.Inl != nil {
|
||||
f.Inl.Properties = funcProps.SerializeToString()
|
||||
|
|
@ -122,11 +123,11 @@ func TearDown() {
|
|||
scoreCallsCache.csl = nil
|
||||
}
|
||||
|
||||
func analyzeFunc(fn *ir.Func, inlineMaxBudget int) *FuncProps {
|
||||
func analyzeFunc(fn *ir.Func, inlineMaxBudget int, nf *nameFinder) *FuncProps {
|
||||
if funcInlHeur, ok := fpmap[fn]; ok {
|
||||
return funcInlHeur.props
|
||||
}
|
||||
funcProps, fcstab := computeFuncProps(fn, inlineMaxBudget)
|
||||
funcProps, fcstab := computeFuncProps(fn, inlineMaxBudget, nf)
|
||||
file, line := fnFileLine(fn)
|
||||
entry := fnInlHeur{
|
||||
fname: fn.Sym().Name,
|
||||
|
|
@ -163,7 +164,7 @@ func revisitInlinability(fn *ir.Func, funcProps *FuncProps, budgetForFunc func(*
|
|||
// computeFuncProps examines the Go function 'fn' and computes for it
|
||||
// a function "properties" object, to be used to drive inlining
|
||||
// heuristics. See comments on the FuncProps type for more info.
|
||||
func computeFuncProps(fn *ir.Func, inlineMaxBudget int) (*FuncProps, CallSiteTab) {
|
||||
func computeFuncProps(fn *ir.Func, inlineMaxBudget int, nf *nameFinder) (*FuncProps, CallSiteTab) {
|
||||
if debugTrace&debugTraceFuncs != 0 {
|
||||
fmt.Fprintf(os.Stderr, "=-= starting analysis of func %v:\n%+v\n",
|
||||
fn, fn)
|
||||
|
|
@ -171,13 +172,13 @@ func computeFuncProps(fn *ir.Func, inlineMaxBudget int) (*FuncProps, CallSiteTab
|
|||
funcProps := new(FuncProps)
|
||||
ffa := makeFuncFlagsAnalyzer(fn)
|
||||
analyzers := []propAnalyzer{ffa}
|
||||
analyzers = addResultsAnalyzer(fn, analyzers, funcProps, inlineMaxBudget)
|
||||
analyzers = addParamsAnalyzer(fn, analyzers, funcProps)
|
||||
analyzers = addResultsAnalyzer(fn, analyzers, funcProps, inlineMaxBudget, nf)
|
||||
analyzers = addParamsAnalyzer(fn, analyzers, funcProps, nf)
|
||||
runAnalyzersOnFunction(fn, analyzers)
|
||||
for _, a := range analyzers {
|
||||
a.setResults(funcProps)
|
||||
}
|
||||
cstab := computeCallSiteTable(fn, fn.Body, nil, ffa.panicPathTable(), 0)
|
||||
cstab := computeCallSiteTable(fn, fn.Body, nil, ffa.panicPathTable(), 0, nf)
|
||||
return funcProps, cstab
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,23 +14,37 @@ import (
|
|||
)
|
||||
|
||||
type callSiteAnalyzer struct {
|
||||
fn *ir.Func
|
||||
*nameFinder
|
||||
}
|
||||
|
||||
type callSiteTableBuilder struct {
|
||||
fn *ir.Func
|
||||
*nameFinder
|
||||
cstab CallSiteTab
|
||||
fn *ir.Func
|
||||
ptab map[ir.Node]pstate
|
||||
nstack []ir.Node
|
||||
loopNest int
|
||||
isInit bool
|
||||
}
|
||||
|
||||
func makeCallSiteAnalyzer(fn *ir.Func, cstab CallSiteTab, ptab map[ir.Node]pstate, loopNestingLevel int) *callSiteAnalyzer {
|
||||
isInit := fn.IsPackageInit() || strings.HasPrefix(fn.Sym().Name, "init.")
|
||||
func makeCallSiteAnalyzer(fn *ir.Func) *callSiteAnalyzer {
|
||||
return &callSiteAnalyzer{
|
||||
fn: fn,
|
||||
cstab: cstab,
|
||||
ptab: ptab,
|
||||
isInit: isInit,
|
||||
loopNest: loopNestingLevel,
|
||||
nstack: []ir.Node{fn},
|
||||
fn: fn,
|
||||
nameFinder: newNameFinder(fn),
|
||||
}
|
||||
}
|
||||
|
||||
func makeCallSiteTableBuilder(fn *ir.Func, cstab CallSiteTab, ptab map[ir.Node]pstate, loopNestingLevel int, nf *nameFinder) *callSiteTableBuilder {
|
||||
isInit := fn.IsPackageInit() || strings.HasPrefix(fn.Sym().Name, "init.")
|
||||
return &callSiteTableBuilder{
|
||||
fn: fn,
|
||||
cstab: cstab,
|
||||
ptab: ptab,
|
||||
isInit: isInit,
|
||||
loopNest: loopNestingLevel,
|
||||
nstack: []ir.Node{fn},
|
||||
nameFinder: nf,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -39,22 +53,22 @@ func makeCallSiteAnalyzer(fn *ir.Func, cstab CallSiteTab, ptab map[ir.Node]pstat
|
|||
// specific subtree within the AST for a function. The main intended
|
||||
// use cases are for 'region' to be either A) an entire function body,
|
||||
// or B) an inlined call expression.
|
||||
func computeCallSiteTable(fn *ir.Func, region ir.Nodes, cstab CallSiteTab, ptab map[ir.Node]pstate, loopNestingLevel int) CallSiteTab {
|
||||
csa := makeCallSiteAnalyzer(fn, cstab, ptab, loopNestingLevel)
|
||||
func computeCallSiteTable(fn *ir.Func, region ir.Nodes, cstab CallSiteTab, ptab map[ir.Node]pstate, loopNestingLevel int, nf *nameFinder) CallSiteTab {
|
||||
cstb := makeCallSiteTableBuilder(fn, cstab, ptab, loopNestingLevel, nf)
|
||||
var doNode func(ir.Node) bool
|
||||
doNode = func(n ir.Node) bool {
|
||||
csa.nodeVisitPre(n)
|
||||
cstb.nodeVisitPre(n)
|
||||
ir.DoChildren(n, doNode)
|
||||
csa.nodeVisitPost(n)
|
||||
cstb.nodeVisitPost(n)
|
||||
return false
|
||||
}
|
||||
for _, n := range region {
|
||||
doNode(n)
|
||||
}
|
||||
return csa.cstab
|
||||
return cstb.cstab
|
||||
}
|
||||
|
||||
func (csa *callSiteAnalyzer) flagsForNode(call *ir.CallExpr) CSPropBits {
|
||||
func (cstb *callSiteTableBuilder) flagsForNode(call *ir.CallExpr) CSPropBits {
|
||||
var r CSPropBits
|
||||
|
||||
if debugTrace&debugTraceCalls != 0 {
|
||||
|
|
@ -63,21 +77,21 @@ func (csa *callSiteAnalyzer) flagsForNode(call *ir.CallExpr) CSPropBits {
|
|||
}
|
||||
|
||||
// Set a bit if this call is within a loop.
|
||||
if csa.loopNest > 0 {
|
||||
if cstb.loopNest > 0 {
|
||||
r |= CallSiteInLoop
|
||||
}
|
||||
|
||||
// Set a bit if the call is within an init function (either
|
||||
// compiler-generated or user-written).
|
||||
if csa.isInit {
|
||||
if cstb.isInit {
|
||||
r |= CallSiteInInitFunc
|
||||
}
|
||||
|
||||
// Decide whether to apply the panic path heuristic. Hack: don't
|
||||
// apply this heuristic in the function "main.main" (mostly just
|
||||
// to avoid annoying users).
|
||||
if !isMainMain(csa.fn) {
|
||||
r = csa.determinePanicPathBits(call, r)
|
||||
if !isMainMain(cstb.fn) {
|
||||
r = cstb.determinePanicPathBits(call, r)
|
||||
}
|
||||
|
||||
return r
|
||||
|
|
@ -88,15 +102,15 @@ func (csa *callSiteAnalyzer) flagsForNode(call *ir.CallExpr) CSPropBits {
|
|||
// panic/exit. Do this by walking back up the node stack to see if we
|
||||
// can find either A) an enclosing panic, or B) a statement node that
|
||||
// we've determined leads to a panic/exit.
|
||||
func (csa *callSiteAnalyzer) determinePanicPathBits(call ir.Node, r CSPropBits) CSPropBits {
|
||||
csa.nstack = append(csa.nstack, call)
|
||||
func (cstb *callSiteTableBuilder) determinePanicPathBits(call ir.Node, r CSPropBits) CSPropBits {
|
||||
cstb.nstack = append(cstb.nstack, call)
|
||||
defer func() {
|
||||
csa.nstack = csa.nstack[:len(csa.nstack)-1]
|
||||
cstb.nstack = cstb.nstack[:len(cstb.nstack)-1]
|
||||
}()
|
||||
|
||||
for ri := range csa.nstack[:len(csa.nstack)-1] {
|
||||
i := len(csa.nstack) - ri - 1
|
||||
n := csa.nstack[i]
|
||||
for ri := range cstb.nstack[:len(cstb.nstack)-1] {
|
||||
i := len(cstb.nstack) - ri - 1
|
||||
n := cstb.nstack[i]
|
||||
_, isCallExpr := n.(*ir.CallExpr)
|
||||
_, isStmt := n.(ir.Stmt)
|
||||
if isCallExpr {
|
||||
|
|
@ -104,7 +118,7 @@ func (csa *callSiteAnalyzer) determinePanicPathBits(call ir.Node, r CSPropBits)
|
|||
}
|
||||
|
||||
if debugTrace&debugTraceCalls != 0 {
|
||||
ps, inps := csa.ptab[n]
|
||||
ps, inps := cstb.ptab[n]
|
||||
fmt.Fprintf(os.Stderr, "=-= callpar %d op=%s ps=%s inptab=%v stmt=%v\n", i, n.Op().String(), ps.String(), inps, isStmt)
|
||||
}
|
||||
|
||||
|
|
@ -112,7 +126,7 @@ func (csa *callSiteAnalyzer) determinePanicPathBits(call ir.Node, r CSPropBits)
|
|||
r |= CallSiteOnPanicPath
|
||||
break
|
||||
}
|
||||
if v, ok := csa.ptab[n]; ok {
|
||||
if v, ok := cstb.ptab[n]; ok {
|
||||
if v == psCallsPanic {
|
||||
r |= CallSiteOnPanicPath
|
||||
break
|
||||
|
|
@ -126,16 +140,15 @@ func (csa *callSiteAnalyzer) determinePanicPathBits(call ir.Node, r CSPropBits)
|
|||
}
|
||||
|
||||
// propsForArg returns property bits for a given call argument expression arg.
|
||||
func (csa *callSiteAnalyzer) propsForArg(arg ir.Node) ActualExprPropBits {
|
||||
_, islit := isLiteral(arg)
|
||||
if islit {
|
||||
func (cstb *callSiteTableBuilder) propsForArg(arg ir.Node) ActualExprPropBits {
|
||||
if cval := cstb.constValue(arg); cval != nil {
|
||||
return ActualExprConstant
|
||||
}
|
||||
if isConcreteConvIface(arg) {
|
||||
if cstb.isConcreteConvIface(arg) {
|
||||
return ActualExprIsConcreteConvIface
|
||||
}
|
||||
fname, isfunc, _ := isFuncName(arg)
|
||||
if isfunc {
|
||||
fname := cstb.funcName(arg)
|
||||
if fname != nil {
|
||||
if fn := fname.Func; fn != nil && typecheck.HaveInlineBody(fn) {
|
||||
return ActualExprIsInlinableFunc
|
||||
}
|
||||
|
|
@ -149,11 +162,11 @@ func (csa *callSiteAnalyzer) propsForArg(arg ir.Node) ActualExprPropBits {
|
|||
// expression; these will be stored in the CallSite object for a given
|
||||
// call and then consulted when scoring. If no arg has any interesting
|
||||
// properties we try to save some space and return a nil slice.
|
||||
func (csa *callSiteAnalyzer) argPropsForCall(ce *ir.CallExpr) []ActualExprPropBits {
|
||||
func (cstb *callSiteTableBuilder) argPropsForCall(ce *ir.CallExpr) []ActualExprPropBits {
|
||||
rv := make([]ActualExprPropBits, len(ce.Args))
|
||||
somethingInteresting := false
|
||||
for idx := range ce.Args {
|
||||
argProp := csa.propsForArg(ce.Args[idx])
|
||||
argProp := cstb.propsForArg(ce.Args[idx])
|
||||
somethingInteresting = somethingInteresting || (argProp != 0)
|
||||
rv[idx] = argProp
|
||||
}
|
||||
|
|
@ -163,9 +176,9 @@ func (csa *callSiteAnalyzer) argPropsForCall(ce *ir.CallExpr) []ActualExprPropBi
|
|||
return rv
|
||||
}
|
||||
|
||||
func (csa *callSiteAnalyzer) addCallSite(callee *ir.Func, call *ir.CallExpr) {
|
||||
flags := csa.flagsForNode(call)
|
||||
argProps := csa.argPropsForCall(call)
|
||||
func (cstb *callSiteTableBuilder) addCallSite(callee *ir.Func, call *ir.CallExpr) {
|
||||
flags := cstb.flagsForNode(call)
|
||||
argProps := cstb.argPropsForCall(call)
|
||||
if debugTrace&debugTraceCalls != 0 {
|
||||
fmt.Fprintf(os.Stderr, "=-= props %+v for call %v\n", argProps, call)
|
||||
}
|
||||
|
|
@ -173,12 +186,12 @@ func (csa *callSiteAnalyzer) addCallSite(callee *ir.Func, call *ir.CallExpr) {
|
|||
cs := &CallSite{
|
||||
Call: call,
|
||||
Callee: callee,
|
||||
Assign: csa.containingAssignment(call),
|
||||
Assign: cstb.containingAssignment(call),
|
||||
ArgProps: argProps,
|
||||
Flags: flags,
|
||||
ID: uint(len(csa.cstab)),
|
||||
ID: uint(len(cstb.cstab)),
|
||||
}
|
||||
if _, ok := csa.cstab[call]; ok {
|
||||
if _, ok := cstb.cstab[call]; ok {
|
||||
fmt.Fprintf(os.Stderr, "*** cstab duplicate entry at: %s\n",
|
||||
fmtFullPos(call.Pos()))
|
||||
fmt.Fprintf(os.Stderr, "*** call: %+v\n", call)
|
||||
|
|
@ -189,38 +202,38 @@ func (csa *callSiteAnalyzer) addCallSite(callee *ir.Func, call *ir.CallExpr) {
|
|||
// on heuristics.
|
||||
cs.Score = int(callee.Inl.Cost)
|
||||
|
||||
if csa.cstab == nil {
|
||||
csa.cstab = make(CallSiteTab)
|
||||
if cstb.cstab == nil {
|
||||
cstb.cstab = make(CallSiteTab)
|
||||
}
|
||||
csa.cstab[call] = cs
|
||||
cstb.cstab[call] = cs
|
||||
if debugTrace&debugTraceCalls != 0 {
|
||||
fmt.Fprintf(os.Stderr, "=-= added callsite: caller=%v callee=%v n=%s\n",
|
||||
csa.fn, callee, fmtFullPos(call.Pos()))
|
||||
cstb.fn, callee, fmtFullPos(call.Pos()))
|
||||
}
|
||||
}
|
||||
|
||||
func (csa *callSiteAnalyzer) nodeVisitPre(n ir.Node) {
|
||||
func (cstb *callSiteTableBuilder) nodeVisitPre(n ir.Node) {
|
||||
switch n.Op() {
|
||||
case ir.ORANGE, ir.OFOR:
|
||||
if !hasTopLevelLoopBodyReturnOrBreak(loopBody(n)) {
|
||||
csa.loopNest++
|
||||
cstb.loopNest++
|
||||
}
|
||||
case ir.OCALLFUNC:
|
||||
ce := n.(*ir.CallExpr)
|
||||
callee := pgo.DirectCallee(ce.Fun)
|
||||
if callee != nil && callee.Inl != nil {
|
||||
csa.addCallSite(callee, ce)
|
||||
cstb.addCallSite(callee, ce)
|
||||
}
|
||||
}
|
||||
csa.nstack = append(csa.nstack, n)
|
||||
cstb.nstack = append(cstb.nstack, n)
|
||||
}
|
||||
|
||||
func (csa *callSiteAnalyzer) nodeVisitPost(n ir.Node) {
|
||||
csa.nstack = csa.nstack[:len(csa.nstack)-1]
|
||||
func (cstb *callSiteTableBuilder) nodeVisitPost(n ir.Node) {
|
||||
cstb.nstack = cstb.nstack[:len(cstb.nstack)-1]
|
||||
switch n.Op() {
|
||||
case ir.ORANGE, ir.OFOR:
|
||||
if !hasTopLevelLoopBodyReturnOrBreak(loopBody(n)) {
|
||||
csa.loopNest--
|
||||
cstb.loopNest--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -281,8 +294,8 @@ func hasTopLevelLoopBodyReturnOrBreak(loopBody ir.Nodes) bool {
|
|||
// call to a pair of auto-temps, then the second one assigning the
|
||||
// auto-temps to the user-visible vars. This helper will return the
|
||||
// second (outer) of these two.
|
||||
func (csa *callSiteAnalyzer) containingAssignment(n ir.Node) ir.Node {
|
||||
parent := csa.nstack[len(csa.nstack)-1]
|
||||
func (cstb *callSiteTableBuilder) containingAssignment(n ir.Node) ir.Node {
|
||||
parent := cstb.nstack[len(cstb.nstack)-1]
|
||||
|
||||
// assignsOnlyAutoTemps returns TRUE of the specified OAS2FUNC
|
||||
// node assigns only auto-temps.
|
||||
|
|
@ -315,12 +328,12 @@ func (csa *callSiteAnalyzer) containingAssignment(n ir.Node) ir.Node {
|
|||
// OAS1({x,y},OCONVNOP(OAS2FUNC({auto1,auto2},OCALLFUNC(bar))))
|
||||
//
|
||||
if assignsOnlyAutoTemps(parent) {
|
||||
par2 := csa.nstack[len(csa.nstack)-2]
|
||||
par2 := cstb.nstack[len(cstb.nstack)-2]
|
||||
if par2.Op() == ir.OAS2 {
|
||||
return par2
|
||||
}
|
||||
if par2.Op() == ir.OCONVNOP {
|
||||
par3 := csa.nstack[len(csa.nstack)-3]
|
||||
par3 := cstb.nstack[len(cstb.nstack)-3]
|
||||
if par3.Op() == ir.OAS2 {
|
||||
return par3
|
||||
}
|
||||
|
|
@ -378,18 +391,23 @@ func UpdateCallsiteTable(callerfn *ir.Func, n *ir.CallExpr, ic *ir.InlinedCallEx
|
|||
loopNestLevel = 1
|
||||
}
|
||||
ptab := map[ir.Node]pstate{ic: icp}
|
||||
icstab := computeCallSiteTable(callerfn, ic.Body, nil, ptab, loopNestLevel)
|
||||
nf := newNameFinder(nil)
|
||||
icstab := computeCallSiteTable(callerfn, ic.Body, nil, ptab, loopNestLevel, nf)
|
||||
|
||||
// Record parent callsite. This is primarily for debug output.
|
||||
for _, cs := range icstab {
|
||||
cs.parent = oldcs
|
||||
}
|
||||
|
||||
// Score the calls in the inlined body. Note the setting of "doCallResults"
|
||||
// to false here: at the moment there isn't any easy way to localize
|
||||
// or region-ize the work done by "rescoreBasedOnCallResultUses", which
|
||||
// currently does a walk over the entire function to look for uses
|
||||
// of a given set of results.
|
||||
// Score the calls in the inlined body. Note the setting of
|
||||
// "doCallResults" to false here: at the moment there isn't any
|
||||
// easy way to localize or region-ize the work done by
|
||||
// "rescoreBasedOnCallResultUses", which currently does a walk
|
||||
// over the entire function to look for uses of a given set of
|
||||
// results. Similarly we're passing nil to makeCallSiteAnalyzer,
|
||||
// so as to run name finding without the use of static value &
|
||||
// friends.
|
||||
csa := makeCallSiteAnalyzer(nil)
|
||||
const doCallResults = false
|
||||
scoreCallsRegion(callerfn, ic.Body, icstab, doCallResults, ic)
|
||||
csa.scoreCallsRegion(callerfn, ic.Body, icstab, doCallResults, ic)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,34 +66,24 @@ func (ffa *funcFlagsAnalyzer) setResults(funcProps *FuncProps) {
|
|||
funcProps.Flags = rv
|
||||
}
|
||||
|
||||
func (ffa *funcFlagsAnalyzer) getstate(n ir.Node) pstate {
|
||||
val, ok := ffa.nstate[n]
|
||||
if !ok {
|
||||
base.Fatalf("funcFlagsAnalyzer: fn %q node %s line %s: internal error, no setting for node:\n%+v\n", ffa.fn.Sym().Name, n.Op().String(), ir.Line(n), n)
|
||||
}
|
||||
return val
|
||||
func (ffa *funcFlagsAnalyzer) getState(n ir.Node) pstate {
|
||||
return ffa.nstate[n]
|
||||
}
|
||||
|
||||
func (ffa *funcFlagsAnalyzer) setstate(n ir.Node, st pstate) {
|
||||
if _, ok := ffa.nstate[n]; ok {
|
||||
base.Fatalf("funcFlagsAnalyzer: fn %q internal error, existing setting for node:\n%+v\n", ffa.fn.Sym().Name, n)
|
||||
} else {
|
||||
func (ffa *funcFlagsAnalyzer) setState(n ir.Node, st pstate) {
|
||||
if st != psNoInfo {
|
||||
ffa.nstate[n] = st
|
||||
}
|
||||
}
|
||||
|
||||
func (ffa *funcFlagsAnalyzer) updatestate(n ir.Node, st pstate) {
|
||||
if _, ok := ffa.nstate[n]; !ok {
|
||||
base.Fatalf("funcFlagsAnalyzer: fn %q internal error, expected existing setting for node:\n%+v\n", ffa.fn.Sym().Name, n)
|
||||
func (ffa *funcFlagsAnalyzer) updateState(n ir.Node, st pstate) {
|
||||
if st == psNoInfo {
|
||||
delete(ffa.nstate, n)
|
||||
} else {
|
||||
ffa.nstate[n] = st
|
||||
}
|
||||
}
|
||||
|
||||
func (ffa *funcFlagsAnalyzer) setstateSoft(n ir.Node, st pstate) {
|
||||
ffa.nstate[n] = st
|
||||
}
|
||||
|
||||
func (ffa *funcFlagsAnalyzer) panicPathTable() map[ir.Node]pstate {
|
||||
return ffa.nstate
|
||||
}
|
||||
|
|
@ -164,13 +154,13 @@ func (ffa *funcFlagsAnalyzer) stateForList(list ir.Nodes) pstate {
|
|||
// line 10 will be on a panic path).
|
||||
for i := len(list) - 1; i >= 0; i-- {
|
||||
n := list[i]
|
||||
psi := ffa.getstate(n)
|
||||
psi := ffa.getState(n)
|
||||
if debugTrace&debugTraceFuncFlags != 0 {
|
||||
fmt.Fprintf(os.Stderr, "=-= %v: stateForList n=%s ps=%s\n",
|
||||
ir.Line(n), n.Op().String(), psi.String())
|
||||
}
|
||||
st = blockCombine(psi, st)
|
||||
ffa.updatestate(n, st)
|
||||
ffa.updateState(n, st)
|
||||
}
|
||||
if st == psTop {
|
||||
st = psNoInfo
|
||||
|
|
@ -237,8 +227,6 @@ func (ffa *funcFlagsAnalyzer) nodeVisitPost(n ir.Node) {
|
|||
ir.Line(n), n.Op().String(), shouldVisit(n))
|
||||
}
|
||||
if !shouldVisit(n) {
|
||||
// invoke soft set, since node may be shared (e.g. ONAME)
|
||||
ffa.setstateSoft(n, psNoInfo)
|
||||
return
|
||||
}
|
||||
var st pstate
|
||||
|
|
@ -361,7 +349,7 @@ func (ffa *funcFlagsAnalyzer) nodeVisitPost(n ir.Node) {
|
|||
fmt.Fprintf(os.Stderr, "=-= %v: visit n=%s returns %s\n",
|
||||
ir.Line(n), n.Op().String(), st.String())
|
||||
}
|
||||
ffa.setstate(n, st)
|
||||
ffa.setState(n, st)
|
||||
}
|
||||
|
||||
func (ffa *funcFlagsAnalyzer) nodeVisitPre(n ir.Node) {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ type paramsAnalyzer struct {
|
|||
params []*ir.Name
|
||||
top []bool
|
||||
*condLevelTracker
|
||||
*nameFinder
|
||||
}
|
||||
|
||||
// getParams returns an *ir.Name slice containing all params for the
|
||||
|
|
@ -34,8 +35,8 @@ func getParams(fn *ir.Func) []*ir.Name {
|
|||
// new list. If the function in question doesn't have any interesting
|
||||
// parameters then the analyzer list is returned unchanged, and the
|
||||
// params flags in "fp" are updated accordingly.
|
||||
func addParamsAnalyzer(fn *ir.Func, analyzers []propAnalyzer, fp *FuncProps) []propAnalyzer {
|
||||
pa, props := makeParamsAnalyzer(fn)
|
||||
func addParamsAnalyzer(fn *ir.Func, analyzers []propAnalyzer, fp *FuncProps, nf *nameFinder) []propAnalyzer {
|
||||
pa, props := makeParamsAnalyzer(fn, nf)
|
||||
if pa != nil {
|
||||
analyzers = append(analyzers, pa)
|
||||
} else {
|
||||
|
|
@ -48,7 +49,7 @@ func addParamsAnalyzer(fn *ir.Func, analyzers []propAnalyzer, fp *FuncProps) []p
|
|||
// 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.
|
||||
func makeParamsAnalyzer(fn *ir.Func) (*paramsAnalyzer, []ParamPropBits) {
|
||||
func makeParamsAnalyzer(fn *ir.Func, nf *nameFinder) (*paramsAnalyzer, []ParamPropBits) {
|
||||
params := getParams(fn) // includes receiver if applicable
|
||||
if len(params) == 0 {
|
||||
return nil, nil
|
||||
|
|
@ -98,6 +99,7 @@ func makeParamsAnalyzer(fn *ir.Func) (*paramsAnalyzer, []ParamPropBits) {
|
|||
params: params,
|
||||
top: top,
|
||||
condLevelTracker: new(condLevelTracker),
|
||||
nameFinder: nf,
|
||||
}
|
||||
return pa, nil
|
||||
}
|
||||
|
|
@ -162,7 +164,7 @@ func (pa *paramsAnalyzer) callCheckParams(ce *ir.CallExpr) {
|
|||
return
|
||||
}
|
||||
sel := ce.Fun.(*ir.SelectorExpr)
|
||||
r := ir.StaticValue(sel.X)
|
||||
r := pa.staticValue(sel.X)
|
||||
if r.Op() != ir.ONAME {
|
||||
return
|
||||
}
|
||||
|
|
@ -193,8 +195,8 @@ func (pa *paramsAnalyzer) callCheckParams(ce *ir.CallExpr) {
|
|||
return name == p, false
|
||||
})
|
||||
} else {
|
||||
cname, isFunc, _ := isFuncName(called)
|
||||
if isFunc {
|
||||
cname := pa.funcName(called)
|
||||
if cname != nil {
|
||||
pa.deriveFlagsFromCallee(ce, cname.Func)
|
||||
}
|
||||
}
|
||||
|
|
@ -238,7 +240,7 @@ func (pa *paramsAnalyzer) deriveFlagsFromCallee(ce *ir.CallExpr, callee *ir.Func
|
|||
}
|
||||
// See if one of the caller's parameters is flowing unmodified
|
||||
// into this actual expression.
|
||||
r := ir.StaticValue(arg)
|
||||
r := pa.staticValue(arg)
|
||||
if r.Op() != ir.ONAME {
|
||||
return
|
||||
}
|
||||
|
|
@ -247,7 +249,13 @@ func (pa *paramsAnalyzer) deriveFlagsFromCallee(ce *ir.CallExpr, callee *ir.Func
|
|||
return
|
||||
}
|
||||
callerParamIdx := pa.findParamIdx(name)
|
||||
if callerParamIdx == -1 || pa.params[callerParamIdx] == nil {
|
||||
// note that callerParamIdx may return -1 in the case where
|
||||
// the param belongs not to the current closure func we're
|
||||
// analyzing but to an outer enclosing func.
|
||||
if callerParamIdx == -1 {
|
||||
return
|
||||
}
|
||||
if pa.params[callerParamIdx] == nil {
|
||||
panic("something went wrong")
|
||||
}
|
||||
if !pa.top[callerParamIdx] &&
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ type resultsAnalyzer struct {
|
|||
props []ResultPropBits
|
||||
values []resultVal
|
||||
inlineMaxBudget int
|
||||
*nameFinder
|
||||
}
|
||||
|
||||
// resultVal captures information about a specific result returned from
|
||||
|
|
@ -28,7 +29,7 @@ type resultsAnalyzer struct {
|
|||
// the same function, etc. This container stores info on a the specific
|
||||
// scenarios we're looking for.
|
||||
type resultVal struct {
|
||||
lit constant.Value
|
||||
cval constant.Value
|
||||
fn *ir.Name
|
||||
fnClo bool
|
||||
top bool
|
||||
|
|
@ -40,8 +41,8 @@ type resultVal struct {
|
|||
// new list. If the function in question doesn't have any returns (or
|
||||
// any interesting returns) then the analyzer list is left as is, and
|
||||
// the result flags in "fp" are updated accordingly.
|
||||
func addResultsAnalyzer(fn *ir.Func, analyzers []propAnalyzer, fp *FuncProps, inlineMaxBudget int) []propAnalyzer {
|
||||
ra, props := makeResultsAnalyzer(fn, inlineMaxBudget)
|
||||
func addResultsAnalyzer(fn *ir.Func, analyzers []propAnalyzer, fp *FuncProps, inlineMaxBudget int, nf *nameFinder) []propAnalyzer {
|
||||
ra, props := makeResultsAnalyzer(fn, inlineMaxBudget, nf)
|
||||
if ra != nil {
|
||||
analyzers = append(analyzers, ra)
|
||||
} else {
|
||||
|
|
@ -54,7 +55,7 @@ func addResultsAnalyzer(fn *ir.Func, analyzers []propAnalyzer, fp *FuncProps, in
|
|||
// in function fn. If the function doesn't have any interesting
|
||||
// results, a nil helper is returned along with a set of default
|
||||
// result flags for the func.
|
||||
func makeResultsAnalyzer(fn *ir.Func, inlineMaxBudget int) (*resultsAnalyzer, []ResultPropBits) {
|
||||
func makeResultsAnalyzer(fn *ir.Func, inlineMaxBudget int, nf *nameFinder) (*resultsAnalyzer, []ResultPropBits) {
|
||||
results := fn.Type().Results()
|
||||
if len(results) == 0 {
|
||||
return nil, nil
|
||||
|
|
@ -84,6 +85,7 @@ func makeResultsAnalyzer(fn *ir.Func, inlineMaxBudget int) (*resultsAnalyzer, []
|
|||
props: props,
|
||||
values: vals,
|
||||
inlineMaxBudget: inlineMaxBudget,
|
||||
nameFinder: nf,
|
||||
}
|
||||
return ra, nil
|
||||
}
|
||||
|
|
@ -143,29 +145,6 @@ func (ra *resultsAnalyzer) nodeVisitPost(n ir.Node) {
|
|||
}
|
||||
}
|
||||
|
||||
// isFuncName returns the *ir.Name for the func or method
|
||||
// corresponding to node 'n', along with a boolean indicating success,
|
||||
// and another boolean indicating whether the func is closure.
|
||||
func isFuncName(n ir.Node) (*ir.Name, bool, bool) {
|
||||
sv := ir.StaticValue(n)
|
||||
if sv.Op() == ir.ONAME {
|
||||
name := sv.(*ir.Name)
|
||||
if name.Sym() != nil && name.Class == ir.PFUNC {
|
||||
return name, true, false
|
||||
}
|
||||
}
|
||||
if sv.Op() == ir.OCLOSURE {
|
||||
cloex := sv.(*ir.ClosureExpr)
|
||||
return cloex.Func.Nname, true, true
|
||||
}
|
||||
if sv.Op() == ir.OMETHEXPR {
|
||||
if mn := ir.MethodExprName(sv); mn != nil {
|
||||
return mn, true, false
|
||||
}
|
||||
}
|
||||
return nil, false, false
|
||||
}
|
||||
|
||||
// analyzeResult examines the expression 'n' being returned as the
|
||||
// 'ii'th argument in some return statement to see whether has
|
||||
// interesting characteristics (for example, returns a constant), then
|
||||
|
|
@ -173,18 +152,22 @@ func isFuncName(n ir.Node) (*ir.Name, bool, bool) {
|
|||
// previous result (for the given return slot) that we've already
|
||||
// processed.
|
||||
func (ra *resultsAnalyzer) analyzeResult(ii int, n ir.Node) {
|
||||
isAllocMem := isAllocatedMem(n)
|
||||
isConcConvItf := isConcreteConvIface(n)
|
||||
lit, isConst := isLiteral(n)
|
||||
rfunc, isFunc, isClo := isFuncName(n)
|
||||
isAllocMem := ra.isAllocatedMem(n)
|
||||
isConcConvItf := ra.isConcreteConvIface(n)
|
||||
constVal := ra.constValue(n)
|
||||
isConst := (constVal != nil)
|
||||
isNil := ra.isNil(n)
|
||||
rfunc := ra.funcName(n)
|
||||
isFunc := (rfunc != nil)
|
||||
isClo := (rfunc != nil && rfunc.Func.OClosure != nil)
|
||||
curp := ra.props[ii]
|
||||
dprops, isDerivedFromCall := deriveReturnFlagsFromCallee(n)
|
||||
dprops, isDerivedFromCall := ra.deriveReturnFlagsFromCallee(n)
|
||||
newp := ResultNoInfo
|
||||
var newlit constant.Value
|
||||
var newcval constant.Value
|
||||
var newfunc *ir.Name
|
||||
|
||||
if debugTrace&debugTraceResults != 0 {
|
||||
fmt.Fprintf(os.Stderr, "=-= %v: analyzeResult n=%s ismem=%v isconcconv=%v isconst=%v isfunc=%v isclo=%v\n", ir.Line(n), n.Op().String(), isAllocMem, isConcConvItf, isConst, isFunc, isClo)
|
||||
fmt.Fprintf(os.Stderr, "=-= %v: analyzeResult n=%s ismem=%v isconcconv=%v isconst=%v isnil=%v isfunc=%v isclo=%v\n", ir.Line(n), n.Op().String(), isAllocMem, isConcConvItf, isConst, isNil, isFunc, isClo)
|
||||
}
|
||||
|
||||
if ra.values[ii].top {
|
||||
|
|
@ -201,7 +184,10 @@ func (ra *resultsAnalyzer) analyzeResult(ii int, n ir.Node) {
|
|||
newfunc = rfunc
|
||||
case isConst:
|
||||
newp = ResultAlwaysSameConstant
|
||||
newlit = lit
|
||||
newcval = constVal
|
||||
case isNil:
|
||||
newp = ResultAlwaysSameConstant
|
||||
newcval = nil
|
||||
case isDerivedFromCall:
|
||||
newp = dprops
|
||||
ra.values[ii].derived = true
|
||||
|
|
@ -214,17 +200,20 @@ func (ra *resultsAnalyzer) analyzeResult(ii int, n ir.Node) {
|
|||
// the previous returns.
|
||||
switch curp {
|
||||
case ResultIsAllocatedMem:
|
||||
if isAllocatedMem(n) {
|
||||
if isAllocMem {
|
||||
newp = ResultIsAllocatedMem
|
||||
}
|
||||
case ResultIsConcreteTypeConvertedToInterface:
|
||||
if isConcreteConvIface(n) {
|
||||
if isConcConvItf {
|
||||
newp = ResultIsConcreteTypeConvertedToInterface
|
||||
}
|
||||
case ResultAlwaysSameConstant:
|
||||
if isConst && isSameLiteral(lit, ra.values[ii].lit) {
|
||||
if isNil && ra.values[ii].cval == nil {
|
||||
newp = ResultAlwaysSameConstant
|
||||
newlit = lit
|
||||
newcval = nil
|
||||
} else if isConst && constant.Compare(constVal, token.EQL, ra.values[ii].cval) {
|
||||
newp = ResultAlwaysSameConstant
|
||||
newcval = constVal
|
||||
}
|
||||
case ResultAlwaysSameFunc:
|
||||
if isFunc && isSameFuncName(rfunc, ra.values[ii].fn) {
|
||||
|
|
@ -236,7 +225,7 @@ func (ra *resultsAnalyzer) analyzeResult(ii int, n ir.Node) {
|
|||
}
|
||||
ra.values[ii].fn = newfunc
|
||||
ra.values[ii].fnClo = isClo
|
||||
ra.values[ii].lit = newlit
|
||||
ra.values[ii].cval = newcval
|
||||
ra.props[ii] = newp
|
||||
|
||||
if debugTrace&debugTraceResults != 0 {
|
||||
|
|
@ -245,15 +234,6 @@ func (ra *resultsAnalyzer) analyzeResult(ii int, n ir.Node) {
|
|||
}
|
||||
}
|
||||
|
||||
func isAllocatedMem(n ir.Node) bool {
|
||||
sv := ir.StaticValue(n)
|
||||
switch sv.Op() {
|
||||
case ir.OMAKESLICE, ir.ONEW, ir.OPTRLIT, ir.OSLICELIT:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// deriveReturnFlagsFromCallee tries to set properties for a given
|
||||
// return result where we're returning call expression; return value
|
||||
// is a return property value and a boolean indicating whether the
|
||||
|
|
@ -270,7 +250,7 @@ func isAllocatedMem(n ir.Node) bool {
|
|||
// set foo's return property to that of bar. In the case of "two", however,
|
||||
// even though each return path returns a constant, we don't know
|
||||
// whether the constants are identical, hence we need to be conservative.
|
||||
func deriveReturnFlagsFromCallee(n ir.Node) (ResultPropBits, bool) {
|
||||
func (ra *resultsAnalyzer) deriveReturnFlagsFromCallee(n ir.Node) (ResultPropBits, bool) {
|
||||
if n.Op() != ir.OCALLFUNC {
|
||||
return 0, false
|
||||
}
|
||||
|
|
@ -282,8 +262,8 @@ func deriveReturnFlagsFromCallee(n ir.Node) (ResultPropBits, bool) {
|
|||
if called.Op() != ir.ONAME {
|
||||
return 0, false
|
||||
}
|
||||
cname, isFunc, _ := isFuncName(called)
|
||||
if !isFunc {
|
||||
cname := ra.funcName(called)
|
||||
if cname == nil {
|
||||
return 0, false
|
||||
}
|
||||
calleeProps := propsForFunc(cname.Func)
|
||||
|
|
@ -295,41 +275,3 @@ func deriveReturnFlagsFromCallee(n ir.Node) (ResultPropBits, bool) {
|
|||
}
|
||||
return calleeProps.ResultFlags[0], true
|
||||
}
|
||||
|
||||
func isLiteral(n ir.Node) (constant.Value, bool) {
|
||||
sv := ir.StaticValue(n)
|
||||
switch sv.Op() {
|
||||
case ir.ONIL:
|
||||
return nil, true
|
||||
case ir.OLITERAL:
|
||||
return sv.Val(), true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// isSameLiteral checks to see if 'v1' and 'v2' correspond to the same
|
||||
// literal value, or if they are both nil.
|
||||
func isSameLiteral(v1, v2 constant.Value) bool {
|
||||
if v1 == nil && v2 == nil {
|
||||
return true
|
||||
}
|
||||
if v1 == nil || v2 == nil {
|
||||
return false
|
||||
}
|
||||
return constant.Compare(v1, token.EQL, v2)
|
||||
}
|
||||
|
||||
func isConcreteConvIface(n ir.Node) bool {
|
||||
sv := ir.StaticValue(n)
|
||||
if sv.Op() != ir.OCONVIFACE {
|
||||
return false
|
||||
}
|
||||
return !sv.(*ir.ConvExpr).X.Type().IsInterface()
|
||||
}
|
||||
|
||||
func isSameFuncName(v1, v2 *ir.Name) bool {
|
||||
// NB: there are a few corner cases where pointer equality
|
||||
// doesn't work here, but this should be good enough for
|
||||
// our purposes here.
|
||||
return v1 == v2
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,129 @@
|
|||
// 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.
|
||||
|
||||
package inlheur
|
||||
|
||||
import (
|
||||
"cmd/compile/internal/ir"
|
||||
"go/constant"
|
||||
)
|
||||
|
||||
// nameFinder provides a set of "isXXX" query methods for clients to
|
||||
// ask whether a given AST node corresponds to a function, a constant
|
||||
// value, and so on. These methods use an underlying ir.ReassignOracle
|
||||
// to return more precise results in cases where an "interesting"
|
||||
// value is assigned to a singly-defined local temp. Example:
|
||||
//
|
||||
// const q = 101
|
||||
// fq := func() int { return q }
|
||||
// copyOfConstant := q
|
||||
// copyOfFunc := f
|
||||
// interestingCall(copyOfConstant, copyOfFunc)
|
||||
//
|
||||
// A name finder query method invoked on the arguments being passed to
|
||||
// "interestingCall" will be able detect that 'copyOfConstant' always
|
||||
// evaluates to a constant (even though it is in fact a PAUTO local
|
||||
// variable). A given nameFinder can also operate without using
|
||||
// ir.ReassignOracle (in cases where it is not practical to look
|
||||
// at the entire function); in such cases queries will still work
|
||||
// for explicit constant values and functions.
|
||||
type nameFinder struct {
|
||||
ro *ir.ReassignOracle
|
||||
}
|
||||
|
||||
// newNameFinder returns a new nameFinder object with a reassignment
|
||||
// oracle initialized based on the function fn, or if fn is nil,
|
||||
// without an underlying ReassignOracle.
|
||||
func newNameFinder(fn *ir.Func) *nameFinder {
|
||||
var ro *ir.ReassignOracle
|
||||
if fn != nil {
|
||||
ro = &ir.ReassignOracle{}
|
||||
ro.Init(fn)
|
||||
}
|
||||
return &nameFinder{ro: ro}
|
||||
}
|
||||
|
||||
// funcName returns the *ir.Name for the func or method
|
||||
// corresponding to node 'n', or nil if n can't be proven
|
||||
// to contain a function value.
|
||||
func (nf *nameFinder) funcName(n ir.Node) *ir.Name {
|
||||
sv := n
|
||||
if nf.ro != nil {
|
||||
sv = nf.ro.StaticValue(n)
|
||||
}
|
||||
if name := ir.StaticCalleeName(sv); name != nil {
|
||||
return name
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// isAllocatedMem returns true if node n corresponds to a memory
|
||||
// allocation expression (make, new, or equivalent).
|
||||
func (nf *nameFinder) isAllocatedMem(n ir.Node) bool {
|
||||
sv := n
|
||||
if nf.ro != nil {
|
||||
sv = nf.ro.StaticValue(n)
|
||||
}
|
||||
switch sv.Op() {
|
||||
case ir.OMAKESLICE, ir.ONEW, ir.OPTRLIT, ir.OSLICELIT:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// constValue returns the underlying constant.Value for an AST node n
|
||||
// if n is itself a constant value/expr, or if n is a singly assigned
|
||||
// local containing constant expr/value (or nil not constant).
|
||||
func (nf *nameFinder) constValue(n ir.Node) constant.Value {
|
||||
sv := n
|
||||
if nf.ro != nil {
|
||||
sv = nf.ro.StaticValue(n)
|
||||
}
|
||||
if sv.Op() == ir.OLITERAL {
|
||||
return sv.Val()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// isNil returns whether n is nil (or singly
|
||||
// assigned local containing nil).
|
||||
func (nf *nameFinder) isNil(n ir.Node) bool {
|
||||
sv := n
|
||||
if nf.ro != nil {
|
||||
sv = nf.ro.StaticValue(n)
|
||||
}
|
||||
return sv.Op() == ir.ONIL
|
||||
}
|
||||
|
||||
func (nf *nameFinder) staticValue(n ir.Node) ir.Node {
|
||||
if nf.ro == nil {
|
||||
return n
|
||||
}
|
||||
return nf.ro.StaticValue(n)
|
||||
}
|
||||
|
||||
func (nf *nameFinder) reassigned(n *ir.Name) bool {
|
||||
if nf.ro == nil {
|
||||
return true
|
||||
}
|
||||
return nf.ro.Reassigned(n)
|
||||
}
|
||||
|
||||
func (nf *nameFinder) isConcreteConvIface(n ir.Node) bool {
|
||||
sv := n
|
||||
if nf.ro != nil {
|
||||
sv = nf.ro.StaticValue(n)
|
||||
}
|
||||
if sv.Op() != ir.OCONVIFACE {
|
||||
return false
|
||||
}
|
||||
return !sv.(*ir.ConvExpr).X.Type().IsInterface()
|
||||
}
|
||||
|
||||
func isSameFuncName(v1, v2 *ir.Name) bool {
|
||||
// NB: there are a few corner cases where pointer equality
|
||||
// doesn't work here, but this should be good enough for
|
||||
// our purposes here.
|
||||
return v1 == v2
|
||||
}
|
||||
|
|
@ -46,7 +46,7 @@ type resultUseAnalyzer struct {
|
|||
// rescoreBasedOnCallResultUses examines how call results are used,
|
||||
// and tries to update the scores of calls based on how their results
|
||||
// are used in the function.
|
||||
func rescoreBasedOnCallResultUses(fn *ir.Func, resultNameTab map[*ir.Name]resultPropAndCS, cstab CallSiteTab) {
|
||||
func (csa *callSiteAnalyzer) rescoreBasedOnCallResultUses(fn *ir.Func, resultNameTab map[*ir.Name]resultPropAndCS, cstab CallSiteTab) {
|
||||
enableDebugTraceIfEnv()
|
||||
rua := &resultUseAnalyzer{
|
||||
resultNameTab: resultNameTab,
|
||||
|
|
@ -65,7 +65,7 @@ func rescoreBasedOnCallResultUses(fn *ir.Func, resultNameTab map[*ir.Name]result
|
|||
disableDebugTrace()
|
||||
}
|
||||
|
||||
func examineCallResults(cs *CallSite, resultNameTab map[*ir.Name]resultPropAndCS) map[*ir.Name]resultPropAndCS {
|
||||
func (csa *callSiteAnalyzer) examineCallResults(cs *CallSite, resultNameTab map[*ir.Name]resultPropAndCS) map[*ir.Name]resultPropAndCS {
|
||||
if debugTrace&debugTraceScoring != 0 {
|
||||
fmt.Fprintf(os.Stderr, "=-= examining call results for %q\n",
|
||||
EncodeCallSiteKey(cs))
|
||||
|
|
@ -103,7 +103,7 @@ func examineCallResults(cs *CallSite, resultNameTab map[*ir.Name]resultPropAndCS
|
|||
if rprop&interesting == 0 {
|
||||
continue
|
||||
}
|
||||
if ir.Reassigned(n) {
|
||||
if csa.nameFinder.reassigned(n) {
|
||||
continue
|
||||
}
|
||||
if resultNameTab == nil {
|
||||
|
|
|
|||
|
|
@ -182,13 +182,14 @@ func mustToMay(x scoreAdjustTyp) scoreAdjustTyp {
|
|||
return 0
|
||||
}
|
||||
|
||||
// computeCallSiteScore takes a given call site whose ir node is 'call' and
|
||||
// callee function is 'callee' and with previously computed call site
|
||||
// properties 'csflags', then computes a score for the callsite that
|
||||
// combines the size cost of the callee with heuristics based on
|
||||
// previously parameter and function properties, then stores the score
|
||||
// and the adjustment mask in the appropriate fields in 'cs'
|
||||
func (cs *CallSite) computeCallSiteScore(calleeProps *FuncProps) {
|
||||
// computeCallSiteScore takes a given call site whose ir node is
|
||||
// 'call' and callee function is 'callee' and with previously computed
|
||||
// call site properties 'csflags', then computes a score for the
|
||||
// callsite that combines the size cost of the callee with heuristics
|
||||
// based on previously computed argument and function properties,
|
||||
// then stores the score and the adjustment mask in the appropriate
|
||||
// fields in 'cs'
|
||||
func (cs *CallSite) computeCallSiteScore(csa *callSiteAnalyzer, calleeProps *FuncProps) {
|
||||
callee := cs.Callee
|
||||
csflags := cs.Flags
|
||||
call := cs.Call
|
||||
|
|
@ -438,8 +439,13 @@ type scoreCallsCacheType struct {
|
|||
// after foo has been analyzed, but it's conceivable that CanInline
|
||||
// might visit bar before foo for this SCC.
|
||||
func ScoreCalls(fn *ir.Func) {
|
||||
if len(fn.Body) == 0 {
|
||||
return
|
||||
}
|
||||
enableDebugTraceIfEnv()
|
||||
|
||||
nameFinder := newNameFinder(fn)
|
||||
|
||||
if debugTrace&debugTraceScoring != 0 {
|
||||
fmt.Fprintf(os.Stderr, "=-= ScoreCalls(%v)\n", ir.FuncName(fn))
|
||||
}
|
||||
|
|
@ -461,21 +467,25 @@ func ScoreCalls(fn *ir.Func) {
|
|||
fmt.Fprintf(os.Stderr, "=-= building cstab for non-inl func %s\n",
|
||||
ir.FuncName(fn))
|
||||
}
|
||||
cstab = computeCallSiteTable(fn, fn.Body, scoreCallsCache.tab, nil, 0)
|
||||
cstab = computeCallSiteTable(fn, fn.Body, scoreCallsCache.tab, nil, 0,
|
||||
nameFinder)
|
||||
}
|
||||
|
||||
csa := makeCallSiteAnalyzer(fn)
|
||||
const doCallResults = true
|
||||
scoreCallsRegion(fn, fn.Body, cstab, doCallResults, nil)
|
||||
csa.scoreCallsRegion(fn, fn.Body, cstab, doCallResults, nil)
|
||||
|
||||
disableDebugTrace()
|
||||
}
|
||||
|
||||
// scoreCallsRegion assigns numeric scores to each of the callsites in
|
||||
// region 'region' within function 'fn'. This can be called on
|
||||
// an entire function, or with 'region' set to a chunk of
|
||||
// code corresponding to an inlined call.
|
||||
func scoreCallsRegion(fn *ir.Func, region ir.Nodes, cstab CallSiteTab, doCallResults bool, ic *ir.InlinedCallExpr) {
|
||||
func (csa *callSiteAnalyzer) scoreCallsRegion(fn *ir.Func, region ir.Nodes, cstab CallSiteTab, doCallResults bool, ic *ir.InlinedCallExpr) {
|
||||
if debugTrace&debugTraceScoring != 0 {
|
||||
fmt.Fprintf(os.Stderr, "=-= scoreCallsRegion(%v, %s)\n",
|
||||
ir.FuncName(fn), region[0].Op().String())
|
||||
fmt.Fprintf(os.Stderr, "=-= scoreCallsRegion(%v, %s) len(cstab)=%d\n",
|
||||
ir.FuncName(fn), region[0].Op().String(), len(cstab))
|
||||
}
|
||||
|
||||
// Sort callsites to avoid any surprises with non deterministic
|
||||
|
|
@ -510,13 +520,13 @@ func scoreCallsRegion(fn *ir.Func, region ir.Nodes, cstab CallSiteTab, doCallRes
|
|||
continue
|
||||
}
|
||||
}
|
||||
cs.computeCallSiteScore(cprops)
|
||||
cs.computeCallSiteScore(csa, cprops)
|
||||
|
||||
if doCallResults {
|
||||
if debugTrace&debugTraceScoring != 0 {
|
||||
fmt.Fprintf(os.Stderr, "=-= examineCallResults at %s: flags=%d score=%d funcInlHeur=%v deser=%v\n", fmtFullPos(cs.Call.Pos()), cs.Flags, cs.Score, fihcprops, desercprops)
|
||||
}
|
||||
resultNameTab = examineCallResults(cs, resultNameTab)
|
||||
resultNameTab = csa.examineCallResults(cs, resultNameTab)
|
||||
}
|
||||
|
||||
if debugTrace&debugTraceScoring != 0 {
|
||||
|
|
@ -525,7 +535,7 @@ func scoreCallsRegion(fn *ir.Func, region ir.Nodes, cstab CallSiteTab, doCallRes
|
|||
}
|
||||
|
||||
if resultNameTab != nil {
|
||||
rescoreBasedOnCallResultUses(fn, resultNameTab, cstab)
|
||||
csa.rescoreBasedOnCallResultUses(fn, resultNameTab, cstab)
|
||||
}
|
||||
|
||||
disableDebugTrace()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
// 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.
|
||||
|
||||
//go:build !checknewoldreassignment
|
||||
|
||||
package ir
|
||||
|
||||
const consistencyCheckEnabled = false
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
// 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.
|
||||
|
||||
//go:build checknewoldreassignment
|
||||
|
||||
package ir
|
||||
|
||||
const consistencyCheckEnabled = true
|
||||
|
|
@ -923,6 +923,8 @@ FindRHS:
|
|||
// NB: global variables are always considered to be re-assigned.
|
||||
// TODO: handle initial declaration not including an assignment and
|
||||
// followed by a single assignment?
|
||||
// NOTE: any changes made here should also be made in the corresponding
|
||||
// code in the ReassignOracle.Init method.
|
||||
func Reassigned(name *Name) bool {
|
||||
if name.Op() != ONAME {
|
||||
base.Fatalf("reassigned %v", name)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
// 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.
|
||||
|
||||
package ir
|
||||
|
||||
import (
|
||||
"cmd/compile/internal/base"
|
||||
"cmd/internal/src"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// checkStaticValueResult compares the result from ReassignOracle.StaticValue
|
||||
// with the corresponding result from ir.StaticValue to make sure they agree.
|
||||
// This method is called only when turned on via build tag.
|
||||
func checkStaticValueResult(n Node, newres Node) {
|
||||
oldres := StaticValue(n)
|
||||
if oldres != newres {
|
||||
base.Fatalf("%s: new/old static value disagreement on %v:\nnew=%v\nold=%v", fmtFullPos(n.Pos()), n, newres, oldres)
|
||||
}
|
||||
}
|
||||
|
||||
// checkStaticValueResult 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) {
|
||||
origres := Reassigned(n)
|
||||
if newres != origres {
|
||||
base.Fatalf("%s: new/old reassigned disagreement on %v (class %s) newres=%v oldres=%v", fmtFullPos(n.Pos()), n, n.Class.String(), newres, origres)
|
||||
}
|
||||
}
|
||||
|
||||
// fmtFullPos returns a verbose dump for pos p, including inlines.
|
||||
func fmtFullPos(p src.XPos) string {
|
||||
var sb strings.Builder
|
||||
sep := ""
|
||||
base.Ctxt.AllPos(p, func(pos src.Pos) {
|
||||
fmt.Fprintf(&sb, sep)
|
||||
sep = "|"
|
||||
file := filepath.Base(pos.Filename())
|
||||
fmt.Fprintf(&sb, "%s:%d:%d", file, pos.Line(), pos.Col())
|
||||
})
|
||||
return sb.String()
|
||||
}
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
// 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.
|
||||
|
||||
package ir
|
||||
|
||||
import (
|
||||
"cmd/compile/internal/base"
|
||||
)
|
||||
|
||||
// A ReassignOracle efficiently answers queries about whether local
|
||||
// variables are reassigned. This helper works by looking for function
|
||||
// params and short variable declarations (e.g.
|
||||
// https://go.dev/ref/spec#Short_variable_declarations) that are
|
||||
// neither address taken nor subsequently re-assigned. It is intended
|
||||
// to operate much like "ir.StaticValue" and "ir.Reassigned", but in a
|
||||
// way that does just a single walk of the containing function (as
|
||||
// opposed to a new walk on every call).
|
||||
type ReassignOracle struct {
|
||||
fn *Func
|
||||
// maps candidate name to its defining assignment (or for
|
||||
// for params, defining func).
|
||||
singleDef map[*Name]Node
|
||||
}
|
||||
|
||||
// Init initializes the oracle based on the IR in function fn, laying
|
||||
// the groundwork for future calls to the StaticValue and Reassigned
|
||||
// methods. If the fn's IR is subsequently modified, Init must be
|
||||
// called again.
|
||||
func (ro *ReassignOracle) Init(fn *Func) {
|
||||
ro.fn = fn
|
||||
|
||||
// Collect candidate map. Start by adding function parameters
|
||||
// explicitly.
|
||||
ro.singleDef = make(map[*Name]Node)
|
||||
sig := fn.Type()
|
||||
numParams := sig.NumRecvs() + sig.NumParams()
|
||||
for _, param := range fn.Dcl[:numParams] {
|
||||
if IsBlank(param) {
|
||||
continue
|
||||
}
|
||||
// For params, use func itself as defining node.
|
||||
ro.singleDef[param] = fn
|
||||
}
|
||||
|
||||
// Walk the function body to discover any locals assigned
|
||||
// via ":=" syntax (e.g. "a := <expr>").
|
||||
var findLocals func(n Node) bool
|
||||
findLocals = func(n Node) bool {
|
||||
if nn, ok := n.(*Name); ok {
|
||||
if nn.Defn != nil && !nn.Addrtaken() && nn.Class == PAUTO {
|
||||
ro.singleDef[nn] = nn.Defn
|
||||
}
|
||||
} else if nn, ok := n.(*ClosureExpr); ok {
|
||||
Any(nn.Func, findLocals)
|
||||
}
|
||||
return false
|
||||
}
|
||||
Any(fn, findLocals)
|
||||
|
||||
outerName := func(x Node) *Name {
|
||||
if x == nil {
|
||||
return nil
|
||||
}
|
||||
n, ok := OuterValue(x).(*Name)
|
||||
if ok {
|
||||
return n.Canonical()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// pruneIfNeeded examines node nn appearing on the left hand side
|
||||
// of assignment statement asn to see if it contains a reassignment
|
||||
// to any nodes in our candidate map ro.singleDef; if a reassignment
|
||||
// is found, the corresponding name is deleted from singleDef.
|
||||
pruneIfNeeded := func(nn Node, asn Node) {
|
||||
oname := outerName(nn)
|
||||
if oname == nil {
|
||||
return
|
||||
}
|
||||
defn, ok := ro.singleDef[oname]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
// any assignment to a param invalidates the entry.
|
||||
paramAssigned := oname.Class == PPARAM
|
||||
// assignment to local ok iff assignment is its orig def.
|
||||
localAssigned := (oname.Class == PAUTO && asn != defn)
|
||||
if paramAssigned || localAssigned {
|
||||
// We found an assignment to name N that doesn't
|
||||
// correspond to its original definition; remove
|
||||
// from candidates.
|
||||
delete(ro.singleDef, oname)
|
||||
}
|
||||
}
|
||||
|
||||
// Prune away anything that looks assigned. This code modeled after
|
||||
// similar code in ir.Reassigned; any changes there should be made
|
||||
// here as well.
|
||||
var do func(n Node) bool
|
||||
do = func(n Node) bool {
|
||||
switch n.Op() {
|
||||
case OAS:
|
||||
asn := n.(*AssignStmt)
|
||||
pruneIfNeeded(asn.X, n)
|
||||
case OAS2, OAS2FUNC, OAS2MAPR, OAS2DOTTYPE, OAS2RECV, OSELRECV2:
|
||||
asn := n.(*AssignListStmt)
|
||||
for _, p := range asn.Lhs {
|
||||
pruneIfNeeded(p, n)
|
||||
}
|
||||
case OASOP:
|
||||
asn := n.(*AssignOpStmt)
|
||||
pruneIfNeeded(asn.X, n)
|
||||
case ORANGE:
|
||||
rs := n.(*RangeStmt)
|
||||
pruneIfNeeded(rs.Key, n)
|
||||
pruneIfNeeded(rs.Value, n)
|
||||
case OCLOSURE:
|
||||
n := n.(*ClosureExpr)
|
||||
Any(n.Func, do)
|
||||
}
|
||||
return false
|
||||
}
|
||||
Any(fn, do)
|
||||
}
|
||||
|
||||
// StaticValue method has the same semantics as the ir package function
|
||||
// of the same name; see comments on [StaticValue].
|
||||
func (ro *ReassignOracle) StaticValue(n Node) Node {
|
||||
arg := n
|
||||
for {
|
||||
if n.Op() == OCONVNOP {
|
||||
n = n.(*ConvExpr).X
|
||||
continue
|
||||
}
|
||||
|
||||
if n.Op() == OINLCALL {
|
||||
n = n.(*InlinedCallExpr).SingleResult()
|
||||
continue
|
||||
}
|
||||
|
||||
n1 := ro.staticValue1(n)
|
||||
if n1 == nil {
|
||||
if consistencyCheckEnabled {
|
||||
checkStaticValueResult(arg, n)
|
||||
}
|
||||
return n
|
||||
}
|
||||
n = n1
|
||||
}
|
||||
}
|
||||
|
||||
func (ro *ReassignOracle) staticValue1(nn Node) Node {
|
||||
if nn.Op() != ONAME {
|
||||
return nil
|
||||
}
|
||||
n := nn.(*Name).Canonical()
|
||||
if n.Class != PAUTO {
|
||||
return nil
|
||||
}
|
||||
|
||||
defn := n.Defn
|
||||
if defn == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var rhs Node
|
||||
FindRHS:
|
||||
switch defn.Op() {
|
||||
case OAS:
|
||||
defn := defn.(*AssignStmt)
|
||||
rhs = defn.Y
|
||||
case OAS2:
|
||||
defn := defn.(*AssignListStmt)
|
||||
for i, lhs := range defn.Lhs {
|
||||
if lhs == n {
|
||||
rhs = defn.Rhs[i]
|
||||
break FindRHS
|
||||
}
|
||||
}
|
||||
base.Fatalf("%v missing from LHS of %v", n, defn)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
if rhs == nil {
|
||||
base.Fatalf("RHS is nil: %v", defn)
|
||||
}
|
||||
|
||||
if _, ok := ro.singleDef[n]; !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return rhs
|
||||
}
|
||||
|
||||
// Reassigned method has the same semantics as the ir package function
|
||||
// of the same name; see comments on [Reassigned] for more info.
|
||||
func (ro *ReassignOracle) Reassigned(n *Name) bool {
|
||||
_, ok := ro.singleDef[n]
|
||||
result := !ok
|
||||
if consistencyCheckEnabled {
|
||||
checkReassignedResult(n, result)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
@ -20,4 +20,6 @@ func Init(arch *ssagen.ArchInfo) {
|
|||
arch.SSAMarkMoves = func(s *ssagen.State, b *ssa.Block) {}
|
||||
arch.SSAGenValue = ssaGenValue
|
||||
arch.SSAGenBlock = ssaGenBlock
|
||||
arch.LoadRegResult = loadRegResult
|
||||
arch.SpillArgReg = spillArgReg
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import (
|
|||
"cmd/compile/internal/base"
|
||||
"cmd/compile/internal/ir"
|
||||
"cmd/compile/internal/logopt"
|
||||
"cmd/compile/internal/objw"
|
||||
"cmd/compile/internal/ssa"
|
||||
"cmd/compile/internal/ssagen"
|
||||
"cmd/compile/internal/types"
|
||||
|
|
@ -144,6 +145,18 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
|
|||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = r
|
||||
ssagen.AddrAuto(&p.To, v)
|
||||
case ssa.OpArgIntReg, ssa.OpArgFloatReg:
|
||||
// The assembler needs to wrap the entry safepoint/stack growth code with spill/unspill
|
||||
// The loop only runs once.
|
||||
for _, a := range v.Block.Func.RegArgs {
|
||||
// Pass the spill/unspill information along to the assembler, offset by size of
|
||||
// the saved LR slot.
|
||||
addr := ssagen.SpillSlotAddr(a, loong64.REGSP, base.Ctxt.Arch.FixedFrameSize)
|
||||
s.FuncInfo().AddSpill(
|
||||
obj.RegSpill{Reg: a.Reg, Addr: addr, Unspill: loadByType(a.Type, a.Reg), Spill: storeByType(a.Type, a.Reg)})
|
||||
}
|
||||
v.Block.Func.RegArgs = nil
|
||||
ssagen.CheckArgReg(v)
|
||||
case ssa.OpLOONG64ADDV,
|
||||
ssa.OpLOONG64SUBV,
|
||||
ssa.OpLOONG64AND,
|
||||
|
|
@ -362,13 +375,12 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
|
|||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = v.Reg()
|
||||
case ssa.OpLOONG64DUFFZERO:
|
||||
// runtime.duffzero expects start address in R19
|
||||
// runtime.duffzero expects start address in R20
|
||||
p := s.Prog(obj.ADUFFZERO)
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Name = obj.NAME_EXTERN
|
||||
p.To.Sym = ir.Syms.Duffzero
|
||||
p.To.Offset = v.AuxInt
|
||||
|
||||
case ssa.OpLOONG64LoweredZero:
|
||||
// MOVx R0, (Rarg0)
|
||||
// ADDV $sz, Rarg0
|
||||
|
|
@ -797,3 +809,22 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
|
|||
b.Fatalf("branch not implemented: %s", b.LongString())
|
||||
}
|
||||
}
|
||||
|
||||
func loadRegResult(s *ssagen.State, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog {
|
||||
p := s.Prog(loadByType(t, reg))
|
||||
p.From.Type = obj.TYPE_MEM
|
||||
p.From.Name = obj.NAME_AUTO
|
||||
p.From.Sym = n.Linksym()
|
||||
p.From.Offset = n.FrameOffset() + off
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = reg
|
||||
return p
|
||||
}
|
||||
|
||||
func spillArgReg(pp *objw.Progs, p *obj.Prog, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog {
|
||||
p = pp.Append(p, storeByType(t, reg), obj.TYPE_REG, reg, 0, obj.TYPE_MEM, 0, n.FrameOffset()+off)
|
||||
p.To.Name = obj.NAME_PARAM
|
||||
p.To.Sym = n.Linksym()
|
||||
p.Pos = p.Pos.WithNotStmt()
|
||||
return p
|
||||
}
|
||||
|
|
|
|||
|
|
@ -416,7 +416,7 @@
|
|||
(GetCallerSP ...) => (LoweredGetCallerSP ...)
|
||||
(GetCallerPC ...) => (LoweredGetCallerPC ...)
|
||||
|
||||
(If cond yes no) => (NE cond yes no)
|
||||
(If cond yes no) => (NE (MOVBUreg <typ.UInt64> cond) yes no)
|
||||
|
||||
// Write barrier.
|
||||
(WB ...) => (LoweredWB ...)
|
||||
|
|
@ -450,71 +450,37 @@
|
|||
(EQ (SGTconst [0] x) yes no) => (GEZ x yes no)
|
||||
(NE (SGT x (MOVVconst [0])) yes no) => (GTZ x yes no)
|
||||
(EQ (SGT x (MOVVconst [0])) yes no) => (LEZ x yes no)
|
||||
(MOVBUreg x:((SGT|SGTU) _ _)) => x
|
||||
|
||||
// fold offset into address
|
||||
(ADDVconst [off1] (MOVVaddr [off2] {sym} ptr)) && is32Bit(off1+int64(off2)) => (MOVVaddr [int32(off1)+int32(off2)] {sym} ptr)
|
||||
|
||||
// fold address into load/store
|
||||
(MOVBload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVBload [off1+int32(off2)] {sym} ptr mem)
|
||||
(MOVBUload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVBUload [off1+int32(off2)] {sym} ptr mem)
|
||||
(MOVHload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVHload [off1+int32(off2)] {sym} ptr mem)
|
||||
(MOVHUload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVHUload [off1+int32(off2)] {sym} ptr mem)
|
||||
(MOVWload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVWload [off1+int32(off2)] {sym} ptr mem)
|
||||
(MOVWUload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVWUload [off1+int32(off2)] {sym} ptr mem)
|
||||
(MOVVload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVVload [off1+int32(off2)] {sym} ptr mem)
|
||||
(MOVFload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVFload [off1+int32(off2)] {sym} ptr mem)
|
||||
(MOVDload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVDload [off1+int32(off2)] {sym} ptr mem)
|
||||
// Do not fold global variable access in -dynlink mode, where it will be rewritten
|
||||
// to use the GOT via REGTMP, which currently cannot handle large offset.
|
||||
(MOV(B|BU|H|HU|W|WU|V|F|D)load [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2)
|
||||
&& (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) =>
|
||||
(MOV(B|BU|H|HU|W|WU|V|F|D)load [off1+int32(off2)] {sym} ptr mem)
|
||||
|
||||
(MOVBstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(int64(off1)+off2) => (MOVBstore [off1+int32(off2)] {sym} ptr val mem)
|
||||
(MOVHstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(int64(off1)+off2) => (MOVHstore [off1+int32(off2)] {sym} ptr val mem)
|
||||
(MOVWstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(int64(off1)+off2) => (MOVWstore [off1+int32(off2)] {sym} ptr val mem)
|
||||
(MOVVstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(int64(off1)+off2) => (MOVVstore [off1+int32(off2)] {sym} ptr val mem)
|
||||
(MOVFstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(int64(off1)+off2) => (MOVFstore [off1+int32(off2)] {sym} ptr val mem)
|
||||
(MOVDstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(int64(off1)+off2) => (MOVDstore [off1+int32(off2)] {sym} ptr val mem)
|
||||
(MOVBstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVBstorezero [off1+int32(off2)] {sym} ptr mem)
|
||||
(MOVHstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVHstorezero [off1+int32(off2)] {sym} ptr mem)
|
||||
(MOVWstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVWstorezero [off1+int32(off2)] {sym} ptr mem)
|
||||
(MOVVstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVVstorezero [off1+int32(off2)] {sym} ptr mem)
|
||||
(MOV(B|H|W|V|F|D)store [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(int64(off1)+off2)
|
||||
&& (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) =>
|
||||
(MOV(B|H|W|V|F|D)store [off1+int32(off2)] {sym} ptr val mem)
|
||||
|
||||
(MOVBload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
|
||||
(MOVBload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem)
|
||||
(MOVBUload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
|
||||
(MOVBUload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem)
|
||||
(MOVHload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
|
||||
(MOVHload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem)
|
||||
(MOVHUload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
|
||||
(MOVHUload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem)
|
||||
(MOVWload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
|
||||
(MOVWload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem)
|
||||
(MOVWUload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
|
||||
(MOVWUload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem)
|
||||
(MOVVload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
|
||||
(MOVVload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem)
|
||||
(MOVFload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
|
||||
(MOVFload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem)
|
||||
(MOVDload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
|
||||
(MOVDload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem)
|
||||
(MOV(B|H|W|V)storezero [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2)
|
||||
&& (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) =>
|
||||
(MOV(B|H|W|V)storezero [off1+int32(off2)] {sym} ptr mem)
|
||||
|
||||
(MOVBstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
|
||||
(MOVBstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem)
|
||||
(MOVHstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
|
||||
(MOVHstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem)
|
||||
(MOVWstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
|
||||
(MOVWstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem)
|
||||
(MOVVstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
|
||||
(MOVVstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem)
|
||||
(MOVFstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
|
||||
(MOVFstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem)
|
||||
(MOVDstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
|
||||
(MOVDstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem)
|
||||
(MOVBstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
|
||||
(MOVBstorezero [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem)
|
||||
(MOVHstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
|
||||
(MOVHstorezero [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem)
|
||||
(MOVWstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
|
||||
(MOVWstorezero [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem)
|
||||
(MOVVstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
|
||||
(MOVVstorezero [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem)
|
||||
(MOV(B|BU|H|HU|W|WU|V|F|D)load [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2)
|
||||
&& is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) =>
|
||||
(MOV(B|BU|H|HU|W|WU|V|F|D)load [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem)
|
||||
|
||||
(MOV(B|H|W|V|F|D)store [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2)
|
||||
&& is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) =>
|
||||
(MOV(B|H|W|V|F|D)store [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem)
|
||||
|
||||
(MOV(B|H|W|V)storezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2)
|
||||
&& is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) =>
|
||||
(MOV(B|H|W|V)storezero [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem)
|
||||
|
||||
(LoweredAtomicStore(32|64) ptr (MOVVconst [0]) mem) => (LoweredAtomicStorezero(32|64) ptr mem)
|
||||
(LoweredAtomicAdd32 ptr (MOVVconst [c]) mem) && is32Bit(c) => (LoweredAtomicAddconst32 [int32(c)] ptr mem)
|
||||
|
|
|
|||
|
|
@ -123,17 +123,17 @@ func init() {
|
|||
|
||||
// Common individual register masks
|
||||
var (
|
||||
gp = buildReg("R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31") // R1 is LR, R2 is thread pointer, R3 is stack pointer, R21-unused, R22 is g, R30 is REGTMP
|
||||
gp = buildReg("R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31") // R1 is LR, R2 is thread pointer, R3 is stack pointer, R22 is g, R30 is REGTMP
|
||||
gpg = gp | buildReg("g")
|
||||
gpsp = gp | buildReg("SP")
|
||||
gpspg = gpg | buildReg("SP")
|
||||
gpspsbg = gpspg | buildReg("SB")
|
||||
fp = buildReg("F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31")
|
||||
callerSave = gp | fp | buildReg("g") // runtime.setg (and anything calling it) may clobber g
|
||||
r1 = buildReg("R19")
|
||||
r2 = buildReg("R18")
|
||||
r3 = buildReg("R17")
|
||||
r4 = buildReg("R4")
|
||||
r1 = buildReg("R20")
|
||||
r2 = buildReg("R21")
|
||||
r3 = buildReg("R23")
|
||||
r4 = buildReg("R24")
|
||||
)
|
||||
// Common regInfo
|
||||
var (
|
||||
|
|
@ -273,32 +273,32 @@ func init() {
|
|||
{name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"}, // float64 -> float32
|
||||
|
||||
// function calls
|
||||
{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
|
||||
{name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
|
||||
{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R29"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
|
||||
{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem
|
||||
{name: "CALLstatic", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). last arg=mem, auxint=argsize, returns mem
|
||||
{name: "CALLtail", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true}, // tail call static function aux.(*obj.LSym). last arg=mem, auxint=argsize, returns mem
|
||||
{name: "CALLclosure", argLength: -1, reg: regInfo{inputs: []regMask{gpsp, buildReg("R29"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, last arg=mem, auxint=argsize, returns mem
|
||||
{name: "CALLinter", argLength: -1, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, last arg=mem, auxint=argsize, returns mem
|
||||
|
||||
// duffzero
|
||||
// arg0 = address of memory to zero
|
||||
// arg1 = mem
|
||||
// auxint = offset into duffzero code to start executing
|
||||
// returns mem
|
||||
// R19 aka loong64.REGRT1 changed as side effect
|
||||
// R20 aka loong64.REGRT1 changed as side effect
|
||||
{
|
||||
name: "DUFFZERO",
|
||||
aux: "Int64",
|
||||
argLength: 2,
|
||||
reg: regInfo{
|
||||
inputs: []regMask{buildReg("R19")},
|
||||
clobbers: buildReg("R19 R1"),
|
||||
inputs: []regMask{buildReg("R20")},
|
||||
clobbers: buildReg("R20 R1"),
|
||||
},
|
||||
typ: "Mem",
|
||||
faultOnNilArg0: true,
|
||||
},
|
||||
|
||||
// duffcopy
|
||||
// arg0 = address of dst memory (in R20, changed as side effect) REGRT2
|
||||
// arg1 = address of src memory (in R19, changed as side effect) REGRT1
|
||||
// arg0 = address of dst memory (in R21, changed as side effect)
|
||||
// arg1 = address of src memory (in R20, changed as side effect)
|
||||
// arg2 = mem
|
||||
// auxint = offset into duffcopy code to start executing
|
||||
// returns mem
|
||||
|
|
@ -307,8 +307,8 @@ func init() {
|
|||
aux: "Int64",
|
||||
argLength: 3,
|
||||
reg: regInfo{
|
||||
inputs: []regMask{buildReg("R20"), buildReg("R19")},
|
||||
clobbers: buildReg("R19 R20 R1"),
|
||||
inputs: []regMask{buildReg("R21"), buildReg("R20")},
|
||||
clobbers: buildReg("R20 R21 R1"),
|
||||
},
|
||||
typ: "Mem",
|
||||
faultOnNilArg0: true,
|
||||
|
|
@ -316,45 +316,45 @@ func init() {
|
|||
},
|
||||
|
||||
// large or unaligned zeroing
|
||||
// arg0 = address of memory to zero (in R19, changed as side effect)
|
||||
// arg0 = address of memory to zero (in R20, changed as side effect)
|
||||
// arg1 = address of the last element to zero
|
||||
// arg2 = mem
|
||||
// auxint = alignment
|
||||
// returns mem
|
||||
// MOVx R0, (R19)
|
||||
// ADDV $sz, R19
|
||||
// BGEU Rarg1, R19, -2(PC)
|
||||
// MOVx R0, (R20)
|
||||
// ADDV $sz, R20
|
||||
// BGEU Rarg1, R20, -2(PC)
|
||||
{
|
||||
name: "LoweredZero",
|
||||
aux: "Int64",
|
||||
argLength: 3,
|
||||
reg: regInfo{
|
||||
inputs: []regMask{buildReg("R19"), gp},
|
||||
clobbers: buildReg("R19"),
|
||||
inputs: []regMask{buildReg("R20"), gp},
|
||||
clobbers: buildReg("R20"),
|
||||
},
|
||||
typ: "Mem",
|
||||
faultOnNilArg0: true,
|
||||
},
|
||||
|
||||
// large or unaligned move
|
||||
// arg0 = address of dst memory (in R20, changed as side effect)
|
||||
// arg1 = address of src memory (in R19, changed as side effect)
|
||||
// arg0 = address of dst memory (in R21, changed as side effect)
|
||||
// arg1 = address of src memory (in R20, changed as side effect)
|
||||
// arg2 = address of the last element of src
|
||||
// arg3 = mem
|
||||
// auxint = alignment
|
||||
// returns mem
|
||||
// MOVx (R19), Rtmp
|
||||
// MOVx Rtmp, (R20)
|
||||
// ADDV $sz, R19
|
||||
// MOVx (R20), Rtmp
|
||||
// MOVx Rtmp, (R21)
|
||||
// ADDV $sz, R20
|
||||
// BGEU Rarg2, R19, -4(PC)
|
||||
// ADDV $sz, R21
|
||||
// BGEU Rarg2, R20, -4(PC)
|
||||
{
|
||||
name: "LoweredMove",
|
||||
aux: "Int64",
|
||||
argLength: 4,
|
||||
reg: regInfo{
|
||||
inputs: []regMask{buildReg("R20"), buildReg("R19"), gp},
|
||||
clobbers: buildReg("R19 R20"),
|
||||
inputs: []regMask{buildReg("R21"), buildReg("R20"), gp},
|
||||
clobbers: buildReg("R20 R21"),
|
||||
},
|
||||
typ: "Mem",
|
||||
faultOnNilArg0: true,
|
||||
|
|
@ -476,8 +476,8 @@ func init() {
|
|||
blocks: blocks,
|
||||
regnames: regNamesLOONG64,
|
||||
// TODO: support register ABI on loong64
|
||||
ParamIntRegNames: "R4 R5 R6 R7 R8 R9 R10 R11",
|
||||
ParamFloatRegNames: "F0 F1 F2 F3 F4 F5 F6 F7",
|
||||
ParamIntRegNames: "R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19",
|
||||
ParamFloatRegNames: "F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15",
|
||||
gpregmask: gp,
|
||||
fpregmask: fp,
|
||||
framepointerreg: -1, // not used
|
||||
|
|
|
|||
|
|
@ -283,6 +283,8 @@ func NewConfig(arch string, types Types, ctxt *obj.Link, optimize, softfloat boo
|
|||
c.registers = registersLOONG64[:]
|
||||
c.gpRegMask = gpRegMaskLOONG64
|
||||
c.fpRegMask = fpRegMaskLOONG64
|
||||
// c.intParamRegs = paramIntRegLOONG64
|
||||
// c.floatParamRegs = paramFloatRegLOONG64
|
||||
c.FPReg = framepointerRegLOONG64
|
||||
c.LinkReg = linkRegLOONG64
|
||||
c.hasGReg = true
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -672,6 +672,8 @@ func (s *regAllocState) init(f *Func) {
|
|||
s.allocatable &^= 1 << 9 // R9
|
||||
case "arm64":
|
||||
// nothing to do
|
||||
case "loong64": // R2 (aka TP) already reserved.
|
||||
// nothing to do
|
||||
case "ppc64le": // R2 already reserved.
|
||||
// nothing to do
|
||||
case "riscv64": // X3 (aka GP) and X4 (aka TP) already reserved.
|
||||
|
|
|
|||
|
|
@ -1724,8 +1724,10 @@ func rewriteValueLOONG64_OpLOONG64MASKNEZ(v *Value) bool {
|
|||
func rewriteValueLOONG64_OpLOONG64MOVBUload(v *Value) bool {
|
||||
v_1 := v.Args[1]
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
config := b.Func.Config
|
||||
// match: (MOVBUload [off1] {sym} (ADDVconst [off2] ptr) mem)
|
||||
// cond: is32Bit(int64(off1)+off2)
|
||||
// cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVBUload [off1+int32(off2)] {sym} ptr mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -1736,7 +1738,7 @@ func rewriteValueLOONG64_OpLOONG64MOVBUload(v *Value) bool {
|
|||
off2 := auxIntToInt64(v_0.AuxInt)
|
||||
ptr := v_0.Args[0]
|
||||
mem := v_1
|
||||
if !(is32Bit(int64(off1) + off2)) {
|
||||
if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVBUload)
|
||||
|
|
@ -1746,7 +1748,7 @@ func rewriteValueLOONG64_OpLOONG64MOVBUload(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
// match: (MOVBUload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVBUload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -1758,7 +1760,7 @@ func rewriteValueLOONG64_OpLOONG64MOVBUload(v *Value) bool {
|
|||
sym2 := auxToSym(v_0.Aux)
|
||||
ptr := v_0.Args[0]
|
||||
mem := v_1
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVBUload)
|
||||
|
|
@ -1771,6 +1773,26 @@ func rewriteValueLOONG64_OpLOONG64MOVBUload(v *Value) bool {
|
|||
}
|
||||
func rewriteValueLOONG64_OpLOONG64MOVBUreg(v *Value) bool {
|
||||
v_0 := v.Args[0]
|
||||
// match: (MOVBUreg x:(SGT _ _))
|
||||
// result: x
|
||||
for {
|
||||
x := v_0
|
||||
if x.Op != OpLOONG64SGT {
|
||||
break
|
||||
}
|
||||
v.copyOf(x)
|
||||
return true
|
||||
}
|
||||
// match: (MOVBUreg x:(SGTU _ _))
|
||||
// result: x
|
||||
for {
|
||||
x := v_0
|
||||
if x.Op != OpLOONG64SGTU {
|
||||
break
|
||||
}
|
||||
v.copyOf(x)
|
||||
return true
|
||||
}
|
||||
// match: (MOVBUreg x:(MOVBUload _ _))
|
||||
// result: (MOVVreg x)
|
||||
for {
|
||||
|
|
@ -1809,8 +1831,10 @@ func rewriteValueLOONG64_OpLOONG64MOVBUreg(v *Value) bool {
|
|||
func rewriteValueLOONG64_OpLOONG64MOVBload(v *Value) bool {
|
||||
v_1 := v.Args[1]
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
config := b.Func.Config
|
||||
// match: (MOVBload [off1] {sym} (ADDVconst [off2] ptr) mem)
|
||||
// cond: is32Bit(int64(off1)+off2)
|
||||
// cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVBload [off1+int32(off2)] {sym} ptr mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -1821,7 +1845,7 @@ func rewriteValueLOONG64_OpLOONG64MOVBload(v *Value) bool {
|
|||
off2 := auxIntToInt64(v_0.AuxInt)
|
||||
ptr := v_0.Args[0]
|
||||
mem := v_1
|
||||
if !(is32Bit(int64(off1) + off2)) {
|
||||
if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVBload)
|
||||
|
|
@ -1831,7 +1855,7 @@ func rewriteValueLOONG64_OpLOONG64MOVBload(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
// match: (MOVBload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVBload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -1843,7 +1867,7 @@ func rewriteValueLOONG64_OpLOONG64MOVBload(v *Value) bool {
|
|||
sym2 := auxToSym(v_0.Aux)
|
||||
ptr := v_0.Args[0]
|
||||
mem := v_1
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVBload)
|
||||
|
|
@ -1895,8 +1919,10 @@ func rewriteValueLOONG64_OpLOONG64MOVBstore(v *Value) bool {
|
|||
v_2 := v.Args[2]
|
||||
v_1 := v.Args[1]
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
config := b.Func.Config
|
||||
// match: (MOVBstore [off1] {sym} (ADDVconst [off2] ptr) val mem)
|
||||
// cond: is32Bit(int64(off1)+off2)
|
||||
// cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVBstore [off1+int32(off2)] {sym} ptr val mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -1908,7 +1934,7 @@ func rewriteValueLOONG64_OpLOONG64MOVBstore(v *Value) bool {
|
|||
ptr := v_0.Args[0]
|
||||
val := v_1
|
||||
mem := v_2
|
||||
if !(is32Bit(int64(off1) + off2)) {
|
||||
if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVBstore)
|
||||
|
|
@ -1918,7 +1944,7 @@ func rewriteValueLOONG64_OpLOONG64MOVBstore(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
// match: (MOVBstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVBstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -1931,7 +1957,7 @@ func rewriteValueLOONG64_OpLOONG64MOVBstore(v *Value) bool {
|
|||
ptr := v_0.Args[0]
|
||||
val := v_1
|
||||
mem := v_2
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVBstore)
|
||||
|
|
@ -2047,8 +2073,10 @@ func rewriteValueLOONG64_OpLOONG64MOVBstore(v *Value) bool {
|
|||
func rewriteValueLOONG64_OpLOONG64MOVBstorezero(v *Value) bool {
|
||||
v_1 := v.Args[1]
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
config := b.Func.Config
|
||||
// match: (MOVBstorezero [off1] {sym} (ADDVconst [off2] ptr) mem)
|
||||
// cond: is32Bit(int64(off1)+off2)
|
||||
// cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVBstorezero [off1+int32(off2)] {sym} ptr mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -2059,7 +2087,7 @@ func rewriteValueLOONG64_OpLOONG64MOVBstorezero(v *Value) bool {
|
|||
off2 := auxIntToInt64(v_0.AuxInt)
|
||||
ptr := v_0.Args[0]
|
||||
mem := v_1
|
||||
if !(is32Bit(int64(off1) + off2)) {
|
||||
if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVBstorezero)
|
||||
|
|
@ -2069,7 +2097,7 @@ func rewriteValueLOONG64_OpLOONG64MOVBstorezero(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
// match: (MOVBstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVBstorezero [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -2081,7 +2109,7 @@ func rewriteValueLOONG64_OpLOONG64MOVBstorezero(v *Value) bool {
|
|||
sym2 := auxToSym(v_0.Aux)
|
||||
ptr := v_0.Args[0]
|
||||
mem := v_1
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVBstorezero)
|
||||
|
|
@ -2095,8 +2123,10 @@ func rewriteValueLOONG64_OpLOONG64MOVBstorezero(v *Value) bool {
|
|||
func rewriteValueLOONG64_OpLOONG64MOVDload(v *Value) bool {
|
||||
v_1 := v.Args[1]
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
config := b.Func.Config
|
||||
// match: (MOVDload [off1] {sym} (ADDVconst [off2] ptr) mem)
|
||||
// cond: is32Bit(int64(off1)+off2)
|
||||
// cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVDload [off1+int32(off2)] {sym} ptr mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -2107,7 +2137,7 @@ func rewriteValueLOONG64_OpLOONG64MOVDload(v *Value) bool {
|
|||
off2 := auxIntToInt64(v_0.AuxInt)
|
||||
ptr := v_0.Args[0]
|
||||
mem := v_1
|
||||
if !(is32Bit(int64(off1) + off2)) {
|
||||
if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVDload)
|
||||
|
|
@ -2117,7 +2147,7 @@ func rewriteValueLOONG64_OpLOONG64MOVDload(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
// match: (MOVDload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVDload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -2129,7 +2159,7 @@ func rewriteValueLOONG64_OpLOONG64MOVDload(v *Value) bool {
|
|||
sym2 := auxToSym(v_0.Aux)
|
||||
ptr := v_0.Args[0]
|
||||
mem := v_1
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVDload)
|
||||
|
|
@ -2144,8 +2174,10 @@ func rewriteValueLOONG64_OpLOONG64MOVDstore(v *Value) bool {
|
|||
v_2 := v.Args[2]
|
||||
v_1 := v.Args[1]
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
config := b.Func.Config
|
||||
// match: (MOVDstore [off1] {sym} (ADDVconst [off2] ptr) val mem)
|
||||
// cond: is32Bit(int64(off1)+off2)
|
||||
// cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVDstore [off1+int32(off2)] {sym} ptr val mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -2157,7 +2189,7 @@ func rewriteValueLOONG64_OpLOONG64MOVDstore(v *Value) bool {
|
|||
ptr := v_0.Args[0]
|
||||
val := v_1
|
||||
mem := v_2
|
||||
if !(is32Bit(int64(off1) + off2)) {
|
||||
if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVDstore)
|
||||
|
|
@ -2167,7 +2199,7 @@ func rewriteValueLOONG64_OpLOONG64MOVDstore(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
// match: (MOVDstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVDstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -2180,7 +2212,7 @@ func rewriteValueLOONG64_OpLOONG64MOVDstore(v *Value) bool {
|
|||
ptr := v_0.Args[0]
|
||||
val := v_1
|
||||
mem := v_2
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVDstore)
|
||||
|
|
@ -2194,8 +2226,10 @@ func rewriteValueLOONG64_OpLOONG64MOVDstore(v *Value) bool {
|
|||
func rewriteValueLOONG64_OpLOONG64MOVFload(v *Value) bool {
|
||||
v_1 := v.Args[1]
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
config := b.Func.Config
|
||||
// match: (MOVFload [off1] {sym} (ADDVconst [off2] ptr) mem)
|
||||
// cond: is32Bit(int64(off1)+off2)
|
||||
// cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVFload [off1+int32(off2)] {sym} ptr mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -2206,7 +2240,7 @@ func rewriteValueLOONG64_OpLOONG64MOVFload(v *Value) bool {
|
|||
off2 := auxIntToInt64(v_0.AuxInt)
|
||||
ptr := v_0.Args[0]
|
||||
mem := v_1
|
||||
if !(is32Bit(int64(off1) + off2)) {
|
||||
if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVFload)
|
||||
|
|
@ -2216,7 +2250,7 @@ func rewriteValueLOONG64_OpLOONG64MOVFload(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
// match: (MOVFload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVFload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -2228,7 +2262,7 @@ func rewriteValueLOONG64_OpLOONG64MOVFload(v *Value) bool {
|
|||
sym2 := auxToSym(v_0.Aux)
|
||||
ptr := v_0.Args[0]
|
||||
mem := v_1
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVFload)
|
||||
|
|
@ -2243,8 +2277,10 @@ func rewriteValueLOONG64_OpLOONG64MOVFstore(v *Value) bool {
|
|||
v_2 := v.Args[2]
|
||||
v_1 := v.Args[1]
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
config := b.Func.Config
|
||||
// match: (MOVFstore [off1] {sym} (ADDVconst [off2] ptr) val mem)
|
||||
// cond: is32Bit(int64(off1)+off2)
|
||||
// cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVFstore [off1+int32(off2)] {sym} ptr val mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -2256,7 +2292,7 @@ func rewriteValueLOONG64_OpLOONG64MOVFstore(v *Value) bool {
|
|||
ptr := v_0.Args[0]
|
||||
val := v_1
|
||||
mem := v_2
|
||||
if !(is32Bit(int64(off1) + off2)) {
|
||||
if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVFstore)
|
||||
|
|
@ -2266,7 +2302,7 @@ func rewriteValueLOONG64_OpLOONG64MOVFstore(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
// match: (MOVFstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVFstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -2279,7 +2315,7 @@ func rewriteValueLOONG64_OpLOONG64MOVFstore(v *Value) bool {
|
|||
ptr := v_0.Args[0]
|
||||
val := v_1
|
||||
mem := v_2
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVFstore)
|
||||
|
|
@ -2293,8 +2329,10 @@ func rewriteValueLOONG64_OpLOONG64MOVFstore(v *Value) bool {
|
|||
func rewriteValueLOONG64_OpLOONG64MOVHUload(v *Value) bool {
|
||||
v_1 := v.Args[1]
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
config := b.Func.Config
|
||||
// match: (MOVHUload [off1] {sym} (ADDVconst [off2] ptr) mem)
|
||||
// cond: is32Bit(int64(off1)+off2)
|
||||
// cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVHUload [off1+int32(off2)] {sym} ptr mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -2305,7 +2343,7 @@ func rewriteValueLOONG64_OpLOONG64MOVHUload(v *Value) bool {
|
|||
off2 := auxIntToInt64(v_0.AuxInt)
|
||||
ptr := v_0.Args[0]
|
||||
mem := v_1
|
||||
if !(is32Bit(int64(off1) + off2)) {
|
||||
if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVHUload)
|
||||
|
|
@ -2315,7 +2353,7 @@ func rewriteValueLOONG64_OpLOONG64MOVHUload(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
// match: (MOVHUload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVHUload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -2327,7 +2365,7 @@ func rewriteValueLOONG64_OpLOONG64MOVHUload(v *Value) bool {
|
|||
sym2 := auxToSym(v_0.Aux)
|
||||
ptr := v_0.Args[0]
|
||||
mem := v_1
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVHUload)
|
||||
|
|
@ -2400,8 +2438,10 @@ func rewriteValueLOONG64_OpLOONG64MOVHUreg(v *Value) bool {
|
|||
func rewriteValueLOONG64_OpLOONG64MOVHload(v *Value) bool {
|
||||
v_1 := v.Args[1]
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
config := b.Func.Config
|
||||
// match: (MOVHload [off1] {sym} (ADDVconst [off2] ptr) mem)
|
||||
// cond: is32Bit(int64(off1)+off2)
|
||||
// cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVHload [off1+int32(off2)] {sym} ptr mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -2412,7 +2452,7 @@ func rewriteValueLOONG64_OpLOONG64MOVHload(v *Value) bool {
|
|||
off2 := auxIntToInt64(v_0.AuxInt)
|
||||
ptr := v_0.Args[0]
|
||||
mem := v_1
|
||||
if !(is32Bit(int64(off1) + off2)) {
|
||||
if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVHload)
|
||||
|
|
@ -2422,7 +2462,7 @@ func rewriteValueLOONG64_OpLOONG64MOVHload(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
// match: (MOVHload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVHload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -2434,7 +2474,7 @@ func rewriteValueLOONG64_OpLOONG64MOVHload(v *Value) bool {
|
|||
sym2 := auxToSym(v_0.Aux)
|
||||
ptr := v_0.Args[0]
|
||||
mem := v_1
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVHload)
|
||||
|
|
@ -2530,8 +2570,10 @@ func rewriteValueLOONG64_OpLOONG64MOVHstore(v *Value) bool {
|
|||
v_2 := v.Args[2]
|
||||
v_1 := v.Args[1]
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
config := b.Func.Config
|
||||
// match: (MOVHstore [off1] {sym} (ADDVconst [off2] ptr) val mem)
|
||||
// cond: is32Bit(int64(off1)+off2)
|
||||
// cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVHstore [off1+int32(off2)] {sym} ptr val mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -2543,7 +2585,7 @@ func rewriteValueLOONG64_OpLOONG64MOVHstore(v *Value) bool {
|
|||
ptr := v_0.Args[0]
|
||||
val := v_1
|
||||
mem := v_2
|
||||
if !(is32Bit(int64(off1) + off2)) {
|
||||
if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVHstore)
|
||||
|
|
@ -2553,7 +2595,7 @@ func rewriteValueLOONG64_OpLOONG64MOVHstore(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
// match: (MOVHstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVHstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -2566,7 +2608,7 @@ func rewriteValueLOONG64_OpLOONG64MOVHstore(v *Value) bool {
|
|||
ptr := v_0.Args[0]
|
||||
val := v_1
|
||||
mem := v_2
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVHstore)
|
||||
|
|
@ -2648,8 +2690,10 @@ func rewriteValueLOONG64_OpLOONG64MOVHstore(v *Value) bool {
|
|||
func rewriteValueLOONG64_OpLOONG64MOVHstorezero(v *Value) bool {
|
||||
v_1 := v.Args[1]
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
config := b.Func.Config
|
||||
// match: (MOVHstorezero [off1] {sym} (ADDVconst [off2] ptr) mem)
|
||||
// cond: is32Bit(int64(off1)+off2)
|
||||
// cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVHstorezero [off1+int32(off2)] {sym} ptr mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -2660,7 +2704,7 @@ func rewriteValueLOONG64_OpLOONG64MOVHstorezero(v *Value) bool {
|
|||
off2 := auxIntToInt64(v_0.AuxInt)
|
||||
ptr := v_0.Args[0]
|
||||
mem := v_1
|
||||
if !(is32Bit(int64(off1) + off2)) {
|
||||
if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVHstorezero)
|
||||
|
|
@ -2670,7 +2714,7 @@ func rewriteValueLOONG64_OpLOONG64MOVHstorezero(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
// match: (MOVHstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVHstorezero [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -2682,7 +2726,7 @@ func rewriteValueLOONG64_OpLOONG64MOVHstorezero(v *Value) bool {
|
|||
sym2 := auxToSym(v_0.Aux)
|
||||
ptr := v_0.Args[0]
|
||||
mem := v_1
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVHstorezero)
|
||||
|
|
@ -2696,8 +2740,10 @@ func rewriteValueLOONG64_OpLOONG64MOVHstorezero(v *Value) bool {
|
|||
func rewriteValueLOONG64_OpLOONG64MOVVload(v *Value) bool {
|
||||
v_1 := v.Args[1]
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
config := b.Func.Config
|
||||
// match: (MOVVload [off1] {sym} (ADDVconst [off2] ptr) mem)
|
||||
// cond: is32Bit(int64(off1)+off2)
|
||||
// cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVVload [off1+int32(off2)] {sym} ptr mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -2708,7 +2754,7 @@ func rewriteValueLOONG64_OpLOONG64MOVVload(v *Value) bool {
|
|||
off2 := auxIntToInt64(v_0.AuxInt)
|
||||
ptr := v_0.Args[0]
|
||||
mem := v_1
|
||||
if !(is32Bit(int64(off1) + off2)) {
|
||||
if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVVload)
|
||||
|
|
@ -2718,7 +2764,7 @@ func rewriteValueLOONG64_OpLOONG64MOVVload(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
// match: (MOVVload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVVload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -2730,7 +2776,7 @@ func rewriteValueLOONG64_OpLOONG64MOVVload(v *Value) bool {
|
|||
sym2 := auxToSym(v_0.Aux)
|
||||
ptr := v_0.Args[0]
|
||||
mem := v_1
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVVload)
|
||||
|
|
@ -2772,8 +2818,10 @@ func rewriteValueLOONG64_OpLOONG64MOVVstore(v *Value) bool {
|
|||
v_2 := v.Args[2]
|
||||
v_1 := v.Args[1]
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
config := b.Func.Config
|
||||
// match: (MOVVstore [off1] {sym} (ADDVconst [off2] ptr) val mem)
|
||||
// cond: is32Bit(int64(off1)+off2)
|
||||
// cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVVstore [off1+int32(off2)] {sym} ptr val mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -2785,7 +2833,7 @@ func rewriteValueLOONG64_OpLOONG64MOVVstore(v *Value) bool {
|
|||
ptr := v_0.Args[0]
|
||||
val := v_1
|
||||
mem := v_2
|
||||
if !(is32Bit(int64(off1) + off2)) {
|
||||
if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVVstore)
|
||||
|
|
@ -2795,7 +2843,7 @@ func rewriteValueLOONG64_OpLOONG64MOVVstore(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
// match: (MOVVstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVVstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -2808,7 +2856,7 @@ func rewriteValueLOONG64_OpLOONG64MOVVstore(v *Value) bool {
|
|||
ptr := v_0.Args[0]
|
||||
val := v_1
|
||||
mem := v_2
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVVstore)
|
||||
|
|
@ -2822,8 +2870,10 @@ func rewriteValueLOONG64_OpLOONG64MOVVstore(v *Value) bool {
|
|||
func rewriteValueLOONG64_OpLOONG64MOVVstorezero(v *Value) bool {
|
||||
v_1 := v.Args[1]
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
config := b.Func.Config
|
||||
// match: (MOVVstorezero [off1] {sym} (ADDVconst [off2] ptr) mem)
|
||||
// cond: is32Bit(int64(off1)+off2)
|
||||
// cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVVstorezero [off1+int32(off2)] {sym} ptr mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -2834,7 +2884,7 @@ func rewriteValueLOONG64_OpLOONG64MOVVstorezero(v *Value) bool {
|
|||
off2 := auxIntToInt64(v_0.AuxInt)
|
||||
ptr := v_0.Args[0]
|
||||
mem := v_1
|
||||
if !(is32Bit(int64(off1) + off2)) {
|
||||
if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVVstorezero)
|
||||
|
|
@ -2844,7 +2894,7 @@ func rewriteValueLOONG64_OpLOONG64MOVVstorezero(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
// match: (MOVVstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVVstorezero [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -2856,7 +2906,7 @@ func rewriteValueLOONG64_OpLOONG64MOVVstorezero(v *Value) bool {
|
|||
sym2 := auxToSym(v_0.Aux)
|
||||
ptr := v_0.Args[0]
|
||||
mem := v_1
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVVstorezero)
|
||||
|
|
@ -2870,8 +2920,10 @@ func rewriteValueLOONG64_OpLOONG64MOVVstorezero(v *Value) bool {
|
|||
func rewriteValueLOONG64_OpLOONG64MOVWUload(v *Value) bool {
|
||||
v_1 := v.Args[1]
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
config := b.Func.Config
|
||||
// match: (MOVWUload [off1] {sym} (ADDVconst [off2] ptr) mem)
|
||||
// cond: is32Bit(int64(off1)+off2)
|
||||
// cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVWUload [off1+int32(off2)] {sym} ptr mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -2882,7 +2934,7 @@ func rewriteValueLOONG64_OpLOONG64MOVWUload(v *Value) bool {
|
|||
off2 := auxIntToInt64(v_0.AuxInt)
|
||||
ptr := v_0.Args[0]
|
||||
mem := v_1
|
||||
if !(is32Bit(int64(off1) + off2)) {
|
||||
if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVWUload)
|
||||
|
|
@ -2892,7 +2944,7 @@ func rewriteValueLOONG64_OpLOONG64MOVWUload(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
// match: (MOVWUload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVWUload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -2904,7 +2956,7 @@ func rewriteValueLOONG64_OpLOONG64MOVWUload(v *Value) bool {
|
|||
sym2 := auxToSym(v_0.Aux)
|
||||
ptr := v_0.Args[0]
|
||||
mem := v_1
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVWUload)
|
||||
|
|
@ -2999,8 +3051,10 @@ func rewriteValueLOONG64_OpLOONG64MOVWUreg(v *Value) bool {
|
|||
func rewriteValueLOONG64_OpLOONG64MOVWload(v *Value) bool {
|
||||
v_1 := v.Args[1]
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
config := b.Func.Config
|
||||
// match: (MOVWload [off1] {sym} (ADDVconst [off2] ptr) mem)
|
||||
// cond: is32Bit(int64(off1)+off2)
|
||||
// cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVWload [off1+int32(off2)] {sym} ptr mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -3011,7 +3065,7 @@ func rewriteValueLOONG64_OpLOONG64MOVWload(v *Value) bool {
|
|||
off2 := auxIntToInt64(v_0.AuxInt)
|
||||
ptr := v_0.Args[0]
|
||||
mem := v_1
|
||||
if !(is32Bit(int64(off1) + off2)) {
|
||||
if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVWload)
|
||||
|
|
@ -3021,7 +3075,7 @@ func rewriteValueLOONG64_OpLOONG64MOVWload(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
// match: (MOVWload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVWload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -3033,7 +3087,7 @@ func rewriteValueLOONG64_OpLOONG64MOVWload(v *Value) bool {
|
|||
sym2 := auxToSym(v_0.Aux)
|
||||
ptr := v_0.Args[0]
|
||||
mem := v_1
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVWload)
|
||||
|
|
@ -3162,8 +3216,10 @@ func rewriteValueLOONG64_OpLOONG64MOVWstore(v *Value) bool {
|
|||
v_2 := v.Args[2]
|
||||
v_1 := v.Args[1]
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
config := b.Func.Config
|
||||
// match: (MOVWstore [off1] {sym} (ADDVconst [off2] ptr) val mem)
|
||||
// cond: is32Bit(int64(off1)+off2)
|
||||
// cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVWstore [off1+int32(off2)] {sym} ptr val mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -3175,7 +3231,7 @@ func rewriteValueLOONG64_OpLOONG64MOVWstore(v *Value) bool {
|
|||
ptr := v_0.Args[0]
|
||||
val := v_1
|
||||
mem := v_2
|
||||
if !(is32Bit(int64(off1) + off2)) {
|
||||
if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVWstore)
|
||||
|
|
@ -3185,7 +3241,7 @@ func rewriteValueLOONG64_OpLOONG64MOVWstore(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
// match: (MOVWstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVWstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -3198,7 +3254,7 @@ func rewriteValueLOONG64_OpLOONG64MOVWstore(v *Value) bool {
|
|||
ptr := v_0.Args[0]
|
||||
val := v_1
|
||||
mem := v_2
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVWstore)
|
||||
|
|
@ -3246,8 +3302,10 @@ func rewriteValueLOONG64_OpLOONG64MOVWstore(v *Value) bool {
|
|||
func rewriteValueLOONG64_OpLOONG64MOVWstorezero(v *Value) bool {
|
||||
v_1 := v.Args[1]
|
||||
v_0 := v.Args[0]
|
||||
b := v.Block
|
||||
config := b.Func.Config
|
||||
// match: (MOVWstorezero [off1] {sym} (ADDVconst [off2] ptr) mem)
|
||||
// cond: is32Bit(int64(off1)+off2)
|
||||
// cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVWstorezero [off1+int32(off2)] {sym} ptr mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -3258,7 +3316,7 @@ func rewriteValueLOONG64_OpLOONG64MOVWstorezero(v *Value) bool {
|
|||
off2 := auxIntToInt64(v_0.AuxInt)
|
||||
ptr := v_0.Args[0]
|
||||
mem := v_1
|
||||
if !(is32Bit(int64(off1) + off2)) {
|
||||
if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVWstorezero)
|
||||
|
|
@ -3268,7 +3326,7 @@ func rewriteValueLOONG64_OpLOONG64MOVWstorezero(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
// match: (MOVWstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)
|
||||
// result: (MOVWstorezero [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem)
|
||||
for {
|
||||
off1 := auxIntToInt32(v.AuxInt)
|
||||
|
|
@ -3280,7 +3338,7 @@ func rewriteValueLOONG64_OpLOONG64MOVWstorezero(v *Value) bool {
|
|||
sym2 := auxToSym(v_0.Aux)
|
||||
ptr := v_0.Args[0]
|
||||
mem := v_1
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpLOONG64MOVWstorezero)
|
||||
|
|
@ -7570,6 +7628,7 @@ func rewriteValueLOONG64_OpZero(v *Value) bool {
|
|||
return false
|
||||
}
|
||||
func rewriteBlockLOONG64(b *Block) bool {
|
||||
typ := &b.Func.Config.Types
|
||||
switch b.Kind {
|
||||
case BlockLOONG64EQ:
|
||||
// match: (EQ (FPFlagTrue cmp) yes no)
|
||||
|
|
@ -7769,10 +7828,12 @@ func rewriteBlockLOONG64(b *Block) bool {
|
|||
}
|
||||
case BlockIf:
|
||||
// match: (If cond yes no)
|
||||
// result: (NE cond yes no)
|
||||
// result: (NE (MOVBUreg <typ.UInt64> cond) yes no)
|
||||
for {
|
||||
cond := b.Controls[0]
|
||||
b.resetWithControl(BlockLOONG64NE, cond)
|
||||
v0 := b.NewValue0(cond.Pos, OpLOONG64MOVBUreg, typ.UInt64)
|
||||
v0.AddArg(cond)
|
||||
b.resetWithControl(BlockLOONG64NE, v0)
|
||||
return true
|
||||
}
|
||||
case BlockLOONG64LEZ:
|
||||
|
|
|
|||
|
|
@ -1627,7 +1627,7 @@ func buildModeSupported(compiler, buildmode, goos, goarch string) bool {
|
|||
|
||||
case "plugin":
|
||||
switch platform {
|
||||
case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/s390x", "linux/ppc64le",
|
||||
case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/loong64", "linux/s390x", "linux/ppc64le",
|
||||
"android/amd64", "android/386",
|
||||
"darwin/amd64", "darwin/arm64",
|
||||
"freebsd/amd64":
|
||||
|
|
|
|||
|
|
@ -157,14 +157,14 @@ const (
|
|||
REGZERO = REG_R0 // set to zero
|
||||
REGLINK = REG_R1
|
||||
REGSP = REG_R3
|
||||
REGRET = REG_R19
|
||||
REGRET = REG_R20 // not use
|
||||
REGARG = -1 // -1 disables passing the first argument in register
|
||||
REGRT1 = REG_R19 // reserved for runtime, duffzero and duffcopy
|
||||
REGRT2 = REG_R20 // reserved for runtime, duffcopy
|
||||
REGRT1 = REG_R20 // reserved for runtime, duffzero and duffcopy
|
||||
REGRT2 = REG_R21 // reserved for runtime, duffcopy
|
||||
REGCTXT = REG_R29 // context for closures
|
||||
REGG = REG_R22 // G in loong64
|
||||
REGTMP = REG_R30 // used by the assembler
|
||||
FREGRET = REG_F0
|
||||
FREGRET = REG_F0 // not use
|
||||
)
|
||||
|
||||
var LOONG64DWARFRegisters = map[int16]int16{}
|
||||
|
|
@ -227,6 +227,7 @@ const (
|
|||
C_ADDR
|
||||
C_TLS_LE
|
||||
C_TLS_IE
|
||||
C_GOTADDR
|
||||
C_TEXTSIZE
|
||||
|
||||
C_NCLASS // must be the last
|
||||
|
|
|
|||
|
|
@ -349,6 +349,8 @@ var optab = []Optab{
|
|||
{AWORD, C_LCON, C_NONE, C_NONE, C_NONE, C_NONE, 40, 4, 0, 0},
|
||||
{AWORD, C_DCON, C_NONE, C_NONE, C_NONE, C_NONE, 61, 4, 0, 0},
|
||||
|
||||
{AMOVV, C_GOTADDR, C_NONE, C_NONE, C_REG, C_NONE, 65, 8, 0, 0},
|
||||
|
||||
{ATEQ, C_SCON, C_REG, C_NONE, C_REG, C_NONE, 15, 8, 0, 0},
|
||||
{ATEQ, C_SCON, C_NONE, C_NONE, C_REG, C_NONE, 15, 8, 0, 0},
|
||||
|
||||
|
|
@ -676,6 +678,9 @@ func (c *ctxt0) aclass(a *obj.Addr) int {
|
|||
return C_SOREG
|
||||
}
|
||||
return C_LOREG
|
||||
|
||||
case obj.NAME_GOTREF:
|
||||
return C_GOTADDR
|
||||
}
|
||||
|
||||
return C_GOK
|
||||
|
|
@ -1776,6 +1781,22 @@ func (c *ctxt0) asmout(p *obj.Prog, o *Optab, out []uint32) {
|
|||
case 64: // movv c_reg, c_fcc0 ==> movgr2cf cd, rj
|
||||
a := OP_TEN(8, 1334)
|
||||
o1 = OP_RR(a, uint32(p.From.Reg), uint32(p.To.Reg))
|
||||
|
||||
case 65: // mov sym@GOT, r ==> pcalau12i + ld.d
|
||||
o1 = OP_IR(c.opir(APCALAU12I), uint32(0), uint32(p.To.Reg))
|
||||
rel := obj.Addrel(c.cursym)
|
||||
rel.Off = int32(c.pc)
|
||||
rel.Siz = 4
|
||||
rel.Sym = p.From.Sym
|
||||
rel.Type = objabi.R_LOONG64_GOT_HI
|
||||
rel.Add = 0x0
|
||||
o2 = OP_12IRR(c.opirr(-p.As), uint32(0), uint32(p.To.Reg), uint32(p.To.Reg))
|
||||
rel2 := obj.Addrel(c.cursym)
|
||||
rel2.Off = int32(c.pc + 4)
|
||||
rel2.Siz = 4
|
||||
rel2.Sym = p.From.Sym
|
||||
rel2.Type = objabi.R_LOONG64_GOT_LO
|
||||
rel2.Add = 0x0
|
||||
}
|
||||
|
||||
out[0] = o1
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ var cnames0 = []string{
|
|||
"ADDR",
|
||||
"TLS_LE",
|
||||
"TLS_IE",
|
||||
"GOTADDR",
|
||||
"TEXTSIZE",
|
||||
"NCLASS",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ package loong64
|
|||
|
||||
import (
|
||||
"cmd/internal/obj"
|
||||
"cmd/internal/objabi"
|
||||
"cmd/internal/sys"
|
||||
"internal/abi"
|
||||
"log"
|
||||
|
|
@ -84,6 +85,122 @@ func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
|
|||
p.As = AADDVU
|
||||
}
|
||||
}
|
||||
|
||||
if ctxt.Flag_dynlink {
|
||||
rewriteToUseGot(ctxt, p, newprog)
|
||||
}
|
||||
}
|
||||
|
||||
func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
|
||||
// ADUFFxxx $offset
|
||||
// becomes
|
||||
// MOVV runtime.duffxxx@GOT, REGTMP
|
||||
// ADD $offset, REGTMP
|
||||
// JAL REGTMP
|
||||
if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
|
||||
var sym *obj.LSym
|
||||
if p.As == obj.ADUFFZERO {
|
||||
sym = ctxt.Lookup("runtime.duffzero")
|
||||
} else {
|
||||
sym = ctxt.Lookup("runtime.duffcopy")
|
||||
}
|
||||
offset := p.To.Offset
|
||||
p.As = AMOVV
|
||||
p.From.Type = obj.TYPE_MEM
|
||||
p.From.Sym = sym
|
||||
p.From.Name = obj.NAME_GOTREF
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = REGTMP
|
||||
p.To.Name = obj.NAME_NONE
|
||||
p.To.Offset = 0
|
||||
p.To.Sym = nil
|
||||
p1 := obj.Appendp(p, newprog)
|
||||
p1.As = AADDV
|
||||
p1.From.Type = obj.TYPE_CONST
|
||||
p1.From.Offset = offset
|
||||
p1.To.Type = obj.TYPE_REG
|
||||
p1.To.Reg = REGTMP
|
||||
p2 := obj.Appendp(p1, newprog)
|
||||
p2.As = AJAL
|
||||
p2.To.Type = obj.TYPE_MEM
|
||||
p2.To.Reg = REGTMP
|
||||
}
|
||||
|
||||
// We only care about global data: NAME_EXTERN means a global
|
||||
// symbol in the Go sense, and p.Sym.Local is true for a few
|
||||
// internally defined symbols.
|
||||
if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
|
||||
// MOVV $sym, Rx becomes MOVV sym@GOT, Rx
|
||||
// MOVV $sym+<off>, Rx becomes MOVV sym@GOT, Rx; ADD <off>, Rx
|
||||
if p.As != AMOVV {
|
||||
ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -shared", p)
|
||||
}
|
||||
if p.To.Type != obj.TYPE_REG {
|
||||
ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -shared", p)
|
||||
}
|
||||
p.From.Type = obj.TYPE_MEM
|
||||
p.From.Name = obj.NAME_GOTREF
|
||||
if p.From.Offset != 0 {
|
||||
q := obj.Appendp(p, newprog)
|
||||
q.As = AADDV
|
||||
q.From.Type = obj.TYPE_CONST
|
||||
q.From.Offset = p.From.Offset
|
||||
q.To = p.To
|
||||
p.From.Offset = 0
|
||||
}
|
||||
}
|
||||
if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
|
||||
ctxt.Diag("don't know how to handle %v with -shared", p)
|
||||
}
|
||||
|
||||
var source *obj.Addr
|
||||
// MOVx sym, Ry becomes MOVV sym@GOT, REGTMP; MOVx (REGTMP), Ry
|
||||
// MOVx Ry, sym becomes MOVV sym@GOT, REGTMP; MOVx Ry, (REGTMP)
|
||||
// An addition may be inserted between the two MOVs if there is an offset.
|
||||
if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
|
||||
if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
|
||||
ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -shared", p)
|
||||
}
|
||||
source = &p.From
|
||||
} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
|
||||
source = &p.To
|
||||
} else {
|
||||
return
|
||||
}
|
||||
if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
|
||||
return
|
||||
}
|
||||
if source.Sym.Type == objabi.STLSBSS {
|
||||
return
|
||||
}
|
||||
if source.Type != obj.TYPE_MEM {
|
||||
ctxt.Diag("don't know how to handle %v with -shared", p)
|
||||
}
|
||||
p1 := obj.Appendp(p, newprog)
|
||||
p2 := obj.Appendp(p1, newprog)
|
||||
p1.As = AMOVV
|
||||
p1.From.Type = obj.TYPE_MEM
|
||||
p1.From.Sym = source.Sym
|
||||
p1.From.Name = obj.NAME_GOTREF
|
||||
p1.To.Type = obj.TYPE_REG
|
||||
p1.To.Reg = REGTMP
|
||||
|
||||
p2.As = p.As
|
||||
p2.From = p.From
|
||||
p2.To = p.To
|
||||
if p.From.Name == obj.NAME_EXTERN {
|
||||
p2.From.Reg = REGTMP
|
||||
p2.From.Name = obj.NAME_NONE
|
||||
p2.From.Sym = nil
|
||||
} else if p.To.Name == obj.NAME_EXTERN {
|
||||
p2.To.Reg = REGTMP
|
||||
p2.To.Name = obj.NAME_NONE
|
||||
p2.To.Sym = nil
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
obj.Nopout(p)
|
||||
}
|
||||
|
||||
func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
||||
|
|
@ -279,13 +396,13 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
|||
if c.cursym.Func().Text.From.Sym.Wrapper() && c.cursym.Func().Text.Mark&LEAF == 0 {
|
||||
// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
|
||||
//
|
||||
// MOV g_panic(g), R1
|
||||
// BEQ R1, end
|
||||
// MOV panic_argp(R1), R2
|
||||
// ADD $(autosize+FIXED_FRAME), R29, R3
|
||||
// BNE R2, R3, end
|
||||
// ADD $FIXED_FRAME, R29, R2
|
||||
// MOV R2, panic_argp(R1)
|
||||
// MOV g_panic(g), R20
|
||||
// BEQ R20, end
|
||||
// MOV panic_argp(R20), R24
|
||||
// ADD $(autosize+FIXED_FRAME), R3, R30
|
||||
// BNE R24, R30, end
|
||||
// ADD $FIXED_FRAME, R3, R24
|
||||
// MOV R24, panic_argp(R20)
|
||||
// end:
|
||||
// NOP
|
||||
//
|
||||
|
|
@ -302,12 +419,12 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
|||
q.From.Reg = REGG
|
||||
q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
|
||||
q.To.Type = obj.TYPE_REG
|
||||
q.To.Reg = REG_R19
|
||||
q.To.Reg = REG_R20
|
||||
|
||||
q = obj.Appendp(q, newprog)
|
||||
q.As = ABEQ
|
||||
q.From.Type = obj.TYPE_REG
|
||||
q.From.Reg = REG_R19
|
||||
q.From.Reg = REG_R20
|
||||
q.To.Type = obj.TYPE_BRANCH
|
||||
q.Mark |= BRANCH
|
||||
p1 = q
|
||||
|
|
@ -315,10 +432,10 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
|||
q = obj.Appendp(q, newprog)
|
||||
q.As = mov
|
||||
q.From.Type = obj.TYPE_MEM
|
||||
q.From.Reg = REG_R19
|
||||
q.From.Reg = REG_R20
|
||||
q.From.Offset = 0 // Panic.argp
|
||||
q.To.Type = obj.TYPE_REG
|
||||
q.To.Reg = REG_R4
|
||||
q.To.Reg = REG_R24
|
||||
|
||||
q = obj.Appendp(q, newprog)
|
||||
q.As = add
|
||||
|
|
@ -326,13 +443,13 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
|||
q.From.Offset = int64(autosize) + ctxt.Arch.FixedFrameSize
|
||||
q.Reg = REGSP
|
||||
q.To.Type = obj.TYPE_REG
|
||||
q.To.Reg = REG_R5
|
||||
q.To.Reg = REG_R30
|
||||
|
||||
q = obj.Appendp(q, newprog)
|
||||
q.As = ABNE
|
||||
q.From.Type = obj.TYPE_REG
|
||||
q.From.Reg = REG_R4
|
||||
q.Reg = REG_R5
|
||||
q.From.Reg = REG_R24
|
||||
q.Reg = REG_R30
|
||||
q.To.Type = obj.TYPE_BRANCH
|
||||
q.Mark |= BRANCH
|
||||
p2 = q
|
||||
|
|
@ -343,14 +460,14 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
|||
q.From.Offset = ctxt.Arch.FixedFrameSize
|
||||
q.Reg = REGSP
|
||||
q.To.Type = obj.TYPE_REG
|
||||
q.To.Reg = REG_R4
|
||||
q.To.Reg = REG_R24
|
||||
|
||||
q = obj.Appendp(q, newprog)
|
||||
q.As = mov
|
||||
q.From.Type = obj.TYPE_REG
|
||||
q.From.Reg = REG_R4
|
||||
q.From.Reg = REG_R24
|
||||
q.To.Type = obj.TYPE_MEM
|
||||
q.To.Reg = REG_R19
|
||||
q.To.Reg = REG_R20
|
||||
q.To.Offset = 0 // Panic.argp
|
||||
|
||||
q = obj.Appendp(q, newprog)
|
||||
|
|
@ -503,6 +620,10 @@ func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
|
|||
|
||||
p = c.ctxt.StartUnsafePoint(p, c.newprog)
|
||||
|
||||
// Spill Arguments. This has to happen before we open
|
||||
// any more frame space.
|
||||
p = c.cursym.Func().SpillRegisterArgs(p, c.newprog)
|
||||
|
||||
// MOV REGLINK, -8/-16(SP)
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = mov
|
||||
|
|
@ -567,13 +688,15 @@ func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
|
|||
p.To.Reg = REGSP
|
||||
p.Spadj = int32(-frameSize)
|
||||
|
||||
// Unspill arguments
|
||||
p = c.cursym.Func().UnspillRegisterArgs(p, c.newprog)
|
||||
p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
|
||||
}
|
||||
|
||||
// Jump back to here after morestack returns.
|
||||
startPred := p
|
||||
|
||||
// MOV g_stackguard(g), R19
|
||||
// MOV g_stackguard(g), R20
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
|
||||
p.As = mov
|
||||
|
|
@ -584,7 +707,7 @@ func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
|
|||
p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
|
||||
}
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = REG_R19
|
||||
p.To.Reg = REG_R20
|
||||
|
||||
// Mark the stack bound check and morestack call async nonpreemptible.
|
||||
// If we get preempted here, when resumed the preemption request is
|
||||
|
|
@ -595,15 +718,15 @@ func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
|
|||
var q *obj.Prog
|
||||
if framesize <= abi.StackSmall {
|
||||
// small stack: SP < stackguard
|
||||
// AGTU SP, stackguard, R19
|
||||
// AGTU SP, stackguard, R20
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
|
||||
p.As = ASGTU
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = REGSP
|
||||
p.Reg = REG_R19
|
||||
p.Reg = REG_R20
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = REG_R19
|
||||
p.To.Reg = REG_R20
|
||||
} else {
|
||||
// large stack: SP-framesize < stackguard-StackSmall
|
||||
offset := int64(framesize) - abi.StackSmall
|
||||
|
|
@ -615,8 +738,8 @@ func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
|
|||
// stack guard to incorrectly succeed. We explicitly
|
||||
// guard against underflow.
|
||||
//
|
||||
// SGTU $(framesize-StackSmall), SP, R4
|
||||
// BNE R4, label-of-call-to-morestack
|
||||
// SGTU $(framesize-StackSmall), SP, R24
|
||||
// BNE R24, label-of-call-to-morestack
|
||||
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = ASGTU
|
||||
|
|
@ -624,13 +747,13 @@ func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
|
|||
p.From.Offset = offset
|
||||
p.Reg = REGSP
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = REG_R4
|
||||
p.To.Reg = REG_R24
|
||||
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
q = p
|
||||
p.As = ABNE
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = REG_R4
|
||||
p.From.Reg = REG_R24
|
||||
p.To.Type = obj.TYPE_BRANCH
|
||||
p.Mark |= BRANCH
|
||||
}
|
||||
|
|
@ -642,35 +765,35 @@ func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
|
|||
p.From.Offset = -offset
|
||||
p.Reg = REGSP
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = REG_R4
|
||||
p.To.Reg = REG_R24
|
||||
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = ASGTU
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = REG_R4
|
||||
p.Reg = REG_R19
|
||||
p.From.Reg = REG_R24
|
||||
p.Reg = REG_R20
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = REG_R19
|
||||
p.To.Reg = REG_R20
|
||||
}
|
||||
|
||||
// q1: BNE R19, done
|
||||
// q1: BNE R20, done
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
q1 := p
|
||||
|
||||
p.As = ABNE
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = REG_R19
|
||||
p.From.Reg = REG_R20
|
||||
p.To.Type = obj.TYPE_BRANCH
|
||||
p.Mark |= BRANCH
|
||||
|
||||
// MOV LINK, R5
|
||||
// MOV LINK, R31
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
|
||||
p.As = mov
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = REGLINK
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = REG_R5
|
||||
p.To.Reg = REG_R31
|
||||
if q != nil {
|
||||
q.To.SetTarget(p)
|
||||
p.Mark |= LABEL
|
||||
|
|
@ -678,6 +801,10 @@ func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
|
|||
|
||||
p = c.ctxt.EmitEntryStackMap(c.cursym, p, c.newprog)
|
||||
|
||||
// Spill the register args that could be clobbered by the
|
||||
// morestack code
|
||||
p = c.cursym.Func().SpillRegisterArgs(p, c.newprog)
|
||||
|
||||
// JAL runtime.morestack(SB)
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
|
||||
|
|
@ -692,6 +819,7 @@ func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
|
|||
}
|
||||
p.Mark |= BRANCH
|
||||
|
||||
p = c.cursym.Func().UnspillRegisterArgs(p, c.newprog)
|
||||
p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
|
||||
|
||||
// JMP start
|
||||
|
|
|
|||
|
|
@ -345,6 +345,11 @@ const (
|
|||
R_LOONG64_TLS_IE_PCREL_HI
|
||||
R_LOONG64_TLS_IE_LO
|
||||
|
||||
// R_LOONG64_GOT_HI and R_LOONG64_GOT_LO resolves a GOT-relative instruction sequence,
|
||||
// usually an pcalau12i followed by another ld or addi instruction.
|
||||
R_LOONG64_GOT_HI
|
||||
R_LOONG64_GOT_LO
|
||||
|
||||
// R_JMPLOONG64 resolves to non-PC-relative target address of a JMP instruction,
|
||||
// by encoding the address into the instruction.
|
||||
R_JMPLOONG64
|
||||
|
|
|
|||
|
|
@ -89,19 +89,21 @@ func _() {
|
|||
_ = x[R_CALLLOONG64-79]
|
||||
_ = x[R_LOONG64_TLS_IE_PCREL_HI-80]
|
||||
_ = x[R_LOONG64_TLS_IE_LO-81]
|
||||
_ = x[R_JMPLOONG64-82]
|
||||
_ = x[R_ADDRMIPSU-83]
|
||||
_ = x[R_ADDRMIPSTLS-84]
|
||||
_ = x[R_ADDRCUOFF-85]
|
||||
_ = x[R_WASMIMPORT-86]
|
||||
_ = x[R_XCOFFREF-87]
|
||||
_ = x[R_PEIMAGEOFF-88]
|
||||
_ = x[R_INITORDER-89]
|
||||
_ = x[R_LOONG64_GOT_HI-82]
|
||||
_ = x[R_LOONG64_GOT_LO-83]
|
||||
_ = x[R_JMPLOONG64-84]
|
||||
_ = x[R_ADDRMIPSU-85]
|
||||
_ = x[R_ADDRMIPSTLS-86]
|
||||
_ = x[R_ADDRCUOFF-87]
|
||||
_ = x[R_WASMIMPORT-88]
|
||||
_ = x[R_XCOFFREF-89]
|
||||
_ = x[R_PEIMAGEOFF-90]
|
||||
_ = x[R_INITORDER-91]
|
||||
}
|
||||
|
||||
const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_USEIFACER_USEIFACEMETHODR_USENAMEDMETHODR_METHODOFFR_KEEPR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFSECREFR_DWARFFILEREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_ARM64_GOTR_ARM64_PCRELR_ARM64_PCREL_LDST8R_ARM64_PCREL_LDST16R_ARM64_PCREL_LDST32R_ARM64_PCREL_LDST64R_ARM64_LDST8R_ARM64_LDST16R_ARM64_LDST32R_ARM64_LDST64R_ARM64_LDST128R_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_POWER_TLS_IE_PCREL34R_POWER_TLS_LE_TPREL34R_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_GOT_PCREL34R_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_ADDRPOWER_D34R_ADDRPOWER_PCREL34R_RISCV_JALR_RISCV_JAL_TRAMPR_RISCV_CALLR_RISCV_PCREL_ITYPER_RISCV_PCREL_STYPER_RISCV_TLS_IER_RISCV_TLS_LER_RISCV_GOT_HI20R_RISCV_PCREL_HI20R_RISCV_PCREL_LO12_IR_RISCV_PCREL_LO12_SR_RISCV_BRANCHR_RISCV_RVC_BRANCHR_RISCV_RVC_JUMPR_PCRELDBLR_ADDRLOONG64R_ADDRLOONG64UR_ADDRLOONG64TLSR_ADDRLOONG64TLSUR_CALLLOONG64R_LOONG64_TLS_IE_PCREL_HIR_LOONG64_TLS_IE_LOR_JMPLOONG64R_ADDRMIPSUR_ADDRMIPSTLSR_ADDRCUOFFR_WASMIMPORTR_XCOFFREFR_PEIMAGEOFFR_INITORDER"
|
||||
const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_USEIFACER_USEIFACEMETHODR_USENAMEDMETHODR_METHODOFFR_KEEPR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFSECREFR_DWARFFILEREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_ARM64_GOTR_ARM64_PCRELR_ARM64_PCREL_LDST8R_ARM64_PCREL_LDST16R_ARM64_PCREL_LDST32R_ARM64_PCREL_LDST64R_ARM64_LDST8R_ARM64_LDST16R_ARM64_LDST32R_ARM64_LDST64R_ARM64_LDST128R_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_POWER_TLS_IE_PCREL34R_POWER_TLS_LE_TPREL34R_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_GOT_PCREL34R_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_ADDRPOWER_D34R_ADDRPOWER_PCREL34R_RISCV_JALR_RISCV_JAL_TRAMPR_RISCV_CALLR_RISCV_PCREL_ITYPER_RISCV_PCREL_STYPER_RISCV_TLS_IER_RISCV_TLS_LER_RISCV_GOT_HI20R_RISCV_PCREL_HI20R_RISCV_PCREL_LO12_IR_RISCV_PCREL_LO12_SR_RISCV_BRANCHR_RISCV_RVC_BRANCHR_RISCV_RVC_JUMPR_PCRELDBLR_ADDRLOONG64R_ADDRLOONG64UR_ADDRLOONG64TLSR_ADDRLOONG64TLSUR_CALLLOONG64R_LOONG64_TLS_IE_PCREL_HIR_LOONG64_TLS_IE_LOR_LOONG64_GOT_HIR_LOONG64_GOT_LOR_JMPLOONG64R_ADDRMIPSUR_ADDRMIPSTLSR_ADDRCUOFFR_WASMIMPORTR_XCOFFREFR_PEIMAGEOFFR_INITORDER"
|
||||
|
||||
var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 53, 59, 68, 79, 88, 99, 109, 116, 123, 131, 139, 147, 153, 159, 165, 175, 184, 194, 210, 226, 237, 243, 254, 264, 273, 286, 300, 314, 328, 344, 355, 368, 387, 407, 427, 447, 460, 474, 488, 502, 517, 531, 545, 556, 578, 600, 614, 629, 652, 669, 687, 708, 723, 742, 753, 770, 782, 801, 820, 834, 848, 864, 882, 902, 922, 936, 954, 970, 980, 993, 1007, 1023, 1040, 1053, 1078, 1097, 1109, 1120, 1133, 1144, 1156, 1166, 1178, 1189}
|
||||
var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 53, 59, 68, 79, 88, 99, 109, 116, 123, 131, 139, 147, 153, 159, 165, 175, 184, 194, 210, 226, 237, 243, 254, 264, 273, 286, 300, 314, 328, 344, 355, 368, 387, 407, 427, 447, 460, 474, 488, 502, 517, 531, 545, 556, 578, 600, 614, 629, 652, 669, 687, 708, 723, 742, 753, 770, 782, 801, 820, 834, 848, 864, 882, 902, 922, 936, 954, 970, 980, 993, 1007, 1023, 1040, 1053, 1078, 1097, 1113, 1129, 1141, 1152, 1165, 1176, 1188, 1198, 1210, 1221}
|
||||
|
||||
func (i RelocType) String() string {
|
||||
i -= 1
|
||||
|
|
|
|||
|
|
@ -14,7 +14,47 @@ import (
|
|||
"log"
|
||||
)
|
||||
|
||||
func gentext(ctxt *ld.Link, ldr *loader.Loader) {}
|
||||
func gentext(ctxt *ld.Link, ldr *loader.Loader) {
|
||||
initfunc, addmoduledata := ld.PrepareAddmoduledata(ctxt)
|
||||
if initfunc == nil {
|
||||
return
|
||||
}
|
||||
|
||||
o := func(op uint32) {
|
||||
initfunc.AddUint32(ctxt.Arch, op)
|
||||
}
|
||||
|
||||
// Emit the following function:
|
||||
//
|
||||
// local.dso_init:
|
||||
// la.pcrel $a0, local.moduledata
|
||||
// b runtime.addmoduledata
|
||||
|
||||
// 0000000000000000 <local.dso_init>:
|
||||
// 0: 1a000004 pcalau12i $a0, 0
|
||||
// 0: R_LARCH_PCALA_HI20 local.moduledata
|
||||
o(0x1a000004)
|
||||
rel, _ := initfunc.AddRel(objabi.R_ADDRLOONG64U)
|
||||
rel.SetOff(0)
|
||||
rel.SetSiz(4)
|
||||
rel.SetSym(ctxt.Moduledata)
|
||||
|
||||
// 4: 02c00084 addi.d $a0, $a0, 0
|
||||
// 4: R_LARCH_PCALA_LO12 local.moduledata
|
||||
o(0x02c00084)
|
||||
rel2, _ := initfunc.AddRel(objabi.R_ADDRLOONG64)
|
||||
rel2.SetOff(4)
|
||||
rel2.SetSiz(4)
|
||||
rel2.SetSym(ctxt.Moduledata)
|
||||
|
||||
// 8: 50000000 b 0
|
||||
// 8: R_LARCH_B26 runtime.addmoduledata
|
||||
o(0x50000000)
|
||||
rel3, _ := initfunc.AddRel(objabi.R_CALLLOONG64)
|
||||
rel3.SetOff(8)
|
||||
rel3.SetSiz(4)
|
||||
rel3.SetSym(addmoduledata)
|
||||
}
|
||||
|
||||
func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool {
|
||||
log.Fatalf("adddynrel not implemented")
|
||||
|
|
@ -78,6 +118,16 @@ func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym,
|
|||
out.Write64(uint64(sectoff))
|
||||
out.Write64(uint64(elf.R_LARCH_PCALA_HI20) | uint64(elfsym)<<32)
|
||||
out.Write64(uint64(r.Xadd))
|
||||
|
||||
case objabi.R_LOONG64_GOT_HI:
|
||||
out.Write64(uint64(sectoff))
|
||||
out.Write64(uint64(elf.R_LARCH_GOT_PC_HI20) | uint64(elfsym)<<32)
|
||||
out.Write64(uint64(0x0))
|
||||
|
||||
case objabi.R_LOONG64_GOT_LO:
|
||||
out.Write64(uint64(sectoff))
|
||||
out.Write64(uint64(elf.R_LARCH_GOT_PC_LO12) | uint64(elfsym)<<32)
|
||||
out.Write64(uint64(0x0))
|
||||
}
|
||||
|
||||
return true
|
||||
|
|
@ -111,7 +161,9 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade
|
|||
objabi.R_CALLLOONG64,
|
||||
objabi.R_JMPLOONG64,
|
||||
objabi.R_LOONG64_TLS_IE_PCREL_HI,
|
||||
objabi.R_LOONG64_TLS_IE_LO:
|
||||
objabi.R_LOONG64_TLS_IE_LO,
|
||||
objabi.R_LOONG64_GOT_HI,
|
||||
objabi.R_LOONG64_GOT_LO:
|
||||
return val, 1, true
|
||||
}
|
||||
}
|
||||
|
|
@ -156,7 +208,10 @@ func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant
|
|||
func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) {
|
||||
switch r.Type() {
|
||||
case objabi.R_ADDRLOONG64,
|
||||
objabi.R_ADDRLOONG64U:
|
||||
objabi.R_ADDRLOONG64U,
|
||||
objabi.R_LOONG64_GOT_HI,
|
||||
objabi.R_LOONG64_GOT_LO:
|
||||
|
||||
return ld.ExtrelocViaOuterSym(ldr, r, s), true
|
||||
|
||||
case objabi.R_ADDRLOONG64TLS,
|
||||
|
|
|
|||
|
|
@ -304,11 +304,13 @@ type ConnectionState struct {
|
|||
// ExportKeyingMaterial returns length bytes of exported key material in a new
|
||||
// slice as defined in RFC 5705. If context is nil, it is not used as part of
|
||||
// the seed. If the connection was set to allow renegotiation via
|
||||
// Config.Renegotiation, this function will return an error.
|
||||
// Config.Renegotiation, or if the connections supports neither TLS 1.3 nor
|
||||
// Extended Master Secret, this function will return an error.
|
||||
//
|
||||
// There are conditions in which the returned values might not be unique to a
|
||||
// connection. See the Security Considerations sections of RFC 5705 and RFC 7627,
|
||||
// and https://mitls.org/pages/attacks/3SHAKE#channelbindings.
|
||||
// Exporting key material without Extended Master Secret or TLS 1.3 was disabled
|
||||
// in Go 1.22 due to security issues (see the Security Considerations sections
|
||||
// of RFC 5705 and RFC 7627), but can be re-enabled with the GODEBUG setting
|
||||
// tlsunsafeekm=1.
|
||||
func (cs *ConnectionState) ExportKeyingMaterial(label string, context []byte, length int) ([]byte, error) {
|
||||
return cs.ekm(label, context, length)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"internal/godebug"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
|
|
@ -1599,6 +1600,8 @@ func (c *Conn) ConnectionState() ConnectionState {
|
|||
return c.connectionStateLocked()
|
||||
}
|
||||
|
||||
var ekmgodebug = godebug.New("tlsunsafeekm")
|
||||
|
||||
func (c *Conn) connectionStateLocked() ConnectionState {
|
||||
var state ConnectionState
|
||||
state.HandshakeComplete = c.isHandshakeComplete.Load()
|
||||
|
|
@ -1620,7 +1623,15 @@ func (c *Conn) connectionStateLocked() ConnectionState {
|
|||
}
|
||||
}
|
||||
if c.config.Renegotiation != RenegotiateNever {
|
||||
state.ekm = noExportedKeyingMaterial
|
||||
state.ekm = noEKMBecauseRenegotiation
|
||||
} else if c.vers != VersionTLS13 && !c.extMasterSecret {
|
||||
state.ekm = func(label string, context []byte, length int) ([]byte, error) {
|
||||
if ekmgodebug.Value() == "1" {
|
||||
ekmgodebug.IncNonDefault()
|
||||
return c.ekm(label, context, length)
|
||||
}
|
||||
return noEKMBecauseNoEMS(label, context, length)
|
||||
}
|
||||
} else {
|
||||
state.ekm = c.ekm
|
||||
}
|
||||
|
|
|
|||
|
|
@ -252,13 +252,20 @@ func (h *finishedHash) discardHandshakeBuffer() {
|
|||
h.buffer = nil
|
||||
}
|
||||
|
||||
// noExportedKeyingMaterial is used as a value of
|
||||
// noEKMBecauseRenegotiation is used as a value of
|
||||
// ConnectionState.ekm when renegotiation is enabled and thus
|
||||
// we wish to fail all key-material export requests.
|
||||
func noExportedKeyingMaterial(label string, context []byte, length int) ([]byte, error) {
|
||||
func noEKMBecauseRenegotiation(label string, context []byte, length int) ([]byte, error) {
|
||||
return nil, errors.New("crypto/tls: ExportKeyingMaterial is unavailable when renegotiation is enabled")
|
||||
}
|
||||
|
||||
// noEKMBecauseNoEMS is used as a value of ConnectionState.ekm when Extended
|
||||
// Master Secret is not negotiated and thus we wish to fail all key-material
|
||||
// export requests.
|
||||
func noEKMBecauseNoEMS(label string, context []byte, length int) ([]byte, error) {
|
||||
return nil, errors.New("crypto/tls: ExportKeyingMaterial is unavailable when neither TLS 1.3 nor Extended Master Secret are negotiated; override with GODEBUG=tlsunsafeekm=1")
|
||||
}
|
||||
|
||||
// ekmFromMasterSecret generates exported keying material as defined in RFC 5705.
|
||||
func ekmFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clientRandom, serverRandom []byte) func(string, []byte, int) ([]byte, error) {
|
||||
return func(label string, context []byte, length int) ([]byte, error) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
// 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.
|
||||
|
||||
//go:build goexperiment.regabiargs
|
||||
|
||||
package abi
|
||||
|
||||
const (
|
||||
// See abi_generic.go.
|
||||
|
||||
// R4 - R19
|
||||
IntArgRegs = 16
|
||||
|
||||
// F0 - F15
|
||||
FloatArgRegs = 16
|
||||
|
||||
EffectiveFloatRegSize = 8
|
||||
)
|
||||
|
|
@ -5,83 +5,102 @@
|
|||
#include "go_asm.h"
|
||||
#include "textflag.h"
|
||||
|
||||
TEXT ·Compare(SB),NOSPLIT,$0-56
|
||||
MOVV a_base+0(FP), R6
|
||||
MOVV b_base+24(FP), R7
|
||||
MOVV a_len+8(FP), R4
|
||||
MOVV b_len+32(FP), R5
|
||||
TEXT ·Compare<ABIInternal>(SB),NOSPLIT,$0-56
|
||||
#ifndef GOEXPERIMENT_regabiargs
|
||||
MOVV a_base+0(FP), R4
|
||||
MOVV a_len+8(FP), R5
|
||||
MOVV b_base+24(FP), R6
|
||||
MOVV b_len+32(FP), R7
|
||||
MOVV $ret+48(FP), R13
|
||||
#else
|
||||
// R4 = a_base
|
||||
// R5 = a_len
|
||||
// R6 = a_cap (unused)
|
||||
// R7 = b_base (want in R6)
|
||||
// R8 = b_len (want in R7)
|
||||
// R9 = b_cap (unused)
|
||||
MOVV R7, R6
|
||||
MOVV R8, R7
|
||||
#endif
|
||||
JMP cmpbody<>(SB)
|
||||
|
||||
TEXT runtime·cmpstring(SB),NOSPLIT,$0-40
|
||||
MOVV a_base+0(FP), R6
|
||||
MOVV b_base+16(FP), R7
|
||||
MOVV a_len+8(FP), R4
|
||||
MOVV b_len+24(FP), R5
|
||||
TEXT runtime·cmpstring<ABIInternal>(SB),NOSPLIT,$0-40
|
||||
#ifndef GOEXPERIMENT_regabiargs
|
||||
MOVV a_base+0(FP), R4
|
||||
MOVV b_base+16(FP), R6
|
||||
MOVV a_len+8(FP), R5
|
||||
MOVV b_len+24(FP), R7
|
||||
MOVV $ret+32(FP), R13
|
||||
#endif
|
||||
// R4 = a_base
|
||||
// R5 = a_len
|
||||
// R6 = b_base
|
||||
// R7 = b_len
|
||||
JMP cmpbody<>(SB)
|
||||
|
||||
// On entry:
|
||||
// R4 length of a
|
||||
// R5 length of b
|
||||
// R6 points to the start of a
|
||||
// R7 points to the start of b
|
||||
// R5 length of a
|
||||
// R7 length of b
|
||||
// R4 points to the start of a
|
||||
// R6 points to the start of b
|
||||
// R13 points to the return value (-1/0/1)
|
||||
TEXT cmpbody<>(SB),NOSPLIT|NOFRAME,$0
|
||||
BEQ R6, R7, samebytes // same start of a and b
|
||||
BEQ R4, R6, samebytes // same start of a and b
|
||||
|
||||
SGTU R4, R5, R9
|
||||
SGTU R5, R7, R9
|
||||
BNE R0, R9, r2_lt_r1
|
||||
MOVV R4, R14
|
||||
MOVV R5, R14
|
||||
JMP entry
|
||||
r2_lt_r1:
|
||||
MOVV R5, R14 // R14 is min(R4, R5)
|
||||
MOVV R7, R14 // R14 is min(R4, R5)
|
||||
entry:
|
||||
ADDV R6, R14, R12 // R6 start of a, R14 end of a
|
||||
BEQ R6, R12, samebytes // length is 0
|
||||
ADDV R4, R14, R12 // R6 start of a, R14 end of a
|
||||
BEQ R4, R12, samebytes // length is 0
|
||||
|
||||
SRLV $4, R14 // R14 is number of chunks
|
||||
BEQ R0, R14, byte_loop
|
||||
|
||||
// make sure both a and b are aligned.
|
||||
OR R6, R7, R15
|
||||
OR R4, R6, R15
|
||||
AND $7, R15
|
||||
BNE R0, R15, byte_loop
|
||||
|
||||
PCALIGN $16
|
||||
chunk16_loop:
|
||||
BEQ R0, R14, byte_loop
|
||||
MOVV (R6), R8
|
||||
MOVV (R7), R9
|
||||
MOVV (R4), R8
|
||||
MOVV (R6), R9
|
||||
BNE R8, R9, byte_loop
|
||||
MOVV 8(R6), R16
|
||||
MOVV 8(R7), R17
|
||||
MOVV 8(R4), R16
|
||||
MOVV 8(R6), R17
|
||||
ADDV $16, R4
|
||||
ADDV $16, R6
|
||||
ADDV $16, R7
|
||||
SUBVU $1, R14
|
||||
BEQ R16, R17, chunk16_loop
|
||||
SUBV $8, R4
|
||||
SUBV $8, R6
|
||||
SUBV $8, R7
|
||||
|
||||
byte_loop:
|
||||
BEQ R6, R12, samebytes
|
||||
MOVBU (R6), R8
|
||||
BEQ R4, R12, samebytes
|
||||
MOVBU (R4), R8
|
||||
ADDVU $1, R4
|
||||
MOVBU (R6), R9
|
||||
ADDVU $1, R6
|
||||
MOVBU (R7), R9
|
||||
ADDVU $1, R7
|
||||
BEQ R8, R9, byte_loop
|
||||
|
||||
byte_cmp:
|
||||
SGTU R8, R9, R12 // R12 = 1 if (R8 > R9)
|
||||
BNE R0, R12, ret
|
||||
MOVV $-1, R12
|
||||
SGTU R8, R9, R4 // R12 = 1 if (R8 > R9)
|
||||
BNE R0, R4, ret
|
||||
MOVV $-1, R4
|
||||
JMP ret
|
||||
|
||||
samebytes:
|
||||
SGTU R4, R5, R8
|
||||
SGTU R5, R4, R9
|
||||
SUBV R9, R8, R12
|
||||
SGTU R5, R7, R8
|
||||
SGTU R7, R5, R9
|
||||
SUBV R9, R8, R4
|
||||
|
||||
ret:
|
||||
MOVV R12, (R13)
|
||||
#ifndef GOEXPERIMENT_regabiargs
|
||||
MOVV R4, (R13)
|
||||
#endif
|
||||
RET
|
||||
|
|
|
|||
|
|
@ -8,17 +8,21 @@
|
|||
#define REGCTXT R29
|
||||
|
||||
// memequal(a, b unsafe.Pointer, size uintptr) bool
|
||||
TEXT runtime·memequal(SB),NOSPLIT|NOFRAME,$0-25
|
||||
TEXT runtime·memequal<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-25
|
||||
#ifndef GOEXPERIMENT_regabiargs
|
||||
MOVV a+0(FP), R4
|
||||
MOVV b+8(FP), R5
|
||||
BEQ R4, R5, eq
|
||||
MOVV size+16(FP), R6
|
||||
#endif
|
||||
BEQ R4, R5, eq
|
||||
ADDV R4, R6, R7
|
||||
PCALIGN $16
|
||||
loop:
|
||||
BNE R4, R7, test
|
||||
MOVV $1, R4
|
||||
#ifndef GOEXPERIMENT_regabiargs
|
||||
MOVB R4, ret+24(FP)
|
||||
#endif
|
||||
RET
|
||||
test:
|
||||
MOVBU (R4), R9
|
||||
|
|
@ -27,17 +31,24 @@ test:
|
|||
ADDV $1, R5
|
||||
BEQ R9, R10, loop
|
||||
|
||||
MOVB R0, R4
|
||||
#ifndef GOEXPERIMENT_regabiargs
|
||||
MOVB R0, ret+24(FP)
|
||||
#endif
|
||||
RET
|
||||
eq:
|
||||
MOVV $1, R4
|
||||
#ifndef GOEXPERIMENT_regabiargs
|
||||
MOVB R4, ret+24(FP)
|
||||
#endif
|
||||
RET
|
||||
|
||||
// memequal_varlen(a, b unsafe.Pointer) bool
|
||||
TEXT runtime·memequal_varlen(SB),NOSPLIT,$40-17
|
||||
TEXT runtime·memequal_varlen<ABIInternal>(SB),NOSPLIT,$40-17
|
||||
#ifndef GOEXPERIMENT_regabiargs
|
||||
MOVV a+0(FP), R4
|
||||
MOVV b+8(FP), R5
|
||||
#endif
|
||||
BEQ R4, R5, eq
|
||||
MOVV 8(REGCTXT), R6 // compiler stores size at offset 8 in the closure
|
||||
MOVV R4, 8(R3)
|
||||
|
|
@ -45,9 +56,13 @@ TEXT runtime·memequal_varlen(SB),NOSPLIT,$40-17
|
|||
MOVV R6, 24(R3)
|
||||
JAL runtime·memequal(SB)
|
||||
MOVBU 32(R3), R4
|
||||
#ifndef GOEXPERIMENT_regabiargs
|
||||
MOVB R4, ret+16(FP)
|
||||
#endif
|
||||
RET
|
||||
eq:
|
||||
MOVV $1, R4
|
||||
#ifndef GOEXPERIMENT_regabiargs
|
||||
MOVB R4, ret+16(FP)
|
||||
#endif
|
||||
RET
|
||||
|
|
|
|||
|
|
@ -5,11 +5,18 @@
|
|||
#include "go_asm.h"
|
||||
#include "textflag.h"
|
||||
|
||||
TEXT ·IndexByte(SB),NOSPLIT,$0-40
|
||||
TEXT ·IndexByte<ABIInternal>(SB),NOSPLIT,$0-40
|
||||
#ifndef GOEXPERIMENT_regabiargs
|
||||
MOVV b_base+0(FP), R4
|
||||
MOVV b_len+8(FP), R5
|
||||
MOVBU c+24(FP), R6 // byte to find
|
||||
MOVV R4, R7 // store base for later
|
||||
MOVBU c+24(FP), R7 // byte to find
|
||||
#endif
|
||||
// R4 = b_base
|
||||
// R5 = b_len
|
||||
// R6 = b_cap (unused)
|
||||
// R7 = byte to find
|
||||
AND $0xff, R7
|
||||
MOVV R4, R6 // store base for later
|
||||
ADDV R4, R5 // end
|
||||
ADDV $-1, R4
|
||||
|
||||
|
|
@ -18,21 +25,30 @@ loop:
|
|||
ADDV $1, R4
|
||||
BEQ R4, R5, notfound
|
||||
MOVBU (R4), R8
|
||||
BNE R6, R8, loop
|
||||
BNE R7, R8, loop
|
||||
|
||||
SUBV R7, R4 // remove base
|
||||
SUBV R6, R4 // remove base
|
||||
#ifndef GOEXPERIMENT_regabiargs
|
||||
MOVV R4, ret+32(FP)
|
||||
#endif
|
||||
RET
|
||||
|
||||
notfound:
|
||||
MOVV $-1, R4
|
||||
#ifndef GOEXPERIMENT_regabiargs
|
||||
MOVV R4, ret+32(FP)
|
||||
#endif
|
||||
RET
|
||||
|
||||
TEXT ·IndexByteString(SB),NOSPLIT,$0-32
|
||||
TEXT ·IndexByteString<ABIInternal>(SB),NOSPLIT,$0-32
|
||||
#ifndef GOEXPERIMENT_regabiargs
|
||||
MOVV s_base+0(FP), R4
|
||||
MOVV s_len+8(FP), R5
|
||||
MOVBU c+16(FP), R6 // byte to find
|
||||
#endif
|
||||
// R4 = s_base
|
||||
// R5 = s_len
|
||||
// R6 = byte to find
|
||||
MOVV R4, R7 // store base for later
|
||||
ADDV R4, R5 // end
|
||||
ADDV $-1, R4
|
||||
|
|
@ -45,10 +61,14 @@ loop:
|
|||
BNE R6, R8, loop
|
||||
|
||||
SUBV R7, R4 // remove base
|
||||
#ifndef GOEXPERIMENT_regabiargs
|
||||
MOVV R4, ret+24(FP)
|
||||
#endif
|
||||
RET
|
||||
|
||||
notfound:
|
||||
MOVV $-1, R4
|
||||
#ifndef GOEXPERIMENT_regabiargs
|
||||
MOVV R4, ret+24(FP)
|
||||
#endif
|
||||
RET
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ var All = []Info{
|
|||
{Name: "tls10server", Package: "crypto/tls", Changed: 22, Old: "1"},
|
||||
{Name: "tlsmaxrsasize", Package: "crypto/tls"},
|
||||
{Name: "tlsrsakex", Package: "crypto/tls", Changed: 22, Old: "1"},
|
||||
{Name: "tlsunsafeekm", Package: "crypto/tls", Changed: 22, Old: "1"},
|
||||
{Name: "x509sha1", Package: "crypto/x509"},
|
||||
{Name: "x509usefallbackroots", Package: "crypto/x509"},
|
||||
{Name: "zipinsecurepath", Package: "archive/zip"},
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool {
|
|||
|
||||
case "plugin":
|
||||
switch platform {
|
||||
case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/s390x", "linux/ppc64le",
|
||||
case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/loong64", "linux/s390x", "linux/ppc64le",
|
||||
"android/amd64", "android/386",
|
||||
"darwin/amd64", "darwin/arm64",
|
||||
"freebsd/amd64":
|
||||
|
|
|
|||
|
|
@ -575,9 +575,8 @@ type writerOnly struct {
|
|||
// to a *net.TCPConn with sendfile, or from a supported src type such
|
||||
// as a *net.TCPConn on Linux with splice.
|
||||
func (w *response) ReadFrom(src io.Reader) (n int64, err error) {
|
||||
bufp := copyBufPool.Get().(*[]byte)
|
||||
buf := *bufp
|
||||
defer copyBufPool.Put(bufp)
|
||||
buf := getCopyBuf()
|
||||
defer putCopyBuf(buf)
|
||||
|
||||
// Our underlying w.conn.rwc is usually a *TCPConn (with its
|
||||
// own ReadFrom method). If not, just fall back to the normal
|
||||
|
|
@ -807,11 +806,18 @@ var (
|
|||
bufioWriter4kPool sync.Pool
|
||||
)
|
||||
|
||||
var copyBufPool = sync.Pool{
|
||||
New: func() any {
|
||||
b := make([]byte, 32*1024)
|
||||
return &b
|
||||
},
|
||||
const copyBufPoolSize = 32 * 1024
|
||||
|
||||
var copyBufPool = sync.Pool{New: func() any { return new([copyBufPoolSize]byte) }}
|
||||
|
||||
func getCopyBuf() []byte {
|
||||
return copyBufPool.Get().(*[copyBufPoolSize]byte)[:]
|
||||
}
|
||||
func putCopyBuf(b []byte) {
|
||||
if len(b) != copyBufPoolSize {
|
||||
panic("trying to put back buffer of the wrong size in the copyBufPool")
|
||||
}
|
||||
copyBufPool.Put((*[copyBufPoolSize]byte)(b))
|
||||
}
|
||||
|
||||
func bufioWriterPool(size int) *sync.Pool {
|
||||
|
|
|
|||
|
|
@ -410,9 +410,8 @@ func (t *transferWriter) writeBody(w io.Writer) (err error) {
|
|||
//
|
||||
// This function is only intended for use in writeBody.
|
||||
func (t *transferWriter) doBodyCopy(dst io.Writer, src io.Reader) (n int64, err error) {
|
||||
bufp := copyBufPool.Get().(*[]byte)
|
||||
buf := *bufp
|
||||
defer copyBufPool.Put(bufp)
|
||||
buf := getCopyBuf()
|
||||
defer putCopyBuf(buf)
|
||||
|
||||
n, err = io.CopyBuffer(dst, src, buf)
|
||||
if err != nil && err != io.EOF {
|
||||
|
|
|
|||
|
|
@ -1620,8 +1620,17 @@ func TestFileChdir(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("Getwd: %s", err)
|
||||
}
|
||||
if !equal(wdNew, wd) {
|
||||
t.Fatalf("fd.Chdir failed, got %s, want %s", wdNew, wd)
|
||||
|
||||
wdInfo, err := fd.Stat()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
newInfo, err := Stat(wdNew)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !SameFile(wdInfo, newInfo) {
|
||||
t.Fatalf("fd.Chdir failed: got %s, want %s", wdNew, wd)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,34 +7,83 @@
|
|||
|
||||
#define REGCTXT R29
|
||||
|
||||
// The frames of each of the two functions below contain two locals, at offsets
|
||||
// that are known to the runtime.
|
||||
//
|
||||
// The first local is a bool called retValid with a whole pointer-word reserved
|
||||
// for it on the stack. The purpose of this word is so that the runtime knows
|
||||
// whether the stack-allocated return space contains valid values for stack
|
||||
// scanning.
|
||||
//
|
||||
// The second local is an abi.RegArgs value whose offset is also known to the
|
||||
// runtime, so that a stack map for it can be constructed, since it contains
|
||||
// pointers visible to the GC.
|
||||
#define LOCAL_RETVALID 40
|
||||
#define LOCAL_REGARGS 48
|
||||
|
||||
// The frame size of the functions below is
|
||||
// 32 (args of callReflect) + 8 (bool + padding) + 392 (abi.RegArgs) = 432.
|
||||
|
||||
// makeFuncStub is the code half of the function returned by MakeFunc.
|
||||
// See the comment on the declaration of makeFuncStub in makefunc.go
|
||||
// for more details.
|
||||
// No arg size here, runtime pulls arg map out of the func value.
|
||||
TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$40
|
||||
TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$432
|
||||
NO_LOCAL_POINTERS
|
||||
ADDV $LOCAL_REGARGS, R3, R25 // spillArgs using R25
|
||||
JAL runtime·spillArgs(SB)
|
||||
MOVV REGCTXT, 32(R3) // save REGCTXT > args of moveMakeFuncArgPtrs < LOCAL_REGARGS
|
||||
|
||||
#ifdef GOEXPERIMENT_regabiargs
|
||||
MOVV REGCTXT, R4
|
||||
MOVV R25, R5
|
||||
#else
|
||||
MOVV REGCTXT, 8(R3)
|
||||
MOVV $argframe+0(FP), R19
|
||||
MOVV R19, 16(R3)
|
||||
MOVB R0, 40(R3)
|
||||
ADDV $40, R3, R19
|
||||
MOVV R19, 24(R3)
|
||||
MOVV R0, 32(R3)
|
||||
MOVV R25, 16(R3)
|
||||
#endif
|
||||
JAL ·moveMakeFuncArgPtrs<ABIInternal>(SB)
|
||||
MOVV 32(R3), REGCTXT // restore REGCTXT
|
||||
|
||||
MOVV REGCTXT, 8(R3)
|
||||
MOVV $argframe+0(FP), R20
|
||||
MOVV R20, 16(R3)
|
||||
MOVV R0, LOCAL_RETVALID(R3)
|
||||
ADDV $LOCAL_RETVALID, R3, R20
|
||||
MOVV R20, 24(R3)
|
||||
ADDV $LOCAL_REGARGS, R3, R20
|
||||
MOVV R20, 32(R3)
|
||||
JAL ·callReflect(SB)
|
||||
ADDV $LOCAL_REGARGS, R3, R25 //unspillArgs using R25
|
||||
JAL runtime·unspillArgs(SB)
|
||||
RET
|
||||
|
||||
// methodValueCall is the code half of the function returned by makeMethodValue.
|
||||
// See the comment on the declaration of methodValueCall in makefunc.go
|
||||
// for more details.
|
||||
// No arg size here; runtime pulls arg map out of the func value.
|
||||
TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$40
|
||||
TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$432
|
||||
NO_LOCAL_POINTERS
|
||||
ADDV $LOCAL_REGARGS, R3, R25 // spillArgs using R25
|
||||
JAL runtime·spillArgs(SB)
|
||||
MOVV REGCTXT, 32(R3) // save REGCTXT > args of moveMakeFuncArgPtrs < LOCAL_REGARGS
|
||||
#ifdef GOEXPERIMENT_regabiargs
|
||||
MOVV REGCTXT, R4
|
||||
MOVV R25, R5
|
||||
#else
|
||||
MOVV REGCTXT, 8(R3)
|
||||
MOVV $argframe+0(FP), R19
|
||||
MOVV R19, 16(R3)
|
||||
MOVB R0, 40(R3)
|
||||
ADDV $40, R3, R19
|
||||
MOVV R19, 24(R3)
|
||||
MOVV R0, 32(R3)
|
||||
MOVV R25, 16(R3)
|
||||
#endif
|
||||
JAL ·moveMakeFuncArgPtrs<ABIInternal>(SB)
|
||||
MOVV 32(R3), REGCTXT // restore REGCTXT
|
||||
MOVV REGCTXT, 8(R3)
|
||||
MOVV $argframe+0(FP), R20
|
||||
MOVV R20, 16(R3)
|
||||
MOVB R0, LOCAL_RETVALID(R3)
|
||||
ADDV $LOCAL_RETVALID, R3, R20
|
||||
MOVV R20, 24(R3)
|
||||
ADDV $LOCAL_REGARGS, R3, R20
|
||||
MOVV R20, 32(R3) // frame size to 32+SP as callreflect args)
|
||||
JAL ·callMethod(SB)
|
||||
ADDV $LOCAL_REGARGS, R3, R25 // unspillArgs using R25
|
||||
JAL runtime·unspillArgs(SB)
|
||||
RET
|
||||
|
|
|
|||
|
|
@ -1598,24 +1598,23 @@ func (v Value) IsZero() bool {
|
|||
case Complex64, Complex128:
|
||||
return v.Complex() == 0
|
||||
case Array:
|
||||
array := (*abi.ArrayType)(unsafe.Pointer(v.typ()))
|
||||
// Avoid performance degradation of small benchmarks.
|
||||
if v.flag&flagIndir == 0 {
|
||||
return v.ptr == nil
|
||||
}
|
||||
typ := (*abi.ArrayType)(unsafe.Pointer(v.typ()))
|
||||
// If the type is comparable, then compare directly with zero.
|
||||
if array.Equal != nil && array.Size() <= maxZero {
|
||||
if v.flag&flagIndir == 0 {
|
||||
return v.ptr == nil
|
||||
}
|
||||
if typ.Equal != nil && typ.Size() <= maxZero {
|
||||
// v.ptr doesn't escape, as Equal functions are compiler generated
|
||||
// and never escape. The escape analysis doesn't know, as it is a
|
||||
// function pointer call.
|
||||
return array.Equal(noescape(v.ptr), unsafe.Pointer(&zeroVal[0]))
|
||||
return typ.Equal(noescape(v.ptr), unsafe.Pointer(&zeroVal[0]))
|
||||
}
|
||||
if array.TFlag&abi.TFlagRegularMemory != 0 {
|
||||
if typ.TFlag&abi.TFlagRegularMemory != 0 {
|
||||
// For some types where the zero value is a value where all bits of this type are 0
|
||||
// optimize it.
|
||||
return isZero(unsafe.Slice(((*byte)(v.ptr)), array.Size()))
|
||||
return isZero(unsafe.Slice(((*byte)(v.ptr)), typ.Size()))
|
||||
}
|
||||
n := int(array.Len)
|
||||
n := int(typ.Len)
|
||||
for i := 0; i < n; i++ {
|
||||
if !v.Index(i).IsZero() {
|
||||
return false
|
||||
|
|
@ -1663,7 +1662,7 @@ func isZero(b []byte) bool {
|
|||
return true
|
||||
}
|
||||
const n = 32
|
||||
// Align memory addresses to 8 bytes
|
||||
// Align memory addresses to 8 bytes.
|
||||
for uintptr(unsafe.Pointer(&b[0]))%8 != 0 {
|
||||
if b[0] != 0 {
|
||||
return false
|
||||
|
|
@ -1690,7 +1689,14 @@ func isZero(b []byte) bool {
|
|||
w = w[1:]
|
||||
}
|
||||
for len(w) >= n {
|
||||
if w[0] != 0 || w[1] != 0 || w[2] != 0 || w[3] != 0 || w[4] != 0 || w[5] != 0 || w[6] != 0 || w[7] != 0 || w[8] != 0 || w[9] != 0 || w[10] != 0 || w[11] != 0 || w[12] != 0 || w[13] != 0 || w[14] != 0 || w[15] != 0 || w[16] != 0 || w[17] != 0 || w[18] != 0 || w[19] != 0 || w[20] != 0 || w[21] != 0 || w[22] != 0 || w[23] != 0 || w[24] != 0 || w[25] != 0 || w[26] != 0 || w[27] != 0 || w[28] != 0 || w[29] != 0 || w[30] != 0 || w[31] != 0 {
|
||||
if w[0] != 0 || w[1] != 0 || w[2] != 0 || w[3] != 0 ||
|
||||
w[4] != 0 || w[5] != 0 || w[6] != 0 || w[7] != 0 ||
|
||||
w[8] != 0 || w[9] != 0 || w[10] != 0 || w[11] != 0 ||
|
||||
w[12] != 0 || w[13] != 0 || w[14] != 0 || w[15] != 0 ||
|
||||
w[16] != 0 || w[17] != 0 || w[18] != 0 || w[19] != 0 ||
|
||||
w[20] != 0 || w[21] != 0 || w[22] != 0 || w[23] != 0 ||
|
||||
w[24] != 0 || w[25] != 0 || w[26] != 0 || w[27] != 0 ||
|
||||
w[28] != 0 || w[29] != 0 || w[30] != 0 || w[31] != 0 {
|
||||
return false
|
||||
}
|
||||
w = w[n:]
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ nocgo:
|
|||
MOVV R0, 1(R0)
|
||||
RET
|
||||
|
||||
DATA runtime·mainPC+0(SB)/8,$runtime·main(SB)
|
||||
DATA runtime·mainPC+0(SB)/8,$runtime·main<ABIInternal>(SB)
|
||||
GLOBL runtime·mainPC(SB),RODATA,$8
|
||||
|
||||
TEXT runtime·breakpoint(SB),NOSPLIT|NOFRAME,$0-0
|
||||
|
|
@ -123,26 +123,31 @@ TEXT gogo<>(SB), NOSPLIT|NOFRAME, $0
|
|||
// Switch to m->g0's stack, call fn(g).
|
||||
// Fn must never return. It should gogo(&g->sched)
|
||||
// to keep running g.
|
||||
TEXT runtime·mcall(SB), NOSPLIT|NOFRAME, $0-8
|
||||
TEXT runtime·mcall<ABIInternal>(SB), NOSPLIT|NOFRAME, $0-8
|
||||
#ifdef GOEXPERIMENT_regabiargs
|
||||
MOVV R4, REGCTXT
|
||||
#else
|
||||
MOVV fn+0(FP), REGCTXT
|
||||
#endif
|
||||
|
||||
// Save caller state in g->sched
|
||||
MOVV R3, (g_sched+gobuf_sp)(g)
|
||||
MOVV R1, (g_sched+gobuf_pc)(g)
|
||||
MOVV R0, (g_sched+gobuf_lr)(g)
|
||||
|
||||
// Switch to m->g0 & its stack, call fn.
|
||||
MOVV g, R19
|
||||
MOVV g_m(g), R4
|
||||
MOVV m_g0(R4), g
|
||||
MOVV g, R4 // arg = g
|
||||
MOVV g_m(g), R20
|
||||
MOVV m_g0(R20), g
|
||||
JAL runtime·save_g(SB)
|
||||
BNE g, R19, 2(PC)
|
||||
BNE g, R4, 2(PC)
|
||||
JMP runtime·badmcall(SB)
|
||||
MOVV fn+0(FP), REGCTXT // context
|
||||
MOVV 0(REGCTXT), R5 // code pointer
|
||||
MOVV 0(REGCTXT), R20 // code pointer
|
||||
MOVV (g_sched+gobuf_sp)(g), R3 // sp = m->g0->sched.sp
|
||||
ADDV $-16, R3
|
||||
MOVV R19, 8(R3)
|
||||
MOVV R4, 8(R3)
|
||||
MOVV R0, 0(R3)
|
||||
JAL (R5)
|
||||
JAL (R20)
|
||||
JMP runtime·badmcall2(SB)
|
||||
|
||||
// systemstack_switch is a dummy routine that systemstack leaves at the bottom
|
||||
|
|
@ -214,7 +219,7 @@ noswitch:
|
|||
|
||||
// Called during function prolog when more stack is needed.
|
||||
// Caller has already loaded:
|
||||
// loong64: R5: LR
|
||||
// loong64: R31: LR
|
||||
//
|
||||
// The traceback routines see morestack on a g0 as being
|
||||
// the top of a stack (for example, morestack calling newstack
|
||||
|
|
@ -238,12 +243,12 @@ TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0
|
|||
// Set g->sched to context in f.
|
||||
MOVV R3, (g_sched+gobuf_sp)(g)
|
||||
MOVV R1, (g_sched+gobuf_pc)(g)
|
||||
MOVV R5, (g_sched+gobuf_lr)(g)
|
||||
MOVV R31, (g_sched+gobuf_lr)(g)
|
||||
MOVV REGCTXT, (g_sched+gobuf_ctxt)(g)
|
||||
|
||||
// Called from f.
|
||||
// Set m->morebuf to f's caller.
|
||||
MOVV R5, (m_morebuf+gobuf_pc)(R7) // f's caller's PC
|
||||
MOVV R31, (m_morebuf+gobuf_pc)(R7) // f's caller's PC
|
||||
MOVV R3, (m_morebuf+gobuf_sp)(R7) // f's caller's SP
|
||||
MOVV g, (m_morebuf+gobuf_g)(R7)
|
||||
|
||||
|
|
@ -272,7 +277,7 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0
|
|||
JMP runtime·morestack(SB)
|
||||
|
||||
// reflectcall: call a function with the given argument list
|
||||
// func call(argtype *_type, f *FuncVal, arg *byte, argsize, retoffset uint32).
|
||||
// func call(stackArgsType *_type, f *FuncVal, stackArgs *byte, stackArgsSize, stackRetOffset, frameSize uint32, regArgs *abi.RegArgs).
|
||||
// we don't have variable-sized frames, so we use a small number
|
||||
// of constant-sized-frame functions to encode a few bits of size in the pc.
|
||||
// Caution: ugly multiline assembly macros in your future!
|
||||
|
|
@ -286,7 +291,7 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0
|
|||
// Note: can't just "BR NAME(SB)" - bad inlining results.
|
||||
|
||||
TEXT ·reflectcall(SB), NOSPLIT|NOFRAME, $0-48
|
||||
MOVWU stackArgsSize+24(FP), R19
|
||||
MOVWU frameSize+32(FP), R19
|
||||
DISPATCH(runtime·call32, 32)
|
||||
DISPATCH(runtime·call64, 64)
|
||||
DISPATCH(runtime·call128, 128)
|
||||
|
|
@ -317,7 +322,7 @@ TEXT ·reflectcall(SB), NOSPLIT|NOFRAME, $0-48
|
|||
JMP (R4)
|
||||
|
||||
#define CALLFN(NAME,MAXSIZE) \
|
||||
TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \
|
||||
TEXT NAME(SB), WRAPPER, $MAXSIZE-48; \
|
||||
NO_LOCAL_POINTERS; \
|
||||
/* copy arguments to stack */ \
|
||||
MOVV arg+16(FP), R4; \
|
||||
|
|
@ -331,12 +336,17 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \
|
|||
MOVBU R6, (R12); \
|
||||
ADDV $1, R12; \
|
||||
JMP -5(PC); \
|
||||
/* set up argument registers */ \
|
||||
MOVV regArgs+40(FP), R25; \
|
||||
JAL ·unspillArgs(SB); \
|
||||
/* call function */ \
|
||||
MOVV f+8(FP), REGCTXT; \
|
||||
MOVV (REGCTXT), R6; \
|
||||
MOVV (REGCTXT), R25; \
|
||||
PCDATA $PCDATA_StackMapIndex, $0; \
|
||||
JAL (R6); \
|
||||
JAL (R25); \
|
||||
/* copy return values back */ \
|
||||
MOVV regArgs+40(FP), R25; \
|
||||
JAL ·spillArgs(SB); \
|
||||
MOVV argtype+0(FP), R7; \
|
||||
MOVV arg+16(FP), R4; \
|
||||
MOVWU n+24(FP), R5; \
|
||||
|
|
@ -352,11 +362,13 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \
|
|||
// separate function so it can allocate stack space for the arguments
|
||||
// to reflectcallmove. It does not follow the Go ABI; it expects its
|
||||
// arguments in registers.
|
||||
TEXT callRet<>(SB), NOSPLIT, $32-0
|
||||
TEXT callRet<>(SB), NOSPLIT, $40-0
|
||||
NO_LOCAL_POINTERS
|
||||
MOVV R7, 8(R3)
|
||||
MOVV R4, 16(R3)
|
||||
MOVV R12, 24(R3)
|
||||
MOVV R5, 32(R3)
|
||||
MOVV R25, 40(R3)
|
||||
JAL runtime·reflectcallmove(SB)
|
||||
RET
|
||||
|
||||
|
|
@ -567,7 +579,7 @@ havem:
|
|||
// If the m on entry wasn't nil,
|
||||
// 1. the thread might be a Go thread,
|
||||
// 2. or it wasn't the first call from a C thread on pthread platforms,
|
||||
// since then we skip dropm to reuse the m in the first call.
|
||||
// since then we skip dropm to resue the m in the first call.
|
||||
MOVV savedm-8(SP), R12
|
||||
BNE R12, droppedm
|
||||
|
||||
|
|
@ -604,14 +616,14 @@ TEXT runtime·abort(SB),NOSPLIT|NOFRAME,$0-0
|
|||
UNDEF
|
||||
|
||||
// AES hashing not implemented for loong64
|
||||
TEXT runtime·memhash(SB),NOSPLIT|NOFRAME,$0-32
|
||||
JMP runtime·memhashFallback(SB)
|
||||
TEXT runtime·strhash(SB),NOSPLIT|NOFRAME,$0-24
|
||||
JMP runtime·strhashFallback(SB)
|
||||
TEXT runtime·memhash32(SB),NOSPLIT|NOFRAME,$0-24
|
||||
JMP runtime·memhash32Fallback(SB)
|
||||
TEXT runtime·memhash64(SB),NOSPLIT|NOFRAME,$0-24
|
||||
JMP runtime·memhash64Fallback(SB)
|
||||
TEXT runtime·memhash<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-32
|
||||
JMP runtime·memhashFallback<ABIInternal>(SB)
|
||||
TEXT runtime·strhash<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-24
|
||||
JMP runtime·strhashFallback<ABIInternal>(SB)
|
||||
TEXT runtime·memhash32<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-24
|
||||
JMP runtime·memhash32Fallback<ABIInternal>(SB)
|
||||
TEXT runtime·memhash64<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-24
|
||||
JMP runtime·memhash64Fallback<ABIInternal>(SB)
|
||||
|
||||
TEXT runtime·return0(SB), NOSPLIT, $0
|
||||
MOVW $0, R19
|
||||
|
|
@ -642,11 +654,102 @@ TEXT runtime·goexit(SB),NOSPLIT|NOFRAME|TOPFRAME,$0-0
|
|||
// traceback from goexit1 must hit code range of goexit
|
||||
NOOP
|
||||
|
||||
// This is called from .init_array and follows the platform, not Go, ABI.
|
||||
TEXT runtime·addmoduledata(SB),NOSPLIT,$0-0
|
||||
ADDV $-0x10, R3
|
||||
MOVV R30, 8(R3) // The access to global variables below implicitly uses R30, which is callee-save
|
||||
MOVV runtime·lastmoduledatap(SB), R12
|
||||
MOVV R4, moduledata_next(R12)
|
||||
MOVV R4, runtime·lastmoduledatap(SB)
|
||||
MOVV 8(R3), R30
|
||||
ADDV $0x10, R3
|
||||
RET
|
||||
|
||||
TEXT ·checkASM(SB),NOSPLIT,$0-1
|
||||
MOVW $1, R19
|
||||
MOVB R19, ret+0(FP)
|
||||
RET
|
||||
|
||||
#ifdef GOEXPERIMENT_regabiargs
|
||||
// spillArgs stores return values from registers to a *internal/abi.RegArgs in R25.
|
||||
TEXT ·spillArgs(SB),NOSPLIT,$0-0
|
||||
MOVV R4, (0*8)(R25)
|
||||
MOVV R5, (1*8)(R25)
|
||||
MOVV R6, (2*8)(R25)
|
||||
MOVV R7, (3*8)(R25)
|
||||
MOVV R8, (4*8)(R25)
|
||||
MOVV R9, (5*8)(R25)
|
||||
MOVV R10, (6*8)(R25)
|
||||
MOVV R11, (7*8)(R25)
|
||||
MOVV R12, (8*8)(R25)
|
||||
MOVV R13, (9*8)(R25)
|
||||
MOVV R14, (10*8)(R25)
|
||||
MOVV R15, (11*8)(R25)
|
||||
MOVV R16, (12*8)(R25)
|
||||
MOVV R17, (13*8)(R25)
|
||||
MOVV R18, (14*8)(R25)
|
||||
MOVV R19, (15*8)(R25)
|
||||
MOVD F0, (16*8)(R25)
|
||||
MOVD F1, (17*8)(R25)
|
||||
MOVD F2, (18*8)(R25)
|
||||
MOVD F3, (19*8)(R25)
|
||||
MOVD F4, (20*8)(R25)
|
||||
MOVD F5, (21*8)(R25)
|
||||
MOVD F6, (22*8)(R25)
|
||||
MOVD F7, (23*8)(R25)
|
||||
MOVD F8, (24*8)(R25)
|
||||
MOVD F9, (25*8)(R25)
|
||||
MOVD F10, (26*8)(R25)
|
||||
MOVD F11, (27*8)(R25)
|
||||
MOVD F12, (28*8)(R25)
|
||||
MOVD F13, (29*8)(R25)
|
||||
MOVD F14, (30*8)(R25)
|
||||
MOVD F15, (31*8)(R25)
|
||||
RET
|
||||
|
||||
// unspillArgs loads args into registers from a *internal/abi.RegArgs in R25.
|
||||
TEXT ·unspillArgs(SB),NOSPLIT,$0-0
|
||||
MOVV (0*8)(R25), R4
|
||||
MOVV (1*8)(R25), R5
|
||||
MOVV (2*8)(R25), R6
|
||||
MOVV (3*8)(R25), R7
|
||||
MOVV (4*8)(R25), R8
|
||||
MOVV (5*8)(R25), R9
|
||||
MOVV (6*8)(R25), R10
|
||||
MOVV (7*8)(R25), R11
|
||||
MOVV (8*8)(R25), R12
|
||||
MOVV (9*8)(R25), R13
|
||||
MOVV (10*8)(R25), R14
|
||||
MOVV (11*8)(R25), R15
|
||||
MOVV (12*8)(R25), R16
|
||||
MOVV (13*8)(R25), R17
|
||||
MOVV (14*8)(R25), R18
|
||||
MOVV (15*8)(R25), R19
|
||||
MOVD (16*8)(R25), F0
|
||||
MOVD (17*8)(R25), F1
|
||||
MOVD (18*8)(R25), F2
|
||||
MOVD (19*8)(R25), F3
|
||||
MOVD (20*8)(R25), F4
|
||||
MOVD (21*8)(R25), F5
|
||||
MOVD (22*8)(R25), F6
|
||||
MOVD (23*8)(R25), F7
|
||||
MOVD (24*8)(R25), F8
|
||||
MOVD (25*8)(R25), F9
|
||||
MOVD (26*8)(R25), F10
|
||||
MOVD (27*8)(R25), F11
|
||||
MOVD (28*8)(R25), F12
|
||||
MOVD (29*8)(R25), F13
|
||||
MOVD (30*8)(R25), F14
|
||||
MOVD (31*8)(R25), F15
|
||||
RET
|
||||
#else
|
||||
TEXT ·spillArgs(SB),NOSPLIT,$0-0
|
||||
RET
|
||||
|
||||
TEXT ·unspillArgs(SB),NOSPLIT,$0-0
|
||||
RET
|
||||
#endif
|
||||
|
||||
// gcWriteBarrier informs the GC about heap pointer writes.
|
||||
//
|
||||
// gcWriteBarrier does NOT follow the Go ABI. It accepts the
|
||||
|
|
@ -774,71 +877,156 @@ TEXT runtime·gcWriteBarrier8<ABIInternal>(SB),NOSPLIT,$0
|
|||
// in the caller's stack frame. These stubs write the args into that stack space and
|
||||
// then tail call to the corresponding runtime handler.
|
||||
// The tail call makes these stubs disappear in backtraces.
|
||||
TEXT runtime·panicIndex(SB),NOSPLIT,$0-16
|
||||
MOVV R19, x+0(FP)
|
||||
MOVV R18, y+8(FP)
|
||||
JMP runtime·goPanicIndex(SB)
|
||||
TEXT runtime·panicIndexU(SB),NOSPLIT,$0-16
|
||||
MOVV R19, x+0(FP)
|
||||
MOVV R18, y+8(FP)
|
||||
JMP runtime·goPanicIndexU(SB)
|
||||
TEXT runtime·panicSliceAlen(SB),NOSPLIT,$0-16
|
||||
MOVV R18, x+0(FP)
|
||||
MOVV R17, y+8(FP)
|
||||
JMP runtime·goPanicSliceAlen(SB)
|
||||
TEXT runtime·panicSliceAlenU(SB),NOSPLIT,$0-16
|
||||
MOVV R18, x+0(FP)
|
||||
MOVV R17, y+8(FP)
|
||||
JMP runtime·goPanicSliceAlenU(SB)
|
||||
TEXT runtime·panicSliceAcap(SB),NOSPLIT,$0-16
|
||||
MOVV R18, x+0(FP)
|
||||
MOVV R17, y+8(FP)
|
||||
JMP runtime·goPanicSliceAcap(SB)
|
||||
TEXT runtime·panicSliceAcapU(SB),NOSPLIT,$0-16
|
||||
MOVV R18, x+0(FP)
|
||||
MOVV R17, y+8(FP)
|
||||
JMP runtime·goPanicSliceAcapU(SB)
|
||||
TEXT runtime·panicSliceB(SB),NOSPLIT,$0-16
|
||||
MOVV R19, x+0(FP)
|
||||
MOVV R18, y+8(FP)
|
||||
JMP runtime·goPanicSliceB(SB)
|
||||
TEXT runtime·panicSliceBU(SB),NOSPLIT,$0-16
|
||||
MOVV R19, x+0(FP)
|
||||
MOVV R18, y+8(FP)
|
||||
JMP runtime·goPanicSliceBU(SB)
|
||||
TEXT runtime·panicSlice3Alen(SB),NOSPLIT,$0-16
|
||||
MOVV R17, x+0(FP)
|
||||
MOVV R4, y+8(FP)
|
||||
JMP runtime·goPanicSlice3Alen(SB)
|
||||
TEXT runtime·panicSlice3AlenU(SB),NOSPLIT,$0-16
|
||||
MOVV R17, x+0(FP)
|
||||
MOVV R4, y+8(FP)
|
||||
JMP runtime·goPanicSlice3AlenU(SB)
|
||||
TEXT runtime·panicSlice3Acap(SB),NOSPLIT,$0-16
|
||||
MOVV R17, x+0(FP)
|
||||
MOVV R4, y+8(FP)
|
||||
JMP runtime·goPanicSlice3Acap(SB)
|
||||
TEXT runtime·panicSlice3AcapU(SB),NOSPLIT,$0-16
|
||||
MOVV R17, x+0(FP)
|
||||
MOVV R4, y+8(FP)
|
||||
JMP runtime·goPanicSlice3AcapU(SB)
|
||||
TEXT runtime·panicSlice3B(SB),NOSPLIT,$0-16
|
||||
MOVV R18, x+0(FP)
|
||||
MOVV R17, y+8(FP)
|
||||
JMP runtime·goPanicSlice3B(SB)
|
||||
TEXT runtime·panicSlice3BU(SB),NOSPLIT,$0-16
|
||||
MOVV R18, x+0(FP)
|
||||
MOVV R17, y+8(FP)
|
||||
JMP runtime·goPanicSlice3BU(SB)
|
||||
TEXT runtime·panicSlice3C(SB),NOSPLIT,$0-16
|
||||
MOVV R19, x+0(FP)
|
||||
MOVV R18, y+8(FP)
|
||||
JMP runtime·goPanicSlice3C(SB)
|
||||
TEXT runtime·panicSlice3CU(SB),NOSPLIT,$0-16
|
||||
MOVV R19, x+0(FP)
|
||||
MOVV R18, y+8(FP)
|
||||
JMP runtime·goPanicSlice3CU(SB)
|
||||
TEXT runtime·panicSliceConvert(SB),NOSPLIT,$0-16
|
||||
MOVV R17, x+0(FP)
|
||||
MOVV R4, y+8(FP)
|
||||
JMP runtime·goPanicSliceConvert(SB)
|
||||
TEXT runtime·panicIndex<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
#ifdef GOEXPERIMENT_regabiargs
|
||||
MOVV R20, R4
|
||||
MOVV R21, R5
|
||||
#else
|
||||
MOVV R20, x+0(FP)
|
||||
MOVV R21, y+8(FP)
|
||||
#endif
|
||||
JMP runtime·goPanicIndex<ABIInternal>(SB)
|
||||
TEXT runtime·panicIndexU<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
#ifdef GOEXPERIMENT_regabiargs
|
||||
MOVV R20, R4
|
||||
MOVV R21, R5
|
||||
#else
|
||||
MOVV R20, x+0(FP)
|
||||
MOVV R21, y+8(FP)
|
||||
#endif
|
||||
JMP runtime·goPanicIndexU<ABIInternal>(SB)
|
||||
TEXT runtime·panicSliceAlen<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
#ifdef GOEXPERIMENT_regabiargs
|
||||
MOVV R21, R4
|
||||
MOVV R23, R5
|
||||
#else
|
||||
MOVV R21, x+0(FP)
|
||||
MOVV R23, y+8(FP)
|
||||
#endif
|
||||
JMP runtime·goPanicSliceAlen<ABIInternal>(SB)
|
||||
TEXT runtime·panicSliceAlenU<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
#ifdef GOEXPERIMENT_regabiargs
|
||||
MOVV R21, R4
|
||||
MOVV R23, R5
|
||||
#else
|
||||
MOVV R21, x+0(FP)
|
||||
MOVV R23, y+8(FP)
|
||||
#endif
|
||||
JMP runtime·goPanicSliceAlenU<ABIInternal>(SB)
|
||||
TEXT runtime·panicSliceAcap<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
#ifdef GOEXPERIMENT_regabiargs
|
||||
MOVV R21, R4
|
||||
MOVV R23, R5
|
||||
#else
|
||||
MOVV R21, x+0(FP)
|
||||
MOVV R23, y+8(FP)
|
||||
#endif
|
||||
JMP runtime·goPanicSliceAcap<ABIInternal>(SB)
|
||||
TEXT runtime·panicSliceAcapU<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
#ifdef GOEXPERIMENT_regabiargs
|
||||
MOVV R21, R4
|
||||
MOVV R23, R5
|
||||
#else
|
||||
MOVV R21, x+0(FP)
|
||||
MOVV R23, y+8(FP)
|
||||
#endif
|
||||
JMP runtime·goPanicSliceAcapU<ABIInternal>(SB)
|
||||
TEXT runtime·panicSliceB<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
#ifdef GOEXPERIMENT_regabiargs
|
||||
MOVV R20, R4
|
||||
MOVV R21, R5
|
||||
#else
|
||||
MOVV R20, x+0(FP)
|
||||
MOVV R21, y+8(FP)
|
||||
#endif
|
||||
JMP runtime·goPanicSliceB<ABIInternal>(SB)
|
||||
TEXT runtime·panicSliceBU<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
#ifdef GOEXPERIMENT_regabiargs
|
||||
MOVV R20, R4
|
||||
MOVV R21, R5
|
||||
#else
|
||||
MOVV R20, x+0(FP)
|
||||
MOVV R21, y+8(FP)
|
||||
#endif
|
||||
JMP runtime·goPanicSliceBU<ABIInternal>(SB)
|
||||
TEXT runtime·panicSlice3Alen<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
#ifdef GOEXPERIMENT_regabiargs
|
||||
MOVV R23, R4
|
||||
MOVV R24, R5
|
||||
#else
|
||||
MOVV R23, x+0(FP)
|
||||
MOVV R24, y+8(FP)
|
||||
#endif
|
||||
JMP runtime·goPanicSlice3Alen<ABIInternal>(SB)
|
||||
TEXT runtime·panicSlice3AlenU<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
#ifdef GOEXPERIMENT_regabiargs
|
||||
MOVV R23, R4
|
||||
MOVV R24, R5
|
||||
#else
|
||||
MOVV R23, x+0(FP)
|
||||
MOVV R24, y+8(FP)
|
||||
#endif
|
||||
JMP runtime·goPanicSlice3AlenU<ABIInternal>(SB)
|
||||
TEXT runtime·panicSlice3Acap<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
#ifdef GOEXPERIMENT_regabiargs
|
||||
MOVV R23, R4
|
||||
MOVV R24, R5
|
||||
#else
|
||||
MOVV R23, x+0(FP)
|
||||
MOVV R24, y+8(FP)
|
||||
#endif
|
||||
JMP runtime·goPanicSlice3Acap<ABIInternal>(SB)
|
||||
TEXT runtime·panicSlice3AcapU<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
#ifdef GOEXPERIMENT_regabiargs
|
||||
MOVV R23, R4
|
||||
MOVV R24, R5
|
||||
#else
|
||||
MOVV R23, x+0(FP)
|
||||
MOVV R24, y+8(FP)
|
||||
#endif
|
||||
JMP runtime·goPanicSlice3AcapU<ABIInternal>(SB)
|
||||
TEXT runtime·panicSlice3B<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
#ifdef GOEXPERIMENT_regabiargs
|
||||
MOVV R21, R4
|
||||
MOVV R23, R5
|
||||
#else
|
||||
MOVV R21, x+0(FP)
|
||||
MOVV R23, y+8(FP)
|
||||
#endif
|
||||
JMP runtime·goPanicSlice3B<ABIInternal>(SB)
|
||||
TEXT runtime·panicSlice3BU<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
#ifdef GOEXPERIMENT_regabiargs
|
||||
MOVV R21, R4
|
||||
MOVV R23, R5
|
||||
#else
|
||||
MOVV R21, x+0(FP)
|
||||
MOVV R23, y+8(FP)
|
||||
#endif
|
||||
JMP runtime·goPanicSlice3BU<ABIInternal>(SB)
|
||||
TEXT runtime·panicSlice3C<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
#ifdef GOEXPERIMENT_regabiargs
|
||||
MOVV R20, R4
|
||||
MOVV R21, R5
|
||||
#else
|
||||
MOVV R20, x+0(FP)
|
||||
MOVV R21, y+8(FP)
|
||||
#endif
|
||||
JMP runtime·goPanicSlice3C<ABIInternal>(SB)
|
||||
TEXT runtime·panicSlice3CU<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
#ifdef GOEXPERIMENT_regabiargs
|
||||
MOVV R20, R4
|
||||
MOVV R21, R5
|
||||
#else
|
||||
MOVV R20, x+0(FP)
|
||||
MOVV R21, y+8(FP)
|
||||
#endif
|
||||
JMP runtime·goPanicSlice3CU<ABIInternal>(SB)
|
||||
TEXT runtime·panicSliceConvert<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
#ifdef GOEXPERIMENT_regabiargs
|
||||
MOVV R23, R4
|
||||
MOVV R24, R5
|
||||
#else
|
||||
MOVV R23, x+0(FP)
|
||||
MOVV R24, y+8(FP)
|
||||
#endif
|
||||
JMP runtime·goPanicSliceConvert<ABIInternal>(SB)
|
||||
|
|
|
|||
|
|
@ -795,14 +795,12 @@ func TestG0StackOverflow(t *testing.T) {
|
|||
if runtime.GOOS == "ios" {
|
||||
testenv.SkipFlaky(t, 62671)
|
||||
}
|
||||
if runtime.GOOS == "windows" && runtime.GOARCH == "arm64" {
|
||||
testenv.SkipFlaky(t, 63938) // TODO(cherry): fix and unskip
|
||||
}
|
||||
|
||||
if os.Getenv("TEST_G0_STACK_OVERFLOW") != "1" {
|
||||
cmd := testenv.CleanCmdEnv(testenv.Command(t, os.Args[0], "-test.run=^TestG0StackOverflow$", "-test.v"))
|
||||
cmd.Env = append(cmd.Env, "TEST_G0_STACK_OVERFLOW=1")
|
||||
out, err := cmd.CombinedOutput()
|
||||
t.Logf("output:\n%s", out)
|
||||
// Don't check err since it's expected to crash.
|
||||
if n := strings.Count(string(out), "morestack on g0\n"); n != 1 {
|
||||
t.Fatalf("%s\n(exit status %v)", out, err)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1920,24 +1920,8 @@ func UserArenaClone[T any](s T) T {
|
|||
|
||||
var AlignUp = alignUp
|
||||
|
||||
// BlockUntilEmptyFinalizerQueue blocks until either the finalizer
|
||||
// queue is emptied (and the finalizers have executed) or the timeout
|
||||
// is reached. Returns true if the finalizer queue was emptied.
|
||||
func BlockUntilEmptyFinalizerQueue(timeout int64) bool {
|
||||
start := nanotime()
|
||||
for nanotime()-start < timeout {
|
||||
lock(&finlock)
|
||||
// We know the queue has been drained when both finq is nil
|
||||
// and the finalizer g has stopped executing.
|
||||
empty := finq == nil
|
||||
empty = empty && readgstatus(fing) == _Gwaiting && fing.waitreason == waitReasonFinalizerWait
|
||||
unlock(&finlock)
|
||||
if empty {
|
||||
return true
|
||||
}
|
||||
Gosched()
|
||||
}
|
||||
return false
|
||||
return blockUntilEmptyFinalizerQueue(timeout)
|
||||
}
|
||||
|
||||
func FrameStartLine(f *Frame) int {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build s390x || loong64 || mips || mipsle || mips64 || mips64le || wasm
|
||||
//go:build arm || s390x || loong64 || mips || mipsle || mips64 || mips64le || wasm
|
||||
|
||||
package atomic
|
||||
|
||||
|
|
|
|||
|
|
@ -208,66 +208,6 @@ func And(addr *uint32, v uint32) {
|
|||
}
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func Or32(addr *uint32, v uint32) uint32 {
|
||||
for {
|
||||
old := *addr
|
||||
if Cas(addr, old, old|v) {
|
||||
return old
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func And32(addr *uint32, v uint32) uint32 {
|
||||
for {
|
||||
old := *addr
|
||||
if Cas(addr, old, old&v) {
|
||||
return old
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func Or64(addr *uint64, v uint64) uint64 {
|
||||
for {
|
||||
old := *addr
|
||||
if Cas64(addr, old, old|v) {
|
||||
return old
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func And64(addr *uint64, v uint64) uint64 {
|
||||
for {
|
||||
old := *addr
|
||||
if Cas64(addr, old, old&v) {
|
||||
return old
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func Oruintptr(addr *uintptr, v uintptr) uintptr {
|
||||
for {
|
||||
old := *addr
|
||||
if Casuintptr(addr, old, old|v) {
|
||||
return old
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func Anduintptr(addr *uintptr, v uintptr) uintptr {
|
||||
for {
|
||||
old := *addr
|
||||
if Casuintptr(addr, old, old&v) {
|
||||
return old
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func armcas(ptr *uint32, old, new uint32) bool
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,32 @@
|
|||
#include "textflag.h"
|
||||
|
||||
// func Syscall6(num, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, errno uintptr)
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
//
|
||||
// We need to convert to the syscall ABI.
|
||||
//
|
||||
// arg | ABIInternal | Syscall
|
||||
// ---------------------------
|
||||
// num | R4 | R11
|
||||
// a1 | R5 | R4
|
||||
// a2 | R6 | R5
|
||||
// a3 | R7 | R6
|
||||
// a4 | R8 | R7
|
||||
// a5 | R9 | R8
|
||||
// a6 | R10 | R9
|
||||
//
|
||||
// r1 | R4 | R4
|
||||
// r2 | R5 | R5
|
||||
// err | R6 | part of R4
|
||||
TEXT ·Syscall6<ABIInternal>(SB),NOSPLIT,$0-80
|
||||
#ifdef GOEXPERIMENT_regabiargs
|
||||
MOVV R4, R11 // syscall entry
|
||||
MOVV R5, R4
|
||||
MOVV R6, R5
|
||||
MOVV R7, R6
|
||||
MOVV R8, R7
|
||||
MOVV R9, R8
|
||||
MOVV R10, R9
|
||||
#else
|
||||
MOVV num+0(FP), R11 // syscall entry
|
||||
MOVV a1+8(FP), R4
|
||||
MOVV a2+16(FP), R5
|
||||
|
|
@ -13,7 +38,15 @@ TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
|||
MOVV a4+32(FP), R7
|
||||
MOVV a5+40(FP), R8
|
||||
MOVV a6+48(FP), R9
|
||||
#endif
|
||||
SYSCALL
|
||||
#ifdef GOEXPERIMENT_regabiargs
|
||||
MOVV R0, R5 // r2 is not used. Always set to 0.
|
||||
MOVW $-4096, R12
|
||||
BGEU R12, R4, ok
|
||||
SUBVU R4, R0, R6 // errno
|
||||
MOVV $-1, R4 // r1
|
||||
#else
|
||||
MOVW $-4096, R12
|
||||
BGEU R12, R4, ok
|
||||
MOVV $-1, R12
|
||||
|
|
@ -21,9 +54,15 @@ TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
|||
MOVV R0, r2+64(FP)
|
||||
SUBVU R4, R0, R4
|
||||
MOVV R4, errno+72(FP)
|
||||
#endif
|
||||
RET
|
||||
ok:
|
||||
#ifdef GOEXPERIMENT_regabiargs
|
||||
// r1 already in R4
|
||||
MOVV R0, R6 // errno
|
||||
#else
|
||||
MOVV R4, r1+56(FP)
|
||||
MOVV R0, r2+64(FP) // r2 is not used. Always set to 0.
|
||||
MOVV R0, errno+72(FP)
|
||||
#endif
|
||||
RET
|
||||
|
|
|
|||
|
|
@ -6,37 +6,39 @@
|
|||
#include "textflag.h"
|
||||
|
||||
// func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)
|
||||
TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT,$0-16
|
||||
MOVV ptr+0(FP), R6
|
||||
MOVV n+8(FP), R7
|
||||
ADDV R6, R7, R4
|
||||
TEXT runtime·memclrNoHeapPointers<ABIInternal>(SB),NOSPLIT,$0-16
|
||||
#ifndef GOEXPERIMENT_regabiargs
|
||||
MOVV ptr+0(FP), R4
|
||||
MOVV n+8(FP), R5
|
||||
#endif
|
||||
ADDV R4, R5, R6
|
||||
|
||||
// if less than 8 bytes, do one byte at a time
|
||||
SGTU $8, R7, R8
|
||||
SGTU $8, R5, R8
|
||||
BNE R8, out
|
||||
|
||||
// do one byte at a time until 8-aligned
|
||||
AND $7, R6, R8
|
||||
AND $7, R4, R8
|
||||
BEQ R8, words
|
||||
MOVB R0, (R6)
|
||||
ADDV $1, R6
|
||||
MOVB R0, (R4)
|
||||
ADDV $1, R4
|
||||
JMP -4(PC)
|
||||
|
||||
words:
|
||||
// do 8 bytes at a time if there is room
|
||||
ADDV $-7, R4, R7
|
||||
ADDV $-7, R6, R5
|
||||
|
||||
PCALIGN $16
|
||||
SGTU R7, R6, R8
|
||||
SGTU R5, R4, R8
|
||||
BEQ R8, out
|
||||
MOVV R0, (R6)
|
||||
ADDV $8, R6
|
||||
MOVV R0, (R4)
|
||||
ADDV $8, R4
|
||||
JMP -4(PC)
|
||||
|
||||
out:
|
||||
BEQ R6, R4, done
|
||||
MOVB R0, (R6)
|
||||
ADDV $1, R6
|
||||
BEQ R4, R6, done
|
||||
MOVB R0, (R4)
|
||||
ADDV $1, R4
|
||||
JMP -3(PC)
|
||||
done:
|
||||
RET
|
||||
|
|
|
|||
|
|
@ -7,10 +7,12 @@
|
|||
// See memmove Go doc for important implementation constraints.
|
||||
|
||||
// func memmove(to, from unsafe.Pointer, n uintptr)
|
||||
TEXT runtime·memmove(SB), NOSPLIT|NOFRAME, $0-24
|
||||
TEXT runtime·memmove<ABIInternal>(SB), NOSPLIT|NOFRAME, $0-24
|
||||
#ifndef GOEXPERIMENT_regabiargs
|
||||
MOVV to+0(FP), R4
|
||||
MOVV from+8(FP), R5
|
||||
MOVV n+16(FP), R6
|
||||
#endif
|
||||
BNE R6, check
|
||||
RET
|
||||
|
||||
|
|
|
|||
|
|
@ -314,6 +314,10 @@ Below is the full list of supported metrics, ordered lexicographically.
|
|||
The number of non-default behaviors executed by the crypto/tls
|
||||
package due to a non-default GODEBUG=tlsrsakex=... setting.
|
||||
|
||||
/godebug/non-default-behavior/tlsunsafeekm:events
|
||||
The number of non-default behaviors executed by the crypto/tls
|
||||
package due to a non-default GODEBUG=tlsunsafeekm=... setting.
|
||||
|
||||
/godebug/non-default-behavior/x509sha1:events
|
||||
The number of non-default behaviors executed by the crypto/x509
|
||||
package due to a non-default GODEBUG=x509sha1=... setting.
|
||||
|
|
|
|||
|
|
@ -300,6 +300,27 @@ func isGoPointerWithoutSpan(p unsafe.Pointer) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// blockUntilEmptyFinalizerQueue blocks until either the finalizer
|
||||
// queue is emptied (and the finalizers have executed) or the timeout
|
||||
// is reached. Returns true if the finalizer queue was emptied.
|
||||
// This is used by the runtime and sync tests.
|
||||
func blockUntilEmptyFinalizerQueue(timeout int64) bool {
|
||||
start := nanotime()
|
||||
for nanotime()-start < timeout {
|
||||
lock(&finlock)
|
||||
// We know the queue has been drained when both finq is nil
|
||||
// and the finalizer g has stopped executing.
|
||||
empty := finq == nil
|
||||
empty = empty && readgstatus(fing) == _Gwaiting && fing.waitreason == waitReasonFinalizerWait
|
||||
unlock(&finlock)
|
||||
if empty {
|
||||
return true
|
||||
}
|
||||
Gosched()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetFinalizer sets the finalizer associated with obj to the provided
|
||||
// finalizer function. When the garbage collector finds an unreachable block
|
||||
// with an associated finalizer, it clears the association and runs
|
||||
|
|
|
|||
|
|
@ -181,21 +181,21 @@ func zeroLOONG64(w io.Writer) {
|
|||
// R0: always zero
|
||||
// R19 (aka REGRT1): ptr to memory to be zeroed
|
||||
// On return, R19 points to the last zeroed dword.
|
||||
fmt.Fprintln(w, "TEXT runtime·duffzero(SB), NOSPLIT|NOFRAME, $0-0")
|
||||
fmt.Fprintln(w, "TEXT runtime·duffzero<ABIInternal>(SB), NOSPLIT|NOFRAME, $0-0")
|
||||
for i := 0; i < 128; i++ {
|
||||
fmt.Fprintln(w, "\tMOVV\tR0, (R19)")
|
||||
fmt.Fprintln(w, "\tADDV\t$8, R19")
|
||||
fmt.Fprintln(w, "\tMOVV\tR0, (R20)")
|
||||
fmt.Fprintln(w, "\tADDV\t$8, R20")
|
||||
}
|
||||
fmt.Fprintln(w, "\tRET")
|
||||
}
|
||||
|
||||
func copyLOONG64(w io.Writer) {
|
||||
fmt.Fprintln(w, "TEXT runtime·duffcopy(SB), NOSPLIT|NOFRAME, $0-0")
|
||||
fmt.Fprintln(w, "TEXT runtime·duffcopy<ABIInternal>(SB), NOSPLIT|NOFRAME, $0-0")
|
||||
for i := 0; i < 128; i++ {
|
||||
fmt.Fprintln(w, "\tMOVV\t(R19), R30")
|
||||
fmt.Fprintln(w, "\tADDV\t$8, R19")
|
||||
fmt.Fprintln(w, "\tMOVV\tR30, (R20)")
|
||||
fmt.Fprintln(w, "\tMOVV\t(R20), R30")
|
||||
fmt.Fprintln(w, "\tADDV\t$8, R20")
|
||||
fmt.Fprintln(w, "\tMOVV\tR30, (R21)")
|
||||
fmt.Fprintln(w, "\tADDV\t$8, R21")
|
||||
fmt.Fprintln(w)
|
||||
}
|
||||
fmt.Fprintln(w, "\tRET")
|
||||
|
|
|
|||
|
|
@ -576,7 +576,10 @@ func switchToCrashStack(fn func()) {
|
|||
abort()
|
||||
}
|
||||
|
||||
const crashStackImplemented = GOARCH == "amd64" || GOARCH == "arm64" || GOARCH == "mips64" || GOARCH == "mips64le" || GOARCH == "ppc64" || GOARCH == "ppc64le" || GOARCH == "riscv64" || GOARCH == "wasm"
|
||||
// Disable crash stack on Windows for now. Apparently, throwing an exception
|
||||
// on a non-system-allocated crash stack causes EXCEPTION_STACK_OVERFLOW and
|
||||
// hangs the process (see issue 63938).
|
||||
const crashStackImplemented = (GOARCH == "amd64" || GOARCH == "arm64" || GOARCH == "mips64" || GOARCH == "mips64le" || GOARCH == "ppc64" || GOARCH == "ppc64le" || GOARCH == "riscv64" || GOARCH == "wasm") && GOOS != "windows"
|
||||
|
||||
//go:noescape
|
||||
func switchToCrashStack0(fn func()) // in assembly
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ func (frame *stkframe) getStackMap(debug bool) (locals, args bitvector, objs []s
|
|||
}
|
||||
|
||||
// stack objects.
|
||||
if (GOARCH == "amd64" || GOARCH == "arm64" || GOARCH == "ppc64" || GOARCH == "ppc64le" || GOARCH == "riscv64") &&
|
||||
if (GOARCH == "amd64" || GOARCH == "arm64" || GOARCH == "loong64" || GOARCH == "ppc64" || GOARCH == "ppc64le" || GOARCH == "riscv64") &&
|
||||
unsafe.Sizeof(abi.RegArgs{}) > 0 && isReflect {
|
||||
// For reflect.makeFuncStub and reflect.methodValueCall,
|
||||
// we need to fake the stack object record.
|
||||
|
|
|
|||
|
|
@ -10,6 +10,13 @@ package runtime
|
|||
func load_g()
|
||||
func save_g()
|
||||
|
||||
// Used by reflectcall and the reflect package.
|
||||
//
|
||||
// Spills/loads arguments in registers to/from an internal/abi.RegArgs
|
||||
// respectively. Does not follow the Go ABI.
|
||||
func spillArgs()
|
||||
func unspillArgs()
|
||||
|
||||
// getfp returns the frame pointer register of its caller or 0 if not implemented.
|
||||
// TODO: Make this a compiler intrinsic
|
||||
func getfp() uintptr { return 0 }
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ func OnceFunc(f func()) func() {
|
|||
}
|
||||
}()
|
||||
f()
|
||||
valid = true // Set only if f does not panic
|
||||
f = nil // Do not keep f alive after invoking it.
|
||||
valid = true // Set only if f does not panic.
|
||||
}
|
||||
return func() {
|
||||
once.Do(g)
|
||||
|
|
@ -54,6 +55,7 @@ func OnceValue[T any](f func() T) func() T {
|
|||
}
|
||||
}()
|
||||
result = f()
|
||||
f = nil
|
||||
valid = true
|
||||
}
|
||||
return func() T {
|
||||
|
|
@ -85,6 +87,7 @@ func OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2) {
|
|||
}
|
||||
}()
|
||||
r1, r2 = f()
|
||||
f = nil
|
||||
valid = true
|
||||
}
|
||||
return func() (T1, T2) {
|
||||
|
|
|
|||
|
|
@ -6,10 +6,13 @@ package sync_test
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
_ "unsafe"
|
||||
)
|
||||
|
||||
// We assume that the Once.Do tests have already covered parallelism.
|
||||
|
|
@ -182,6 +185,53 @@ func onceFuncPanic() {
|
|||
panic("x")
|
||||
}
|
||||
|
||||
func TestOnceXGC(t *testing.T) {
|
||||
fns := map[string]func([]byte) func(){
|
||||
"OnceFunc": func(buf []byte) func() {
|
||||
return sync.OnceFunc(func() { buf[0] = 1 })
|
||||
},
|
||||
"OnceValue": func(buf []byte) func() {
|
||||
f := sync.OnceValue(func() any { buf[0] = 1; return nil })
|
||||
return func() { f() }
|
||||
},
|
||||
"OnceValues": func(buf []byte) func() {
|
||||
f := sync.OnceValues(func() (any, any) { buf[0] = 1; return nil, nil })
|
||||
return func() { f() }
|
||||
},
|
||||
}
|
||||
for n, fn := range fns {
|
||||
t.Run(n, func(t *testing.T) {
|
||||
buf := make([]byte, 1024)
|
||||
var gc atomic.Bool
|
||||
runtime.SetFinalizer(&buf[0], func(_ *byte) {
|
||||
gc.Store(true)
|
||||
})
|
||||
f := fn(buf)
|
||||
gcwaitfin()
|
||||
if gc.Load() != false {
|
||||
t.Fatal("wrapped function garbage collected too early")
|
||||
}
|
||||
f()
|
||||
gcwaitfin()
|
||||
if gc.Load() != true {
|
||||
// Even if f is still alive, the function passed to Once(Func|Value|Values)
|
||||
// is not kept alive after the first call to f.
|
||||
t.Fatal("wrapped function should be garbage collected, but still live")
|
||||
}
|
||||
f()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// gcwaitfin performs garbage collection and waits for all finalizers to run.
|
||||
func gcwaitfin() {
|
||||
runtime.GC()
|
||||
runtime_blockUntilEmptyFinalizerQueue(math.MaxInt64)
|
||||
}
|
||||
|
||||
//go:linkname runtime_blockUntilEmptyFinalizerQueue runtime.blockUntilEmptyFinalizerQueue
|
||||
func runtime_blockUntilEmptyFinalizerQueue(int64) bool
|
||||
|
||||
var (
|
||||
onceFunc = sync.OnceFunc(func() {})
|
||||
|
||||
|
|
|
|||
|
|
@ -2,98 +2,107 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package testing
|
||||
package testing_test
|
||||
|
||||
import (
|
||||
"internal/testenv"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTBHelper(t *T) {
|
||||
var buf strings.Builder
|
||||
ctx := newTestContext(1, allMatcher())
|
||||
t1 := &T{
|
||||
common: common{
|
||||
signal: make(chan bool),
|
||||
w: &buf,
|
||||
},
|
||||
context: ctx,
|
||||
}
|
||||
t1.Run("Test", testHelper)
|
||||
func TestTBHelper(t *testing.T) {
|
||||
if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
|
||||
testTestHelper(t)
|
||||
|
||||
want := `--- FAIL: Test (?s)
|
||||
helperfuncs_test.go:12: 0
|
||||
helperfuncs_test.go:40: 1
|
||||
helperfuncs_test.go:21: 2
|
||||
helperfuncs_test.go:42: 3
|
||||
helperfuncs_test.go:49: 4
|
||||
--- FAIL: Test/sub (?s)
|
||||
helperfuncs_test.go:52: 5
|
||||
helperfuncs_test.go:21: 6
|
||||
helperfuncs_test.go:51: 7
|
||||
helperfuncs_test.go:63: 8
|
||||
--- FAIL: Test/sub2 (?s)
|
||||
helperfuncs_test.go:78: 11
|
||||
helperfuncs_test.go:82: recover 12
|
||||
helperfuncs_test.go:84: GenericFloat64
|
||||
helperfuncs_test.go:85: GenericInt
|
||||
helperfuncs_test.go:71: 9
|
||||
helperfuncs_test.go:67: 10
|
||||
// Check that calling Helper from inside a top-level test function
|
||||
// has no effect.
|
||||
t.Helper()
|
||||
t.Error("8")
|
||||
return
|
||||
}
|
||||
|
||||
testenv.MustHaveExec(t)
|
||||
t.Parallel()
|
||||
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cmd := testenv.Command(t, exe, "-test.run=^TestTBHelper$")
|
||||
cmd = testenv.CleanCmdEnv(cmd)
|
||||
cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=1")
|
||||
out, _ := cmd.CombinedOutput()
|
||||
|
||||
want := `--- FAIL: TestTBHelper \([^)]+\)
|
||||
helperfuncs_test.go:15: 0
|
||||
helperfuncs_test.go:47: 1
|
||||
helperfuncs_test.go:24: 2
|
||||
helperfuncs_test.go:49: 3
|
||||
helperfuncs_test.go:56: 4
|
||||
--- FAIL: TestTBHelper/sub \([^)]+\)
|
||||
helperfuncs_test.go:59: 5
|
||||
helperfuncs_test.go:24: 6
|
||||
helperfuncs_test.go:58: 7
|
||||
--- FAIL: TestTBHelper/sub2 \([^)]+\)
|
||||
helperfuncs_test.go:80: 11
|
||||
helperfuncs_test.go:84: recover 12
|
||||
helperfuncs_test.go:86: GenericFloat64
|
||||
helperfuncs_test.go:87: GenericInt
|
||||
helper_test.go:22: 8
|
||||
helperfuncs_test.go:73: 9
|
||||
helperfuncs_test.go:69: 10
|
||||
`
|
||||
lines := strings.Split(buf.String(), "\n")
|
||||
durationRE := regexp.MustCompile(`\(.*\)$`)
|
||||
for i, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
line = durationRE.ReplaceAllString(line, "(?s)")
|
||||
lines[i] = line
|
||||
}
|
||||
got := strings.Join(lines, "\n")
|
||||
if got != want {
|
||||
t.Errorf("got output:\n\n%s\nwant:\n\n%s", got, want)
|
||||
if !regexp.MustCompile(want).Match(out) {
|
||||
t.Errorf("got output:\n\n%s\nwant matching:\n\n%s", out, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTBHelperParallel(t *T) {
|
||||
var buf strings.Builder
|
||||
ctx := newTestContext(1, newMatcher(regexp.MatchString, "", "", ""))
|
||||
t1 := &T{
|
||||
common: common{
|
||||
signal: make(chan bool),
|
||||
w: &buf,
|
||||
},
|
||||
context: ctx,
|
||||
func TestTBHelperParallel(t *testing.T) {
|
||||
if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
|
||||
parallelTestHelper(t)
|
||||
return
|
||||
}
|
||||
t1.Run("Test", parallelTestHelper)
|
||||
|
||||
lines := strings.Split(strings.TrimSpace(buf.String()), "\n")
|
||||
if len(lines) != 6 {
|
||||
t.Fatalf("parallelTestHelper gave %d lines of output; want 6", len(lines))
|
||||
testenv.MustHaveExec(t)
|
||||
t.Parallel()
|
||||
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := "helperfuncs_test.go:21: parallel"
|
||||
|
||||
cmd := testenv.Command(t, exe, "-test.run=^TestTBHelperParallel$")
|
||||
cmd = testenv.CleanCmdEnv(cmd)
|
||||
cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=1")
|
||||
out, _ := cmd.CombinedOutput()
|
||||
|
||||
t.Logf("output:\n%s", out)
|
||||
|
||||
lines := strings.Split(strings.TrimSpace(string(out)), "\n")
|
||||
|
||||
// We expect to see one "--- FAIL" line at the start
|
||||
// of the log, five lines of "parallel" logging,
|
||||
// and a final "FAIL" line at the end of the test.
|
||||
const wantLines = 7
|
||||
|
||||
if len(lines) != wantLines {
|
||||
t.Fatalf("parallelTestHelper gave %d lines of output; want %d", len(lines), wantLines)
|
||||
}
|
||||
want := "helperfuncs_test.go:24: parallel"
|
||||
if got := strings.TrimSpace(lines[1]); got != want {
|
||||
t.Errorf("got output line %q; want %q", got, want)
|
||||
t.Errorf("got second output line %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
type noopWriter int
|
||||
|
||||
func (nw *noopWriter) Write(b []byte) (int, error) { return len(b), nil }
|
||||
|
||||
func BenchmarkTBHelper(b *B) {
|
||||
w := noopWriter(0)
|
||||
ctx := newTestContext(1, allMatcher())
|
||||
t1 := &T{
|
||||
common: common{
|
||||
signal: make(chan bool),
|
||||
w: &w,
|
||||
},
|
||||
context: ctx,
|
||||
}
|
||||
func BenchmarkTBHelper(b *testing.B) {
|
||||
f1 := func() {
|
||||
t1.Helper()
|
||||
b.Helper()
|
||||
}
|
||||
f2 := func() {
|
||||
t1.Helper()
|
||||
b.Helper()
|
||||
}
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
|
|
|||
|
|
@ -2,38 +2,45 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package testing
|
||||
package testing_test
|
||||
|
||||
import "sync"
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// The line numbering of this file is important for TestTBHelper.
|
||||
|
||||
func notHelper(t *T, msg string) {
|
||||
func notHelper(t *testing.T, msg string) {
|
||||
t.Error(msg)
|
||||
}
|
||||
|
||||
func helper(t *T, msg string) {
|
||||
func helper(t *testing.T, msg string) {
|
||||
t.Helper()
|
||||
t.Error(msg)
|
||||
}
|
||||
|
||||
func notHelperCallingHelper(t *T, msg string) {
|
||||
func notHelperCallingHelper(t *testing.T, msg string) {
|
||||
helper(t, msg)
|
||||
}
|
||||
|
||||
func helperCallingHelper(t *T, msg string) {
|
||||
func helperCallingHelper(t *testing.T, msg string) {
|
||||
t.Helper()
|
||||
helper(t, msg)
|
||||
}
|
||||
|
||||
func genericHelper[G any](t *T, msg string) {
|
||||
func genericHelper[G any](t *testing.T, msg string) {
|
||||
t.Helper()
|
||||
t.Error(msg)
|
||||
}
|
||||
|
||||
var genericIntHelper = genericHelper[int]
|
||||
|
||||
func testHelper(t *T) {
|
||||
func testTestHelper(t *testing.T) {
|
||||
testHelper(t)
|
||||
}
|
||||
|
||||
func testHelper(t *testing.T) {
|
||||
// Check combinations of directly and indirectly
|
||||
// calling helper functions.
|
||||
notHelper(t, "0")
|
||||
|
|
@ -48,7 +55,7 @@ func testHelper(t *T) {
|
|||
}
|
||||
fn("4")
|
||||
|
||||
t.Run("sub", func(t *T) {
|
||||
t.Run("sub", func(t *testing.T) {
|
||||
helper(t, "5")
|
||||
notHelperCallingHelper(t, "6")
|
||||
// Check that calling Helper from inside a subtest entry function
|
||||
|
|
@ -57,11 +64,6 @@ func testHelper(t *T) {
|
|||
t.Error("7")
|
||||
})
|
||||
|
||||
// Check that calling Helper from inside a top-level test function
|
||||
// has no effect.
|
||||
t.Helper()
|
||||
t.Error("8")
|
||||
|
||||
// Check that right caller is reported for func passed to Cleanup when
|
||||
// multiple cleanup functions have been registered.
|
||||
t.Cleanup(func() {
|
||||
|
|
@ -85,7 +87,7 @@ func testHelper(t *T) {
|
|||
genericIntHelper(t, "GenericInt")
|
||||
}
|
||||
|
||||
func parallelTestHelper(t *T) {
|
||||
func parallelTestHelper(t *testing.T) {
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 5; i++ {
|
||||
wg.Add(1)
|
||||
|
|
@ -97,15 +99,15 @@ func parallelTestHelper(t *T) {
|
|||
wg.Wait()
|
||||
}
|
||||
|
||||
func helperSubCallingHelper(t *T, msg string) {
|
||||
func helperSubCallingHelper(t *testing.T, msg string) {
|
||||
t.Helper()
|
||||
t.Run("sub2", func(t *T) {
|
||||
t.Run("sub2", func(t *testing.T) {
|
||||
t.Helper()
|
||||
t.Fatal(msg)
|
||||
})
|
||||
}
|
||||
|
||||
func recoverHelper(t *T, msg string) {
|
||||
func recoverHelper(t *testing.T, msg string) {
|
||||
t.Helper()
|
||||
defer func() {
|
||||
t.Helper()
|
||||
|
|
@ -116,7 +118,7 @@ func recoverHelper(t *T, msg string) {
|
|||
doPanic(t, msg)
|
||||
}
|
||||
|
||||
func doPanic(t *T, msg string) {
|
||||
func doPanic(t *testing.T, msg string) {
|
||||
t.Helper()
|
||||
panic(msg)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue