Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / 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") && !strings.Contains(line, "(SB)") {
312                                 // RET f(SB) is a tail call. It is okay to not write the results.
313                                 retLine = append(retLine, lineno)
314                         }
315
316                         if fnName == "" {
317                                 continue
318                         }
319
320                         if asmDATA.FindStringSubmatch(line) != nil {
321                                 fn = nil
322                         }
323
324                         if archDef == nil {
325                                 continue
326                         }
327
328                         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) {
329                                 wroteSP = true
330                                 continue
331                         }
332
333                         if arch == "wasm" && strings.Contains(line, "CallImport") {
334                                 // CallImport is a call out to magic that can write the result.
335                                 haveRetArg = true
336                         }
337
338                         for _, m := range asmSP.FindAllStringSubmatch(line, -1) {
339                                 if m[3] != archDef.stack || wroteSP || noframe {
340                                         continue
341                                 }
342                                 off := 0
343                                 if m[1] != "" {
344                                         off, _ = strconv.Atoi(m[2])
345                                 }
346                                 if off >= localSize {
347                                         if fn != nil {
348                                                 v := fn.varByOffset[off-localSize]
349                                                 if v != nil {
350                                                         badf("%s should be %s+%d(FP)", m[1], v.name, off-localSize)
351                                                         continue
352                                                 }
353                                         }
354                                         if off >= localSize+argSize {
355                                                 badf("use of %s points beyond argument frame", m[1])
356                                                 continue
357                                         }
358                                         badf("use of %s to access argument frame", m[1])
359                                 }
360                         }
361
362                         if fn == nil {
363                                 continue
364                         }
365
366                         for _, m := range asmUnnamedFP.FindAllStringSubmatch(line, -1) {
367                                 off, _ := strconv.Atoi(m[2])
368                                 v := fn.varByOffset[off]
369                                 if v != nil {
370                                         badf("use of unnamed argument %s; offset %d is %s+%d(FP)", m[1], off, v.name, v.off)
371                                 } else {
372                                         badf("use of unnamed argument %s", m[1])
373                                 }
374                         }
375
376                         for _, m := range asmNamedFP.FindAllStringSubmatch(line, -1) {
377                                 name := m[1]
378                                 off := 0
379                                 if m[2] != "" {
380                                         off, _ = strconv.Atoi(m[2])
381                                 }
382                                 if name == "ret" || strings.HasPrefix(name, "ret_") {
383                                         haveRetArg = true
384                                 }
385                                 v := fn.vars[name]
386                                 if v == nil {
387                                         // Allow argframe+0(FP).
388                                         if name == "argframe" && off == 0 {
389                                                 continue
390                                         }
391                                         v = fn.varByOffset[off]
392                                         if v != nil {
393                                                 badf("unknown variable %s; offset %d is %s+%d(FP)", name, off, v.name, v.off)
394                                         } else {
395                                                 badf("unknown variable %s", name)
396                                         }
397                                         continue
398                                 }
399                                 asmCheckVar(badf, fn, line, m[0], off, v, archDef)
400                         }
401                 }
402                 flushRet()
403         }
404         return nil, nil
405 }
406
407 func asmKindForType(t types.Type, size int) asmKind {
408         switch t := t.Underlying().(type) {
409         case *types.Basic:
410                 switch t.Kind() {
411                 case types.String:
412                         return asmString
413                 case types.Complex64, types.Complex128:
414                         return asmComplex
415                 }
416                 return asmKind(size)
417         case *types.Pointer, *types.Chan, *types.Map, *types.Signature:
418                 return asmKind(size)
419         case *types.Struct:
420                 return asmStruct
421         case *types.Interface:
422                 if t.Empty() {
423                         return asmEmptyInterface
424                 }
425                 return asmInterface
426         case *types.Array:
427                 return asmArray
428         case *types.Slice:
429                 return asmSlice
430         }
431         panic("unreachable")
432 }
433
434 // A component is an assembly-addressable component of a composite type,
435 // or a composite type itself.
436 type component struct {
437         size   int
438         offset int
439         kind   asmKind
440         typ    string
441         suffix string // Such as _base for string base, _0_lo for lo half of first element of [1]uint64 on 32 bit machine.
442         outer  string // The suffix for immediately containing composite type.
443 }
444
445 func newComponent(suffix string, kind asmKind, typ string, offset, size int, outer string) component {
446         return component{suffix: suffix, kind: kind, typ: typ, offset: offset, size: size, outer: outer}
447 }
448
449 // componentsOfType generates a list of components of type t.
450 // For example, given string, the components are the string itself, the base, and the length.
451 func componentsOfType(arch *asmArch, t types.Type) []component {
452         return appendComponentsRecursive(arch, t, nil, "", 0)
453 }
454
455 // appendComponentsRecursive implements componentsOfType.
456 // Recursion is required to correct handle structs and arrays,
457 // which can contain arbitrary other types.
458 func appendComponentsRecursive(arch *asmArch, t types.Type, cc []component, suffix string, off int) []component {
459         s := t.String()
460         size := int(arch.sizes.Sizeof(t))
461         kind := asmKindForType(t, size)
462         cc = append(cc, newComponent(suffix, kind, s, off, size, suffix))
463
464         switch kind {
465         case 8:
466                 if arch.ptrSize == 4 {
467                         w1, w2 := "lo", "hi"
468                         if arch.bigEndian {
469                                 w1, w2 = w2, w1
470                         }
471                         cc = append(cc, newComponent(suffix+"_"+w1, 4, "half "+s, off, 4, suffix))
472                         cc = append(cc, newComponent(suffix+"_"+w2, 4, "half "+s, off+4, 4, suffix))
473                 }
474
475         case asmEmptyInterface:
476                 cc = append(cc, newComponent(suffix+"_type", asmKind(arch.ptrSize), "interface type", off, arch.ptrSize, suffix))
477                 cc = append(cc, newComponent(suffix+"_data", asmKind(arch.ptrSize), "interface data", off+arch.ptrSize, arch.ptrSize, suffix))
478
479         case asmInterface:
480                 cc = append(cc, newComponent(suffix+"_itable", asmKind(arch.ptrSize), "interface itable", off, arch.ptrSize, suffix))
481                 cc = append(cc, newComponent(suffix+"_data", asmKind(arch.ptrSize), "interface data", off+arch.ptrSize, arch.ptrSize, suffix))
482
483         case asmSlice:
484                 cc = append(cc, newComponent(suffix+"_base", asmKind(arch.ptrSize), "slice base", off, arch.ptrSize, suffix))
485                 cc = append(cc, newComponent(suffix+"_len", asmKind(arch.intSize), "slice len", off+arch.ptrSize, arch.intSize, suffix))
486                 cc = append(cc, newComponent(suffix+"_cap", asmKind(arch.intSize), "slice cap", off+arch.ptrSize+arch.intSize, arch.intSize, suffix))
487
488         case asmString:
489                 cc = append(cc, newComponent(suffix+"_base", asmKind(arch.ptrSize), "string base", off, arch.ptrSize, suffix))
490                 cc = append(cc, newComponent(suffix+"_len", asmKind(arch.intSize), "string len", off+arch.ptrSize, arch.intSize, suffix))
491
492         case asmComplex:
493                 fsize := size / 2
494                 cc = append(cc, newComponent(suffix+"_real", asmKind(fsize), fmt.Sprintf("real(complex%d)", size*8), off, fsize, suffix))
495                 cc = append(cc, newComponent(suffix+"_imag", asmKind(fsize), fmt.Sprintf("imag(complex%d)", size*8), off+fsize, fsize, suffix))
496
497         case asmStruct:
498                 tu := t.Underlying().(*types.Struct)
499                 fields := make([]*types.Var, tu.NumFields())
500                 for i := 0; i < tu.NumFields(); i++ {
501                         fields[i] = tu.Field(i)
502                 }
503                 offsets := arch.sizes.Offsetsof(fields)
504                 for i, f := range fields {
505                         cc = appendComponentsRecursive(arch, f.Type(), cc, suffix+"_"+f.Name(), off+int(offsets[i]))
506                 }
507
508         case asmArray:
509                 tu := t.Underlying().(*types.Array)
510                 elem := tu.Elem()
511                 // Calculate offset of each element array.
512                 fields := []*types.Var{
513                         types.NewVar(token.NoPos, nil, "fake0", elem),
514                         types.NewVar(token.NoPos, nil, "fake1", elem),
515                 }
516                 offsets := arch.sizes.Offsetsof(fields)
517                 elemoff := int(offsets[1])
518                 for i := 0; i < int(tu.Len()); i++ {
519                         cc = appendComponentsRecursive(arch, elem, cc, suffix+"_"+strconv.Itoa(i), off+i*elemoff)
520                 }
521         }
522
523         return cc
524 }
525
526 // asmParseDecl parses a function decl for expected assembly variables.
527 func asmParseDecl(pass *analysis.Pass, decl *ast.FuncDecl) map[string]*asmFunc {
528         var (
529                 arch   *asmArch
530                 fn     *asmFunc
531                 offset int
532         )
533
534         // addParams adds asmVars for each of the parameters in list.
535         // isret indicates whether the list are the arguments or the return values.
536         // TODO(adonovan): simplify by passing (*types.Signature).{Params,Results}
537         // instead of list.
538         addParams := func(list []*ast.Field, isret bool) {
539                 argnum := 0
540                 for _, fld := range list {
541                         t := pass.TypesInfo.Types[fld.Type].Type
542
543                         // Work around https://golang.org/issue/28277.
544                         if t == nil {
545                                 if ell, ok := fld.Type.(*ast.Ellipsis); ok {
546                                         t = types.NewSlice(pass.TypesInfo.Types[ell.Elt].Type)
547                                 }
548                         }
549
550                         align := int(arch.sizes.Alignof(t))
551                         size := int(arch.sizes.Sizeof(t))
552                         offset += -offset & (align - 1)
553                         cc := componentsOfType(arch, t)
554
555                         // names is the list of names with this type.
556                         names := fld.Names
557                         if len(names) == 0 {
558                                 // Anonymous args will be called arg, arg1, arg2, ...
559                                 // Similarly so for return values: ret, ret1, ret2, ...
560                                 name := "arg"
561                                 if isret {
562                                         name = "ret"
563                                 }
564                                 if argnum > 0 {
565                                         name += strconv.Itoa(argnum)
566                                 }
567                                 names = []*ast.Ident{ast.NewIdent(name)}
568                         }
569                         argnum += len(names)
570
571                         // Create variable for each name.
572                         for _, id := range names {
573                                 name := id.Name
574                                 for _, c := range cc {
575                                         outer := name + c.outer
576                                         v := asmVar{
577                                                 name: name + c.suffix,
578                                                 kind: c.kind,
579                                                 typ:  c.typ,
580                                                 off:  offset + c.offset,
581                                                 size: c.size,
582                                         }
583                                         if vo := fn.vars[outer]; vo != nil {
584                                                 vo.inner = append(vo.inner, &v)
585                                         }
586                                         fn.vars[v.name] = &v
587                                         for i := 0; i < v.size; i++ {
588                                                 fn.varByOffset[v.off+i] = &v
589                                         }
590                                 }
591                                 offset += size
592                         }
593                 }
594         }
595
596         m := make(map[string]*asmFunc)
597         for _, arch = range arches {
598                 fn = &asmFunc{
599                         arch:        arch,
600                         vars:        make(map[string]*asmVar),
601                         varByOffset: make(map[int]*asmVar),
602                 }
603                 offset = 0
604                 addParams(decl.Type.Params.List, false)
605                 if decl.Type.Results != nil && len(decl.Type.Results.List) > 0 {
606                         offset += -offset & (arch.maxAlign - 1)
607                         addParams(decl.Type.Results.List, true)
608                 }
609                 fn.size = offset
610                 m[arch.name] = fn
611         }
612
613         return m
614 }
615
616 // asmCheckVar checks a single variable reference.
617 func asmCheckVar(badf func(string, ...interface{}), fn *asmFunc, line, expr string, off int, v *asmVar, archDef *asmArch) {
618         m := asmOpcode.FindStringSubmatch(line)
619         if m == nil {
620                 if !strings.HasPrefix(strings.TrimSpace(line), "//") {
621                         badf("cannot find assembly opcode")
622                 }
623                 return
624         }
625
626         addr := strings.HasPrefix(expr, "$")
627
628         // Determine operand sizes from instruction.
629         // Typically the suffix suffices, but there are exceptions.
630         var src, dst, kind asmKind
631         op := m[1]
632         switch fn.arch.name + "." + op {
633         case "386.FMOVLP":
634                 src, dst = 8, 4
635         case "arm.MOVD":
636                 src = 8
637         case "arm.MOVW":
638                 src = 4
639         case "arm.MOVH", "arm.MOVHU":
640                 src = 2
641         case "arm.MOVB", "arm.MOVBU":
642                 src = 1
643         // LEA* opcodes don't really read the second arg.
644         // They just take the address of it.
645         case "386.LEAL":
646                 dst = 4
647                 addr = true
648         case "amd64.LEAQ":
649                 dst = 8
650                 addr = true
651         default:
652                 switch fn.arch.name {
653                 case "386", "amd64":
654                         if strings.HasPrefix(op, "F") && (strings.HasSuffix(op, "D") || strings.HasSuffix(op, "DP")) {
655                                 // FMOVDP, FXCHD, etc
656                                 src = 8
657                                 break
658                         }
659                         if strings.HasPrefix(op, "P") && strings.HasSuffix(op, "RD") {
660                                 // PINSRD, PEXTRD, etc
661                                 src = 4
662                                 break
663                         }
664                         if strings.HasPrefix(op, "F") && (strings.HasSuffix(op, "F") || strings.HasSuffix(op, "FP")) {
665                                 // FMOVFP, FXCHF, etc
666                                 src = 4
667                                 break
668                         }
669                         if strings.HasSuffix(op, "SD") {
670                                 // MOVSD, SQRTSD, etc
671                                 src = 8
672                                 break
673                         }
674                         if strings.HasSuffix(op, "SS") {
675                                 // MOVSS, SQRTSS, etc
676                                 src = 4
677                                 break
678                         }
679                         if op == "MOVO" || op == "MOVOU" {
680                                 src = 16
681                                 break
682                         }
683                         if strings.HasPrefix(op, "SET") {
684                                 // SETEQ, etc
685                                 src = 1
686                                 break
687                         }
688                         switch op[len(op)-1] {
689                         case 'B':
690                                 src = 1
691                         case 'W':
692                                 src = 2
693                         case 'L':
694                                 src = 4
695                         case 'D', 'Q':
696                                 src = 8
697                         }
698                 case "ppc64", "ppc64le":
699                         // Strip standard suffixes to reveal size letter.
700                         m := ppc64Suff.FindStringSubmatch(op)
701                         if m != nil {
702                                 switch m[1][0] {
703                                 case 'B':
704                                         src = 1
705                                 case 'H':
706                                         src = 2
707                                 case 'W':
708                                         src = 4
709                                 case 'D':
710                                         src = 8
711                                 }
712                         }
713                 case "mips", "mipsle", "mips64", "mips64le":
714                         switch op {
715                         case "MOVB", "MOVBU":
716                                 src = 1
717                         case "MOVH", "MOVHU":
718                                 src = 2
719                         case "MOVW", "MOVWU", "MOVF":
720                                 src = 4
721                         case "MOVV", "MOVD":
722                                 src = 8
723                         }
724                 case "s390x":
725                         switch op {
726                         case "MOVB", "MOVBZ":
727                                 src = 1
728                         case "MOVH", "MOVHZ":
729                                 src = 2
730                         case "MOVW", "MOVWZ", "FMOVS":
731                                 src = 4
732                         case "MOVD", "FMOVD":
733                                 src = 8
734                         }
735                 }
736         }
737         if dst == 0 {
738                 dst = src
739         }
740
741         // Determine whether the match we're holding
742         // is the first or second argument.
743         if strings.Index(line, expr) > strings.Index(line, ",") {
744                 kind = dst
745         } else {
746                 kind = src
747         }
748
749         vk := v.kind
750         vs := v.size
751         vt := v.typ
752         switch vk {
753         case asmInterface, asmEmptyInterface, asmString, asmSlice:
754                 // allow reference to first word (pointer)
755                 vk = v.inner[0].kind
756                 vs = v.inner[0].size
757                 vt = v.inner[0].typ
758         case asmComplex:
759                 // Allow a single instruction to load both parts of a complex.
760                 if int(kind) == vs {
761                         kind = asmComplex
762                 }
763         }
764         if addr {
765                 vk = asmKind(archDef.ptrSize)
766                 vs = archDef.ptrSize
767                 vt = "address"
768         }
769
770         if off != v.off {
771                 var inner bytes.Buffer
772                 for i, vi := range v.inner {
773                         if len(v.inner) > 1 {
774                                 fmt.Fprintf(&inner, ",")
775                         }
776                         fmt.Fprintf(&inner, " ")
777                         if i == len(v.inner)-1 {
778                                 fmt.Fprintf(&inner, "or ")
779                         }
780                         fmt.Fprintf(&inner, "%s+%d(FP)", vi.name, vi.off)
781                 }
782                 badf("invalid offset %s; expected %s+%d(FP)%s", expr, v.name, v.off, inner.String())
783                 return
784         }
785         if kind != 0 && kind != vk {
786                 var inner bytes.Buffer
787                 if len(v.inner) > 0 {
788                         fmt.Fprintf(&inner, " containing")
789                         for i, vi := range v.inner {
790                                 if i > 0 && len(v.inner) > 2 {
791                                         fmt.Fprintf(&inner, ",")
792                                 }
793                                 fmt.Fprintf(&inner, " ")
794                                 if i > 0 && i == len(v.inner)-1 {
795                                         fmt.Fprintf(&inner, "and ")
796                                 }
797                                 fmt.Fprintf(&inner, "%s+%d(FP)", vi.name, vi.off)
798                         }
799                 }
800                 badf("invalid %s of %s; %s is %d-byte value%s", op, expr, vt, vs, inner.String())
801         }
802 }