PR tree-optimization/86274 - SEGFAULT when logging std::to_string(NAN)
[official-gcc.git] / libgo / go / cmd / vet / asmdecl.go
blobb01d23d342ba5372f2c06ffbf829ab2e5d8bc1dc
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.
5 // Identify mismatches between assembly files and Go func declarations.
7 package main
9 import (
10 "bytes"
11 "fmt"
12 "go/ast"
13 "go/build"
14 "go/token"
15 "go/types"
16 "regexp"
17 "strconv"
18 "strings"
21 // 'kind' is a kind of assembly variable.
22 // The kinds 1, 2, 4, 8 stand for values of that size.
23 type asmKind int
25 // These special kinds are not valid sizes.
26 const (
27 asmString asmKind = 100 + iota
28 asmSlice
29 asmArray
30 asmInterface
31 asmEmptyInterface
32 asmStruct
33 asmComplex
36 // An asmArch describes assembly parameters for an architecture
37 type asmArch struct {
38 name string
39 bigEndian bool
40 stack string
41 lr bool
42 // calculated during initialization
43 sizes types.Sizes
44 intSize int
45 ptrSize int
46 maxAlign int
49 // An asmFunc describes the expected variables for a function on a given architecture.
50 type asmFunc struct {
51 arch *asmArch
52 size int // size of all arguments
53 vars map[string]*asmVar
54 varByOffset map[int]*asmVar
57 // An asmVar describes a single assembly variable.
58 type asmVar struct {
59 name string
60 kind asmKind
61 typ string
62 off int
63 size int
64 inner []*asmVar
67 var (
68 asmArch386 = asmArch{name: "386", bigEndian: false, stack: "SP", lr: false}
69 asmArchArm = asmArch{name: "arm", bigEndian: false, stack: "R13", lr: true}
70 asmArchArm64 = asmArch{name: "arm64", bigEndian: false, stack: "RSP", lr: true}
71 asmArchAmd64 = asmArch{name: "amd64", bigEndian: false, stack: "SP", lr: false}
72 asmArchAmd64p32 = asmArch{name: "amd64p32", bigEndian: false, stack: "SP", lr: false}
73 asmArchMips = asmArch{name: "mips", bigEndian: true, stack: "R29", lr: true}
74 asmArchMipsLE = asmArch{name: "mipsle", bigEndian: false, stack: "R29", lr: true}
75 asmArchMips64 = asmArch{name: "mips64", bigEndian: true, stack: "R29", lr: true}
76 asmArchMips64LE = asmArch{name: "mips64le", bigEndian: false, stack: "R29", lr: true}
77 asmArchPpc64 = asmArch{name: "ppc64", bigEndian: true, stack: "R1", lr: true}
78 asmArchPpc64LE = asmArch{name: "ppc64le", bigEndian: false, stack: "R1", lr: true}
79 asmArchS390X = asmArch{name: "s390x", bigEndian: true, stack: "R15", lr: true}
81 arches = []*asmArch{
82 &asmArch386,
83 &asmArchArm,
84 &asmArchArm64,
85 &asmArchAmd64,
86 &asmArchAmd64p32,
87 &asmArchMips,
88 &asmArchMipsLE,
89 &asmArchMips64,
90 &asmArchMips64LE,
91 &asmArchPpc64,
92 &asmArchPpc64LE,
93 &asmArchS390X,
97 func init() {
98 for _, arch := range arches {
99 arch.sizes = types.SizesFor("gc", arch.name)
100 if arch.sizes == nil {
101 panic("missing SizesFor for gc/" + arch.name)
103 arch.intSize = int(arch.sizes.Sizeof(types.Typ[types.Int]))
104 arch.ptrSize = int(arch.sizes.Sizeof(types.Typ[types.UnsafePointer]))
105 arch.maxAlign = int(arch.sizes.Alignof(types.Typ[types.Int64]))
109 var (
110 re = regexp.MustCompile
111 asmPlusBuild = re(`//\s+\+build\s+([^\n]+)`)
112 asmTEXT = re(`\bTEXT\b(.*)ยท([^\(]+)\(SB\)(?:\s*,\s*([0-9A-Z|+()]+))?(?:\s*,\s*\$(-?[0-9]+)(?:-([0-9]+))?)?`)
113 asmDATA = re(`\b(DATA|GLOBL)\b`)
114 asmNamedFP = re(`([a-zA-Z0-9_\xFF-\x{10FFFF}]+)(?:\+([0-9]+))\(FP\)`)
115 asmUnnamedFP = re(`[^+\-0-9](([0-9]+)\(FP\))`)
116 asmSP = re(`[^+\-0-9](([0-9]+)\(([A-Z0-9]+)\))`)
117 asmOpcode = re(`^\s*(?:[A-Z0-9a-z_]+:)?\s*([A-Z]+)\s*([^,]*)(?:,\s*(.*))?`)
118 ppc64Suff = re(`([BHWD])(ZU|Z|U|BR)?$`)
121 func asmCheck(pkg *Package) {
122 if !vet("asmdecl") {
123 return
126 // No work if no assembly files.
127 if !pkg.hasFileWithSuffix(".s") {
128 return
131 // Gather declarations. knownFunc[name][arch] is func description.
132 knownFunc := make(map[string]map[string]*asmFunc)
134 for _, f := range pkg.files {
135 if f.file != nil {
136 for _, decl := range f.file.Decls {
137 if decl, ok := decl.(*ast.FuncDecl); ok && decl.Body == nil {
138 knownFunc[decl.Name.Name] = f.asmParseDecl(decl)
144 Files:
145 for _, f := range pkg.files {
146 if !strings.HasSuffix(f.name, ".s") {
147 continue
149 Println("Checking file", f.name)
151 // Determine architecture from file name if possible.
152 var arch string
153 var archDef *asmArch
154 for _, a := range arches {
155 if strings.HasSuffix(f.name, "_"+a.name+".s") {
156 arch = a.name
157 archDef = a
158 break
162 lines := strings.SplitAfter(string(f.content), "\n")
163 var (
164 fn *asmFunc
165 fnName string
166 localSize, argSize int
167 wroteSP bool
168 haveRetArg bool
169 retLine []int
172 flushRet := func() {
173 if fn != nil && fn.vars["ret"] != nil && !haveRetArg && len(retLine) > 0 {
174 v := fn.vars["ret"]
175 for _, line := range retLine {
176 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)
179 retLine = nil
181 for lineno, line := range lines {
182 lineno++
184 badf := func(format string, args ...interface{}) {
185 f.Badf(token.NoPos, "%s:%d: [%s] %s: %s", f.name, lineno, arch, fnName, fmt.Sprintf(format, args...))
188 if arch == "" {
189 // Determine architecture from +build line if possible.
190 if m := asmPlusBuild.FindStringSubmatch(line); m != nil {
191 // There can be multiple architectures in a single +build line,
192 // so accumulate them all and then prefer the one that
193 // matches build.Default.GOARCH.
194 var archCandidates []*asmArch
195 for _, fld := range strings.Fields(m[1]) {
196 for _, a := range arches {
197 if a.name == fld {
198 archCandidates = append(archCandidates, a)
202 for _, a := range archCandidates {
203 if a.name == build.Default.GOARCH {
204 archCandidates = []*asmArch{a}
205 break
208 if len(archCandidates) > 0 {
209 arch = archCandidates[0].name
210 archDef = archCandidates[0]
215 if m := asmTEXT.FindStringSubmatch(line); m != nil {
216 flushRet()
217 if arch == "" {
218 // Arch not specified by filename or build tags.
219 // Fall back to build.Default.GOARCH.
220 for _, a := range arches {
221 if a.name == build.Default.GOARCH {
222 arch = a.name
223 archDef = a
224 break
227 if arch == "" {
228 f.Warnf(token.NoPos, "%s: cannot determine architecture for assembly file", f.name)
229 continue Files
232 fnName = m[2]
233 if pkgName := strings.TrimSpace(m[1]); pkgName != "" {
234 pathParts := strings.Split(pkgName, "โˆ•")
235 pkgName = pathParts[len(pathParts)-1]
236 if pkgName != f.pkg.path {
237 f.Warnf(token.NoPos, "%s:%d: [%s] cannot check cross-package assembly function: %s is in package %s", f.name, lineno, arch, fnName, pkgName)
238 fn = nil
239 fnName = ""
240 continue
243 fn = knownFunc[fnName][arch]
244 if fn != nil {
245 size, _ := strconv.Atoi(m[5])
246 flag := m[3]
247 if size != fn.size && (flag != "7" && !strings.Contains(flag, "NOSPLIT") || size != 0) {
248 badf("wrong argument size %d; expected $...-%d", size, fn.size)
251 localSize, _ = strconv.Atoi(m[4])
252 localSize += archDef.intSize
253 if archDef.lr {
254 // Account for caller's saved LR
255 localSize += archDef.intSize
257 argSize, _ = strconv.Atoi(m[5])
258 if fn == nil && !strings.Contains(fnName, "<>") {
259 badf("function %s missing Go declaration", fnName)
261 wroteSP = false
262 haveRetArg = false
263 continue
264 } else if strings.Contains(line, "TEXT") && strings.Contains(line, "SB") {
265 // function, but not visible from Go (didn't match asmTEXT), so stop checking
266 flushRet()
267 fn = nil
268 fnName = ""
269 continue
272 if strings.Contains(line, "RET") {
273 retLine = append(retLine, lineno)
276 if fnName == "" {
277 continue
280 if asmDATA.FindStringSubmatch(line) != nil {
281 fn = nil
284 if archDef == nil {
285 continue
288 if strings.Contains(line, ", "+archDef.stack) || strings.Contains(line, ",\t"+archDef.stack) {
289 wroteSP = true
290 continue
293 for _, m := range asmSP.FindAllStringSubmatch(line, -1) {
294 if m[3] != archDef.stack || wroteSP {
295 continue
297 off := 0
298 if m[1] != "" {
299 off, _ = strconv.Atoi(m[2])
301 if off >= localSize {
302 if fn != nil {
303 v := fn.varByOffset[off-localSize]
304 if v != nil {
305 badf("%s should be %s+%d(FP)", m[1], v.name, off-localSize)
306 continue
309 if off >= localSize+argSize {
310 badf("use of %s points beyond argument frame", m[1])
311 continue
313 badf("use of %s to access argument frame", m[1])
317 if fn == nil {
318 continue
321 for _, m := range asmUnnamedFP.FindAllStringSubmatch(line, -1) {
322 off, _ := strconv.Atoi(m[2])
323 v := fn.varByOffset[off]
324 if v != nil {
325 badf("use of unnamed argument %s; offset %d is %s+%d(FP)", m[1], off, v.name, v.off)
326 } else {
327 badf("use of unnamed argument %s", m[1])
331 for _, m := range asmNamedFP.FindAllStringSubmatch(line, -1) {
332 name := m[1]
333 off := 0
334 if m[2] != "" {
335 off, _ = strconv.Atoi(m[2])
337 if name == "ret" || strings.HasPrefix(name, "ret_") {
338 haveRetArg = true
340 v := fn.vars[name]
341 if v == nil {
342 // Allow argframe+0(FP).
343 if name == "argframe" && off == 0 {
344 continue
346 v = fn.varByOffset[off]
347 if v != nil {
348 badf("unknown variable %s; offset %d is %s+%d(FP)", name, off, v.name, v.off)
349 } else {
350 badf("unknown variable %s", name)
352 continue
354 asmCheckVar(badf, fn, line, m[0], off, v)
357 flushRet()
361 func asmKindForType(t types.Type, size int) asmKind {
362 switch t := t.Underlying().(type) {
363 case *types.Basic:
364 switch t.Kind() {
365 case types.String:
366 return asmString
367 case types.Complex64, types.Complex128:
368 return asmComplex
370 return asmKind(size)
371 case *types.Pointer, *types.Chan, *types.Map, *types.Signature:
372 return asmKind(size)
373 case *types.Struct:
374 return asmStruct
375 case *types.Interface:
376 if t.Empty() {
377 return asmEmptyInterface
379 return asmInterface
380 case *types.Array:
381 return asmArray
382 case *types.Slice:
383 return asmSlice
385 panic("unreachable")
388 // A component is an assembly-addressable component of a composite type,
389 // or a composite type itself.
390 type component struct {
391 size int
392 offset int
393 kind asmKind
394 typ string
395 suffix string // Such as _base for string base, _0_lo for lo half of first element of [1]uint64 on 32 bit machine.
396 outer string // The suffix for immediately containing composite type.
399 func newComponent(suffix string, kind asmKind, typ string, offset, size int, outer string) component {
400 return component{suffix: suffix, kind: kind, typ: typ, offset: offset, size: size, outer: outer}
403 // componentsOfType generates a list of components of type t.
404 // For example, given string, the components are the string itself, the base, and the length.
405 func componentsOfType(arch *asmArch, t types.Type) []component {
406 return appendComponentsRecursive(arch, t, nil, "", 0)
409 // appendComponentsRecursive implements componentsOfType.
410 // Recursion is required to correct handle structs and arrays,
411 // which can contain arbitrary other types.
412 func appendComponentsRecursive(arch *asmArch, t types.Type, cc []component, suffix string, off int) []component {
413 s := t.String()
414 size := int(arch.sizes.Sizeof(t))
415 kind := asmKindForType(t, size)
416 cc = append(cc, newComponent(suffix, kind, s, off, size, suffix))
418 switch kind {
419 case 8:
420 if arch.ptrSize == 4 {
421 w1, w2 := "lo", "hi"
422 if arch.bigEndian {
423 w1, w2 = w2, w1
425 cc = append(cc, newComponent(suffix+"_"+w1, 4, "half "+s, off, 4, suffix))
426 cc = append(cc, newComponent(suffix+"_"+w2, 4, "half "+s, off+4, 4, suffix))
429 case asmEmptyInterface:
430 cc = append(cc, newComponent(suffix+"_type", asmKind(arch.ptrSize), "interface type", off, arch.ptrSize, suffix))
431 cc = append(cc, newComponent(suffix+"_data", asmKind(arch.ptrSize), "interface data", off+arch.ptrSize, arch.ptrSize, suffix))
433 case asmInterface:
434 cc = append(cc, newComponent(suffix+"_itable", asmKind(arch.ptrSize), "interface itable", off, arch.ptrSize, suffix))
435 cc = append(cc, newComponent(suffix+"_data", asmKind(arch.ptrSize), "interface data", off+arch.ptrSize, arch.ptrSize, suffix))
437 case asmSlice:
438 cc = append(cc, newComponent(suffix+"_base", asmKind(arch.ptrSize), "slice base", off, arch.ptrSize, suffix))
439 cc = append(cc, newComponent(suffix+"_len", asmKind(arch.intSize), "slice len", off+arch.ptrSize, arch.intSize, suffix))
440 cc = append(cc, newComponent(suffix+"_cap", asmKind(arch.intSize), "slice cap", off+arch.ptrSize+arch.intSize, arch.intSize, suffix))
442 case asmString:
443 cc = append(cc, newComponent(suffix+"_base", asmKind(arch.ptrSize), "string base", off, arch.ptrSize, suffix))
444 cc = append(cc, newComponent(suffix+"_len", asmKind(arch.intSize), "string len", off+arch.ptrSize, arch.intSize, suffix))
446 case asmComplex:
447 fsize := size / 2
448 cc = append(cc, newComponent(suffix+"_real", asmKind(fsize), fmt.Sprintf("real(complex%d)", size*8), off, fsize, suffix))
449 cc = append(cc, newComponent(suffix+"_imag", asmKind(fsize), fmt.Sprintf("imag(complex%d)", size*8), off+fsize, fsize, suffix))
451 case asmStruct:
452 tu := t.Underlying().(*types.Struct)
453 fields := make([]*types.Var, tu.NumFields())
454 for i := 0; i < tu.NumFields(); i++ {
455 fields[i] = tu.Field(i)
457 offsets := arch.sizes.Offsetsof(fields)
458 for i, f := range fields {
459 cc = appendComponentsRecursive(arch, f.Type(), cc, suffix+"_"+f.Name(), off+int(offsets[i]))
462 case asmArray:
463 tu := t.Underlying().(*types.Array)
464 elem := tu.Elem()
465 // Calculate offset of each element array.
466 fields := []*types.Var{
467 types.NewVar(token.NoPos, nil, "fake0", elem),
468 types.NewVar(token.NoPos, nil, "fake1", elem),
470 offsets := arch.sizes.Offsetsof(fields)
471 elemoff := int(offsets[1])
472 for i := 0; i < int(tu.Len()); i++ {
473 cc = appendComponentsRecursive(arch, elem, cc, suffix+"_"+strconv.Itoa(i), i*elemoff)
477 return cc
480 // asmParseDecl parses a function decl for expected assembly variables.
481 func (f *File) asmParseDecl(decl *ast.FuncDecl) map[string]*asmFunc {
482 var (
483 arch *asmArch
484 fn *asmFunc
485 offset int
488 // addParams adds asmVars for each of the parameters in list.
489 // isret indicates whether the list are the arguments or the return values.
490 addParams := func(list []*ast.Field, isret bool) {
491 argnum := 0
492 for _, fld := range list {
493 t := f.pkg.types[fld.Type].Type
494 align := int(arch.sizes.Alignof(t))
495 size := int(arch.sizes.Sizeof(t))
496 offset += -offset & (align - 1)
497 cc := componentsOfType(arch, t)
499 // names is the list of names with this type.
500 names := fld.Names
501 if len(names) == 0 {
502 // Anonymous args will be called arg, arg1, arg2, ...
503 // Similarly so for return values: ret, ret1, ret2, ...
504 name := "arg"
505 if isret {
506 name = "ret"
508 if argnum > 0 {
509 name += strconv.Itoa(argnum)
511 names = []*ast.Ident{ast.NewIdent(name)}
513 argnum += len(names)
515 // Create variable for each name.
516 for _, id := range names {
517 name := id.Name
518 for _, c := range cc {
519 outer := name + c.outer
520 v := asmVar{
521 name: name + c.suffix,
522 kind: c.kind,
523 typ: c.typ,
524 off: offset + c.offset,
525 size: c.size,
527 if vo := fn.vars[outer]; vo != nil {
528 vo.inner = append(vo.inner, &v)
530 fn.vars[v.name] = &v
531 for i := 0; i < v.size; i++ {
532 fn.varByOffset[v.off+i] = &v
535 offset += size
540 m := make(map[string]*asmFunc)
541 for _, arch = range arches {
542 fn = &asmFunc{
543 arch: arch,
544 vars: make(map[string]*asmVar),
545 varByOffset: make(map[int]*asmVar),
547 offset = 0
548 addParams(decl.Type.Params.List, false)
549 if decl.Type.Results != nil && len(decl.Type.Results.List) > 0 {
550 offset += -offset & (arch.maxAlign - 1)
551 addParams(decl.Type.Results.List, true)
553 fn.size = offset
554 m[arch.name] = fn
557 return m
560 // asmCheckVar checks a single variable reference.
561 func asmCheckVar(badf func(string, ...interface{}), fn *asmFunc, line, expr string, off int, v *asmVar) {
562 m := asmOpcode.FindStringSubmatch(line)
563 if m == nil {
564 if !strings.HasPrefix(strings.TrimSpace(line), "//") {
565 badf("cannot find assembly opcode")
567 return
570 // Determine operand sizes from instruction.
571 // Typically the suffix suffices, but there are exceptions.
572 var src, dst, kind asmKind
573 op := m[1]
574 switch fn.arch.name + "." + op {
575 case "386.FMOVLP":
576 src, dst = 8, 4
577 case "arm.MOVD":
578 src = 8
579 case "arm.MOVW":
580 src = 4
581 case "arm.MOVH", "arm.MOVHU":
582 src = 2
583 case "arm.MOVB", "arm.MOVBU":
584 src = 1
585 // LEA* opcodes don't really read the second arg.
586 // They just take the address of it.
587 case "386.LEAL":
588 dst = 4
589 case "amd64.LEAQ":
590 dst = 8
591 case "amd64p32.LEAL":
592 dst = 4
593 default:
594 switch fn.arch.name {
595 case "386", "amd64":
596 if strings.HasPrefix(op, "F") && (strings.HasSuffix(op, "D") || strings.HasSuffix(op, "DP")) {
597 // FMOVDP, FXCHD, etc
598 src = 8
599 break
601 if strings.HasPrefix(op, "P") && strings.HasSuffix(op, "RD") {
602 // PINSRD, PEXTRD, etc
603 src = 4
604 break
606 if strings.HasPrefix(op, "F") && (strings.HasSuffix(op, "F") || strings.HasSuffix(op, "FP")) {
607 // FMOVFP, FXCHF, etc
608 src = 4
609 break
611 if strings.HasSuffix(op, "SD") {
612 // MOVSD, SQRTSD, etc
613 src = 8
614 break
616 if strings.HasSuffix(op, "SS") {
617 // MOVSS, SQRTSS, etc
618 src = 4
619 break
621 if strings.HasPrefix(op, "SET") {
622 // SETEQ, etc
623 src = 1
624 break
626 switch op[len(op)-1] {
627 case 'B':
628 src = 1
629 case 'W':
630 src = 2
631 case 'L':
632 src = 4
633 case 'D', 'Q':
634 src = 8
636 case "ppc64", "ppc64le":
637 // Strip standard suffixes to reveal size letter.
638 m := ppc64Suff.FindStringSubmatch(op)
639 if m != nil {
640 switch m[1][0] {
641 case 'B':
642 src = 1
643 case 'H':
644 src = 2
645 case 'W':
646 src = 4
647 case 'D':
648 src = 8
651 case "mips", "mipsle", "mips64", "mips64le":
652 switch op {
653 case "MOVB", "MOVBU":
654 src = 1
655 case "MOVH", "MOVHU":
656 src = 2
657 case "MOVW", "MOVWU", "MOVF":
658 src = 4
659 case "MOVV", "MOVD":
660 src = 8
662 case "s390x":
663 switch op {
664 case "MOVB", "MOVBZ":
665 src = 1
666 case "MOVH", "MOVHZ":
667 src = 2
668 case "MOVW", "MOVWZ", "FMOVS":
669 src = 4
670 case "MOVD", "FMOVD":
671 src = 8
675 if dst == 0 {
676 dst = src
679 // Determine whether the match we're holding
680 // is the first or second argument.
681 if strings.Index(line, expr) > strings.Index(line, ",") {
682 kind = dst
683 } else {
684 kind = src
687 vk := v.kind
688 vs := v.size
689 vt := v.typ
690 switch vk {
691 case asmInterface, asmEmptyInterface, asmString, asmSlice:
692 // allow reference to first word (pointer)
693 vk = v.inner[0].kind
694 vs = v.inner[0].size
695 vt = v.inner[0].typ
698 if off != v.off {
699 var inner bytes.Buffer
700 for i, vi := range v.inner {
701 if len(v.inner) > 1 {
702 fmt.Fprintf(&inner, ",")
704 fmt.Fprintf(&inner, " ")
705 if i == len(v.inner)-1 {
706 fmt.Fprintf(&inner, "or ")
708 fmt.Fprintf(&inner, "%s+%d(FP)", vi.name, vi.off)
710 badf("invalid offset %s; expected %s+%d(FP)%s", expr, v.name, v.off, inner.String())
711 return
713 if kind != 0 && kind != vk {
714 var inner bytes.Buffer
715 if len(v.inner) > 0 {
716 fmt.Fprintf(&inner, " containing")
717 for i, vi := range v.inner {
718 if i > 0 && len(v.inner) > 2 {
719 fmt.Fprintf(&inner, ",")
721 fmt.Fprintf(&inner, " ")
722 if i > 0 && i == len(v.inner)-1 {
723 fmt.Fprintf(&inner, "and ")
725 fmt.Fprintf(&inner, "%s+%d(FP)", vi.name, vi.off)
728 badf("invalid %s of %s; %s is %d-byte value%s", op, expr, vt, vs, inner.String())