⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mips.cpp

📁 一个面向对像语言的编译器
💻 CPP
📖 第 1 页 / 共 2 页
字号:
/*
 * File: mips.cc
 * -------------
 * Implementation of Mips class, which is responsible for TAC->MIPS
 * translation, register allocation, etc.
 *
 * Julie Zelenski academic year 2001-02 for CS143
 * Loosely based on some earlier work by Steve Freund
 *
 * A simple final code generator to translate Tac to MIPS.
 * It uses simplistic algorithms, in particular, its register handling
 * and spilling strategy is inefficient to the point of begin mocked
 * by elementary school children.
 */

#include "mips.h"
#include "declaration.h"
#include <stdarg.h>

/* Method: GetRegister
 * -------------------
 * Given a decl for a current var, a reason (ForRead or ForWrite)
 * and up to two registers to avoid, this method will assign
 * to a var to register trying these alternatives in order:
 *  1) if that var is already in a register ("same" is determined
 *     by matching name and same scope), we use that one
 *  2) find an empty register to use for the var
 *  3) find an in-use register that is not dirty.  We don't need
 *     to write value back to memory since it's clean, so we just
 *     replace with the new var
 *  4) spill an in-use, dirty register, by writing its contents to
 *     memory and then replace with the new var
 * For steps 3 & 4, we respect the registers given to avoid (ie the
 * other operands in this operation). The register descriptors are
 * updated to show the new state of the world. If for read, we
 * load the current value from memory into the register. If for
 * write, we mark the register as dirty (since it is getting a
 * new value).
 */
Mips::Register Mips::GetRegister(Declaration *var, Reason reason,
					   Register avoid1, Register avoid2)
{
  Register reg;

  if (!FindRegisterWithContents(var, reg)) {
    if (!FindRegisterWithContents(NULL, reg)) { 
	reg = SelectRegisterToSpill(avoid1, avoid2);
	SpillRegister(reg);
    }
    regs[reg].decl = var;
    if (reason == ForRead) {                 // load current value
	Assert(var->GetOffset() != Unassigned);
	const char *offsetFromWhere = var->IsGlobal() ? regs[gp].name : regs[fp].name;
	Emit("lw %s, %d(%s)\t# load %s from %s%+d into %s", regs[reg].name,
	     var->GetOffset(), offsetFromWhere, var->GetName(),
	     offsetFromWhere, var->GetOffset(), regs[reg].name);
	regs[reg].isDirty = false;
    }
  }
  if (reason == ForWrite)
    regs[reg].isDirty = true;
  return reg;
}


// Two covers for the above method to make it slightly more
// convenient to call it
Mips::Register Mips::GetRegister(Declaration *var, Register avoid1)
{
  return GetRegister(var, ForRead, avoid1, zero);
}

Mips::Register Mips::GetRegisterForWrite(Declaration *var, Register avoid1,
						     Register avoid2)
{
  return GetRegister(var, ForWrite, avoid1, avoid2);
} 



/* Helper function: SameDecl
 * -------------------------
 * Two decls are considered the same if they have the same name and
 * the same scope. We use this when determining if the current variable
 * slaved to a register is the same as a variable we are trying to
 * locate.
 */
static bool SameDecl(Declaration *one, Declaration *two)
{
  if (one == two) return true;
  if (!one || !two ) return false;
  return (!strcmp(one->GetName(), two->GetName()) &&
	    one->GetScope() == two->GetScope());
}


/* Method: FindRegisterWithContents
 * --------------------------------
 * Searches the descriptors for one with contents var. Assigns
 * register by reference, and returns true/false on whether match found.
 */
bool Mips::FindRegisterWithContents(Declaration *var, Register& reg)
{
  for (reg = zero; reg < NumRegs; reg = Register(reg+1)) {
    if (regs[reg].isGeneralPurpose && SameDecl(var, regs[reg].decl))
	return true;
  }
  return false;
}


/* Method: SelectRegisterToSpill
 * -----------------------------
 * Chooses an in-use register to replace with a new variable. We
 * prefer to replace a non-dirty once since we would not have to
 * write its contents back to memory, so the first loop searches
 * for a clean one. If none found, we take a dirty one.  In both
 * loops we deliberately won't choose either of the registers we
 * were asked to avoid.  We track the last dirty register spilled
 * and advance on each subsequent spill as a primitive means of
 * trying to not throw out things we just loaded and thus are likely
 * to need.
 */
Mips::Register Mips::SelectRegisterToSpill(Register avoid1, Register avoid2)
{
            // first hunt for a non-dirty one, since no work to spill
  for (Register i = zero; i < NumRegs; i = (Register)(i+1)) {
    if (i != avoid1 && i != avoid2 && regs[i].isGeneralPurpose &&
	  !regs[i].isDirty)
	return i;
  }
  do {      // otherwise just pick the next usuable register
    lastUsed = (Register)((lastUsed + 1) % NumRegs);
  } while (lastUsed == avoid1 || lastUsed == avoid2 ||
           !regs[lastUsed].isGeneralPurpose);
  return lastUsed;
}


/* Method: SpillRegister
 * ---------------------
 * "Empties" register.  If variable is currently slaved in this register
 * and its contents are out of synch with memory (isDiry), we write back
 * the current contents to memory. We then clear the descriptor so we
 * realize the register is empty.
 */
void Mips::SpillRegister(Register reg)
{
  Declaration *var = regs[reg].decl;
  if (var && regs[reg].isDirty) {
    const char *offsetFromWhere = var->IsGlobal() ? regs[gp].name : regs[fp].name;
    Assert(var->GetOffset() != Unassigned);
    Emit("sw %s, %d(%s)\t# spill %s from %s to %s%+d", regs[reg].name,
	   var->GetOffset(), offsetFromWhere, var->GetName(), regs[reg].name,
	   offsetFromWhere,var->GetOffset());
  }
  regs[reg].decl = NULL;
}       


/* Method: SpillAllDirtyRegisters
 * ------------------------------
 * Used before flow of control change (branch, label, jump, etc.) to
 * save contents of all dirty registers. This synchs the contents of
 * the registers with the memory locations for the variables.
 */
void Mips::SpillAllDirtyRegisters()
{
  Register i;
  for (i = zero; i < NumRegs; i = Register(i+1)) 
    if (regs[i].decl && regs[i].isDirty) break;
  if (i != NumRegs) // none are dirty, don't print message to avoid confusion
    Emit("# (save modified registers before flow of control change)");
  for (i = zero; i < NumRegs; i = Register(i+1)) 
    SpillRegister(i);
}


/* Method: SpillForEndFunction
 * ---------------------------
 * Slight optimization on the above method used when spilling for
 * end of function (return/fall off). In such a case, we do not
 * need to save values for locals, temps, and parameters because the
 * function is about to exit and those variables are going away
 * immediately, so no need to bother with updating contents.
 */
void Mips::SpillForEndFunction()
{
  for (Register i = zero; i < NumRegs; i = Register(i+1)) {
    if (regs[i].isGeneralPurpose && regs[i].decl) {
	if (regs[i].decl->IsGlobal())
	  SpillRegister(i);
	else  // all stack variables can just be tossed at end func
	  regs[i].decl = NULL;
    }
  }
}


/* Method: Emit
 * ------------
 * General purpose helper used to emit assembly instructions in
 * a reasonable tidy manner.  Takes printf-style formatting strings
 * and variable arguments.
 */
void Mips::Emit(const char *fmt, ...)
{
  va_list args;
  char buf[1024];
  
  va_start(args, fmt);
  vsprintf(buf, fmt, args);
  va_end(args);
  if (buf[strlen(buf) - 1] != ':') printf("\t"); // don't tab in labels
  if (buf[0] != '#') printf("  ");   // outdent comments a little
  printf("%s", buf);
  if (buf[strlen(buf)-1] != '\n') printf("\n"); // end with a newline
}



/* Method: EmitLoadConstant
 * ------------------------
 * Used to assign variable an integer constant value.  Slaves dst into
 * a register (using GetRegister above) and then emits an li (load
 * immediate) instruction with the constant value.
 */
void Mips::EmitLoadConstant(Declaration *dst, int val)
{
  Register reg = GetRegisterForWrite(dst);
  Emit("li %s, %d\t\t# load constant value %d into %s", regs[reg].name,
	 val, val, regs[reg].name);
}

/* Method: EmitLoadStringConstant
 * ------------------------------
 * Used to assign a variable a pointer to string constant. Emits
 * assembly directives to create a new null-terminated string in the
 * data segment and assigns it a unique label. Slaves dst into a register
 * and loads that label address into the register.
 */
void Mips::EmitLoadStringConstant(Declaration *dst, const char *str)
{
  static int strNum = 1;
  char label[16];
  sprintf(label, "_string%d", strNum++);
  Emit(".data\t\t\t# create string constant marked with label");
  Emit("%s: .asciiz %s", label, str);
  Emit(".text");
  EmitLoadLabel(dst, label);
}


/* Method: EmitLoadLabel
 * ---------------------
 * Used to load a label (ie address in text/data segment) into a variable.
 * Slaves dst into a register and emits an la (load address) instruction
 */
void Mips::EmitLoadLabel(Declaration *dst, const char *label)
{
  Register reg = GetRegisterForWrite(dst);
  Emit("la %s, %s\t# load label", regs[reg].name, label);
}
 

/* Method: EmitCopy
 * ----------------
 * Used to copy the value of one variable to another.  Slaves both
 * src and dst into registers and then emits a move instruction to
 * copy the contents from src to dst.
 */
void Mips::EmitCopy(Declaration *dst, Declaration *src)
{
  Register rSrc = GetRegister(src), rDst = GetRegisterForWrite(dst, rSrc);
  Emit("move %s, %s\t\t# copy value", regs[rDst].name, regs[rSrc].name);

}


/* Method: EmitLoad
 * ----------------
 * Used to assign dst the contents of memory at the address in reference,
 * potentially with some positive/negative offset (defaults to 0).
 * Slaves both ref and dst to registers, then emits a lw instruction
 * using constant-offset addressing mode y(rx) which accesses the address
 * at an offset of y bytes from the address currently contained in rx.
 */
void Mips::EmitLoad(Declaration *dst, Declaration *reference, int offset)
{
  Register rSrc = GetRegister(reference), rDst = GetRegisterForWrite(dst, rSrc);
  Emit("lw %s, %d(%s) \t# load with offset", regs[rDst].name,
	 offset, regs[rSrc].name);
}


/* Method: EmitStore
 * -----------------
 * Used to write value to  memory at the address in reference,
 * potentially with some positive/negative offset (defaults to 0).
 * Slaves both ref and dst to registers, then emits a sw instruction
 * using constant-offset addressing mode y(rx) which writes to the address
 * at an offset of y bytes from the address currently contained in rx.
 */
void Mips::EmitStore(Declaration *reference, Declaration *value, int offset)
{
  Register rVal = GetRegister(value), rRef = GetRegister(reference, rVal);
  Emit("sw %s, %d(%s) \t# store with offset",
	 regs[rVal].name, offset, regs[rRef].name);
}


/* Method: EmitBinaryOp
 * --------------------
 * Used to perform a binary operation on 2 operands and store result
 * in dst. All binary forms for arithmetic, logical, relational, equality
 * use this method. Slaves both operands and dst to registers, then
 * emits the appropriate instruction by looking up the mips name
 * for the particular op code.
 */
void Mips::EmitBinaryOp(Tac::OpCode code, Declaration *dst, 
				 Declaration *op1, Declaration *op2)
{
  Register rLeft = GetRegister(op1), rRight = GetRegister(op2, rLeft);
  Register rDst = GetRegisterForWrite(dst, rLeft, rRight);
  Emit("%s %s, %s, %s\t", NameForTac(code), regs[rDst].name,
	 regs[rLeft].name, regs[rRight].name);

}


/* Method: EmitLabel
 * -----------------
 * Used to emit label marker. Before a label, we spill all registers since
 * we can't be sure what the situation upon arriving at this label (ie
 * starts new basic block), and rather than try to be clever, we just
 * wipe the slate clean.
 */
void Mips::EmitLabel(const char *label)
{ 
  SpillAllDirtyRegisters(); 
  Emit("%s:", label);
}


/* Method: EmitGoto
 * ----------------
 * Used for an unconditional transfer to a named label. Before a goto,
 * we spill all registers, since we don't know what the situation is
 * we are heading to (ie this ends current basic block) and rather than
 * try to be clever, we just wipe slate clean.
 */
void Mips::EmitGoto(const char *label)
{
  SpillAllDirtyRegisters(); 
  Emit("b %s\t\t# unconditional branch", label);
}


/* Method: EmitIfZ
 * ---------------
 * Used for a conditional branch based on value of test variable.
 * We slave test var to register and use in the emitted test instruction,
 * either beqz. See comments above on Goto for why we spill

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -