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