Skip to content

Commit fbd2061

Browse files
committed
impl and test backend
1 parent 9e78b4e commit fbd2061

24 files changed

+1298
-20
lines changed

src/main/java/decaf/lowlevel/log/Log.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ public static void ifLoggable(Level level, Consumer<IndentPrinter> action) {
141141
if (L.isLoggable(level)) {
142142
var printer = new IndentPrinter(outs, true);
143143
action.accept(printer);
144+
printer.flush();
144145
}
145146
}
146147
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package decaf.backend.asm
2+
3+
import java.io.PrintWriter
4+
import java.util.logging.Level
5+
6+
import decaf.backend.dataflow.{CFG, LivenessAnalyzer}
7+
import decaf.backend.reg.RegAlloc
8+
import decaf.driver.{Config, Phase}
9+
import decaf.lowlevel.instr.PseudoInstr
10+
import decaf.lowlevel.log.Log
11+
import decaf.lowlevel.tac.TacProg
12+
import decaf.printing.PrettyCFG
13+
import decaf.util.Conversions._
14+
15+
/**
16+
* The assembly code generation phase: translate a TAC program to assembly code.
17+
*
18+
* @param emitter helper assembly code emitter
19+
* @param regAlloc register allocator
20+
*/
21+
class Asm(val emitter: AsmEmitter, val regAlloc: RegAlloc) extends Phase[TacProg, String]("asm: " + emitter) {
22+
23+
override def transform(prog: TacProg): String = {
24+
Log.info("phase: asm")
25+
val analyzer = new LivenessAnalyzer[PseudoInstr]
26+
27+
for (vtbl <- prog.vtables) {
28+
Log.info("emit vtable for %s", vtbl.className)
29+
emitter.emitVTable(vtbl)
30+
}
31+
32+
emitter.emitSubroutineBegin()
33+
for (func <- prog.funcs) {
34+
Log.info("emit func for %s", func.entry.prettyString)
35+
val (instrSeq, info) = emitter.selectInstr(func)
36+
val cfg = CFG.buildFrom(instrSeq)
37+
analyzer(cfg)
38+
Log.ifLoggable(Level.FINE, printer => new PrettyCFG[PseudoInstr](printer).pretty(cfg))
39+
regAlloc(cfg, info)
40+
}
41+
42+
emitter.emitEnd()
43+
}
44+
45+
override def post(code: String)(implicit config: Config): Unit = {
46+
if (config.target.equals(Config.Target.PA5)) {
47+
val path = config.dstDir / config.sourceBaseName + ".s"
48+
val printer = new PrintWriter(path)
49+
printer.print(code)
50+
printer.close()
51+
}
52+
}
53+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package decaf.backend.asm
2+
3+
import decaf.lowlevel.AsmCodePrinter
4+
import decaf.lowlevel.instr.{PseudoInstr, Reg}
5+
import decaf.lowlevel.tac.{TacFunc, VTable}
6+
7+
8+
/**
9+
* Emit assembly code.
10+
*
11+
* @param platformName target platform name
12+
* @param allocatableRegs platform-specific registers accounted for register allocation, i.e. general registers
13+
* @param callerSaveRegs platform-specific registers that need be saved by caller.
14+
*/
15+
abstract class AsmEmitter(val platformName: String, val allocatableRegs: Array[Reg], val callerSaveRegs: Array[Reg]) {
16+
/**
17+
* Emit assembly code for a virtual table.
18+
*
19+
* @param vtbl virtual table
20+
*/
21+
def emitVTable(vtbl: VTable): Unit
22+
23+
/**
24+
* Instruction selection for a TAC function.
25+
* <p>
26+
* Since no register allocation is done, the generated instructions may still contain pseudo registers (temps).
27+
*
28+
* @param func TAC function
29+
* @return a pair of the instruction sequence, and the basic info of the function
30+
*/
31+
def selectInstr(func: TacFunc): (List[PseudoInstr], SubroutineInfo)
32+
33+
/**
34+
* Call this when all virtual tables are done, and you want to emit code for subroutines.
35+
*/
36+
def emitSubroutineBegin(): Unit
37+
38+
/**
39+
* Begin to emit code for a subroutine.
40+
*
41+
* @param info basic info of this subroutine
42+
* @return emitter of this subroutine
43+
*/
44+
def emitSubroutine(info: SubroutineInfo): SubroutineEmitter
45+
46+
/**
47+
* Call this when all subroutines are done, and you want to finish.
48+
*
49+
* @return string representation of the emitted assembly code
50+
*/
51+
def emitEnd(): String
52+
53+
override def toString: String = platformName
54+
55+
/**
56+
* Assembly code pretty printer.
57+
*/
58+
final protected[asm] val printer = new AsmCodePrinter
59+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package decaf.backend.asm
2+
3+
import decaf.lowlevel.instr.{PseudoInstr, Temp}
4+
5+
/**
6+
* A trick to encode some holes as normal pseudo instructions.
7+
*
8+
* @see [[decaf.backend.asm.Holes.CallerSave]]
9+
* @see [[decaf.backend.asm.Holes.CallerRestore]]
10+
*/
11+
object Holes {
12+
13+
object CallerSave extends PseudoInstr(new Array[Temp](0), new Array[Temp](0)) {
14+
override def toString: String = "# TODO: caller save"
15+
}
16+
17+
object CallerRestore extends PseudoInstr(new Array[Temp](0), new Array[Temp](0)) {
18+
override def toString: String = "# TODO: caller restore"
19+
}
20+
21+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package decaf.backend.asm
2+
3+
import decaf.lowlevel.AsmCodePrinter
4+
import decaf.lowlevel.instr.{NativeInstr, Reg, Temp}
5+
import decaf.lowlevel.label.Label
6+
7+
/**
8+
* Emit assembly code for a subroutine.
9+
*
10+
* @param emitter attached [[AsmEmitter]]
11+
* @param info basic info of this subroutine
12+
*/
13+
abstract class SubroutineEmitter protected(val emitter: AsmEmitter, var info: SubroutineInfo) {
14+
/**
15+
* Append an assembly instruction that stores the value of a register to stack.
16+
*
17+
* @param src source register
18+
* @param temp the bound temp of `src`
19+
*/
20+
def emitStoreToStack(src: Reg, temp: Temp): Unit
21+
22+
/**
23+
* Append an assembly instruction that loads a value from stack to a register.
24+
*
25+
* @param dst destination register
26+
* @param src source temp
27+
*/
28+
def emitLoadFromStack(dst: Reg, src: Temp): Unit
29+
30+
/**
31+
* Append an assembly instruction that copies value between two registers.
32+
*
33+
* @param dst destination register
34+
* @param src source register
35+
*/
36+
def emitMove(dst: Reg, src: Reg): Unit
37+
38+
/**
39+
* Append a given assembly instruction.
40+
*
41+
* @param instr assembly instruction
42+
*/
43+
def emitNative(instr: NativeInstr): Unit
44+
45+
/**
46+
* Append a label.
47+
*
48+
* @param label label
49+
*/
50+
def emitLabel(label: Label): Unit
51+
52+
/**
53+
* Call this when you have appended all user and synthetic (by register allocation algorithm) instructions of
54+
* this subroutine.
55+
*
56+
* @param used used registers during register allocation
57+
*/
58+
def emitEnd(used: Set[Reg]): Unit
59+
60+
/**
61+
* Assembly code pretty printer.
62+
*/
63+
protected var printer: AsmCodePrinter = emitter.printer
64+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package decaf.backend.asm
2+
3+
import decaf.lowlevel.label.FuncLabel
4+
5+
/**
6+
* Basic info of subroutine.
7+
*
8+
* @param funcLabel label of the function entry
9+
* @param numArgs number of arguments
10+
* @param hasCalls does this subroutine call others?
11+
* @param argsSize max. stack size needed to store arguments
12+
*/
13+
case class SubroutineInfo(funcLabel: FuncLabel, numArgs: Int, hasCalls: Boolean, argsSize: Int)

0 commit comments

Comments
 (0)