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.
ChocoPy/src/main/java/chocopy/common/codegen/RiscVBackend.java

607 lines
22 KiB

package chocopy.common.codegen;
import java.io.PrintWriter;
import java.io.StringWriter;
/** RISC V assembly-language generation utilities. */
public class RiscVBackend {
/** Accumulator for assembly code output. */
protected final StringWriter asmText = new StringWriter();
/** Allows print, println, and printf of assembly code. */
private final PrintWriter out = new PrintWriter(asmText);
/** The word size in bytes for RISC-V 32-bit. */
protected static final int WORD_SIZE = 4;
/** The RISC-V registers. */
public enum Register {
A0("a0"),
A1("a1"),
A2("a2"),
A3("a3"),
A4("a4"),
A5("a5"),
A6("a6"),
A7("a7"),
T0("t0"),
T1("t1"),
T2("t2"),
T3("t3"),
T4("t4"),
T5("t5"),
T6("t6"),
S1("s1"),
S2("s2"),
S3("s3"),
S4("s4"),
S5("s5"),
S6("s6"),
S7("s7"),
S8("s8"),
S9("s9"),
S10("s10"),
S11("s11"),
FP("fp"),
SP("sp"),
GP("gp"),
RA("ra"),
ZERO("zero");
/** The name of the register used in assembly. */
protected final String name;
/** This register's code representation is NAME. */
Register(String name) {
this.name = name;
}
@Override
public String toString() {
return this.name;
}
}
@Override
public String toString() {
return asmText.toString();
}
/**
* Define @NAME to have the value VALUE. Here, NAME is assumed to be an identifier consisting of
* letters, digits, underscores, and any of the characters '$' or '.', and that does not start
* with a digit. Value may be a numeral or another symbol.
*/
public void defineSym(String name, String value) {
if (name.startsWith("@")) {
emitInsn(String.format(".equiv %s, %s", name, value), null);
} else {
emitInsn(String.format(".equiv @%s, %s", name, value), null);
}
}
/**
* Define @NAME to have the value VALUE, where value is converted to a string. See {@link
* #defineSym(java.lang.String, java.lang.String)}.
*/
public void defineSym(String name, int value) {
defineSym(name, Integer.toString(value));
}
/**
* Returns the word size in bytes.
*
* <p>This method is used instead of directly accessing the static field {@link #WORD_SIZE}, so
* that this class may be extended with alternate word sizes.
*/
public int getWordSize() {
return WORD_SIZE;
}
/** Emit the text STR to the output stream verbatim. STR should have no trailing newline. */
protected void emit(String str) {
out.println(str);
}
/** Emit instruction or directive INSN along with COMMENT as a one-line comment, if non-null. */
public void emitInsn(String insn, String comment) {
if (comment != null) {
emit(String.format(" %-40s # %s", insn, comment));
} else {
emitInsn(insn);
}
}
/** Emit instruction or directive INSN without a comment. */
protected void emitInsn(String insn) {
emit(String.format(" %s", insn));
}
/**
* Emit a local label marker for LABEL with one-line comment COMMENT (null if missing). Invoke
* only once per unique label.
*/
public void emitLocalLabel(Label label, String comment) {
if (comment != null) {
emit(String.format("%-42s # %s", label + ":", comment));
} else {
emit(String.format("%s:", label + ":"));
}
}
/** Emit a global label marker for LABEL. Invoke only once per unique label. */
public void emitGlobalLabel(Label label) {
emit(String.format("\n.globl %s", label));
emit(String.format("%s:", label));
}
/**
* Emit a data word containing VALUE as an integer value. COMMENT is a emitted as a one-line
* comment, if non-null.
*/
public void emitWordLiteral(Integer value, String comment) {
emitInsn(String.format(".word %s", value), comment);
}
/**
* Emit a data word containing the address ADDR, or 0 if LABEL is null. COMMENT is a emitted as
* a one-line comment, if non-null.
*/
public void emitWordAddress(Label addr, String comment) {
if (addr == null) {
emitWordLiteral(0, comment);
} else {
emitInsn(String.format(".word %s", addr), comment);
}
}
/**
* Emit VALUE as an ASCII null-terminated string constant, with COMMENT as its one-line comment,
* if non-null.
*/
public void emitString(String value, String comment) {
String quoted =
value.replace("\\", "\\\\")
.replace("\n", "\\n")
.replace("\t", "\\t")
.replace("\"", "\\\"");
emitInsn(String.format(".string \"%s\"", quoted), comment);
}
/** Mark the start of a data section. */
public void startData() {
emit("\n.data");
}
/** Mark the start of a code/text section. */
public void startCode() {
emit("\n.text");
}
/** Align the next instruction/word in memory to a multiple of 2**POW bytes. */
public void alignNext(int pow) {
emitInsn(String.format(".align %d", pow));
}
/** Emit an ecall instruction, with one-line comment COMMENT, if non-null. */
public void emitEcall(String comment) {
emitInsn("ecall", comment);
}
/**
* Emit a load-address instruction with destination RD and source LABEL. COMMENT is an optional
* one-line comment (null if missing).
*/
public void emitLA(Register rd, Label label, String comment) {
emitInsn(String.format("la %s, %s", rd, label), comment);
}
/**
* Emit a load-immediate pseudo-op to set RD to IMM. COMMENT is an optional one-line comment
* (null if missing).
*/
public void emitLI(Register rd, Integer imm, String comment) {
emitInsn(String.format("li %s, %d", rd, imm), comment);
}
/**
* Emit a load-upper-immediate instruction to set the upper 20 bits of RD to IMM, where 0 <= IMM
* < 2**20. COMMENT is an optional one-line comment (null if missing).
*/
public void emitLUI(Register rd, Integer imm, String comment) {
emitInsn(String.format("lui %s, %d", rd, imm), comment);
}
/**
* Emit a move instruction to set RD to the contents of RS. COMMENT is an optional one-line
* comment (null if missing).
*/
public void emitMV(Register rd, Register rs, String comment) {
emitInsn(String.format("mv %s, %s", rd, rs), comment);
}
/**
* Emit a jump-register (computed jump) instruction to the address in RS. COMMENT is an optional
* one-line comment (null if missing).
*/
public void emitJR(Register rs, String comment) {
emitInsn(String.format("jr %s", rs), comment);
}
/**
* Emit a jump (unconditional jump) instruction to LABEL. COMMENT is an optional one-line
* comment (null if missing).
*/
public void emitJ(Label label, String comment) {
emitInsn(String.format("j %s", label), comment);
}
/**
* Emit a jump-and-link instruction to LABEL. COMMENT is an optional one-line comment (null if
* missing).
*/
public void emitJAL(Label label, String comment) {
emitInsn(String.format("jal %s", label), comment);
}
/**
* Emit a computed-jump-and-link instruction to the address in RS. COMMENT is an optional
* one-line comment (null if missing).
*/
public void emitJALR(Register rs, String comment) {
emitInsn(String.format("jalr %s", rs), comment);
}
/**
* Emit an add-immediate instruction performing RD = RS + IMM. Requires -2048 <= IMM < 2048.
* COMMENT is an optional one-line comment (null if missing).
*/
public void emitADDI(Register rd, Register rs, Integer imm, String comment) {
emitInsn(String.format("addi %s, %s, %d", rd, rs, imm), comment);
}
/**
* Emit an add-immediate instruction performing RD = RS + IMM. Here, IMM is a string generally
* containing a symbolic assembler constant (see defineSym) representing an integer value, or an
* expression of the form @NAME+NUM or @NAME-NUM. COMMENT is an optional one-line comment (null
* if missing).
*/
public void emitADDI(Register rd, Register rs, String imm, String comment) {
emitInsn(String.format("addi %s, %s, %s", rd, rs, imm), comment);
}
/**
* Emit an add instruction performing RD = RS1 + RS2 mod 2**32. COMMENT is an optional one-line
* comment (null if missing).
*/
public void emitADD(Register rd, Register rs1, Register rs2, String comment) {
emitInsn(String.format("add %s, %s, %s", rd, rs1, rs2), comment);
}
/**
* Emit a subtract instruction performing RD = RS1 - RS2 mod 2**32. COMMENT is an optional
* one-line comment (null if missing).
*/
public void emitSUB(Register rd, Register rs1, Register rs2, String comment) {
emitInsn(String.format("sub %s, %s, %s", rd, rs1, rs2), comment);
}
/**
* Emit a multiply instruction performing RD = RS1 * RS2 mod 2**32. COMMENT is an optional
* one-line comment (null if missing).
*/
public void emitMUL(Register rd, Register rs1, Register rs2, String comment) {
emitInsn(String.format("mul %s, %s, %s", rd, rs1, rs2), comment);
}
/**
* Emit a signed integer divide instruction performing RD = RS1 / RS2 mod 2**32, rounding the
* result toward 0. If RS2 == 0, sets RD to -1. If RS1 == -2**31 and RS2 == -1, sets RD to
* -2**31. COMMENT is an optional one-line comment (null if missing).
*/
public void emitDIV(Register rd, Register rs1, Register rs2, String comment) {
emitInsn(String.format("div %s, %s, %s", rd, rs1, rs2), comment);
}
/**
* Emit a remainder instruction: RD = RS1 rem RS2 defined so that (RS1 / RS2) * RS2 + (RS1 rem
* RS2) == RS1, where / is as for emitDIV. COMMENT is an optional one-line comment (null if
* missing).
*/
public void emitREM(Register rd, Register rs1, Register rs2, String comment) {
emitInsn(String.format("rem %s, %s, %s", rd, rs1, rs2), comment);
}
/**
* Emit an xor instruction: RD = RS1 ^ RS2. COMMENT is an optional one-line comment (null if
* missing).
*/
public void emitXOR(Register rd, Register rs1, Register rs2, String comment) {
emitInsn(String.format("xor %s, %s, %s", rd, rs1, rs2), comment);
}
/**
* Emit an xor-immediate instruction: RD = RS ^ IMM, where -2048 <= IMM < 2048. COMMENT is an
* optional one-line comment (null if missing).
*/
public void emitXORI(Register rd, Register rs, Integer imm, String comment) {
emitInsn(String.format("xori %s, %s, %d", rd, rs, imm), comment);
}
/**
* Emit a bitwise and instruction: RD = RS1 & RS2. COMMENT is an optional one-line comment (null
* if missing).
*/
public void emitAND(Register rd, Register rs1, Register rs2, String comment) {
emitInsn(String.format("and %s, %s, %s", rd, rs1, rs2), comment);
}
/**
* Emit a bitwise and-immediate instruction: RD = RS & IMM, where -2048 <= IMM < 2048. COMMENT
* is an optional one-line comment (null if missing).
*/
public void emitANDI(Register rd, Register rs, Integer imm, String comment) {
emitInsn(String.format("andi %s, %s, %d", rd, rs, imm), comment);
}
/**
* Emit a bitwise or instruction: RD = RS1 | RS2. COMMENT is an optional one-line comment (null
* if missing).
*/
public void emitOR(Register rd, Register rs1, Register rs2, String comment) {
emitInsn(String.format("or %s, %s, %s", rd, rs1, rs2), comment);
}
/**
* Emit a bitwise or-immediate instruction: RD = RS | IMM, where -2048 <= IMM < 2048. COMMENT is
* an optional one-line comment (null if missing).
*/
public void emitORI(Register rd, Register rs, Integer imm, String comment) {
emitInsn(String.format("ori %s, %s, %d", rd, rs, imm), comment);
}
/**
* Emit a logical left shift instruction: RD = RS1 << (RS2 & 0x31). COMMENT is an optional
* one-line comment (null if missing).
*/
public void emitSLL(Register rd, Register rs1, Register rs2, String comment) {
emitInsn(String.format("sll %s, %s, %s", rd, rs1, rs2), comment);
}
/**
* Emit a logical left shift instruction: RD = RS << (IMM & 0x31). COMMENT is an optional
* one-line comment (null if missing).
*/
public void emitSLLI(Register rd, Register rs, Integer imm, String comment) {
emitInsn(String.format("slli %s, %s, %d", rd, rs, imm), comment);
}
/**
* Emit a logical right shift instruction: RD = RS1 >>> (RS2 & 0x31). COMMENT is an optional
* one-line comment (null if missing).
*/
public void emitSRL(Register rd, Register rs1, Register rs2, String comment) {
emitInsn(String.format("srl %s, %s, %s", rd, rs1, rs2), comment);
}
/**
* Emit a logical right shift instruction: RD = RS >>> (IMM & 0x31). COMMENT is an optional
* one-line comment (null if missing).
*/
public void emitSRLI(Register rd, Register rs, Integer imm, String comment) {
emitInsn(String.format("srli %s, %s, %d", rd, rs, imm), comment);
}
/**
* Emit an arithmetic right shift instruction: RD = RS1 >> (RS2 & 0x31). COMMENT is an optional
* one-line comment (null if missing).
*/
public void emitSRA(Register rd, Register rs1, Register rs2, String comment) {
emitInsn(String.format("sra %s, %s, %s", rd, rs1, rs2), comment);
}
/**
* Emit an arithmetic right shift instruction: RD = RS >> (IMM & 0x31). COMMENT is an optional
* one-line comment (null if missing).
*/
public void emitSRAI(Register rd, Register rs, Integer imm, String comment) {
emitInsn(String.format("srai %s, %s, %d", rd, rs, imm), comment);
}
/**
* Emit a load-word instruction: RD = MEMORY[RS + IMM]:4, where -2048 <= IMM < 2048. COMMENT is
* an optional one-line comment (null if missing).
*/
public void emitLW(Register rd, Register rs, Integer imm, String comment) {
emitInsn(String.format("lw %s, %d(%s)", rd, imm, rs), comment);
}
/**
* Emit a load-word instruction: RD = MEMORY[RS + IMM]:4, where -2048 <= IMM < 2048. Here, IMM
* is symbolic constant expression (see emitADDI). COMMENT is an optional one-line comment (null
* if missing).
*/
public void emitLW(Register rd, Register rs, String imm, String comment) {
emitInsn(String.format("lw %s, %s(%s)", rd, imm, rs), comment);
}
/**
* Emit a store-word instruction: MEMORY[RS1 + IMM]:4 = RS2, where -2048 <= IMM < 2048. COMMENT
* is an optional one-line comment (null if missing).
*/
public void emitSW(Register rs2, Register rs1, Integer imm, String comment) {
emitInsn(String.format("sw %s, %d(%s)", rs2, imm, rs1), comment);
}
/**
* Emit a store-word instruction: MEMORY[RS1 + IMM]:4 = RS2, where -2048 <= IMM < 2048. Here,
* IMM is symbolic constant expression (see emitADDI). COMMENT is an optional one-line comment
* (null if missing).
*/
public void emitSW(Register rs2, Register rs1, String imm, String comment) {
emitInsn(String.format("sw %s, %s(%s)", rs2, imm, rs1), comment);
}
/**
* Emit a load-word instruction for globals: RD = MEMORY[LABEL]:4. COMMENT is an optional
* one-line comment (null if missing).
*/
public void emitLW(Register rd, Label label, String comment) {
emitInsn(String.format("lw %s, %s", rd, label), comment);
}
/**
* Emit a store-word instruction for globals: MEMORY[LABEL]:4 = RS, using TMP as a temporary
* register. COMMENT is an optional one-line comment (null if missing).
*/
public void emitSW(Register rs, Label label, Register tmp, String comment) {
emitInsn(String.format("sw %s, %s, %s", rs, label, tmp), comment);
}
/**
* Emit a load-byte instruction: RD = MEMORY[RS + IMM]:1, where -2048 <= IMM < 2048. Sign
* extends the byte loaded. COMMENT is an optional one-line comment (null if missing).
*/
public void emitLB(Register rd, Register rs, Integer imm, String comment) {
emitInsn(String.format("lb %s, %d(%s)", rd, imm, rs), comment);
}
/**
* Emit a load-byte-unsigned instruction: RD = MEMORY[RS + IMM]:1, where -2048 <= IMM < 2048.
* Zero-extends the byte loaded. COMMENT is an optional one-line comment (null if missing).
*/
public void emitLBU(Register rd, Register rs, Integer imm, String comment) {
emitInsn(String.format("lbu %s, %d(%s)", rd, imm, rs), comment);
}
/**
* Emit a store-byte instruction: MEMORY[RS1 + IMM]:1 = RS2, where -2048 <= IMM < 2048. Assigns
* the low-order byte of RS2 to memory. COMMENT is an optional one-line comment (null if
* missing).
*/
public void emitSB(Register rs2, Register rs1, Integer imm, String comment) {
emitInsn(String.format("sb %s, %d(%s)", rs2, imm, rs1), comment);
}
/**
* Emit a branch-if-equal instruction: if RS1 == RS2 goto LABEL. COMMENT is an optional one-line
* comment (null if missing).
*/
public void emitBEQ(Register rs1, Register rs2, Label label, String comment) {
emitInsn(String.format("beq %s, %s, %s", rs1, rs2, label), comment);
}
/**
* Emit a branch-if-unequal instruction: if RS1 != RS2 goto LABEL. COMMENT is an optional
* one-line comment (null if missing).
*/
public void emitBNE(Register rs1, Register rs2, Label label, String comment) {
emitInsn(String.format("bne %s, %s, %s", rs1, rs2, label), comment);
}
/**
* Emit a branch-if-greater-or-equal (signed) instruction: if RS1 >= RS2 goto LABEL. COMMENT is
* an optional one-line comment (null if missing).
*/
public void emitBGE(Register rs1, Register rs2, Label label, String comment) {
emitInsn(String.format("bge %s, %s, %s", rs1, rs2, label), comment);
}
/**
* Emit a branch-if-greater-or-equal (unsigned) instruction: if RS1 >= RS2 goto LABEL. COMMENT
* is an optional one-line comment (null if missing).
*/
public void emitBGEU(Register rs1, Register rs2, Label label, String comment) {
emitInsn(String.format("bgeu %s, %s, %s", rs1, rs2, label), comment);
}
/**
* Emit a branch-if-less-than (signed) instruction: if RS1 < RS2 goto LABEL. COMMENT is an
* optional one-line comment (null if missing).
*/
public void emitBLT(Register rs1, Register rs2, Label label, String comment) {
emitInsn(String.format("blt %s, %s, %s", rs1, rs2, label), comment);
}
/**
* Emit a branch-if-less-than (unsigned) instruction: if RS1 < RS2 goto LABEL. COMMENT is an
* optional one-line comment (null if missing).
*/
public void emitBLTU(Register rs1, Register rs2, Label label, String comment) {
emitInsn(String.format("bltu %s, %s, %s", rs1, rs2, label), comment);
}
/**
* Emit a branch-if-zero instruction: if RS == 0 goto LABEL. COMMENT is an optional one-line
* comment (null if missing).
*/
public void emitBEQZ(Register rs, Label label, String comment) {
emitInsn(String.format("beqz %s, %s", rs, label), comment);
}
/**
* Emit a branch-if-not-zero instruction: if RS != 0 goto LABEL. COMMENT is an optional one-line
* comment (null if missing).
*/
public void emitBNEZ(Register rs, Label label, String comment) {
emitInsn(String.format("bnez %s, %s", rs, label), comment);
}
/**
* Emit a branch-if-less-than-zero instruction: if RS < 0 goto LABEL. COMMENT is an optional
* one-line comment (null if missing).
*/
public void emitBLTZ(Register rs, Label label, String comment) {
emitInsn(String.format("bltz %s, %s", rs, label), comment);
}
/**
* Emit a branch-if-greater-than-zero instruction: if RS > 0 goto LABEL. COMMENT is an optional
* one-line comment (null if missing).
*/
public void emitBGTZ(Register rs, Label label, String comment) {
emitInsn(String.format("bgtz %s, %s", rs, label), comment);
}
/**
* Emit a branch-if-less-than-equal-to-zero instruction: if RS <= 0 goto LABEL. COMMENT is an
* optional one-line comment (null if missing).
*/
public void emitBLEZ(Register rs, Label label, String comment) {
emitInsn(String.format("blez %s, %s", rs, label), comment);
}
/**
* Emit a branch-if-greater-than-equal-to-zero instruction: if RS >= 0 goto LABEL. COMMENT is an
* optional one-line comment (null if missing).
*/
public void emitBGEZ(Register rs, Label label, String comment) {
emitInsn(String.format("bgez %s, %s", rs, label), comment);
}
/**
* Emit a set-less-than instruction: RD = 1 if RS1 < RS2 else 0. COMMENT is an optional one-line
* comment (null if missing).
*/
public void emitSLT(Register rd, Register rs1, Register rs2, String comment) {
emitInsn(String.format("slt %s, %s, %s", rd, rs1, rs2), comment);
}
/**
* Emit a set-if-zero instruction: RD = 1 if RS == 0 else 0. COMMENT is an optional one-line
* comment (null if missing).
*/
public void emitSEQZ(Register rd, Register rs, String comment) {
emitInsn(String.format("seqz %s, %s", rd, rs), comment);
}
/**
* Emit a set-if-not-zero instruction: RD = 1 if RS != 0 else 0. COMMENT is an optional one-line
* comment (null if missing).
*/
public void emitSNEZ(Register rd, Register rs, String comment) {
emitInsn(String.format("snez %s, %s", rd, rs), comment);
}
}