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

// 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 ( *Asm) ( string) *Func {
	,  := strings.CutPrefix(, "func ")
	if ! {
		.Fatalf("func decl does not begin with 'func '")
	}
	, ,  := strings.Cut(, "(")
	if ! {
		.Fatalf("func decl does not have func arg list")
	}
	 := &Func{
		Name: ,
		Asm:  ,
		args: make(map[string]int),
	}
	.FreeAll()

	// Parse argument names and types. Quick and dirty.
	// Convert (args) (results) into args, results.
	 = strings.ReplaceAll(, ") (", ", ")
	 = strings.TrimSuffix(, ")")
	 := strings.Split(, ",")

	// Assign implicit types to all arguments (x, y int -> x int, y int).
	 := ""
	for ,  := range slices.Backward() {
		 = strings.TrimSpace()
		if !strings.Contains(, " ") {
			if  == "" {
				.Fatalf("missing argument type")
			}
			 += " " + 
		} else {
			_, , _ = strings.Cut(, " ")
		}
		[] = 
	}

	// Record mapping from names to offsets.
	 := 0
	for ,  := range  {
		, ,  := strings.Cut(, " ")
		switch  {
		default:
			.Fatalf("unknown type %s", )
		case "Word", "uint", "int":
			.args[] = 
			 += .Arch.WordBytes
		case "[]Word":
			if strings.HasPrefix(, "z") {
				.outputs = append(.outputs, )
			} else {
				.inputs = append(.inputs, )
			}
			.args[+"_base"] = 
			.args[+"_len"] =  + .Arch.WordBytes
			.args[+"_cap"] =  + 2*.Arch.WordBytes
			 += 3 * .Arch.WordBytes
		}
	}

	.Printf("\n")
	.Printf("// %s\n", )
	.Printf("TEXT ยท%s(SB), NOSPLIT, $0\n", )
	if .Arch.setup != nil {
		.Arch.setup()
	}
	return 
}

// Arg allocates a new register, copies the named argument (or result) into it,
// and returns that register.
func ( *Func) ( string) Reg {
	return .ArgHint(, HintNone)
}

// ArgHint is like Arg but uses a register allocation hint.
func ( *Func) ( string,  Hint) Reg {
	,  := .args[]
	if ! {
		.Asm.Fatalf("unknown argument %s", )
	}
	 := Reg{fmt.Sprintf("%s+%d(FP)", , )}
	if  == HintMemOK && .Asm.Arch.memOK {
		return 
	}
	 := .Asm.RegHint()
	.Asm.Mov(, )
	return 
}

// ArgPtr is like Arg but returns a RegPtr.
func ( *Func) ( string) RegPtr {
	return RegPtr(.Arg())
}

// StoreArg stores src into the named argument (or result).
func ( *Func) ( Reg,  string) {
	,  := .args[]
	if ! {
		.Asm.Fatalf("unknown argument %s", )
	}
	 := .Asm
	 := Reg{fmt.Sprintf("%s+%d(FP)", , )}
	if .IsImm() && !.Arch.memOK {
		 := .Reg()
		.Mov(, )
		.Mov(, )
		.Free()
		return
	}
	.Mov(, )
}