mirror of
https://github.com/autc04/Retro68.git
synced 2024-09-08 20:55:01 +00:00
731 lines
19 KiB
Go
731 lines
19 KiB
Go
// Copyright 2013 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.
|
||
|
||
// Identify mismatches between assembly files and Go func declarations.
|
||
|
||
package main
|
||
|
||
import (
|
||
"bytes"
|
||
"fmt"
|
||
"go/ast"
|
||
"go/build"
|
||
"go/token"
|
||
"go/types"
|
||
"regexp"
|
||
"strconv"
|
||
"strings"
|
||
)
|
||
|
||
// 'kind' is a kind of assembly variable.
|
||
// The kinds 1, 2, 4, 8 stand for values of that size.
|
||
type asmKind int
|
||
|
||
// These special kinds are not valid sizes.
|
||
const (
|
||
asmString asmKind = 100 + iota
|
||
asmSlice
|
||
asmArray
|
||
asmInterface
|
||
asmEmptyInterface
|
||
asmStruct
|
||
asmComplex
|
||
)
|
||
|
||
// An asmArch describes assembly parameters for an architecture
|
||
type asmArch struct {
|
||
name string
|
||
bigEndian bool
|
||
stack string
|
||
lr bool
|
||
// calculated during initialization
|
||
sizes types.Sizes
|
||
intSize int
|
||
ptrSize int
|
||
maxAlign int
|
||
}
|
||
|
||
// An asmFunc describes the expected variables for a function on a given architecture.
|
||
type asmFunc struct {
|
||
arch *asmArch
|
||
size int // size of all arguments
|
||
vars map[string]*asmVar
|
||
varByOffset map[int]*asmVar
|
||
}
|
||
|
||
// An asmVar describes a single assembly variable.
|
||
type asmVar struct {
|
||
name string
|
||
kind asmKind
|
||
typ string
|
||
off int
|
||
size int
|
||
inner []*asmVar
|
||
}
|
||
|
||
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}
|
||
asmArchAmd64p32 = asmArch{name: "amd64p32", bigEndian: false, stack: "SP", lr: false}
|
||
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}
|
||
asmArchMips64LE = asmArch{name: "mips64le", bigEndian: false, stack: "R29", lr: true}
|
||
asmArchPpc64 = asmArch{name: "ppc64", bigEndian: true, stack: "R1", lr: true}
|
||
asmArchPpc64LE = asmArch{name: "ppc64le", bigEndian: false, stack: "R1", lr: true}
|
||
asmArchS390X = asmArch{name: "s390x", bigEndian: true, stack: "R15", lr: true}
|
||
|
||
arches = []*asmArch{
|
||
&asmArch386,
|
||
&asmArchArm,
|
||
&asmArchArm64,
|
||
&asmArchAmd64,
|
||
&asmArchAmd64p32,
|
||
&asmArchMips,
|
||
&asmArchMipsLE,
|
||
&asmArchMips64,
|
||
&asmArchMips64LE,
|
||
&asmArchPpc64,
|
||
&asmArchPpc64LE,
|
||
&asmArchS390X,
|
||
}
|
||
)
|
||
|
||
func init() {
|
||
for _, arch := range arches {
|
||
arch.sizes = types.SizesFor("gc", arch.name)
|
||
if arch.sizes == nil {
|
||
panic("missing SizesFor for gc/" + arch.name)
|
||
}
|
||
arch.intSize = int(arch.sizes.Sizeof(types.Typ[types.Int]))
|
||
arch.ptrSize = int(arch.sizes.Sizeof(types.Typ[types.UnsafePointer]))
|
||
arch.maxAlign = int(arch.sizes.Alignof(types.Typ[types.Int64]))
|
||
}
|
||
}
|
||
|
||
var (
|
||
re = regexp.MustCompile
|
||
asmPlusBuild = re(`//\s+\+build\s+([^\n]+)`)
|
||
asmTEXT = re(`\bTEXT\b(.*)·([^\(]+)\(SB\)(?:\s*,\s*([0-9A-Z|+()]+))?(?:\s*,\s*\$(-?[0-9]+)(?:-([0-9]+))?)?`)
|
||
asmDATA = re(`\b(DATA|GLOBL)\b`)
|
||
asmNamedFP = re(`([a-zA-Z0-9_\xFF-\x{10FFFF}]+)(?:\+([0-9]+))\(FP\)`)
|
||
asmUnnamedFP = re(`[^+\-0-9](([0-9]+)\(FP\))`)
|
||
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)?$`)
|
||
)
|
||
|
||
func asmCheck(pkg *Package) {
|
||
if !vet("asmdecl") {
|
||
return
|
||
}
|
||
|
||
// No work if no assembly files.
|
||
if !pkg.hasFileWithSuffix(".s") {
|
||
return
|
||
}
|
||
|
||
// Gather declarations. knownFunc[name][arch] is func description.
|
||
knownFunc := make(map[string]map[string]*asmFunc)
|
||
|
||
for _, f := range pkg.files {
|
||
if f.file != nil {
|
||
for _, decl := range f.file.Decls {
|
||
if decl, ok := decl.(*ast.FuncDecl); ok && decl.Body == nil {
|
||
knownFunc[decl.Name.Name] = f.asmParseDecl(decl)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
Files:
|
||
for _, f := range pkg.files {
|
||
if !strings.HasSuffix(f.name, ".s") {
|
||
continue
|
||
}
|
||
Println("Checking file", f.name)
|
||
|
||
// Determine architecture from file name if possible.
|
||
var arch string
|
||
var archDef *asmArch
|
||
for _, a := range arches {
|
||
if strings.HasSuffix(f.name, "_"+a.name+".s") {
|
||
arch = a.name
|
||
archDef = a
|
||
break
|
||
}
|
||
}
|
||
|
||
lines := strings.SplitAfter(string(f.content), "\n")
|
||
var (
|
||
fn *asmFunc
|
||
fnName string
|
||
localSize, argSize int
|
||
wroteSP bool
|
||
haveRetArg bool
|
||
retLine []int
|
||
)
|
||
|
||
flushRet := func() {
|
||
if fn != nil && fn.vars["ret"] != nil && !haveRetArg && len(retLine) > 0 {
|
||
v := fn.vars["ret"]
|
||
for _, line := range retLine {
|
||
f.Badf(token.NoPos, "%s:%d: [%s] %s: RET without writing to %d-byte ret+%d(FP)", f.name, line, arch, fnName, v.size, v.off)
|
||
}
|
||
}
|
||
retLine = nil
|
||
}
|
||
for lineno, line := range lines {
|
||
lineno++
|
||
|
||
badf := func(format string, args ...interface{}) {
|
||
f.Badf(token.NoPos, "%s:%d: [%s] %s: %s", f.name, lineno, arch, fnName, fmt.Sprintf(format, args...))
|
||
}
|
||
|
||
if arch == "" {
|
||
// Determine architecture from +build line if possible.
|
||
if m := asmPlusBuild.FindStringSubmatch(line); m != nil {
|
||
// There can be multiple architectures in a single +build line,
|
||
// so accumulate them all and then prefer the one that
|
||
// matches build.Default.GOARCH.
|
||
var archCandidates []*asmArch
|
||
for _, fld := range strings.Fields(m[1]) {
|
||
for _, a := range arches {
|
||
if a.name == fld {
|
||
archCandidates = append(archCandidates, a)
|
||
}
|
||
}
|
||
}
|
||
for _, a := range archCandidates {
|
||
if a.name == build.Default.GOARCH {
|
||
archCandidates = []*asmArch{a}
|
||
break
|
||
}
|
||
}
|
||
if len(archCandidates) > 0 {
|
||
arch = archCandidates[0].name
|
||
archDef = archCandidates[0]
|
||
}
|
||
}
|
||
}
|
||
|
||
if m := asmTEXT.FindStringSubmatch(line); m != nil {
|
||
flushRet()
|
||
if arch == "" {
|
||
// Arch not specified by filename or build tags.
|
||
// Fall back to build.Default.GOARCH.
|
||
for _, a := range arches {
|
||
if a.name == build.Default.GOARCH {
|
||
arch = a.name
|
||
archDef = a
|
||
break
|
||
}
|
||
}
|
||
if arch == "" {
|
||
f.Warnf(token.NoPos, "%s: cannot determine architecture for assembly file", f.name)
|
||
continue Files
|
||
}
|
||
}
|
||
fnName = m[2]
|
||
if pkgName := strings.TrimSpace(m[1]); pkgName != "" {
|
||
pathParts := strings.Split(pkgName, "∕")
|
||
pkgName = pathParts[len(pathParts)-1]
|
||
if pkgName != f.pkg.path {
|
||
f.Warnf(token.NoPos, "%s:%d: [%s] cannot check cross-package assembly function: %s is in package %s", f.name, lineno, arch, fnName, pkgName)
|
||
fn = nil
|
||
fnName = ""
|
||
continue
|
||
}
|
||
}
|
||
fn = knownFunc[fnName][arch]
|
||
if fn != nil {
|
||
size, _ := strconv.Atoi(m[5])
|
||
flag := m[3]
|
||
if size != fn.size && (flag != "7" && !strings.Contains(flag, "NOSPLIT") || size != 0) {
|
||
badf("wrong argument size %d; expected $...-%d", size, fn.size)
|
||
}
|
||
}
|
||
localSize, _ = strconv.Atoi(m[4])
|
||
localSize += archDef.intSize
|
||
if archDef.lr {
|
||
// Account for caller's saved LR
|
||
localSize += archDef.intSize
|
||
}
|
||
argSize, _ = strconv.Atoi(m[5])
|
||
if fn == nil && !strings.Contains(fnName, "<>") {
|
||
badf("function %s missing Go declaration", fnName)
|
||
}
|
||
wroteSP = false
|
||
haveRetArg = false
|
||
continue
|
||
} else if strings.Contains(line, "TEXT") && strings.Contains(line, "SB") {
|
||
// function, but not visible from Go (didn't match asmTEXT), so stop checking
|
||
flushRet()
|
||
fn = nil
|
||
fnName = ""
|
||
continue
|
||
}
|
||
|
||
if strings.Contains(line, "RET") {
|
||
retLine = append(retLine, lineno)
|
||
}
|
||
|
||
if fnName == "" {
|
||
continue
|
||
}
|
||
|
||
if asmDATA.FindStringSubmatch(line) != nil {
|
||
fn = nil
|
||
}
|
||
|
||
if archDef == nil {
|
||
continue
|
||
}
|
||
|
||
if strings.Contains(line, ", "+archDef.stack) || strings.Contains(line, ",\t"+archDef.stack) {
|
||
wroteSP = true
|
||
continue
|
||
}
|
||
|
||
for _, m := range asmSP.FindAllStringSubmatch(line, -1) {
|
||
if m[3] != archDef.stack || wroteSP {
|
||
continue
|
||
}
|
||
off := 0
|
||
if m[1] != "" {
|
||
off, _ = strconv.Atoi(m[2])
|
||
}
|
||
if off >= localSize {
|
||
if fn != nil {
|
||
v := fn.varByOffset[off-localSize]
|
||
if v != nil {
|
||
badf("%s should be %s+%d(FP)", m[1], v.name, off-localSize)
|
||
continue
|
||
}
|
||
}
|
||
if off >= localSize+argSize {
|
||
badf("use of %s points beyond argument frame", m[1])
|
||
continue
|
||
}
|
||
badf("use of %s to access argument frame", m[1])
|
||
}
|
||
}
|
||
|
||
if fn == nil {
|
||
continue
|
||
}
|
||
|
||
for _, m := range asmUnnamedFP.FindAllStringSubmatch(line, -1) {
|
||
off, _ := strconv.Atoi(m[2])
|
||
v := fn.varByOffset[off]
|
||
if v != nil {
|
||
badf("use of unnamed argument %s; offset %d is %s+%d(FP)", m[1], off, v.name, v.off)
|
||
} else {
|
||
badf("use of unnamed argument %s", m[1])
|
||
}
|
||
}
|
||
|
||
for _, m := range asmNamedFP.FindAllStringSubmatch(line, -1) {
|
||
name := m[1]
|
||
off := 0
|
||
if m[2] != "" {
|
||
off, _ = strconv.Atoi(m[2])
|
||
}
|
||
if name == "ret" || strings.HasPrefix(name, "ret_") {
|
||
haveRetArg = true
|
||
}
|
||
v := fn.vars[name]
|
||
if v == nil {
|
||
// Allow argframe+0(FP).
|
||
if name == "argframe" && off == 0 {
|
||
continue
|
||
}
|
||
v = fn.varByOffset[off]
|
||
if v != nil {
|
||
badf("unknown variable %s; offset %d is %s+%d(FP)", name, off, v.name, v.off)
|
||
} else {
|
||
badf("unknown variable %s", name)
|
||
}
|
||
continue
|
||
}
|
||
asmCheckVar(badf, fn, line, m[0], off, v)
|
||
}
|
||
}
|
||
flushRet()
|
||
}
|
||
}
|
||
|
||
func asmKindForType(t types.Type, size int) asmKind {
|
||
switch t := t.Underlying().(type) {
|
||
case *types.Basic:
|
||
switch t.Kind() {
|
||
case types.String:
|
||
return asmString
|
||
case types.Complex64, types.Complex128:
|
||
return asmComplex
|
||
}
|
||
return asmKind(size)
|
||
case *types.Pointer, *types.Chan, *types.Map, *types.Signature:
|
||
return asmKind(size)
|
||
case *types.Struct:
|
||
return asmStruct
|
||
case *types.Interface:
|
||
if t.Empty() {
|
||
return asmEmptyInterface
|
||
}
|
||
return asmInterface
|
||
case *types.Array:
|
||
return asmArray
|
||
case *types.Slice:
|
||
return asmSlice
|
||
}
|
||
panic("unreachable")
|
||
}
|
||
|
||
// A component is an assembly-addressable component of a composite type,
|
||
// or a composite type itself.
|
||
type component struct {
|
||
size int
|
||
offset int
|
||
kind asmKind
|
||
typ string
|
||
suffix string // Such as _base for string base, _0_lo for lo half of first element of [1]uint64 on 32 bit machine.
|
||
outer string // The suffix for immediately containing composite type.
|
||
}
|
||
|
||
func newComponent(suffix string, kind asmKind, typ string, offset, size int, outer string) component {
|
||
return component{suffix: suffix, kind: kind, typ: typ, offset: offset, size: size, outer: outer}
|
||
}
|
||
|
||
// componentsOfType generates a list of components of type t.
|
||
// For example, given string, the components are the string itself, the base, and the length.
|
||
func componentsOfType(arch *asmArch, t types.Type) []component {
|
||
return appendComponentsRecursive(arch, t, nil, "", 0)
|
||
}
|
||
|
||
// appendComponentsRecursive implements componentsOfType.
|
||
// Recursion is required to correct handle structs and arrays,
|
||
// which can contain arbitrary other types.
|
||
func appendComponentsRecursive(arch *asmArch, t types.Type, cc []component, suffix string, off int) []component {
|
||
s := t.String()
|
||
size := int(arch.sizes.Sizeof(t))
|
||
kind := asmKindForType(t, size)
|
||
cc = append(cc, newComponent(suffix, kind, s, off, size, suffix))
|
||
|
||
switch kind {
|
||
case 8:
|
||
if arch.ptrSize == 4 {
|
||
w1, w2 := "lo", "hi"
|
||
if arch.bigEndian {
|
||
w1, w2 = w2, w1
|
||
}
|
||
cc = append(cc, newComponent(suffix+"_"+w1, 4, "half "+s, off, 4, suffix))
|
||
cc = append(cc, newComponent(suffix+"_"+w2, 4, "half "+s, off+4, 4, suffix))
|
||
}
|
||
|
||
case asmEmptyInterface:
|
||
cc = append(cc, newComponent(suffix+"_type", asmKind(arch.ptrSize), "interface type", off, arch.ptrSize, suffix))
|
||
cc = append(cc, newComponent(suffix+"_data", asmKind(arch.ptrSize), "interface data", off+arch.ptrSize, arch.ptrSize, suffix))
|
||
|
||
case asmInterface:
|
||
cc = append(cc, newComponent(suffix+"_itable", asmKind(arch.ptrSize), "interface itable", off, arch.ptrSize, suffix))
|
||
cc = append(cc, newComponent(suffix+"_data", asmKind(arch.ptrSize), "interface data", off+arch.ptrSize, arch.ptrSize, suffix))
|
||
|
||
case asmSlice:
|
||
cc = append(cc, newComponent(suffix+"_base", asmKind(arch.ptrSize), "slice base", off, arch.ptrSize, suffix))
|
||
cc = append(cc, newComponent(suffix+"_len", asmKind(arch.intSize), "slice len", off+arch.ptrSize, arch.intSize, suffix))
|
||
cc = append(cc, newComponent(suffix+"_cap", asmKind(arch.intSize), "slice cap", off+arch.ptrSize+arch.intSize, arch.intSize, suffix))
|
||
|
||
case asmString:
|
||
cc = append(cc, newComponent(suffix+"_base", asmKind(arch.ptrSize), "string base", off, arch.ptrSize, suffix))
|
||
cc = append(cc, newComponent(suffix+"_len", asmKind(arch.intSize), "string len", off+arch.ptrSize, arch.intSize, suffix))
|
||
|
||
case asmComplex:
|
||
fsize := size / 2
|
||
cc = append(cc, newComponent(suffix+"_real", asmKind(fsize), fmt.Sprintf("real(complex%d)", size*8), off, fsize, suffix))
|
||
cc = append(cc, newComponent(suffix+"_imag", asmKind(fsize), fmt.Sprintf("imag(complex%d)", size*8), off+fsize, fsize, suffix))
|
||
|
||
case asmStruct:
|
||
tu := t.Underlying().(*types.Struct)
|
||
fields := make([]*types.Var, tu.NumFields())
|
||
for i := 0; i < tu.NumFields(); i++ {
|
||
fields[i] = tu.Field(i)
|
||
}
|
||
offsets := arch.sizes.Offsetsof(fields)
|
||
for i, f := range fields {
|
||
cc = appendComponentsRecursive(arch, f.Type(), cc, suffix+"_"+f.Name(), off+int(offsets[i]))
|
||
}
|
||
|
||
case asmArray:
|
||
tu := t.Underlying().(*types.Array)
|
||
elem := tu.Elem()
|
||
// Calculate offset of each element array.
|
||
fields := []*types.Var{
|
||
types.NewVar(token.NoPos, nil, "fake0", elem),
|
||
types.NewVar(token.NoPos, nil, "fake1", elem),
|
||
}
|
||
offsets := arch.sizes.Offsetsof(fields)
|
||
elemoff := int(offsets[1])
|
||
for i := 0; i < int(tu.Len()); i++ {
|
||
cc = appendComponentsRecursive(arch, elem, cc, suffix+"_"+strconv.Itoa(i), i*elemoff)
|
||
}
|
||
}
|
||
|
||
return cc
|
||
}
|
||
|
||
// asmParseDecl parses a function decl for expected assembly variables.
|
||
func (f *File) asmParseDecl(decl *ast.FuncDecl) map[string]*asmFunc {
|
||
var (
|
||
arch *asmArch
|
||
fn *asmFunc
|
||
offset int
|
||
)
|
||
|
||
// addParams adds asmVars for each of the parameters in list.
|
||
// isret indicates whether the list are the arguments or the return values.
|
||
addParams := func(list []*ast.Field, isret bool) {
|
||
argnum := 0
|
||
for _, fld := range list {
|
||
t := f.pkg.types[fld.Type].Type
|
||
align := int(arch.sizes.Alignof(t))
|
||
size := int(arch.sizes.Sizeof(t))
|
||
offset += -offset & (align - 1)
|
||
cc := componentsOfType(arch, t)
|
||
|
||
// names is the list of names with this type.
|
||
names := fld.Names
|
||
if len(names) == 0 {
|
||
// Anonymous args will be called arg, arg1, arg2, ...
|
||
// Similarly so for return values: ret, ret1, ret2, ...
|
||
name := "arg"
|
||
if isret {
|
||
name = "ret"
|
||
}
|
||
if argnum > 0 {
|
||
name += strconv.Itoa(argnum)
|
||
}
|
||
names = []*ast.Ident{ast.NewIdent(name)}
|
||
}
|
||
argnum += len(names)
|
||
|
||
// Create variable for each name.
|
||
for _, id := range names {
|
||
name := id.Name
|
||
for _, c := range cc {
|
||
outer := name + c.outer
|
||
v := asmVar{
|
||
name: name + c.suffix,
|
||
kind: c.kind,
|
||
typ: c.typ,
|
||
off: offset + c.offset,
|
||
size: c.size,
|
||
}
|
||
if vo := fn.vars[outer]; vo != nil {
|
||
vo.inner = append(vo.inner, &v)
|
||
}
|
||
fn.vars[v.name] = &v
|
||
for i := 0; i < v.size; i++ {
|
||
fn.varByOffset[v.off+i] = &v
|
||
}
|
||
}
|
||
offset += size
|
||
}
|
||
}
|
||
}
|
||
|
||
m := make(map[string]*asmFunc)
|
||
for _, arch = range arches {
|
||
fn = &asmFunc{
|
||
arch: arch,
|
||
vars: make(map[string]*asmVar),
|
||
varByOffset: make(map[int]*asmVar),
|
||
}
|
||
offset = 0
|
||
addParams(decl.Type.Params.List, false)
|
||
if decl.Type.Results != nil && len(decl.Type.Results.List) > 0 {
|
||
offset += -offset & (arch.maxAlign - 1)
|
||
addParams(decl.Type.Results.List, true)
|
||
}
|
||
fn.size = offset
|
||
m[arch.name] = fn
|
||
}
|
||
|
||
return m
|
||
}
|
||
|
||
// asmCheckVar checks a single variable reference.
|
||
func asmCheckVar(badf func(string, ...interface{}), fn *asmFunc, line, expr string, off int, v *asmVar) {
|
||
m := asmOpcode.FindStringSubmatch(line)
|
||
if m == nil {
|
||
if !strings.HasPrefix(strings.TrimSpace(line), "//") {
|
||
badf("cannot find assembly opcode")
|
||
}
|
||
return
|
||
}
|
||
|
||
// Determine operand sizes from instruction.
|
||
// Typically the suffix suffices, but there are exceptions.
|
||
var src, dst, kind asmKind
|
||
op := m[1]
|
||
switch fn.arch.name + "." + op {
|
||
case "386.FMOVLP":
|
||
src, dst = 8, 4
|
||
case "arm.MOVD":
|
||
src = 8
|
||
case "arm.MOVW":
|
||
src = 4
|
||
case "arm.MOVH", "arm.MOVHU":
|
||
src = 2
|
||
case "arm.MOVB", "arm.MOVBU":
|
||
src = 1
|
||
// LEA* opcodes don't really read the second arg.
|
||
// They just take the address of it.
|
||
case "386.LEAL":
|
||
dst = 4
|
||
case "amd64.LEAQ":
|
||
dst = 8
|
||
case "amd64p32.LEAL":
|
||
dst = 4
|
||
default:
|
||
switch fn.arch.name {
|
||
case "386", "amd64":
|
||
if strings.HasPrefix(op, "F") && (strings.HasSuffix(op, "D") || strings.HasSuffix(op, "DP")) {
|
||
// FMOVDP, FXCHD, etc
|
||
src = 8
|
||
break
|
||
}
|
||
if strings.HasPrefix(op, "P") && strings.HasSuffix(op, "RD") {
|
||
// PINSRD, PEXTRD, etc
|
||
src = 4
|
||
break
|
||
}
|
||
if strings.HasPrefix(op, "F") && (strings.HasSuffix(op, "F") || strings.HasSuffix(op, "FP")) {
|
||
// FMOVFP, FXCHF, etc
|
||
src = 4
|
||
break
|
||
}
|
||
if strings.HasSuffix(op, "SD") {
|
||
// MOVSD, SQRTSD, etc
|
||
src = 8
|
||
break
|
||
}
|
||
if strings.HasSuffix(op, "SS") {
|
||
// MOVSS, SQRTSS, etc
|
||
src = 4
|
||
break
|
||
}
|
||
if strings.HasPrefix(op, "SET") {
|
||
// SETEQ, etc
|
||
src = 1
|
||
break
|
||
}
|
||
switch op[len(op)-1] {
|
||
case 'B':
|
||
src = 1
|
||
case 'W':
|
||
src = 2
|
||
case 'L':
|
||
src = 4
|
||
case 'D', 'Q':
|
||
src = 8
|
||
}
|
||
case "ppc64", "ppc64le":
|
||
// Strip standard suffixes to reveal size letter.
|
||
m := ppc64Suff.FindStringSubmatch(op)
|
||
if m != nil {
|
||
switch m[1][0] {
|
||
case 'B':
|
||
src = 1
|
||
case 'H':
|
||
src = 2
|
||
case 'W':
|
||
src = 4
|
||
case 'D':
|
||
src = 8
|
||
}
|
||
}
|
||
case "mips", "mipsle", "mips64", "mips64le":
|
||
switch op {
|
||
case "MOVB", "MOVBU":
|
||
src = 1
|
||
case "MOVH", "MOVHU":
|
||
src = 2
|
||
case "MOVW", "MOVWU", "MOVF":
|
||
src = 4
|
||
case "MOVV", "MOVD":
|
||
src = 8
|
||
}
|
||
case "s390x":
|
||
switch op {
|
||
case "MOVB", "MOVBZ":
|
||
src = 1
|
||
case "MOVH", "MOVHZ":
|
||
src = 2
|
||
case "MOVW", "MOVWZ", "FMOVS":
|
||
src = 4
|
||
case "MOVD", "FMOVD":
|
||
src = 8
|
||
}
|
||
}
|
||
}
|
||
if dst == 0 {
|
||
dst = src
|
||
}
|
||
|
||
// Determine whether the match we're holding
|
||
// is the first or second argument.
|
||
if strings.Index(line, expr) > strings.Index(line, ",") {
|
||
kind = dst
|
||
} else {
|
||
kind = src
|
||
}
|
||
|
||
vk := v.kind
|
||
vs := v.size
|
||
vt := v.typ
|
||
switch vk {
|
||
case asmInterface, asmEmptyInterface, asmString, asmSlice:
|
||
// allow reference to first word (pointer)
|
||
vk = v.inner[0].kind
|
||
vs = v.inner[0].size
|
||
vt = v.inner[0].typ
|
||
}
|
||
|
||
if off != v.off {
|
||
var inner bytes.Buffer
|
||
for i, vi := range v.inner {
|
||
if len(v.inner) > 1 {
|
||
fmt.Fprintf(&inner, ",")
|
||
}
|
||
fmt.Fprintf(&inner, " ")
|
||
if i == len(v.inner)-1 {
|
||
fmt.Fprintf(&inner, "or ")
|
||
}
|
||
fmt.Fprintf(&inner, "%s+%d(FP)", vi.name, vi.off)
|
||
}
|
||
badf("invalid offset %s; expected %s+%d(FP)%s", expr, v.name, v.off, inner.String())
|
||
return
|
||
}
|
||
if kind != 0 && kind != vk {
|
||
var inner bytes.Buffer
|
||
if len(v.inner) > 0 {
|
||
fmt.Fprintf(&inner, " containing")
|
||
for i, vi := range v.inner {
|
||
if i > 0 && len(v.inner) > 2 {
|
||
fmt.Fprintf(&inner, ",")
|
||
}
|
||
fmt.Fprintf(&inner, " ")
|
||
if i > 0 && i == len(v.inner)-1 {
|
||
fmt.Fprintf(&inner, "and ")
|
||
}
|
||
fmt.Fprintf(&inner, "%s+%d(FP)", vi.name, vi.off)
|
||
}
|
||
}
|
||
badf("invalid %s of %s; %s is %d-byte value%s", op, expr, vt, vs, inner.String())
|
||
}
|
||
}
|