123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- // Copyright 2022 gorse Project Authors
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package main
- import (
- "bufio"
- "fmt"
- "os"
- "regexp"
- "strings"
- "unicode"
- "github.com/klauspost/asmfmt"
- )
- const buildTags = "//go:build !noasm && arm64\n"
- var (
- attributeLine = regexp.MustCompile(`^\s+\..+$`)
- nameLine = regexp.MustCompile(`^\w+:.+$`)
- labelLine = regexp.MustCompile(`^\.\w+_\d+:.*$`)
- codeLine = regexp.MustCompile(`^\s+\w+.+$`)
- symbolLine = regexp.MustCompile(`^\w+\s+<\w+>:$`)
- dataLine = regexp.MustCompile(`^\w+:\s+\w+\s+.+$`)
- registers = []string{"R0", "R1", "R2", "R3"}
- )
- type Line struct {
- Labels []string
- Assembly string
- Binary string
- }
- func (line *Line) String() string {
- var builder strings.Builder
- for _, label := range line.Labels {
- builder.WriteString(label)
- builder.WriteString(":\n")
- }
- builder.WriteString("\t")
- builder.WriteString(fmt.Sprintf("WORD $0x%v", line.Binary))
- builder.WriteString("\t// ")
- builder.WriteString(line.Assembly)
- builder.WriteString("\n")
- return builder.String()
- }
- func parseAssembly(path string) (map[string][]Line, error) {
- file, err := os.Open(path)
- if err != nil {
- return nil, err
- }
- defer func(file *os.File) {
- if err = file.Close(); err != nil {
- _, _ = fmt.Fprintln(os.Stderr, err)
- os.Exit(1)
- }
- }(file)
- var (
- functions = make(map[string][]Line)
- functionName string
- labelName string
- )
- scanner := bufio.NewScanner(file)
- for scanner.Scan() {
- line := scanner.Text()
- if attributeLine.MatchString(line) {
- continue
- } else if nameLine.MatchString(line) {
- functionName = strings.Split(line, ":")[0]
- functions[functionName] = make([]Line, 0)
- } else if labelLine.MatchString(line) {
- labelName = strings.Split(line, ":")[0]
- labelName = labelName[1:]
- lines := functions[functionName]
- if len(lines) == 1 || lines[len(lines)-1].Assembly != "" {
- functions[functionName] = append(functions[functionName], Line{Labels: []string{labelName}})
- } else {
- lines[len(lines)-1].Labels = append(lines[len(lines)-1].Labels, labelName)
- }
- } else if codeLine.MatchString(line) {
- asm := strings.Split(line, "#")[0]
- asm = strings.TrimSpace(asm)
- if labelName == "" {
- functions[functionName] = append(functions[functionName], Line{Assembly: asm})
- } else {
- lines := functions[functionName]
- lines[len(lines)-1].Assembly = asm
- labelName = ""
- }
- }
- }
- if err = scanner.Err(); err != nil {
- return nil, err
- }
- return functions, nil
- }
- func parseObjectDump(dump string, functions map[string][]Line) error {
- var (
- functionName string
- lineNumber int
- )
- for i, line := range strings.Split(dump, "\n") {
- line = strings.TrimSpace(line)
- if symbolLine.MatchString(line) {
- functionName = strings.Split(line, "<")[1]
- functionName = strings.Split(functionName, ">")[0]
- lineNumber = 0
- } else if dataLine.MatchString(line) {
- data := strings.Split(line, ":")[1]
- data = strings.TrimSpace(data)
- splits := strings.Split(data, " ")
- var (
- binary string
- assembly string
- )
- for i, s := range splits {
- if s == "" || unicode.IsSpace(rune(s[0])) {
- assembly = strings.Join(splits[i:], " ")
- assembly = strings.TrimSpace(assembly)
- break
- }
- binary = s
- }
- if lineNumber >= len(functions[functionName]) {
- return fmt.Errorf("%d: unexpected objectdump line: %s", i, line)
- }
- functions[functionName][lineNumber].Binary = binary
- lineNumber++
- }
- }
- return nil
- }
- func generateGoAssembly(path string, functions []Function) error {
- // generate code
- var builder strings.Builder
- builder.WriteString(buildTags)
- builder.WriteString("// AUTO-GENERATED BY GOAT -- DO NOT EDIT\n")
- for _, function := range functions {
- builder.WriteString(fmt.Sprintf("\nTEXT ·%v(SB), $0-32\n", function.Name))
- for i, param := range function.Parameters {
- builder.WriteString(fmt.Sprintf("\tMOVD %s+%d(FP), %s\n", param, i*8, registers[i]))
- }
- for _, line := range function.Lines {
- builder.WriteString(line.String())
- }
- }
- // write file
- f, err := os.Create(path)
- if err != nil {
- return err
- }
- defer func(f *os.File) {
- if err = f.Close(); err != nil {
- _, _ = fmt.Fprintln(os.Stderr, err)
- os.Exit(1)
- }
- }(f)
- bytes, err := asmfmt.Format(strings.NewReader(builder.String()))
- if err != nil {
- return err
- }
- _, err = f.Write(bytes)
- return err
- }
|