diff options
| author | Mustafa Quraish <[email protected]> | 2022-02-05 20:28:56 -0500 |
|---|---|---|
| committer | Mustafa Quraish <[email protected]> | 2022-02-05 20:28:56 -0500 |
| commit | 4f99b1e1a4528d679b64915446663091ca09bb7a (patch) | |
| tree | 5033313c0cdca28fb7dd84abf9073ac664a0c305 | |
| parent | [compiler.cup] codegen for unary ops, short circuiting &&/|| (diff) | |
| download | cup-4f99b1e1a4528d679b64915446663091ca09bb7a.tar.xz cup-4f99b1e1a4528d679b64915446663091ca09bb7a.zip | |
[compiler.cup] Add support for function calls!
| -rw-r--r-- | compiler/codegen.cup | 24 | ||||
| -rw-r--r-- | compiler/parser.cup | 114 | ||||
| -rw-r--r-- | std/common.cup | 2 | ||||
| -rw-r--r-- | std/vector.cup | 2 |
4 files changed, 130 insertions, 12 deletions
diff --git a/compiler/codegen.cup b/compiler/codegen.cup index 407fab5..975844d 100644 --- a/compiler/codegen.cup +++ b/compiler/codegen.cup @@ -27,6 +27,11 @@ fn emit_asm(msg: char*) { } fn emit_num(num: int) { + // FIXME: Just support printing negatives directly. + if (num < 0) { + emit_asm("-"); + num = -num; + } fputu(gen_out_file, num); } @@ -35,6 +40,8 @@ fn generate_syscall(num: int) { emit_asm(" syscall\n"); } +fn generate_expr_into_rax(node: Node*); + fn generate_lvalue_into_rax(node: Node*) { if (node.typ == AST_LOCAL_VAR) { let offset = node.d.variable.offset; @@ -45,6 +52,20 @@ fn generate_lvalue_into_rax(node: Node*) { } } +fn generate_function_call(node: Node*) { + let total_size = 0; + let n = node.d.call.args.size; + for (let i = n-1; i >= 0; --i) { + let expr: Node* = node.d.call.args.data[i]; + generate_expr_into_rax(expr); + emit_asm(" push rax\n"); + // TODO: this might be an issue if we pass structs some day + total_size = total_size + 8; + } + emit_asm3(" call func_", node.d.call.func.d.func.name, "\n"); + emit_asm(" add rsp, "); emit_num(total_size); emit_asm("\n"); +} + fn generate_expr_into_rax(node: Node*) { if (node.typ == AST_LITERAL) { if (node.etyp.typ == TYPE_INT) { @@ -233,6 +254,9 @@ fn generate_expr_into_rax(node: Node*) { emit_asm(" pop rbx\n"); emit_asm(" mov [rbx], rax\n"); + } else if (node.typ == AST_FUNCCALL) { + generate_function_call(node); + } else { die2("Unsupported node type in generate_expr_into_rax: ", node_type_to_string(node.typ)); } diff --git a/compiler/parser.cup b/compiler/parser.cup index 4d4c67d..1335fba 100644 --- a/compiler/parser.cup +++ b/compiler/parser.cup @@ -2,7 +2,6 @@ import "compiler/ast.cup" import "compiler/lexer.cup" // p_ prefix for parser global variables. - let p_all_functions = vector_new(); let p_current_function: Node* = null; @@ -31,9 +30,34 @@ fn find_local_variable(token: Token*): Variable* { } } + let args = p_current_function.d.func.args; + for (let i = 0; i < args.size; ++i) { + let var: Variable* = args.data[i]; + if (streq(token.value.as_string, var.name)) { + return var; + } + } + + return null; +} + +fn find_function_definition(token: Token*): Node* { + for (let i = 0; i < p_all_functions.size; ++i) { + let func: Node* = p_all_functions.data[i]; + if (streq(token.value.as_string, func.d.func.name)) { + return func; + } + } return null; } +fn identifier_exists(token: Token*): int { + if (find_local_variable(token)) return true; + if (find_function_definition(token)) return true; + return false; +} + + fn parse_literal(lexer: Lexer*): Node* { let token: Token; lexer_next(lexer, &token); @@ -86,25 +110,59 @@ fn parse_type(lexer: Lexer*): Type* { return typ; } +fn parse_expression(lexer: Lexer*): Node*; + +fn parse_function_call_args(lexer: Lexer*, func: Node*): Node* { + let token: Token; + + let node = node_new(AST_FUNCCALL); + node.d.call.func = func; + node.d.call.args = vector_new(); + + lexer_next_assert(lexer, &token, TOKEN_OPEN_PAREN); + lexer_peek(lexer, &token); + + while (token.typ != TOKEN_CLOSE_PAREN) { + // TODO: Check types + let arg = parse_expression(lexer); + vector_push(node.d.call.args, arg); + + lexer_peek(lexer, &token); + + if (token.typ == TOKEN_COMMA) { + lexer_next(lexer, &token); + lexer_peek(lexer, &token); + } + } + lexer_next_assert(lexer, &token, TOKEN_CLOSE_PAREN); + + if (node.d.call.args.size != func.d.func.args.size) + die_loc2(&token.loc, "Function call argument count mismatch: ", func.d.func.name); + + return node; +} + fn parse_identifier(lexer: Lexer*): Node* { let token: Token; lexer_next_assert(lexer, &token, TOKEN_IDENTIFIER); - let expr: Node*; + let node: Node*; let var = find_local_variable(&token); if (var != null) { - expr = node_new(AST_LOCAL_VAR); - expr.d.variable = var; - expr.etyp = var.typ; - return expr; + node = node_new(AST_LOCAL_VAR); + node.d.variable = var; + node.etyp = var.typ; + return node; + } + + let func = find_function_definition(&token); + if (func != null) { + return parse_function_call_args(lexer, func); } die_loc2(&token.loc, "Unknown identifier in parse_identifier: ", token.value.as_string); } - -fn parse_expression(lexer: Lexer*): Node*; - fn parse_factor(lexer: Lexer*): Node* { let token: Token; let expr: Node*; @@ -385,9 +443,45 @@ fn parse_var_declaration(lexer: Lexer*): Node* { fn parse_function_params(lexer: Lexer*, func: Node*) { let token: Token; lexer_peek(lexer, &token); + + func.d.func.args = vector_new(); + // TODO: Actually parse params while (token.typ != TOKEN_CLOSE_PAREN) { - lexer_next(lexer, &token); + lexer_next_assert(lexer, &token, TOKEN_IDENTIFIER); + let name = token.value.as_string; + + if (identifier_exists(&token)) + die_loc2(&token.loc, "Identifier already defined: ", name); + + lexer_next_assert(lexer, &token, TOKEN_COLON); + let typ = parse_type(lexer); + + let var: Variable* = malloc(sizeof(Variable)); + var.name = name; + var.typ = typ; + vector_push(func.d.func.args, var); + + lexer_peek(lexer, &token); + if (token.typ == TOKEN_COMMA) { + lexer_next(lexer, &token); + lexer_peek(lexer, &token); + } + } + + // Set the offsets for the arguments + + // IMPORTANT: We want to skip the saved ret_addr+old_rbp that we + // pushed on the stack. Each of these is 8 bytes. + let offset = -16; + let n = func.d.func.args.size; + for (let i = 0; i < n; ++i) { + let var: Variable* = func.d.func.args.data[i]; + var.offset = offset; + // TODO: (Here and other uses of `size_for_type`): + // Should we only align to max(8, type->size) instead? + let var_size = align_up(size_for_type(var.typ), 8); + offset = offset - var_size; } } diff --git a/std/common.cup b/std/common.cup index c2d008e..3d0b269 100644 --- a/std/common.cup +++ b/std/common.cup @@ -214,7 +214,7 @@ fn factorial(n: int): int { } fn align_up(val: int, algn: int): int { - return 8; + return (val + algn - 1) & ~(algn - 1); } /////////////////////////////////////////////////////////////////////////////// diff --git a/std/vector.cup b/std/vector.cup index 6b42fdc..6953e9b 100644 --- a/std/vector.cup +++ b/std/vector.cup @@ -15,7 +15,7 @@ fn vector_new_sized(capacity: int): Vector* { } fn vector_new(): Vector* { - const initial_default_capacity = 4; + const initial_default_capacity = 8; return vector_new_sized(initial_default_capacity); } |