diff --git a/assignments/PA5/cgen.cc b/assignments/PA5/cgen.cc index 9113b49..4b46a43 100644 --- a/assignments/PA5/cgen.cc +++ b/assignments/PA5/cgen.cc @@ -356,6 +356,63 @@ static void emit_gc_check(char *source, ostream &s) { s << JAL << "_gc_check" << endl; } +/* + | ........ | + |------------| + | param 2 | + |------------| + | param 1 | + |------------| + | old $fp |<----------- $sp before entry + |------------| + | old $s0 | + |------------| + | ret addr |<----------- current $fp + |------------| + | local 1 |<----------- $sp at entry + |------------| + | local 2 | + |------------| + | ........ | + Throughout the code generated for expressions, I used 4 registers: + $a0, $s0, $a1, $t1. According to MIPS calling conventions, only $s0 needs + to be saved. Thus the callee pushes and pops $fp,$s0,$ra when entering and + leaving the context. + By default, self object is passed to called in $a0, so move it to $s0 +*/ +static void emit_procedure_header(ostream &s) { + /* + addiu $sp $sp -12 + sw $fp 12($sp) + sw $s0 8($sp) + sw $ra 4($sp) + addiu $fp $sp 4 + move $s0 $a0 + */ + emit_addiu(SP, SP, -12, s); + emit_store(FP, 3, SP, s); + emit_store(SELF, 2, SP, s); + emit_store(RA, 1, SP, s); + emit_addiu(FP, SP, 4, s); + emit_move(SELF, ACC, s); + rel_stack_depth = 0; +} + +static void emit_procedure_footer(int nparams, ostream &s) { + /* + lw $fp 12($sp) + lw $s0 8($sp) + lw $ra 4($sp) + addiu $sp $sp (12 + 4 * nparams) + jr $ra + */ + emit_load(FP, 3, SP, s); + emit_load(SELF, 2, SP, s); + emit_load(RA, 1, SP, s); + emit_addiu(SP, SP, 4 * (3 + nparams), s); + emit_return(s); +} + #pragma endregion /////////////////////////////////////////////////////////////////////////////// @@ -635,16 +692,16 @@ void CgenClassTable::code_prototypeObject() { str << WORD << node->get_class_tag() << endl; // class tag str << WORD << node->get_object_size() << endl; // object size str << WORD << node->get_name() << DISPTAB_SUFFIX << endl; // dispatch ptr - for (auto attr : *node->get_attributes()) { - if (attr->type_decl == Int) { + for (auto attr_pair : *node->get_attributes()) { + if (attr_pair.second->type_decl == Int) { str << WORD; _int_default->code_ref(str); str << endl; - } else if (attr->type_decl == Bool) { + } else if (attr_pair.second->type_decl == Bool) { str << WORD; _bool_default->code_ref(str); str << endl; - } else if (attr->type_decl == Str) { + } else if (attr_pair.second->type_decl == Str) { str << WORD; _str_default->code_ref(str); str << endl; @@ -666,6 +723,81 @@ void CgenClassTable::code_objectTab() { } } +void CgenClassTable::gen_init_code() { + for (auto node : nodes) { + this->cur_class = node; + str << node->get_name() << CLASSINIT_SUFFIX << LABEL; + emit_procedure_header(str); + if (node->get_parentnd() && node->get_parentnd()->name != No_class) { + str << JAL << node->parent->get_string() << CLASSINIT_SUFFIX << endl; + } + auto attrs = node->get_attributes(); + // attributes are available when initializing + loctab.enterscope(); + for (auto i = 0U; i < attrs->size(); ++i) { + auto attr_pair = (*attrs)[i]; + loctab.add(LocationTableItem(attr_pair.second->name, Attribute, + i + DEFAULT_OBJFIELDS)); + } + for (auto i = 0U; i < attrs->size(); ++i) { + auto attr_pair = (*attrs)[i]; + if (attr_pair.first->name != node->name) + continue; + // base class initializer will handle inherited attributes + // here we only deal with our new attributes + if (is_no_expr(attr_pair.second->init)) + continue; + // we can safely skip for those attributes without initialization expr + // because the default values are set in the prototype object, when + // the object is created by Object.copy(), they will be set to correct + // default values + this->expr_type_decl = attr_pair.second->type_decl; + attr_pair.second->init->code(str, this); + emit_store(ACC, i + DEFAULT_OBJFIELDS, SELF, str); + } + loctab.exitscope(); + emit_move(ACC, SELF, str); + // remember to place the initialized object back to $a0, other code rely on + // this behavior + emit_procedure_footer(0, str); + } +} + +void CgenClassTable::gen_method_code() { + for (auto node : nodes) { + if (node->basic()) + continue; + this->cur_class = node; + for (auto method_pair : *node->get_methods()) { + if (method_pair.first->name != node->name) + continue; // skip inherited methods + auto method = method_pair.second; + auto attrs = node->get_attributes(); + // add attributes and formals to location table + loctab.enterscope(); + // add attributes + for (auto i = 0U; i < attrs->size(); ++i) { + auto attr_pair = (*attrs)[i]; + loctab.add(LocationTableItem(attr_pair.second->name, Attribute, + i + DEFAULT_OBJFIELDS)); + } + // add formals + for (auto i = method->formals->first(); method->formals->more(i); + i = method->formals->next(i)) { + auto formal = static_cast(method->formals->nth(i)); + loctab.add(LocationTableItem(formal->name, Stack, + method->formals->len() - i + 3 - 1)); + // &formal[i] = (4 * i + 12)($fp) + } + str << node->name << "." << method->name << LABEL; + emit_procedure_header(str); + method->expr->code(str, this); + emit_procedure_footer(method->formals->len(), str); + loctab.exitscope(); + } + } +} + CgenClassTable::CgenClassTable(Classes classes, ostream &s) : str(s) { enterscope(); if (cgen_debug) @@ -974,10 +1106,13 @@ void CgenClassTable::code() { } #endif - // Add your code to emit - // - object initializer - // - the class methods - // - etc... + if (cgen_debug) + cout << "generating object initializers" << endl; + gen_init_code(); + + if (cgen_debug) + cout << "generating class methods" << endl; + gen_method_code(); } /////////////////////////////////////////////////////////////////////// @@ -1031,7 +1166,9 @@ void CgenNode::traverse_generate_object() { feature_i = features->next(feature_i)) { auto feature = features->nth(feature_i); if (typeid(*feature) == typeid(attr_class)) { - this->attributes.push_back(static_cast(feature)); + this->attributes.push_back( + std::make_pair(static_cast(this), + static_cast(feature))); // inherited attrs cannot be redefined, so simply add to list } else { auto method = static_cast(feature); @@ -1055,7 +1192,8 @@ void CgenNode::traverse_generate_object() { std::cerr << "Object info -- " << this->name << "\n"; std::cerr << "Attributes:\n"; for (auto attr : attributes) { - std::cerr << " " << attr->name << ":" << attr->type_decl << "\n"; + std::cerr << " " << attr.first->name << "." << attr.second->name << ":" + << attr.second->type_decl << "\n"; } std::cerr << "Methods:\n"; for (auto method : methods) { @@ -1118,11 +1256,13 @@ void assign_class::code(ostream &s, CgenClassTable *classtab) { and jalr. */ void static_dispatch_class::code(ostream &s, CgenClassTable *classtab) { + int nparams = 0; for (auto actual_i = this->actual->first(); this->actual->more(actual_i); actual_i = this->actual->next(actual_i)) { auto actual_expr = this->actual->nth(actual_i); actual_expr->code(s, classtab); emit_push(ACC, s); // push param to stack + ++ nparams; } this->expr->code(s, classtab); auto label_dispatch = classtab->alloc_label_index(); @@ -1130,11 +1270,11 @@ void static_dispatch_class::code(ostream &s, CgenClassTable *classtab) { 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); emit_partial_load_address(ACC, s); stringtable.lookup_string(classtab->cur_class->get_filename()->get_string()) ->code_ref(s); s << endl; + emit_load_imm(T1, this->line_number, s); emit_jal("_dispatch_abort", s); // ready to call the method emit_label_def(label_dispatch, s); @@ -1146,6 +1286,7 @@ void static_dispatch_class::code(ostream &s, CgenClassTable *classtab) { emit_load(T1, classtab->get_method_index(this->type_name, this->name), T1, s); // call the method whose address is in $t1, with self object in $a0 emit_jalr(T1, s); + rel_stack_depth -= nparams; } /* @@ -1154,11 +1295,13 @@ void static_dispatch_class::code(ostream &s, CgenClassTable *classtab) { dispTab feild(offset 8) instead of querying method address at compile time. */ void dispatch_class::code(ostream &s, CgenClassTable *classtab) { + int nparams = 0; for (auto actual_i = this->actual->first(); this->actual->more(actual_i); actual_i = this->actual->next(actual_i)) { auto actual_expr = this->actual->nth(actual_i); actual_expr->code(s, classtab); emit_push(ACC, s); // push param to stack + ++ nparams; } this->expr->code(s, classtab); auto label_dispatch = classtab->alloc_label_index(); @@ -1166,11 +1309,11 @@ void dispatch_class::code(ostream &s, CgenClassTable *classtab) { 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); emit_partial_load_address(ACC, s); stringtable.lookup_string(classtab->cur_class->get_filename()->get_string()) ->code_ref(s); s << endl; + emit_load_imm(T1, this->line_number, s); emit_jal("_dispatch_abort", s); // ready to call the method emit_label_def(label_dispatch, s); @@ -1188,6 +1331,7 @@ void dispatch_class::code(ostream &s, CgenClassTable *classtab) { emit_load(T1, method_index, T1, s); // call the method whose address is in $t1, with self object in $a0 emit_jalr(T1, s); + rel_stack_depth -= nparams; } /* @@ -1233,14 +1377,14 @@ void loop_class::code(ostream &s, CgenClassTable *classtab) { 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 + 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 -> + 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) { @@ -1282,10 +1426,11 @@ void typcase_class::code(ostream &s, CgenClassTable *classtab) { 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); + emit_push(ACC, s); // allocate a new location - classtab->enterscope(); - classtab->loctab.add(LocationTableItem(branch->name, Stack, -rel_stack_depth)); + classtab->loctab.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(); @@ -1295,7 +1440,7 @@ void typcase_class::code(ostream &s, CgenClassTable *classtab) { // 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 + // 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); @@ -1423,7 +1568,7 @@ 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_neg(T1, T1, s); emit_store_int(T1, ACC, s); } @@ -1633,6 +1778,8 @@ void no_expr_class::code(ostream &s, CgenClassTable *classtab) { emit_load_bool(ACC, *classtab->bool_default(), s); } else if (classtab->expr_type_decl == Str) { emit_load_string(ACC, classtab->str_default(), s); + } else if (classtab->expr_type_decl == prim_slot) { + assert(0); // basic type should not get attrs init } else { assert(classtab->expr_type_decl); emit_move(ACC, ZERO, s); diff --git a/assignments/PA5/cgen.h b/assignments/PA5/cgen.h index 353c4e4..7b6a598 100644 --- a/assignments/PA5/cgen.h +++ b/assignments/PA5/cgen.h @@ -1,4 +1,5 @@ #include "cool-tree.h" +#include "cool-tree.handcode.h" #include "emit.h" #include "symtab.h" #include @@ -24,7 +25,7 @@ typedef CgenNode *CgenNodeP; class BoolConst; typedef std::vector> MethodListT; -typedef std::vector AttrListT; +typedef std::vector> AttrListT; struct LocationTableItem { LocationTableItem(Symbol id, VariableLocation loc, int off) @@ -108,6 +109,7 @@ private: CgenNode *get_node(Symbol); void code_objectTab(); void gen_init_code(); + void gen_method_code(); public: LocationTable loctab; @@ -168,3 +170,7 @@ public: void code_def(ostream &, int boolclasstag); void code_ref(ostream &) const; }; + +inline bool is_no_expr(Expression expr) { + return typeid(*expr) == typeid(no_expr_class); +} \ No newline at end of file diff --git a/assignments/PA5/example.cl b/assignments/PA5/example.cl index e12b5b8..9205019 100644 --- a/assignments/PA5/example.cl +++ b/assignments/PA5/example.cl @@ -20,8 +20,11 @@ class B inherits A { }; class C inherits A { - attr_C_1 : String <- "This is C\n".concat(new A.method_common2()); attr_C_2 : SELF_TYPE; + attr_C_0 : Int <- 10; + attr_C_1 : String <- "This is C\n".concat(new A.method_common2()); + attr_C_3 : Int <- "12345".length(); + attr_C_4 : Bool <- "12345".length() = 5; method_common(x1: Int) : SELF_TYPE { self }; diff --git a/assignments/PA5/mycoolc b/assignments/PA5/mycoolc index bc836bc..9b5ce43 100755 --- a/assignments/PA5/mycoolc +++ b/assignments/PA5/mycoolc @@ -1,2 +1,4 @@ #!/bin/bash +./lexer $* | ./parser $* | ./semant $* > debug.in ./lexer $* | ./parser $* | ./semant $* | ./cgen $* +rm debug.in \ No newline at end of file diff --git a/assignments/PA5/stdcoolc b/assignments/PA5/stdcoolc index 726379a..8892332 100755 --- a/assignments/PA5/stdcoolc +++ b/assignments/PA5/stdcoolc @@ -1,2 +1,5 @@ #!/bin/bash +filename=$(basename -- "$*") +filename="${filename%.*}" ../../bin/lexer $* | ../../bin/parser $* | ../../bin/semant $* | ../../bin/cgen $* +mv $filename.s $filename.std.s \ No newline at end of file