all expression generator done

This commit is contained in:
ridethepig 2023-03-31 16:34:05 +00:00
parent 7140f01bb0
commit c29b455305
3 changed files with 166 additions and 74 deletions

View File

@ -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
@ -435,8 +438,7 @@ void IntEntry::code_def(ostream &s, int intclasstag) {
code_ref(s);
s << LABEL // label
<< WORD << intclasstag << endl // class tag
<< WORD << (DEFAULT_OBJFIELDS + INT_SLOTS) << endl // object size
<< WORD;
<< WORD << (DEFAULT_OBJFIELDS + INT_SLOTS) << endl; // object size
s << WORD << INTNAME << DISPTAB_SUFFIX << endl; // dispatch ptr
s << WORD << str << endl; // integer value
@ -471,8 +473,7 @@ void BoolConst::code_def(ostream &s, int boolclasstag) {
code_ref(s);
s << LABEL // label
<< WORD << boolclasstag << endl // class tag
<< WORD << (DEFAULT_OBJFIELDS + BOOL_SLOTS) << endl // object size
<< WORD;
<< 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
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_index_exit, s); // $a0 <- then, and we goto exit
emit_label_def(label_index_false, s);
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,16 +1453,16 @@ 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_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_index_exit, s); // second jump goes to here
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);
}
/*

View File

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

View File

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