blob: f4859dc06d5d97a0a11012829a03a99e7ca8e6a3 [file] [log] [blame]
#include "supp.h"
//#define DEBUG_MARK
static char FILE_[]=__FILE__;
//#include "version.h"
char cg_copyright[]="not for public release";
int g_flags[MAXGF]={};
char *g_flags_name[MAXGF]={};
union ppi g_flags_val[MAXGF];
struct reg_handle empty_reg_handle={0};
extern int handle_pragma(const char * c){return 0;}
//support for ISR
char *g_attr_name[] = {"__interrupt", 0};
#define INTERRUPT 1
/*
* Define registers codes
*/
#define R0 1 //zero register
#define R1 2 //reserved for compiler
#define R2 3 //reserved for compiler
#define R3 4 //reserved for compiler
#define R4 5 //condition codes
#define R5 6 //return value
#define R6 7
#define R7 8
#define R8 9
#define R9 10
#define R10 11
#define R11 12
#define R12 13
#define FP 14 //frame pointer
#define PC 15 //program counter
#define SP 16 //stack pointer
/*
* Custom function
*/
//evalue compare IC and prepare condition codes in R3 (COMPARE IC is followed by BRANCH allways)
void compare(FILE *f, struct IC *p);
//helper function for loading obj o into register dest_reg
void load_into_reg(FILE *f, int dest_reg, struct obj *o, int type, int tmp_reg);
//store reg into obj o
void store_from_reg(FILE *f, int source_reg, struct obj *o, int type, int tmp_reg, int tmp_reg_b);
//take care about all arithmetic IC
void arithmetic(FILE *f, struct IC *p);
//load constant into register
void load_cons(FILE *f, int reg, long int value);
/*
* Data Types
*/
zmax char_bit; // CHAR_BIT for the target machine.
zmax align[MAX_TYPE+1]; // Alignment-requirements for all types in bytes.
zmax maxalign; // Alignment that is sufficient for every object.
zmax sizetab[MAX_TYPE+1]; // sizes of the basic types (in bytes)
// Minimum and Maximum values each type can have.
zmax t_min[MAX_TYPE+1];
zumax t_max[MAX_TYPE+1];
zumax tu_max[MAX_TYPE+1];
/*
* Register Set
*/
/*
* Names of all registers. will be initialized in init_cg(),
* register number 0 is invalid, valid registers start at 1
*/
char *regnames[MAXR+1];
/*
* The Size of each register in bytes.
*/
zmax regsize[MAXR+1];
/*
* Specifies which registers may be scratched by functions.
*/
int regscratch[MAXR+1];
/*
* a type which can store each register.
*/
struct Typ *regtype[MAXR+1];
/* regsa[reg]!=0 if a certain register is allocated and should
* not be used by the compiler pass.
*/
int regsa[MAXR+1];
/*
* specifies the priority for the register-allocator, if the same
* estimated cost-saving can be obtained by several registers, the
* one with the highest priority will be used
*/
int reg_prio[MAXR+1];
/*
* Does necessary initializations for the code-generator. Gets called
* once at the beginning and should return 0 in case of problems.
*/
int init_cg(void){
#ifdef DEBUG_MARK
printf("Called init_cg()\n");
#endif
int i;
maxalign=l2zm(1L);
char_bit=l2zm(32L);
for(i=0;i<=MAX_TYPE;i++){
align[i] = l2zm(1L);
sizetab[i] = l2zm(1L);
}
t_min[CHAR] = zmsub(l2zm(-2147483647L),l2zm(1L));
t_min[SHORT] = t_min[CHAR];
t_min[INT] = t_min[CHAR];
t_min[LONG] = t_min[CHAR];
t_min[LLONG] = t_min[CHAR];
t_min[MAXINT] = t_min[CHAR];
t_max[CHAR] = ul2zum(2147483647UL);
t_max[SHORT] = t_max[CHAR];
t_max[INT] = t_max[CHAR];
t_max[LONG] = t_max[CHAR];
t_max[LLONG] = t_max[CHAR];
t_max[MAXINT] = t_max[CHAR];
tu_max[CHAR]=ul2zum(4294967295UL);
tu_max[SHORT] = tu_max[CHAR];
tu_max[INT] = tu_max[CHAR];
tu_max[LONG] = tu_max[CHAR];
tu_max[LLONG] = tu_max[CHAR];
tu_max[MAXINT] = tu_max[CHAR];
regnames[0] = "noreg";
reg_prio[0] = 0;
regscratch[0] = 0;
regsa[0] = 0;
//zero register
regnames[1] = "R0";
reg_prio[1] = 0;
regscratch[1] = 0;
regsa[1] = 1;
//R1 reserved for backed
regnames[2] = "R1";
reg_prio[2] = 0;
regscratch[2] = 0;
regsa[2] = 1;
//R2 reserved for backed
regnames[3] = "R2";
reg_prio[3] = 0;
regscratch[3] = 0;
regsa[3] = 1;
//R3 reserved for backed
regnames[4] = "R3";
reg_prio[4] = 0;
regscratch[4] = 0;
regsa[4] = 1;
//R4 condition codes
regnames[5] = "R4";
reg_prio[5] = 0;
regscratch[5] = 0;
regsa[5] = 1;
//R5 return value for function
regnames[6] = "R5";
reg_prio[6] = 0;
regscratch[6] = 0;
regsa[6] = 1;
regnames[7] = "R6";
reg_prio[7] = 0;
regscratch[7] = 0;
regsa[7] = 0;
regnames[8] = "R7";
reg_prio[8] = 0;
regscratch[8] = 0;
regsa[8] = 0;
regnames[9] = "R8";
reg_prio[9] = 0;
regscratch[9] = 0;
regsa[9] = 0;
regnames[10] = "R9";
reg_prio[10] = 0;
regscratch[10] = 0;
regsa[10] = 0;
regnames[11] = "R10";
reg_prio[11] = 0;
regscratch[11] = 0;
regsa[11] = 0;
regnames[12] = "R11";
reg_prio[12] = 0;
regscratch[12] = 0;
regsa[12] = 0;
regnames[13] = "R12";
reg_prio[13] = 0;
regscratch[13] = 0;
regsa[13] = 0;
// Frame pointer
regnames[FP] = "R13";
reg_prio[14] = 0;
regscratch[14] = 0;
regsa[14] = 1;
// Program counter
regnames[15] = "PC";
reg_prio[15] = 0;
regscratch[15] = 0;
regsa[15] = 1;
// Stack pointer
regnames[16] = "SP";
reg_prio[16] = 0;
regscratch[16] = 0;
regsa[16] = 1;
for(i=0;i<=MAXR;i++){
regsize[i] = l2zm(1L);
}
// Use multiple ccs
multiple_ccs = 0;
return 1;
}
void cleanup_cg(FILE *f){
#ifdef DEBUG_MARK
printf("Called cleanup_cg()\n");
#endif
}
/*
* Returns the register in which variables of type t are returned.
* If the value cannot be returned in a register returns 0.
*/
int freturn(struct Typ *t){
return R5;
}
/*
* Returns 0 if register r cannot store variables of
* type t. If t==POINTER and mode!=0 then it returns
* non-zero only if the register can store a pointer
* and dereference a pointer to mode.
*/
int regok(int r,int t,int mode){
return 1;
}
/*
* Returns zero if the IC p can be safely executed
* without danger of exceptions or similar things.
* vbcc may generate code in which non-dangerous ICs
* are sometimes executed although control-flow may
* never reach them (mainly when moving computations
* out of loops).
* Typical ICs that generate exceptions on some
* machines are:
* - accesses via pointers
* - division/modulo
* - overflow on signed integer/floats
*/
int dangerous_IC(struct IC *p){
return 0;
}
/*
* Returns zero if code for converting np to type t
* can be omitted.
* On the PowerPC cpu pointers and 32bit
* integers have the same representation and can use
* the same registers.
*/
int must_convert(int o,int t,int const_expr){
return 0;
}
int shortcut(int code,int typ){
return 0;
}
/*
* The main code-generation routine.
* f is the stream the code should be written to.
* p is a pointer to a doubly linked list of ICs
* containing the function body to generate code for.
* v is a pointer to the function.
* offset is the size of the stackframe the function
* needs for local variables.
*/
void gen_code(FILE *f,struct IC *p,struct Var *v,zmax offset){
#ifdef DEBUG_MARK
printf("Called gen_code(FILE *f,struct IC *p,struct Var *v,zmax offset)\n");
printf("\tIdentifier: %s", v->identifier);
#endif
//emit function head
if(v->storage_class==EXTERN){
if( (v->flags & (INLINEFUNC|INLINEEXT)) != INLINEFUNC ){
emit(f,".EXPORT \t %s \n",v->identifier);
}
emit(f,"%s: \n",v->identifier);
}
else{
emit(f,"L_%ld:\n",zm2l(v->offset));
}
//emit function prologue
emit(f, "\tPUSH \t %s\n", regnames[FP]); //push FP
emit(f, "\tOR \t %s %s %s\n",regnames[R0], regnames[SP], regnames[FP]); //MOVE SP -> FP
//make space for auto variables at stack
for(int i = 0; i < zm2l(offset); i++){
emit(f, "\tDEC \t %s %s\n", regnames[SP], regnames[SP]);
}
//store backend registers
emit(f, "\tPUSH \t R1\n\tPUSH \t R2\n\tPUSH \t R3\n\tPUSH \t R4\n");
//find used registers
int saved_regs[7] = {0, 0, 0, 0, 0, 0, 0};
struct IC *ps = p;
for(;ps;ps=ps->next){
if( ((ps->code) != FREEREG) && ((ps->code) != ALLOCREG) ){
if(((ps->q1.flags) & (KONST|VAR|REG|DREFOBJ|VARADR)) == REG){
if(((ps->q1.reg) > R5) && ((ps->q1.reg) < FP)){
saved_regs[(ps->q1.reg) - 7] = 1;
}
}
if(((ps->q2.flags) & (KONST|VAR|REG|DREFOBJ|VARADR)) == REG){
if(((ps->q2.reg) > R5) && ((ps->q2.reg) < FP)){
saved_regs[(ps->q2.reg) - 7] = 1;
}
}
if(((ps->z.flags) & (KONST|VAR|REG|DREFOBJ|VARADR)) == REG){
if(((ps->z.reg) > R5) && ((ps->z.reg) < FP)){
saved_regs[(ps->z.reg) - 7] = 1;
}
}
}
}
//save used registers
for(int i = 0; i < 7; i++){
if(saved_regs[i] == 1){
emit(f, "\tPUSH \t %s\n", regnames[i + 7]);
}
}
//emit function body
for(;p;p=p->next){
int c = p->code;
#ifdef DEBUG_MARK
emit(f, "\n\t;p->code: %d\n", p->code);
#endif
switch(p->code){
case ASSIGN:
#ifdef DEBUG_MARK
printf("\n\tASSIGN\n\tz.flags:%d\tq1.flags:%d\ttypf:%d\n", p->z.flags, p->q1.flags, p->typf);
#endif
//we can simplify assign when both operands are in registers
if((((p->q1.flags) & (KONST|VAR|REG|DREFOBJ|VARADR)) == REG) &&
(((p->z.flags) & (KONST|VAR|REG|DREFOBJ|VARADR)) == REG) ){
emit(f, "\tOR \t %s %s %s",regnames[R0], regnames[p->q1.reg], regnames[p->z.reg]);
}
//this is another optimalization, if have to assign zero; then
//zero is read from R0 insted pushing constant into register
else if(
(((p->q1.flags) & (KONST|VAR|REG|DREFOBJ|VARADR)) == KONST) &&
((p->q1.val.vmax) == 0)
){
store_from_reg(f, R0, &(p->z), p->typf, R2, R3);
}
else{
load_into_reg(f, R1, &(p->q1), p->typf, R2);
store_from_reg(f, R1, &(p->z), p->typf, R2, R3);
}
break;
case OR:
#ifdef DEBUG_MARK
printf("\n\tOR\n");
#endif
arithmetic(f, p);
break;
case XOR:
#ifdef DEBUG_MARK
printf("\n\tXOR\n");
#endif
arithmetic(f, p);
break;
case AND:
#ifdef DEBUG_MARK
printf("\n\tAND\n");
#endif
arithmetic(f, p);
break;
case LSHIFT:
#ifdef DEBUG_MARK
printf("\n\tLSHIFT\n");
#endif
arithmetic(f, p);
break;
case RSHIFT:
#ifdef DEBUG_MARK
printf("\n\tRSHIFT\n");
#endif
arithmetic(f, p);
break;
case ADD:
#ifdef DEBUG_MARK
printf("\n\tADD\n");
#endif
arithmetic(f, p);
break;
case SUB:
#ifdef DEBUG_MARK
printf("\n\tSUB\n");
#endif
arithmetic(f, p);
break;
case MULT:
#ifdef DEBUG_MARK
printf("\n\tMULT\n");
#endif
arithmetic(f, p);
break;
case DIV:
#ifdef DEBUG_MARK
printf("\n\tDIV\n");
#endif
arithmetic(f, p);
break;
case MOD:
#ifdef DEBUG_MARK
printf("\n\tMOD\n");
#endif
arithmetic(f, p);
break;
case KOMPLEMENT:
#ifdef DEBUG_MARK
printf("\n\tKOMPLEMENT\n");
#endif
arithmetic(f, p);
break;
case MINUS:
#ifdef DEBUG_MARK
printf("\n\tMINUS\n");
#endif
arithmetic(f, p);
break;
case ADDRESS:
#ifdef DEBUG_MARK
printf("\n\tADDRESS\n");
#endif
if(
(((p->q1.flags) & (KONST|VAR|REG|DREFOBJ|VARADR)) == VAR) &&
(((p->q1.v->storage_class) & (AUTO|REGISTER|STATIC|EXTERN)) == AUTO)
){
if(ISARRAY(p->q1.v->flags)){
load_cons(f, R1, zm2l(p->q1.v->offset)+zm2l(p->q1.val.vmax));
}
else{
load_cons(f, R1, zm2l(p->q1.v->offset));
}
emit(f, "\tADD \t R1 R13 R1\n");
store_from_reg(f, R1, &(p->z), p->typf, R2, R3);
}
else{
ierror(0);
}
break;
case CALL:
#ifdef DEBUG_MARK
printf("\n\tCALL\n\tq1.flags: %d\n", p->q1.flags);
#endif
if((p->q1.flags & (VAR|DREFOBJ)) == VAR && p->q1.v->fi && p->q1.v->fi->inline_asm){
emit_inline_asm(f,p->q1.v->fi->inline_asm);
}
else{
if(((p->q1.flags) & (KONST|VAR|REG|DREFOBJ|VARADR)) == VAR){
#ifdef DEBUG_MARK
printf("\tq1.v->storage_class: %d\n", p->q1.v->storage_class);
#endif
switch((p->q1.v->storage_class) & (AUTO|REGISTER|STATIC|EXTERN)){
case EXTERN:
emit(f, "\tCALL \t %s\n", p->q1.v->identifier);
for(int i = 0; i < (p->q2.val.vmax); i++){
emit(f, "\tINC \t %s %s\n", regnames[SP], regnames[SP]);
}
break;
case STATIC:
emit(f, "\tCALL \t L_%ld\n", zm2l(p->q1.v->offset));
for(int i = 0; i < (p->q2.val.vmax); i++){
emit(f, "\tINC \t %s %s\n", regnames[SP], regnames[SP]);
}
break;
default:
#ifdef DEBUG_MARK
printf("\tThis is not implemented!\n");
#else
ierror(0);
#endif
break;
}
}
else if(((p->q1.flags) & (KONST|VAR|REG|DREFOBJ|VARADR)) == (VAR|DREFOBJ)){
#ifdef DEBUG_MARK
printf("\tq1.v->storage_class: %d\n", p->q1.v->storage_class);
#endif
load_into_reg(f, R1, &(p->q1), p->typf, R3);
emit(f, "\tCALLI\t %s\n", regnames[R1]);
emit(f, "\tINC \t %s %s\n", regnames[SP], regnames[SP]);
}
else{
#ifdef DEBUG_MARK
printf("\tThis is not implemented!\n");
#else
ierror(0);
#endif
}
}
break;
case CONVERT:
#ifdef DEBUG_MARK
printf("\n\tCONVERT\n");
#endif
break;
case ALLOCREG:
#ifdef DEBUG_MARK
printf("\n\tALLOCREG\n");
#endif
regs[p->q1.reg] = 1;
break;
case FREEREG:
#ifdef DEBUG_MARK
printf("\n\tFREEREG\n");
#endif
regs[p->q1.reg] = 0;
break;
case COMPARE:
#ifdef DEBUG_MARK
printf("\n\tCOMPARE\n");
#endif
compare(f, p);
break;
case TEST:
#ifdef DEBUG_MARK
printf("\n\tTEST\n");
#endif
compare(f, p);
break;
case LABEL:
#ifdef DEBUG_MARK
printf("\n\tLABEL\n");
#endif
emit(f,"L_%d:\n",p->typf);
break;
case BEQ:
#ifdef DEBUG_MARK
printf("\n\tBEQ\n");
#endif
emit(f, "\tBNZ \t R4 L_%d\n", p->typf);
break;
case BNE:
#ifdef DEBUG_MARK
printf("\n\tBNE\n");
#endif
emit(f, "\tBNZ \t R4 L_%d\n", p->typf);
break;
case BLT:
#ifdef DEBUG_MARK
printf("\n\tBLT\n");
#endif
emit(f, "\tBNZ \t R4 L_%d\n", p->typf);
break;
case BGE:
#ifdef DEBUG_MARK
printf("\n\tBGE\n");
#endif
emit(f, "\tBNZ \t R4 L_%d\n", p->typf);
break;
case BLE:
#ifdef DEBUG_MARK
printf("\n\tBLE\n");
#endif
emit(f, "\tBNZ \t R4 L_%d\n", p->typf);
break;
case BGT:
#ifdef DEBUG_MARK
printf("\n\tBGT\n");
#endif
emit(f, "\tBNZ \t R4 L_%d\n", p->typf);
break;
case BRA:
#ifdef DEBUG_MARK
printf("\n\tBRA\n");
#endif
emit(f, "\tBZ \t R0 L_%d\n", p->typf);
break;
case PUSH:
#ifdef DEBUG_MARK
printf("\n\tPUSH\n");
#endif
load_into_reg(f, R1, &(p->q1), p->typf, R2);
emit(f, "\tPUSH \t R1\n");
break;
case ADDI2P:
#ifdef DEBUG_MARK
printf("\n\tADDI2P\n");
#endif
arithmetic(f, p);
break;
case SUBIFP:
#ifdef DEBUG_MARK
printf("\n\tSUBIFP\n");
#endif
arithmetic(f, p);
break;
case SUBPFP:
#ifdef DEBUG_MARK
printf("\n\tSUBPFP\n");
#endif
arithmetic(f, p);
break;
case GETRETURN:
#ifdef DEBUG_MARK
printf("\n\tGETRETURN\n");
#endif
if((p->q1.reg) != 0){
store_from_reg(f, p->q1.reg, &(p->z), p->typf, R2, R3);
}
else{
#ifdef DEBUG_MARK
printf("\tq1.reg == 0, didn't know how to dealt with it!");
#else
ierror(0);
#endif
}
break;
case SETRETURN:
#ifdef DEBUG_MARK
printf("\n\tSETRETURN\n\tz.flags:%d\n", p->z.flags);
#endif
if((p->z.reg) != 0){
load_into_reg(f, p->z.reg, &(p->q1), p->typf, R1);
}
else{
#ifdef DEBUG_MARK
printf("\tz.reg == 0, didn't know how to dealt with it!");
#else
ierror(0);
#endif
}
break;
case MOVEFROMREG:
#ifdef DEBUG_MARK
printf("\n\tMOVEFROMREG\n");
#endif
store_from_reg(f, p->q1.reg, &(p->z), p->typf, R1, R3);
break;
case MOVETOREG:
#ifdef DEBUG_MARK
printf("\n\tMOVETOREG\n");
#endif
load_into_reg(f, p->z.reg, &(p->q1), p->typf, R1);
break;
case NOP:
#ifdef DEBUG_MARK
printf("\n\tNOP\n");
#endif
break;
default:
#ifdef DEBUG_MARK
printf("\tSomething is wrong in gencode()!\n");
#else
ierror(0);
#endif
break;
}
}
//restore used registers
for(int i = 6; i >= 0; i--){
if(saved_regs[i] == 1){
emit(f, "\tPOP \t %s\n", regnames[i + 7]);
}
}
//restore backend registers
emit(f, "\tPOP \t R4\n\tPOP \t R3\n\tPOP \t R2\n\tPOP \t R1\n");
//emit function epilogue
emit(f, "\tOR \t %s %s %s\n",regnames[R0], regnames[FP], regnames[SP]); //restore SP from FP
emit(f, "\tPOP \t %s\n", regnames[FP]); //restore old FP from stack
//return
if((v->tattr)&INTERRUPT){
emit(f, "\tRETI\n");
}
else{
emit(f, "\tRET\n");
}
}
/*
* This function has to create <size> bytes of storage
* initialized with zero.
*/
void gen_ds(FILE *f,zmax size,struct Typ *t){
#ifdef DEBUG_MARK
printf("Called gen_ds(FILE *f,zmax size,struct Typ *t)\n");
#endif
emit(f, "\t.DS \t%ld\n", zm2l(size));
}
/*
* This function has to make sure the next data is
* aligned to multiples of <align> bytes.
*/
void gen_align(FILE *f,zmax align){}
/*
* This function has to create the head of a variable
* definition, i.e. the label and information for
* linkage etc.
*/
void gen_var_head(FILE *f,struct Var *v){
#ifdef DEBUG_MARK
printf("Called gen_var_head(FILE *f,struct Var *v)\n");
#endif
switch((v->storage_class) & (STATIC|EXTERN|AUTO|REGISTER)){
case STATIC:
#ifdef DEBUG_MARK
printf("\tHave to emit static variable head.\n");
#endif
emit(f,"L_%ld:\n", zm2l(v->offset));
break;
case EXTERN:
#ifdef DEBUG_MARK
printf("\tHave to emit extern variable head.\n");
#endif
if(v->flags&(DEFINED|TENTATIVE)){
emit(f,".EXPORT \t %s\n", v->identifier);
emit(f,"%s:\n", v->identifier);
}
else{
emit(f,".IMPORT \t %s\n", v->identifier);
}
break;
default:
#ifdef DEBUG_MARK
printf("\tCant generate head, unknown storage class: %d\n", v->storage_class);
#else
ierror(0);
#endif
break;
}
}
/*
* This function has to create static storage
* initialized with const-list p.
*/
void gen_dc(FILE *f,int t,struct const_list *p){
#ifdef DEBUG_MARK
printf("Called gen_dc(FILE *f,int t,struct const_list *p)\n");
#endif
if(!p->tree){
if(ISFLOAT(t)){
emit(f,"\t.DAT \t ");
emit(f,"0x%x", *(unsigned int*)&p->val);
emit(f,"\n");
}
else{
emit(f,"\t.DAT \t ");
emitval(f,&p->val,t&NU);
emit(f,"\n");
}
}
else{
emit(f,"\t.DAT \t ");
struct const_list *p_next = p;
for(;p_next;p_next=p_next->next){
emitval(f,&p_next->val,t&NU);
emit(f, " ");
}
emit(f, "\n");
}
}
//this is for debug, not needed now
void init_db(FILE *f){}
void cleanup_db(FILE *f){}
int reg_parm(struct reg_handle *m, struct Typ *t,int vararg,struct Typ *d){
//this will put all arguments into stack
return 0;
}
/*
* Returns 0 if the register is no register pair. If r
* is a register pair non-zero will be returned and the
* structure pointed to p will be filled with the two
* elements.
*/
int reg_pair(int r,struct rpair *p){
return 0;
}
void compare(FILE *f, struct IC *p){
int q1reg = 0;
int q2reg = 0;
//load operands into R1 and R2
if(((p->q1.flags) & (KONST|VAR|REG|DREFOBJ|VARADR)) != REG){
load_into_reg(f, R1, &(p->q1), p->typf, R3);
q1reg = R1;
}
else{
q1reg = p->q1.reg;
}
if((p->code) != TEST){
if(((p->q2.flags) & (KONST|VAR|REG|DREFOBJ|VARADR)) != REG){
load_into_reg(f, R2, &(p->q2), p->typf, R3);
q2reg = R2;
}
else{
q2reg = p->q2.reg;
}
}
else{
q2reg = R2;
emit(f, "\tOR \t %s %s %s\n",regnames[R0], regnames[R0], regnames[R2]);
}
//find branch IC
struct IC *branch_ic;
branch_ic = p->next;
while(branch_ic && ((branch_ic->code) == FREEREG) ) {
branch_ic = branch_ic->next;
}
//emit compare code
if (((p->typf) & FLOAT) == FLOAT || ((p->typf) & DOUBLE) == DOUBLE || ((p->typf) & LDOUBLE) == LDOUBLE){
switch(branch_ic->code){
case BEQ:
emit(f, "\tCMPF \t EQ %s %s R4\n", regnames[q1reg], regnames[q2reg]);
break;
case BNE:
emit(f, "\tCMPF \t NEQ %s %s R4\n", regnames[q1reg], regnames[q2reg]);
break;
case BLT:
emit(f, "\tCMPF \t L %s %s R4\n", regnames[q1reg], regnames[q2reg]);
break;
case BGE:
emit(f, "\tCMPF \t GE %s %s R4\n", regnames[q1reg], regnames[q2reg]);
break;
case BLE:
emit(f, "\tCMPF \t LE %s %s R4\n", regnames[q1reg], regnames[q2reg]);
break;
case BGT:
emit(f, "\tCMPF \t G %s %s R4\n", regnames[q1reg], regnames[q2reg]);
break;
default:
ierror(0);
break;
}
}
else{
switch(branch_ic->code){
case BEQ:
emit(f, "\tCMPI \t EQ %s %s R4\n", regnames[q1reg], regnames[q2reg]);
break;
case BNE:
emit(f, "\tCMPI \t NEQ %s %s R4\n", regnames[q1reg], regnames[q2reg]);
break;
case BLT:
if((p->typf & UNSIGNED) == UNSIGNED){
emit(f, "\tCMPI \t LU %s %s R4\n", regnames[q1reg], regnames[q2reg]);
}
else{
emit(f, "\tCMPI \t L %s %s R4\n", regnames[q1reg], regnames[q2reg]);
}
break;
case BGE:
if((p->typf & UNSIGNED) == UNSIGNED){
emit(f, "\tCMPI \t GEU %s %s R4\n", regnames[q1reg], regnames[q2reg]);
}
else{
emit(f, "\tCMPI \t GE %s %s R4\n", regnames[q1reg], regnames[q2reg]);
}
break;
case BLE:
if((p->typf & UNSIGNED) == UNSIGNED){
emit(f, "\tCMPI \t LEU %s %s R4\n", regnames[q1reg], regnames[q2reg]);
}
else{
emit(f, "\tCMPI \t LE %s %s R4\n", regnames[q1reg], regnames[q2reg]);
}
break;
case BGT:
if((p->typf & UNSIGNED) == UNSIGNED){
emit(f, "\tCMPI \t GU %s %s R4\n", regnames[q1reg], regnames[q2reg]);
}
else{
emit(f, "\tCMPI \t G %s %s R4\n", regnames[q1reg], regnames[q2reg]);
}
break;
default:
ierror(0);
break;
}
}
}
void load_into_reg(FILE *f, int dest_reg, struct obj *o, int type, int tmp_reg){
switch((o->flags) & (KONST|VAR|REG|DREFOBJ|VARADR)){
case KONST:
load_cons(f, dest_reg, o->val.vmax);
break;
case (KONST|DREFOBJ):
//place memory location constant point to into register
emit(f, "\tLD \t ");
emitval(f, &(o->val), type);
emit(f, " %s\n", regnames[dest_reg]);
break;
case REG:
//move between registers
if((o->reg) != dest_reg){
emit(f, "\tOR \t %s %s %s\n",regnames[R0], regnames[o->reg], regnames[dest_reg]);
}
break;
case VAR:
//put value of variable into register
switch((o->v->storage_class) & (STATIC|EXTERN|AUTO|REGISTER)){
case STATIC:
if(zm2l(o->val.vmax) != 0){
emit(f, "\tMVIA \t %s L_%ld\n", regnames[dest_reg], zm2l(o->v->offset));
load_cons(f, tmp_reg, zm2l(o->val.vmax));
emit(f, "\tADD \t %s %s %s\n", regnames[dest_reg], regnames[tmp_reg], regnames[tmp_reg]);
emit(f, "\tLDI \t %s %s\n", regnames[tmp_reg], regnames[dest_reg]);
}
else{
emit(f, "\tLD \t L_%ld %s\n", zm2l(o->v->offset), regnames[dest_reg]);
}
break;
case EXTERN:
if(zm2l(o->val.vmax) != 0){
emit(f, "\tMVIA \t %s %s\n", regnames[dest_reg], o->v->identifier);
load_cons(f, tmp_reg, zm2l(o->val.vmax));
emit(f, "\tADD \t %s %s %s\n", regnames[dest_reg], regnames[tmp_reg], regnames[tmp_reg]);
emit(f, "\tLDI \t %s %s\n", regnames[tmp_reg], regnames[dest_reg]);
}
else{
emit(f, "\tLD \t %s %s\n", o->v->identifier , regnames[dest_reg]);
}
break;
case AUTO:
if((o->v->offset) < 0){
//this is argument
load_cons(f, dest_reg, (zm2l(o->v->offset)/(-1L))+2+zm2l(o->val.vmax));
emit(f, "\tADD \t %s %s %s\n", regnames[dest_reg],regnames[FP], regnames[tmp_reg]);
emit(f, "\tLDI \t %s %s\n", regnames[tmp_reg], regnames[dest_reg]);
}
else{
//this is auto variable
int offset = zm2l(o->v->offset)+zm2l(o->val.vmax);
if(offset == 0){
emit(f, "\tLDI \t %s %s\n", regnames[FP], regnames[dest_reg]);
}
else if(offset == 1){
emit(f, "\tDEC \t %s %s\n", regnames[FP], regnames[tmp_reg]);
emit(f, "\tLDI \t %s %s\n", regnames[tmp_reg], regnames[dest_reg]);
}
else{
load_cons(f, dest_reg, offset);
emit(f, "\tSUB \t %s %s %s\n", regnames[FP],regnames[dest_reg], regnames[tmp_reg]);
emit(f, "\tLDI \t %s %s\n", regnames[tmp_reg], regnames[dest_reg]);
}
}
break;
default:
#ifdef DEBUG_MARK
printf("\tHave to load variable that is not static, extern or auto, this is not implemented!\n");
#else
ierror(0);
#endif
break;
}
break;
case (VAR|REG):
if((o->reg) != dest_reg){
emit(f, "\tOR \t %s %s %s\n", regnames[R0], regnames[o->reg], regnames[dest_reg]);
}
break;
case (REG|DREFOBJ):
//point into memory with register value
emit(f, "\tLDI \t %s %s\n", regnames[o->reg], regnames[dest_reg]);
break;
case (VAR|DREFOBJ):
//use variable value as pointer to memory
switch((o->v->storage_class) & (STATIC|EXTERN|AUTO|REGISTER)){
case STATIC:
if(zm2l(o->val.vmax) != 0){
emit(f, "\tMVIA \t %s L_%ld\n", regnames[dest_reg], zm2l(o->v->offset));
load_cons(f, tmp_reg, zm2l(o->val.vmax));
emit(f, "\tADD \t %s %s %s\n", regnames[dest_reg], regnames[tmp_reg], regnames[dest_reg]);
emit(f, "\tLDI \t %s %s\n", regnames[dest_reg], regnames[tmp_reg]);
}
else{
emit(f, "\tLD \t L_%ld %s\n", zm2l(o->v->offset), regnames[tmp_reg]);
}
emit(f, "\tLDI \t %s %s\n", regnames[tmp_reg], regnames[dest_reg]);
break;
case EXTERN:
if(zm2l(o->val.vmax) != 0){
emit(f, "\tMVIA \t %s %s\n", regnames[dest_reg], o->v->identifier);
load_cons(f, tmp_reg, zm2l(o->val.vmax));
emit(f, "\tADD \t %s %s %s\n", regnames[dest_reg], regnames[tmp_reg], regnames[dest_reg]);
emit(f, "\tLDI \t %s %s\n", regnames[dest_reg], regnames[tmp_reg]);
}
else{
emit(f, "\tLD \t %s %s\n", o->v->identifier , regnames[tmp_reg]);
}
emit(f, "\tLDI \t %s %s\n", regnames[tmp_reg], regnames[dest_reg]);
break;
case AUTO:
if((o->v->offset) < 0){
//this is argument
load_cons(f, dest_reg, (zm2l(o->v->offset)/(-1L))+2+zm2l(o->val.vmax));
emit(f, "\tADD \t %s %s %s\n", regnames[dest_reg],regnames[FP], regnames[tmp_reg]);
emit(f, "\tLDI \t %s %s\n", regnames[tmp_reg], regnames[dest_reg]);
}
else{
//this is auto variable
int offset = zm2l(o->v->offset)+zm2l(o->val.vmax);
if(offset == 0){
emit(f, "\tLDI \t %s %s\n", regnames[FP], regnames[dest_reg]);
}
else if(offset == 1){
emit(f, "\tDEC \t %s %s\n", regnames[FP], regnames[tmp_reg]);
emit(f, "\tLDI \t %s %s\n", regnames[tmp_reg], regnames[dest_reg]);
}
else{
load_cons(f, dest_reg, offset);
emit(f, "\tSUB \t %s %s %s\n", regnames[FP],regnames[dest_reg], regnames[tmp_reg]);
emit(f, "\tLDI \t %s %s\n", regnames[tmp_reg], regnames[dest_reg]);
}
}
break;
default:
#ifdef DEBUG_MARK
printf("\tHave to load variable that is not static, extern or auto, this is not implemented!\n");
#else
ierror(0);
#endif
break;
}
emit(f, "\tLDI \t %s %s\n", regnames[dest_reg], regnames[tmp_reg]);
emit(f, "\tOR \t R0 %s %s\n", regnames[tmp_reg], regnames[dest_reg]);
break;
case (VAR|REG|DREFOBJ):
if((o->reg) != dest_reg){
emit(f, "\tOR \t %s %s %s\n",regnames[R0], regnames[o->reg], regnames[tmp_reg]);
}
emit(f, "\tLDI \t %s %s\n", regnames[tmp_reg], regnames[dest_reg]);
break;
case (VAR|VARADR):
//into dest_reg store address of variable
switch((o->v->storage_class) & (STATIC|EXTERN)){
case EXTERN:
emit(f, "\tMVIA \t %s %s\n", regnames[dest_reg], o->v->identifier);
break;
case STATIC:
emit(f, "\tMVIA \t %s L_%ld\n", regnames[dest_reg], zm2l(o->v->offset));
break;
default: //this is pointless storage_class can be only static or extern with VARADR
ierror(0);
}
//this is useful when object is array and we want adres of nonfirst element
if(o->val.vmax > 0){
load_cons(f, tmp_reg, o->val.vmax);
emit(f, "\tADD \t %s %s %s\n", regnames[dest_reg], regnames[tmp_reg], regnames[dest_reg]);
}
break;
default:
#ifdef DEBUG_MARK
printf("\tSomething is wrong while acuring operand!\n");
#else
ierror(0);
#endif
break;
}
}
void store_from_reg(FILE *f, int source_reg, struct obj *o, int type, int tmp_reg, int tmp_reg_b){
switch((o->flags) & (KONST|VAR|REG|DREFOBJ|VARADR)){
case KONST:
//How can I store register into KONST?!
ierror(0);
break;
case (KONST|DREFOBJ):
//use konstant as pointer into memory
emit(f, "\tST \t %s ", regnames[source_reg]);
emitval(f, &(o->val), type);
emit(f, "\n");
break;
case REG:
//move from register into register
if(source_reg != (o->reg)){
emit(f, "\tOR \t %s %s %s\n",regnames[R0], regnames[source_reg], regnames[o->reg]);
}
break;
case VAR:
//load register into variable
switch((o->v->storage_class) & (STATIC|EXTERN|AUTO|REGISTER)){
case STATIC:
if(zm2l(o->val.vmax) != 0){
emit(f, "\tMVIA \t %s L_%ld\n", regnames[tmp_reg], zm2l(o->v->offset));
load_cons(f, tmp_reg_b, zm2l(o->val.vmax));
emit(f, "\tADD \t %s %s %s\n", regnames[tmp_reg], regnames[tmp_reg_b], regnames[tmp_reg]);
emit(f, "\tSTI \t %s %s\n", regnames[source_reg], regnames[tmp_reg]);
}
else{
emit(f, "\tST \t %s L_%ld\n", regnames[source_reg], zm2l(o->v->offset));
}
break;
case EXTERN:
if(zm2l(o->val.vmax) != 0){
emit(f, "\tMVIA \t %s %s\n", regnames[tmp_reg], o->v->identifier);
load_cons(f, tmp_reg_b, zm2l(o->val.vmax));
emit(f, "\tADD \t %s %s %s\n", regnames[tmp_reg], regnames[tmp_reg_b], regnames[tmp_reg]);
emit(f, "\tSTI \t %s %s\n", regnames[source_reg], regnames[tmp_reg]);
}
else{
emit(f, "\tST \t %s %s\n", regnames[source_reg], o->v->identifier);
}
break;
case AUTO:
if((o->v->offset) < 0){
//function argument
load_cons(f, tmp_reg, (zm2l(o->v->offset)/(-1L))+2+zm2l(o->val.vmax));
emit(f, "\tADD \t %s %s %s\n", regnames[tmp_reg],regnames[FP], regnames[tmp_reg]);
emit(f, "\tSTI \t %s %s\n", regnames[source_reg], regnames[tmp_reg]);
}
else{
//auto variable
int offset = zm2l(o->v->offset)+zm2l(o->val.vmax);
if (offset == 0){
emit(f, "\tSTI \t %s %s\n", regnames[source_reg], regnames[FP]);
}
else if(offset == 1){
emit(f, "\tDEC \t %s %s\n", regnames[FP], regnames[tmp_reg]);
emit(f, "\tSTI \t %s %s\n", regnames[source_reg], regnames[tmp_reg]);
}
else{
load_cons(f, tmp_reg, offset);
emit(f, "\tSUB \t %s %s %s\n", regnames[FP],regnames[tmp_reg], regnames[tmp_reg]);
emit(f, "\tSTI \t %s %s\n", regnames[source_reg], regnames[tmp_reg]);
}
}
break;
default:
#ifdef DEBUG_MARK
printf("\tHave to store into variable that is not static, extern or auto, this is not implemented!\n");
#else
ierror(0);
#endif
break;
}
break;
case (VAR|REG):
emit(f, "\tOR \t %s %s %s\n", regnames[R0], regnames[source_reg], regnames[o->reg]);
break;
case (REG|DREFOBJ):
//use value in register as pointer into memory
emit(f, "\tSTI \t %s %s\n", regnames[source_reg], regnames[o->reg]);
break;
case (VAR|DREFOBJ):
//use value in variable as pointer into memory
switch((o->v->storage_class) & (STATIC|EXTERN|AUTO|REGISTER)){
case STATIC:
if(zm2l(o->val.vmax) != 0){
emit(f, "\tMVIA \t %s L_%ld\n", regnames[tmp_reg], zm2l(o->v->offset));
load_cons(f, tmp_reg_b, zm2l(o->val.vmax));
emit(f, "\tADD \t %s %s %s\n", regnames[tmp_reg], regnames[tmp_reg_b], regnames[tmp_reg_b]);
emit(f, "\tLDI \t %s %s\n", regnames[tmp_reg_b], regnames[tmp_reg]);
}
else{
emit(f, "\tLD \t L_%ld %s\n", zm2l(o->v->offset), regnames[tmp_reg]);
}
emit(f, "\tSTI \t %s %s\n", regnames[source_reg], regnames[tmp_reg]);
break;
case EXTERN:
if(zm2l(o->val.vmax) != 0){
emit(f, "\tMVIA \t %s %s\n", regnames[tmp_reg], o->v->identifier);
load_cons(f, tmp_reg_b, zm2l(o->val.vmax));
emit(f, "\tADD \t %s %s %s\n", regnames[tmp_reg], regnames[tmp_reg_b], regnames[tmp_reg_b]);
emit(f, "\tLDI \t %s %s\n", regnames[tmp_reg_b], regnames[tmp_reg]);
}
else{
emit(f, "\tLD \t %s %s\n", o->v->identifier, regnames[tmp_reg] );
}
emit(f, "\tSTI \t %s %s\n", regnames[source_reg], regnames[tmp_reg]);
break;
case AUTO:
if((o->v->offset) < 0){
//function argument
load_cons(f, tmp_reg, (zm2l(o->v->offset)/(-1L))+2+zm2l(o->val.vmax));
emit(f, "\tADD \t %s %s %s\n", regnames[tmp_reg],regnames[FP], regnames[tmp_reg_b]);
emit(f, "\tLDI \t %s %s\n", regnames[tmp_reg_b], regnames[tmp_reg]);
emit(f, "\tSTI \t %s %s\n", regnames[source_reg], regnames[tmp_reg]);
}
else{
//auto variable
int offset = zm2l(o->v->offset)+zm2l(o->val.vmax);
if(offset == 0){
emit(f, "\tLDI \t %s %s\n", regnames[FP], regnames[tmp_reg_b]);
}
else if(offset == 1){
emit(f, "\tDEC \t %s %s\n", regnames[FP], regnames[tmp_reg_b]);
emit(f, "\tLDI \t %s %s\n", regnames[tmp_reg_b], regnames[tmp_reg]);
}
else{
load_cons(f, tmp_reg, offset);
emit(f, "\tSUB \t %s %s %s\n", regnames[FP],regnames[tmp_reg], regnames[tmp_reg]);
emit(f, "\tLDI \t %s %s\n", regnames[tmp_reg], regnames[tmp_reg_b]);
}
emit(f, "\tSTI \t %s %s\n", regnames[source_reg], regnames[tmp_reg_b]);
}
break;
default:
#ifdef DEBUG_MARK
printf("\tHave to store into variable that is not static, extern or auto, this is not implemented!\n");
#else
ierror(0);
#endif
break;
}
break;
case (VAR|REG|DREFOBJ):
emit(f, "\tSTI \t %s %s\n", regnames[source_reg], regnames[o->reg]);
break;
case (VAR|VARADR): //use variable address as pointer
switch(o->v->storage_class){
case STATIC:
emit(f, "\tMVIA \t %s L_%ld\n", regnames[tmp_reg], zm2l(o->v->offset));
emit(f, "\tSTI \t %s %s\n", regnames[source_reg], regnames[tmp_reg]);
break;
case EXTERN:
emit(f, "\tMVIA \t %s %s\n", regnames[tmp_reg], o->v->identifier);
emit(f, "\tSTI \t %s %s\n", regnames[source_reg], regnames[tmp_reg]);
break;
default: //can be only static or extern
ierror(0);
break;
}
break;
default:
#ifdef DEBUG_MARK
printf("\tCant store reg into object, unknown object!\n");
#else
ierror(0);
#endif
break;
}
}
void arithmetic(FILE *f, struct IC *p){
int q1reg = 0;
int q2reg = 0;
int zreg = 0;
int movez = 0;
int unary = 0;
int isunsigned = 0;
if (((p->typf) & UNSIGNED) == UNSIGNED){
isunsigned = 1;
}
if(((p->code) == MINUS) || ((p->code) == KOMPLEMENT)){
unary = 1;
}
//load first operand
if(((p->q1.flags) & (KONST|VAR|REG|DREFOBJ|VARADR)) == REG){
q1reg = p->q1.reg;
}
else{
load_into_reg(f, R1, &(p->q1), p->typf, R3);
q1reg = R1;
}
//load second operand
if(unary != 1){
if(((p->q2.flags) & (KONST|VAR|REG|DREFOBJ|VARADR)) == REG){
q2reg = p->q2.reg;
}
else{
load_into_reg(f, R2, &(p->q2), p->typf, R3);
q2reg = R2;
}
}
//prepare target register
if(((p->z.flags) & (KONST|VAR|REG|DREFOBJ|VARADR)) == REG){
zreg = p->z.reg;
}
else{
zreg = R1;
movez = 1;
}
if (((p->typf) & FLOAT) == FLOAT || ((p->typf) & DOUBLE) == DOUBLE || ((p->typf) & LDOUBLE) == LDOUBLE){
switch(p->code){
case ADD:
emit(f, "\tFADD \t ");
break;
case SUB:
emit(f, "\tFSUB \t ");
break;
case MULT:
emit(f, "\tFMUL \t ");
break;
case DIV:
emit(f, "\tFDIV \t ");
break;
case MINUS:
load_cons(f, R2, 0x3f800000);
emit(f, "\tSUB \t ");
unary = 0;
q2reg = R2;
break;
default:
#ifdef DEBUG_MARK
printf("This is not implemented!\n");
#else
ierror(0);
#endif
break;
}
}
else{
//emit instruction opcode
switch(p->code){
case OR:
emit(f, "\tOR \t ");
break;
case XOR:
emit(f, "\tXOR \t ");
break;
case AND:
emit(f, "\tAND \t ");
break;
case LSHIFT:
emit(f, "\tLSL \t ");
break;
case RSHIFT:
if(isunsigned == 1) {
emit(f, "\tLSR \t ");
}
else{
emit(f, "\tASR \t ");
}
break;
case ADD:
emit(f, "\tADD \t ");
break;
case SUB:
emit(f, "\tSUB \t ");
break;
case MULT:
if(isunsigned == 1) {
emit(f, "\tMULU \t ");
}
else{
emit(f, "\tMUL \t ");
}
break;
case DIV:
if(isunsigned == 1) {
emit(f, "\tDIVU \t ");
}
else{
emit(f, "\tDIV \t ");
}
break;
case MOD:
if(isunsigned == 1) {
emit(f, "\tREMU \t ");
}
else{
emit(f, "\tREM \t ");
}
break;
case ADDI2P:
emit(f, "\tADD \t ");
break;
case SUBIFP:
emit(f, "\tSUB \t ");
break;
case SUBPFP:
emit(f, "\tSUB \t ");
break;
case MINUS:
emit(f, "\tDEC \t ");
break;
case KOMPLEMENT:
emit(f, "\tNOT \t ");
break;
default:
#ifdef DEBUG_MARK
printf("\tPassed invalid IC into arithmetic()\n\tp->code: %d\n", p->code);
#else
ierror(0);
#endif
break;
}
}
//emit instruction arguments
if(unary != 1){
emit(f, "%s %s %s\n", regnames[q1reg], regnames[q2reg], regnames[zreg]);
}
else{
emit(f, "%s %s\n", regnames[q1reg], regnames[zreg]);
}
if(movez == 1){
store_from_reg(f, R1, &(p->z), p->typf, R2, R3);
}
}
void load_cons(FILE *f, int reg, long int value){
if(value == 0){
emit(f, "\tOR \t R0 R0 %s\n", regnames[reg]);
}
else if((16777216 > value) && (value > 0)){
emit(f, "\tMVIA \t %s %ld\n", regnames[reg], value);
}
else if (value > 0){
emit(f, "\t.MVI \t %s %ld\n", regnames[reg], value);
}
else{
emit(f, "\t.MVI \t %s 0x%x\n", regnames[reg], value);
}
}