all expression generator done
This commit is contained in:
parent
7140f01bb0
commit
c29b455305
@ -25,9 +25,11 @@
|
||||
#include "cgen.h"
|
||||
#include "cgen_gc.h"
|
||||
#include "cool-tree.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
|
||||
extern void emit_string_constant(ostream &str, char *s);
|
||||
@ -95,6 +97,7 @@ BoolConst falsebool(FALSE);
|
||||
BoolConst truebool(TRUE);
|
||||
|
||||
static int rel_stack_depth;
|
||||
static int class_tag_count;
|
||||
//*********************************************************
|
||||
//
|
||||
// Define method for code generation
|
||||
@ -433,10 +436,9 @@ void IntEntry::code_def(ostream &s, int intclasstag) {
|
||||
s << WORD << "-1" << endl;
|
||||
|
||||
code_ref(s);
|
||||
s << LABEL // label
|
||||
<< WORD << intclasstag << endl // class tag
|
||||
<< WORD << (DEFAULT_OBJFIELDS + INT_SLOTS) << endl // object size
|
||||
<< WORD;
|
||||
s << LABEL // label
|
||||
<< WORD << intclasstag << endl // class tag
|
||||
<< WORD << (DEFAULT_OBJFIELDS + INT_SLOTS) << endl; // object size
|
||||
|
||||
s << WORD << INTNAME << DISPTAB_SUFFIX << endl; // dispatch ptr
|
||||
s << WORD << str << endl; // integer value
|
||||
@ -469,10 +471,9 @@ void BoolConst::code_def(ostream &s, int boolclasstag) {
|
||||
s << WORD << "-1" << endl;
|
||||
|
||||
code_ref(s);
|
||||
s << LABEL // label
|
||||
<< WORD << boolclasstag << endl // class tag
|
||||
<< WORD << (DEFAULT_OBJFIELDS + BOOL_SLOTS) << endl // object size
|
||||
<< WORD;
|
||||
s << LABEL // label
|
||||
<< WORD << boolclasstag << endl // class tag
|
||||
<< WORD << (DEFAULT_OBJFIELDS + BOOL_SLOTS) << endl; // object size
|
||||
|
||||
s << WORD << BOOLNAME << DISPTAB_SUFFIX << endl; // dispatch ptr
|
||||
s << WORD << val << endl; // value (0 or 1)
|
||||
@ -674,6 +675,18 @@ CgenClassTable::CgenClassTable(Classes classes, ostream &s) : str(s) {
|
||||
build_inheritance_tree();
|
||||
if (cgen_debug)
|
||||
dump_inheritance_tree();
|
||||
root()->traverse_allocate_tag();
|
||||
std::sort(nodes.begin(), nodes.end(), [](CgenNode *a, CgenNode *b) {
|
||||
return a->get_class_tag() < b->get_class_tag();
|
||||
});
|
||||
if (cgen_debug) {
|
||||
std::cerr << "Dump class tags:\n";
|
||||
for (auto node : nodes) {
|
||||
std::cerr << node->name << ": " << node->get_class_tag() << ","
|
||||
<< node->get_children_tag() << "\n";
|
||||
}
|
||||
}
|
||||
// sort the vector is in tag order to generate correct class_objTab
|
||||
root()->traverse_generate_object();
|
||||
// nodes[0] is the Object class
|
||||
stringclasstag = get_node(Str)->get_class_tag();
|
||||
@ -818,7 +831,6 @@ void CgenClassTable::install_class(CgenNodeP nd) {
|
||||
// The class name is legal, so add it to the list of classes
|
||||
// and the symbol table.
|
||||
nodes.push_back(nd);
|
||||
nd->set_class_tag(nodes.size());
|
||||
addid(name, nd);
|
||||
}
|
||||
|
||||
@ -1057,6 +1069,14 @@ void CgenNode::traverse_generate_object() {
|
||||
}
|
||||
}
|
||||
|
||||
void CgenNode::traverse_allocate_tag() {
|
||||
this->_class_tag = class_tag_count++;
|
||||
for (auto child : children) {
|
||||
child->traverse_allocate_tag();
|
||||
}
|
||||
this->_children_tag_range = class_tag_count - 1;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
//******************************************************************
|
||||
@ -1105,9 +1125,9 @@ void static_dispatch_class::code(ostream &s, CgenClassTable *classtab) {
|
||||
emit_push(ACC, s); // push param to stack
|
||||
}
|
||||
this->expr->code(s, classtab);
|
||||
auto label_index_dispatch = classtab->alloc_label_index();
|
||||
auto label_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);
|
||||
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);
|
||||
@ -1117,7 +1137,7 @@ void static_dispatch_class::code(ostream &s, CgenClassTable *classtab) {
|
||||
s << endl;
|
||||
emit_jal("_dispatch_abort", s);
|
||||
// ready to call the method
|
||||
emit_label_def(label_index_dispatch, s);
|
||||
emit_label_def(label_dispatch, s);
|
||||
// load dispatch table
|
||||
emit_partial_load_address(T1, s);
|
||||
emit_disptable_ref(this->type_name, s);
|
||||
@ -1141,9 +1161,9 @@ void dispatch_class::code(ostream &s, CgenClassTable *classtab) {
|
||||
emit_push(ACC, s); // push param to stack
|
||||
}
|
||||
this->expr->code(s, classtab);
|
||||
auto label_index_dispatch = classtab->alloc_label_index();
|
||||
auto label_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);
|
||||
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);
|
||||
@ -1153,7 +1173,7 @@ void dispatch_class::code(ostream &s, CgenClassTable *classtab) {
|
||||
s << endl;
|
||||
emit_jal("_dispatch_abort", s);
|
||||
// ready to call the method
|
||||
emit_label_def(label_index_dispatch, s);
|
||||
emit_label_def(label_dispatch, s);
|
||||
// load dispatch table
|
||||
emit_load(T1, DISPTABLE_OFFSET, ACC, s);
|
||||
// load method address
|
||||
@ -1178,15 +1198,15 @@ void dispatch_class::code(ostream &s, CgenClassTable *classtab) {
|
||||
void cond_class::code(ostream &s, CgenClassTable *classtab) {
|
||||
this->pred->code(s, classtab);
|
||||
emit_fetch_bool(ACC, ACC, s); // fetch the boolean value into $a0
|
||||
auto label_index_false = classtab->alloc_label_index();
|
||||
auto label_index_exit = classtab->alloc_label_index();
|
||||
emit_beqz(ACC, label_index_false, s); // $a0==0, pred is false, goto else
|
||||
this->then_exp->code(s, classtab); // pred is true, eval then branch
|
||||
emit_branch(label_index_exit, s); // $a0 <- then, and we goto exit
|
||||
emit_label_def(label_index_false, s);
|
||||
auto label_false = classtab->alloc_label_index();
|
||||
auto label_exit = classtab->alloc_label_index();
|
||||
emit_beqz(ACC, label_false, s); // $a0==0, pred is false, goto else
|
||||
this->then_exp->code(s, classtab); // pred is true, eval then branch
|
||||
emit_branch(label_exit, s); // $a0 <- then, and we goto exit
|
||||
emit_label_def(label_false, s);
|
||||
// the label is preserved in advance so that we can define it here safely
|
||||
this->else_exp->code(s, classtab); // $a0 <- else
|
||||
emit_label_def(label_index_exit, s);
|
||||
emit_label_def(label_exit, s);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1194,20 +1214,92 @@ void cond_class::code(ostream &s, CgenClassTable *classtab) {
|
||||
loop always evaluate to void, so remember to set $a0<-0
|
||||
*/
|
||||
void loop_class::code(ostream &s, CgenClassTable *classtab) {
|
||||
auto label_index_predicate = classtab->alloc_label_index();
|
||||
emit_label_def(label_index_predicate, s);
|
||||
auto label_predicate = classtab->alloc_label_index();
|
||||
emit_label_def(label_predicate, s);
|
||||
// predefine a label and our loop could restart and check predicate from here
|
||||
this->pred->code(s, classtab); // check predicate, which sets $a0<-Bool object
|
||||
emit_fetch_bool(ACC, ACC, s); // extrace 0 or 1 from Bool object in $a0
|
||||
auto label_index_exit = classtab->alloc_label_index();
|
||||
emit_beqz(ACC, label_index_exit, s); // if predicate is false, goto exit
|
||||
auto label_exit = classtab->alloc_label_index();
|
||||
emit_beqz(ACC, label_exit, s); // if predicate is false, goto exit
|
||||
this->body->code(s, classtab); // if predicate is true, move on with our loop
|
||||
emit_branch(label_index_predicate, s); // anyways, go back and check predicate
|
||||
emit_label_def(label_index_exit, s);
|
||||
emit_branch(label_predicate, s); // anyways, go back and check predicate
|
||||
emit_label_def(label_exit, s);
|
||||
emit_load_imm(ACC, 0, s); // whatever the case, loop expr evaluates to void!
|
||||
}
|
||||
|
||||
void typcase_class::code(ostream &s, CgenClassTable *classtab) {}
|
||||
/*
|
||||
I think type case is the most difficult part in this PA. It selects the
|
||||
closest ancestor of the expr's type, thus requiring hierarchy infomation
|
||||
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
|
||||
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 ->
|
||||
`_case_abort2`. Remember to set these two guys.
|
||||
*/
|
||||
void typcase_class::code(ostream &s, CgenClassTable *classtab) {
|
||||
this->expr->code(s, classtab);
|
||||
auto label_case = classtab->alloc_label_index();
|
||||
// use bne to save a label, because we'll never come back if abort
|
||||
emit_bne(ACC, ZERO, label_case, s);
|
||||
// Prints the line number, from $t1, and filename, from $a0, at which the
|
||||
// type case 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("_case_abort2", s);
|
||||
// start type case here
|
||||
emit_label_def(label_case, s);
|
||||
// load class tag, if no error, object ref is in $a0
|
||||
emit_load(T1, TAG_OFFSET, ACC, s);
|
||||
// generate a map<Type, BranchNode> to simplify branch search
|
||||
std::map<Symbol, branch_class *> sym_branch;
|
||||
for (auto branch_i = this->cases->first(); this->cases->more(branch_i);
|
||||
branch_i = this->cases->next(branch_i)) {
|
||||
auto branch = static_cast<branch_class *>(this->cases->nth(branch_i));
|
||||
sym_branch[branch->type_decl] = branch;
|
||||
}
|
||||
auto label_exit = classtab->alloc_label_index();
|
||||
// generate the branches in reverse pre-order
|
||||
for (auto node_ritr = classtab->nodes.rbegin();
|
||||
node_ritr != classtab->nodes.rend(); ++node_ritr) {
|
||||
auto node = *node_ritr;
|
||||
auto branch_itr = sym_branch.find(node->name);
|
||||
if (branch_itr == sym_branch.end())
|
||||
continue;
|
||||
auto branch = branch_itr->second;
|
||||
auto label_branch = classtab->alloc_label_index();
|
||||
// if expr's tag is not in the branch's children range, test next branch
|
||||
emit_blti(T1, node->get_class_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
|
||||
// because we dont know whether $a0 is going to be overwritten
|
||||
emit_push(ACC, s);
|
||||
// allocate a new location
|
||||
classtab->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();
|
||||
emit_pop(s); // balance stack
|
||||
// already matched, the matching process ends here and go to exit
|
||||
emit_branch(label_exit, s);
|
||||
// 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
|
||||
// is taken, thus unnecessary to set it
|
||||
emit_jal("_case_abort", s);
|
||||
emit_label_def(label_exit, s);
|
||||
}
|
||||
|
||||
/*
|
||||
For block expression, generate code expression by expression, nothing else
|
||||
@ -1361,17 +1453,17 @@ void lt_class::code(ostream &s, CgenClassTable *classtab) {
|
||||
this->e2->code(s, classtab);
|
||||
emit_fetch_int(ACC, ACC, s); // $a0 <- val(e2)
|
||||
emit_load(T1, 1, SP, s); // $t1 <- val(e1)
|
||||
auto label_index_true = classtab->alloc_label_index();
|
||||
auto label_index_exit = classtab->alloc_label_index();
|
||||
emit_blt(T1, ACC, label_index_true, s);
|
||||
auto label_true = classtab->alloc_label_index();
|
||||
auto label_exit = classtab->alloc_label_index();
|
||||
emit_blt(T1, ACC, label_true, s);
|
||||
// after the branch, val(e2) in $a0 is no more needed, making space for result
|
||||
emit_load_bool(ACC, falsebool, s);
|
||||
// didn't jump in last instruction, so it is false
|
||||
emit_branch(label_index_exit, s); // dont want it to be overridden
|
||||
emit_label_def(label_index_true, s); // first branch goes to here
|
||||
emit_load_bool(ACC, truebool, s); // branch means true
|
||||
emit_label_def(label_index_exit, s); // second jump goes to here
|
||||
emit_pop(1, s); // cleanup anyway, only one push is made
|
||||
emit_branch(label_exit, s); // dont want it to be overridden
|
||||
emit_label_def(label_true, s); // first branch goes to here
|
||||
emit_load_bool(ACC, truebool, s); // branch means true
|
||||
emit_label_def(label_exit, s); // second jump goes to here
|
||||
emit_pop(1, s); // cleanup anyway, only one push is made
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1384,14 +1476,14 @@ void leq_class::code(ostream &s, CgenClassTable *classtab) {
|
||||
this->e2->code(s, classtab);
|
||||
emit_fetch_int(ACC, ACC, s);
|
||||
emit_load(T1, 1, SP, s);
|
||||
auto label_index_true = classtab->alloc_label_index();
|
||||
auto label_index_exit = classtab->alloc_label_index();
|
||||
emit_bleq(T1, ACC, label_index_true, s);
|
||||
auto label_true = classtab->alloc_label_index();
|
||||
auto label_exit = classtab->alloc_label_index();
|
||||
emit_bleq(T1, ACC, label_true, s);
|
||||
emit_load_bool(ACC, falsebool, s);
|
||||
emit_branch(label_index_exit, s);
|
||||
emit_label_def(label_index_true, s);
|
||||
emit_branch(label_exit, s);
|
||||
emit_label_def(label_true, s);
|
||||
emit_load_bool(ACC, truebool, s);
|
||||
emit_label_def(label_index_exit, s);
|
||||
emit_label_def(label_exit, s);
|
||||
emit_pop(1, s);
|
||||
}
|
||||
|
||||
@ -1435,13 +1527,13 @@ void eq_class::code(ostream &s, CgenClassTable *classtab) {
|
||||
emit_move(T2, ACC, s); // $t2<-&e2
|
||||
// there's no choice but to break our 2 register rule ~_~
|
||||
emit_load_bool(ACC, truebool, s); // load true in advance in case &e1==&e2
|
||||
auto label_index_exit = classtab->alloc_label_index();
|
||||
emit_beq(T1, T2, label_index_exit, s); // if &e1==&e2, then we are done here
|
||||
auto label_exit = classtab->alloc_label_index();
|
||||
emit_beq(T1, T2, label_exit, s); // if &e1==&e2, then we are done here
|
||||
emit_load_bool(A1, falsebool, s); // prepare a false choice for equality_test
|
||||
emit_jal("equality_test", s);
|
||||
// at this point, $t1=&e1, $t2=&e2, $a0=true, $a1=false. ready to call
|
||||
// $a0 will be either true or false, so just keep it as the result
|
||||
emit_label_def(label_index_exit, s);
|
||||
emit_label_def(label_exit, s);
|
||||
emit_pop(1, s); // balance stack, 1 push in this expression
|
||||
}
|
||||
|
||||
@ -1522,11 +1614,11 @@ void isvoid_class::code(ostream &s, CgenClassTable *classtab) {
|
||||
this->e1->code(s, classtab);
|
||||
emit_move(T1, ACC, s); // $t1 <- &e1
|
||||
emit_load_bool(ACC, truebool, s);
|
||||
auto label_index_exit = classtab->alloc_label_index();
|
||||
emit_beqz(T1, label_index_exit, s);
|
||||
auto label_exit = classtab->alloc_label_index();
|
||||
emit_beqz(T1, label_exit, s);
|
||||
// &e1 == nullptr ?, if so skip the next load false
|
||||
emit_load_bool(ACC, falsebool, s);
|
||||
emit_label_def(label_index_exit, s);
|
||||
emit_label_def(label_exit, s);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@ -38,10 +38,11 @@ class LocationTable {
|
||||
private:
|
||||
std::vector<LocationTableItem> table;
|
||||
std::vector<size_t> scope;
|
||||
|
||||
public:
|
||||
// lookup the inner-most item with the same id as name
|
||||
LocationTableItem* lookup(Symbol name) {
|
||||
for (auto itr = table.rbegin(); itr != table.rend(); ++ itr) {
|
||||
LocationTableItem *lookup(Symbol name) {
|
||||
for (auto itr = table.rbegin(); itr != table.rend(); ++itr) {
|
||||
if (itr->id == name) {
|
||||
return &*itr;
|
||||
}
|
||||
@ -49,12 +50,8 @@ public:
|
||||
assert(0);
|
||||
return nullptr;
|
||||
}
|
||||
void add(LocationTableItem item) {
|
||||
table.push_back(item);
|
||||
};
|
||||
void enterscope() {
|
||||
scope.push_back(table.size());
|
||||
}
|
||||
void add(LocationTableItem item) { table.push_back(item); };
|
||||
void enterscope() { scope.push_back(table.size()); }
|
||||
void exitscope() {
|
||||
auto end = scope.back();
|
||||
scope.pop_back();
|
||||
@ -62,10 +59,11 @@ public:
|
||||
}
|
||||
void dump() {
|
||||
scope.push_back(table.size());
|
||||
for (size_t i = 0; i < scope.size() - 1; ++ i) {
|
||||
for (size_t i = 0; i < scope.size() - 1; ++i) {
|
||||
std::cerr << "scope" << i << "\n";
|
||||
for (size_t j = scope[i]; j < scope[i+1]; j ++) {
|
||||
std::cerr << table[j].id << ", " << table[j].location << ", " << table[j].offset << "\n";
|
||||
for (size_t j = scope[i]; j < scope[i + 1]; j++) {
|
||||
std::cerr << table[j].id << ", " << table[j].location << ", "
|
||||
<< table[j].offset << "\n";
|
||||
}
|
||||
}
|
||||
scope.pop_back();
|
||||
@ -74,14 +72,13 @@ public:
|
||||
|
||||
class CgenClassTable : public SymbolTable<Symbol, CgenNode> {
|
||||
private:
|
||||
std::vector<CgenNode *> nodes;
|
||||
ostream &str;
|
||||
int stringclasstag;
|
||||
int intclasstag;
|
||||
int boolclasstag;
|
||||
IntEntry* _int_default = nullptr;
|
||||
StringEntry* _str_default = nullptr;
|
||||
BoolConst* _bool_default = nullptr;
|
||||
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
|
||||
@ -115,15 +112,16 @@ private:
|
||||
public:
|
||||
LocationTable loctab;
|
||||
Symbol expr_type_decl;
|
||||
CgenNode* cur_class;
|
||||
std::vector<CgenNode *> nodes;
|
||||
CgenNode *cur_class;
|
||||
CgenClassTable(Classes, ostream &str);
|
||||
void code();
|
||||
CgenNode* root() { return nodes[0]; }
|
||||
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; };
|
||||
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);
|
||||
};
|
||||
|
||||
@ -133,10 +131,11 @@ private:
|
||||
std::vector<CgenNode *> children; // Children of class
|
||||
Basicness basic_status; // `Basic' if class is basic
|
||||
// `NotBasic' otherwise
|
||||
/* class_tag is allocated in pre-order */
|
||||
uint32_t _class_tag = 0;
|
||||
/* Allocate a class_tag on first scan increase in scanning order,
|
||||
starting from 1, so 0 is actually invalid by design
|
||||
*/
|
||||
/* record children's class tag range for type case code gen */
|
||||
uint32_t _children_tag_range;
|
||||
|
||||
AttrListT attributes;
|
||||
// attr list including inherited
|
||||
MethodListT methods;
|
||||
@ -151,10 +150,11 @@ public:
|
||||
CgenNodeP get_parentnd() { return parentnd; }
|
||||
int basic() { return (basic_status == Basic); }
|
||||
uint32_t get_class_tag() { return _class_tag; }
|
||||
void set_class_tag(uint32_t val) { _class_tag = val; }
|
||||
uint32_t get_children_tag() { return _children_tag_range; }
|
||||
void traverse_dump(int pad);
|
||||
uint32_t get_object_size() { return (3 + attributes.size()); }
|
||||
void traverse_generate_object();
|
||||
void traverse_allocate_tag();
|
||||
AttrListT *get_attributes() { return &attributes; };
|
||||
MethodListT *get_methods() { return &methods; };
|
||||
};
|
||||
|
||||
@ -68,7 +68,7 @@ class D inherits C {
|
||||
method_case(x1 : Object) : Int {
|
||||
case (x1) of
|
||||
t1: Bool => 1;
|
||||
t2: Int => 2;
|
||||
t2: Int => t2 + 100;
|
||||
t3: String => 3;
|
||||
t4: Object => 4;
|
||||
t5: A => 5;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user