...
Run Format

Source file src/cmd/vet/asmdecl.go

Documentation: cmd/vet

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

View as plain text