/* zweic -- a compiler for zwei
*
* Stephane Micheloud & LAMP
*
* $Id$
*/
package zweic;
import java.io.PrintWriter;
class Code {
import RISC._;
//########################################################################
// Constants
/** Index of first general-use register. */
private val RC_MIN = 1;
/** Index of last general-use register. */
private val RC_MAX = 29;
//########################################################################
// Instance variables
/** Free registers */
private val freeRegisters = new Array[Boolean](32);
/** Already emitted instructions */
private var code = List[Instruction]();
/** Stack frame size */
private var frameSize = 0;
//########################################################################
// Initialization
Iterator.range(RC_MIN, RC_MAX+1) foreach { r => freeRegisters(r) = true };
//########################################################################
// Methods - register allocation
/** Allocate a register. */
def getRegister(): Int =
Iterator.range(RC_MIN, RC_MAX+1) find { r => freeRegisters(r) } match {
case Some(r) => getRegister(r)
case None => throw new Error("no more free registers")
}
/**
* Allocate the given register, which has to be free (otherwise an
* exception is thrown).
*/
def getRegister(r: Int): Int = {
if (! freeRegisters(r))
throw new Error("attempt to allocate non-free register R" + r);
freeRegisters(r) = false;
r
}
/** Free a register. */
def freeRegister(r: Int): Unit = {
if (freeRegisters(r))
throw new Error("attempt to free already-free register R" + r);
if (r < RC_MIN || r > RC_MAX)
throw new Error("attempt to free special register R" + r);
freeRegisters(r) = true
}
/**
* Return the set of used registers as an array. If element at
* index I is true, this means that register R[i] is in use.
*/
def usedRegisters(): Array[Boolean] = {
val used = new Array[Boolean](freeRegisters.length);
for (val i <- Iterator.range(0, used.length))
used(i) = (i >= RC_MIN) && (i <= RC_MAX) && !freeRegisters(i);
used
}
//########################################################################
// Methods - address handling
/** Return address of next instruction. */
def pc(): Int = WORD_SIZE * code.length;
//########################################################################
// Methods - frame handling
/** Increment frame size by given value. */
def incFrameSize(bytes: Int) =
frameSize = frameSize + bytes;
/** Decrement frame size by given value. */
def decFrameSize(bytes: Int): Unit = {
frameSize = frameSize - bytes;
assert(frameSize >= 0);
}
/** Return current frame size. */
def getFrameSize(): Int = frameSize;
//########################################################################
// Methods - code emitting
def emit(opcode: Int): Unit =
emit(opcode, Integer.MIN_VALUE);
def emit(opcode: Int, c: Int): Unit =
emit(opcode, Integer.MIN_VALUE, c);
def emit(opcode: Int, c: Int, s: String): Unit =
emit(opcode, Integer.MIN_VALUE, Integer.MIN_VALUE, c, s);
def emit(opcode: Int, l: Label): Unit =
emit(opcode, Integer.MIN_VALUE, l);
def emit(opcode: Int, a: Int, c: Int): Unit =
emit(opcode, a, Integer.MIN_VALUE, c);
def emit(opcode: Int, a: Int, l: Label): Unit =
if (l.isAnchored())
emit(opcode, a, (l.getAnchor() - pc()) / WORD_SIZE);
else {
l.recordInstructionToPatch(pc());
emit(opcode, a, Integer.MIN_VALUE, Integer.MIN_VALUE);
};
private def syscall2string(c: Int): String = c match {
case SYS_IO_RD_CHR => "SYS_IO_RD_CHR"
case SYS_IO_RD_INT => "SYS_IO_RD_INT"
case SYS_IO_WR_CHR => "SYS_IO_WR_CHR"
case SYS_IO_WR_INT => "SYS_IO_WR_INT"
case SYS_GC_INIT => "SYS_GC_INIT"
case SYS_GC_ALLOC => "SYS_GC_ALLOC"
case SYS_GET_TOTAL_MEM_SIZE => "SYS_GET_TOTAL_MEM_SIZE"
case SYS_EXIT => "SYS_EXIT"
case _ => Integer.toString(c)
};
def emit(opcode: Int, a: Int, b: Int, c: Int): Unit = {
val s = if (opcode == SYSCALL) syscall2string(c) else null;
emit(opcode, a, b, c, s)
}
def emit(opcode: Int, a: Int, b: Int, c: Int, s: String): Unit =
code = new Instruction(opcode, a, b, c, s) :: code;
//########################################################################
// Methods - program printing.
/** Print program. */
def write(out: PrintWriter): Unit = {
val n = code.length;
var i:Int = 0;
while(i < n) {
var label = Integer.toString(WORD_SIZE * i);
while (label.length() < 4) label = '0' + label;
out.print("/* " + label + " */ ");
out.print(code(n - i - 1));
out.println();
i=i+1;
}
out.flush()
}
//########################################################################
// Labels
def getLabel(): Label = new Label();
def anchorLabel(l: Label): Unit = l.setAnchor(pc());
class Label {
private var toPatch = List[Int]();
private var pc = -1;
def setAnchor(pc: Int): Unit = {
this.pc = pc;
toPatch foreach { instrPC =>
code((pc - instrPC) / WORD_SIZE - 1).c = (pc - instrPC) / WORD_SIZE; }
}
def getAnchor(): Int = pc;
def isAnchored(): Boolean = pc >= 0;
def recordInstructionToPatch(pc: Int): Unit =
toPatch = pc :: toPatch;
}
//########################################################################
// Instructions
private case class Instruction(opcode: Int, a: Int, b: Int, _c: Int, s: String) {
var c = _c;
override def toString(): String = {
val buffer = new StringBuffer();
buffer.append(mnemonics(opcode));
if (a != Integer.MIN_VALUE) buffer.append(' ').append(a);
if (b != Integer.MIN_VALUE) buffer.append(' ').append(b);
if (c != Integer.MIN_VALUE) buffer.append(' ').append(c);
if (s != null) {
var i = buffer.length();
while (i < 17) { buffer.append(' '); i = i + 1; }
buffer.append("/* ").append(s).append(" */");
}
buffer.toString()
}
}
//########################################################################
}