package core import ( "fmt" "git.akyoto.dev/cli/q/src/build/asm" "git.akyoto.dev/cli/q/src/build/errors" "git.akyoto.dev/cli/q/src/build/expression" ) // CompileCall executes a function call. // All call registers must hold the correct parameter values before the function invocation. // Registers that are in use must be saved if they are modified by the function. // After the function call, they must be restored in reverse order. func (f *Function) CompileCall(root *expression.Expression) error { var ( pkg = f.Package nameRoot = root.Children[0] name string fullName string ) if nameRoot.IsLeaf() { name = nameRoot.Token.Text(f.File.Bytes) } else { pkg = nameRoot.Children[0].Token.Text(f.File.Bytes) name = nameRoot.Children[1].Token.Text(f.File.Bytes) } isSyscall := name == "syscall" if !isSyscall { fullName = fmt.Sprintf("%s.%s", pkg, name) _, exists := f.Functions[fullName] if !exists { return errors.New(&errors.UnknownFunction{Name: name}, f.File, root.Children[0].Token.Position) } } parameters := root.Children[1:] registers := f.CPU.Input[:len(parameters)] if isSyscall { registers = f.CPU.Syscall[:len(parameters)] } err := f.ExpressionsToRegisters(parameters, registers) if err != nil { return err } f.SaveRegister(f.CPU.Output[0]) // Push for _, register := range f.CPU.General { if f.RegisterIsUsed(register) { f.Register(asm.PUSH, register) } } // Call if isSyscall { f.Syscall() } else { f.Call(fullName) } // Free parameter registers for _, register := range registers { if register == f.CPU.Output[0] && root.Parent != nil { continue } f.FreeRegister(register) } // Pop for i := len(f.CPU.General) - 1; i >= 0; i-- { register := f.CPU.General[i] if f.RegisterIsUsed(register) { f.Register(asm.POP, register) } } return nil }