You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
compiler_proj2/src/main/scala/project2/Compiler.scala

311 lines
9.0 KiB

package project2
import scala.collection.mutable.HashMap
/*
* This compiler generates Scala-like code that computes the
* result of a given AST.
*/
abstract class StackCompiler extends BugReporter with Codegen {
import Language._
type Loc = Int
// Pretty printing
var nTab = 0
def emitln(msg: String): Unit = emitln(msg, nTab)
/**
* Env of the compiler. Keeps track of the location
* in memory of each variable defined.
*/
class Env {
def undef(name: String) = BUG(s"Undefined identifier $name (should have been found during the semantic analysis)")
def apply(name: String): Loc = undef(name)
}
case class LocationEnv(
vars: Map[String, Loc] = Map.empty,
outer: Env = new Env) extends Env {
/*
* Return a copy of the current state plus a
* variable 'name' at the location 'loc'
*/
def withVal(name: String, loc: Loc): LocationEnv = {
copy(vars = vars + (name -> loc))
}
/*
* Return the location of the variable 'name'
*/
override def apply(name: String): Loc = vars.get(name) match {
case Some(loc) => loc
case _ =>outer(name)
}
}
/*
* Generate code that computes the unary operator
* 'op' on the value at memory location 'sp' and that
* stores the result at 'sp'.
*/
def transUn(op: String)(sp: Loc) = op match {
case "-" => emitln(s"memory($sp) = -memory($sp)")
case "+" => ()
}
/*
* Generate code that computes the binary operator
* 'op' on the values at memory location 'sp' and
* 'sp1' and that stores the result at 'sp'.
*/
def transBin(op: String)(sp: Loc, sp1: Loc) = op match {
case "-" => emitln(s"memory($sp) -= memory($sp1)")
case "+" => emitln(s"memory($sp) += memory($sp1)")
case "*" => emitln(s"memory($sp) *= memory($sp1)")
case "/" => emitln(s"memory($sp) /= memory($sp1)")
case "%" => emitln(s"memory($sp) %= memory($sp1)")
}
/*
* Generate code that computes the binary operator
* 'op' on the values at memory location 'sp' and
* 'sp1' and that stores the result in 'flag'.
*/
def transCond(op: String)(sp: Loc, sp1: Loc) = op match {
case "==" => emitln(s"flag = (memory($sp) == memory($sp1))")
case "!=" => emitln(s"flag = (memory($sp) != memory($sp1))")
case "<=" => emitln(s"flag = (memory($sp) <= memory($sp1))")
case ">=" => emitln(s"flag = (memory($sp) >= memory($sp1))")
case "<" => emitln(s"flag = (memory($sp) < memory($sp1))")
case ">" => emitln(s"flag = (memory($sp) > memory($sp1))")
}
/*
* Generate code that computesS the result of the
* computation represented by the AST 'exp'.
*/
def emitCode(exp: Exp): Unit = {
emitln("type Val = Int")
emitln("val memory = new Array[Val](1000)")
emitln("var flag = true")
trans(exp, 0)(LocationEnv())
emitln(s"memory(0)")
}
/*
* Generate code that computes the result of the
* computation represented by the AST 'exp'. The
* value will be placed at memory location 'sp'
*/
def trans(exp: Exp, sp: Loc)(env: LocationEnv): Unit = exp match {
case Lit(x) =>
emitln(s"memory($sp) = $x")
case Unary(op, v) =>
trans(v, sp)(env)
transUn(op)(sp)
case Prim(op, lop, rop) =>
trans(lop, sp)(env)
trans(rop, sp + 1)(env)
transBin(op)(sp, sp + 1)
case Let(x, a, b) =>
trans(a, sp)(env)
trans(b, sp + 1)(env.withVal(x, sp))
emitln(s"memory($sp) = memory(${sp + 1})")
case Ref(x) =>
emitln(s"memory($sp) = memory(${env(x)})")
case Cond(op, l, r) =>
trans(l, sp)(env)
trans(r, sp+1)(env)
transCond(op)(sp, sp + 1)
case If(cond, tBranch, eBranch) =>
trans(cond, sp)(env) // Set flag value
emitln(s"if (flag) {")
nTab += 1
trans(tBranch, sp)(env)
nTab -= 1
emitln("} else {")
nTab += 1
trans(eBranch, sp)(env)
nTab -= 1
emitln("}")
case VarDec(x, rhs, body) =>
trans(rhs, sp)(env)
trans(body, sp + 1)(env.withVal(x, sp))
emitln(s"memory($sp) = memory(${sp + 1})")
case VarAssign(x, rhs) =>
trans(rhs, sp)(env)
emitln(s"memory(${env(x)}) = memory($sp)")
case While(cond, lBody, body) =>
trans(cond, sp)(env)
emitln(s"while (flag) {")
nTab += 1
trans(lBody, sp)(env)
trans(cond, sp)(env)
nTab -= 1
emitln(s"}")
trans(body, sp)(env)
}
}
abstract class X86Compiler extends BugReporter with Codegen {
import Language._
type Loc = Int
type PhyLoc = String
/**
* Env of the compiler. Keep track of the location
* in memory of each variable defined.
*/
private class Env {
def undef(name: String) = BUG(s"Undefined identifier $name (should have been found during the semantic analysis)")
def apply(name: String): Loc = undef(name)
}
private case class LocationEnv(
vars: Map[String, Loc] = Map.empty,
outer: Env = new Env) extends Env {
/*
* Return a copy of the current state plus a
* variable 'name' at the location 'loc'
*/
def withVal(name: String, loc: Loc): LocationEnv = {
copy(vars = vars + (name -> loc))
}
/*
* Return the location of the variable 'name'
*/
override def apply(name: String): Loc = vars.get(name) match {
case Some(loc) => loc
case _ => outer(name)
}
}
// List of available register.
val regs = Seq("%rbx", "%rcx", "%rdi", "%rsi", "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15")
/*
* Generate code that computes the unary operator
* 'op' on the value at memory location 'sp' and that
* stores the result at 'sp'.
*
* TODO: Fill in transUn with the appropriate code.
*/
def transUn(op: String)(sp: Loc) = op match {
case "-" => emitln(s"neg ${regs(sp)}")
case "+" => ()
case _ => BUG(s"Unary operator $op undefined")
}
/*
* Generate code that computes the binary operator
* 'op' on the values at memory location 'sp' and
* 'sp1' and that stores the result at 'sp'.
*/
def transBin(op: String)(sp: Loc, sp1: Loc) = op match {
case "+" => emitln(s"addq ${regs(sp1)}, ${regs(sp)}")
case "-" => emitln(s"subq ${regs(sp1)}, ${regs(sp)}")
case "*" => emitln(s"imulq ${regs(sp1)}, ${regs(sp)}")
case "/" =>
emitln(s"movq ${regs(sp)}, %rax")
emitln(s"cqto")
emitln(s"idiv ${regs(sp1)}")
emitln(s"movq %rax, ${regs(sp)}")
case _ => BUG(s"Binary operator $op undefined")
}
type Label = String
var nLabel = 0
def freshLabel(pref: String) = { nLabel += 1; s"$pref$nLabel" }
/*
* Generate code that jumps to the label 'label'
* if the CPU flags are verifying the operator
* 'op'
* NOTE: The flags will need to be set before.
*/
def transJumpIf(op: String)(label: Label) = {
op match {
case "==" => emitln(s"je $label")
case "!=" => emitln(s"jne $label")
case "<=" => emitln(s"jle $label")
case ">=" => emitln(s"jge $label")
case "<" => emitln(s"jl $label")
case ">" => emitln(s"jg $label")
}
}
/*
* Generate code that compute the result of the
* computation represented by the AST 'exp'.
*/
def emitCode(exp: Exp): Unit = {
trans(exp, 0)(LocationEnv())
emitln(s"movq ${regs(0)}, %rax")
}
/*
* Generate code that compute the result og the
* computation represented by the AST 'exp'. The
* value will be placed at memory location 'sp'
*
* TODO: Fill in each () with the appropriate code.
*/
def trans(exp: Exp, sp: Loc)(env: LocationEnv): Unit = exp match {
case Lit(x) =>
emitln(s"movq $$$x, ${regs(sp)}")
case Unary(op, v) =>
trans(v, sp)(env)
transUn(op)(sp)
case Prim(op, lop, rop) =>
trans(lop, sp)(env)
trans(rop, sp + 1)(env)
transBin(op)(sp, sp + 1)
case Let(x, a, b) =>
trans(a, sp)(env)
trans(b, sp + 1)(env.withVal(x, sp))
emitln(s"movq ${regs(sp + 1)}, ${regs(sp)}")
case Ref(x) =>
emitln(s"movq ${regs(env.apply(x))}, ${regs(sp)}")
case Cond(op, l, r) =>
trans(l, sp)(env)
trans(r, sp + 1)(env)
emitln(s"cmpq ${regs(sp + 1)}, ${regs(sp)}")
case If(cond, tBranch, eBranch) =>
trans(cond, sp)(env)
val lab = freshLabel("if")
transJumpIf(cond.op)(lab)
trans(eBranch, sp)(env)
emitln(s"jmp ${lab}_skip")
emitln(s"${lab}:", 0)
trans(tBranch, sp)(env)
emitln(s"${lab}_skip:", 0)
case VarDec(x, rhs, body) =>
trans(rhs, sp)(env)
trans(body, sp + 1)(env.withVal(x, sp))
emitln(s"movq ${regs(sp + 1)}, ${regs(sp)}")
case VarAssign(x, rhs) =>
trans(rhs, sp)(env)
emitln(s"movq ${regs(sp)}, ${regs(env.apply(x))}")
case While(cond, lBody, body) =>
val lab = freshLabel("loop")
emitln(s"jmp ${lab}_cond")
emitln(s"${lab}_body:", 0)
trans(lBody, sp)(env)
emitln(s"${lab}_cond:", 0)
trans(cond, sp)(env)
transJumpIf(cond.op)(s"${lab}_body")
trans(body, sp)(env)
// TODO: continue ... Done
}
}