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

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

View as plain text