context independent code generation done

This commit is contained in:
ridethepig 2023-03-31 14:39:27 +08:00
parent 3ef6812784
commit 2db8b1f4e7
5 changed files with 326 additions and 27 deletions

View File

@ -316,6 +316,15 @@ static void emit_store_int(char *source, char *dest, ostream &s) {
emit_store(source, DEFAULT_OBJFIELDS, dest, s); emit_store(source, DEFAULT_OBJFIELDS, dest, s);
} }
//
// Fetch the boolean value in an Bool object.
// Emits code to fetch the boolean value of the Bool object pointed
// to by register source into the register dest
//
static void emit_fetch_bool(char *dest, char *source, ostream &s) {
emit_load(dest, DEFAULT_OBJFIELDS, source, s);
}
static void emit_test_collector(ostream &s) { static void emit_test_collector(ostream &s) {
emit_push(ACC, s); emit_push(ACC, s);
emit_move(ACC, SP, s); // stack end emit_move(ACC, SP, s); // stack end
@ -976,61 +985,311 @@ CgenNode::CgenNode(Class_ nd, Basicness bstatus, CgenClassTableP ct)
#pragma region ExpressionCoding #pragma region ExpressionCoding
void assign_class::code(ostream &s) {} void assign_class::code(ostream &s, CgenClassTable *classtab) {}
void static_dispatch_class::code(ostream &s) {} void static_dispatch_class::code(ostream &s, CgenClassTable *classtab) {}
void dispatch_class::code(ostream &s) {} void dispatch_class::code(ostream &s, CgenClassTable *classtab) {}
void cond_class::code(ostream &s) {} /*
There's nothing special for if-then-else. Write at your convenience.
Note that value of the predicate is a Bool object, not a boolean, so, dont
forget to extract its value from the object
*/
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);
// 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);
}
void loop_class::code(ostream &s) {} /*
The code for loop is also straightforward. The only thing special is that,
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);
// 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
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_load_imm(ACC, 0, s); // whatever the case, loop expr evaluates to void!
}
void typcase_class::code(ostream &s) {} void typcase_class::code(ostream &s, CgenClassTable *classtab) {}
void block_class::code(ostream &s) {} /*
For block expression, generate code expression by expression, nothing else
*/
void block_class::code(ostream &s, CgenClassTable *classtab) {
for (auto expr_i = this->body->first(); this->body->more(expr_i);
expr_i = this->body->next(expr_i)) {
auto expr = this->body->nth(expr_i);
expr->code(s, classtab);
}
}
void let_class::code(ostream &s) {} void let_class::code(ostream &s, CgenClassTable *classtab) {}
void plus_class::code(ostream &s) {} /*
Operations for Int objects is the most simple one, let's start here
In previous stages, we have make sure that both ends are Int type. Thus, some
hard-coded stuff is acceptable.
First, we evaluate the left part and the result, the address of the Int
object, is in $a0. In the very beginning, I think it is a good idea to keep
it simple, so I'd prefer not to allocate temporary space on stack ahead.
The solution here is to push $a0 to stack.
Then evaluate the right part.
At this time, we have addr(e1) on stack top and addr(e2) in $a0. According to
imagination (>_<), the right thing to do is to extract the value attributes
of the two Int objects (at offset 12), add them up, create a new Int
object and set its value attribute to the result. Finally store the address
of the new Int in $a0 as the return value.
To actually perform these operations, assuming that we are not going to use
any registers other than $a0 and $t0, we need to write some MIPS assembly.
*/
void plus_class::code(ostream &s, CgenClassTable *classtab) {
this->e1->code(s, classtab);
emit_fetch_int(ACC, ACC, s); // load value of e1 to a0
emit_push(ACC, s); // push the 32bit int value instead of object ref
this->e2->code(s, classtab);
emit_jal("Object.copy", s); // copy e2 as the new Int object
emit_push(ACC, s); // save new object's addr, will need it later
emit_fetch_int(ACC, ACC, s); // load value of e2 to a0
emit_load(T1, 2, SP, s);
// load value of e1 to t1, remind we have pushed again, so -8($sp) is val(e1)
emit_add(T1, T1, ACC, s); // $t1 <- val(e1) + val(e2)
emit_load(ACC, 1, SP, s); // load new object addr
emit_store_int(T1, ACC, s); // store result(now in $t1) to the object
emit_addiu(SP, SP, 8, s); // maintain stack balance
}
void sub_class::code(ostream &s) {} /*
Substraction is quite the same as addition, the only thing to note is to get
the substraction order right. e1-e2 != e2-e1, but e1+e2 == e2+e1
*/
void sub_class::code(ostream &s, CgenClassTable *classtab) {
this->e1->code(s, classtab);
emit_fetch_int(ACC, ACC, s);
emit_push(ACC, s);
this->e2->code(s, classtab);
emit_jal("Object.copy", s);
emit_push(ACC, s);
emit_fetch_int(ACC, ACC, s); // $a0 <- val(e2)
emit_load(T1, 2, SP, s); // $t1 <- val(e1)
emit_sub(T1, T1, ACC, s); // $t1 <- val(e1),$t1 + val(e2),$a0
emit_load(ACC, 1, SP, s);
emit_store_int(T1, ACC, s);
emit_addiu(SP, SP, 8, s);
}
void mul_class::code(ostream &s) {} /*
Thanks to pseudo-instruction in spim emulator, we dont need to deal with
hi/lo registers by hand, so the overall code is just the same as addition
*/
void mul_class::code(ostream &s, CgenClassTable *classtab) {
this->e1->code(s, classtab);
emit_fetch_int(ACC, ACC, s);
emit_push(ACC, s);
this->e2->code(s, classtab);
emit_jal("Object.copy", s);
emit_push(ACC, s);
emit_fetch_int(ACC, ACC, s); // $a0 <- val(e2)
emit_load(T1, 2, SP, s); // $t1 <- val(e1)
emit_mul(T1, T1, ACC, s); // $t1 <- val(e1),$t1 + val(e2),$a0
emit_load(ACC, 1, SP, s);
emit_store_int(T1, ACC, s);
emit_addiu(SP, SP, 8, s);
}
void divide_class::code(ostream &s) {} void divide_class::code(ostream &s, CgenClassTable *classtab) {
this->e1->code(s, classtab);
emit_fetch_int(ACC, ACC, s);
emit_push(ACC, s);
this->e2->code(s, classtab);
emit_jal("Object.copy", s);
emit_push(ACC, s);
emit_fetch_int(ACC, ACC, s); // $a0 <- val(e2)
emit_load(T1, 2, SP, s); // $t1 <- val(e1)
emit_div(T1, T1, ACC, s); // $t1 <- val(e1),$t1 + val(e2),$a0
emit_load(ACC, 1, SP, s);
emit_store_int(T1, ACC, s);
emit_addiu(SP, SP, 8, s);
}
void neg_class::code(ostream &s) {} /*
Things are even simpler for unary operations: No need for stack!
Just copy the Int object in $a0 generated by e1 to $a0 (we dont need the e1's
object any more), since we have a temp reg, fetch value into $t1, negate it
and send it back to the new Int object. That's all!
*/
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_store_int(T1, ACC, s);
}
void lt_class::code(ostream &s) {} /*
Though involving two different types, it doesn't make things harder.
We first deal with Int comparsion.
Quite similar to arithmetic operations, evaluate and extract the value
attributes from the sub-expressions.
Things afterwards are different. We need a label and branch instruction to
generate true or false for the expression.
Since there are only 2 registers available, occupied by two operands, we need
two labels (if more registers are available, then only 1 label should
suffice). The logic looks like this:
......(evaluate e1, e2)
if val(e1) < val(e2) goto label1;
$a0 = bool_const0;
goto label2;
label1:
$a0 = bool_const1;
label2:
......(clean up the expression)
*/
void lt_class::code(ostream &s, CgenClassTable *classtab) {
this->e1->code(s, classtab);
emit_fetch_int(ACC, ACC, s);
emit_push(ACC, s); // $sp+4 == val(e1)
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);
// 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_addiu(SP, SP, 4, s); // cleanup anyway, only one push is made
}
void eq_class::code(ostream &s) {} /*
<= operation is exactly the same as < operation except the operator
*/
void leq_class::code(ostream &s, CgenClassTable *classtab) {
this->e1->code(s, classtab);
emit_fetch_int(ACC, ACC, s);
emit_push(ACC, s);
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);
emit_load_bool(ACC, falsebool, s);
emit_branch(label_index_exit, s);
emit_label_def(label_index_true, s);
emit_load_bool(ACC, truebool, s);
emit_label_def(label_index_exit, s);
emit_addiu(SP, SP, 4, s);
}
void leq_class::code(ostream &s) {} /*
Unary Bool operation `not` is more complicated than its Int counterpart
It uses branch, in that we doesn't want to generate bool objects other than
the two predefined constants.
*/
void comp_class::code(ostream &s, CgenClassTable *classtab) {
this->e1->code(s, classtab); // this must be a Bool object in $a0
emit_fetch_bool(T1, ACC, s); // extract boolean value 0 or 1 to $t1
emit_load_bool(ACC, truebool, s); // default load true(assume val(e1)==false)
auto label_index = classtab->alloc_label_index();
emit_beqz(T1, label_index, s); // val(e1)==false, skip the next load
emit_load_bool(ACC, falsebool, s); // val(e1)==true, load false to $a0
emit_label_def(label_index, s); // no clean up needed, just exit here
}
void comp_class::code(ostream &s) {} /*
Equality operator is much more complicated than others.
According to the manual, when comparing two objects, their pointersn first get
compared.
If they resides in the same address, they are definitely equal, return true
at once.
Otherwise, call primitive procedure `equality_test`. As the Runtime System
tour says, it accepts 2 params which resides in $t1 and $t2. The procedure
checks the following condition:
typeid($t1) in {Int, Bool, String}
&& typeid($t1)==typeid($t2)
&& value($1) == value($t2)
The return value is in $a0. If condition is true, $a0<-$a0, else $a0<-$a1,
where $a0, $a1 remain unmodified until return. Utilizing this feature,
we can set $a0<-truebool, $a1<-falsebool before calling the procedure.
Understanding the stuff above, the assembly is just there.
*/
void eq_class::code(ostream &s, CgenClassTable *classtab) {
this->e1->code(s, classtab);
emit_push(ACC, s); // e1 can evaluate to anything, so simply push the ref
this->e2->code(s, classtab); // $a0<-&e2
emit_load(T1, 1, SP, s); // $t1<-&e1
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
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_addiu(SP, SP, 4, s); // balance stack, 1 push in this expression
}
void int_const_class::code(ostream &s) { void int_const_class::code(ostream &s, CgenClassTable *classtab) {
// //
// Need to be sure we have an IntEntry *, not an arbitrary Symbol // Need to be sure we have an IntEntry *, not an arbitrary Symbol
// //
emit_load_int(ACC, inttable.lookup_string(token->get_string()), s); emit_load_int(ACC, inttable.lookup_string(token->get_string()), s);
} }
void string_const_class::code(ostream &s) { void string_const_class::code(ostream &s, CgenClassTable *classtab) {
emit_load_string(ACC, stringtable.lookup_string(token->get_string()), s); emit_load_string(ACC, stringtable.lookup_string(token->get_string()), s);
} }
void bool_const_class::code(ostream &s) { void bool_const_class::code(ostream &s, CgenClassTable *classtab) {
emit_load_bool(ACC, BoolConst(val), s); emit_load_bool(ACC, BoolConst(val), s);
} }
void new__class::code(ostream &s) {} void new__class::code(ostream &s, CgenClassTable *classtab) {}
void isvoid_class::code(ostream &s) {} /*
`isvoid` operation is quite simple and stack-operation free, since we only
test the reference is zero or not. A straightforward beqz should suffice.
*/
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);
// &e1 == nullptr ?, if so skip the next load false
emit_load_bool(ACC, falsebool, s);
emit_label_def(label_index_exit, s);
}
void no_expr_class::code(ostream &s) {} /*
The control flow should not reach here by design
*/
void no_expr_class::code(ostream &s, CgenClassTable *classtab) { assert(0); }
void object_class::code(ostream &s) {} void object_class::code(ostream &s, CgenClassTable *classtab) {}
#pragma endregion #pragma endregion

View File

@ -29,6 +29,7 @@ private:
int stringclasstag; int stringclasstag;
int intclasstag; int intclasstag;
int boolclasstag; int boolclasstag;
int label_count = 0; // an incremental counter to avoid label conflicts
// The following methods emit code for // The following methods emit code for
// constants and global declarations. // constants and global declarations.
@ -55,11 +56,14 @@ private:
void code_dispatchTable(); void code_dispatchTable();
void code_prototypeObject(); void code_prototypeObject();
CgenNode *get_node(Symbol); CgenNode *get_node(Symbol);
void gen_init_code();
public: public:
CgenClassTable(Classes, ostream &str); CgenClassTable(Classes, ostream &str);
void code(); void code();
CgenNodeP root(); CgenNodeP root();
/* aquire an unused label index */
int alloc_label_index() { return label_count++; }
}; };
class CgenNode : public class__class { class CgenNode : public class__class {

View File

@ -11,6 +11,8 @@
#include "cool-tree.handcode.h" #include "cool-tree.handcode.h"
#include "tree.h" #include "tree.h"
class CgenClassTable;
// define the class for phylum // define the class for phylum
// define simple phylum - Program // define simple phylum - Program
typedef class Program_class *Program; typedef class Program_class *Program;

View File

@ -84,13 +84,13 @@ typedef Cases_class *Cases;
type = s; \ type = s; \
return this; \ return this; \
} \ } \
virtual void code(ostream &) = 0; \ virtual void code(ostream &, CgenClassTable *) = 0; \
virtual void dump_with_types(ostream &, int) = 0; \ virtual void dump_with_types(ostream &, int) = 0; \
void dump_type(ostream &, int); \ void dump_type(ostream &, int); \
Expression_class() { type = (Symbol)NULL; } Expression_class() { type = (Symbol)NULL; }
#define Expression_SHARED_EXTRAS \ #define Expression_SHARED_EXTRAS \
void code(ostream &); \ void code(ostream &, CgenClassTable *); \
void dump_with_types(ostream &, int); void dump_with_types(ostream &, int);
#endif #endif

View File

@ -20,7 +20,7 @@ class B inherits A {
}; };
class C inherits A { class C inherits A {
attr_C_1 : String <- "This is C\n"; attr_C_1 : String <- "This is C\n".concat(new A.method_common2());
attr_C_2 : SELF_TYPE; attr_C_2 : SELF_TYPE;
method_common(x1: Int) : SELF_TYPE { method_common(x1: Int) : SELF_TYPE {
self self
@ -31,6 +31,22 @@ class C inherits A {
method_1(x1: Int) : Bool { method_1(x1: Int) : Bool {
true true
}; };
method_3(x1: Int, x2: A, x3: String) : Object {
{
x1 <- x1 + 00010 - x1 / 10;
x2 <- x2.method_common(x1);
x3 <- x3.concat("schade");
}
};
method_2(x1: Bool, x2: C) : Object {
{
if (x1) then
x2 <- x2.method_common(20)
else
"hello world"
fi;
}
};
}; };
class D inherits C { class D inherits C {
@ -39,6 +55,24 @@ class D inherits C {
}; };
}; };
class E {
method_compare(x1: Int, x2: Int) : Bool {
x1 < x2
};
method_compare_eq(x1: String, x2: String) : Bool {
x1 = x2
};
method_compare_eq2(x1: D, x2: D) : Bool {
x1 = x2
};
method_not(x1: Bool) : Bool {
not x1
};
method_isvoid(x1: Object) : Bool {
isvoid x1
};
};
class Main inherits IO { class Main inherits IO {
main():Int { 0 }; main():Int { 0 };
}; };