/* @@@@@ @@@@@@@ @@@@@@ @@@@@@@ @@@@@ @@@@@@@ @ @ @@@@@ @ @ @ @ @ @ @ @ @ @ @@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @@@@@ @ @@@@ @@@@@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @@@ @ @ @ @ @ @ @ @ @ @ @ @ @@ @@@ @ @ @@@@@ @@@@@@@ @@@@@@ @@@@@@@ @@@@@ @@@@@@@ @ @ @@@ @@@@@ * codegen.c - generate assemebly language code * * this code has evolved in time - we now will generate SPIM code * for the MIPS RISC simulator by Larus * * 3/10/00 removed the WriteStringAddress module which isn't used * 1/28/02 use s0 to reference global variables - since s1, s2, s3 will be * used later in the semester for procedures */ #include #include #define ASTDEF_LOCAL #include "astdef.h" #include "codegen.h" #include "filedef.h" #include "spim_def.h" #include "sem.h" #include "tracing.h" static void DisplayRegs(void); static void GenCodeHdr(void); static void WriteAddr(MACHINEADDRPTR addr); static void WriteInstr(INSTRUCTION instr); static void Gen0Op(INSTRUCTION instr); static void Gen1Op(INSTRUCTION instr, MACHINEADDRPTR a); static void Gen2Op(INSTRUCTION instr, MACHINEADDRPTR a1, MACHINEADDRPTR a2); static void FreeArithRegs(void); static int GetArithReg(void); static void LoadArithReg (MACHINEADDRPTR dest, MACHINEADDRPTR source); static void Gen3Op (INSTRUCTION instr, MACHINEADDRPTR result, MACHINEADDRPTR addr1, MACHINEADDRPTR addr2); static void GenExpr (EXPRTREE expr, MACHINEADDRPTR resaddrs); static void GenReads (PARAMLIST params); static void GenWrites (PARAMLIST params); static void GenStmt (STMTPTR stmt); static void GenIfExpr (EXPRTREE bexpr, MACHINEADDRPTR outlabel); static void FreeReg(int registernumber); static void WriteLabel (MACHINEADDRPTR labeladdr); static void GenStmtList (STMTPTR stmt); static int GenOffsets (VARLIST vars); static void GenStorage (VARLIST vars); static void MaCopy (MACHINEADDRPTR dest, MACHINEADDRPTR source); static void CalcConstOffsets(void); static int ConstLen(char *s); static void ConstStorage(void); /* -------------------------------------------------------------------- */ /* ----------------------------- GenCode ------------------------------ */ /* -------------------------------------------------------------------- */ int GenCode(void) { /* we need the character new_line to generate the appropriate assembler code for a writeln - might as well use this as our first string constant */ char *new_line = "\\n"; if (PtraceCode) {Entering("GenCode");} /* DisplayRegs shows with registers are allocated - good for debug */ FreeArithRegs(); DisplayRegs(); /* Since I did this in codegen, new_line will always be at the * * beginning of the constant linked list. */ AddStringConstant( new_line ); if(GenOffsets(Prog.vars) == 0) printf ("Warning (codegen): no entries found in variable list\n"); /* Calculate constant string offsets */ CalcConstOffsets(); /* Generate CodeFile headers */ GenCodeHdr(); /* generate code for the statements in the main program */ GenStmtList(Prog.main); /* generate a halt instruction */ fprintf(CodeFile,"#\n# Halt execution\n"); Gen0Op(Halt); fprintf(CodeFile,"#\n# Finish up by writing out constants \n"); if (PtraceCode){ printf("#\n"); printf("# Finish up by writing out constants \n"); } /* Output constant area */ ConstStorage(); fprintf(CodeFile, "#\n"); fprintf(CodeFile, "# Reserve space for global variables\n"); if (PtraceCode){ printf( "#\n"); printf( "# Reserve space for global variables\n"); } /* Declare the storage for the symbolic variable names */ GenStorage(Prog.vars); /* need to generate a temporary location to write the results of all write ( expression tree); so that it will be properly aligned data so that spim won't issue an exception error. this is a cludge, but i couldn't find another way around this. if the compiler generates code (which I think is correct in this case) ulw $a0,0($t1) where 0($t1) is the target address from genexpr when there has been a write(x+y*3); say - this produces the proper output from spim, but the exception error message from spim is annoying. therefore, we will generate the following code when the GenWrite is handling an expression tree (not an immediate value or a variable) sw $t1,Temp_Wr stores result to get word alignment- don't know why li $v0,1 sets up flag for syscall lw $a0,_Temp_Wr loads designed register for syscall */ fprintf(CodeFile,"\t.data\n"); fprintf(CodeFile, "Temp_Wr:\n\t.word 0 #just for alignment of write(exprtree)\n"); if (PtraceCode) {fprintf(CodeFile,"\n"); Leaving("GenCode");} return(OK); } /* GenCode */ /* -------------------------------------------------------*/ /* ------------------- DisplayRegs -----------------------*/ /* -------------------------------------------------------*/ void DisplayRegs(void) /* the boolean array regfree keeps track when arithmetic registers are allocated and when available for use */ { int i; if (PtraceCode) {Entering("DisplayRegs");} printf("\n\nFrom codegen.c - DisplayRegs() marks used registers\n"); printf("\nRegister: 0 1 2 3 4 5 6 7 8 9 \n"); printf(" - - - - - - - - - - \n"); printf("Used = x:"); for (i=MINREG; i<= MAXREG; i++) if (regfree[i] == FALSE) printf(" x"); else printf(" "); printf("\n\n"); if (PtraceCode) {fprintf(CodeFile,"\n"); Leaving("DisplayRegs");} } /* DisplayRegs */ /* ------------------------------------------------------------------------ */ /* ------------ GenCodeHDR: WRITE OUT CODEFILE FILE HEADER INFORMATION --- */ /* ------------------------------------------------------------------------ */ /* This procedure writes out the proper SPIM code to set up necessary * * registers to startup up simple_in_c. * ****************************************************************************/ /* 07/28/90 MDM */ void GenCodeHdr(void) { if (PtraceCode) {Entering("GenCodeHdr");} /* Set up registers */ /* Comment for future expansion */ fprintf(CodeFile,"# Register Usage:\n"); /*fprintf(CodeFile,"# $t%d for constants\n",MAXREG); spr02 */ /*fprintf(CodeFile,"# $t%d for global variables\n",MAXREG-1); spr02 */ fprintf(CodeFile,"# $s0 for global variables\n"); /* spr02 */ fprintf(CodeFile,"#\n"); fprintf(CodeFile,"\t.text \n \t.globl main \n main:\n"); /* Load instruction */ /* WriteInstr(Loadaddr); spr02 */ /* Register $t9 for constants - MAXREG = 9 is set in spim_def.h spr02 */ /* fprintf(CodeFile, "$t%d, CONST\n",MAXREG);*/ /* Load instruction */ WriteInstr(Loadaddr); /* Register $t8 for variables sp02*/ /* fprintf(CodeFile, "$t%d, VARS\n",MAXREG-1); spr02*/ /* Register $s0 for variables */ fprintf(CodeFile, "$s%d, GVARS\n",CurProcLevel); fprintf(CodeFile, "#\n"); fprintf(CodeFile, "# Start Code\n"); if (PtraceCode){ printf("#\n"); printf("# Start Code\n"); Leaving("GenCodeHdr");} } /* GenCodeHdr */ /* ------------------------------------------------------------------------ */ /* ------------ WriteAddr: WRITE AN ADDRESS TO THE CODE FILE ------------- */ /* ------------------------------------------------------------------------ */ static void WriteAddr(MACHINEADDRPTR addr) { if (PtraceCode) {Entering("WriteAddr");} switch(addr->mode){ case directregister: fprintf(CodeFile,"$t%d",addr->ma.regaddr); break; case immediateValue: fprintf(CodeFile,"%d",addr->ma.iValue); break; case labeladdress: /* for if-then */ fprintf(CodeFile,"IF%d",addr->ma.labelnumber); break; case registeroffset: /* global vars off CurProcLevel (which = 0) for Phase0 */ fprintf(CodeFile,"%d($s%d)", addr->ma.memaddr->offset, CurProcLevel-addr->ma.memaddr->level); /* spr02*/ break; } if (PtraceCode) {fprintf(CodeFile,"\n"); Leaving("WriteAddr");} } /* WriteAddr */ /* ----------------------------------------------------------------------- */ /* - WriteInstr: WRITE arithmetic or logical or jump/branch instruction - */ /* I/O is handled separately since it's a SysCall */ /* ----------------------------------------------------------------------- */ static void WriteInstr(INSTRUCTION instr) { if (PtraceCode) {Entering("WriteInstr");} switch(instr){ case Halt: fprintf(CodeFile,"\tli\t$v0 10\n"); fprintf(CodeFile,"\tsyscall\n"); break; case Wrln: fprintf(CodeFile,"\tla\t$a0, S0\n"); fprintf(CodeFile,"\tli\t$v0, 4\n"); fprintf(CodeFile,"\tsyscall\n"); break; case Iadd: fprintf(CodeFile,"\tadd\t"); break; case Idiv: fprintf(CodeFile,"\tdiv\t"); break; case Imult: fprintf(CodeFile,"\tmul\t"); break; case Ineg: fprintf(CodeFile,"\tneg\t"); break; case Isub: fprintf(CodeFile,"\tsub\t"); break; case Loadaddr: fprintf(CodeFile,"\tla\t"); break; case Loadword: fprintf(CodeFile,"\tlw\t"); break; case Loadimme: fprintf(CodeFile,"\tli\t"); break; case Store: fprintf(CodeFile,"\tsw\t"); break; case Icomp: fprintf(CodeFile,"\tseq\t"); break; } /* case */ if (PtraceCode) {fprintf(CodeFile,"\n"); Leaving("WriteInstr");} } /* WriteInstr */ /* ------------------------------------------------------------------------ */ /* ------------ Gen0Op: WRITE A ZERO ADDRESS INSTRUCTION ------------- */ /* ------------------------------------------------------------------------ */ static void Gen0Op(INSTRUCTION instr) { if (PtraceCode) {Entering("Gen0Op");} WriteInstr(instr); fprintf(CodeFile,"\n"); if (PtraceCode) {/*fprintf(CodeFile,"\n");*/ Leaving("Gen0Op");} } /* Gen0Op */ /* ------------------------------------------------------------------------ */ /* ------------ Gen1Op: WRITE A ONE ADDRESS INSTRUCTION -------------- */ /* ------------------------------------------------------------------------ */ static void Gen1Op(INSTRUCTION instr,MACHINEADDRPTR a) { if (PtraceCode) {Entering("Gen1Op");} WriteInstr(instr); WriteAddr(a); fprintf(CodeFile,"\n"); if (PtraceCode) {/*fprintf(CodeFile,"\n");*/ Leaving("Gen1Op");} } /* Gen1Op */ /* ------------------------------------------------------------------------ */ /* ------------- Gen2Op: WRITE A TWO ADDRESS INSTRUCTION ------------- */ /* ------------------------------------------------------------------------ */ static void Gen2Op(INSTRUCTION instr, MACHINEADDRPTR a1, MACHINEADDRPTR a2) { if (PtraceCode) {Entering("Gen2Op");} WriteInstr(instr); WriteAddr(a1); fprintf(CodeFile,","); WriteAddr(a2); fprintf(CodeFile,"\n"); if (PtraceCode) {/*fprintf(CodeFile,"\n");*/ Leaving("Gen2Op");} } /* Gen2Op */ /* ------------------------------------------------------------------------ */ /* -------------- FreeArithRegs: SET ALL ARITHMETIC TO FREE -------------- */ /* ------------------------------------------------------------------------ */ /* Registers become undefined, i.e., free, between expressions. * Considerably fancier optimizations are possible. */ static void FreeArithRegs(void) { int i; if (PtraceCode) {Entering("FreeArithRegs");} for(i=MINREG ; i<=MAXREG ; i++) regfree[i] = TRUE; /* Mark $t9, $t8 unusable for normal arithmetic operatiations */ /* regfree[MAXREG] = FALSE; regfree[MAXREG-1] = FALSE; spr02 */ if (PtraceCode) {/*fprintf(CodeFile,"\n");*/ Leaving("FreeArithRegs");} } /* FreeArithRegs */ /* ------------------------------------------------------------------------ */ /* --------------- GetArithReg: GET AN ARITHMETIC REGISTER --------------- */ /* ------------------------------------------------------------------------ */ /* Get the first free register - integers take one; reals take * two consecutive registers, a register-pair - but we won't be using * floating point allocations for SPIM */ static int GetArithReg() { int i; if (PtraceCode) {Entering("GetArithReg");} for(i = MINREG ; i <= MAXREG; i++){ if (regfree[i] == TRUE) { /* Give first register away -- second, if used at all, will only be used very briefly if ever implement floating point ops. */ regfree[i] = FALSE; if (PtraceCode) { Leaving("GetArithReg"); } return(i); } } /* in case we run out of registers (shouldn't happen) get some debug info */ DisplayRegs(); printf("Codegen: expression too complex; ran out of registers - giving up\n"); /* fatal error - get out of here */ exit(1); } /* GetArithReg */ /* ------------------------------------------------------------------------ */ /* -------------- LoadArithReg: LOAD AN ARITHMETIC REGISTER -------------- */ /* ------------------------------------------------------------------------ */ /* Load any free register */ static void LoadArithReg(MACHINEADDRPTR dest, MACHINEADDRPTR source) { MACHINEADDR tempaddr; /* temporary storage for maddr in case source and dest are same */ if (PtraceCode) {Entering("LoadArithReg");} MaCopy(&tempaddr,source); /* in case maddr and loc are the same */ /* Find a free register. */ dest->mode = directregister; dest->ma.regaddr = GetArithReg(); /* Loadword or Loadimme chosen based on source's mode */ if (tempaddr.mode == immediateValue ) Gen2Op(Loadimme, dest, &tempaddr); else if (tempaddr.mode == registeroffset) Gen2Op(Loadword, dest, &tempaddr); else { /* fatal error - only immediates and reg offset can load to reg */ fprintf(stderr, "In LoadArithReg (FATAL): tried to load invalid address type to register\n"); exit(1); } if (PtraceCode) {/*fprintf(CodeFile,"\n");*/ Leaving("LoadArithReg");} } /* LoadArithReg */ /* ------------------------------------------------------------------------ */ /* ---------- Gen3Op: GENERATE CODE FOR A 3 OPERAND INSTRUCTION ---------- */ /* ------------------------------------------------------------------------ */ /* Generate the code to perform "result := addr1 instr addr2", * i.e., generate code for a "quadruple", where result is always a * register (determined by this routine), but addr1 and addr2 can * be arbitrary addresses. * SPIM: instr result, addr1, addr2 * To minimize register usage, if addr1 is already in a register * use this as the result register. * * If the instr can commute, then swap addr1 and addr2 depending on * which is already in a register. * Spim restrictions: addr2 can be register or immediate * so when the instr commutes you want to swap addr1 * and addr2 to have immediate last */ static void Gen3Op (INSTRUCTION instr, MACHINEADDRPTR result, MACHINEADDRPTR addr1, MACHINEADDRPTR addr2) { MACHINEADDRPTR temp; if (PtraceCode) {Entering("Gen3Op");} switch(instr){ case Icomp : /* for the if-then */ case Imult : case Iadd : /* These operations commute, so we will reverse the order of evaluation if it is to our advantage. */ if(addr1->mode == directregister){ MaCopy (result,addr1); /* keep the result in same register */ /* note - if addr2 is a direct register or an immediate, addr2 ready to go */ /* if addr2 is registeroffset, must load in a register */ if (addr2->mode == registeroffset ){ temp = mach_alloc(); LoadArithReg(temp,addr2); /* overwrite addr2 with temp - addr2 is already to go now */ MaCopy(addr2,temp); } WriteInstr(instr); WriteAddr(result); fprintf(CodeFile,","); WriteAddr(addr1); fprintf(CodeFile,","); WriteAddr(addr2); fprintf(CodeFile,"\n"); /* if both operands were in registers, indicate the second register is free.*/ if(addr2->mode == directregister) regfree[addr2->ma.regaddr] = TRUE; } /* end if addr1 is directregister */ else if(addr2->mode == directregister){ /* see comments above when addr1 was a directregister, similar ops */ MaCopy (result,addr2); if (addr1->mode == registeroffset){ temp = mach_alloc(); LoadArithReg(temp,addr1); MaCopy(addr1,temp); } WriteInstr(instr); WriteAddr(result); fprintf(CodeFile,","); WriteAddr(addr2); fprintf(CodeFile,","); WriteAddr(addr1); fprintf(CodeFile,"\n"); /* second address may be an immediate or directregister - must check */ if (addr1->mode == directregister) regfree[addr1->ma.regaddr] = TRUE; } /* end elseif addr2 is directregister */ /* neither operand is an expression already since it's not already in a tree - so now we to load the leaves */ else { /* due do SPIM, can be efficient with immediates here */ /* check if either operand is an immediate then load the other one - note an exprtree with 2 + 4 would have been folded in sem.c */ if (addr1->mode == immediateValue) { /* Move a copy of the second operand to a register */ LoadArithReg(result,addr2); WriteInstr(instr); WriteAddr(result); fprintf(CodeFile,","); WriteAddr(result); fprintf(CodeFile,","); WriteAddr(addr1); fprintf(CodeFile,"\n"); } /* end if addr1 is immediate */ else if (addr2->mode == immediateValue) { /* Move a copy of the first operand to a register */ LoadArithReg(result,addr1); WriteInstr(instr); WriteAddr(result); fprintf(CodeFile,","); WriteAddr(result); fprintf(CodeFile,","); WriteAddr(addr2); fprintf(CodeFile,"\n"); } /* end else if addr2 is immediate */ /* both operands are register offset, so both must be loaded to reg */ else { LoadArithReg(result,addr1); temp = mach_alloc(); LoadArithReg(temp,addr2); WriteInstr(instr); WriteAddr(result); fprintf(CodeFile,","); WriteAddr(result); fprintf(CodeFile,","); WriteAddr(temp); regfree[temp->ma.regaddr] = TRUE; fprintf(CodeFile,"\n"); } /* end else - neither was immediate */ } /* end else - neither is a directregister */ break; /* ------- non-commuting operators -------------------------------*/ case Idiv: case Isub: if(addr1->mode == directregister){ MaCopy (result,addr1); /* keep the result in same register */ /* note - if addr2 is a direct register or an immediate, addr2 ready to go */ /* if addr2 is registeroffset, must load in a register */ if (addr2->mode == registeroffset ){ temp = mach_alloc(); LoadArithReg(temp,addr2); /* overwrite addr2 with temp - addr2 is already to go now */ MaCopy(addr2,temp); } WriteInstr(instr); WriteAddr(result); fprintf(CodeFile,","); WriteAddr(addr1); fprintf(CodeFile,","); WriteAddr(addr2); fprintf(CodeFile,"\n"); /* if both operands were in registers, indicate the second register is free.*/ if(addr2->mode == directregister) regfree[addr2->ma.regaddr] = TRUE; } /* end if addr1 is directregister */ else /* need to load addr1 into a register, but know nothing about addr2 yet */ { LoadArithReg(result,addr1); if (addr2->mode != directregister) { temp = mach_alloc(); LoadArithReg(temp,addr2); MaCopy(addr2,temp); } WriteInstr(instr); WriteAddr(result); fprintf(CodeFile,","); WriteAddr(result); fprintf(CodeFile,","); WriteAddr(addr2); fprintf(CodeFile,"\n"); /* second address may be immediate or directregister - must check */ if(addr2->mode == directregister) regfree[addr2->ma.regaddr] = TRUE; } break; /* sub/div which do not commute */ } /* case */ if (PtraceCode) {/*fprintf(CodeFile,"\n");*/ Leaving("Gen3Op");} } /* Gen3Op */ /* ------------------------------------------------------------------------ */ /* -------------- GenExpr: GENERATE CODE FOR AN EXPRESSION --------------- */ /* ------------------------------------------------------------------------ */ /* Generate code for an expression tree and return which register the * answer is in. If the expression is just a simple Memory * reference or Immediate operand avoid moving Value to a register * at this time. recursion is used heavily, especially for complicated expressions. the local variables a1 and a2 are the machine addresses built to hold results of the right and left subtree of the expressiontree (input: expr) the result is placed in output variable resaddr (a machine address) this routine does the bulk of converting from the semantic representation built by sem.c triggered by the parser generated simple.lr (which is displayed in the output of the AST) - into the representation for the underlying machine (MACHINEADDR) and its addressing modes for SPIM: register-offset only in Load/Store immediates only as third operand for arithmetic and logical operations Gen3Op takes care of most of this. this routine is called to: 1) generate rhs of an assignment statement 2) generate the elements in a write statement */ static void GenExpr(EXPRTREE expr, MACHINEADDRPTR resaddr) /* resaddr is registeroff if input tree is variable name immediateValue if input tree is constant integer directregister if input tree is an expression */ { MACHINEADDR a1,a2; if (PtraceCode) {Entering("GenExpr");} switch(expr->op){ case Fetch: /* a variable or integer constant - leaves of exprtree */ switch(expr->ee.addr->type){ case Memory: /*printf("GenExpr : mem fetch\n");*/ resaddr->mode = registeroffset; resaddr->ma.memaddr = expr->ee.addr->ae.attrs; break; case Immediate: /*printf("GenExpr : immed fetch (%d)\n", expr->ee.addr->ae.i);*/ resaddr->mode = immediateValue; resaddr->ma.iValue = expr->ee.addr->ae.i; break; } break; case UsubOp: /* an expression tree with unary at root */ /*printf("GenExpr : UsubOp\n");*/ GenExpr(expr->ee.child, &a1); if(a1.mode == registeroffset) LoadArithReg(resaddr,&a1); else /* a1 is already directregister (immediate has been folded) */ MaCopy (resaddr,&a1); Gen2Op(Ineg,resaddr,resaddr); break; case AddOp: /* an expression tree with binary op at root */ case MpyOp: case SubOp: case DivOp: case EqOp: /* Generate code for child with the largest height to attempt to minimize the need for temporary locations. Note that there are no side effects, so either expression can be evaluated first. */ if(expr->ee.atree.right->height > expr->ee.atree.left->height){ GenExpr(expr->ee.atree.right,&a2); GenExpr(expr->ee.atree.left,&a1); } else{ GenExpr(expr->ee.atree.left,&a1); GenExpr(expr->ee.atree.right,&a2); } switch(expr->op){ case AddOp: /*printf("GenExpr : AddOp\n");*/ Gen3Op(Iadd,resaddr,&a1,&a2); break; case SubOp: /*printf("GenExpr : SubOp\n");*/ Gen3Op(Isub,resaddr,&a1,&a2); break; case MpyOp: /*printf("GenExpr : MpyOp\n");*/ Gen3Op(Imult,resaddr,&a1,&a2); break; case DivOp: /*printf("GenExpr : DivOp\n");*/ Gen3Op(Idiv,resaddr,&a1,&a2); break; case EqOp: Gen3Op(Icomp,resaddr,&a1,&a2); /* perform comparison */ break; /* resaddr is a register with 0/1 */ } /* switch (expr-op) = binary arithmetic */ break; } /* swtich (expr->) */ if (PtraceCode) {/*fprintf(CodeFile,"\n");*/ Leaving("GenExpr");} } /* GenExpr */ /* ------------------------------------------------------------------------ */ /* ---------------- GenReads: GENERATE READ INSTRUCTIONS ----------------- */ /* ------------------------------------------------------------------------ */ static void GenReads(PARAMLIST params) { MACHINEADDR addr; if (PtraceCode) {Entering("GenReads");} while(params != NULL){ addr.mode = registeroffset; addr.ma.memaddr = params->pe.loc->ae.attrs; fprintf(CodeFile,"\tli\t$v0, 5\n"); /* set system call for print_int */ fprintf(CodeFile,"\tsyscall\n"); fprintf(CodeFile,"\tsw\t$v0,"); WriteAddr(&addr); fprintf(CodeFile,"\n"); params = params->next; } if (PtraceCode) {fprintf(CodeFile,"\n"); Leaving("GenReads");} } /* GenReads */ /* ------------------------------------------------------------------------ */ /* ---------------- GenWrites: GENERATE WRITE INSTRUCTIONS --------------- */ /* ------------------------------------------------------------------------ */ static void GenWrites(PARAMLIST params) { MACHINEADDR Valueloc; /* can only write integers initially */ if (PtraceCode) {Entering("GenWrites");} while(params != NULL){ /* Simple Memory references and constant expressions will not be moved to registers */ /* 9-21-93 */ FreeArithRegs(); GenExpr(params->pe.val, &Valueloc); fprintf(CodeFile,"\tli\t$v0, 1\n"); /* set system call for print_int */ if (Valueloc.mode == immediateValue){ fprintf(CodeFile,"\tli \t$a0,"); WriteAddr(&Valueloc); fprintf(CodeFile,"\n"); } else if (Valueloc.mode == registeroffset){ fprintf(CodeFile,"\tlw \t$a0,"); WriteAddr(&Valueloc); fprintf(CodeFile,"\n"); } else { fprintf(CodeFile,"\tmove\t$a0,"); WriteAddr(&Valueloc); fprintf(CodeFile,"\n"); } fprintf(CodeFile,"\tsyscall\n"); params = params->next; } if (PtraceCode) {fprintf(CodeFile,"\n"); Leaving("GenWrites");} } /* GenWrites */ /* ------------------------------------------------------------------------ */ /* --------------- GenStmt: GENERATE CODE FOR A STATEMENT ---------------- */ /* ------------------------------------------------------------------------ */ static void GenStmt(STMTPTR stmt) { MACHINEADDR source, dest; MACHINEADDRPTR outlabel; /* for the if-then */ if (PtraceCode) {Entering("GenStmt");} switch(stmt->type){ case AssignStmt: fprintf(CodeFile,"# \n"); fprintf(CodeFile,"# Generate Assignment Statement \n"); if (PtraceCode) { printf("# Generate Assignment Statement \n"); printf("# \n"); } FreeArithRegs(); GenExpr(stmt->se.assign.rhs,&source); /* source will be - directregister if rhs was an expression, e.g. y:=x+2 - registeroffset if rhs was fetch/memory, e.g. y:=x - immediateValue if rhs was fetch/immediate, e.g. y:=2;*/ if(source.mode != directregister) LoadArithReg(&source,&source); /* must have been a fetch - LoadArithReg must check which one */ dest.mode = registeroffset; dest.ma.memaddr = stmt->se.assign.lhs->ae.attrs; Gen2Op(Store,&source,&dest); break; /* assignment */ case StdProcCall: switch(stmt->se.procall.type){ case ReadlnProc: fprintf(CodeFile,"# \n"); fprintf(CodeFile, "# Generate Readln statement \n"); if (PtraceCode) { printf("# \n"); printf("# Generate Readln statement \n");} GenReads(stmt->se.procall.params); break; case WriteProc: fprintf(CodeFile,"# \n"); fprintf(CodeFile, "# Generate Write statement \n"); if (PtraceCode) { printf("# \n"); printf("# Generate Write statement \n");} GenWrites(stmt->se.procall.params); break; case WritelnProc: fprintf(CodeFile,"# \n"); fprintf(CodeFile, "# Generate Writeln statement \n"); if (PtraceCode) { printf("# \n"); printf("# Generate Writeln statement \n");} GenWrites(stmt->se.procall.params); Gen0Op(Wrln); break; } /* switch(stmt->se.type) */ break; /* StdProcCall */ case IfThenStmt: fprintf(CodeFile,"#\n"); fprintf(CodeFile,"# Generate If-Then statement \n"); if (PtraceCode) { printf("#\n"); printf("# Generate If-Then statement \n");} /* get the outlabel for end of the if structure */ outlabel = mach_alloc(); outlabel->mode = labeladdress; outlabel->ma.labelnumber = nextlabelnum; nextlabelnum++; /* increment global variable as label is set */ GenIfExpr(stmt->se.ifthen.bexpr,outlabel); /* GenIfExpr has evaluated lhs,rhs of btree using GenExpr generating code to jump to outlabel if appropriate */ GenStmtList(stmt->se.ifthen.slist); fprintf(CodeFile,"#\n"); fprintf(CodeFile,"# End If \n"); if (PtraceCode) { printf("#\n"); printf("# End If \n"); } WriteLabel(outlabel); break; } /* switch(stmt->type) */ if (PtraceCode) {fprintf(CodeFile,"\n"); Leaving("GenStmt");} } /* GenStmt */ /* --------------------------------------------------- */ /* ----------------- GenIfExpr ----------------------- */ /* --------------------------------------------------- */ static void GenIfExpr(EXPRTREE bexpr,MACHINEADDRPTR outlabel) { MACHINEADDR result; if (PtraceCode) {Entering("GENIFEXPR");} FreeArithRegs(); GenExpr(bexpr,&result); /* GenExpr returns boolean value in result.ma.regaddr */ /* need to generate code to see if this returned value was true */ fprintf(CodeFile,"\tbeq\t"); WriteAddr(&result); fprintf(CodeFile,", 0,"); WriteAddr(outlabel); fprintf(CodeFile,"\n"); /* Gen2Op(Icomp,&result,immetrue); Gen1Op(Jne,outlabel); */ FreeReg(result.ma.regaddr); if (PtraceCode) {fprintf(CodeFile,"\n"); Leaving("GENIFEXPR");} } /* GenIfExpr */ /* ------------------------------------- */ /* -------------- FreeReg -------------- */ /* ------------------------------------- */ static void FreeReg(int registernum) { if (PtraceCode) {Entering("FREEREG");} regfree[registernum] = TRUE; if (PtraceCode) {fprintf(CodeFile,"\n"); Leaving("FREEREG");} } /* ---------------------------------------- */ /* ------------ write label --------------- */ /* ---------------------------------------- */ static void WriteLabel(MACHINEADDRPTR labeladdr) { if (PtraceCode) {Entering("WRITELABEL");} fprintf(CodeFile, "IF%d:\n",labeladdr->ma.labelnumber); if (PtraceCode) {fprintf(CodeFile,"\n"); Leaving("WRITELABEL");} } /* ------------------------------------------------------------------------ */ /* ----------- GenStmtLIST: GENERATE CODE FOR A STATEMENT LIST ----------- */ /* ------------------------------------------------------------------------ */ static void GenStmtList(STMTPTR stmt) { if (PtraceCode) {Entering("GenStmtLIST");} while(stmt != NULL ){ GenStmt(stmt); stmt = stmt->next; } if (PtraceCode) {fprintf(CodeFile,"\n"); Leaving("GenStmtLIST");} } /* GenStmtList */ /* ------------------------------------------------------------------------ */ /* ----------- GenOffSets: GENERATE REGISTER OFFSETS FOR VARIABLES ------- */ /* ------------------------------------------------------------------------ */ /* Effectively increment a counter by two for every name in the symbol table. * The value returned is the number of variables that are processed. */ static int GenOffsets(VARLIST vars) { int current_offset = 0; int number_of_symbols = 0; if (PtraceCode) {Entering("GenOffSets");} while(vars != NULL ){ vars->cur->offset = current_offset; vars = vars->next; current_offset += 4; /* MDM 07/23/90 */ number_of_symbols++; } if (PtraceCode) {fprintf(CodeFile,"\n"); Leaving("GenOffSets");} return(number_of_symbols); } /* GenOffsets */ /* ------------------------------------------------------------------------ */ /* ----------- GenStorage: GENERATE STORAGE AREA FOR VARIABLES ----------- */ /* ------------------------------------------------------------------------ */ /* This procedure calculates the necessary skip value for the global * * variables. */ /* 07/28/90 MDM */ static void GenStorage(VARLIST vars) { int last_offset = 0; if (PtraceCode) {Entering("GenStorage");} fprintf(CodeFile,"\t.word 0\n"); /* Actually generate storage declaration */ fprintf(CodeFile, "%s:\t\t%s\n", "GVARS","# space for Global Variables"); /* Determine how much storage I need */ if (Vartrace) printf("\nVariable dump from GenStorage \n"); while(vars != NULL ){ if(Vartrace) printf("ID: %-12s\tOffset: %5d\tProc. Level: %d\n", vars->cur->id, vars->cur->offset, vars->cur->level); fprintf(CodeFile,"\t.data\n"); fprintf(CodeFile,"_%s:\t.word 0\t# Offset at %d\n", vars->cur->id,vars->cur->offset); last_offset = vars->cur->offset; vars = vars->next; } if (PtraceCode) {fprintf(CodeFile,"\n"); Leaving("GenStorage");} } /* GenStorage */ /* ------------------------------------------------------------------------ */ /* ------- MaCopy: COPY THE CONTENT OF ONE MACHINADDRESS TO ANOTHER ------ */ /* ------------------------------------------------------------------------ */ static void MaCopy(MACHINEADDRPTR dest,MACHINEADDRPTR source) { if (PtraceCode) {Entering("MaCopy");} dest->mode = source->mode; switch(source->mode){ case directregister : dest->ma.regaddr = source->ma.regaddr; break; case immediateValue : dest->ma.iValue = source->ma.iValue; break; case registeroffset : /* MDM 07/23/90 */ dest->ma.memaddr = source->ma.memaddr; break; } if (PtraceCode) {/*fprintf(CodeFile,"\n");*/ Leaving("MaCopy");} } /* ------------------------------------------------------------------------ */ /* ------- CalcConstOffsets: Calculate the offsets for const list ------- */ /* ------------------------------------------------------------------------ */ /* This procedure traverses the constant linked list and calculates the * * offset off of register 15 required to access the constant string. * * For example, if const_listptr points to the a list containing "fu" * * and "bar", the offset for the node containing "fu" would be zero since * * it will be the first string statement; $t9 contains this address. * * The node containing "bar" would be three since its two bytes plus * null further than where $t9 points. * ****************************************************************************/ static void CalcConstOffsets(void) { int x = 0; struct constlist *ptr = Prog.const_listptr; if (PtraceCode) {Entering("CalcConstOffsets");} while (ptr) { /* Set ptr's offset */ ptr->offset = x; /* Calculate the next offset */ x = ConstLen(ptr->info); /* Point to next element in the list */ ptr = ptr->next; } if (PtraceCode) {fprintf(CodeFile,"\n"); Leaving("CalcConstOffsets");} } /* calc_const_offset */ /* ------------------------------------------------------------------------ */ /* ------------- CONSTLEN: Calculate a given strings length -------------- */ /* ------------------------------------------------------------------------ */ /* SPIM - string length + 1 for null */ static int ConstLen(char *s) { if ( strlen( s ) < 2) return 2; else return 1 + strlen( s ); } /* ConstLen */ /* ------------------------------------------------------------------------ */ /* ------------- CONSTSTORAGE: Output constant storage area -------------- */ /* ------------------------------------------------------------------------ */ /* This procedure actually generates code to declare constant strings. * * For example, if the constant linked list contained "fu" and "bar", then * * the following would be generated in the CodeFile: * * * * LABEL CONST * * STRING "fu" * * STRING "bar" * ****************************************************************************/ static void ConstStorage(void) { struct constlist *list_ptr = Prog.const_listptr; int str_ctr = 0; if (PtraceCode) Entering("ConstStorage"); /* align on word boundaries */ fprintf(CodeFile,"\t.word 0\n"); /* Generate label for this area */ fprintf(CodeFile, "CONST:\t\t#Constant storage area\n"); /* Output the constants */ while (list_ptr) { fprintf(CodeFile,"\t.data\n"); /* Display string constant. Notice I include quotes */ fprintf(CodeFile, "S%d:\t%s\t\"%s\"\n", str_ctr, ".asciiz",list_ptr->info); str_ctr++; /* Next node please */ list_ptr = list_ptr->next; } /* end of while list to go */ if (PtraceCode) {fprintf(CodeFile,"\n"); Leaving("ConstStorage");} } /* ConstStorage() */