diff --git a/assignments/PA5/cgen.cc b/assignments/PA5/cgen.cc index cf52926..36887e0 100644 --- a/assignments/PA5/cgen.cc +++ b/assignments/PA5/cgen.cc @@ -316,6 +316,15 @@ static void emit_store_int(char *source, char *dest, ostream &s) { emit_store(source, DEFAULT_OBJFIELDS, dest, s); } +// +// Fetch the boolean value in an Bool object. +// Emits code to fetch the boolean value of the Bool object pointed +// to by register source into the register dest +// +static void emit_fetch_bool(char *dest, char *source, ostream &s) { + emit_load(dest, DEFAULT_OBJFIELDS, source, s); +} + static void emit_test_collector(ostream &s) { emit_push(ACC, s); emit_move(ACC, SP, s); // stack end @@ -976,61 +985,311 @@ CgenNode::CgenNode(Class_ nd, Basicness bstatus, CgenClassTableP ct) #pragma region ExpressionCoding -void assign_class::code(ostream &s) {} +void assign_class::code(ostream &s, CgenClassTable *classtab) {} -void static_dispatch_class::code(ostream &s) {} +void static_dispatch_class::code(ostream &s, CgenClassTable *classtab) {} -void dispatch_class::code(ostream &s) {} +void dispatch_class::code(ostream &s, CgenClassTable *classtab) {} -void cond_class::code(ostream &s) {} +/* + There's nothing special for if-then-else. Write at your convenience. + Note that value of the predicate is a Bool object, not a boolean, so, dont + forget to extract its value from the object +*/ +void cond_class::code(ostream &s, CgenClassTable *classtab) { + this->pred->code(s, classtab); + emit_fetch_bool(ACC, ACC, s); // fetch the boolean value into $a0 + auto label_index_false = classtab->alloc_label_index(); + auto label_index_exit = classtab->alloc_label_index(); + emit_beqz(ACC, label_index_false, s); // $a0==0, pred is false, goto else + this->then_exp->code(s, classtab); // pred is true, eval then branch + emit_branch(label_index_exit, s); // $a0 <- then, and we goto exit + emit_label_def(label_index_false, s); + // the label is preserved in advance so that we can define it here safely + this->else_exp->code(s, classtab); // $a0 <- else + emit_label_def(label_index_exit, s); +} -void loop_class::code(ostream &s) {} +/* + The code for loop is also straightforward. The only thing special is that, + loop always evaluate to void, so remember to set $a0<-0 +*/ +void loop_class::code(ostream &s, CgenClassTable *classtab) { + auto label_index_predicate = classtab->alloc_label_index(); + emit_label_def(label_index_predicate, s); + // predefine a label and our loop could restart and check predicate from here + this->pred->code(s, classtab); // check predicate, which sets $a0<-Bool object + emit_fetch_bool(ACC, ACC, s); // extrace 0 or 1 from Bool object in $a0 + auto label_index_exit = classtab->alloc_label_index(); + emit_beqz(ACC, label_index_exit, s); // if predicate is false, goto exit + this->body->code(s, classtab); // if predicate is true, move on with our loop + emit_branch(label_index_predicate, s); // anyways, go back and check predicate + emit_label_def(label_index_exit, s); + emit_load_imm(ACC, 0, s); // whatever the case, loop expr evaluates to void! +} -void typcase_class::code(ostream &s) {} +void typcase_class::code(ostream &s, CgenClassTable *classtab) {} -void block_class::code(ostream &s) {} +/* + For block expression, generate code expression by expression, nothing else +*/ +void block_class::code(ostream &s, CgenClassTable *classtab) { + for (auto expr_i = this->body->first(); this->body->more(expr_i); + expr_i = this->body->next(expr_i)) { + auto expr = this->body->nth(expr_i); + expr->code(s, classtab); + } +} -void let_class::code(ostream &s) {} +void let_class::code(ostream &s, CgenClassTable *classtab) {} -void plus_class::code(ostream &s) {} +/* + Operations for Int objects is the most simple one, let's start here + In previous stages, we have make sure that both ends are Int type. Thus, some + hard-coded stuff is acceptable. + First, we evaluate the left part and the result, the address of the Int + object, is in $a0. In the very beginning, I think it is a good idea to keep + it simple, so I'd prefer not to allocate temporary space on stack ahead. + The solution here is to push $a0 to stack. + Then evaluate the right part. + At this time, we have addr(e1) on stack top and addr(e2) in $a0. According to + imagination (>_<), the right thing to do is to extract the value attributes + of the two Int objects (at offset 12), add them up, create a new Int + object and set its value attribute to the result. Finally store the address + of the new Int in $a0 as the return value. + To actually perform these operations, assuming that we are not going to use + any registers other than $a0 and $t0, we need to write some MIPS assembly. +*/ +void plus_class::code(ostream &s, CgenClassTable *classtab) { + this->e1->code(s, classtab); + emit_fetch_int(ACC, ACC, s); // load value of e1 to a0 + emit_push(ACC, s); // push the 32bit int value instead of object ref + this->e2->code(s, classtab); + emit_jal("Object.copy", s); // copy e2 as the new Int object + emit_push(ACC, s); // save new object's addr, will need it later + emit_fetch_int(ACC, ACC, s); // load value of e2 to a0 + emit_load(T1, 2, SP, s); + // load value of e1 to t1, remind we have pushed again, so -8($sp) is val(e1) + emit_add(T1, T1, ACC, s); // $t1 <- val(e1) + val(e2) + emit_load(ACC, 1, SP, s); // load new object addr + emit_store_int(T1, ACC, s); // store result(now in $t1) to the object + emit_addiu(SP, SP, 8, s); // maintain stack balance +} -void sub_class::code(ostream &s) {} +/* + Substraction is quite the same as addition, the only thing to note is to get + the substraction order right. e1-e2 != e2-e1, but e1+e2 == e2+e1 +*/ +void sub_class::code(ostream &s, CgenClassTable *classtab) { + this->e1->code(s, classtab); + emit_fetch_int(ACC, ACC, s); + emit_push(ACC, s); + this->e2->code(s, classtab); + emit_jal("Object.copy", s); + emit_push(ACC, s); + emit_fetch_int(ACC, ACC, s); // $a0 <- val(e2) + emit_load(T1, 2, SP, s); // $t1 <- val(e1) + emit_sub(T1, T1, ACC, s); // $t1 <- val(e1),$t1 + val(e2),$a0 + emit_load(ACC, 1, SP, s); + emit_store_int(T1, ACC, s); + emit_addiu(SP, SP, 8, s); +} -void mul_class::code(ostream &s) {} +/* + Thanks to pseudo-instruction in spim emulator, we dont need to deal with + hi/lo registers by hand, so the overall code is just the same as addition +*/ +void mul_class::code(ostream &s, CgenClassTable *classtab) { + this->e1->code(s, classtab); + emit_fetch_int(ACC, ACC, s); + emit_push(ACC, s); + this->e2->code(s, classtab); + emit_jal("Object.copy", s); + emit_push(ACC, s); + emit_fetch_int(ACC, ACC, s); // $a0 <- val(e2) + emit_load(T1, 2, SP, s); // $t1 <- val(e1) + emit_mul(T1, T1, ACC, s); // $t1 <- val(e1),$t1 + val(e2),$a0 + emit_load(ACC, 1, SP, s); + emit_store_int(T1, ACC, s); + emit_addiu(SP, SP, 8, s); +} -void divide_class::code(ostream &s) {} +void divide_class::code(ostream &s, CgenClassTable *classtab) { + this->e1->code(s, classtab); + emit_fetch_int(ACC, ACC, s); + emit_push(ACC, s); + this->e2->code(s, classtab); + emit_jal("Object.copy", s); + emit_push(ACC, s); + emit_fetch_int(ACC, ACC, s); // $a0 <- val(e2) + emit_load(T1, 2, SP, s); // $t1 <- val(e1) + emit_div(T1, T1, ACC, s); // $t1 <- val(e1),$t1 + val(e2),$a0 + emit_load(ACC, 1, SP, s); + emit_store_int(T1, ACC, s); + emit_addiu(SP, SP, 8, s); +} -void neg_class::code(ostream &s) {} +/* + Things are even simpler for unary operations: No need for stack! + Just copy the Int object in $a0 generated by e1 to $a0 (we dont need the e1's + object any more), since we have a temp reg, fetch value into $t1, negate it + and send it back to the new Int object. That's all! +*/ +void neg_class::code(ostream &s, CgenClassTable *classtab) { + this->e1->code(s, classtab); + emit_jal("Object.copy", s); + emit_fetch_int(T1, ACC, s); + emit_neg(T1, ACC, s); + emit_store_int(T1, ACC, s); +} -void lt_class::code(ostream &s) {} +/* + Though involving two different types, it doesn't make things harder. + We first deal with Int comparsion. + Quite similar to arithmetic operations, evaluate and extract the value + attributes from the sub-expressions. + Things afterwards are different. We need a label and branch instruction to + generate true or false for the expression. + Since there are only 2 registers available, occupied by two operands, we need + two labels (if more registers are available, then only 1 label should + suffice). The logic looks like this: + ......(evaluate e1, e2) + if val(e1) < val(e2) goto label1; + $a0 = bool_const0; + goto label2; + label1: + $a0 = bool_const1; + label2: + ......(clean up the expression) +*/ +void lt_class::code(ostream &s, CgenClassTable *classtab) { + this->e1->code(s, classtab); + emit_fetch_int(ACC, ACC, s); + emit_push(ACC, s); // $sp+4 == val(e1) + this->e2->code(s, classtab); + emit_fetch_int(ACC, ACC, s); // $a0 <- val(e2) + emit_load(T1, 1, SP, s); // $t1 <- val(e1) + auto label_index_true = classtab->alloc_label_index(); + auto label_index_exit = classtab->alloc_label_index(); + emit_blt(T1, ACC, label_index_true, s); + // after the branch, val(e2) in $a0 is no more needed, making space for result + emit_load_bool(ACC, falsebool, s); + // didn't jump in last instruction, so it is false + emit_branch(label_index_exit, s); // dont want it to be overridden + emit_label_def(label_index_true, s); // first branch goes to here + emit_load_bool(ACC, truebool, s); // branch means true + emit_label_def(label_index_exit, s); // second jump goes to here + emit_addiu(SP, SP, 4, s); // cleanup anyway, only one push is made +} -void eq_class::code(ostream &s) {} +/* + <= operation is exactly the same as < operation except the operator +*/ +void leq_class::code(ostream &s, CgenClassTable *classtab) { + this->e1->code(s, classtab); + emit_fetch_int(ACC, ACC, s); + emit_push(ACC, s); + this->e2->code(s, classtab); + emit_fetch_int(ACC, ACC, s); + emit_load(T1, 1, SP, s); + auto label_index_true = classtab->alloc_label_index(); + auto label_index_exit = classtab->alloc_label_index(); + emit_bleq(T1, ACC, label_index_true, s); + emit_load_bool(ACC, falsebool, s); + emit_branch(label_index_exit, s); + emit_label_def(label_index_true, s); + emit_load_bool(ACC, truebool, s); + emit_label_def(label_index_exit, s); + emit_addiu(SP, SP, 4, s); +} -void leq_class::code(ostream &s) {} +/* + Unary Bool operation `not` is more complicated than its Int counterpart + It uses branch, in that we doesn't want to generate bool objects other than + the two predefined constants. +*/ +void comp_class::code(ostream &s, CgenClassTable *classtab) { + this->e1->code(s, classtab); // this must be a Bool object in $a0 + emit_fetch_bool(T1, ACC, s); // extract boolean value 0 or 1 to $t1 + emit_load_bool(ACC, truebool, s); // default load true(assume val(e1)==false) + auto label_index = classtab->alloc_label_index(); + emit_beqz(T1, label_index, s); // val(e1)==false, skip the next load + emit_load_bool(ACC, falsebool, s); // val(e1)==true, load false to $a0 + emit_label_def(label_index, s); // no clean up needed, just exit here +} -void comp_class::code(ostream &s) {} +/* + Equality operator is much more complicated than others. + According to the manual, when comparing two objects, their pointersn first get + compared. + If they resides in the same address, they are definitely equal, return true + at once. + Otherwise, call primitive procedure `equality_test`. As the Runtime System + tour says, it accepts 2 params which resides in $t1 and $t2. The procedure + checks the following condition: + typeid($t1) in {Int, Bool, String} + && typeid($t1)==typeid($t2) + && value($1) == value($t2) + The return value is in $a0. If condition is true, $a0<-$a0, else $a0<-$a1, + where $a0, $a1 remain unmodified until return. Utilizing this feature, + we can set $a0<-truebool, $a1<-falsebool before calling the procedure. + Understanding the stuff above, the assembly is just there. +*/ +void eq_class::code(ostream &s, CgenClassTable *classtab) { + this->e1->code(s, classtab); + emit_push(ACC, s); // e1 can evaluate to anything, so simply push the ref + this->e2->code(s, classtab); // $a0<-&e2 + emit_load(T1, 1, SP, s); // $t1<-&e1 + emit_move(T2, ACC, s); // $t2<-&e2 + // there's no choice but to break our 2 register rule ~_~ + emit_load_bool(ACC, truebool, s); // load true in advance in case &e1==&e2 + auto label_index_exit = classtab->alloc_label_index(); + emit_beq(T1, T2, label_index_exit, s); // if &e1==&e2, then we are done here + emit_load_bool(A1, falsebool, s); // prepare a false choice for equality_test + emit_jal("equality_test", s); + // at this point, $t1=&e1, $t2=&e2, $a0=true, $a1=false. ready to call + // $a0 will be either true or false, so just keep it as the result + emit_label_def(label_index_exit, s); + emit_addiu(SP, SP, 4, s); // balance stack, 1 push in this expression +} -void int_const_class::code(ostream &s) { +void int_const_class::code(ostream &s, CgenClassTable *classtab) { // // Need to be sure we have an IntEntry *, not an arbitrary Symbol // emit_load_int(ACC, inttable.lookup_string(token->get_string()), s); } -void string_const_class::code(ostream &s) { +void string_const_class::code(ostream &s, CgenClassTable *classtab) { emit_load_string(ACC, stringtable.lookup_string(token->get_string()), s); } -void bool_const_class::code(ostream &s) { +void bool_const_class::code(ostream &s, CgenClassTable *classtab) { emit_load_bool(ACC, BoolConst(val), s); } -void new__class::code(ostream &s) {} +void new__class::code(ostream &s, CgenClassTable *classtab) {} -void isvoid_class::code(ostream &s) {} +/* + `isvoid` operation is quite simple and stack-operation free, since we only + test the reference is zero or not. A straightforward beqz should suffice. +*/ +void isvoid_class::code(ostream &s, CgenClassTable *classtab) { + this->e1->code(s, classtab); + emit_move(T1, ACC, s); // $t1 <- &e1 + emit_load_bool(ACC, truebool, s); + auto label_index_exit = classtab->alloc_label_index(); + emit_beqz(T1, label_index_exit, s); + // &e1 == nullptr ?, if so skip the next load false + emit_load_bool(ACC, falsebool, s); + emit_label_def(label_index_exit, s); +} -void no_expr_class::code(ostream &s) {} +/* + The control flow should not reach here by design +*/ +void no_expr_class::code(ostream &s, CgenClassTable *classtab) { assert(0); } -void object_class::code(ostream &s) {} +void object_class::code(ostream &s, CgenClassTable *classtab) {} #pragma endregion diff --git a/assignments/PA5/cgen.h b/assignments/PA5/cgen.h index 527674b..4f71f27 100644 --- a/assignments/PA5/cgen.h +++ b/assignments/PA5/cgen.h @@ -29,6 +29,7 @@ private: int stringclasstag; int intclasstag; int boolclasstag; + int label_count = 0; // an incremental counter to avoid label conflicts // The following methods emit code for // constants and global declarations. @@ -55,11 +56,14 @@ private: void code_dispatchTable(); void code_prototypeObject(); CgenNode *get_node(Symbol); + void gen_init_code(); public: CgenClassTable(Classes, ostream &str); void code(); CgenNodeP root(); + /* aquire an unused label index */ + int alloc_label_index() { return label_count++; } }; class CgenNode : public class__class { diff --git a/assignments/PA5/cool-tree.h b/assignments/PA5/cool-tree.h index a662905..3ca0540 100644 --- a/assignments/PA5/cool-tree.h +++ b/assignments/PA5/cool-tree.h @@ -11,6 +11,8 @@ #include "cool-tree.handcode.h" #include "tree.h" +class CgenClassTable; + // define the class for phylum // define simple phylum - Program typedef class Program_class *Program; diff --git a/assignments/PA5/cool-tree.handcode.h b/assignments/PA5/cool-tree.handcode.h index e68e60f..8f9af2a 100644 --- a/assignments/PA5/cool-tree.handcode.h +++ b/assignments/PA5/cool-tree.handcode.h @@ -84,13 +84,13 @@ typedef Cases_class *Cases; type = s; \ return this; \ } \ - virtual void code(ostream &) = 0; \ + virtual void code(ostream &, CgenClassTable *) = 0; \ virtual void dump_with_types(ostream &, int) = 0; \ void dump_type(ostream &, int); \ Expression_class() { type = (Symbol)NULL; } #define Expression_SHARED_EXTRAS \ - void code(ostream &); \ + void code(ostream &, CgenClassTable *); \ void dump_with_types(ostream &, int); #endif diff --git a/assignments/PA5/example.cl b/assignments/PA5/example.cl index 020fb7b..fbf83ba 100644 --- a/assignments/PA5/example.cl +++ b/assignments/PA5/example.cl @@ -20,7 +20,7 @@ class B inherits A { }; class C inherits A { - attr_C_1 : String <- "This is C\n"; + attr_C_1 : String <- "This is C\n".concat(new A.method_common2()); attr_C_2 : SELF_TYPE; method_common(x1: Int) : SELF_TYPE { self @@ -31,6 +31,22 @@ class C inherits A { method_1(x1: Int) : Bool { true }; + method_3(x1: Int, x2: A, x3: String) : Object { + { + x1 <- x1 + 00010 - x1 / 10; + x2 <- x2.method_common(x1); + x3 <- x3.concat("schade"); + } + }; + method_2(x1: Bool, x2: C) : Object { + { + if (x1) then + x2 <- x2.method_common(20) + else + "hello world" + fi; + } + }; }; class D inherits C { @@ -39,6 +55,24 @@ class D inherits C { }; }; +class E { + method_compare(x1: Int, x2: Int) : Bool { + x1 < x2 + }; + method_compare_eq(x1: String, x2: String) : Bool { + x1 = x2 + }; + method_compare_eq2(x1: D, x2: D) : Bool { + x1 = x2 + }; + method_not(x1: Bool) : Bool { + not x1 + }; + method_isvoid(x1: Object) : Bool { + isvoid x1 + }; +}; + class Main inherits IO { main():Int { 0 }; };