48 | * Since no register allocation is done, the generated instructions may still contain pseudo registers (temps).
49 | *
50 | * @param func TAC function
51 | * @return a pair of the instruction sequence, and the basic info of the function
52 | */
53 | public abstract Pair
20 | * Recall the stack frame of a MIPS subroutine looks this:
21 | *
38 | * The parenthesized slots may not be used, but to make our life easier, we always reserve them.
39 | */
40 | public class MipsSubroutineEmitter extends SubroutineEmitter {
41 |
42 | MipsSubroutineEmitter(MipsAsmEmitter emitter, SubroutineInfo info) {
43 | super(emitter, info);
44 | nextLocalOffset = info.argsSize + 4 * Mips.calleeSaved.length + 4;
45 | printer.printLabel(info.funcLabel, "function " + info.funcLabel.prettyString());
46 | }
47 |
48 | @Override
49 | public void emitComment(String comment) {
50 | buf.add(NativeInstr.nativeComment(String.format("; %s", comment)));
51 | }
52 |
53 | @Override
54 | public void emitStoreToStack(Reg src) {
55 | if (!offsets.containsKey(src.temp)) {
56 | if (src.temp.index < info.numArg) { // Always map arg `i` to `SP + 4 * i`.
57 | offsets.put(src.temp, 4 * src.temp.index);
58 | } else {
59 | offsets.put(src.temp, nextLocalOffset);
60 | nextLocalOffset += 4;
61 | }
62 | }
63 |
64 | buf.add(new Mips.NativeStoreWord(src, Mips.SP, offsets.get(src.temp)));
65 | }
66 |
67 | @Override
68 | public void emitLoadFromStack(Reg dst, Temp src) {
69 | if (!offsets.containsKey(src)) {
70 | if (src.index < info.numArg) { // arg
71 | var offset = 4 * src.index;
72 | offsets.put(src, offset);
73 | buf.add(new Mips.NativeLoadWord(dst, Mips.SP, offset));
74 | return;
75 | }
76 |
77 | throw new IllegalArgumentException("offsets doesn't contain " + src + " when loading " + dst);
78 | }
79 |
80 | buf.add(new Mips.NativeLoadWord(dst, Mips.SP, offsets.get(src)));
81 | }
82 |
83 | @Override
84 | public void emitMove(Reg dst, Reg src) {
85 | buf.add(new Mips.NativeMove(dst, src));
86 | }
87 |
88 | @Override
89 | public void emitNative(NativeInstr instr) {
90 | buf.add(instr);
91 | }
92 |
93 | @Override
94 | public void emitLabel(Label label) {
95 | buf.add(new Mips.MipsLabel(label).toNative(new Reg[]{}, new Reg[]{}));
96 | }
97 |
98 | @Override
99 | public void emitEnd() {
100 | printer.printComment("start of prologue");
101 | printer.printInstr(new Mips.SPAdd(-nextLocalOffset), "push stack frame");
102 | if (Mips.RA.isUsed() || info.hasCalls) {
103 | printer.printInstr(new Mips.NativeStoreWord(Mips.RA, Mips.SP, info.argsSize + 4 * Mips.calleeSaved.length),
104 | "save the return address");
105 | }
106 | for (var i = 0; i < Mips.calleeSaved.length; i++) {
107 | if (Mips.calleeSaved[i].isUsed()) {
108 | printer.printInstr(new Mips.NativeStoreWord(Mips.calleeSaved[i], Mips.SP, info.argsSize + 4 * i),
109 | "save value of $S" + i);
110 | }
111 | }
112 | printer.printComment("end of prologue");
113 | printer.println();
114 |
115 | printer.printComment("start of body");
116 | for (var i = 0; i < Math.min(info.numArg, Mips.argRegs.length); i++) {
117 | printer.printInstr(new Mips.NativeStoreWord(Mips.argRegs[i], Mips.SP, 4 * i),
118 | "save arg " + i);
119 | }
120 | for (var i = Mips.argRegs.length; i < info.numArg; i++) {
121 | printer.printInstr(new Mips.NativeLoadWord(Mips.callerSaved[0], Mips.SP, nextLocalOffset + 4 * i),
122 | "load arg " + i);
123 | printer.printInstr(new Mips.NativeStoreWord(Mips.callerSaved[0], Mips.SP, 4 * i),
124 | "save arg " + i);
125 | }
126 | for (var instr : buf) {
127 | printer.printInstr(instr);
128 | }
129 | printer.printComment("end of body");
130 | printer.println();
131 |
132 | printer.printLabel(new Label(info.funcLabel.name + Mips.EPILOGUE_SUFFIX));
133 | printer.printComment("start of epilogue");
134 | for (var i = 0; i < Mips.calleeSaved.length; i++) {
135 | if (Mips.calleeSaved[i].isUsed()) {
136 | printer.printInstr(new Mips.NativeLoadWord(Mips.calleeSaved[i], Mips.SP, info.argsSize + 4 * i),
137 | "restore value of $S" + i);
138 | }
139 | }
140 | if (Mips.RA.isUsed() || info.hasCalls) {
141 | printer.printInstr(new Mips.NativeLoadWord(Mips.RA, Mips.SP, info.argsSize + 4 * Mips.calleeSaved.length),
142 | "restore the return address");
143 | }
144 | printer.printInstr(new Mips.SPAdd(nextLocalOffset), "pop stack frame");
145 | printer.printComment("end of epilogue");
146 | printer.println();
147 |
148 | printer.printInstr(new Mips.NativeReturn(), "return");
149 | printer.println();
150 | }
151 |
152 | private List, SubroutineInfo> selectInstr(TacFunc func);
54 |
55 | /**
56 | * Call this when all virtual tables are done, and you want to emit code for subroutines.
57 | */
58 | public abstract void emitSubroutineBegin();
59 |
60 | /**
61 | * Begin to emit code for a subroutine.
62 | *
63 | * @param info basic info of this subroutine
64 | * @return emitter of this subroutine
65 | */
66 | public abstract SubroutineEmitter emitSubroutine(SubroutineInfo info);
67 |
68 | /**
69 | * Call this when all subroutines are done, and you want to finish.
70 | *
71 | * @return string representation of the emitted assembly code
72 | */
73 | public abstract String emitEnd();
74 |
75 | /**
76 | * Assembly code pretty printer.
77 | */
78 | protected final AsmCodePrinter printer = new AsmCodePrinter();
79 | }
80 |
--------------------------------------------------------------------------------
/src/main/java/decaf/backend/asm/SubroutineEmitter.java:
--------------------------------------------------------------------------------
1 | package decaf.backend.asm;
2 |
3 | import decaf.lowlevel.AsmCodePrinter;
4 | import decaf.lowlevel.instr.HoleInstr;
5 | import decaf.lowlevel.instr.NativeInstr;
6 | import decaf.lowlevel.instr.Reg;
7 | import decaf.lowlevel.instr.Temp;
8 | import decaf.lowlevel.label.Label;
9 |
10 | /**
11 | * Emit assembly code for a subroutine.
12 | */
13 | public abstract class SubroutineEmitter {
14 |
15 | protected SubroutineEmitter(AsmEmitter emitter, SubroutineInfo info) {
16 | this.info = info;
17 | this.printer = emitter.printer;
18 | }
19 |
20 | /**
21 | * Append an assembly comment.
22 | *
23 | * @param comment does not include the leading comment indicator
24 | */
25 | public abstract void emitComment(String comment);
26 |
27 | /**
28 | * Append an assembly instruction that stores the value of a register to stack.
29 | *
30 | * @param src source register
31 | */
32 | public abstract void emitStoreToStack(Reg src);
33 |
34 | /**
35 | * Append an assembly instruction that loads a value from stack to a register.
36 | *
37 | * @param dst destination register
38 | * @param src source temp
39 | */
40 | public abstract void emitLoadFromStack(Reg dst, Temp src);
41 |
42 | /**
43 | * Append an assembly instruction that copies value between two registers.
44 | *
45 | * @param dst destination register
46 | * @param src source register
47 | */
48 | public abstract void emitMove(Reg dst, Reg src);
49 |
50 | /**
51 | * Append a given assembly instruction.
52 | *
53 | * @param instr assembly instruction
54 | */
55 | public abstract void emitNative(NativeInstr instr);
56 |
57 | /**
58 | * Append a label.
59 | *
60 | * @param label label
61 | */
62 | public abstract void emitLabel(Label label);
63 |
64 | /**
65 | * Call this when you have appended all user and synthetic (by register allocation algorithm) instructions of
66 | * this subroutine.
67 | */
68 | public abstract void emitEnd();
69 |
70 | /**
71 | * Basic info of this subroutine.
72 | */
73 | protected SubroutineInfo info;
74 |
75 | /**
76 | * Assembly code pretty printer.
77 | */
78 | protected AsmCodePrinter printer;
79 |
80 | /**
81 | * Expand hole instructions with necessary native instructions.
82 | *
83 | * Note that hole instruction expansion is done after register allocation.
84 | */
85 | public void emitHoleInstr(HoleInstr instr, Reg[] srcRegs, Reg[] dstRegs) {}
86 | }
--------------------------------------------------------------------------------
/src/main/java/decaf/backend/asm/SubroutineInfo.java:
--------------------------------------------------------------------------------
1 | package decaf.backend.asm;
2 |
3 | import decaf.lowlevel.label.FuncLabel;
4 |
5 | /**
6 | * Basic info of subroutine.
7 | */
8 | public class SubroutineInfo {
9 | /**
10 | * Label of the function entry.
11 | */
12 | public final FuncLabel funcLabel;
13 |
14 | /**
15 | * Number of arguments.
16 | */
17 | public final int numArg;
18 |
19 | /**
20 | * Does this subroutine call others?
21 | */
22 | public final boolean hasCalls;
23 |
24 | /**
25 | * Max. stack size needed to store arguments.
26 | */
27 | public final int argsSize;
28 |
29 | public SubroutineInfo(FuncLabel funcLabel, int numArg, boolean hasCalls, int argsSize) {
30 | this.funcLabel = funcLabel;
31 | this.numArg = numArg;
32 | this.hasCalls = hasCalls;
33 | this.argsSize = argsSize;
34 | }
35 | }
--------------------------------------------------------------------------------
/src/main/java/decaf/backend/asm/mips/MipsSubroutineEmitter.java:
--------------------------------------------------------------------------------
1 | package decaf.backend.asm.mips;
2 |
3 | import decaf.backend.asm.SubroutineEmitter;
4 | import decaf.backend.asm.SubroutineInfo;
5 | import decaf.lowlevel.Mips;
6 | import decaf.lowlevel.instr.HoleInstr;
7 | import decaf.lowlevel.instr.NativeInstr;
8 | import decaf.lowlevel.instr.Reg;
9 | import decaf.lowlevel.instr.Temp;
10 | import decaf.lowlevel.label.Label;
11 |
12 | import java.util.ArrayList;
13 | import java.util.List;
14 | import java.util.Map;
15 | import java.util.TreeMap;
16 |
17 | /**
18 | * Emit MIPS assembly code for a subroutine.
19 | *
22 | * previous stack frame ...
23 | * SP + 4n + 40 + : local data m - 1
24 | * 4(m - 1)
25 | * ...
26 | * SP + 4n + 40 : local data 0
27 | * SP + 4n + 36 : ($RA)
28 | * SP + 4n + 32 : ($S8)
29 | * ...
30 | * SP + 4n + 0 : ($S0)
31 | * SP + 4(n - 1) : arg n - 1
32 | * ...
33 | * SP + 16 : arg 4
34 | * ...
35 | * SP : (arg 0)
36 | *
37 | *
18 | *
23 | */
24 | public enum Kind {
25 | CONTINUOUS, END_BY_JUMP, END_BY_COND_JUMP, END_BY_RETURN
26 | }
27 |
28 | public final Kind kind;
29 |
30 | /**
31 | * Block id.
32 | */
33 | public final int id;
34 |
35 | /**
36 | * Entry label of this block, if any.
37 | */
38 | public final Optional