github.com/riscv/riscv-go@v0.0.0-20200123204226-124ebd6fcc8e/src/cmd/vet/asmdecl.go (about)

     1  // Copyright 2013 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Identify mismatches between assembly files and Go func declarations.
     6  
     7  package main
     8  
     9  import (
    10  	"bytes"
    11  	"fmt"
    12  	"go/ast"
    13  	"go/build"
    14  	"go/token"
    15  	"go/types"
    16  	"regexp"
    17  	"strconv"
    18  	"strings"
    19  )
    20  
    21  // 'kind' is a kind of assembly variable.
    22  // The kinds 1, 2, 4, 8 stand for values of that size.
    23  type asmKind int
    24  
    25  // These special kinds are not valid sizes.
    26  const (
    27  	asmString asmKind = 100 + iota
    28  	asmSlice
    29  	asmArray
    30  	asmInterface
    31  	asmEmptyInterface
    32  	asmStruct
    33  	asmComplex
    34  )
    35  
    36  // An asmArch describes assembly parameters for an architecture
    37  type asmArch struct {
    38  	name      string
    39  	sizes     *types.StdSizes
    40  	bigEndian bool
    41  	stack     string
    42  	lr        bool
    43  }
    44  
    45  // An asmFunc describes the expected variables for a function on a given architecture.
    46  type asmFunc struct {
    47  	arch        *asmArch
    48  	size        int // size of all arguments
    49  	vars        map[string]*asmVar
    50  	varByOffset map[int]*asmVar
    51  }
    52  
    53  // An asmVar describes a single assembly variable.
    54  type asmVar struct {
    55  	name  string
    56  	kind  asmKind
    57  	typ   string
    58  	off   int
    59  	size  int
    60  	inner []*asmVar
    61  }
    62  
    63  // Common architecture word sizes and alignments.
    64  var (
    65  	size44 = &types.StdSizes{WordSize: 4, MaxAlign: 4}
    66  	size48 = &types.StdSizes{WordSize: 4, MaxAlign: 8}
    67  	size88 = &types.StdSizes{WordSize: 8, MaxAlign: 8}
    68  )
    69  
    70  var (
    71  	asmArch386      = asmArch{"386", size44, false, "SP", false}
    72  	asmArchArm      = asmArch{"arm", size44, false, "R13", true}
    73  	asmArchArm64    = asmArch{"arm64", size88, false, "RSP", true}
    74  	asmArchAmd64    = asmArch{"amd64", size88, false, "SP", false}
    75  	asmArchAmd64p32 = asmArch{"amd64p32", size48, false, "SP", false}
    76  	asmArchMips     = asmArch{"mips", size44, true, "R29", true}
    77  	asmArchMipsLE   = asmArch{"mipsle", size44, false, "R29", true}
    78  	asmArchMips64   = asmArch{"mips64", size88, true, "R29", true}
    79  	asmArchMips64LE = asmArch{"mips64le", size88, false, "R29", true}
    80  	asmArchPpc64    = asmArch{"ppc64", size88, true, "R1", true}
    81  	asmArchPpc64LE  = asmArch{"ppc64le", size88, false, "R1", true}
    82  	asmArchS390X    = asmArch{"s390x", size88, true, "R15", true}
    83  	asmArchRISCV    = asmArch{"riscv", size88, false, "SP", true}
    84  
    85  	arches = []*asmArch{
    86  		&asmArch386,
    87  		&asmArchArm,
    88  		&asmArchArm64,
    89  		&asmArchAmd64,
    90  		&asmArchAmd64p32,
    91  		&asmArchMips,
    92  		&asmArchMipsLE,
    93  		&asmArchMips64,
    94  		&asmArchMips64LE,
    95  		&asmArchPpc64,
    96  		&asmArchPpc64LE,
    97  		&asmArchS390X,
    98  		&asmArchRISCV,
    99  	}
   100  )
   101  
   102  func (a *asmArch) intSize() int  { return int(a.sizes.WordSize) }
   103  func (a *asmArch) ptrSize() int  { return int(a.sizes.WordSize) }
   104  func (a *asmArch) maxAlign() int { return int(a.sizes.MaxAlign) }
   105  
   106  var (
   107  	re           = regexp.MustCompile
   108  	asmPlusBuild = re(`//\s+\+build\s+([^\n]+)`)
   109  	asmTEXT      = re(`\bTEXT\b(.*)·([^\(]+)\(SB\)(?:\s*,\s*([0-9A-Z|+]+))?(?:\s*,\s*\$(-?[0-9]+)(?:-([0-9]+))?)?`)
   110  	asmDATA      = re(`\b(DATA|GLOBL)\b`)
   111  	asmNamedFP   = re(`([a-zA-Z0-9_\xFF-\x{10FFFF}]+)(?:\+([0-9]+))\(FP\)`)
   112  	asmUnnamedFP = re(`[^+\-0-9](([0-9]+)\(FP\))`)
   113  	asmSP        = re(`[^+\-0-9](([0-9]+)\(([A-Z0-9]+)\))`)
   114  	asmOpcode    = re(`^\s*(?:[A-Z0-9a-z_]+:)?\s*([A-Z]+)\s*([^,]*)(?:,\s*(.*))?`)
   115  	ppc64Suff    = re(`([BHWD])(ZU|Z|U|BR)?$`)
   116  )
   117  
   118  func asmCheck(pkg *Package) {
   119  	if !vet("asmdecl") {
   120  		return
   121  	}
   122  
   123  	// No work if no assembly files.
   124  	if !pkg.hasFileWithSuffix(".s") {
   125  		return
   126  	}
   127  
   128  	// Gather declarations. knownFunc[name][arch] is func description.
   129  	knownFunc := make(map[string]map[string]*asmFunc)
   130  
   131  	for _, f := range pkg.files {
   132  		if f.file != nil {
   133  			for _, decl := range f.file.Decls {
   134  				if decl, ok := decl.(*ast.FuncDecl); ok && decl.Body == nil {
   135  					knownFunc[decl.Name.Name] = f.asmParseDecl(decl)
   136  				}
   137  			}
   138  		}
   139  	}
   140  
   141  Files:
   142  	for _, f := range pkg.files {
   143  		if !strings.HasSuffix(f.name, ".s") {
   144  			continue
   145  		}
   146  		Println("Checking file", f.name)
   147  
   148  		// Determine architecture from file name if possible.
   149  		var arch string
   150  		var archDef *asmArch
   151  		for _, a := range arches {
   152  			if strings.HasSuffix(f.name, "_"+a.name+".s") {
   153  				arch = a.name
   154  				archDef = a
   155  				break
   156  			}
   157  		}
   158  
   159  		lines := strings.SplitAfter(string(f.content), "\n")
   160  		var (
   161  			fn                 *asmFunc
   162  			fnName             string
   163  			localSize, argSize int
   164  			wroteSP            bool
   165  			haveRetArg         bool
   166  			retLine            []int
   167  		)
   168  
   169  		flushRet := func() {
   170  			if fn != nil && fn.vars["ret"] != nil && !haveRetArg && len(retLine) > 0 {
   171  				v := fn.vars["ret"]
   172  				for _, line := range retLine {
   173  					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)
   174  				}
   175  			}
   176  			retLine = nil
   177  		}
   178  		for lineno, line := range lines {
   179  			lineno++
   180  
   181  			badf := func(format string, args ...interface{}) {
   182  				f.Badf(token.NoPos, "%s:%d: [%s] %s: %s", f.name, lineno, arch, fnName, fmt.Sprintf(format, args...))
   183  			}
   184  
   185  			if arch == "" {
   186  				// Determine architecture from +build line if possible.
   187  				if m := asmPlusBuild.FindStringSubmatch(line); m != nil {
   188  					// There can be multiple architectures in a single +build line,
   189  					// so accumulate them all and then prefer the one that
   190  					// matches build.Default.GOARCH.
   191  					var archCandidates []*asmArch
   192  					for _, fld := range strings.Fields(m[1]) {
   193  						for _, a := range arches {
   194  							if a.name == fld {
   195  								archCandidates = append(archCandidates, a)
   196  							}
   197  						}
   198  					}
   199  					for _, a := range archCandidates {
   200  						if a.name == build.Default.GOARCH {
   201  							archCandidates = []*asmArch{a}
   202  							break
   203  						}
   204  					}
   205  					if len(archCandidates) > 0 {
   206  						arch = archCandidates[0].name
   207  						archDef = archCandidates[0]
   208  					}
   209  				}
   210  			}
   211  
   212  			if m := asmTEXT.FindStringSubmatch(line); m != nil {
   213  				flushRet()
   214  				if arch == "" {
   215  					// Arch not specified by filename or build tags.
   216  					// Fall back to build.Default.GOARCH.
   217  					for _, a := range arches {
   218  						if a.name == build.Default.GOARCH {
   219  							arch = a.name
   220  							archDef = a
   221  							break
   222  						}
   223  					}
   224  					if arch == "" {
   225  						f.Warnf(token.NoPos, "%s: cannot determine architecture for assembly file", f.name)
   226  						continue Files
   227  					}
   228  				}
   229  				fnName = m[2]
   230  				if pkgName := strings.TrimSpace(m[1]); pkgName != "" {
   231  					pathParts := strings.Split(pkgName, "∕")
   232  					pkgName = pathParts[len(pathParts)-1]
   233  					if pkgName != f.pkg.path {
   234  						f.Warnf(token.NoPos, "%s:%d: [%s] cannot check cross-package assembly function: %s is in package %s", f.name, lineno, arch, fnName, pkgName)
   235  						fn = nil
   236  						fnName = ""
   237  						continue
   238  					}
   239  				}
   240  				fn = knownFunc[fnName][arch]
   241  				if fn != nil {
   242  					size, _ := strconv.Atoi(m[5])
   243  					flag := m[3]
   244  					if size != fn.size && (flag != "7" && !strings.Contains(flag, "NOSPLIT") || size != 0) {
   245  						badf("wrong argument size %d; expected $...-%d", size, fn.size)
   246  					}
   247  				}
   248  				localSize, _ = strconv.Atoi(m[4])
   249  				localSize += archDef.intSize()
   250  				if archDef.lr {
   251  					// Account for caller's saved LR
   252  					localSize += archDef.intSize()
   253  				}
   254  				argSize, _ = strconv.Atoi(m[5])
   255  				if fn == nil && !strings.Contains(fnName, "<>") {
   256  					badf("function %s missing Go declaration", fnName)
   257  				}
   258  				wroteSP = false
   259  				haveRetArg = false
   260  				continue
   261  			} else if strings.Contains(line, "TEXT") && strings.Contains(line, "SB") {
   262  				// function, but not visible from Go (didn't match asmTEXT), so stop checking
   263  				flushRet()
   264  				fn = nil
   265  				fnName = ""
   266  				continue
   267  			}
   268  
   269  			if strings.Contains(line, "RET") {
   270  				retLine = append(retLine, lineno)
   271  			}
   272  
   273  			if fnName == "" {
   274  				continue
   275  			}
   276  
   277  			if asmDATA.FindStringSubmatch(line) != nil {
   278  				fn = nil
   279  			}
   280  
   281  			if archDef == nil {
   282  				continue
   283  			}
   284  
   285  			if strings.Contains(line, ", "+archDef.stack) || strings.Contains(line, ",\t"+archDef.stack) {
   286  				wroteSP = true
   287  				continue
   288  			}
   289  
   290  			for _, m := range asmSP.FindAllStringSubmatch(line, -1) {
   291  				if m[3] != archDef.stack || wroteSP {
   292  					continue
   293  				}
   294  				off := 0
   295  				if m[1] != "" {
   296  					off, _ = strconv.Atoi(m[2])
   297  				}
   298  				if off >= localSize {
   299  					if fn != nil {
   300  						v := fn.varByOffset[off-localSize]
   301  						if v != nil {
   302  							badf("%s should be %s+%d(FP)", m[1], v.name, off-localSize)
   303  							continue
   304  						}
   305  					}
   306  					if off >= localSize+argSize {
   307  						badf("use of %s points beyond argument frame", m[1])
   308  						continue
   309  					}
   310  					badf("use of %s to access argument frame", m[1])
   311  				}
   312  			}
   313  
   314  			if fn == nil {
   315  				continue
   316  			}
   317  
   318  			for _, m := range asmUnnamedFP.FindAllStringSubmatch(line, -1) {
   319  				off, _ := strconv.Atoi(m[2])
   320  				v := fn.varByOffset[off]
   321  				if v != nil {
   322  					badf("use of unnamed argument %s; offset %d is %s+%d(FP)", m[1], off, v.name, v.off)
   323  				} else {
   324  					badf("use of unnamed argument %s", m[1])
   325  				}
   326  			}
   327  
   328  			for _, m := range asmNamedFP.FindAllStringSubmatch(line, -1) {
   329  				name := m[1]
   330  				off := 0
   331  				if m[2] != "" {
   332  					off, _ = strconv.Atoi(m[2])
   333  				}
   334  				if name == "ret" || strings.HasPrefix(name, "ret_") {
   335  					haveRetArg = true
   336  				}
   337  				v := fn.vars[name]
   338  				if v == nil {
   339  					// Allow argframe+0(FP).
   340  					if name == "argframe" && off == 0 {
   341  						continue
   342  					}
   343  					v = fn.varByOffset[off]
   344  					if v != nil {
   345  						badf("unknown variable %s; offset %d is %s+%d(FP)", name, off, v.name, v.off)
   346  					} else {
   347  						badf("unknown variable %s", name)
   348  					}
   349  					continue
   350  				}
   351  				asmCheckVar(badf, fn, line, m[0], off, v)
   352  			}
   353  		}
   354  		flushRet()
   355  	}
   356  }
   357  
   358  func asmKindForType(t types.Type, size int) asmKind {
   359  	switch t := t.Underlying().(type) {
   360  	case *types.Basic:
   361  		switch t.Kind() {
   362  		case types.String:
   363  			return asmString
   364  		case types.Complex64, types.Complex128:
   365  			return asmComplex
   366  		}
   367  		return asmKind(size)
   368  	case *types.Pointer, *types.Chan, *types.Map, *types.Signature:
   369  		return asmKind(size)
   370  	case *types.Struct:
   371  		return asmStruct
   372  	case *types.Interface:
   373  		if t.Empty() {
   374  			return asmEmptyInterface
   375  		}
   376  		return asmInterface
   377  	case *types.Array:
   378  		return asmArray
   379  	case *types.Slice:
   380  		return asmSlice
   381  	}
   382  	panic("unreachable")
   383  }
   384  
   385  // A component is an assembly-addressable component of a composite type,
   386  // or a composite type itself.
   387  type component struct {
   388  	size   int
   389  	offset int
   390  	kind   asmKind
   391  	typ    string
   392  	suffix string // Such as _base for string base, _0_lo for lo half of first element of [1]uint64 on 32 bit machine.
   393  	outer  string // The suffix for immediately containing composite type.
   394  }
   395  
   396  func newComponent(suffix string, kind asmKind, typ string, offset, size int, outer string) component {
   397  	return component{suffix: suffix, kind: kind, typ: typ, offset: offset, size: size, outer: outer}
   398  }
   399  
   400  // componentsOfType generates a list of components of type t.
   401  // For example, given string, the components are the string itself, the base, and the length.
   402  func componentsOfType(arch *asmArch, t types.Type) []component {
   403  	return appendComponentsRecursive(arch, t, nil, "", 0)
   404  }
   405  
   406  // appendComponentsRecursive implements componentsOfType.
   407  // Recursion is required to correct handle structs and arrays,
   408  // which can contain arbitrary other types.
   409  func appendComponentsRecursive(arch *asmArch, t types.Type, cc []component, suffix string, off int) []component {
   410  	s := t.String()
   411  	size := int(arch.sizes.Sizeof(t))
   412  	kind := asmKindForType(t, size)
   413  	cc = append(cc, newComponent(suffix, kind, s, off, size, suffix))
   414  
   415  	switch kind {
   416  	case 8:
   417  		if arch.ptrSize() == 4 {
   418  			w1, w2 := "lo", "hi"
   419  			if arch.bigEndian {
   420  				w1, w2 = w2, w1
   421  			}
   422  			cc = append(cc, newComponent(suffix+"_"+w1, 4, "half "+s, off, 4, suffix))
   423  			cc = append(cc, newComponent(suffix+"_"+w2, 4, "half "+s, off+4, 4, suffix))
   424  		}
   425  
   426  	case asmEmptyInterface:
   427  		cc = append(cc, newComponent(suffix+"_type", asmKind(arch.ptrSize()), "interface type", off, arch.ptrSize(), suffix))
   428  		cc = append(cc, newComponent(suffix+"_data", asmKind(arch.ptrSize()), "interface data", off+arch.ptrSize(), arch.ptrSize(), suffix))
   429  
   430  	case asmInterface:
   431  		cc = append(cc, newComponent(suffix+"_itable", asmKind(arch.ptrSize()), "interface itable", off, arch.ptrSize(), suffix))
   432  		cc = append(cc, newComponent(suffix+"_data", asmKind(arch.ptrSize()), "interface data", off+arch.ptrSize(), arch.ptrSize(), suffix))
   433  
   434  	case asmSlice:
   435  		cc = append(cc, newComponent(suffix+"_base", asmKind(arch.ptrSize()), "slice base", off, arch.ptrSize(), suffix))
   436  		cc = append(cc, newComponent(suffix+"_len", asmKind(arch.intSize()), "slice len", off+arch.ptrSize(), arch.intSize(), suffix))
   437  		cc = append(cc, newComponent(suffix+"_cap", asmKind(arch.intSize()), "slice cap", off+arch.ptrSize()+arch.intSize(), arch.intSize(), suffix))
   438  
   439  	case asmString:
   440  		cc = append(cc, newComponent(suffix+"_base", asmKind(arch.ptrSize()), "string base", off, arch.ptrSize(), suffix))
   441  		cc = append(cc, newComponent(suffix+"_len", asmKind(arch.intSize()), "string len", off+arch.ptrSize(), arch.intSize(), suffix))
   442  
   443  	case asmComplex:
   444  		fsize := size / 2
   445  		cc = append(cc, newComponent(suffix+"_real", asmKind(fsize), fmt.Sprintf("real(complex%d)", size*8), off, fsize, suffix))
   446  		cc = append(cc, newComponent(suffix+"_imag", asmKind(fsize), fmt.Sprintf("imag(complex%d)", size*8), off+fsize, fsize, suffix))
   447  
   448  	case asmStruct:
   449  		tu := t.Underlying().(*types.Struct)
   450  		fields := make([]*types.Var, tu.NumFields())
   451  		for i := 0; i < tu.NumFields(); i++ {
   452  			fields[i] = tu.Field(i)
   453  		}
   454  		offsets := arch.sizes.Offsetsof(fields)
   455  		for i, f := range fields {
   456  			cc = appendComponentsRecursive(arch, f.Type(), cc, suffix+"_"+f.Name(), off+int(offsets[i]))
   457  		}
   458  
   459  	case asmArray:
   460  		tu := t.Underlying().(*types.Array)
   461  		elem := tu.Elem()
   462  		// Calculate offset of each element array.
   463  		fields := []*types.Var{
   464  			types.NewVar(token.NoPos, nil, "fake0", elem),
   465  			types.NewVar(token.NoPos, nil, "fake1", elem),
   466  		}
   467  		offsets := arch.sizes.Offsetsof(fields)
   468  		elemoff := int(offsets[1])
   469  		for i := 0; i < int(tu.Len()); i++ {
   470  			cc = appendComponentsRecursive(arch, elem, cc, suffix+"_"+strconv.Itoa(i), i*elemoff)
   471  		}
   472  	}
   473  
   474  	return cc
   475  }
   476  
   477  // asmParseDecl parses a function decl for expected assembly variables.
   478  func (f *File) asmParseDecl(decl *ast.FuncDecl) map[string]*asmFunc {
   479  	var (
   480  		arch   *asmArch
   481  		fn     *asmFunc
   482  		offset int
   483  	)
   484  
   485  	// addParams adds asmVars for each of the parameters in list.
   486  	// isret indicates whether the list are the arguments or the return values.
   487  	addParams := func(list []*ast.Field, isret bool) {
   488  		argnum := 0
   489  		for _, fld := range list {
   490  			t := f.pkg.types[fld.Type].Type
   491  			align := int(arch.sizes.Alignof(t))
   492  			size := int(arch.sizes.Sizeof(t))
   493  			offset += -offset & (align - 1)
   494  			cc := componentsOfType(arch, t)
   495  
   496  			// names is the list of names with this type.
   497  			names := fld.Names
   498  			if len(names) == 0 {
   499  				// Anonymous args will be called arg, arg1, arg2, ...
   500  				// Similarly so for return values: ret, ret1, ret2, ...
   501  				name := "arg"
   502  				if isret {
   503  					name = "ret"
   504  				}
   505  				if argnum > 0 {
   506  					name += strconv.Itoa(argnum)
   507  				}
   508  				names = []*ast.Ident{ast.NewIdent(name)}
   509  			}
   510  			argnum += len(names)
   511  
   512  			// Create variable for each name.
   513  			for _, id := range names {
   514  				name := id.Name
   515  				for _, c := range cc {
   516  					outer := name + c.outer
   517  					v := asmVar{
   518  						name: name + c.suffix,
   519  						kind: c.kind,
   520  						typ:  c.typ,
   521  						off:  offset + c.offset,
   522  						size: c.size,
   523  					}
   524  					if vo := fn.vars[outer]; vo != nil {
   525  						vo.inner = append(vo.inner, &v)
   526  					}
   527  					fn.vars[v.name] = &v
   528  					for i := 0; i < v.size; i++ {
   529  						fn.varByOffset[v.off+i] = &v
   530  					}
   531  				}
   532  				offset += size
   533  			}
   534  		}
   535  	}
   536  
   537  	m := make(map[string]*asmFunc)
   538  	for _, arch = range arches {
   539  		fn = &asmFunc{
   540  			arch:        arch,
   541  			vars:        make(map[string]*asmVar),
   542  			varByOffset: make(map[int]*asmVar),
   543  		}
   544  		offset = 0
   545  		addParams(decl.Type.Params.List, false)
   546  		if decl.Type.Results != nil && len(decl.Type.Results.List) > 0 {
   547  			offset += -offset & (arch.maxAlign() - 1)
   548  			addParams(decl.Type.Results.List, true)
   549  		}
   550  		fn.size = offset
   551  		m[arch.name] = fn
   552  	}
   553  
   554  	return m
   555  }
   556  
   557  // asmCheckVar checks a single variable reference.
   558  func asmCheckVar(badf func(string, ...interface{}), fn *asmFunc, line, expr string, off int, v *asmVar) {
   559  	m := asmOpcode.FindStringSubmatch(line)
   560  	if m == nil {
   561  		if !strings.HasPrefix(strings.TrimSpace(line), "//") {
   562  			badf("cannot find assembly opcode")
   563  		}
   564  		return
   565  	}
   566  
   567  	// Determine operand sizes from instruction.
   568  	// Typically the suffix suffices, but there are exceptions.
   569  	var src, dst, kind asmKind
   570  	op := m[1]
   571  	switch fn.arch.name + "." + op {
   572  	case "386.FMOVLP":
   573  		src, dst = 8, 4
   574  	case "arm.MOVD":
   575  		src = 8
   576  	case "arm.MOVW":
   577  		src = 4
   578  	case "arm.MOVH", "arm.MOVHU":
   579  		src = 2
   580  	case "arm.MOVB", "arm.MOVBU":
   581  		src = 1
   582  	// LEA* opcodes don't really read the second arg.
   583  	// They just take the address of it.
   584  	case "386.LEAL":
   585  		dst = 4
   586  	case "amd64.LEAQ":
   587  		dst = 8
   588  	case "amd64p32.LEAL":
   589  		dst = 4
   590  	default:
   591  		switch fn.arch.name {
   592  		case "386", "amd64":
   593  			if strings.HasPrefix(op, "F") && (strings.HasSuffix(op, "D") || strings.HasSuffix(op, "DP")) {
   594  				// FMOVDP, FXCHD, etc
   595  				src = 8
   596  				break
   597  			}
   598  			if strings.HasPrefix(op, "P") && strings.HasSuffix(op, "RD") {
   599  				// PINSRD, PEXTRD, etc
   600  				src = 4
   601  				break
   602  			}
   603  			if strings.HasPrefix(op, "F") && (strings.HasSuffix(op, "F") || strings.HasSuffix(op, "FP")) {
   604  				// FMOVFP, FXCHF, etc
   605  				src = 4
   606  				break
   607  			}
   608  			if strings.HasSuffix(op, "SD") {
   609  				// MOVSD, SQRTSD, etc
   610  				src = 8
   611  				break
   612  			}
   613  			if strings.HasSuffix(op, "SS") {
   614  				// MOVSS, SQRTSS, etc
   615  				src = 4
   616  				break
   617  			}
   618  			if strings.HasPrefix(op, "SET") {
   619  				// SETEQ, etc
   620  				src = 1
   621  				break
   622  			}
   623  			switch op[len(op)-1] {
   624  			case 'B':
   625  				src = 1
   626  			case 'W':
   627  				src = 2
   628  			case 'L':
   629  				src = 4
   630  			case 'D', 'Q':
   631  				src = 8
   632  			}
   633  		case "ppc64", "ppc64le":
   634  			// Strip standard suffixes to reveal size letter.
   635  			m := ppc64Suff.FindStringSubmatch(op)
   636  			if m != nil {
   637  				switch m[1][0] {
   638  				case 'B':
   639  					src = 1
   640  				case 'H':
   641  					src = 2
   642  				case 'W':
   643  					src = 4
   644  				case 'D':
   645  					src = 8
   646  				}
   647  			}
   648  		case "mips", "mipsle", "mips64", "mips64le":
   649  			switch op {
   650  			case "MOVB", "MOVBU":
   651  				src = 1
   652  			case "MOVH", "MOVHU":
   653  				src = 2
   654  			case "MOVW", "MOVWU", "MOVF":
   655  				src = 4
   656  			case "MOVV", "MOVD":
   657  				src = 8
   658  			}
   659  		case "s390x":
   660  			switch op {
   661  			case "MOVB", "MOVBZ":
   662  				src = 1
   663  			case "MOVH", "MOVHZ":
   664  				src = 2
   665  			case "MOVW", "MOVWZ", "FMOVS":
   666  				src = 4
   667  			case "MOVD", "FMOVD":
   668  				src = 8
   669  			}
   670  		}
   671  	}
   672  	if dst == 0 {
   673  		dst = src
   674  	}
   675  
   676  	// Determine whether the match we're holding
   677  	// is the first or second argument.
   678  	if strings.Index(line, expr) > strings.Index(line, ",") {
   679  		kind = dst
   680  	} else {
   681  		kind = src
   682  	}
   683  
   684  	vk := v.kind
   685  	vs := v.size
   686  	vt := v.typ
   687  	switch vk {
   688  	case asmInterface, asmEmptyInterface, asmString, asmSlice:
   689  		// allow reference to first word (pointer)
   690  		vk = v.inner[0].kind
   691  		vs = v.inner[0].size
   692  		vt = v.inner[0].typ
   693  	}
   694  
   695  	if off != v.off {
   696  		var inner bytes.Buffer
   697  		for i, vi := range v.inner {
   698  			if len(v.inner) > 1 {
   699  				fmt.Fprintf(&inner, ",")
   700  			}
   701  			fmt.Fprintf(&inner, " ")
   702  			if i == len(v.inner)-1 {
   703  				fmt.Fprintf(&inner, "or ")
   704  			}
   705  			fmt.Fprintf(&inner, "%s+%d(FP)", vi.name, vi.off)
   706  		}
   707  		badf("invalid offset %s; expected %s+%d(FP)%s", expr, v.name, v.off, inner.String())
   708  		return
   709  	}
   710  	if kind != 0 && kind != vk {
   711  		var inner bytes.Buffer
   712  		if len(v.inner) > 0 {
   713  			fmt.Fprintf(&inner, " containing")
   714  			for i, vi := range v.inner {
   715  				if i > 0 && len(v.inner) > 2 {
   716  					fmt.Fprintf(&inner, ",")
   717  				}
   718  				fmt.Fprintf(&inner, " ")
   719  				if i > 0 && i == len(v.inner)-1 {
   720  					fmt.Fprintf(&inner, "and ")
   721  				}
   722  				fmt.Fprintf(&inner, "%s+%d(FP)", vi.name, vi.off)
   723  			}
   724  		}
   725  		badf("invalid %s of %s; %s is %d-byte value%s", op, expr, vt, vs, inner.String())
   726  	}
   727  }