done without gc
This commit is contained in:
parent
c29b455305
commit
fec2e03a75
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
@ -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
|
||||
};
|
||||
|
||||
@ -1,2 +1,4 @@
|
||||
#!/bin/bash
|
||||
./lexer $* | ./parser $* | ./semant $* > debug.in
|
||||
./lexer $* | ./parser $* | ./semant $* | ./cgen $*
|
||||
rm debug.in
|
||||
@ -1,2 +1,5 @@
|
||||
#!/bin/bash
|
||||
filename=$(basename -- "$*")
|
||||
filename="${filename%.*}"
|
||||
../../bin/lexer $* | ../../bin/parser $* | ../../bin/semant $* | ../../bin/cgen $*
|
||||
mv $filename.s $filename.std.s
|
||||
Loading…
Reference in New Issue
Block a user