func (v *CompileVisitor) Visit(n0 ast.Node) (w ast.Visitor) { // The following only handles functions (not methods) if n, ok := n0.(*ast.FuncDecl); ok && n.Recv == nil { v.FunctionPrologue(n) for _, statement := range n.Body.List { v.CompileStatement(statement) } v.FunctionPostlogue() v.Append(x86.GlobalSymbol("return_" + n.Name.Name)) v.Append(x86.Commented(x86.PopL(x86.EAX), "Pop the return address")) // Pop off function arguments... fmt.Println("Function", v.Stack.Name, "has stack size", v.Stack.Size) fmt.Println("Function", v.Stack.Name, "has return values size", v.Stack.ReturnSize) v.Append(x86.Commented(x86.AddL(x86.Imm32(v.Stack.Size-4-v.Stack.ReturnSize), x86.ESP), "Popping "+v.Stack.Name+" arguments.")) // Then we return! v.Append(x86.RawAssembly("\tjmp *%eax")) v.Stack = v.Stack.Parent return nil // No need to peek inside the func declaration! } return v }
func (v *CompileVisitor) FunctionPrologue(fn *ast.FuncDecl) { v.Stack = v.Stack.New(fn.Name.Name) ftype := ast.NewType(ast.Function) ftype.N = uint(fn.Type.Results.NumFields()) ftype.Params = ast.NewScope(nil) fmt.Println("Working on function", fn.Name.Name) if fn.Type.Results != nil { resultnum := 0 for _, resultfield := range fn.Type.Results.List { names := []string{"_"} if resultfield.Names != nil { names = []string{} for _, i := range resultfield.Names { names = append(names, i.Name) } } t := TypeExpression(resultfield.Type) for _, n := range names { ftype.Params.Insert(&ast.Object{ast.Fun, n, t, resultfield, 0}) resultnum++ v.Stack.DefineVariable(n, t, fmt.Sprintf("return_value_%d", resultnum)) // The return values are actually allocated elsewhere... here // we just need to define the function type properly so it // gets called properly. } } } fmt.Println("Stack size after results is", v.Stack.Size) v.Stack.ReturnSize = v.Stack.Size // The arguments are pushed last argument first, so that eventually // the types of the "later" arguments can depend on the first // arguments, which seems nice to me. for pi := len(fn.Type.Params.List) - 1; pi >= 0; pi-- { paramfield := fn.Type.Params.List[pi] names := []string{"_"} if paramfield.Names != nil { names = []string{} for _, i := range paramfield.Names { names = append(names, i.Name) } } t := TypeExpression(paramfield.Type) for i := len(names) - 1; i >= 0; i-- { n := names[i] ftype.Params.Insert(&ast.Object{ast.Fun, n, t, paramfield, 0}) v.Stack.DefineVariable(n, t) // The function parameters are actually allocated // elsewhere... here we just need to define the function type // properly so it gets called properly. } } fmt.Println("Stack size after params is", v.Stack.Size) v.Stack.DefineVariable("return", IntType) fmt.Println("Stack size after return is", v.Stack.Size) v.Stack = v.Stack.New("_") DefineGlobal(fn.Name.Name, ftype) // symbol for the start name pos := myfiles.Position(fn.Pos()) v.Append(x86.Commented(x86.GlobalSymbol("main_"+fn.Name.Name), fmt.Sprint(pos.Filename, ": line ", pos.Line))) // If we had arguments, we'd want to swap them with the return // address here... }