| // 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) |
| } |