GoPLS Viewer

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