Merge remote-tracking branch 'origin' into hotfix/atomic-and-or-wasm-dup

Change-Id: I256e2c89ffe8dc5ee5a7fe16f25a1cbd41ffd592
This commit is contained in:
Mauri de Souza Meneguzzo 2023-11-21 16:33:58 -03:00
commit 92736a6e34
64 changed files with 2800 additions and 1685 deletions

View File

@ -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,

View File

@ -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 Gos 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

View File

@ -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
}

View File

@ -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)
}

View File

@ -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) {

View File

@ -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] &&

View File

@ -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
}

View File

@ -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
}

View File

@ -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 {

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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()
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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:

View File

@ -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":

View File

@ -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

View File

@ -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

View File

@ -39,6 +39,7 @@ var cnames0 = []string{
"ADDR",
"TLS_LE",
"TLS_IE",
"GOTADDR",
"TEXTSIZE",
"NCLASS",
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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)
}

View File

@ -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
}

View File

@ -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) {

View File

@ -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
)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"},

View File

@ -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":

View File

@ -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 {

View File

@ -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 {

View File

@ -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)
}
}

View File

@ -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

View File

@ -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:]

View File

@ -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)

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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")

View File

@ -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

View File

@ -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.

View File

@ -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 }

View File

@ -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) {

View File

@ -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() {})

View File

@ -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()

View File

@ -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)
}