...

Source file src/cmd/vendor/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go

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

View as plain text