/* See test/38.[ic] Current bug with recordformats: field 'flags' in pass1.i is being generated as 'n1' Note: pass1 generates a tag for the recordformat. We generate a cell containing that tag, plus all the field names in the attached array. However, when pass1 generates code for a record, and attaches the recordformat tag number to it, it is the original one, not our modified one. Solution is to set the 'tag' field of the recordformat tag to point to the generated cell. (Be careful to avoid loops) */ #ifdef NEVER // test/27 int main (int argc, char **argv) { auto void jim (int x, int y, int z); // ^^^^ add auto so that forward declarations work properly void fred (int i, int j, int k) { jim (k, j, i); return; // ^^^^ remove (0) (may have been inserted assuming end of program) } void jim (int p, int q, int r) { p = (q + r); } // ^^^ make sure closing braces are there at outer level fred (11, 22, 33); return(0); // ^^^^^^^^^^ make sure final return or exit is present, // and final closing brace } #endif /* Current difficulties with C generation: the jumps around procedures appear to cause problems with gcc which doesn't like code coming before nested procedures. We'll probably have to exbed all declarations so they come first (or code so it comes last - same thing, different approach) but a cheap hack for most programs which don't have procedures in the middle of data will be to edit pass1 and remove the jumps around the procedures: def lab(0) %IF block tag # 0 %AND level # 1; !for jump around c(130):block x = x op('F', 0) %IF decl&spec = 0 %AND level # 0; !jump round proc The problem with exbedding is that subsequent declarations may shadow variables, eg %begin %routine outer(%integer i) %end %routine inner %routine test1 %end outer(nl) %routine outer(%integer i) %end test1 %end inner %endofprogram This can probably be worked around by name mangling... */ //#define GDBARG "test/4" #include #include #include #include #include #include #include #include #include #include "auto_gdb.c" //#include "../../../../devel/mnemosyne/mnemosyne.h" int memprintf(char *s, ...); /* WORK IN PROGRESS: trying to generate low-level C as an alternative to native code generation for icode. I'm actually programming right here in this web directory, so this file is quite liable to be broken at any time you might look at it, and indeed I might be editing it *right now*. So by all means browse to see how development is coming along, but don't take copies until it is released. You'll know when that is, because it will start having version numbers attached. */ #ifndef FALSE #define TRUE (0==0) #define FALSE (0!=0) #endif /* these three communication variables are a bit of a hack, but make parsing parameter lists and record formats much much easier */ static int RECORDFORMAT = FALSE; static int SPEC = FALSE; static int PROCFNMAPPRED = FALSE; static int debug_input = FALSE; static int opt_safeforloop = FALSE; static int opt_safenames = FALSE; static int opt_c_only = FALSE; static char *indent = " "; char *imp_dirname; FILE *cur_impfile; FILE *icode_file; FILE *source_file; int source_line = 0; #define MAX_STACK 1024 int cstack[MAX_STACK]; /* compile-time stack */ static int *cstackp = cstack; /*static int next_stack = 0;*/ static int nest_proc = 0; /* depth of procedure/begin blocks */ #define FOR_LOOP 1 #define OTHER_LOOP 2 static int loop_type[128]; /* real for loop or one of our fake ones? */ static int nest_loop = 0; /* depth of structured loops */ char *cond[256]; char *rcond[256]; #include "lines.c" #include "tags.c" #include "names.c" #include "io.c" #include "codegen.c" void check_empty_stack(void) { if (cstackp != cstack) { memprintf("STACK:\n"); while (cstackp != cstack) { memprintf(" %s\n", tag_rhs_value(cpop())); } *(int *)0 = 0; } } int main (int argc, char **argv) { char *p1, *p2, *p3, *p4, *p5; int i1/*, i2, i3, i4, i5*/; short h1, h2/*, h3, h4, h5*/; /* half. 's' is for strings */ int opcode; short int d; int w; char icode_filename[256], source_filename[256]; // restart_under_gdb(argc, argv); /* Note: pass1 is not handling %include well. Could just be a problem with the imp80 runtime, however the icode definition for this version doesn't appear to have the include filename support that later ones have, which is needed for good source diagnostics. It may be necessary to rebuild from the Mouses pass1 without Andy's mods. */ #include "init.c" opcode = get_icode (icode_file); for (;;) { if (opcode == EOF) break; /* used[opcode] = TRUE; */ /* if invoked with the option for coverage testing (not yet written) we log every opcode used, and check at the end to see if any were omitted. The option should be invoked only when testing with a special imp program carefully designed to generate every possible icode supported by pass1. */ switch (opcode) { case '\n': break; case '$': /* DEF TAG TEXT TYPE FORM SIZE SPEC PREFIX */ { /* create the descriptor. If the object requires space, create it and set 'data' to point to it. Otherwise stuff the actual data into 'data' */ short int deftag, deftagcopy, a, b, c; int started = FALSE; p1 = getvar (&deftag); p2 = getname (); assert (get_icode (icode_file) == ','); p3 = strdup (getshort (&a)); assert (get_icode (icode_file) == ','); p4 = strdup (getshort (&b)); assert (get_icode (icode_file) == ','); p5 = getshort (&c); /* define may set one of these ... */ RECORDFORMAT = FALSE; SPEC = FALSE; PROCFNMAPPRED = FALSE; memprintf("%s", indent); define_tag(deftag, strdup(p2) /* heap lossage */, a, b, c); /* may set one of above */ deftagcopy = deftag; free (p3); free (p4); /* At the start of a procedure, bump the lexical level, and note the current tag pointer in an array indexed by lexical level. on exit from the procedure, the tag pointer will be reset to that value, thus losing all declarations made within the procedure. (This also applies to %begin/%end blocks which are implemented as anonymous procedures. Sort of. */ if (!(RECORDFORMAT || PROCFNMAPPRED || SPEC)) { memprintf(";\n"); break; } opcode = get_icode (icode_file); if (opcode != '{' /* START */) { /* if procedure, if spec output: (...); if body output: (...) { if recordformat output: (....) FORMATNAME; */ memprintf("/*2*/;\n"); continue; /* back to dispatch loop */ } /* now pick up parameters and attach to definition */ push(deftag); memprintf("("); while (opcode != '}' /* FINISH */) { if (started) memprintf(", "); if (opcode == '$') started = TRUE; if (RECORDFORMAT) { /* add another variable to format */ //memprintf("*** Declaring record format\n"); /* opcode may be DEF (or ALTBEG/ALT/ALTEND???) */ if (opcode == '$') { int fieldtag; started = TRUE; p1 = getvar (NULL); p2 = getname (); assert (get_icode (icode_file) == ','); p3 = strdup (getshort (&a)); assert (get_icode (icode_file) == ','); p4 = strdup (getshort (&b)); assert (get_icode (icode_file) == ','); p5 = getshort (&c); /* fieldtag is always 0 so issue one of our own */ fieldtag = makenewinternaltag(); define_tag(fieldtag, strdup(p2), a, b, c); // need sos = recordforat, tos = field push(fieldtag); //memprintf("adding record field %s deftag=%d fieldtag=%d\n", p2, deftag, fieldtag); asspar(); /* identical to adding a param to a procdeure */ deftag = cpop(); cpush(deftag); /* asspar modifies deftag when it adds params */ assert(deftag != deftagcopy); tag[deftagcopy].b = deftag; /* is b the correct field to use? */ free (p3); free (p4); } /* else error? */ } else if (PROCFNMAPPRED) { /* add another parameter to procedure */ /* opcode may only be DEF */ if (opcode == '$') { /* reads tags and attaches */ p1 = getvar (&deftag); p2 = getname (); //memprintf("adding parameter/local declaration %s\n", p2); assert (get_icode (icode_file) == ','); p3 = strdup (getshort (&a)); assert (get_icode (icode_file) == ','); p4 = strdup (getshort (&b)); assert (get_icode (icode_file) == ','); p5 = getshort (&c); define_tag(deftag, strdup(p2), a, b, c); free (p3); free (p4); } } else if (SPEC) { /* add another parameter to procedure */ /* opcode may only be DEF */ if (opcode == '$') { /* reads tags and attaches */ p1 = getvar (&deftag); p2 = getname (); //memprintf("adding dummy format parameter %s\n", p2); assert (get_icode (icode_file) == ','); p3 = strdup (getshort (&a)); assert (get_icode (icode_file) == ','); p4 = strdup (getshort (&b)); assert (get_icode (icode_file) == ','); p5 = getshort (&c); define_tag(deftag, strdup(p2), a, b, c); /* definitions are lost almost immediately on the closing bracket of the parameter list */ free (p3); free (p4); } } else { /* ERROR */ assert(opcode == '$'); } opcode = get_icode (icode_file); } memprintf(")"); (void)cpop(); if (SPEC||RECORDFORMAT) memprintf(";\n"); else if (PROCFNMAPPRED) memprintf("\n{\n"); else /* ERROR */; /* FINISH has no parameters */ } break; case '"': /* JUMPIFD cond label */ /* leaves tos on the stack */ /* to translate this properly we should assign tos to a temp (local off the stack) and replace tos with the temp for the subequent JUMPIF. Otherwise if the evaluation of TOS has side-effects, they will occur twice. */ case '?': /* JUMPIF cond label */ /* if you have the JUMPIF first then the LOCATE clears it. */ /* if you have the LOCATE first then the REPEAT clears it. */ p1 = getcond (&i1); p2 = getforwardlab (&h2); ifcond(opcode, i1, h2); break; case 'k': /* BF label */ case 't': /* BT label */ p2 = getforwardlab (&h2); ifcond(opcode, opcode, h2); break; case 'C': /* JUMPIFA cond label */ p1 = getcond (&i1); p2 = getforwardlab (&h2); ifcond(opcode, i1, h2); break; case '\'': /* PUSHS sconst */ (void)getimpstring (&p1); pushs(p1); break; case 'D': /* PUSHR rconst */ memprintf("%s/* TODO */ %s %s\n", indent, icode_name[opcode], getwordconst (NULL)); break; case 'N': /* PUSHI iconst */ (void)getwordconst (&w); pushi(w); break; case 'L': /* LABEL label - permanent, not wiped? */ (void)getshort(&d); memprintf("%s:\n", setuserlab (d)); // memprintf("%s: /* LABEL (user) */\n", setuserlab (d)); break; case ':': /* LOCATE label */ { int linenod; (void)getshort(&d); if (lineno[d] != 0) { memprintf("%s:\n", setforwardlab (d)); break; } else { memprintf("%sfor (;;) {\n%s:\n", indent, setbackwardlab (d, &linenod)); loop_type[nest_loop] = OTHER_LOOP; nest_loop += 1; break; } // memprintf("%s: /* LOCATE label */\n", setforwardlab (d)); break; } case 'B': /* REPEAT label */ { int linenod, saved; nest_loop -= 1; (void)getshort(&d); saved = lineno[d]; p1 = setrepeatlab (d, &linenod); if (loop_type[nest_loop] == FOR_LOOP) { // memprintf("L_%04x: /* %%continue */\n", linenod); } memprintf("%s}\n", indent); // memprintf("%s} /* end loop L_%04x */\n", indent, saved); if (loop_type[nest_loop] == FOR_LOOP) { // memprintf("L_%04x: /* %%exit */\n", linenod+1); } } break; case 'F': /* GOTO label */ /* check for nest and if id matches, map to an exit/continue */ memprintf("%sgoto %s;\n", indent, getforwardlab (NULL)); break; case 'J': /* JUMP label */ memprintf("%sgoto %s;\n", indent, getuserlab ()); break; case 'f': /* FOR label (label was missing from thesis description) */ p1 = getbackwardlab (&d); p2 = nextlab (d+1); /* ASSUME EXIT LABEL IS IMPLICITLY NEXT LABEL */ for_loop(opcode, nest_loop, d, d+1); loop_type[nest_loop] = FOR_LOOP; nest_loop += 1; break; case 'O': /* LINE decimal */ p1 = getshortdecimal (&d); if (!opt_c_only) memprintf("#line %d\n", d + 1); /* output source file up to line %d */ if (!opt_c_only && (source_file != NULL)) { while (source_line < d + 1) { if (fgets (line, MAX_LINE, source_file) == NULL) break; source_line += 1; memprintf("//%6d ", source_line); line[MAX_LINE] = '\0'; memprintf("%s\n", line, stdout); } } break; case '@': /* PUSH tag */ (void)getvar (&d); push(d); break; case '^': /* PROC tag */ memprintf("%s%s %s\n", indent, icode_name[opcode], getvar (NULL)); break; case 'n': /* SELECT tag */ { short int subtag; (void)getshort (&subtag); cpush(subtag); fieldselect(opcode); // memprintf("%s%s %d\n", indent, icode_name[opcode], subtag); } break; case 'W': /* SJUMP sd */ (void)getshortdecimal (&h1), memprintf("%sgoto %s[%s];\n", indent, tag_rhs_value(h1), tag_rhs_value(cpop())); break; /* You can specify a range of consecutive values in a single case label, like this: case low ... high: This has the same effect as the proper number of individual case labels, one for each integer value from low to high, inclusive. This feature is especially useful for ranges of ASCII character codes: case 'A' ... 'Z': Be careful: Write spaces around the ..., for otherwise it may be parsed wrongly when you use it with integer values. For example, write this: case 1 ... 5: rather than this: case 1...5: */ case '_': /* SLABEL sd */ (void)getshortdecimal (&h1), memprintf("%s_%s:\n", /* no indent */ tag_rhs_value(h1), tag_rhs_value(cpop())); break; case 'd': /* DIM short,short */ { int i; short int numdecls, numbounds; p1 = strdup (getshort (&numdecls)); assert (get_icode (icode_file) == ','); p2 = getshort (&numbounds); for (i = 0; i < numbounds; i++) { (void)cpop(); (void)cpop(); } check_empty_stack(); memprintf("%s%s %s %s\n", indent, icode_name[opcode], p1, p2); free (p1); } break; /* In GNU C you can give the elements in any order, specifying the array indices or structure field names they apply to. This extension is not implemented in GNU C++. To specify an array index, write `[index]' or `[index] =' before the element value. For example, int a[6] = { [4] 29, [2] = 15 }; To initialize a range of elements to the same value, write `[first ... last] = value'. For example, int widths[] = { [0 ... 9] = 1, [10 ... 99] = 2, [100] = 3 }; */ case 'b': /* BOUNDS */ p1 = tag_rhs_value(cpop()); p2 = tag_rhs_value(cpop()); memprintf("%s// bounds [%s : %s]\n", indent, p2, p1); break; case 'Y': /* DEFAULT short */ // memprintf("%s%s %s\n", // indent, icode_name[opcode], p1); break; case 'A': /* INIT short */ p1 = getshortdecimal(NULL); if (cstackp == cstack) { /* init to unass pattern */ } else { (void)cpop(); } // memprintf("%s%s %s\n", // indent, icode_name[opcode], p1); break; case 'e': /* EVENT short */ cpop(); /* pop 2nd int in %signal 15,15 */ memprintf("// %s%s %s\n", indent, icode_name[opcode], getshort (NULL)); break; case 'l': /* LANG short */ getshort (&h1); if (h1 == 0) { if (!opt_c_only) memprintf("\n/*\n %s\n */\n", "Edinburgh IMP77 Compiler - Version 8.4"); } else { memprintf("// %s%s %04x\n", indent, icode_name[opcode], h1); } break; case 'o': /* ON byte, short mask, short label */ (void)getshort(&h1); assert (get_icode (icode_file) == ','); p1 = getforwardlab (NULL); memprintf("%sif (imp_onevent(0x%04x)) goto %s /* returns FALSE during longjump setup */\n", indent, h1, p1); break; case 'r': /* RESOLVE m */ memprintf("%s/* TODO */ %s %s\n", indent, icode_name[opcode], getbyte ()); break; case '!': /* OR */ case '%': /* XOR */ case '&': /* AND */ case '*': /* MUL */ case 'u': /* ADDA */ /* don't yet know what this does */ case '+': /* ADD */ // sos_op_tos(opcode); // break; case '-': /* SUB */ case '/': /* QUOT */ case 'Q': /* DIVIDE (reversed params) */ case '[': /* LSH */ case ']': /* RSH */ sos_op_tos(opcode); break; case '.': /* CONCAT */ cpop(); /* TODO */ // sos_op_tos(opcode); break; case 'v': /* MOD */ case '\\': /* NOT */ case 'U': /* NEGATE */ monop(opcode); break; case 'x': /* REXP */ case 'X': /* IEXP */ call_tos_sos(opcode); break; case 'S': /* ASSVAL */ assval(); break; case 'Z': /* ASSREF */ assref(); break; case 'i': /* INDEX */ /* TODO multi-dimensional arrays */ break; case 'a': /* ACCESS */ arrayaccess(opcode); break; case 'm': /* MONITOR *//* Should really invoke imp diag routine */ memprintf("%sassert(FALSE);\n", indent); break; case 'H': /* BEGIN *//* Assume main program for now, fix later */ nest_proc += 1; memprintf("int main(int argc, char **argv) {\n"); break; case ';': /* END */ nest_proc -= 1; /* memprintf("%sEND %d\n", indent, nest_proc); */ if (nest_proc < 0) break; /* probably end of file */ if (nest_proc == 0) { memprintf("%sreturn(0);\n", indent); /* I hope this is end of program */ } memprintf("}\n"); /* end of any begin/end or proc */ break; case '{': /* START */ /* Could lookahead for this opcode after $DEF of a record format or a procedure/fn/map/pred [spec?]. Or... keep a variable noting the address of forementioned in opcode/tag array and hook these on to last active item. */ memprintf("(/*should not get here any more*/"); break; case '}': /* FINISH */ memprintf("/*or here*/)\n"); break; /* start and end of variant records */ case 'h': /* ALTBEG */ case '|': /* ALT */ case 'q': /* ALTEND */ break; case 'p': /* ASSPAR */ asspar(); break; case 'E': /* CALL */ call(); break; case 'K': /* FALSE */ case 'T': /* TRUE */ break; case 'M': /* MAP */ /* TODO */ case 'V': /* RESULT */ result(opcode); /* tos */ break; case 'R': /* RETURN */ memprintf("%sreturn;\n", indent); break; case 's': /* STOP */ memprintf("%sexit(0);\n", indent); break; case 'j': /* JAM */ /* like assign? */ assval(); break; case 'y': /* DIAG */ case 'z': /* CONTROL */ p1 = getshort (&d); break; case '#': /* BNE possibly not implemented */ case 'G': /* ALIAS */ /* See http://gcc.gnu.org/onlinedocs/gcc/Asm-Labels.html#Asm%20Labels */ case 'P': /* PLANT */ memprintf("%s%s\n", indent, icode_name[opcode]); break; case 'c': /* MCODE */ memprintf("%s*** icode documentation bug\n", indent); break; case 'w': /* SUBA */ /* appears to actually be MCODE */ { int i1; short int h1; memprintf("%sasm {", indent); for (;;) { p1 = getmcstring(&i1); memprintf(" %s", p1); if (i1 == ';') break; p2 = getshort(&h1); memprintf("%s", tag_rhs_value(h1)); } memprintf("};\n"); } break; default: memprintf("?%s%s\n", indent, icode_name[opcode]); break; } opcode = get_icode (icode_file); } if (!opt_c_only && (source_file != NULL)) { for (;;) { /* mop up any remaining source lines */ if (fgets (line, MAX_LINE, source_file) == NULL) break; source_line += 1; memprintf("//%6d ", source_line); line[MAX_LINE] = '\0'; memprintf("%s\n", line, stdout); } } check_empty_stack(); exit (0); return (0); }