func cgenFnCall(node *Node, ops *x64.OpcodeList) { // Check function fn, ok := node.sym.(*Function) if !ok { panic(fmt.Sprintf("Unknown symbol: %v, node: %v", node.sym, node)) } switch fn.fnName { default: ops.CALL(fn.Rva()) case printlnFuncName: cgenPrintCall(node, ops) } }
func cgenPrintCall(node *Node, ops *x64.OpcodeList) { // Get symbol fn, ok := node.sym.(*Function) if !ok { panic(fmt.Sprintf("Unknown symbol: %v", node.sym)) } // Push the base/frame pointer register value onto stack and overwrite base/frame pointer register to point to // the top of stack. This stack pointer can now be modified by us and restored at the end of the function to its // previous state // TODO: commented out as we must inline this call // ops.PUSH(x64.Rbp) // ops.MOV(x64.Rbp, x64.Rsp) // Push values onto stack (string RVA & length) str, ok := node.stats[0].sym.(*StringLiteralSymbol) if !ok { panic(fmt.Sprintf("print function parameter not string literal! - %v", str)) } ops.PUSHI(str.Rva()) ops.PUSHI(uint32(len(str.Val()))) // Call print ops.CALL(fn.Rva()) // Clean up stack by overwriting the stack pointer register with the base/frame pointer register. This discards // the two values we pushed onto the stack. Then pop the old base/frame pointer value off the stack & back into // the base/frame pointer register. This restores the caller's stack. // TODO: on x64 we should use 'leave' instruction // ops.MOV(x64.Rsp, x64.Rbp) // ops.POP(x64.Rbp) // ops.RET() }
func cgenFnDecl(node *Node, imports ImportList, ops *x64.OpcodeList) { // Get function & set RVA fn, ok := node.sym.(*Function) if !ok { panic(fmt.Sprintf("Unknown symbol: %v, node: %v", node.sym, node.token)) } fn.rva = uint32(ops.Rva()) ops.PUSH(x64.Rbp) ops.MOV(x64.Rbp, x64.Rsp) // Generate calls for all functions for _, n := range node.stats { cgenFnCall(n, ops) } // Check if we are main - we need to exit! if node.token.Val == "main" { ops.MOVI(x64.Rcx, 0) ops.CALLPTR(imports.funcRva("ExitProcess")) } else { // TODO: When local variables added must update this! ops.MOV(x64.Rsp, x64.Rbp) ops.POP(x64.Rbp) ops.RET() } }
func cgenPrintlnDecl(node *Node, imports ImportList, ops *x64.OpcodeList) { // Get function & set RVA fn, ok := node.sym.(*Function) if !ok { panic(fmt.Sprintf("Unknown symbol: %v", node.sym)) } fn.rva = uint32(ops.Rva()) ops.PUSH(x64.Rbp) ops.MOV(x64.Rbp, x64.Rsp) // Get the output handle ops.MOVI(x64.Rcx, STD_OUTPUT_HANDLE) ops.CALLPTR(imports.funcRva("GetStdHandle")) // Write to console // See [https://msdn.microsoft.com/en-us/library/ms235286.aspx] for more information ops.MOV(x64.Rcx, x64.Rax) // Copy output handle result from AX into register ops.MOVM(x64.Rdx, x64.Rbp, 24) // Move string literal RVA into register ops.MOVM(x64.R8, x64.Rbp, 16) // Move string length into register ops.MOVI(x64.R9, 0) // TODO: This should be a local variable on the stack! ops.PUSHI(0) ops.CALLPTR(imports.funcRva("WriteConsoleA")) // Clean stack ops.MOV(x64.Rsp, x64.Rbp) ops.POP(x64.Rbp) ops.RET() }