-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfunc.go
138 lines (125 loc) · 3.32 KB
/
func.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package asmgen
import (
"fmt"
"slices"
"strings"
)
// Note: Exported fields and methods are expected to be used
// by function generators (like the ones in add.go and so on).
// Unexported fields and methods should not be.
// A Func represents a single assembly function.
type Func struct {
Name string
Asm *Asm
inputs []string // name of input slices (not beginning with z)
outputs []string // names of output slices (beginning with z)
args map[string]int // offsets of args, results on stack
}
// Func starts a new function in the assembly output.
func (a *Asm) Func(decl string) *Func {
d, ok := strings.CutPrefix(decl, "func ")
if !ok {
a.Fatalf("func decl does not begin with 'func '")
}
name, d, ok := strings.Cut(d, "(")
if !ok {
a.Fatalf("func decl does not have func arg list")
}
f := &Func{
Name: name,
Asm: a,
args: make(map[string]int),
}
a.FreeAll()
// Parse argument names and types. Quick and dirty.
// Convert (args) (results) into args, results.
d = strings.ReplaceAll(d, ") (", ", ")
d = strings.TrimSuffix(d, ")")
args := strings.Split(d, ",")
// Assign implicit types to all arguments (x, y int -> x int, y int).
typ := ""
for i, arg := range slices.Backward(args) {
arg = strings.TrimSpace(arg)
if !strings.Contains(arg, " ") {
if typ == "" {
a.Fatalf("missing argument type")
}
arg += " " + typ
} else {
_, typ, _ = strings.Cut(arg, " ")
}
args[i] = arg
}
// Record mapping from names to offsets.
off := 0
for _, arg := range args {
name, typ, _ := strings.Cut(arg, " ")
switch typ {
default:
a.Fatalf("unknown type %s", typ)
case "Word", "uint", "int":
f.args[name] = off
off += a.Arch.WordBytes
case "[]Word":
if strings.HasPrefix(name, "z") {
f.outputs = append(f.outputs, name)
} else {
f.inputs = append(f.inputs, name)
}
f.args[name+"_base"] = off
f.args[name+"_len"] = off + a.Arch.WordBytes
f.args[name+"_cap"] = off + 2*a.Arch.WordBytes
off += 3 * a.Arch.WordBytes
}
}
a.Printf("\n")
a.Printf("// %s\n", decl)
a.Printf("TEXT ·%s(SB), NOSPLIT, $0\n", name)
if a.Arch.setup != nil {
a.Arch.setup(f)
}
return f
}
// Arg allocates a new register, copies the named argument (or result) into it,
// and returns that register.
func (f *Func) Arg(name string) Reg {
return f.ArgHint(name, HintNone)
}
// ArgHint is like Arg but uses a register allocation hint.
func (f *Func) ArgHint(name string, hint Hint) Reg {
off, ok := f.args[name]
if !ok {
f.Asm.Fatalf("unknown argument %s", name)
}
mem := Reg{fmt.Sprintf("%s+%d(FP)", name, off)}
if hint == HintMemOK && f.Asm.Arch.memOK {
return mem
}
r := f.Asm.RegHint(hint)
f.Asm.Mov(mem, r)
return r
}
// ArgPtr is like Arg but returns a RegPtr.
func (f *Func) ArgPtr(name string) RegPtr {
return RegPtr(f.Arg(name))
}
// StoreArg stores src into the named argument (or result).
func (f *Func) StoreArg(src Reg, name string) {
off, ok := f.args[name]
if !ok {
f.Asm.Fatalf("unknown argument %s", name)
}
a := f.Asm
mem := Reg{fmt.Sprintf("%s+%d(FP)", name, off)}
if src.IsImm() && !a.Arch.memOK {
r := a.Reg()
a.Mov(src, r)
a.Mov(r, mem)
a.Free(r)
return
}
a.Mov(src, mem)
}