go/analysis/passes/asmdecl: support in-register result in ABIInternal

With register ABI, for an ABIInternal function, understand that
the result may be written to a register instead of to memory.

TODO: argument area size calculation is still not fixed for
register ABI.

Change-Id: I109a5dc03ff0acc4bc04502710daf32efd1b08f6
Reviewed-on: https://go-review.googlesource.com/c/tools/+/339250
Trust: Cherry Mui <cherryyz@google.com>
Run-TryBot: Cherry Mui <cherryyz@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
This commit is contained in:
Cherry Mui 2021-08-02 18:20:32 -04:00
parent fcc905b221
commit 337cebd2c1
3 changed files with 40 additions and 8 deletions

View file

@ -51,6 +51,11 @@ type asmArch struct {
bigEndian bool
stack string
lr bool
// retRegs is a list of registers for return value in register ABI (ABIInternal).
// For now, as we only check whether we write to any result, here we only need to
// include the first integer register and first floating-point register. Accessing
// any of them counts as writing to result.
retRegs []string
// calculated during initialization
sizes types.Sizes
intSize int
@ -79,8 +84,8 @@ type asmVar struct {
var (
asmArch386 = asmArch{name: "386", bigEndian: false, stack: "SP", lr: false}
asmArchArm = asmArch{name: "arm", bigEndian: false, stack: "R13", lr: true}
asmArchArm64 = asmArch{name: "arm64", bigEndian: false, stack: "RSP", lr: true}
asmArchAmd64 = asmArch{name: "amd64", bigEndian: false, stack: "SP", lr: false}
asmArchArm64 = asmArch{name: "arm64", bigEndian: false, stack: "RSP", lr: true, retRegs: []string{"R0", "F0"}}
asmArchAmd64 = asmArch{name: "amd64", bigEndian: false, stack: "SP", lr: false, retRegs: []string{"AX", "X0"}}
asmArchMips = asmArch{name: "mips", bigEndian: true, stack: "R29", lr: true}
asmArchMipsLE = asmArch{name: "mipsle", bigEndian: false, stack: "R29", lr: true}
asmArchMips64 = asmArch{name: "mips64", bigEndian: true, stack: "R29", lr: true}
@ -137,7 +142,7 @@ var (
asmSP = re(`[^+\-0-9](([0-9]+)\(([A-Z0-9]+)\))`)
asmOpcode = re(`^\s*(?:[A-Z0-9a-z_]+:)?\s*([A-Z]+)\s*([^,]*)(?:,\s*(.*))?`)
ppc64Suff = re(`([BHWD])(ZU|Z|U|BR)?$`)
abiSuff = re(`^(.+)<ABI.+>$`)
abiSuff = re(`^(.+)<(ABI.+)>$`)
)
func run(pass *analysis.Pass) (interface{}, error) {
@ -185,6 +190,7 @@ Files:
var (
fn *asmFunc
fnName string
abi string
localSize, argSize int
wroteSP bool
noframe bool
@ -195,18 +201,22 @@ Files:
flushRet := func() {
if fn != nil && fn.vars["ret"] != nil && !haveRetArg && len(retLine) > 0 {
v := fn.vars["ret"]
resultStr := fmt.Sprintf("%d-byte ret+%d(FP)", v.size, v.off)
if abi == "ABIInternal" {
resultStr = "result register"
}
for _, line := range retLine {
pass.Reportf(analysisutil.LineStart(tf, line), "[%s] %s: RET without writing to %d-byte ret+%d(FP)", arch, fnName, v.size, v.off)
pass.Reportf(analysisutil.LineStart(tf, line), "[%s] %s: RET without writing to %s", arch, fnName, resultStr)
}
}
retLine = nil
}
trimABI := func(fnName string) string {
trimABI := func(fnName string) (string, string) {
m := abiSuff.FindStringSubmatch(fnName)
if m != nil {
return m[1]
return m[1], m[2]
}
return fnName
return fnName, ""
}
for lineno, line := range lines {
lineno++
@ -273,11 +283,12 @@ Files:
// log.Printf("%s:%d: [%s] cannot check cross-package assembly function: %s is in package %s", fname, lineno, arch, fnName, pkgPath)
fn = nil
fnName = ""
abi = ""
continue
}
}
// Trim off optional ABI selector.
fnName := trimABI(fnName)
fnName, abi = trimABI(fnName)
flag := m[3]
fn = knownFunc[fnName][arch]
if fn != nil {
@ -305,6 +316,7 @@ Files:
flushRet()
fn = nil
fnName = ""
abi = ""
continue
}
@ -335,6 +347,15 @@ Files:
haveRetArg = true
}
if abi == "ABIInternal" && !haveRetArg {
for _, reg := range archDef.retRegs {
if strings.Contains(line, reg) {
haveRetArg = true
break
}
}
}
for _, m := range asmSP.FindAllStringSubmatch(line, -1) {
if m[3] != archDef.stack || wroteSP || noframe {
continue

View file

@ -52,4 +52,7 @@ func pickStableABI(x int)
func pickInternalABI(x int)
func pickFutureABI(x int)
func returnABIInternal() int
func returnmissingABIInternal() int
func retjmp() int

View file

@ -346,6 +346,14 @@ TEXT ·pickFutureABI<ABISomethingNotyetInvented>(SB), NOSPLIT, $32
MOVQ x+0(FP), AX
RET
// writing to result in ABIInternal function
TEXT ·returnABIInternal<ABIInternal>(SB), NOSPLIT, $32
MOVQ $123, AX
RET
TEXT ·returnmissingABIInternal<ABIInternal>(SB), NOSPLIT, $32
MOVQ $123, CX
RET // want `RET without writing to result register`
// return jump
TEXT ·retjmp(SB), NOSPLIT, $0-8
RET retjmp1(SB) // It's okay to not write results if there's a tail call.