github.com/liujq9674git/golang-src-1.7@v0.0.0-20230517174348-17f6ec47f3f8/src/cmd/vet/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 // Identify mismatches between assembly files and Go func declarations. 6 7 package main 8 9 import ( 10 "bytes" 11 "fmt" 12 "go/ast" 13 "go/token" 14 "regexp" 15 "strconv" 16 "strings" 17 ) 18 19 // 'kind' is a kind of assembly variable. 20 // The kinds 1, 2, 4, 8 stand for values of that size. 21 type asmKind int 22 23 // These special kinds are not valid sizes. 24 const ( 25 asmString asmKind = 100 + iota 26 asmSlice 27 asmInterface 28 asmEmptyInterface 29 ) 30 31 // An asmArch describes assembly parameters for an architecture 32 type asmArch struct { 33 name string 34 ptrSize int 35 intSize int 36 maxAlign int 37 bigEndian bool 38 stack string 39 lr bool 40 } 41 42 // An asmFunc describes the expected variables for a function on a given architecture. 43 type asmFunc struct { 44 arch *asmArch 45 size int // size of all arguments 46 vars map[string]*asmVar 47 varByOffset map[int]*asmVar 48 } 49 50 // An asmVar describes a single assembly variable. 51 type asmVar struct { 52 name string 53 kind asmKind 54 typ string 55 off int 56 size int 57 inner []*asmVar 58 } 59 60 var ( 61 asmArch386 = asmArch{"386", 4, 4, 4, false, "SP", false} 62 asmArchArm = asmArch{"arm", 4, 4, 4, false, "R13", true} 63 asmArchArm64 = asmArch{"arm64", 8, 8, 8, false, "RSP", true} 64 asmArchAmd64 = asmArch{"amd64", 8, 8, 8, false, "SP", false} 65 asmArchAmd64p32 = asmArch{"amd64p32", 4, 4, 8, false, "SP", false} 66 asmArchMips64 = asmArch{"mips64", 8, 8, 8, true, "R29", true} 67 asmArchMips64LE = asmArch{"mips64", 8, 8, 8, false, "R29", true} 68 asmArchPpc64 = asmArch{"ppc64", 8, 8, 8, true, "R1", true} 69 asmArchPpc64LE = asmArch{"ppc64le", 8, 8, 8, false, "R1", true} 70 71 arches = []*asmArch{ 72 &asmArch386, 73 &asmArchArm, 74 &asmArchArm64, 75 &asmArchAmd64, 76 &asmArchAmd64p32, 77 &asmArchMips64, 78 &asmArchMips64LE, 79 &asmArchPpc64, 80 &asmArchPpc64LE, 81 } 82 ) 83 84 var ( 85 re = regexp.MustCompile 86 asmPlusBuild = re(`//\s+\+build\s+([^\n]+)`) 87 asmTEXT = re(`\bTEXT\b.*ยท([^\(]+)\(SB\)(?:\s*,\s*([0-9A-Z|+]+))?(?:\s*,\s*\$(-?[0-9]+)(?:-([0-9]+))?)?`) 88 asmDATA = re(`\b(DATA|GLOBL)\b`) 89 asmNamedFP = re(`([a-zA-Z0-9_\xFF-\x{10FFFF}]+)(?:\+([0-9]+))\(FP\)`) 90 asmUnnamedFP = re(`[^+\-0-9](([0-9]+)\(FP\))`) 91 asmSP = re(`[^+\-0-9](([0-9]+)\(([A-Z0-9]+)\))`) 92 asmOpcode = re(`^\s*(?:[A-Z0-9a-z_]+:)?\s*([A-Z]+)\s*([^,]*)(?:,\s*(.*))?`) 93 ppc64Suff = re(`([BHWD])(ZU|Z|U|BR)?$`) 94 ) 95 96 func asmCheck(pkg *Package) { 97 if !vet("asmdecl") { 98 return 99 } 100 101 // No work if no assembly files. 102 if !pkg.hasFileWithSuffix(".s") { 103 return 104 } 105 106 // Gather declarations. knownFunc[name][arch] is func description. 107 knownFunc := make(map[string]map[string]*asmFunc) 108 109 for _, f := range pkg.files { 110 if f.file != nil { 111 for _, decl := range f.file.Decls { 112 if decl, ok := decl.(*ast.FuncDecl); ok && decl.Body == nil { 113 knownFunc[decl.Name.Name] = f.asmParseDecl(decl) 114 } 115 } 116 } 117 } 118 119 Files: 120 for _, f := range pkg.files { 121 if !strings.HasSuffix(f.name, ".s") { 122 continue 123 } 124 Println("Checking file", f.name) 125 126 // Determine architecture from file name if possible. 127 var arch string 128 var archDef *asmArch 129 for _, a := range arches { 130 if strings.HasSuffix(f.name, "_"+a.name+".s") { 131 arch = a.name 132 archDef = a 133 break 134 } 135 } 136 137 lines := strings.SplitAfter(string(f.content), "\n") 138 var ( 139 fn *asmFunc 140 fnName string 141 localSize, argSize int 142 wroteSP bool 143 haveRetArg bool 144 retLine []int 145 ) 146 147 flushRet := func() { 148 if fn != nil && fn.vars["ret"] != nil && !haveRetArg && len(retLine) > 0 { 149 v := fn.vars["ret"] 150 for _, line := range retLine { 151 f.Badf(token.NoPos, "%s:%d: [%s] %s: RET without writing to %d-byte ret+%d(FP)", f.name, line, arch, fnName, v.size, v.off) 152 } 153 } 154 retLine = nil 155 } 156 for lineno, line := range lines { 157 lineno++ 158 159 badf := func(format string, args ...interface{}) { 160 f.Badf(token.NoPos, "%s:%d: [%s] %s: %s", f.name, lineno, arch, fnName, fmt.Sprintf(format, args...)) 161 } 162 163 if arch == "" { 164 // Determine architecture from +build line if possible. 165 if m := asmPlusBuild.FindStringSubmatch(line); m != nil { 166 Fields: 167 for _, fld := range strings.Fields(m[1]) { 168 for _, a := range arches { 169 if a.name == fld { 170 arch = a.name 171 archDef = a 172 break Fields 173 } 174 } 175 } 176 } 177 } 178 179 if m := asmTEXT.FindStringSubmatch(line); m != nil { 180 flushRet() 181 if arch == "" { 182 f.Warnf(token.NoPos, "%s: cannot determine architecture for assembly file", f.name) 183 continue Files 184 } 185 fnName = m[1] 186 fn = knownFunc[m[1]][arch] 187 if fn != nil { 188 size, _ := strconv.Atoi(m[4]) 189 if size != fn.size && (m[2] != "7" && !strings.Contains(m[2], "NOSPLIT") || size != 0) { 190 badf("wrong argument size %d; expected $...-%d", size, fn.size) 191 } 192 } 193 localSize, _ = strconv.Atoi(m[3]) 194 localSize += archDef.intSize 195 if archDef.lr { 196 // Account for caller's saved LR 197 localSize += archDef.intSize 198 } 199 argSize, _ = strconv.Atoi(m[4]) 200 if fn == nil && !strings.Contains(fnName, "<>") { 201 badf("function %s missing Go declaration", fnName) 202 } 203 wroteSP = false 204 haveRetArg = false 205 continue 206 } else if strings.Contains(line, "TEXT") && strings.Contains(line, "SB") { 207 // function, but not visible from Go (didn't match asmTEXT), so stop checking 208 flushRet() 209 fn = nil 210 fnName = "" 211 continue 212 } 213 214 if strings.Contains(line, "RET") { 215 retLine = append(retLine, lineno) 216 } 217 218 if fnName == "" { 219 continue 220 } 221 222 if asmDATA.FindStringSubmatch(line) != nil { 223 fn = nil 224 } 225 226 if archDef == nil { 227 continue 228 } 229 230 if strings.Contains(line, ", "+archDef.stack) || strings.Contains(line, ",\t"+archDef.stack) { 231 wroteSP = true 232 continue 233 } 234 235 for _, m := range asmSP.FindAllStringSubmatch(line, -1) { 236 if m[3] != archDef.stack || wroteSP { 237 continue 238 } 239 off := 0 240 if m[1] != "" { 241 off, _ = strconv.Atoi(m[2]) 242 } 243 if off >= localSize { 244 if fn != nil { 245 v := fn.varByOffset[off-localSize] 246 if v != nil { 247 badf("%s should be %s+%d(FP)", m[1], v.name, off-localSize) 248 continue 249 } 250 } 251 if off >= localSize+argSize { 252 badf("use of %s points beyond argument frame", m[1]) 253 continue 254 } 255 badf("use of %s to access argument frame", m[1]) 256 } 257 } 258 259 if fn == nil { 260 continue 261 } 262 263 for _, m := range asmUnnamedFP.FindAllStringSubmatch(line, -1) { 264 off, _ := strconv.Atoi(m[2]) 265 v := fn.varByOffset[off] 266 if v != nil { 267 badf("use of unnamed argument %s; offset %d is %s+%d(FP)", m[1], off, v.name, v.off) 268 } else { 269 badf("use of unnamed argument %s", m[1]) 270 } 271 } 272 273 for _, m := range asmNamedFP.FindAllStringSubmatch(line, -1) { 274 name := m[1] 275 off := 0 276 if m[2] != "" { 277 off, _ = strconv.Atoi(m[2]) 278 } 279 if name == "ret" || strings.HasPrefix(name, "ret_") { 280 haveRetArg = true 281 } 282 v := fn.vars[name] 283 if v == nil { 284 // Allow argframe+0(FP). 285 if name == "argframe" && off == 0 { 286 continue 287 } 288 v = fn.varByOffset[off] 289 if v != nil { 290 badf("unknown variable %s; offset %d is %s+%d(FP)", name, off, v.name, v.off) 291 } else { 292 badf("unknown variable %s", name) 293 } 294 continue 295 } 296 asmCheckVar(badf, fn, line, m[0], off, v) 297 } 298 } 299 flushRet() 300 } 301 } 302 303 // asmParseDecl parses a function decl for expected assembly variables. 304 func (f *File) asmParseDecl(decl *ast.FuncDecl) map[string]*asmFunc { 305 var ( 306 arch *asmArch 307 fn *asmFunc 308 offset int 309 failed bool 310 ) 311 312 addVar := func(outer string, v asmVar) { 313 if vo := fn.vars[outer]; vo != nil { 314 vo.inner = append(vo.inner, &v) 315 } 316 fn.vars[v.name] = &v 317 for i := 0; i < v.size; i++ { 318 fn.varByOffset[v.off+i] = &v 319 } 320 } 321 322 addParams := func(list []*ast.Field) { 323 for i, fld := range list { 324 // Determine alignment, size, and kind of type in declaration. 325 var align, size int 326 var kind asmKind 327 names := fld.Names 328 typ := f.gofmt(fld.Type) 329 switch t := fld.Type.(type) { 330 default: 331 switch typ { 332 default: 333 f.Warnf(fld.Type.Pos(), "unknown assembly argument type %s", typ) 334 failed = true 335 return 336 case "int8", "uint8", "byte", "bool": 337 size = 1 338 case "int16", "uint16": 339 size = 2 340 case "int32", "uint32", "float32": 341 size = 4 342 case "int64", "uint64", "float64": 343 align = arch.maxAlign 344 size = 8 345 case "int", "uint": 346 size = arch.intSize 347 case "uintptr", "iword", "Word", "Errno", "unsafe.Pointer": 348 size = arch.ptrSize 349 case "string", "ErrorString": 350 size = arch.ptrSize * 2 351 align = arch.ptrSize 352 kind = asmString 353 } 354 case *ast.ChanType, *ast.FuncType, *ast.MapType, *ast.StarExpr: 355 size = arch.ptrSize 356 case *ast.InterfaceType: 357 align = arch.ptrSize 358 size = 2 * arch.ptrSize 359 if len(t.Methods.List) > 0 { 360 kind = asmInterface 361 } else { 362 kind = asmEmptyInterface 363 } 364 case *ast.ArrayType: 365 if t.Len == nil { 366 size = arch.ptrSize + 2*arch.intSize 367 align = arch.ptrSize 368 kind = asmSlice 369 break 370 } 371 f.Warnf(fld.Type.Pos(), "unsupported assembly argument type %s", typ) 372 failed = true 373 case *ast.StructType: 374 f.Warnf(fld.Type.Pos(), "unsupported assembly argument type %s", typ) 375 failed = true 376 } 377 if align == 0 { 378 align = size 379 } 380 if kind == 0 { 381 kind = asmKind(size) 382 } 383 offset += -offset & (align - 1) 384 385 // Create variable for each name being declared with this type. 386 if len(names) == 0 { 387 name := "unnamed" 388 if decl.Type.Results != nil && len(decl.Type.Results.List) > 0 && &list[0] == &decl.Type.Results.List[0] && i == 0 { 389 // Assume assembly will refer to single unnamed result as r. 390 name = "ret" 391 } 392 names = []*ast.Ident{{Name: name}} 393 } 394 for _, id := range names { 395 name := id.Name 396 addVar("", asmVar{ 397 name: name, 398 kind: kind, 399 typ: typ, 400 off: offset, 401 size: size, 402 }) 403 switch kind { 404 case 8: 405 if arch.ptrSize == 4 { 406 w1, w2 := "lo", "hi" 407 if arch.bigEndian { 408 w1, w2 = w2, w1 409 } 410 addVar(name, asmVar{ 411 name: name + "_" + w1, 412 kind: 4, 413 typ: "half " + typ, 414 off: offset, 415 size: 4, 416 }) 417 addVar(name, asmVar{ 418 name: name + "_" + w2, 419 kind: 4, 420 typ: "half " + typ, 421 off: offset + 4, 422 size: 4, 423 }) 424 } 425 426 case asmEmptyInterface: 427 addVar(name, asmVar{ 428 name: name + "_type", 429 kind: asmKind(arch.ptrSize), 430 typ: "interface type", 431 off: offset, 432 size: arch.ptrSize, 433 }) 434 addVar(name, asmVar{ 435 name: name + "_data", 436 kind: asmKind(arch.ptrSize), 437 typ: "interface data", 438 off: offset + arch.ptrSize, 439 size: arch.ptrSize, 440 }) 441 442 case asmInterface: 443 addVar(name, asmVar{ 444 name: name + "_itable", 445 kind: asmKind(arch.ptrSize), 446 typ: "interface itable", 447 off: offset, 448 size: arch.ptrSize, 449 }) 450 addVar(name, asmVar{ 451 name: name + "_data", 452 kind: asmKind(arch.ptrSize), 453 typ: "interface data", 454 off: offset + arch.ptrSize, 455 size: arch.ptrSize, 456 }) 457 458 case asmSlice: 459 addVar(name, asmVar{ 460 name: name + "_base", 461 kind: asmKind(arch.ptrSize), 462 typ: "slice base", 463 off: offset, 464 size: arch.ptrSize, 465 }) 466 addVar(name, asmVar{ 467 name: name + "_len", 468 kind: asmKind(arch.intSize), 469 typ: "slice len", 470 off: offset + arch.ptrSize, 471 size: arch.intSize, 472 }) 473 addVar(name, asmVar{ 474 name: name + "_cap", 475 kind: asmKind(arch.intSize), 476 typ: "slice cap", 477 off: offset + arch.ptrSize + arch.intSize, 478 size: arch.intSize, 479 }) 480 481 case asmString: 482 addVar(name, asmVar{ 483 name: name + "_base", 484 kind: asmKind(arch.ptrSize), 485 typ: "string base", 486 off: offset, 487 size: arch.ptrSize, 488 }) 489 addVar(name, asmVar{ 490 name: name + "_len", 491 kind: asmKind(arch.intSize), 492 typ: "string len", 493 off: offset + arch.ptrSize, 494 size: arch.intSize, 495 }) 496 } 497 offset += size 498 } 499 } 500 } 501 502 m := make(map[string]*asmFunc) 503 for _, arch = range arches { 504 fn = &asmFunc{ 505 arch: arch, 506 vars: make(map[string]*asmVar), 507 varByOffset: make(map[int]*asmVar), 508 } 509 offset = 0 510 addParams(decl.Type.Params.List) 511 if decl.Type.Results != nil && len(decl.Type.Results.List) > 0 { 512 offset += -offset & (arch.maxAlign - 1) 513 addParams(decl.Type.Results.List) 514 } 515 fn.size = offset 516 m[arch.name] = fn 517 } 518 519 if failed { 520 return nil 521 } 522 return m 523 } 524 525 // asmCheckVar checks a single variable reference. 526 func asmCheckVar(badf func(string, ...interface{}), fn *asmFunc, line, expr string, off int, v *asmVar) { 527 m := asmOpcode.FindStringSubmatch(line) 528 if m == nil { 529 if !strings.HasPrefix(strings.TrimSpace(line), "//") { 530 badf("cannot find assembly opcode") 531 } 532 return 533 } 534 535 // Determine operand sizes from instruction. 536 // Typically the suffix suffices, but there are exceptions. 537 var src, dst, kind asmKind 538 op := m[1] 539 switch fn.arch.name + "." + op { 540 case "386.FMOVLP": 541 src, dst = 8, 4 542 case "arm.MOVD": 543 src = 8 544 case "arm.MOVW": 545 src = 4 546 case "arm.MOVH", "arm.MOVHU": 547 src = 2 548 case "arm.MOVB", "arm.MOVBU": 549 src = 1 550 // LEA* opcodes don't really read the second arg. 551 // They just take the address of it. 552 case "386.LEAL": 553 dst = 4 554 case "amd64.LEAQ": 555 dst = 8 556 case "amd64p32.LEAL": 557 dst = 4 558 default: 559 switch fn.arch.name { 560 case "386", "amd64": 561 if strings.HasPrefix(op, "F") && (strings.HasSuffix(op, "D") || strings.HasSuffix(op, "DP")) { 562 // FMOVDP, FXCHD, etc 563 src = 8 564 break 565 } 566 if strings.HasPrefix(op, "P") && strings.HasSuffix(op, "RD") { 567 // PINSRD, PEXTRD, etc 568 src = 4 569 break 570 } 571 if strings.HasPrefix(op, "F") && (strings.HasSuffix(op, "F") || strings.HasSuffix(op, "FP")) { 572 // FMOVFP, FXCHF, etc 573 src = 4 574 break 575 } 576 if strings.HasSuffix(op, "SD") { 577 // MOVSD, SQRTSD, etc 578 src = 8 579 break 580 } 581 if strings.HasSuffix(op, "SS") { 582 // MOVSS, SQRTSS, etc 583 src = 4 584 break 585 } 586 if strings.HasPrefix(op, "SET") { 587 // SETEQ, etc 588 src = 1 589 break 590 } 591 switch op[len(op)-1] { 592 case 'B': 593 src = 1 594 case 'W': 595 src = 2 596 case 'L': 597 src = 4 598 case 'D', 'Q': 599 src = 8 600 } 601 case "ppc64", "ppc64le": 602 // Strip standard suffixes to reveal size letter. 603 m := ppc64Suff.FindStringSubmatch(op) 604 if m != nil { 605 switch m[1][0] { 606 case 'B': 607 src = 1 608 case 'H': 609 src = 2 610 case 'W': 611 src = 4 612 case 'D': 613 src = 8 614 } 615 } 616 case "mips64", "mips64le": 617 switch op { 618 case "MOVB", "MOVBU": 619 src = 1 620 case "MOVH", "MOVHU": 621 src = 2 622 case "MOVW", "MOVWU", "MOVF": 623 src = 4 624 case "MOVV", "MOVD": 625 src = 8 626 } 627 } 628 } 629 if dst == 0 { 630 dst = src 631 } 632 633 // Determine whether the match we're holding 634 // is the first or second argument. 635 if strings.Index(line, expr) > strings.Index(line, ",") { 636 kind = dst 637 } else { 638 kind = src 639 } 640 641 vk := v.kind 642 vt := v.typ 643 switch vk { 644 case asmInterface, asmEmptyInterface, asmString, asmSlice: 645 // allow reference to first word (pointer) 646 vk = v.inner[0].kind 647 vt = v.inner[0].typ 648 } 649 650 if off != v.off { 651 var inner bytes.Buffer 652 for i, vi := range v.inner { 653 if len(v.inner) > 1 { 654 fmt.Fprintf(&inner, ",") 655 } 656 fmt.Fprintf(&inner, " ") 657 if i == len(v.inner)-1 { 658 fmt.Fprintf(&inner, "or ") 659 } 660 fmt.Fprintf(&inner, "%s+%d(FP)", vi.name, vi.off) 661 } 662 badf("invalid offset %s; expected %s+%d(FP)%s", expr, v.name, v.off, inner.String()) 663 return 664 } 665 if kind != 0 && kind != vk { 666 var inner bytes.Buffer 667 if len(v.inner) > 0 { 668 fmt.Fprintf(&inner, " containing") 669 for i, vi := range v.inner { 670 if i > 0 && len(v.inner) > 2 { 671 fmt.Fprintf(&inner, ",") 672 } 673 fmt.Fprintf(&inner, " ") 674 if i > 0 && i == len(v.inner)-1 { 675 fmt.Fprintf(&inner, "and ") 676 } 677 fmt.Fprintf(&inner, "%s+%d(FP)", vi.name, vi.off) 678 } 679 } 680 badf("invalid %s of %s; %s is %d-byte value%s", op, expr, vt, vk, inner.String()) 681 } 682 }