Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 41 additions & 10 deletions internal/amd64/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,32 @@ import (
var (
attributeLine = regexp.MustCompile(`^\s+\..+$`)
nameLine = regexp.MustCompile(`^\w+:.*$`)
dataNameLine = 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+.+$`)
leaqRIPLine = regexp.MustCompile(`^leaq\s+([A-Za-z_][A-Za-z0-9_]*)\(%rip\), %([a-z0-9]+)$`)
symbolLine = regexp.MustCompile(`^\w+\s+<\w+>:$`)
dataLine = regexp.MustCompile(`^\w+:\s+\w+\s+.+$`)
leaqRIPLine = regexp.MustCompile(`^leaq\s+([.A-Za-z_$][A-Za-z0-9_.$]*)\(%rip\), %([a-z0-9]+)(?:\s+#.*)?$`)
ripRelativeDataLoadLine = regexp.MustCompile(`^([a-z0-9]+)\s+(\.[A-Za-z0-9_.$]+)\(%rip\), %([a-z0-9]+)(?:\s+#.*)?$`)

registers = []string{"DI", "SI", "DX", "CX", "R8", "R9"}
xmmRegisters = []string{"X0", "X1", "X2", "X3", "X4", "X5", "X6", "X7"}
dataSymbols []internal.DataSymbol
)

func amd64Register(reg string) string {
switch strings.TrimPrefix(reg, "%") {
reg = strings.TrimPrefix(reg, "%")
if strings.HasPrefix(reg, "xmm") {
return "X" + strings.TrimPrefix(reg, "xmm")
}
if strings.HasPrefix(reg, "ymm") {
return "Y" + strings.TrimPrefix(reg, "ymm")
}
if strings.HasPrefix(reg, "zmm") {
return "Z" + strings.TrimPrefix(reg, "zmm")
}
switch reg {
case "rax", "eax", "ax", "al":
return "AX"
case "rbx", "ebx", "bx", "bl":
Expand Down Expand Up @@ -79,10 +91,14 @@ func amd64Register(reg string) string {
case "r15", "r15d", "r15w", "r15b":
return "R15"
default:
return strings.ToUpper(strings.TrimPrefix(reg, "%"))
return strings.ToUpper(reg)
}
}

func dataSymbolName(name string) string {
return internal.GoDataSymbolName(name)
}

func init() {
internal.RegisterTarget("amd64", internal.Target{
GOARCH: "amd64",
Expand All @@ -104,7 +120,10 @@ func generateLine(line internal.Line) string {
operand := splits[1]
builder.WriteString(fmt.Sprintf("%s %s", strings.ToUpper(op), operand))
} else if matches := leaqRIPLine.FindStringSubmatch(line.Assembly); matches != nil {
builder.WriteString(fmt.Sprintf("LEAQ %s<>(SB), %s", matches[1], amd64Register(matches[2])))
builder.WriteString(fmt.Sprintf("LEAQ %s<>(SB), %s", dataSymbolName(matches[1]), amd64Register(matches[2])))
} else if matches := ripRelativeDataLoadLine.FindStringSubmatch(line.Assembly); matches != nil {
builder.WriteString(fmt.Sprintf("%s %s<>(SB), %s",
strings.ToUpper(matches[1]), dataSymbolName(matches[2]), amd64Register(matches[3])))
} else {
pos := 0
for pos < len(line.Binary) {
Expand Down Expand Up @@ -161,15 +180,27 @@ func parseAssembly(path string) (map[string][]internal.Line, map[string]int, err
line := scanner.Text()
trimmed := strings.TrimSpace(line)
if strings.HasPrefix(trimmed, ".section") {
dataSection = strings.Contains(trimmed, ".rodata") || strings.Contains(trimmed, ".data")
dataSection = strings.Contains(trimmed, ".rodata") || strings.Contains(trimmed, ".rdata") || strings.Contains(trimmed, ".data") || strings.Contains(trimmed, ".sdata")
if !dataSection {
dataName = ""
}
} else if trimmed == ".text" {
dataSection = false
dataName = ""
}
if parsed, ok, err := internal.ParseDataDirective(line); err != nil {
if dataSection && dataNameLine.MatchString(line) {
name, _, _ := strings.Cut(line, ":")
dataName = dataSymbolName(name)
continue
}
if parsed, ok, err := internal.ParseDataDirective(line, binary.LittleEndian); err != nil {
return nil, nil, err
} else if ok && dataName != "" {
data = append(data, internal.DataSymbol{Name: dataName, Data: parsed})
dataName = ""
if len(data) > 0 && data[len(data)-1].Name == dataName {
data[len(data)-1].Data = append(data[len(data)-1].Data, parsed...)
} else {
data = append(data, internal.DataSymbol{Name: dataName, Data: parsed})
}
} else if attributeLine.MatchString(line) {
continue
} else if nameLine.MatchString(line) {
Expand Down
32 changes: 25 additions & 7 deletions internal/arm64/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,25 @@ import (
var (
attributeLine = regexp.MustCompile(`^\s+\..+$`)
nameLine = regexp.MustCompile(`^\w+:.*$`)
dataNameLine = regexp.MustCompile(`^[.\w$]+:.*$`)
labelLine = regexp.MustCompile(`^\.\w+_\d+:.*$`)
codeLine = regexp.MustCompile(`^\s+\w+.+$`)
jmpLine = regexp.MustCompile(`^(b|b\.\w{2})\t\.\w+_\d+$`)

symbolLine = regexp.MustCompile(`^\w+\s+<\w+>:$`)
dataLine = regexp.MustCompile(`^\w+:\s+\w+\s+.+$`)
adrpLine = regexp.MustCompile(`^adrp\s+x([0-9]+), ([A-Za-z_][A-Za-z0-9_]*)$`)
lo12Line = regexp.MustCompile(`^add\s+x([0-9]+), x([0-9]+), :lo12:([A-Za-z_][A-Za-z0-9_]*)$`)
adrpLine = regexp.MustCompile(`^adrp\s+x([0-9]+), ([.A-Za-z_][.A-Za-z0-9_$]*)$`)
lo12Line = regexp.MustCompile(`^add\s+x([0-9]+), x([0-9]+), :lo12:([.A-Za-z_][.A-Za-z0-9_$]*)$`)

registers = []string{"R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7"}
fpRegisters = []string{"F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7"}
dataSymbols []internal.DataSymbol
)

func dataSymbolName(name string) string {
return internal.GoDataSymbolName(name)
}

func init() {
internal.RegisterTarget("arm64", internal.Target{
GOARCH: "arm64",
Expand Down Expand Up @@ -71,7 +76,7 @@ func generateLine(line internal.Line) string {
label := splits[1][1:]
builder.WriteString(fmt.Sprintf("%s %s\n", instruction, label))
} else if matches := adrpLine.FindStringSubmatch(line.Assembly); matches != nil {
builder.WriteString(fmt.Sprintf("\tMOVD $%s<>(SB), R%s\n", matches[2], matches[1]))
builder.WriteString(fmt.Sprintf(" MOVD $%s<>(SB), R%s\n", dataSymbolName(matches[2]), matches[1]))
} else if lo12Line.MatchString(line.Assembly) {
// The preceding ADRP is rewritten to load the full Go symbol address.
} else {
Expand Down Expand Up @@ -110,15 +115,27 @@ func parseAssembly(path string) (map[string][]internal.Line, map[string]int, err
line := scanner.Text()
trimmed := strings.TrimSpace(line)
if strings.HasPrefix(trimmed, ".section") {
dataSection = strings.Contains(trimmed, ".rodata") || strings.Contains(trimmed, ".data")
dataSection = strings.Contains(trimmed, ".rodata") || strings.Contains(trimmed, ".data") || strings.Contains(trimmed, ".sdata")
if !dataSection {
dataName = ""
}
} else if trimmed == ".text" {
dataSection = false
dataName = ""
}
if parsed, ok, err := internal.ParseDataDirective(line); err != nil {
if dataSection && dataNameLine.MatchString(line) {
name, _, _ := strings.Cut(line, ":")
dataName = dataSymbolName(name)
continue
}
if parsed, ok, err := internal.ParseDataDirective(line, binary.LittleEndian); err != nil {
return nil, nil, err
} else if ok && dataName != "" {
data = append(data, internal.DataSymbol{Name: dataName, Data: parsed})
dataName = ""
if len(data) > 0 && data[len(data)-1].Name == dataName {
data[len(data)-1].Data = append(data[len(data)-1].Data, parsed...)
} else {
data = append(data, internal.DataSymbol{Name: dataName, Data: parsed})
}
} else if attributeLine.MatchString(line) {
continue
} else if nameLine.MatchString(line) {
Expand All @@ -131,6 +148,7 @@ func parseAssembly(path string) (map[string][]internal.Line, map[string]int, err
} else {
functionName = name
functions[functionName] = make([]internal.Line, 0)
labelName = ""
}
} else if labelLine.MatchString(line) {
labelName = strings.Split(line, ":")[0]
Expand Down
65 changes: 64 additions & 1 deletion internal/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ package internal

import (
"encoding/binary"
"encoding/hex"
"fmt"
"strconv"
"strings"
Expand All @@ -27,10 +28,72 @@ type DataSymbol struct {
Data []byte
}

const escapedDataSymbolPrefix = "__goat_data_"

// GoDataSymbolName converts an assembler data symbol into a valid, unique Go
// assembler symbol while preserving ordinary C identifiers for readability.
func GoDataSymbolName(name string) string {
if !strings.ContainsAny(name, ".$") && !strings.HasPrefix(name, escapedDataSymbolPrefix) {
return name
}
return escapedDataSymbolPrefix + hex.EncodeToString([]byte(name))
}

// ParseDataDirective parses data directives that embed literal bytes in clang
// assembly output.
func ParseDataDirective(line string) ([]byte, bool, error) {
func ParseDataDirective(line string, byteOrder binary.ByteOrder) ([]byte, bool, error) {
line = strings.TrimSpace(line)
fields := strings.Fields(line)
if len(fields) > 0 {
var size int
switch fields[0] {
case ".byte":
size = 1
case ".short":
size = 2
case ".long":
size = 4
case ".quad", ".xword", ".dword":
size = 8
}
if size > 0 {
values := strings.TrimSpace(strings.TrimPrefix(line, fields[0]))
values, _, _ = strings.Cut(values, "#")
values, _, _ = strings.Cut(values, "//")
if values == "" {
return nil, false, fmt.Errorf("invalid %s directive: %s", fields[0], line)
}
var data []byte
for _, value := range strings.Split(values, ",") {
value = strings.TrimSpace(value)
var parsed uint64
var err error
if strings.HasPrefix(value, "-") {
var signed int64
signed, err = strconv.ParseInt(value, 0, size*8)
parsed = uint64(signed)
} else {
parsed, err = strconv.ParseUint(value, 0, size*8)
}
if err != nil {
return nil, false, nil
}
encoded := make([]byte, size)
switch size {
case 1:
encoded[0] = byte(parsed)
case 2:
byteOrder.PutUint16(encoded, uint16(parsed))
case 4:
byteOrder.PutUint32(encoded, uint32(parsed))
case 8:
byteOrder.PutUint64(encoded, parsed)
}
data = append(data, encoded...)
}
return data, true, nil
}
}
if strings.HasPrefix(line, ".ascii") || strings.HasPrefix(line, ".asciz") {
parts := strings.Fields(line)
if len(parts) < 2 {
Expand Down
32 changes: 25 additions & 7 deletions internal/loong64/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,14 @@ import (
var (
attributeLine = regexp.MustCompile(`^\s+\..+$`)
nameLine = regexp.MustCompile(`^\w+:.*$`)
dataNameLine = 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+.+$`)
pcHiLine = regexp.MustCompile(`^pcalau12i\s+(\$[a-z0-9]+), %pc_hi20\(([A-Za-z_][A-Za-z0-9_]*)\)$`)
pcLoLine = regexp.MustCompile(`^addi\.d\s+(\$[a-z0-9]+), (\$[a-z0-9]+), %pc_lo12\(([A-Za-z_][A-Za-z0-9_]*)\)$`)
pcHiLine = regexp.MustCompile(`^pcalau12i\s+(\$[a-z0-9]+), %pc_hi20\(([.A-Za-z_][.A-Za-z0-9_$]*)\)$`)
pcLoLine = regexp.MustCompile(`^addi\.d\s+(\$[a-z0-9]+), (\$[a-z0-9]+), %pc_lo12\(([.A-Za-z_][.A-Za-z0-9_$]*)\)$`)

registers = []string{"R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11"}
fpRegisters = []string{"F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7"}
Expand Down Expand Up @@ -83,6 +84,10 @@ var (
dataSymbols []internal.DataSymbol
)

func dataSymbolName(name string) string {
return internal.GoDataSymbolName(name)
}

func init() {
internal.RegisterTarget("loong64", internal.Target{
GOARCH: "loong64",
Expand All @@ -102,7 +107,7 @@ func generateLine(line internal.Line) string {
_, _ = fmt.Fprintln(os.Stderr, "unexpected register alias:", matches[1])
os.Exit(1)
} else {
builder.WriteString(fmt.Sprintf("MOVV $%s<>(SB), %s", matches[2], r))
builder.WriteString(fmt.Sprintf("MOVV $%s<>(SB), %s", dataSymbolName(matches[2]), r))
}
} else if pcLoLine.MatchString(line.Assembly) {
// The preceding PCALAU12I is rewritten to load the full Go symbol address.
Expand Down Expand Up @@ -227,15 +232,27 @@ func parseAssembly(path string) (map[string][]internal.Line, map[string]int, err
line := scanner.Text()
trimmed := strings.TrimSpace(line)
if strings.HasPrefix(trimmed, ".section") {
dataSection = strings.Contains(trimmed, ".rodata") || strings.Contains(trimmed, ".data")
dataSection = strings.Contains(trimmed, ".rodata") || strings.Contains(trimmed, ".data") || strings.Contains(trimmed, ".sdata")
if !dataSection {
dataName = ""
}
} else if trimmed == ".text" {
dataSection = false
dataName = ""
}
if parsed, ok, err := internal.ParseDataDirective(line); err != nil {
if dataSection && dataNameLine.MatchString(line) {
name, _, _ := strings.Cut(line, ":")
dataName = dataSymbolName(name)
continue
}
if parsed, ok, err := internal.ParseDataDirective(line, binary.LittleEndian); err != nil {
return nil, nil, err
} else if ok && dataName != "" {
data = append(data, internal.DataSymbol{Name: dataName, Data: parsed})
dataName = ""
if len(data) > 0 && data[len(data)-1].Name == dataName {
data[len(data)-1].Data = append(data[len(data)-1].Data, parsed...)
} else {
data = append(data, internal.DataSymbol{Name: dataName, Data: parsed})
}
} else if attributeLine.MatchString(line) {
continue
} else if nameLine.MatchString(line) {
Expand All @@ -248,6 +265,7 @@ func parseAssembly(path string) (map[string][]internal.Line, map[string]int, err
} else {
functionName = name
functions[functionName] = make([]internal.Line, 0)
labelName = ""
}
} else if labelLine.MatchString(line) {
labelName = strings.Split(line, ":")[0]
Expand Down
Loading
Loading