455 lines
14 KiB
C
455 lines
14 KiB
C
#include <vslc.h>
|
|
|
|
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; i<tlhash_size(global_names); i++ )
|
|
if ( global_list[i]->type == 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; i<tlhash_size(global_names); i++ )
|
|
if ( global_list[i]->type == 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; s<stringc; s++ )
|
|
printf ( ".STR%zu: .string %s\n", s, string_list[s] );
|
|
}
|
|
|
|
|
|
void
|
|
generate_global_variables ( void )
|
|
{
|
|
puts ( ".section .data" );
|
|
size_t nsyms = tlhash_size ( global_names );
|
|
symbol_t *syms[nsyms];
|
|
tlhash_values ( global_names, (void **)&syms );
|
|
for ( size_t n=0; n<nsyms; n++ )
|
|
{
|
|
if ( syms[n]->type == 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; arg<MIN(6,first->nparms); 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; i<statement->n_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; i<node->n_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;
|
|
}
|