diff --git a/assignments/PA5/cgen.cc b/assignments/PA5/cgen.cc index 2129adf..9113b49 100644 --- a/assignments/PA5/cgen.cc +++ b/assignments/PA5/cgen.cc @@ -25,9 +25,11 @@ #include "cgen.h" #include "cgen_gc.h" #include "cool-tree.h" +#include #include #include #include +#include #include extern void emit_string_constant(ostream &str, char *s); @@ -95,6 +97,7 @@ BoolConst falsebool(FALSE); BoolConst truebool(TRUE); static int rel_stack_depth; +static int class_tag_count; //********************************************************* // // Define method for code generation @@ -433,10 +436,9 @@ void IntEntry::code_def(ostream &s, int intclasstag) { s << WORD << "-1" << endl; code_ref(s); - s << LABEL // label - << WORD << intclasstag << endl // class tag - << WORD << (DEFAULT_OBJFIELDS + INT_SLOTS) << endl // object size - << WORD; + s << LABEL // label + << WORD << intclasstag << endl // class tag + << WORD << (DEFAULT_OBJFIELDS + INT_SLOTS) << endl; // object size s << WORD << INTNAME << DISPTAB_SUFFIX << endl; // dispatch ptr s << WORD << str << endl; // integer value @@ -469,10 +471,9 @@ void BoolConst::code_def(ostream &s, int boolclasstag) { s << WORD << "-1" << endl; code_ref(s); - s << LABEL // label - << WORD << boolclasstag << endl // class tag - << WORD << (DEFAULT_OBJFIELDS + BOOL_SLOTS) << endl // object size - << WORD; + s << LABEL // label + << WORD << boolclasstag << endl // class tag + << WORD << (DEFAULT_OBJFIELDS + BOOL_SLOTS) << endl; // object size s << WORD << BOOLNAME << DISPTAB_SUFFIX << endl; // dispatch ptr s << WORD << val << endl; // value (0 or 1) @@ -674,6 +675,18 @@ CgenClassTable::CgenClassTable(Classes classes, ostream &s) : str(s) { build_inheritance_tree(); if (cgen_debug) dump_inheritance_tree(); + root()->traverse_allocate_tag(); + std::sort(nodes.begin(), nodes.end(), [](CgenNode *a, CgenNode *b) { + return a->get_class_tag() < b->get_class_tag(); + }); + if (cgen_debug) { + std::cerr << "Dump class tags:\n"; + for (auto node : nodes) { + std::cerr << node->name << ": " << node->get_class_tag() << "," + << node->get_children_tag() << "\n"; + } + } + // sort the vector is in tag order to generate correct class_objTab root()->traverse_generate_object(); // nodes[0] is the Object class stringclasstag = get_node(Str)->get_class_tag(); @@ -818,7 +831,6 @@ void CgenClassTable::install_class(CgenNodeP nd) { // The class name is legal, so add it to the list of classes // and the symbol table. nodes.push_back(nd); - nd->set_class_tag(nodes.size()); addid(name, nd); } @@ -1057,6 +1069,14 @@ void CgenNode::traverse_generate_object() { } } +void CgenNode::traverse_allocate_tag() { + this->_class_tag = class_tag_count++; + for (auto child : children) { + child->traverse_allocate_tag(); + } + this->_children_tag_range = class_tag_count - 1; +} + #pragma endregion //****************************************************************** @@ -1105,9 +1125,9 @@ void static_dispatch_class::code(ostream &s, CgenClassTable *classtab) { emit_push(ACC, s); // push param to stack } this->expr->code(s, classtab); - auto label_index_dispatch = classtab->alloc_label_index(); + auto label_dispatch = classtab->alloc_label_index(); // use bne to save a label, because we'll never come back if abort - emit_bne(ACC, ZERO, label_index_dispatch, s); + emit_bne(ACC, ZERO, label_dispatch, s); // Prints the line number, from $t1, and filename, from $a0, at which the // dispatch occurred, and aborts. emit_load_imm(T1, this->line_number, s); @@ -1117,7 +1137,7 @@ void static_dispatch_class::code(ostream &s, CgenClassTable *classtab) { s << endl; emit_jal("_dispatch_abort", s); // ready to call the method - emit_label_def(label_index_dispatch, s); + emit_label_def(label_dispatch, s); // load dispatch table emit_partial_load_address(T1, s); emit_disptable_ref(this->type_name, s); @@ -1141,9 +1161,9 @@ void dispatch_class::code(ostream &s, CgenClassTable *classtab) { emit_push(ACC, s); // push param to stack } this->expr->code(s, classtab); - auto label_index_dispatch = classtab->alloc_label_index(); + auto label_dispatch = classtab->alloc_label_index(); // use bne to save a label, because we'll never come back if abort - emit_bne(ACC, ZERO, label_index_dispatch, s); + emit_bne(ACC, ZERO, label_dispatch, s); // Prints the line number, from $t1, and filename, from $a0, at which the // dispatch occurred, and aborts. emit_load_imm(T1, this->line_number, s); @@ -1153,7 +1173,7 @@ void dispatch_class::code(ostream &s, CgenClassTable *classtab) { s << endl; emit_jal("_dispatch_abort", s); // ready to call the method - emit_label_def(label_index_dispatch, s); + emit_label_def(label_dispatch, s); // load dispatch table emit_load(T1, DISPTABLE_OFFSET, ACC, s); // load method address @@ -1178,15 +1198,15 @@ void dispatch_class::code(ostream &s, CgenClassTable *classtab) { 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); + auto label_false = classtab->alloc_label_index(); + auto label_exit = classtab->alloc_label_index(); + emit_beqz(ACC, label_false, s); // $a0==0, pred is false, goto else + this->then_exp->code(s, classtab); // pred is true, eval then branch + emit_branch(label_exit, s); // $a0 <- then, and we goto exit + emit_label_def(label_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); + emit_label_def(label_exit, s); } /* @@ -1194,20 +1214,92 @@ void cond_class::code(ostream &s, CgenClassTable *classtab) { 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); + auto label_predicate = classtab->alloc_label_index(); + emit_label_def(label_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 + auto label_exit = classtab->alloc_label_index(); + emit_beqz(ACC, label_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_branch(label_predicate, s); // anyways, go back and check predicate + emit_label_def(label_exit, s); emit_load_imm(ACC, 0, s); // whatever the case, loop expr evaluates to void! } -void typcase_class::code(ostream &s, CgenClassTable *classtab) {} +/* + I think type case is the most difficult part in this PA. It selects the + closest ancestor of the expr's type, thus requiring hierarchy infomation + available at runtime. + Again, through reverse engineering (^_^) the reference compiler, I managed to + find out a relatively simple way to embed hierarchy into the program. + We may use the class tag to do this. In advance, allocate the class tags in + pre-order traversal of the inheritance tree, and record for each node when + all its children is allocated. In this way, every node has a range of tags + covering itself and its children. When generating `case`, start from the + leaves, because we need to match the closest ancestor. If the tag is in some + range, then we can say it matches the branch. + Besides the hierarchy problem, there are two runtime error here: a case + statement has no match -> `_case_abort`; a case on a void object -> + `_case_abort2`. Remember to set these two guys. +*/ +void typcase_class::code(ostream &s, CgenClassTable *classtab) { + this->expr->code(s, classtab); + auto label_case = classtab->alloc_label_index(); + // use bne to save a label, because we'll never come back if abort + emit_bne(ACC, ZERO, label_case, s); + // Prints the line number, from $t1, and filename, from $a0, at which the + // type case occurred, and aborts. + emit_load_imm(T1, this->line_number, s); + emit_partial_load_address(ACC, s); + stringtable.lookup_string(classtab->cur_class->get_filename()->get_string()) + ->code_ref(s); + s << endl; + emit_jal("_case_abort2", s); + // start type case here + emit_label_def(label_case, s); + // load class tag, if no error, object ref is in $a0 + emit_load(T1, TAG_OFFSET, ACC, s); + // generate a map to simplify branch search + std::map sym_branch; + for (auto branch_i = this->cases->first(); this->cases->more(branch_i); + branch_i = this->cases->next(branch_i)) { + auto branch = static_cast(this->cases->nth(branch_i)); + sym_branch[branch->type_decl] = branch; + } + auto label_exit = classtab->alloc_label_index(); + // generate the branches in reverse pre-order + for (auto node_ritr = classtab->nodes.rbegin(); + node_ritr != classtab->nodes.rend(); ++node_ritr) { + auto node = *node_ritr; + auto branch_itr = sym_branch.find(node->name); + if (branch_itr == sym_branch.end()) + continue; + auto branch = branch_itr->second; + auto label_branch = classtab->alloc_label_index(); + // if expr's tag is not in the branch's children range, test next branch + emit_blti(T1, node->get_class_tag(), label_branch, s); + emit_bgti(T1, node->get_children_tag(), label_branch, s); + // though maybe quite trivial(just bind another name), still push to stack + // because we dont know whether $a0 is going to be overwritten + emit_push(ACC, s); + // allocate a new location + classtab->enterscope(); + classtab->loctab.add(LocationTableItem(branch->name, Stack, -rel_stack_depth)); + // if expr's tag is in range, evaluate this branch + branch->expr->code(s, classtab); + classtab->loctab.exitscope(); + emit_pop(s); // balance stack + // already matched, the matching process ends here and go to exit + emit_branch(label_exit, s); + // the label for the next branch + emit_label_def(label_branch, s); + } + // no match, the procedure's is in $a0, which remains unmodified if no branch + // is taken, thus unnecessary to set it + emit_jal("_case_abort", s); + emit_label_def(label_exit, s); +} /* For block expression, generate code expression by expression, nothing else @@ -1361,17 +1453,17 @@ void lt_class::code(ostream &s, CgenClassTable *classtab) { 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); + auto label_true = classtab->alloc_label_index(); + auto label_exit = classtab->alloc_label_index(); + emit_blt(T1, ACC, label_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_pop(1, s); // cleanup anyway, only one push is made + emit_branch(label_exit, s); // dont want it to be overridden + emit_label_def(label_true, s); // first branch goes to here + emit_load_bool(ACC, truebool, s); // branch means true + emit_label_def(label_exit, s); // second jump goes to here + emit_pop(1, s); // cleanup anyway, only one push is made } /* @@ -1384,14 +1476,14 @@ void leq_class::code(ostream &s, CgenClassTable *classtab) { 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); + auto label_true = classtab->alloc_label_index(); + auto label_exit = classtab->alloc_label_index(); + emit_bleq(T1, ACC, label_true, s); emit_load_bool(ACC, falsebool, s); - emit_branch(label_index_exit, s); - emit_label_def(label_index_true, s); + emit_branch(label_exit, s); + emit_label_def(label_true, s); emit_load_bool(ACC, truebool, s); - emit_label_def(label_index_exit, s); + emit_label_def(label_exit, s); emit_pop(1, s); } @@ -1435,13 +1527,13 @@ void eq_class::code(ostream &s, CgenClassTable *classtab) { 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 + auto label_exit = classtab->alloc_label_index(); + emit_beq(T1, T2, label_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_label_def(label_exit, s); emit_pop(1, s); // balance stack, 1 push in this expression } @@ -1522,11 +1614,11 @@ 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); + auto label_exit = classtab->alloc_label_index(); + emit_beqz(T1, label_exit, s); // &e1 == nullptr ?, if so skip the next load false emit_load_bool(ACC, falsebool, s); - emit_label_def(label_index_exit, s); + emit_label_def(label_exit, s); } /* diff --git a/assignments/PA5/cgen.h b/assignments/PA5/cgen.h index 862f1bc..353c4e4 100644 --- a/assignments/PA5/cgen.h +++ b/assignments/PA5/cgen.h @@ -38,10 +38,11 @@ class LocationTable { private: std::vector table; std::vector scope; + public: // lookup the inner-most item with the same id as name - LocationTableItem* lookup(Symbol name) { - for (auto itr = table.rbegin(); itr != table.rend(); ++ itr) { + LocationTableItem *lookup(Symbol name) { + for (auto itr = table.rbegin(); itr != table.rend(); ++itr) { if (itr->id == name) { return &*itr; } @@ -49,12 +50,8 @@ public: assert(0); return nullptr; } - void add(LocationTableItem item) { - table.push_back(item); - }; - void enterscope() { - scope.push_back(table.size()); - } + void add(LocationTableItem item) { table.push_back(item); }; + void enterscope() { scope.push_back(table.size()); } void exitscope() { auto end = scope.back(); scope.pop_back(); @@ -62,10 +59,11 @@ public: } void dump() { scope.push_back(table.size()); - for (size_t i = 0; i < scope.size() - 1; ++ i) { + for (size_t i = 0; i < scope.size() - 1; ++i) { std::cerr << "scope" << i << "\n"; - for (size_t j = scope[i]; j < scope[i+1]; j ++) { - std::cerr << table[j].id << ", " << table[j].location << ", " << table[j].offset << "\n"; + for (size_t j = scope[i]; j < scope[i + 1]; j++) { + std::cerr << table[j].id << ", " << table[j].location << ", " + << table[j].offset << "\n"; } } scope.pop_back(); @@ -74,14 +72,13 @@ public: class CgenClassTable : public SymbolTable { private: - std::vector nodes; ostream &str; int stringclasstag; int intclasstag; int boolclasstag; - IntEntry* _int_default = nullptr; - StringEntry* _str_default = nullptr; - BoolConst* _bool_default = nullptr; + IntEntry *_int_default = nullptr; + StringEntry *_str_default = nullptr; + BoolConst *_bool_default = nullptr; int label_count = 0; // an incremental counter to avoid label conflicts // The following methods emit code for @@ -115,15 +112,16 @@ private: public: LocationTable loctab; Symbol expr_type_decl; - CgenNode* cur_class; + std::vector nodes; + CgenNode *cur_class; CgenClassTable(Classes, ostream &str); void code(); - CgenNode* root() { return nodes[0]; } + CgenNode *root() { return nodes[0]; } /* aquire an unused label index */ int alloc_label_index() { return label_count++; } - IntEntry* int_default() const { return _int_default; }; - StringEntry* str_default() const { return _str_default; }; - BoolConst* bool_default() const { return _bool_default; }; + IntEntry *int_default() const { return _int_default; }; + StringEntry *str_default() const { return _str_default; }; + BoolConst *bool_default() const { return _bool_default; }; int get_method_index(Symbol class_name, Symbol method_name); }; @@ -133,10 +131,11 @@ private: std::vector children; // Children of class Basicness basic_status; // `Basic' if class is basic // `NotBasic' otherwise + /* class_tag is allocated in pre-order */ uint32_t _class_tag = 0; - /* Allocate a class_tag on first scan increase in scanning order, - starting from 1, so 0 is actually invalid by design - */ + /* record children's class tag range for type case code gen */ + uint32_t _children_tag_range; + AttrListT attributes; // attr list including inherited MethodListT methods; @@ -151,10 +150,11 @@ public: CgenNodeP get_parentnd() { return parentnd; } int basic() { return (basic_status == Basic); } uint32_t get_class_tag() { return _class_tag; } - void set_class_tag(uint32_t val) { _class_tag = val; } + uint32_t get_children_tag() { return _children_tag_range; } void traverse_dump(int pad); uint32_t get_object_size() { return (3 + attributes.size()); } void traverse_generate_object(); + void traverse_allocate_tag(); AttrListT *get_attributes() { return &attributes; }; MethodListT *get_methods() { return &methods; }; }; diff --git a/assignments/PA5/example.cl b/assignments/PA5/example.cl index 0493c6e..e12b5b8 100644 --- a/assignments/PA5/example.cl +++ b/assignments/PA5/example.cl @@ -68,7 +68,7 @@ class D inherits C { method_case(x1 : Object) : Int { case (x1) of t1: Bool => 1; - t2: Int => 2; + t2: Int => t2 + 100; t3: String => 3; t4: Object => 4; t5: A => 5;