#include #include #include #include #include #include "list.h" #include "shared.h" #include "intcode.h" unsigned int sizeoftype(id_type_t *t) { if(t->type == int_var) { return sizeof(int); } else if(t->type == float_var) { return sizeof(double); } else { return 0; } } unsigned int sizeofidtype(id_type_t *t) { if(t->dimension == 0) { return sizeoftype(t); } else { //this code is in here so the program can handle both short form and long form of arrays //(long form has the scalar as the last subtype if(t->subtype == NULL) { return sizeoftype(t) * t->size; } else { return t->size * sizeofidtype(t->subtype); } } } /*this is the print address we need to make. It prints the value of the address which is as follows: Symbol: the name of the symbol float: the value as a double int: the value as an int bool: either true or false code: the instruction number within the code list */ void printaddr2(intmdt_addr_t *addr, intmdt_code_t *cd_list) { if(addr == NULL) { return; } switch(addr->type) { case symbol : printf("%s",((char*)((addr->addr).entry_ptr->key))); break; case int_const : printf("%d",*(addr->addr.int_const_ptr)); break; case float_const : printf("%f", *(addr->addr.double_const_ptr)); break; case bool_const : printf("%s", *(addr->addr.bool_const_ptr) ? "true" : "false"); break; case code : //TODO: should switch to this- it's more efficient, but I need to verify that it's right. { long line; line = (addr->addr.instr_ptr - cd_list->code ) ; printf("(%ld)",line); } break; default: break; } } /*prints the address. Uses the main code list to search for refs to other variables*/ void printaddr(intmdt_addr_t *addr) { printaddr2(addr, code_list); } /*initializes the code list*/ intmdt_code_t *code_init() { intmdt_code_t *code = (intmdt_code_t*)malloc(sizeof(intmdt_code_t)); code->n = 0; return code; } /*takes the descriptors of 2 arrays. Returns 1 if they are the same dimensions and sizes, 0 if they are different this includes NULLs*/ int equalSize(id_type_t *a, id_type_t *b) { while(a != NULL && b != NULL) { if(a->dimension != b->dimension || a->size != b->size) { return 0; } a = a->subtype; b = b->subtype; } return a==b; } //creates an exact copy of the given id_type_t id_type_t *copy_id(id_type_t *orig) { id_type_t *copy = (id_type_t*)malloc(sizeof(id_type_t)); copy->type = orig->type; copy->size = orig->size; copy->dimension = orig->dimension; if(orig->subtype != NULL) { copy->subtype = copy_id(orig->subtype); copy->subtype->supertype = copy; } return copy; } //returns the type of the object as an int. //key : 1 = int, 2 = float, 3 = int_array, 4 = float_array, 5 = invalid, 6= no type info //6 should only appear on my temp variables, so I can cast them to whatever type they need to be int getType(intmdt_addr_t *a) { switch(a->type) { case symbol: //handle the complicated one first {id_type_t *val = (id_type_t*)a->addr.entry_ptr->value; if(val == NULL) { return 6; } if(val->type == int_var) { if(val->dimension == 0) { return 1; } return 3; } else if (val->type == float_var) { if(val->dimension == 0) { return 2; } return 4; } //else return 5; } break; case int_const : return 1; break; case float_const : return 2; break; default : break; } return 5; } /*convinience method for type checking. Returns true if all 3 types are the same or if you can get from a (anyop) b to result purely by widening. So if a or b are floats, result must be a float. Also, all must be scalars or all must be arrays, no mix of both so : int int float int float float float int float float float float and same with arrays, assuming arrays are the same size */ int types_compatible(intmdt_addr_t *a, intmdt_addr_t *b, intmdt_addr_t *result) { assert(result->type == symbol);//you can only put the result in a symbol, so let's make sure assert(a != NULL || b != NULL); int typea; int typeb; if(a!= NULL) { typea = getType(a); } else { typea = 0;} if(b != NULL) { typeb = getType(b); } else {typeb = 0; } int typeres = getType(result); int set_type = 0; if(typea==5 || typeb==5) { //we can't find anything, so default to not compatible fprintf(stderr, "unable to determine type information of instruction\n"); return 0; } if(typeres == 6) { //if we don't know the type, let's set it to the max of the two. that way, the whole thing will be compatible if a and //be are compatible typeres = (typea-typeb) < 0 ? typeb : typea; set_type = 1; } else if(typeres > 2) { //they're arrays, so we need to check array sizes if((typea <= 2 && typea >0) || (typeb <= 2 && typeb>0) ){ //they're not arrays fprintf(stderr, "cannot convert a scalar into an array\n"); return 0; } id_type_t *result_val = (id_type_t*)result->addr.entry_ptr->value; if(a->type != symbol || b->type != symbol || !equalSize((id_type_t*)a->addr.entry_ptr->value,result_val) || !equalSize((id_type_t*)b->addr.entry_ptr->value,result_val)) { return 0; } } else if(typeres <= 2) { //storing in a scalar, make sure both are scalar if(typea > 2 || typeb > 2) { fprintf(stderr, "cannot convert an array into a scalar\n"); return 0; } } //using flags to determine which types should be switched //There are 3 bits used. Bit 0 says whether the types are compatible //bit one says that argument 1 needs to be widened, and bit 2 says //that arguement 2 needs to be widened int retval = typea <= typeres && typeb <= typeres; if(typea < typeres && typea != 0) { retval |= 2; } if(typeb < typeres && typeb != 0) { retval |= 4; } //And here's what is now the extra credit for hw 4: //if the variable doesn't have a type set (which means it's a temp variable, //set the type. if(set_type) { if(typeres == 1 || 2) { id_type_t *newId = (id_type_t*)malloc(sizeof(id_type_t)); newId->type = typeres-1 ? float_var : int_var; newId->dimension = 0; newId->size = 0; newId->subtype = NULL; newId->supertype = NULL; result->addr.entry_ptr->value = newId; } else if(typeres == typea) { result->addr.entry_ptr->value = copy_id(a->addr.entry_ptr->value); } else { //typeb == typeres result->addr.entry_ptr->value = copy_id(b->addr.entry_ptr->value); } } return retval; } /*gen function, creates a quadruple puts it in the code list*/ //gen without type checking quadruple_t *gen2(intmdt_code_t *intermediate_code, char *op, intmdt_addr_t *arg1, intmdt_addr_t *arg2, intmdt_addr_t *result) { if(intermediate_code->n >= MAXCODELEN) { return NULL; } quadruple_t *cur = &intermediate_code->code[intermediate_code->n]; cur->op = op; cur->arg1 = arg1; cur->arg2 = arg2; cur->result = result; ++(intermediate_code->n); return cur; } /*creates a new temporary variable. Does some pretty complicated tricks to make sure there are no buffer overflows. to separate the compiler generated variables from user variables, temp variables all start with a #, a symbol that's illegal in user variables */ intmdt_addr_t *newtemp(env_t *top) { if(top == NULL) { return NULL; } int leng; if(top->num_temps == 0) { leng = 2; } else{ /*this is expensive computationally, but it 1)protects against buffer overflows and 2)uses the least amount of memory possible*/ leng = floor(log10(top->num_temps)) + 2; } char *key = (char*)calloc(leng ,sizeof(char)); sprintf(key,"#%d",top->num_temps); list_entry_t *new_ent = env_insert(top,key,NULL); if(new_ent != NULL) { (top->num_temps)++; } else { fprintf(stderr, "Failed to insert temp value\n."); return NULL; } intmdt_addr_t *newTemp = (intmdt_addr_t*)malloc(sizeof(intmdt_addr_t)); newTemp->type = symbol; newTemp->addr.entry_ptr = new_ent; return newTemp; } /*checks to see if the given variable is temporary, based on the fact that all temporary variables in my code begin with #, a symbol illegal for user-defined variables */ int is_temp_variable(list_entry_t *var) { return (char)(((char*)(var->key))[0]) == '#'; } int is_temp_var_addr(intmdt_addr_t *var) { return var->type == symbol && is_temp_variable(var->addr.entry_ptr); } //gets a string representing the type of the intermediate address. Currenty, //it's one of int, float, int[], or float[] char *getTypeName(intmdt_addr_t *addr) { int type = getType(addr); switch(type) { case 1: return "int"; break; case 2: return "float"; break; case 3: return "int[]"; break; case 4 : return "float[]"; break; default: return NULL; break; } } //given the ID, gets a string representing the type //this is one of the few functions where I somewhat paid attention //to freeing memory, so probably not going to have mem leaks here //(assumming I get around to fixing those) char* getTypeName_FromID(id_type_t *t) { intmdt_addr_t *a = (intmdt_addr_t*)malloc(sizeof(intmdt_addr_t)); a->type = symbol; list_entry_t* le = (list_entry_t*)malloc(sizeof(list_entry_t)); le->key = "/"; le->value = t; a->addr.entry_ptr = le; char *typeName = getTypeName(a); free(le); free(a); return typeName; } /*This is the widen function. It takes an address and a type to cast it to and attempts to make the cast (this is for implicit casts). It will return the list_entry_t of the temp variable that stores the cast variable if successful, or NULL on a failure */ intmdt_addr_t *widen(intmdt_code_t *intermediate_code, env_t *cur, intmdt_addr_t *a, id_type_t *t) { assert(cur != NULL && a != NULL && t != NULL); switch( a->type) { case symbol : {list_entry_t *le = a->addr.entry_ptr; if(le->value != NULL && ((id_type_t*)le->value)->type == int_var && t->type == float_var) { intmdt_addr_t *new_var = NULL; if(equalSize(le->value,t)) { new_var = newtemp(cur); } if(new_var != NULL) { new_var->addr.entry_ptr->value = copy_id(t); } else { return NULL; } char* type_name = getTypeName_FromID(t); char* cast = calloc(strlen(type_name) + 2, sizeof(char)); sprintf(cast, "(%s)",type_name); gen2(intermediate_code, cast, NULL, a,new_var); return new_var; }} break; case int_const : if(t->type == float_var && t->size == 0) { intmdt_addr_t *new_var = newtemp(cur); new_var->addr.entry_ptr->value = copy_id(t); gen2(intermediate_code,"(float)",NULL,a,new_var); return new_var; } break; default : //I only widen int and int[] to float and float[]. break; } return NULL; } intmdt_var_t *make_symbol_addr(list_entry_t *t) { intmdt_addr_t *newSym = (intmdt_addr_t*)malloc(sizeof(intmdt_addr_t)); newSym->type = symbol; newSym->addr.entry_ptr = t; intmdt_var_t *newVar = (intmdt_var_t*)malloc(sizeof(intmdt_var_t)); newVar->type = var_addr; newVar->var.addr_ptr = newSym; return newVar; } //gen with type checking quadruple_t *gen(intmdt_code_t *intermediate_code, env_t *cur, char *op, intmdt_addr_t *arg1, intmdt_addr_t *arg2, intmdt_addr_t *result) { //type checking int type_check = 0; assert((arg1 != NULL || arg2 != NULL) && cur != NULL && intermediate_code != NULL); assert(arg2 == NULL || strcmp(op,"")); //if arg2 is NULL, then op can be empty (assignment) if( strcmp(op,"[]") && strcmp(op,"(float)") && strcmp(op,"(float[])")) { int i = 0; if(result->addr.entry_ptr->value == NULL ) { i = 1; } type_check = types_compatible(arg1,arg2,result); if(i) { //set location and size. This is for temp variables list_entry_t *le = result->addr.entry_ptr; ((id_type_t*)(le->value))->loc = cur->cur_size; cur->cur_size += sizeofidtype(le->value); } } else if(!strcmp(op, "[]")) { type_check = (getType(arg2) == 1); } else if(!strcmp(op,"goto")) { assert(arg1 == NULL && result == NULL); assert(arg2->type == code); type_check = 1; } intmdt_addr_t *final_1; intmdt_addr_t *final_2; if(!type_check) { printf("type check failed\n"); //incompatible types. Hopefully, we'll never get here, unless it's intentional return NULL; } else { if(type_check & 2) { //this implies that arg1 must be widened final_1 = widen(intermediate_code, cur, arg1, (id_type_t*)(result->addr.entry_ptr->value)); } else {final_1 = arg1;} if(type_check & 4 ) { final_2 = widen(intermediate_code, cur, arg2, (id_type_t*)(result->addr.entry_ptr->value)); } else {final_2 = arg2;} } return gen2(intermediate_code,op,final_1,final_2,result); } //creates a temporary variable pointing to an element in an array and returns the temp variable quadruple_t *get_array_loc(intmdt_addr_t *array_addr, intmdt_addr_t *index_addr,env_t *cur, intmdt_code_t *int_code) { //make sure that the array variable is a symbol. assert(array_addr->type ==symbol); //make a new temp to store the array element intmdt_addr_t *t1 = newtemp(cur); //make a new variable to store the location of the array intmdt_addr_t *val = newtemp(cur); //an int_const that's set to the the size of each element in the array intmdt_addr_t *t2 = (intmdt_addr_t*)malloc(sizeof(intmdt_addr_t)); t2->type = int_const; int* size= malloc(sizeof(int)); *size= sizeofidtype(((id_type_t*)array_addr->addr.entry_ptr->value)->subtype); t2->addr.int_const_ptr = size; gen(int_code, cur, "*",t2,index_addr,val); quadruple_t *res = gen(int_code, cur, "[]",array_addr,val,t1); return res; } //checks to see if the given operator is one of the relational or equality operators int is_rel_op(char* op) { char* rel_ops[6] = {">","<",">=","<=","==","!="}; int i; for(i = 0; i < 6; ++i) { if(!strcmp(op, rel_ops[i])) { return 1; } } return 0; } /*prints the given qudruple. It shows the 4 args separated by |*/ void printQuadruple(intmdt_code_t *cd_list, int index) { quadruple_t quad = cd_list->code[index]; //special code for gotos: if(quad.op == NULL) { //it's an unconditional jump printf("goto "); if(!quad.result) { printf("-"); } else { printaddr2(quad.result, cd_list); } return; } if(is_rel_op(quad.op)) { printf("if "); printaddr2(quad.arg1,cd_list); printf(" %s ",quad.op); printaddr2(quad.arg2,cd_list); printf(" goto "); if(!quad.result) { printf("-"); } else { assert(quad.result->type == code); printaddr2(quad.result, cd_list); } return; } if(!strcmp(quad.op,"[]=")) { printaddr2(quad.result, cd_list); printf(" [ "); printaddr2(quad.arg2,cd_list); printf(" ] = "); printaddr2(quad.arg1,cd_list); return; } printaddr2(quad.result, cd_list); printf(" = "); printaddr2(quad.arg1, cd_list); if(strcmp(quad.op,"[]")) { //this will properly handle mathematical and relational operators. //Also, if arg1 is NULL, it will handle prefix operators. //if arg2 is NULL, it will appear to handle postfix operators printf(" %s ",quad.op); printaddr2(quad.arg2, cd_list); } else { printf("[ "); printaddr2(quad.arg2,cd_list); printf(" ]"); } } //creates a temp variable for the array address. intmdt_var_t *makeArrayAddr(void *id_v, void*index_v, intmdt_code_t *code, env_t *cur) { //start with casts intmdt_var_t *id_t = (intmdt_var_t*)id_v; intmdt_addr_t *id; assert(id_t != NULL); switch(id_t->type) { case(var_addr) : id = id_t->var.addr_ptr; break; default : fprintf(stderr, "ERROR: must have a variable for an array, line #%d\n",yylineno); return NULL; } intmdt_var_t *index_t = (intmdt_var_t*)index_v; assert(index_t != NULL); intmdt_addr_t *index; switch(index_t->type) { case(var_addr) : index = index_t->var.addr_ptr; break; case(arith_expr) : index = index_t->var.quad_ptr->result; break; default : fprintf(stderr, "ERROR: cannot use bool for array index, line #%d\n",yylineno); return NULL; } quadruple_t *result = get_array_loc(id,index, cur, code); id_type_t *new_type;// = (id_type_t*)malloc(sizeof(id_type_t)); new_type = copy_id(((id_type_t*)id->addr.entry_ptr->value)->subtype); new_type->loc = cur->cur_size; result->result->addr.entry_ptr->value = new_type; intmdt_var_t *newVar = (intmdt_var_t*)malloc(sizeof(intmdt_var_t)); newVar->type = arith_expr; newVar->var.quad_ptr = result; cur->cur_size += sizeofidtype(new_type); return newVar; } void shift_down(intmdt_code_t *code, int start) { int i = 0; free(code->code[start].result); for(i = start; i < code->n; ++i) { code->code[i] = code->code[i+1]; } code->n--; } //Used for loc = bool->stmt assignemnts intmdt_var_t *assignValue(void *id_v, void* val_v, intmdt_code_t *code, env_t *cur) { //start with casts intmdt_var_t *id_t = (intmdt_var_t*)id_v; intmdt_addr_t *id = id_t->var.addr_ptr; intmdt_var_t *val_t = (intmdt_var_t*)val_v; intmdt_addr_t *val = NULL; if(val_t->type == arith_expr) { quadruple_t *val_q = val_t->var.quad_ptr; val = val_q->result; if(is_temp_var_addr(val) && id_t->type != arith_expr) { env_remove(cur,val->addr.entry_ptr->key); *val = *id; //here's the code to get rid of the redundant assign. return val_t; } } else if(val_t->type == var_addr) { val = val_t->var.addr_ptr; } else { fprintf(stderr, "cannot assign bool to variable, line %d\n",yylineno); return NULL; } quadruple_t *res = NULL; if(id_t->type == arith_expr) { //it's an array reference res = id_t->var.quad_ptr; intmdt_addr_t *arg1 = val; intmdt_addr_t *arg2 = res->arg2; intmdt_addr_t *result = res->arg1; //env_remove(cur,res->result->addr.entry_ptr->key);i shift_down(code, (res - code->code)); res = gen2(code, "[]=",arg1,arg2,result); id = id_t->var.quad_ptr->result; } else if(!(res=gen(code, cur, "",val,NULL,id))) { fprintf(stderr,"error on assignemnt to %s at line %d\n",(char*)id->addr.entry_ptr->key,yylineno); return NULL; } intmdt_var_t* var = (intmdt_var_t*)malloc(sizeof(intmdt_var_t)); var->type = var_addr; var->var.quad_ptr = res; return var; } //used for the num-> factor. creates an intmdt_var_t from the int pointer intmdt_var_t *makeNum(void *num) { int num_i = *((int*)num); int *new_ref = (int*)malloc(sizeof(int)); *new_ref = num_i; intmdt_addr_t *addr = (intmdt_addr_t*)malloc(sizeof(intmdt_addr_t)); addr->type = int_const; // addr->addr.int_const_ptr = (int*)malloc(sizeof(int)); addr->addr.int_const_ptr = new_ref; intmdt_var_t *var = (intmdt_var_t*)malloc(sizeof(intmdt_var_t)); var->type = var_addr; var->var.addr_ptr = addr; return var; } //used for real->factor. Creates the intmdt_var_t from the double pointer intmdt_var_t *makeReal(void *num) { double *new_ref = (double*)malloc(sizeof(double)); *new_ref = *(double*)num; intmdt_addr_t *addr = (intmdt_addr_t*)malloc(sizeof(intmdt_addr_t)); addr->type = float_const; addr->addr.double_const_ptr = new_ref; intmdt_var_t *var = (intmdt_var_t*)malloc(sizeof(intmdt_var_t)); var->type = var_addr; var->var.addr_ptr = addr; return var; } //used for the operator assignments (+ - * /) intmdt_var_t *makeOp(void *arg1_v, void *op_v, void *arg2_v, intmdt_code_t *cd_list, env_t *cur_env) { //first off, let's cast everything intmdt_var_t *arg1_t = (intmdt_var_t*)arg1_v; intmdt_var_t *arg2_t = (intmdt_var_t*)arg2_v; intmdt_addr_t *arg1, *arg2; if(arg1_t != NULL) { if(arg1_t->type == arith_expr) { arg1 = arg1_t->var.quad_ptr->result; } else if(arg1_t->type == var_addr) { arg1 = arg1_t->var.addr_ptr; } else { fprintf(stderr,"ERROR: cannot assign bool to operation: line %d\n",yylineno); return NULL; } } else {arg1 = NULL;} char* op = (char*) op_v; if(arg2_t != NULL) { if(arg2_t->type == arith_expr) { arg2 = arg2_t->var.quad_ptr->result; } else if(arg2_t->type == var_addr) { arg2 = arg2_t->var.addr_ptr; } else { fprintf(stderr,"ERROR: cannot assign bool to operation: line %d\n",yylineno); } } else { arg2=NULL;} //now lets make our result temp variable intmdt_addr_t *t1 = newtemp(cur_env); quadruple_t *res = NULL; if((res = gen(cd_list,cur_env, op, arg1, arg2,t1))) { //everything went well, we have the new code thing. intmdt_var_t *newVar = (intmdt_var_t*)malloc(sizeof(intmdt_var_t)); newVar->type = arith_expr; newVar->var.quad_ptr = res; return newVar; } else { fprintf(stderr,"ERROR: problem with assignment statements at %d.\n",yylineno); return NULL; } } //turns a symbol into an intmdt_var_t. used in the ID->loc intmdt_var_t *getSymbol(void *id, env_t *cur) { list_entry_t *le = env_get(cur,id); if(le == NULL) { fprintf(stderr,"error: symbol not found %s at line %d\n", (char*)id,yylineno); } return make_symbol_addr(le); } //a function to print out the intermediate code list void printCodeList(intmdt_code_t *cd_list) { int i; for(i = 0; i < cd_list->n; ++i) { printQuadruple(cd_list,i); printf("\n"); } }