context independent code generation done
This commit is contained in:
parent
3ef6812784
commit
2db8b1f4e7
@ -316,6 +316,15 @@ static void emit_store_int(char *source, char *dest, ostream &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) {
|
||||
emit_push(ACC, s);
|
||||
emit_move(ACC, SP, s); // stack end
|
||||
@ -976,61 +985,311 @@ CgenNode::CgenNode(Class_ nd, Basicness bstatus, CgenClassTableP ct)
|
||||
|
||||
#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
|
||||
//
|
||||
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);
|
||||
}
|
||||
|
||||
void bool_const_class::code(ostream &s) {
|
||||
void bool_const_class::code(ostream &s, CgenClassTable *classtab) {
|
||||
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
|
||||
|
||||
@ -29,6 +29,7 @@ private:
|
||||
int stringclasstag;
|
||||
int intclasstag;
|
||||
int boolclasstag;
|
||||
int label_count = 0; // an incremental counter to avoid label conflicts
|
||||
|
||||
// The following methods emit code for
|
||||
// constants and global declarations.
|
||||
@ -55,11 +56,14 @@ private:
|
||||
void code_dispatchTable();
|
||||
void code_prototypeObject();
|
||||
CgenNode *get_node(Symbol);
|
||||
void gen_init_code();
|
||||
|
||||
public:
|
||||
CgenClassTable(Classes, ostream &str);
|
||||
void code();
|
||||
CgenNodeP root();
|
||||
/* aquire an unused label index */
|
||||
int alloc_label_index() { return label_count++; }
|
||||
};
|
||||
|
||||
class CgenNode : public class__class {
|
||||
|
||||
@ -11,6 +11,8 @@
|
||||
#include "cool-tree.handcode.h"
|
||||
#include "tree.h"
|
||||
|
||||
class CgenClassTable;
|
||||
|
||||
// define the class for phylum
|
||||
// define simple phylum - Program
|
||||
typedef class Program_class *Program;
|
||||
|
||||
@ -84,13 +84,13 @@ typedef Cases_class *Cases;
|
||||
type = s; \
|
||||
return this; \
|
||||
} \
|
||||
virtual void code(ostream &) = 0; \
|
||||
virtual void code(ostream &, CgenClassTable *) = 0; \
|
||||
virtual void dump_with_types(ostream &, int) = 0; \
|
||||
void dump_type(ostream &, int); \
|
||||
Expression_class() { type = (Symbol)NULL; }
|
||||
|
||||
#define Expression_SHARED_EXTRAS \
|
||||
void code(ostream &); \
|
||||
void code(ostream &, CgenClassTable *); \
|
||||
void dump_with_types(ostream &, int);
|
||||
|
||||
#endif
|
||||
|
||||
@ -20,7 +20,7 @@ class B 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;
|
||||
method_common(x1: Int) : SELF_TYPE {
|
||||
self
|
||||
@ -31,6 +31,22 @@ class C inherits A {
|
||||
method_1(x1: Int) : Bool {
|
||||
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 {
|
||||
@ -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 {
|
||||
main():Int { 0 };
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user