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