From 7140f01bb0b12fb760d12c864137ab59ea89a06d Mon Sep 17 00:00:00 2001 From: ridethepig Date: Fri, 31 Mar 2023 15:09:48 +0000 Subject: [PATCH] expression except case done --- assignments/PA5/cgen.cc | 251 +++++++++++++++++++++++++++++++++---- assignments/PA5/cgen.h | 13 ++ assignments/PA5/example.cl | 37 ++++++ 3 files changed, 279 insertions(+), 22 deletions(-) diff --git a/assignments/PA5/cgen.cc b/assignments/PA5/cgen.cc index 5046abf..2129adf 100644 --- a/assignments/PA5/cgen.cc +++ b/assignments/PA5/cgen.cc @@ -26,6 +26,7 @@ #include "cgen_gc.h" #include "cool-tree.h" #include +#include #include #include @@ -623,9 +624,9 @@ void CgenClassTable::code_dispatchTable() { } void CgenClassTable::code_prototypeObject() { - auto int_default = inttable.lookup_string("0"); - auto str_default = stringtable.lookup_string(""); - auto bool_default = &falsebool; + this->_int_default = inttable.lookup_string("0"); + this->_str_default = stringtable.lookup_string(""); + this->_bool_default = &falsebool; for (auto node : nodes) { str << WORD << "-1\n"; // GC tag @@ -636,15 +637,15 @@ void CgenClassTable::code_prototypeObject() { for (auto attr : *node->get_attributes()) { if (attr->type_decl == Int) { str << WORD; - int_default->code_ref(str); + _int_default->code_ref(str); str << endl; } else if (attr->type_decl == Bool) { str << WORD; - bool_default->code_ref(str); + _bool_default->code_ref(str); str << endl; } else if (attr->type_decl == Str) { str << WORD; - str_default->code_ref(str); + _str_default->code_ref(str); str << endl; } else { // for other types(including _prim_slot type), set to void(null pointer) @@ -656,6 +657,14 @@ void CgenClassTable::code_prototypeObject() { } } +void CgenClassTable::code_objectTab() { + str << CLASSOBJTAB << LABEL; + for (auto node : nodes) { + str << WORD << node->get_name() << PROTOBJ_SUFFIX << endl; + str << WORD << node->get_name() << CLASSINIT_SUFFIX << endl; + } +} + CgenClassTable::CgenClassTable(Classes classes, ostream &s) : str(s) { enterscope(); if (cgen_debug) @@ -665,6 +674,11 @@ CgenClassTable::CgenClassTable(Classes classes, ostream &s) : str(s) { build_inheritance_tree(); if (cgen_debug) dump_inheritance_tree(); + root()->traverse_generate_object(); + // nodes[0] is the Object class + stringclasstag = get_node(Str)->get_class_tag(); + intclasstag = get_node(Int)->get_class_tag(); + boolclasstag = get_node(Bool)->get_class_tag(); code(); exitscope(); } @@ -787,10 +801,6 @@ void CgenClassTable::install_basic_classes() { Str, no_expr()))), filename), Basic)); - - stringclasstag = get_node(Str)->get_class_tag(); - intclasstag = get_node(Int)->get_class_tag(); - boolclasstag = get_node(Bool)->get_class_tag(); } // CgenClassTable::install_class @@ -861,6 +871,19 @@ CgenNode *CgenClassTable::get_node(Symbol class_name) { return nullptr; } +int CgenClassTable::get_method_index(Symbol class_name, Symbol method_name) { + auto class_node = get_node(class_name); + auto methods = class_node->get_methods(); + for (size_t i = 0; i < methods->size(); ++i) { + auto method = (*methods)[i]; + if (method.second->name == method_name) { + return i; + } + } + assert(0); + return -1; +} + void CgenClassTable::code() { if (cgen_debug) cout << "coding global data" << endl; @@ -878,10 +901,12 @@ void CgenClassTable::code() { cout << "coding class_nameTab" << endl; code_class_nameTable(); + if (cgen_debug) + cout << "coding object table" << endl; + code_objectTab(); + if (cgen_debug) cout << "coding dispatch tables" << endl; - nodes[0]->traverse_generate_object(); - // nodes[0] is the Object class code_dispatchTable(); if (cgen_debug) @@ -893,7 +918,7 @@ void CgenClassTable::code() { code_global_text(); #if 0 -// test my symtab impl + // test my symtab impl for (auto node : nodes) { auto attrs = *node->get_attributes(); auto methods = *node->get_methods(); @@ -936,6 +961,7 @@ void CgenClassTable::code() { } } #endif + // Add your code to emit // - object initializer // - the class methods @@ -1045,11 +1071,104 @@ void CgenNode::traverse_generate_object() { #pragma region ExpressionCoding -void assign_class::code(ostream &s, CgenClassTable *classtab) {} +/* + Assignment actually does little. First evaluate its rhs. Then look up the lhs + identifier's location and simply store into the location. +*/ +void assign_class::code(ostream &s, CgenClassTable *classtab) { + this->expr->code(s, classtab); + auto loc = classtab->loctab.lookup(this->name); + if (loc->location == Stack) { + emit_store(ACC, loc->offset, FP, s); + } else { + emit_store(ACC, loc->offset, SELF, s); + } +} +/* + static dispatch is a little bit simpler than normal dispatch + Intuitively, first evaluate all the parameter expression and push stack. Note + that, by design, the caller doesn't clear up the params, the callee do the + clear up on return. + Then evaluate the expr before `.`, which serves as the self object when + invoking the method. Thus it resides in $a0. + Before the flow goes to the method, check whether the object is void. This is + mentioned near the end of section 13 in the manual. If void, set $t1 and $a0 + and call _dispatch_abort. + Finally, invoke the method by looking up its address from the dispatch table + and jalr. +*/ +void static_dispatch_class::code(ostream &s, CgenClassTable *classtab) { + 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 + } + this->expr->code(s, classtab); + auto label_index_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); + // 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_jal("_dispatch_abort", s); + // ready to call the method + emit_label_def(label_index_dispatch, s); + // load dispatch table + emit_partial_load_address(T1, s); + emit_disptable_ref(this->type_name, s); + s << endl; + // load method address + 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); +} -void static_dispatch_class::code(ostream &s, CgenClassTable *classtab) {} - -void dispatch_class::code(ostream &s, CgenClassTable *classtab) {} +/* + Normal dispatch is only different from static dispatch in deciding the + dispatch table. Normal dispatch extract the dispatch table from the object's + dispTab feild(offset 8) instead of querying method address at compile time. +*/ +void dispatch_class::code(ostream &s, CgenClassTable *classtab) { + 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 + } + this->expr->code(s, classtab); + auto label_index_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); + // 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_jal("_dispatch_abort", s); + // ready to call the method + emit_label_def(label_index_dispatch, s); + // load dispatch table + emit_load(T1, DISPTABLE_OFFSET, ACC, s); + // load method address + int method_index = -1; + if (this->expr->type == SELF_TYPE) { + method_index = + classtab->get_method_index(classtab->cur_class->name, this->name); + // if the expr is SELF_TYPE, look up the method from current classs + } else { + method_index = classtab->get_method_index(this->expr->type, this->name); + } + emit_load(T1, method_index, T1, s); + // call the method whose address is in $t1, with self object in $a0 + emit_jalr(T1, s); +} /* There's nothing special for if-then-else. Write at your convenience. @@ -1102,9 +1221,18 @@ void block_class::code(ostream &s, CgenClassTable *classtab) { } void let_class::code(ostream &s, CgenClassTable *classtab) { + classtab->expr_type_decl = this->type_decl; this->init->code(s, classtab); emit_push(ZERO, s); // make space for let's new object - + emit_store(ACC, -rel_stack_depth, FP, s); + // set initial value to the new ident + classtab->loctab.enterscope(); + classtab->loctab.add( + LocationTableItem(this->identifier, Stack, -rel_stack_depth)); + this->body->code(s, classtab); + classtab->loctab.exitscope(); + emit_pop(s); // balacnce stack + // the value of let is body's value, so leave $a0 unmodified } /* @@ -1332,7 +1460,59 @@ void bool_const_class::code(ostream &s, CgenClassTable *classtab) { emit_load_bool(ACC, BoolConst(val), s); } -void new__class::code(ostream &s, CgenClassTable *classtab) {} +/* + Create a normal type object is quite simple, which could be done by 3 + instructions: + la $a0, T_protoObj + call Object.copy + call T_init + Create SELF_TYPE object is very very tricky, because the real type is decided + at run time. I managed to get this done by looking at the target code + generated by the reference compiler. + We need a table indexed by class tag, which contains T[i]_protoObj and + T[i]_init. When creating such objects, we look up the actual protoObj and + init method by the self object's class tag. This involves dereferencing + pointers for multiple times, which is hard to get right. +*/ +void new__class::code(ostream &s, CgenClassTable *classtab) { + if (this->type_name == SELF_TYPE) { + // first get the class tag at offset 0, $a0 <- 0($s0) + emit_load(ACC, 0, SELF, s); + // load the address of class_objTab, $t1 <- &class_objTab + emit_load_address(T1, CLASSOBJTAB, s); + // convert class tag to index, index = tag * 8, $a0 <- $a0 << 3 + emit_sll(ACC, ACC, 3, s); + // get the address of so_protoObj, $a0 <- $t1 + $a0(base + index) + emit_addu(ACC, ACC, T1, s); + // save index on stack, we are to use $a0 for param + emit_push(ACC, s); + // get the so_protoObj, $a0 <- 0($a0) + emit_load(ACC, 0, ACC, s); + // call Object.copy + emit_jal("Object.copy", s); + // recover the addresss of so_protoObj, $t1 <- stack top + emit_load(T1, 1, SP, s); + emit_pop(s); // pop + // load address of so_init, $t1 <- 4($t1) + emit_load(T1, 1, T1, s); + // with $a0 storing the new object's ref, call so_init whose address is in + // $t1, which accepts $a0 as the parameter + emit_jalr(T1, s); + // initialized brand new object already has its ref in $a0 + } else { + // load address: $a0 <- T_protObj + emit_partial_load_address(ACC, s); + emit_protobj_ref(this->type_name, s); + s << endl; + // call Object.copy + emit_jal("Object.copy", s); + // call T_init + s << JAL; + emit_init_ref(this->type_name, s); + s << endl; + // initialized brand new object already has its ref in $a0 + } +} /* `isvoid` operation is quite simple and stack-operation free, since we only @@ -1350,10 +1530,37 @@ void isvoid_class::code(ostream &s, CgenClassTable *classtab) { } /* - The control flow should not reach here by design + No_expr means the init expression is omitted. However, the object has to be + assigned to some value. CgenClassTable::expr_type_decl is set for this + method to generate a propriate default initial value. */ -void no_expr_class::code(ostream &s, CgenClassTable *classtab) { assert(0); } +void no_expr_class::code(ostream &s, CgenClassTable *classtab) { + if (classtab->expr_type_decl == Int) { + emit_load_int(ACC, classtab->int_default(), s); + } else if (classtab->expr_type_decl == Bool) { + emit_load_bool(ACC, *classtab->bool_default(), s); + } else if (classtab->expr_type_decl == Str) { + emit_load_string(ACC, classtab->str_default(), s); + } else { + assert(classtab->expr_type_decl); + emit_move(ACC, ZERO, s); + } +} -void object_class::code(ostream &s, CgenClassTable *classtab) {} +/* + ObjectID simply perform a load from store to $a0 + Note that `self` is not in location table, add a special case and $a0<-$s0 +*/ +void object_class::code(ostream &s, CgenClassTable *classtab) { + if (this->name == self) { + emit_move(ACC, SELF, s); + } else { + auto loc = classtab->loctab.lookup(this->name); + if (loc->location == Stack) + emit_load(ACC, loc->offset, FP, s); + else + emit_load(ACC, loc->offset, SELF, s); + } +} #pragma endregion diff --git a/assignments/PA5/cgen.h b/assignments/PA5/cgen.h index 6ef275e..862f1bc 100644 --- a/assignments/PA5/cgen.h +++ b/assignments/PA5/cgen.h @@ -21,6 +21,8 @@ typedef CgenClassTable *CgenClassTableP; class CgenNode; typedef CgenNode *CgenNodeP; +class BoolConst; + typedef std::vector> MethodListT; typedef std::vector AttrListT; @@ -44,6 +46,7 @@ public: return &*itr; } } + assert(0); return nullptr; } void add(LocationTableItem item) { @@ -76,6 +79,9 @@ private: int stringclasstag; int intclasstag; int boolclasstag; + 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 @@ -103,15 +109,22 @@ private: void code_dispatchTable(); void code_prototypeObject(); CgenNode *get_node(Symbol); + void code_objectTab(); void gen_init_code(); public: LocationTable loctab; + Symbol expr_type_decl; + CgenNode* cur_class; CgenClassTable(Classes, ostream &str); void code(); 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; }; + int get_method_index(Symbol class_name, Symbol method_name); }; class CgenNode : public class__class { diff --git a/assignments/PA5/example.cl b/assignments/PA5/example.cl index 84d148e..0493c6e 100644 --- a/assignments/PA5/example.cl +++ b/assignments/PA5/example.cl @@ -53,6 +53,31 @@ class D inherits C { method_common(x1: Int) : SELF_TYPE { self }; + method_new() : D { + new D + }; + method_new_SELF() : D { + new SELF_TYPE + }; + method_new2() : Object { + (new SELF_TYPE).method_2(false, self) + }; + method_staticdispath() : Object { + (new SELF_TYPE)@D.method_2(false, self) + }; + method_case(x1 : Object) : Int { + case (x1) of + t1: Bool => 1; + t2: Int => 2; + t3: String => 3; + t4: Object => 4; + t5: A => 5; + t6: C => 6; + t7: B => 7; + t8: D => 8; + t9: E => 9; + esac + }; }; class E { @@ -79,6 +104,18 @@ class E { let x2 : Int <- x1 in x2 <- x1 + 100 }; + method_2(x1: Bool, x2: C) : Object { + { + if (x1) then + x2 <- x2.method_common(20) + else + "hello world" + fi; + } + }; + method_SELF() : SELF_TYPE { + self + }; }; class Main inherits IO {