compiler/
lib/
examples/
std/
arch/
rv64/
asm/
image/
shared/
asm.rad
24.8 KiB
atomicTests.rad
5.0 KiB
atomics.rad
3.6 KiB
bounds.rad
21.0 KiB
decode.rad
15.2 KiB
emit.rad
33.2 KiB
encode.rad
22.0 KiB
image.rad
4.5 KiB
isel.rad
52.1 KiB
printer.rad
13.9 KiB
shared.rad
16.8 KiB
tests.rad
17.2 KiB
rv64.rad
17.5 KiB
char/
collections/
lang/
sys/
arch.rad
68 B
char.rad
855 B
collections.rad
39 B
fmt.rad
8.3 KiB
intrinsics.rad
467 B
io.rad
1.7 KiB
lang.rad
276 B
mem.rad
2.3 KiB
sys.rad
179 B
testing.rad
2.4 KiB
tests.rad
15.7 KiB
vec.rad
3.2 KiB
std.rad
281 B
scripts/
seed/
sublime/
test/
vim/
.gitignore
336 B
.gitsigners
112 B
CONTRIBUTING
2.1 KiB
LICENSE
1.1 KiB
Makefile
5.4 KiB
README
2.5 KiB
STYLE
2.5 KiB
std.lib
1.5 KiB
std.lib.test
662 B
lib/std/arch/rv64/printer.rad
raw
| 1 | //! RV64 instruction printer. |
| 2 | //! |
| 3 | //! Prints 32-bit instructions in assembly text format. |
| 4 | |
| 5 | use std::fmt; |
| 6 | use std::lang::gen; |
| 7 | use std::lang::sexpr; |
| 8 | use std::lang::gen::types; |
| 9 | |
| 10 | use super::decode; |
| 11 | use super::atomics; |
| 12 | use super::emit; |
| 13 | |
| 14 | ///////////////////// |
| 15 | // Register Names // |
| 16 | ///////////////////// |
| 17 | |
| 18 | /// ABI register names. |
| 19 | constant REG_NAMES: [*[u8]; 32] = [ |
| 20 | "%zero", "%ra", "%sp", "%gp", "%tp", "%t0", "%t1", "%t2", |
| 21 | "%fp", "%s1", "%a0", "%a1", "%a2", "%a3", "%a4", "%a5", |
| 22 | "%a6", "%a7", "%s2", "%s3", "%s4", "%s5", "%s6", "%s7", |
| 23 | "%s8", "%s9", "%s10", "%s11", "%t3", "%t4", "%t5", "%t6" |
| 24 | ]; |
| 25 | |
| 26 | /// Get register name from number. |
| 27 | fn regName(n: u8) -> *[u8] { |
| 28 | return "?" if n >= 32 else REG_NAMES[n as u32]; |
| 29 | } |
| 30 | |
| 31 | /// Get register name from Reg. |
| 32 | fn regNameR(r: gen::Reg) -> *[u8] { |
| 33 | return regName(*r); |
| 34 | } |
| 35 | |
| 36 | /////////////////////// |
| 37 | // Output Helpers // |
| 38 | /////////////////////// |
| 39 | |
| 40 | /// Write a string to output. |
| 41 | fn write(out: &mut opaque sexpr::Output, s: &[u8]) { |
| 42 | sexpr::write(out, s); |
| 43 | } |
| 44 | |
| 45 | /// Write an `i32` before its stack buffer leaves scope. |
| 46 | fn writeI32(out: &mut opaque sexpr::Output, val: i32) { |
| 47 | let mut digits: [u8; 12] = [0; 12]; |
| 48 | let start = fmt::formatI32(val, &mut digits[..]); |
| 49 | write(out, &digits[start..]); |
| 50 | } |
| 51 | |
| 52 | /// Write a `u32` before its stack buffer leaves scope. |
| 53 | fn writeU32(out: &mut opaque sexpr::Output, val: u32) { |
| 54 | let mut digits: [u8; 10] = [0; 10]; |
| 55 | let start = fmt::formatU32(val, &mut digits[..]); |
| 56 | write(out, &digits[start..]); |
| 57 | } |
| 58 | |
| 59 | /////////////////////////////// |
| 60 | // Instruction Printing // |
| 61 | /////////////////////////////// |
| 62 | |
| 63 | /// Mnemonic column width for alignment. |
| 64 | constant MNEMONIC_WIDTH: u32 = 8; |
| 65 | |
| 66 | /// Write text wrapped in parentheses. |
| 67 | fn writeParens(out: &mut opaque sexpr::Output, s: &[u8]) { |
| 68 | write(out, "("); |
| 69 | write(out, s); |
| 70 | write(out, ")"); |
| 71 | } |
| 72 | |
| 73 | /// Write strings separated by ", ". |
| 74 | fn writeDelim(out: &mut opaque sexpr::Output, parts: &[*[u8]]) { |
| 75 | for part, i in parts { |
| 76 | if i > 0 { |
| 77 | write(out, ", "); |
| 78 | } |
| 79 | write(out, part); |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | /// Write mnemonic with padding for alignment. |
| 84 | fn writeMnem(out: &mut opaque sexpr::Output, m: *[u8]) { |
| 85 | write(out, m); |
| 86 | let mut i = m.len; |
| 87 | while i < MNEMONIC_WIDTH { |
| 88 | write(out, " "); |
| 89 | set i += 1; |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | //////////////////////////////// |
| 94 | // Instruction Format Helpers // |
| 95 | //////////////////////////////// |
| 96 | |
| 97 | /// R-type: `op rd, rs1, rs2`. |
| 98 | fn fmtR(out: &mut opaque sexpr::Output, m: *[u8], rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) { |
| 99 | writeMnem(out, m); |
| 100 | writeDelim(out, &[regNameR(rd), regNameR(rs1), regNameR(rs2)]); |
| 101 | } |
| 102 | |
| 103 | /// I-type: `op rd, rs1, imm`. |
| 104 | fn fmtI(out: &mut opaque sexpr::Output, m: *[u8], rd: gen::Reg, rs1: gen::Reg, imm: i32) { |
| 105 | writeMnem(out, m); |
| 106 | writeDelim(out, &[regNameR(rd), regNameR(rs1)]); |
| 107 | write(out, ", "); |
| 108 | writeI32(out, imm); |
| 109 | } |
| 110 | |
| 111 | /// 2-reg: `op rd, rs`. |
| 112 | fn fmt2R(out: &mut opaque sexpr::Output, m: *[u8], rd: gen::Reg, rs: gen::Reg) { |
| 113 | writeMnem(out, m); |
| 114 | writeDelim(out, &[regNameR(rd), regNameR(rs)]); |
| 115 | } |
| 116 | |
| 117 | /// reg + imm: `op rd, imm`. |
| 118 | fn fmtRI(out: &mut opaque sexpr::Output, m: *[u8], rd: gen::Reg, imm: i32) { |
| 119 | writeMnem(out, m); |
| 120 | write(out, regNameR(rd)); |
| 121 | write(out, ", "); |
| 122 | writeI32(out, imm); |
| 123 | } |
| 124 | |
| 125 | /// imm only: `op imm`. |
| 126 | fn fmtImm(out: &mut opaque sexpr::Output, m: *[u8], imm: i32) { |
| 127 | writeMnem(out, m); |
| 128 | writeI32(out, imm); |
| 129 | } |
| 130 | |
| 131 | /// 1-reg: `op rs`. |
| 132 | fn fmt1R(out: &mut opaque sexpr::Output, m: *[u8], rs: gen::Reg) { |
| 133 | writeMnem(out, m); |
| 134 | write(out, regNameR(rs)); |
| 135 | } |
| 136 | |
| 137 | /// Load: `op rd, imm(rs1)`. |
| 138 | fn fmtLoad(out: &mut opaque sexpr::Output, m: *[u8], rd: gen::Reg, rs1: gen::Reg, imm: i32) { |
| 139 | writeMnem(out, m); |
| 140 | write(out, regNameR(rd)); |
| 141 | write(out, ", "); |
| 142 | writeI32(out, imm); |
| 143 | writeParens(out, regNameR(rs1)); |
| 144 | } |
| 145 | |
| 146 | /// Store: `op rs2, imm(rs1)`. |
| 147 | fn fmtStore(out: &mut opaque sexpr::Output, m: *[u8], rs2: gen::Reg, rs1: gen::Reg, imm: i32) { |
| 148 | writeMnem(out, m); |
| 149 | write(out, regNameR(rs2)); |
| 150 | write(out, ", "); |
| 151 | writeI32(out, imm); |
| 152 | writeParens(out, regNameR(rs1)); |
| 153 | } |
| 154 | |
| 155 | /// Branch: `op rs1, rs2, imm`. |
| 156 | fn fmtB(out: &mut opaque sexpr::Output, m: *[u8], rs1: gen::Reg, rs2: gen::Reg, imm: i32) { |
| 157 | writeMnem(out, m); |
| 158 | writeDelim(out, &[regNameR(rs1), regNameR(rs2)]); |
| 159 | write(out, ", "); |
| 160 | writeI32(out, imm); |
| 161 | } |
| 162 | |
| 163 | /// Branch zero: `op rs1, imm`. |
| 164 | fn fmtBz(out: &mut opaque sexpr::Output, m: *[u8], rs1: gen::Reg, imm: i32) { |
| 165 | writeMnem(out, m); |
| 166 | write(out, regNameR(rs1)); |
| 167 | write(out, ", "); |
| 168 | writeI32(out, imm); |
| 169 | } |
| 170 | |
| 171 | /// Print a single instruction to output buffer. |
| 172 | export fn printInstr(out: &mut opaque sexpr::Output, instr: u32) { |
| 173 | let decoded = decode::decode(instr); |
| 174 | |
| 175 | match decoded { |
| 176 | case decode::Instr::Lui { rd, imm } => fmtRI(out, "lui", rd, imm), |
| 177 | case decode::Instr::Auipc { rd, imm } => fmtRI(out, "auipc", rd, imm), |
| 178 | case decode::Instr::Jal { rd, imm } => { |
| 179 | if *rd == 0 { |
| 180 | fmtImm(out, "j", imm); |
| 181 | } else { |
| 182 | fmtRI(out, "jal", rd, imm); |
| 183 | } |
| 184 | }, |
| 185 | case decode::Instr::Jalr { rd, rs1, imm } => { |
| 186 | if *rd == 0 and *rs1 == 1 and imm == 0 { |
| 187 | write(out, "ret"); |
| 188 | } else if *rd == 0 and imm == 0 { |
| 189 | fmt1R(out, "jr", rs1); |
| 190 | } else { |
| 191 | fmtI(out, "jalr", rd, rs1, imm); |
| 192 | } |
| 193 | }, |
| 194 | case decode::Instr::Beq { rs1, rs2, imm } => { |
| 195 | if *rs2 == 0 { |
| 196 | fmtBz(out, "beqz", rs1, imm); |
| 197 | } else { |
| 198 | fmtB(out, "beq", rs1, rs2, imm); |
| 199 | } |
| 200 | }, |
| 201 | case decode::Instr::Bne { rs1, rs2, imm } => { |
| 202 | if *rs2 == 0 { |
| 203 | fmtBz(out, "bnez", rs1, imm); |
| 204 | } else { |
| 205 | fmtB(out, "bne", rs1, rs2, imm); |
| 206 | } |
| 207 | }, |
| 208 | case decode::Instr::Blt { rs1, rs2, imm } => fmtB(out, "blt", rs1, rs2, imm), |
| 209 | case decode::Instr::Bge { rs1, rs2, imm } => fmtB(out, "bge", rs1, rs2, imm), |
| 210 | case decode::Instr::Bltu { rs1, rs2, imm } => fmtB(out, "bltu", rs1, rs2, imm), |
| 211 | case decode::Instr::Bgeu { rs1, rs2, imm } => fmtB(out, "bgeu", rs1, rs2, imm), |
| 212 | case decode::Instr::Lb { rd, rs1, imm } => fmtLoad(out, "lb", rd, rs1, imm), |
| 213 | case decode::Instr::Lh { rd, rs1, imm } => fmtLoad(out, "lh", rd, rs1, imm), |
| 214 | case decode::Instr::Lw { rd, rs1, imm } => fmtLoad(out, "lw", rd, rs1, imm), |
| 215 | case decode::Instr::Ld { rd, rs1, imm } => fmtLoad(out, "ld", rd, rs1, imm), |
| 216 | case decode::Instr::Lbu { rd, rs1, imm } => fmtLoad(out, "lbu", rd, rs1, imm), |
| 217 | case decode::Instr::Lhu { rd, rs1, imm } => fmtLoad(out, "lhu", rd, rs1, imm), |
| 218 | case decode::Instr::Lwu { rd, rs1, imm } => fmtLoad(out, "lwu", rd, rs1, imm), |
| 219 | case decode::Instr::Sb { rs2, rs1, imm } => fmtStore(out, "sb", rs2, rs1, imm), |
| 220 | case decode::Instr::Sh { rs2, rs1, imm } => fmtStore(out, "sh", rs2, rs1, imm), |
| 221 | case decode::Instr::Sw { rs2, rs1, imm } => fmtStore(out, "sw", rs2, rs1, imm), |
| 222 | case decode::Instr::Sd { rs2, rs1, imm } => fmtStore(out, "sd", rs2, rs1, imm), |
| 223 | case decode::Instr::Addi { rd, rs1, imm } => { |
| 224 | if *rd == 0 and *rs1 == 0 and imm == 0 { |
| 225 | write(out, "nop"); |
| 226 | } else if imm == 0 { |
| 227 | fmt2R(out, "mv", rd, rs1); |
| 228 | } else if *rs1 == 0 { |
| 229 | fmtRI(out, "li", rd, imm); |
| 230 | } else { |
| 231 | fmtI(out, "addi", rd, rs1, imm); |
| 232 | } |
| 233 | }, |
| 234 | case decode::Instr::Slti { rd, rs1, imm } => fmtI(out, "slti", rd, rs1, imm), |
| 235 | case decode::Instr::Sltiu { rd, rs1, imm } => { |
| 236 | if imm == 1 { |
| 237 | fmt2R(out, "seqz", rd, rs1); |
| 238 | } else { |
| 239 | fmtI(out, "sltiu", rd, rs1, imm); |
| 240 | } |
| 241 | }, |
| 242 | case decode::Instr::Xori { rd, rs1, imm } => { |
| 243 | if imm == -1 { |
| 244 | fmt2R(out, "not", rd, rs1); |
| 245 | } else { |
| 246 | fmtI(out, "xori", rd, rs1, imm); |
| 247 | } |
| 248 | }, |
| 249 | case decode::Instr::Ori { rd, rs1, imm } => fmtI(out, "ori", rd, rs1, imm), |
| 250 | case decode::Instr::Andi { rd, rs1, imm } => fmtI(out, "andi", rd, rs1, imm), |
| 251 | case decode::Instr::Slli { rd, rs1, shamt } => fmtI(out, "slli", rd, rs1, shamt), |
| 252 | case decode::Instr::Srli { rd, rs1, shamt } => fmtI(out, "srli", rd, rs1, shamt), |
| 253 | case decode::Instr::Srai { rd, rs1, shamt } => fmtI(out, "srai", rd, rs1, shamt), |
| 254 | case decode::Instr::Add { rd, rs1, rs2 } => fmtR(out, "add", rd, rs1, rs2), |
| 255 | case decode::Instr::Sub { rd, rs1, rs2 } => { |
| 256 | if *rs1 == 0 { |
| 257 | fmt2R(out, "neg", rd, rs2); |
| 258 | } else { |
| 259 | fmtR(out, "sub", rd, rs1, rs2); |
| 260 | } |
| 261 | }, |
| 262 | case decode::Instr::Sll { rd, rs1, rs2 } => fmtR(out, "sll", rd, rs1, rs2), |
| 263 | case decode::Instr::Slt { rd, rs1, rs2 } => fmtR(out, "slt", rd, rs1, rs2), |
| 264 | case decode::Instr::Sltu { rd, rs1, rs2 } => { |
| 265 | if *rs1 == 0 { |
| 266 | fmt2R(out, "snez", rd, rs2); |
| 267 | } else { |
| 268 | fmtR(out, "sltu", rd, rs1, rs2); |
| 269 | } |
| 270 | }, |
| 271 | case decode::Instr::Xor { rd, rs1, rs2 } => fmtR(out, "xor", rd, rs1, rs2), |
| 272 | case decode::Instr::Srl { rd, rs1, rs2 } => fmtR(out, "srl", rd, rs1, rs2), |
| 273 | case decode::Instr::Sra { rd, rs1, rs2 } => fmtR(out, "sra", rd, rs1, rs2), |
| 274 | case decode::Instr::Or { rd, rs1, rs2 } => fmtR(out, "or", rd, rs1, rs2), |
| 275 | case decode::Instr::And { rd, rs1, rs2 } => fmtR(out, "and", rd, rs1, rs2), |
| 276 | case decode::Instr::Mul { rd, rs1, rs2 } => fmtR(out, "mul", rd, rs1, rs2), |
| 277 | case decode::Instr::Mulh { rd, rs1, rs2 } => fmtR(out, "mulh", rd, rs1, rs2), |
| 278 | case decode::Instr::Mulhsu { rd, rs1, rs2 } => fmtR(out, "mulhsu", rd, rs1, rs2), |
| 279 | case decode::Instr::Mulhu { rd, rs1, rs2 } => fmtR(out, "mulhu", rd, rs1, rs2), |
| 280 | case decode::Instr::Div { rd, rs1, rs2 } => fmtR(out, "div", rd, rs1, rs2), |
| 281 | case decode::Instr::Divu { rd, rs1, rs2 } => fmtR(out, "divu", rd, rs1, rs2), |
| 282 | case decode::Instr::Rem { rd, rs1, rs2 } => fmtR(out, "rem", rd, rs1, rs2), |
| 283 | case decode::Instr::Remu { rd, rs1, rs2 } => fmtR(out, "remu", rd, rs1, rs2), |
| 284 | case decode::Instr::Addiw { rd, rs1, imm } => { |
| 285 | if imm == 0 { |
| 286 | fmt2R(out, "sext.w", rd, rs1); |
| 287 | } else { |
| 288 | fmtI(out, "addiw", rd, rs1, imm); |
| 289 | } |
| 290 | }, |
| 291 | case decode::Instr::Slliw { rd, rs1, shamt } => fmtI(out, "slliw", rd, rs1, shamt), |
| 292 | case decode::Instr::Srliw { rd, rs1, shamt } => fmtI(out, "srliw", rd, rs1, shamt), |
| 293 | case decode::Instr::Sraiw { rd, rs1, shamt } => fmtI(out, "sraiw", rd, rs1, shamt), |
| 294 | case decode::Instr::Addw { rd, rs1, rs2 } => fmtR(out, "addw", rd, rs1, rs2), |
| 295 | case decode::Instr::Subw { rd, rs1, rs2 } => fmtR(out, "subw", rd, rs1, rs2), |
| 296 | case decode::Instr::Sllw { rd, rs1, rs2 } => fmtR(out, "sllw", rd, rs1, rs2), |
| 297 | case decode::Instr::Srlw { rd, rs1, rs2 } => fmtR(out, "srlw", rd, rs1, rs2), |
| 298 | case decode::Instr::Sraw { rd, rs1, rs2 } => fmtR(out, "sraw", rd, rs1, rs2), |
| 299 | case decode::Instr::Mulw { rd, rs1, rs2 } => fmtR(out, "mulw", rd, rs1, rs2), |
| 300 | case decode::Instr::Divw { rd, rs1, rs2 } => fmtR(out, "divw", rd, rs1, rs2), |
| 301 | case decode::Instr::Divuw { rd, rs1, rs2 } => fmtR(out, "divuw", rd, rs1, rs2), |
| 302 | case decode::Instr::Remw { rd, rs1, rs2 } => fmtR(out, "remw", rd, rs1, rs2), |
| 303 | case decode::Instr::Remuw { rd, rs1, rs2 } => fmtR(out, "remuw", rd, rs1, rs2), |
| 304 | case decode::Instr::Atomic(instruction) => { |
| 305 | let stem = atomics::name(instruction.format.operation) else panic "invalid atomic operation"; |
| 306 | write(out, stem); |
| 307 | write(out, ".w" if instruction.format.width == 2 else ".d"); |
| 308 | match instruction.format.order { |
| 309 | case 1 => write(out, ".rl"), case 2 => write(out, ".aq"), case 3 => write(out, ".aqrl"), |
| 310 | else => { |
| 311 | }, |
| 312 | } |
| 313 | write(out, " "); write(out, regNameR(instruction.rd)); write(out, ", "); |
| 314 | if instruction.format.operation <> 2 { |
| 315 | write(out, regNameR(instruction.rs2)); |
| 316 | write(out, ", "); |
| 317 | } |
| 318 | write(out, "0("); write(out, regNameR(instruction.rs1)); write(out, ")"); |
| 319 | }, |
| 320 | case decode::Instr::Fence { predecessor, successor } => { |
| 321 | write(out, "fence "); fenceMask(out, predecessor); write(out, ", "); fenceMask(out, successor); |
| 322 | }, |
| 323 | case decode::Instr::FenceI => write(out, "fence.i"), |
| 324 | case decode::Instr::Ecall => write(out, "ecall"), |
| 325 | case decode::Instr::Ebreak => write(out, "ebreak"), |
| 326 | case decode::Instr::Unknown { bits } => { |
| 327 | write(out, "unknown"); |
| 328 | write(out, "("); |
| 329 | writeU32(out, bits); |
| 330 | write(out, ")"); |
| 331 | }, |
| 332 | } |
| 333 | } |
| 334 | |
| 335 | /// Print code with labels to the given output. |
| 336 | export fn printCodeTo(out: &mut opaque sexpr::Output, pkgName: *[u8], code: *[u32], funcs: *[types::FuncAddr]) { |
| 337 | // Package header. |
| 338 | write(out, "# package `"); |
| 339 | write(out, pkgName); |
| 340 | write(out, "`\n\n"); |
| 341 | |
| 342 | for instr, i in code { |
| 343 | if let name = findFunc(funcs, i) { |
| 344 | write(out, "\n# "); |
| 345 | write(out, name); |
| 346 | write(out, "\n\n"); |
| 347 | } |
| 348 | printInstr(out, instr); |
| 349 | write(out, "\n"); |
| 350 | } |
| 351 | } |
| 352 | |
| 353 | /// Find function at given instruction index. |
| 354 | fn findFunc(funcs: *[types::FuncAddr], index: u32) -> ?*[u8] { |
| 355 | for i in 0..funcs.len { |
| 356 | if funcs[i].index == index { |
| 357 | return funcs[i].name; |
| 358 | } |
| 359 | } |
| 360 | return nil; |
| 361 | } |
| 362 | |
| 363 | /// Print a memory-ordering mask in canonical order. |
| 364 | fn fenceMask(out: &mut opaque sexpr::Output, mask: u32) { |
| 365 | if mask == 0 { |
| 366 | write(out, "0"); |
| 367 | return; |
| 368 | } |
| 369 | if (mask & 8) <> 0 { |
| 370 | write(out, "i"); |
| 371 | } |
| 372 | if (mask & 4) <> 0 { |
| 373 | write(out, "o"); |
| 374 | } |
| 375 | if (mask & 2) <> 0 { |
| 376 | write(out, "r"); |
| 377 | } |
| 378 | if (mask & 1) <> 0 { |
| 379 | write(out, "w"); |
| 380 | } |
| 381 | } |