📄 mips.cpp
字号:
* all registers here.
*/
void Mips::EmitIfZ(Declaration *test, const char *label)
{
Register testReg = GetRegister(test);
SpillAllDirtyRegisters();
Emit("beqz %s, %s\t# branch if %s is zero ", regs[testReg].name, label,
test->GetName());
}
/* Method: EmitArg
* ---------------
* Used to push a parameter on the stack in anticipation of upcoming
* function call. Decrements the stack pointer by 4. Slaves argument into
* register and then stores contents to location just made at end of
* stack.
*/
void Mips::EmitArg(Declaration *arg)
{
Emit("subu $sp, $sp, 4\t# decrement sp to make space for param");
Register reg = GetRegister(arg);
Emit("sw %s, 4($sp)\t# copy param value to stack", regs[reg].name);
}
/* Method: EmitCallInstr
* ---------------------
* Used to effect a function call. All necessary arguments should have
* already been pushed on the stack, this is the last step that
* transfers control from caller to callee. See comments on Goto method
* above for why we spill all registers before making the jump. We issue
* jal for a label, a jalr if address in register. Both will save the
* return address in $ra. If there is an expected result passed, we slave
* the var to a register and copy function return value from $v0 into that
* register. We also remove all parameters from the stack (the bytes
* param tells us how much space to remove) by adjusting the stack pointer
* upwards.
*/
void Mips::EmitCallInstr(Declaration *result, const char *fn, int bytes,
bool isLabel)
{
SpillAllDirtyRegisters();
Emit("%s %-15s\t# jump to function", isLabel? "jal": "jalr", fn);
if (result != NULL) {
Register r1 = GetRegisterForWrite(result);
Emit("move %s, %s\t\t# copy function return value from $v0", regs[r1].name, regs[v0].name);
}
if (bytes != 0)
Emit("add $sp, $sp, %d\t# pop params off stack", bytes);
}
// Two covers for the above method for specific LCall/ACall variants
void Mips::EmitLCall(Declaration *dst, const char *label, int bytes)
{
EmitCallInstr(dst, label, bytes, true);
}
void Mips::EmitACall(Declaration *dst, Declaration *fn, int bytes)
{
EmitCallInstr(dst, regs[GetRegister(fn)].name, bytes, false);
}
/* Method: EmitReturn
* ------------------
* Used to emit code for returning from a function (either from an
* explicit return or falling off the end of the function body).
* If there is an expression to return, we slave that variable into
* a register and move its contents to $v0 (the standard register for
* function result). Before exiting, we spill dirty registers (to
* commit contents of slaved registers to memory, necessary for
* consistency, see comments at SpillForEndFunction above). We also
* do the last part of the callee's job in function call protocol,
* which is to remove our locals/temps from the stack, remove
* saved registers ($fp and $ra) and restore previous values of
* $fp and $ra so everything is returned to the state we entered.
* We then emit jr to jump to the saved $ra.
*/
void Mips::EmitReturn(Declaration *returnVal)
{
if (returnVal != NULL)
Emit("move $v0, %s\t\t# assign return value into $v0",
regs[GetRegister(returnVal)].name);
SpillForEndFunction();
Emit("move $sp, $fp\t\t# pop callee frame off stack");
Emit("lw $ra, -4($fp)\t# restore saved ra");
Emit("lw $fp, 0($fp)\t# restore saved fp");
Emit("jr $ra\t\t# return from function");
}
/* Method: EmitBeginFunction
* -------------------------
* Used to handle the callee's part of the function call protocol
* upon entering a new function. We decrement the $sp to make space
* and then save the current values of $fp and $ra (since we are
* going to change them), then set up the $fp and bump the $sp down
* to make space for all our locals/temps.
*/
void Mips::EmitBeginFunction(int frameSize)
{
Assert(frameSize >= 0);
Emit("subu $sp, $sp, 8\t# decrement sp to make space to save ra, fp");
Emit("sw $fp, 8($sp)\t# save fp");
Emit("sw $ra, 4($sp)\t# save ra");
Emit("addiu $fp, $sp, 8\t# set up new fp");
if (frameSize != 0)
Emit("subu $sp, $sp, %d\t# decrement sp to make space for locals/temps",
frameSize);
}
/* Method: EmitEndFunction
* -----------------------
* Used to end the body of a function. Does an implicit return in fall off
* case to clean up stack frame, return to caller etc. See comments on
* EmitReturn above.
*/
void Mips::EmitEndFunction()
{
Emit("# (below handles reaching end of fn body with no explicit return)");
EmitReturn(NULL);
}
/* Method: EmitVTable
* ------------------
* Used to layout a vtable. Uses assembly directives to set up new
* entry in data segment, emits label, and lays out the function
* labels one after another.
*/
void Mips::EmitVTable(const char *label, DeclList *methods)
{
Emit(".data");
Emit(".align 2");
Emit("%s:\t\t# label for class %s vtable", label, label);
for (int i = 0; i < methods->NumElements(); i++)
Emit(".word %s\n", methods->Nth(i)->GetFunctionLabel());
Emit(".text");
}
/* Method: EmitPreamble
* --------------------
* Used to emit the starting sequence needed for a program. Not much
* here, but need to indicate what follows is in text segment and
* needs to be aligned on word boundary. main is our only global symbol.
*/
void Mips::EmitPreamble()
{
Emit("# standard Decaf preamble ");
Emit(".text");
Emit(".align 2");
Emit(".globl main");
}
/* Method: NameForTac
* ------------------
* Returns the appropriate MIPS instruction (add, seq, etc.) for
* a given Tac::OpCode (Tac::Add, Tac::Equals, etc.). Assets if
* asked for an instruction for an unset/out of bounds code.
*/
const char *Mips::NameForTac(Tac::OpCode code)
{
Assert(code >=0 && code < Tac::NumOps);
const char *name = mipsName[code];
Assert(name != NULL);
return name;
}
/* Constructor
* ----------
* Constructor sets up the mips names and register descriptors to
* the initial starting state.
*/
Mips::Mips() {
mipsName[Tac::Add] = "add";
mipsName[Tac::Sub] = "sub";
mipsName[Tac::Mul] = "mul";
mipsName[Tac::Div] = "div";
mipsName[Tac::Mod] = "rem";
mipsName[Tac::Eq] = "seq";
mipsName[Tac::Less] = "slt";
mipsName[Tac::And] = "and";
mipsName[Tac::Or] = "or";
regs[zero].isDirty = false;
regs[zero].decl = NULL;
regs[zero].name = "$zero";
regs[zero].isGeneralPurpose = false;
regs[at].isDirty = false;
regs[at].decl = NULL;
regs[at].name = "$at";
regs[at].isGeneralPurpose = false;
regs[v0].isDirty = false;
regs[v0].decl = NULL;
regs[v0].name = "$v0";
regs[v0].isGeneralPurpose = false;
regs[v1].isDirty = false;
regs[v1].decl = NULL;
regs[v1].name = "$v1";
regs[v1].isGeneralPurpose = false;
regs[a0].isDirty = false;
regs[a0].decl = NULL;
regs[a0].name = "$a0";
regs[a0].isGeneralPurpose = false;
regs[a1].isDirty = false;
regs[a1].decl = NULL;
regs[a1].name = "$a1";
regs[a1].isGeneralPurpose = false;
regs[a2].isDirty = false;
regs[a2].decl = NULL;
regs[a2].name = "$a2";
regs[a2].isGeneralPurpose = false;
regs[a3].isDirty = false;
regs[a3].decl = NULL;
regs[a3].name = "$a3";
regs[a3].isGeneralPurpose = false;
regs[k0].isDirty = false;
regs[k0].decl = NULL;
regs[k0].name = "$k0";
regs[k0].isGeneralPurpose = false;
regs[k1].isDirty = false;
regs[k1].decl = NULL;
regs[k1].name = "$k1";
regs[k1].isGeneralPurpose = false;
regs[gp].isDirty = false;
regs[gp].decl = NULL;
regs[gp].name = "$gp";
regs[gp].isGeneralPurpose = false;
regs[sp].isDirty = false;
regs[sp].decl = NULL;
regs[sp].name = "$sp";
regs[sp].isGeneralPurpose = false;
regs[fp].isDirty = false;
regs[fp].decl = NULL;
regs[fp].name = "$fp";
regs[fp].isGeneralPurpose = false;
regs[ra].isDirty = false;
regs[ra].decl = NULL;
regs[ra].name = "$ra";
regs[ra].isGeneralPurpose = false;
regs[t0].isDirty = false;
regs[t0].decl = NULL;
regs[t0].name = "$t0";
regs[t0].isGeneralPurpose = true;
regs[t1].isDirty = false;
regs[t1].decl = NULL;
regs[t1].name = "$t1";
regs[t1].isGeneralPurpose = true;
regs[t2].isDirty = false;
regs[t2].decl = NULL;
regs[t2].name = "$t2";
regs[t2].isGeneralPurpose = true;
regs[t3].isDirty = false;
regs[t3].decl = NULL;
regs[t3].name = "$t3";
regs[t3].isGeneralPurpose = true;
regs[t4].isDirty = false;
regs[t4].decl = NULL;
regs[t4].name = "$t4";
regs[t4].isGeneralPurpose = true;
regs[t5].isDirty = false;
regs[t5].decl = NULL;
regs[t5].name = "$t5";
regs[t5].isGeneralPurpose = true;
regs[t6].isDirty = false;
regs[t6].decl = NULL;
regs[t6].name = "$t6";
regs[t6].isGeneralPurpose = true;
regs[t7].isDirty = false;
regs[t7].decl = NULL;
regs[t7].name = "$t7";
regs[t7].isGeneralPurpose = true;
regs[t8].isDirty = false;
regs[t8].decl = NULL;
regs[t8].name = "$t8";
regs[t8].isGeneralPurpose = true;
regs[t9].isDirty = false;
regs[t9].decl = NULL;
regs[t9].name = "$t9";
regs[t9].isGeneralPurpose = true;
regs[s0].isDirty = false;
regs[s0].decl = NULL;
regs[s0].name = "$s0";
regs[s0].isGeneralPurpose = true;
regs[s1].isDirty = false;
regs[s1].decl = NULL;
regs[s1].name = "$s1";
regs[s1].isGeneralPurpose = true;
regs[s2].isDirty = false;
regs[s2].decl = NULL;
regs[s2].name = "$s2";
regs[s2].isGeneralPurpose = true;
regs[s3].isDirty = false;
regs[s3].decl = NULL;
regs[s3].name = "$s3";
regs[s3].isGeneralPurpose = true;
regs[s4].isDirty = false;
regs[s4].decl = NULL;
regs[s4].name = "$s4";
regs[s4].isGeneralPurpose = true;
regs[s5].isDirty = false;
regs[s5].decl = NULL;
regs[s5].name = "$s5";
regs[s5].isGeneralPurpose = true;
regs[s6].isDirty = false;
regs[s6].decl = NULL;
regs[s6].name = "$s6";
regs[s6].isGeneralPurpose = true;
regs[s7].isDirty = false;
regs[s7].decl = NULL;
regs[s7].name = "$s7";
regs[s7].isGeneralPurpose = true;
lastUsed = zero;
}
const char *Mips::mipsName[Tac::NumOps];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -