parser_arm64.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. // Copyright 2022 gorse Project Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package main
  15. import (
  16. "bufio"
  17. "fmt"
  18. "os"
  19. "regexp"
  20. "strings"
  21. "unicode"
  22. "github.com/klauspost/asmfmt"
  23. )
  24. const buildTags = "//go:build !noasm && arm64\n"
  25. var (
  26. attributeLine = regexp.MustCompile(`^\s+\..+$`)
  27. nameLine = regexp.MustCompile(`^\w+:.+$`)
  28. labelLine = regexp.MustCompile(`^\.\w+_\d+:.*$`)
  29. codeLine = regexp.MustCompile(`^\s+\w+.+$`)
  30. symbolLine = regexp.MustCompile(`^\w+\s+<\w+>:$`)
  31. dataLine = regexp.MustCompile(`^\w+:\s+\w+\s+.+$`)
  32. registers = []string{"R0", "R1", "R2", "R3"}
  33. )
  34. type Line struct {
  35. Labels []string
  36. Assembly string
  37. Binary string
  38. }
  39. func (line *Line) String() string {
  40. var builder strings.Builder
  41. for _, label := range line.Labels {
  42. builder.WriteString(label)
  43. builder.WriteString(":\n")
  44. }
  45. builder.WriteString("\t")
  46. builder.WriteString(fmt.Sprintf("WORD $0x%v", line.Binary))
  47. builder.WriteString("\t// ")
  48. builder.WriteString(line.Assembly)
  49. builder.WriteString("\n")
  50. return builder.String()
  51. }
  52. func parseAssembly(path string) (map[string][]Line, error) {
  53. file, err := os.Open(path)
  54. if err != nil {
  55. return nil, err
  56. }
  57. defer func(file *os.File) {
  58. if err = file.Close(); err != nil {
  59. _, _ = fmt.Fprintln(os.Stderr, err)
  60. os.Exit(1)
  61. }
  62. }(file)
  63. var (
  64. functions = make(map[string][]Line)
  65. functionName string
  66. labelName string
  67. )
  68. scanner := bufio.NewScanner(file)
  69. for scanner.Scan() {
  70. line := scanner.Text()
  71. if attributeLine.MatchString(line) {
  72. continue
  73. } else if nameLine.MatchString(line) {
  74. functionName = strings.Split(line, ":")[0]
  75. functions[functionName] = make([]Line, 0)
  76. } else if labelLine.MatchString(line) {
  77. labelName = strings.Split(line, ":")[0]
  78. labelName = labelName[1:]
  79. lines := functions[functionName]
  80. if len(lines) == 1 || lines[len(lines)-1].Assembly != "" {
  81. functions[functionName] = append(functions[functionName], Line{Labels: []string{labelName}})
  82. } else {
  83. lines[len(lines)-1].Labels = append(lines[len(lines)-1].Labels, labelName)
  84. }
  85. } else if codeLine.MatchString(line) {
  86. asm := strings.Split(line, "#")[0]
  87. asm = strings.TrimSpace(asm)
  88. if labelName == "" {
  89. functions[functionName] = append(functions[functionName], Line{Assembly: asm})
  90. } else {
  91. lines := functions[functionName]
  92. lines[len(lines)-1].Assembly = asm
  93. labelName = ""
  94. }
  95. }
  96. }
  97. if err = scanner.Err(); err != nil {
  98. return nil, err
  99. }
  100. return functions, nil
  101. }
  102. func parseObjectDump(dump string, functions map[string][]Line) error {
  103. var (
  104. functionName string
  105. lineNumber int
  106. )
  107. for i, line := range strings.Split(dump, "\n") {
  108. line = strings.TrimSpace(line)
  109. if symbolLine.MatchString(line) {
  110. functionName = strings.Split(line, "<")[1]
  111. functionName = strings.Split(functionName, ">")[0]
  112. lineNumber = 0
  113. } else if dataLine.MatchString(line) {
  114. data := strings.Split(line, ":")[1]
  115. data = strings.TrimSpace(data)
  116. splits := strings.Split(data, " ")
  117. var (
  118. binary string
  119. assembly string
  120. )
  121. for i, s := range splits {
  122. if s == "" || unicode.IsSpace(rune(s[0])) {
  123. assembly = strings.Join(splits[i:], " ")
  124. assembly = strings.TrimSpace(assembly)
  125. break
  126. }
  127. binary = s
  128. }
  129. if lineNumber >= len(functions[functionName]) {
  130. return fmt.Errorf("%d: unexpected objectdump line: %s", i, line)
  131. }
  132. functions[functionName][lineNumber].Binary = binary
  133. lineNumber++
  134. }
  135. }
  136. return nil
  137. }
  138. func generateGoAssembly(path string, functions []Function) error {
  139. // generate code
  140. var builder strings.Builder
  141. builder.WriteString(buildTags)
  142. builder.WriteString("// AUTO-GENERATED BY GOAT -- DO NOT EDIT\n")
  143. for _, function := range functions {
  144. builder.WriteString(fmt.Sprintf("\nTEXT ·%v(SB), $0-32\n", function.Name))
  145. for i, param := range function.Parameters {
  146. builder.WriteString(fmt.Sprintf("\tMOVD %s+%d(FP), %s\n", param, i*8, registers[i]))
  147. }
  148. for _, line := range function.Lines {
  149. builder.WriteString(line.String())
  150. }
  151. }
  152. // write file
  153. f, err := os.Create(path)
  154. if err != nil {
  155. return err
  156. }
  157. defer func(f *os.File) {
  158. if err = f.Close(); err != nil {
  159. _, _ = fmt.Fprintln(os.Stderr, err)
  160. os.Exit(1)
  161. }
  162. }(f)
  163. bytes, err := asmfmt.Format(strings.NewReader(builder.String()))
  164. if err != nil {
  165. return err
  166. }
  167. _, err = f.Write(bytes)
  168. return err
  169. }