done without gc

This commit is contained in:
ridethepig 2023-04-01 15:34:50 +08:00
parent c29b455305
commit fec2e03a75
5 changed files with 183 additions and 22 deletions

View File

@ -356,6 +356,63 @@ static void emit_gc_check(char *source, ostream &s) {
s << JAL << "_gc_check" << endl; 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 #pragma endregion
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -635,16 +692,16 @@ void CgenClassTable::code_prototypeObject() {
str << WORD << node->get_class_tag() << endl; // class tag str << WORD << node->get_class_tag() << endl; // class tag
str << WORD << node->get_object_size() << endl; // object size str << WORD << node->get_object_size() << endl; // object size
str << WORD << node->get_name() << DISPTAB_SUFFIX << endl; // dispatch ptr str << WORD << node->get_name() << DISPTAB_SUFFIX << endl; // dispatch ptr
for (auto attr : *node->get_attributes()) { for (auto attr_pair : *node->get_attributes()) {
if (attr->type_decl == Int) { if (attr_pair.second->type_decl == Int) {
str << WORD; str << WORD;
_int_default->code_ref(str); _int_default->code_ref(str);
str << endl; str << endl;
} else if (attr->type_decl == Bool) { } else if (attr_pair.second->type_decl == Bool) {
str << WORD; str << WORD;
_bool_default->code_ref(str); _bool_default->code_ref(str);
str << endl; str << endl;
} else if (attr->type_decl == Str) { } else if (attr_pair.second->type_decl == Str) {
str << WORD; str << WORD;
_str_default->code_ref(str); _str_default->code_ref(str);
str << endl; 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<formal_class *>(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) { CgenClassTable::CgenClassTable(Classes classes, ostream &s) : str(s) {
enterscope(); enterscope();
if (cgen_debug) if (cgen_debug)
@ -974,10 +1106,13 @@ void CgenClassTable::code() {
} }
#endif #endif
// Add your code to emit if (cgen_debug)
// - object initializer cout << "generating object initializers" << endl;
// - the class methods gen_init_code();
// - etc...
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)) { feature_i = features->next(feature_i)) {
auto feature = features->nth(feature_i); auto feature = features->nth(feature_i);
if (typeid(*feature) == typeid(attr_class)) { if (typeid(*feature) == typeid(attr_class)) {
this->attributes.push_back(static_cast<attr_class *>(feature)); this->attributes.push_back(
std::make_pair(static_cast<class__class *>(this),
static_cast<attr_class *>(feature)));
// inherited attrs cannot be redefined, so simply add to list // inherited attrs cannot be redefined, so simply add to list
} else { } else {
auto method = static_cast<method_class *>(feature); auto method = static_cast<method_class *>(feature);
@ -1055,7 +1192,8 @@ void CgenNode::traverse_generate_object() {
std::cerr << "Object info -- " << this->name << "\n"; std::cerr << "Object info -- " << this->name << "\n";
std::cerr << "Attributes:\n"; std::cerr << "Attributes:\n";
for (auto attr : attributes) { 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"; std::cerr << "Methods:\n";
for (auto method : methods) { for (auto method : methods) {
@ -1118,11 +1256,13 @@ void assign_class::code(ostream &s, CgenClassTable *classtab) {
and jalr. and jalr.
*/ */
void static_dispatch_class::code(ostream &s, CgenClassTable *classtab) { void static_dispatch_class::code(ostream &s, CgenClassTable *classtab) {
int nparams = 0;
for (auto actual_i = this->actual->first(); this->actual->more(actual_i); for (auto actual_i = this->actual->first(); this->actual->more(actual_i);
actual_i = this->actual->next(actual_i)) { actual_i = this->actual->next(actual_i)) {
auto actual_expr = this->actual->nth(actual_i); auto actual_expr = this->actual->nth(actual_i);
actual_expr->code(s, classtab); actual_expr->code(s, classtab);
emit_push(ACC, s); // push param to stack emit_push(ACC, s); // push param to stack
++ nparams;
} }
this->expr->code(s, classtab); this->expr->code(s, classtab);
auto label_dispatch = classtab->alloc_label_index(); 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); emit_bne(ACC, ZERO, label_dispatch, s);
// Prints the line number, from $t1, and filename, from $a0, at which the // Prints the line number, from $t1, and filename, from $a0, at which the
// dispatch occurred, and aborts. // dispatch occurred, and aborts.
emit_load_imm(T1, this->line_number, s);
emit_partial_load_address(ACC, s); emit_partial_load_address(ACC, s);
stringtable.lookup_string(classtab->cur_class->get_filename()->get_string()) stringtable.lookup_string(classtab->cur_class->get_filename()->get_string())
->code_ref(s); ->code_ref(s);
s << endl; s << endl;
emit_load_imm(T1, this->line_number, s);
emit_jal("_dispatch_abort", s); emit_jal("_dispatch_abort", s);
// ready to call the method // ready to call the method
emit_label_def(label_dispatch, s); 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); 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 // call the method whose address is in $t1, with self object in $a0
emit_jalr(T1, s); 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. dispTab feild(offset 8) instead of querying method address at compile time.
*/ */
void dispatch_class::code(ostream &s, CgenClassTable *classtab) { void dispatch_class::code(ostream &s, CgenClassTable *classtab) {
int nparams = 0;
for (auto actual_i = this->actual->first(); this->actual->more(actual_i); for (auto actual_i = this->actual->first(); this->actual->more(actual_i);
actual_i = this->actual->next(actual_i)) { actual_i = this->actual->next(actual_i)) {
auto actual_expr = this->actual->nth(actual_i); auto actual_expr = this->actual->nth(actual_i);
actual_expr->code(s, classtab); actual_expr->code(s, classtab);
emit_push(ACC, s); // push param to stack emit_push(ACC, s); // push param to stack
++ nparams;
} }
this->expr->code(s, classtab); this->expr->code(s, classtab);
auto label_dispatch = classtab->alloc_label_index(); 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); emit_bne(ACC, ZERO, label_dispatch, s);
// Prints the line number, from $t1, and filename, from $a0, at which the // Prints the line number, from $t1, and filename, from $a0, at which the
// dispatch occurred, and aborts. // dispatch occurred, and aborts.
emit_load_imm(T1, this->line_number, s);
emit_partial_load_address(ACC, s); emit_partial_load_address(ACC, s);
stringtable.lookup_string(classtab->cur_class->get_filename()->get_string()) stringtable.lookup_string(classtab->cur_class->get_filename()->get_string())
->code_ref(s); ->code_ref(s);
s << endl; s << endl;
emit_load_imm(T1, this->line_number, s);
emit_jal("_dispatch_abort", s); emit_jal("_dispatch_abort", s);
// ready to call the method // ready to call the method
emit_label_def(label_dispatch, s); 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); emit_load(T1, method_index, T1, s);
// call the method whose address is in $t1, with self object in $a0 // call the method whose address is in $t1, with self object in $a0
emit_jalr(T1, s); emit_jalr(T1, s);
rel_stack_depth -= nparams;
} }
/* /*
@ -1233,14 +1377,14 @@ void loop_class::code(ostream &s, CgenClassTable *classtab) {
available at runtime. available at runtime.
Again, through reverse engineering (^_^) the reference compiler, I managed to Again, through reverse engineering (^_^) the reference compiler, I managed to
find out a relatively simple way to embed hierarchy into the program. 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 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 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 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 leaves, because we need to match the closest ancestor. If the tag is in some
range, then we can say it matches the branch. range, then we can say it matches the branch.
Besides the hierarchy problem, there are two runtime error here: a case Besides the hierarchy problem, there are two runtime error here: a case
statement has no match -> `_case_abort`; a case on a void object -> statement has no match -> `_case_abort`; a case on a void object ->
`_case_abort2`. Remember to set these two guys. `_case_abort2`. Remember to set these two guys.
*/ */
void typcase_class::code(ostream &s, CgenClassTable *classtab) { 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); emit_bgti(T1, node->get_children_tag(), label_branch, s);
// though maybe quite trivial(just bind another name), still push to stack // though maybe quite trivial(just bind another name), still push to stack
// because we dont know whether $a0 is going to be overwritten // because we dont know whether $a0 is going to be overwritten
emit_push(ACC, s); emit_push(ACC, s);
// allocate a new location // allocate a new location
classtab->enterscope(); classtab->loctab.enterscope();
classtab->loctab.add(LocationTableItem(branch->name, Stack, -rel_stack_depth)); classtab->loctab.add(
LocationTableItem(branch->name, Stack, -rel_stack_depth));
// if expr's tag is in range, evaluate this branch // if expr's tag is in range, evaluate this branch
branch->expr->code(s, classtab); branch->expr->code(s, classtab);
classtab->loctab.exitscope(); classtab->loctab.exitscope();
@ -1295,7 +1440,7 @@ void typcase_class::code(ostream &s, CgenClassTable *classtab) {
// the label for the next branch // the label for the next branch
emit_label_def(label_branch, s); 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 // is taken, thus unnecessary to set it
emit_jal("_case_abort", s); emit_jal("_case_abort", s);
emit_label_def(label_exit, s); emit_label_def(label_exit, s);
@ -1423,7 +1568,7 @@ void neg_class::code(ostream &s, CgenClassTable *classtab) {
this->e1->code(s, classtab); this->e1->code(s, classtab);
emit_jal("Object.copy", s); emit_jal("Object.copy", s);
emit_fetch_int(T1, ACC, s); emit_fetch_int(T1, ACC, s);
emit_neg(T1, ACC, s); emit_neg(T1, T1, s);
emit_store_int(T1, ACC, 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); emit_load_bool(ACC, *classtab->bool_default(), s);
} else if (classtab->expr_type_decl == Str) { } else if (classtab->expr_type_decl == Str) {
emit_load_string(ACC, classtab->str_default(), s); 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 { } else {
assert(classtab->expr_type_decl); assert(classtab->expr_type_decl);
emit_move(ACC, ZERO, s); emit_move(ACC, ZERO, s);

View File

@ -1,4 +1,5 @@
#include "cool-tree.h" #include "cool-tree.h"
#include "cool-tree.handcode.h"
#include "emit.h" #include "emit.h"
#include "symtab.h" #include "symtab.h"
#include <assert.h> #include <assert.h>
@ -24,7 +25,7 @@ typedef CgenNode *CgenNodeP;
class BoolConst; class BoolConst;
typedef std::vector<std::pair<class__class *, method_class *>> MethodListT; typedef std::vector<std::pair<class__class *, method_class *>> MethodListT;
typedef std::vector<attr_class *> AttrListT; typedef std::vector<std::pair<class__class *, attr_class *>> AttrListT;
struct LocationTableItem { struct LocationTableItem {
LocationTableItem(Symbol id, VariableLocation loc, int off) LocationTableItem(Symbol id, VariableLocation loc, int off)
@ -108,6 +109,7 @@ private:
CgenNode *get_node(Symbol); CgenNode *get_node(Symbol);
void code_objectTab(); void code_objectTab();
void gen_init_code(); void gen_init_code();
void gen_method_code();
public: public:
LocationTable loctab; LocationTable loctab;
@ -168,3 +170,7 @@ public:
void code_def(ostream &, int boolclasstag); void code_def(ostream &, int boolclasstag);
void code_ref(ostream &) const; void code_ref(ostream &) const;
}; };
inline bool is_no_expr(Expression expr) {
return typeid(*expr) == typeid(no_expr_class);
}

View File

@ -20,8 +20,11 @@ class B inherits A {
}; };
class C 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_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 { method_common(x1: Int) : SELF_TYPE {
self self
}; };

View File

@ -1,2 +1,4 @@
#!/bin/bash #!/bin/bash
./lexer $* | ./parser $* | ./semant $* > debug.in
./lexer $* | ./parser $* | ./semant $* | ./cgen $* ./lexer $* | ./parser $* | ./semant $* | ./cgen $*
rm debug.in

View File

@ -1,2 +1,5 @@
#!/bin/bash #!/bin/bash
filename=$(basename -- "$*")
filename="${filename%.*}"
../../bin/lexer $* | ../../bin/parser $* | ../../bin/semant $* | ../../bin/cgen $* ../../bin/lexer $* | ../../bin/parser $* | ../../bin/semant $* | ../../bin/cgen $*
mv $filename.s $filename.std.s