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;
}
/*
| ........ |
|------------|
| 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<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) {
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<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
} else {
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 << "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);

View File

@ -1,4 +1,5 @@
#include "cool-tree.h"
#include "cool-tree.handcode.h"
#include "emit.h"
#include "symtab.h"
#include <assert.h>
@ -24,7 +25,7 @@ typedef CgenNode *CgenNodeP;
class BoolConst;
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 {
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);
}

View File

@ -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
};

View File

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

View File

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