#include void generate_stringtable ( void ); void generate_global_variables ( void ); void generate_function ( symbol_t *function ); static void generate_node ( node_t *node ); void generate_main ( symbol_t *first ); static void generate_function_call ( node_t *call ); #define MIN(a,b) (((a)<(b)) ? (a):(b)) static const char *record[6] = { "%rdi", "%rsi", "%rdx", "%rcx", "%r8", "%r9" }; static symbol_t *current_function = NULL; void generate_program ( void ) { size_t n_globals = tlhash_size(global_names); symbol_t *global_list[n_globals]; tlhash_values ( global_names, (void **)&global_list ); symbol_t *first_function; for ( size_t i=0; itype == SYM_FUNCTION ) { // Allows the use of main as name to override entry point if (!strcmp(global_list[i]->name, "main")) { first_function = global_list[i]; break; } else if (global_list[i]->seq == 0) { first_function = global_list[i]; } } generate_stringtable(); generate_global_variables(); generate_main ( first_function ); for ( size_t i=0; itype == SYM_FUNCTION ) generate_function ( global_list[i] ); } void generate_stringtable ( void ) { puts ( ".section .rodata" ); puts ( ".intout: .string \"\%ld \"" ); puts ( ".strout: .string \"\%s \"" ); puts ( ".errout: .string \"Wrong number of arguments\"" ); for ( size_t s=0; stype == SYM_GLOBAL_VAR ) printf ( "._%s: .zero 8\n", syms[n]->name ); } } void generate_main ( symbol_t *first ) { puts ( ".globl main" ); puts ( ".section .text" ); puts ( "main:" ); puts ( "\tpushq %rbp" ); puts ( "\tmovq %rsp, %rbp" ); printf ( "\tsubq\t$1,%%rdi\n" ); printf ( "\tcmpq\t$%zu,%%rdi\n", first->nparms ); printf ( "\tjne\tABORT\n" ); printf ( "\tcmpq\t$0,%%rdi\n" ); printf ( "\tjz\tSKIP_ARGS\n" ); printf ( "\tmovq\t%%rdi,%%rcx\n" ); printf ( "\taddq $%zu, %%rsi\n", 8*first->nparms ); printf ( "PARSE_ARGV:\n" ); printf ( "\tpushq %%rcx\n" ); printf ( "\tpushq %%rsi\n" ); printf ( "\tmovq\t(%%rsi),%%rdi\n" ); printf ( "\tmovq\t$0,%%rsi\n" ); printf ( "\tmovq\t$10,%%rdx\n" ); printf ( "\tcall\tstrtol\n" ); /* Now a new argument is an integer in rax */ printf ( "\tpopq %%rsi\n" ); printf ( "\tpopq %%rcx\n" ); printf ( "\tpushq %%rax\n" ); printf ( "\tsubq $8, %%rsi\n" ); printf ( "\tloop PARSE_ARGV\n" ); /* Now the arguments are in order on stack */ for ( int arg=0; argnparms); arg++ ) printf ( "\tpopq\t%s\n", record[arg] ); printf ( "SKIP_ARGS:\n" ); printf ( "\tcall\t_%s\n", first->name ); printf ( "\tjmp\tEND\n" ); printf ( "ABORT:\n" ); printf ( "\tmovq\t$.errout, %%rdi\n" ); printf ( "\tcall puts\n" ); printf ( "END:\n" ); puts ( "\tmovq %rax, %rdi" ); puts ( "\tcall exit" ); } static void generate_identifier ( node_t *ident ) { symbol_t *symbol = ident->entry; int64_t argument_offset; switch ( symbol->type ) { case SYM_GLOBAL_VAR: /* Global variables called by name */ printf ( "._%s", symbol->name ); break; case SYM_PARAMETER: if ( symbol->seq > 5 ) /* Extra parameters pushed in decreasing order */ printf ( "%ld(%%rbp)", 8+8*(symbol->seq-5) ); else /* First six parameters directly after base poiter */ printf ( "%ld(%%rbp)", -8*(symbol->seq+1) ); break; case SYM_LOCAL_VAR: /* Local variables places after parameters in stack */ argument_offset = -8*MIN(6,current_function->nparms); printf ( "%ld(%%rbp)", -8*(symbol->seq+1) + argument_offset ); break; } } static void generate_expression ( node_t *expr ) { if ( expr->type == IDENTIFIER_DATA ) { printf ( "\tmovq\t" ); generate_identifier ( expr ); printf ( ", %%rax\n" ); } else if ( expr->type == NUMBER_DATA ) { printf ( "\tmovq\t$%ld, %%rax\n", *(int64_t *)expr->data ); } else if ( expr->n_children == 1 ) { switch ( *((char*)(expr->data)) ) { case '-': generate_expression ( expr->children[0] ); printf ( "\tnegq\t%%rax\n" ); break; case '~': generate_expression ( expr->children[0] ); printf ( "\tnotq\t%%rax\n" ); break; } } else if ( expr->n_children == 2 ) { if ( expr->data != NULL ) { switch ( *((char *)expr->data) ) { case '+': generate_expression ( expr->children[0] ); printf ( "\tpushq\t%%rax\n" ); generate_expression ( expr->children[1] ); printf ( "\taddq\t%%rax, (%%rsp)\n" ); printf ( "\tpopq\t%%rax\n" ); break; case '-': generate_expression ( expr->children[0] ); printf ( "\tpushq\t%%rax\n" ); generate_expression ( expr->children[1] ); printf ( "\tsubq\t%%rax, (%%rsp)\n" ); printf ( "\tpopq\t%%rax\n" ); break; case '*': printf ( "\tpushq\t%%rdx\n" ); generate_expression ( expr->children[1] ); printf ( "\tpushq\t%%rax\n" ); generate_expression ( expr->children[0] ); printf ( "\tmulq\t(%%rsp)\n" ); printf ( "\tpopq\t%%rdx\n" ); printf ( "\tpopq\t%%rdx\n" ); break; case '/': printf ( "\tpushq\t%%rdx\n" ); generate_expression ( expr->children[1] ); printf ( "\tpushq\t%%rax\n" ); generate_expression ( expr->children[0] ); printf ( "\tcqo\n" ); printf ( "\tidivq\t(%%rsp)\n" ); printf ( "\tpopq\t%%rdx\n" ); printf ( "\tpopq\t%%rdx\n" ); break; case '|': generate_expression ( expr->children[0] ); printf ( "\tpushq\t%%rax\n" ); generate_expression ( expr->children[1] ); printf ( "\torq\t%%rax, (%%rsp)\n" ); printf ( "\tpopq\t%%rax\n" ); break; case '^': generate_expression ( expr->children[0] ); printf ( "\tpushq\t%%rax\n" ); generate_expression ( expr->children[1] ); printf ( "\txorq\t%%rax, (%%rsp)\n" ); printf ( "\tpopq\t%%rax\n" ); break; case '&': generate_expression ( expr->children[0] ); printf ( "\tpushq\t%%rax\n" ); generate_expression ( expr->children[1] ); printf ( "\tandq\t%%rax, (%%rsp)\n" ); printf ( "\tpopq\t%%rax\n" ); break; } } else { generate_function_call ( expr ); } } } static void generate_function_call ( node_t *call ) { /* Check function call */ size_t n_arguments = 0; if ( call->children[1] != NULL ) n_arguments = call->children[1]->n_children; symbol_t *function = call->children[0]->entry; if ( n_arguments != function->nparms ) { fprintf ( stderr, "Function %s has %zu parameters, called with %zu arguments\n", (char *) call->children[0]->data, (size_t) call->children[0]->entry->nparms, n_arguments ); exit ( EXIT_FAILURE ); } /* Generate function call: */ /* Push all the arguments */ node_t *arglist = call->children[1]; if ( arglist != NULL ) { for ( size_t p=arglist->n_children; p>0; p-- ) { generate_expression ( arglist->children[(p-1)] ); if ( (p-1)>5 ) printf ( "\tpushq\t%%rax\n" ); else printf ( "\tmovq\t%%rax, %s\n", record[(p-1)] ); } } /* Call the function */ printf ( "\tcall _%s\n", (char *)call->children[0]->data ); } static void generate_assignment_statement ( node_t *statement ) { switch ( statement->type ) { case ASSIGNMENT_STATEMENT: generate_expression ( statement->children[1] ); printf ( "\tmovq\t%%rax, " ); generate_identifier ( statement->children[0] ); printf ( "\n" ); break; case ADD_STATEMENT: generate_expression ( statement->children[1] ); printf ( "\taddq\t%%rax, " ); generate_identifier ( statement->children[0] ); printf ( "\n" ); break; case SUBTRACT_STATEMENT: generate_expression ( statement->children[1] ); printf ( "\tsubq\t%%rax, " ); generate_identifier ( statement->children[0] ); printf ( "\n" ); break; case MULTIPLY_STATEMENT: generate_expression ( statement->children[1] ); printf ( "\tmulq\t " ); generate_identifier ( statement->children[0] ); printf ( "\n" ); printf ( "\tmovq\t%%rax, " ); generate_identifier ( statement->children[0] ); printf ( "\n" ); break; case DIVIDE_STATEMENT: generate_expression ( statement->children[1] ); printf ( "\txchgq\t%%rax, " ); generate_identifier ( statement->children[0] ); printf ( "\n" ); printf ( "\tcqo\n" ); printf ( "\tidivq\t" ); generate_identifier ( statement->children[0] ); printf ( "\n" ); printf ( "\txchgq\t%%rax, " ); generate_identifier ( statement->children[0] ); printf ( "\n" ); break; } } static void generate_print_statement ( node_t *statement ) { for ( size_t i=0; in_children; i++ ) { node_t *item = statement->children[i]; switch ( item->type ) { case STRING_DATA: printf ( "\tmovq\t$.STR%zu, %%rsi\n", *((size_t *)item->data) ); printf ( "\tmovq\t$.strout, %%rdi\n" ); break; case NUMBER_DATA: printf ("\tmovq\t$%ld, %%rsi\n", *((int64_t *)item->data) ); printf ( "\tmovq\t$.intout, %%rdi\n" ); break; case IDENTIFIER_DATA: printf ( "\tmovq\t" ); generate_identifier ( item ); printf ( ", %%rsi\n" ); printf ( "\tmovq\t$.intout, %%rdi\n" ); break; case EXPRESSION: generate_expression ( item ); printf ( "\tmovq\t%%rax, %%rsi\n" ); printf ( "\tmovq\t$.intout, %%rdi\n" ); break; } puts ( "\tmovq\t$0, %rax\n" // Clear rax to indicate not to use SSE instructions "\tcall\tprintf" ); } printf ( "\tmovq\t$0x0A, %%rdi\n" ); // Finish statement by inserting a newline puts ( "\tcall\tputchar" ); } static void generate_if_statement ( node_t *statement ) { // TODO: Handle if statement // statement->nodetype = IF_STATEMENT } static void generate_while_statement ( node_t *statement ) { // TODO: Handle while statement // statement->nodetype = WHILE_STATEMENT } static void generate_node ( node_t *node ) { switch (node->type) { case PRINT_STATEMENT: generate_print_statement ( node ); break; case ASSIGNMENT_STATEMENT: case ADD_STATEMENT: case SUBTRACT_STATEMENT: case MULTIPLY_STATEMENT: case DIVIDE_STATEMENT: generate_assignment_statement ( node ); break; case RETURN_STATEMENT: generate_expression ( node->children[0] ); printf ( "\tleave\n" ); printf ( "\tret\n" ); break; case IF_STATEMENT: // TODO: Implement break; case WHILE_STATEMENT: // TODO: Implement break; case NULL_STATEMENT: // TODO: Implement break; default: for ( size_t i=0; in_children; i++ ) generate_node ( node->children[i] ); break; } } void generate_function ( symbol_t *function ) { current_function = function; printf ( "_%s:\n", function->name ); puts ( "\tpushq %rbp" ); puts ( "\tmovq %rsp, %rbp" ); /* Save arguments in local stack frame */ for ( size_t arg=1; arg<=MIN(6,function->nparms); arg++ ) printf ( "\tpushq\t%s\n", record[arg-1] ); /* Make space for locals in local stack frame */ size_t local_vars = tlhash_size(function->locals) - function->nparms; if ( local_vars > 0 ) printf ( "\tsubq $%zu, %%rsp\n", 8*local_vars ); if ( (tlhash_size(function->locals)&1) == 1 ) puts ( "\tpushq\t$0 /* Stack padding for 16-byte alignment */" ); generate_node ( function->node ); printf( "\tmovq\t%%rbp, %%rsp\n" // movq %rbp, %rsp // restore stack pointer "\tmovq\t$0, %%rax\n" // movq $0, %rax // return 0 if nothing else "\tpopq\t%%rbp\n" // popq %rbp // restore base pointer "\tret\n"); // ret current_function = NULL; }