compiler/
kernel/
lib/
examples/
std/
arch/
char/
collections/
lang/
alloc/
ast/
gen/
il/
binary/
binary.rad
5.9 KiB
printer.rad
15.4 KiB
tests.rad
3.8 KiB
module/
parser/
resolver/
scanner/
alloc.rad
7.1 KiB
ast.rad
26.7 KiB
gen.rad
513 B
il.rad
19.5 KiB
lower.rad
308.1 KiB
module.rad
14.9 KiB
package.rad
1.3 KiB
parser.rad
89.5 KiB
resolver.rad
439.6 KiB
scanner.rad
18.0 KiB
sexpr.rad
6.4 KiB
strings.rad
2.2 KiB
types.rad
1.6 KiB
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
9.2 KiB
README
2.5 KiB
STYLE
2.5 KiB
std.lib
1.5 KiB
std.lib.test
662 B
lib/std/lang/il/printer.rad
raw
| 1 | //! IL pretty printer. |
| 2 | |
| 3 | use std::fmt; |
| 4 | use std::lang::sexpr; |
| 5 | |
| 6 | /////////////////////// |
| 7 | // String formatting // |
| 8 | /////////////////////// |
| 9 | |
| 10 | /// Write a `u32` before its stack buffer leaves scope. |
| 11 | unsafe fn writeU32(out: &mut sexpr::Output, val: u32) { |
| 12 | let mut digits: [u8; 10] = undefined; |
| 13 | let start = fmt::formatU32(val, &mut digits[..]); |
| 14 | write(out, &digits[start..]); |
| 15 | } |
| 16 | |
| 17 | /// Write an `i32` before its stack buffer leaves scope. |
| 18 | unsafe fn writeI32(out: &mut sexpr::Output, val: i32) { |
| 19 | let mut digits: [u8; 12] = undefined; |
| 20 | let start = fmt::formatI32(val, &mut digits[..]); |
| 21 | write(out, &digits[start..]); |
| 22 | } |
| 23 | |
| 24 | /// Write an `i64` before its stack buffer leaves scope. |
| 25 | unsafe fn writeI64(out: &mut sexpr::Output, val: i64) { |
| 26 | let mut digits: [u8; 20] = undefined; |
| 27 | let start = fmt::formatI64(val, &mut digits[..]); |
| 28 | write(out, &digits[start..]); |
| 29 | } |
| 30 | |
| 31 | ///////////////////// |
| 32 | // Output helpers // |
| 33 | ///////////////////// |
| 34 | |
| 35 | /// Write a string to the output. |
| 36 | unsafe fn write(out: &mut sexpr::Output, s: &[u8]) { |
| 37 | sexpr::write(out, s); |
| 38 | } |
| 39 | |
| 40 | /// Emit indentation. |
| 41 | unsafe fn indent(out: &mut sexpr::Output, depth: u32) { |
| 42 | for _ in 0..depth { |
| 43 | write(out, " "); |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | /// Check if name contains the path separator. |
| 48 | fn needsQuoting(name: *[u8]) -> bool { |
| 49 | for ch in name { |
| 50 | if ch == ':' { |
| 51 | return true; |
| 52 | } |
| 53 | } |
| 54 | return false; |
| 55 | } |
| 56 | |
| 57 | /// Write a symbol name. Simple names use `$name`, qualified names use `$"name"`. |
| 58 | unsafe fn writeSymbol(out: &mut sexpr::Output, name: *[u8]) { |
| 59 | write(out, "$"); |
| 60 | if needsQuoting(name) { |
| 61 | write(out, "\""); |
| 62 | write(out, name); |
| 63 | write(out, "\""); |
| 64 | } else { |
| 65 | write(out, name); |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | //////////////////// |
| 70 | // Type printing // |
| 71 | //////////////////// |
| 72 | |
| 73 | /// Get the string representation of a type. |
| 74 | fn typeStr(typ: super::Type) -> *[u8] { |
| 75 | match typ { |
| 76 | case super::Type::W8 => return "w8", |
| 77 | case super::Type::W16 => return "w16", |
| 78 | case super::Type::W32 => return "w32", |
| 79 | case super::Type::W64 => return "w64", |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | /// Write a type. |
| 84 | unsafe fn writeType(out: &mut sexpr::Output, typ: super::Type) { |
| 85 | write(out, typeStr(typ)); |
| 86 | } |
| 87 | |
| 88 | //////////////////////// |
| 89 | // Operation printing // |
| 90 | //////////////////////// |
| 91 | |
| 92 | /// Get the string representation of a binary ALU operation. |
| 93 | fn binOpStr(op: super::BinOp) -> *[u8] { |
| 94 | match op { |
| 95 | case super::BinOp::Add => return "add", |
| 96 | case super::BinOp::Sub => return "sub", |
| 97 | case super::BinOp::Mul => return "mul", |
| 98 | case super::BinOp::Sdiv => return "sdiv", |
| 99 | case super::BinOp::Udiv => return "udiv", |
| 100 | case super::BinOp::Srem => return "srem", |
| 101 | case super::BinOp::Urem => return "urem", |
| 102 | case super::BinOp::Eq => return "eq", |
| 103 | case super::BinOp::Ne => return "ne", |
| 104 | case super::BinOp::Slt => return "slt", |
| 105 | case super::BinOp::Sge => return "sge", |
| 106 | case super::BinOp::Ult => return "ult", |
| 107 | case super::BinOp::Uge => return "uge", |
| 108 | case super::BinOp::And => return "and", |
| 109 | case super::BinOp::Or => return "or", |
| 110 | case super::BinOp::Xor => return "xor", |
| 111 | case super::BinOp::Shl => return "shl", |
| 112 | case super::BinOp::Sshr => return "sshr", |
| 113 | case super::BinOp::Ushr => return "ushr", |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | /// Get the string representation of a unary ALU operation. |
| 118 | fn unOpStr(op: super::UnOp) -> *[u8] { |
| 119 | match op { |
| 120 | case super::UnOp::Neg => return "neg", |
| 121 | case super::UnOp::Not => return "not", |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | //////////////////// |
| 126 | // Value printing // |
| 127 | //////////////////// |
| 128 | |
| 129 | /// Write a value (register, immediate, symbol, or undefined). |
| 130 | unsafe fn writeVal(out: &mut sexpr::Output, val: super::Val) { |
| 131 | match val { |
| 132 | case super::Val::Reg(reg) => writeReg(out, reg), |
| 133 | case super::Val::Imm(v) => writeI64(out, v), |
| 134 | case super::Val::DataSym(name) => writeSymbol(out, name), |
| 135 | case super::Val::FnAddr(name) => writeSymbol(out, name), |
| 136 | case super::Val::Undef => write(out, "undefined"), |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | /// Write a register in one operation before its stack buffer leaves scope. |
| 141 | unsafe fn writeReg(out: &mut sexpr::Output, reg: super::Reg) { |
| 142 | let mut buffer: [u8; 11] = undefined; |
| 143 | let start = fmt::formatU32(reg.n, &mut buffer[1..]); |
| 144 | set buffer[start] = '%'; |
| 145 | write(out, &buffer[start..]); |
| 146 | } |
| 147 | |
| 148 | /// Write a comma-separated argument list in parentheses. |
| 149 | unsafe fn writeArgs(out: &mut sexpr::Output, args: *unsafe [super::Val]) { |
| 150 | write(out, "("); |
| 151 | for arg, i in args { |
| 152 | if i > 0 { |
| 153 | write(out, ", "); |
| 154 | } |
| 155 | writeVal(out, arg); |
| 156 | } |
| 157 | write(out, ")"); |
| 158 | } |
| 159 | |
| 160 | /// Write a typed parameter (e.g., `w32 %0`). |
| 161 | unsafe fn writeParam(out: &mut sexpr::Output, param: super::Param) { |
| 162 | writeType(out, param.type); |
| 163 | write(out, " "); |
| 164 | writeReg(out, param.value); |
| 165 | } |
| 166 | |
| 167 | /// Write a comma-separated parameter list. |
| 168 | unsafe fn writeParams(out: &mut sexpr::Output, params: *unsafe [super::Param]) { |
| 169 | for param, i in params { |
| 170 | if i > 0 { |
| 171 | write(out, ", "); |
| 172 | } |
| 173 | writeParam(out, param); |
| 174 | } |
| 175 | } |
| 176 | |
| 177 | ////////////////////////// |
| 178 | // Instruction printing // |
| 179 | ////////////////////////// |
| 180 | |
| 181 | /// Write an instruction. |
| 182 | unsafe fn writeInstr(out: &mut sexpr::Output, blocks: *unsafe [super::Block], inst: super::Instr) { |
| 183 | match inst { |
| 184 | // Memory operations. |
| 185 | case super::Instr::Reserve { dst, size, alignment } => { |
| 186 | write(out, "reserve "); |
| 187 | writeReg(out, dst); |
| 188 | write(out, " "); |
| 189 | writeVal(out, size); |
| 190 | write(out, " "); |
| 191 | writeU32(out, alignment); |
| 192 | } |
| 193 | case super::Instr::Load { dst, typ, src, offset } => { |
| 194 | write(out, "load "); |
| 195 | writeType(out, typ); |
| 196 | write(out, " "); |
| 197 | writeReg(out, dst); |
| 198 | write(out, " "); |
| 199 | writeReg(out, src); |
| 200 | write(out, " "); |
| 201 | writeI32(out, offset); |
| 202 | } |
| 203 | case super::Instr::Sload { dst, typ, src, offset } => { |
| 204 | write(out, "sload "); |
| 205 | writeType(out, typ); |
| 206 | write(out, " "); |
| 207 | writeReg(out, dst); |
| 208 | write(out, " "); |
| 209 | writeReg(out, src); |
| 210 | write(out, " "); |
| 211 | writeI32(out, offset); |
| 212 | } |
| 213 | case super::Instr::Store { typ, src, dst, offset } => { |
| 214 | write(out, "store "); |
| 215 | writeType(out, typ); |
| 216 | write(out, " "); |
| 217 | writeVal(out, src); |
| 218 | write(out, " "); |
| 219 | writeReg(out, dst); |
| 220 | write(out, " "); |
| 221 | writeI32(out, offset); |
| 222 | } |
| 223 | case super::Instr::Blit { dst, src, size } => { |
| 224 | write(out, "blit "); |
| 225 | writeReg(out, dst); |
| 226 | write(out, " "); |
| 227 | writeReg(out, src); |
| 228 | write(out, " "); |
| 229 | writeVal(out, size); |
| 230 | } |
| 231 | case super::Instr::Copy { dst, val } => { |
| 232 | write(out, "copy "); |
| 233 | writeReg(out, dst); |
| 234 | write(out, " "); |
| 235 | writeVal(out, val); |
| 236 | } |
| 237 | |
| 238 | // ALU operations. |
| 239 | case super::Instr::BinOp { op, dst, typ, a: va, b } => |
| 240 | writeTypedBinOp(out, binOpStr(op), typ, dst, va, b), |
| 241 | case super::Instr::UnOp { op, dst, typ, a: va } => |
| 242 | writeTypedUnaryOp(out, unOpStr(op), typ, dst, va), |
| 243 | |
| 244 | // Conversion operations. |
| 245 | case super::Instr::Zext { dst, typ, val } => |
| 246 | writeTypedUnaryOp(out, "zext", typ, dst, val), |
| 247 | case super::Instr::Sext { dst, typ, val } => |
| 248 | writeTypedUnaryOp(out, "sext", typ, dst, val), |
| 249 | |
| 250 | // Call. |
| 251 | case super::Instr::Call { dst, retTy, func, args } => { |
| 252 | write(out, "call "); |
| 253 | writeType(out, retTy); |
| 254 | write(out, " "); |
| 255 | if let d = dst { |
| 256 | writeReg(out, d); |
| 257 | write(out, " "); |
| 258 | } |
| 259 | writeVal(out, func); |
| 260 | writeArgs(out, args); |
| 261 | } |
| 262 | |
| 263 | // Terminators. |
| 264 | case super::Instr::Ret { val } => { |
| 265 | write(out, "ret"); |
| 266 | if let v = val { |
| 267 | write(out, " "); |
| 268 | writeVal(out, v); |
| 269 | } |
| 270 | } |
| 271 | case super::Instr::Jmp { target, args } => { |
| 272 | write(out, "jmp @"); |
| 273 | write(out, blocks[target].label); |
| 274 | if args.len > 0 { |
| 275 | writeArgs(out, args); |
| 276 | } |
| 277 | } |
| 278 | case super::Instr::Br { op, typ, a: va, b: vb, thenTarget, thenArgs, elseTarget, elseArgs } => { |
| 279 | write(out, "br."); |
| 280 | match op { |
| 281 | case super::CmpOp::Eq => write(out, "eq"), |
| 282 | case super::CmpOp::Ne => write(out, "ne"), |
| 283 | case super::CmpOp::Slt => write(out, "slt"), |
| 284 | case super::CmpOp::Ult => write(out, "ult"), |
| 285 | } |
| 286 | write(out, " "); |
| 287 | writeType(out, typ); |
| 288 | write(out, " "); |
| 289 | writeVal(out, va); |
| 290 | write(out, " "); |
| 291 | writeVal(out, vb); |
| 292 | write(out, " @"); |
| 293 | write(out, blocks[thenTarget].label); |
| 294 | if thenArgs.len > 0 { |
| 295 | writeArgs(out, thenArgs); |
| 296 | } |
| 297 | write(out, " @"); |
| 298 | write(out, blocks[elseTarget].label); |
| 299 | if elseArgs.len > 0 { |
| 300 | writeArgs(out, elseArgs); |
| 301 | } |
| 302 | } |
| 303 | case super::Instr::Switch { val, defaultTarget, defaultArgs, cases } => { |
| 304 | write(out, "switch "); |
| 305 | writeVal(out, val); |
| 306 | for c in cases { |
| 307 | write(out, " ("); |
| 308 | writeI64(out, c.value); |
| 309 | write(out, " @"); |
| 310 | write(out, blocks[c.target].label); |
| 311 | if c.args.len > 0 { |
| 312 | writeArgs(out, c.args); |
| 313 | } |
| 314 | write(out, ")"); |
| 315 | } |
| 316 | write(out, " @"); |
| 317 | write(out, blocks[defaultTarget].label); |
| 318 | if defaultArgs.len > 0 { |
| 319 | writeArgs(out, defaultArgs); |
| 320 | } |
| 321 | } |
| 322 | case super::Instr::Unreachable => { |
| 323 | write(out, "unreachable"); |
| 324 | } |
| 325 | |
| 326 | // Intrinsics. |
| 327 | case super::Instr::Ecall { dst, num, a0, a1, a2, a3 } => { |
| 328 | write(out, "ecall "); |
| 329 | writeReg(out, dst); |
| 330 | write(out, " "); |
| 331 | writeVal(out, num); |
| 332 | write(out, " "); |
| 333 | writeVal(out, a0); |
| 334 | write(out, " "); |
| 335 | writeVal(out, a1); |
| 336 | write(out, " "); |
| 337 | writeVal(out, a2); |
| 338 | write(out, " "); |
| 339 | writeVal(out, a3); |
| 340 | } |
| 341 | case super::Instr::DeviceRead { typ, dst, handle, offset } => { |
| 342 | write(out, "device-read "); write(out, typeStr(typ)); write(out, " "); |
| 343 | writeReg(out, dst); write(out, " "); writeVal(out, handle); write(out, " "); writeVal(out, offset); |
| 344 | } |
| 345 | case super::Instr::DeviceWrite { typ, handle, offset, value } => { |
| 346 | write(out, "device-write "); write(out, typeStr(typ)); write(out, " "); |
| 347 | writeVal(out, handle); write(out, " "); writeVal(out, offset); write(out, " "); writeVal(out, value); |
| 348 | } |
| 349 | case super::Instr::Ebreak => { |
| 350 | write(out, "ebreak"); |
| 351 | } |
| 352 | case super::Instr::MemoryFence => { |
| 353 | write(out, "memory-fence"); |
| 354 | } |
| 355 | } |
| 356 | } |
| 357 | |
| 358 | /// Write a typed binary operation: `op type %dst %a %b`. |
| 359 | unsafe fn writeTypedBinOp( |
| 360 | out: &mut sexpr::Output, |
| 361 | name: *[u8], |
| 362 | typ: super::Type, |
| 363 | dst: super::Reg, |
| 364 | va: super::Val, |
| 365 | vb: super::Val |
| 366 | ) { |
| 367 | write(out, name); |
| 368 | write(out, " "); |
| 369 | writeType(out, typ); |
| 370 | write(out, " "); |
| 371 | writeReg(out, dst); |
| 372 | write(out, " "); |
| 373 | writeVal(out, va); |
| 374 | write(out, " "); |
| 375 | writeVal(out, vb); |
| 376 | } |
| 377 | |
| 378 | /// Write a typed unary operation: `op type %dst %val`. |
| 379 | unsafe fn writeTypedUnaryOp( |
| 380 | out: &mut sexpr::Output, |
| 381 | name: *[u8], |
| 382 | typ: super::Type, |
| 383 | dst: super::Reg, |
| 384 | val: super::Val |
| 385 | ) { |
| 386 | write(out, name); |
| 387 | write(out, " "); |
| 388 | writeType(out, typ); |
| 389 | write(out, " "); |
| 390 | writeReg(out, dst); |
| 391 | write(out, " "); |
| 392 | writeVal(out, val); |
| 393 | } |
| 394 | |
| 395 | //////////////////// |
| 396 | // Block printing // |
| 397 | //////////////////// |
| 398 | |
| 399 | /// Write a basic block. |
| 400 | unsafe fn writeBlock(out: &mut sexpr::Output, blocks: *unsafe [super::Block], block: *unsafe super::Block) { |
| 401 | // Block label. |
| 402 | write(out, " @"); |
| 403 | write(out, block.label); |
| 404 | |
| 405 | // Block parameters. |
| 406 | if block.params.len > 0 { |
| 407 | write(out, "("); |
| 408 | writeParams(out, block.params); |
| 409 | write(out, ")"); |
| 410 | } |
| 411 | write(out, "\n"); |
| 412 | |
| 413 | // Instructions. |
| 414 | for instr in block.instrs { |
| 415 | indent(out, 1); |
| 416 | writeInstr(out, blocks, instr); |
| 417 | write(out, ";\n"); |
| 418 | } |
| 419 | } |
| 420 | |
| 421 | /////////////////////// |
| 422 | // Function printing // |
| 423 | /////////////////////// |
| 424 | |
| 425 | /// Write a function. |
| 426 | unsafe fn writeFn(out: &mut sexpr::Output, f: *unsafe super::Fn) { |
| 427 | // Function signature. |
| 428 | if f.isExtern { |
| 429 | write(out, "extern "); |
| 430 | } |
| 431 | write(out, "fn "); |
| 432 | writeType(out, f.returnType); |
| 433 | write(out, " "); |
| 434 | writeSymbol(out, f.name); |
| 435 | write(out, "("); |
| 436 | writeParams(out, f.params); |
| 437 | write(out, ")"); |
| 438 | |
| 439 | // Extern functions have no body. |
| 440 | if f.isExtern { |
| 441 | write(out, ";\n"); |
| 442 | return; |
| 443 | } |
| 444 | write(out, " {\n"); |
| 445 | |
| 446 | // Blocks. |
| 447 | for i in 0..f.blocks.len { |
| 448 | if f.blocks[i].instrs.len > 0 { |
| 449 | writeBlock(out, f.blocks, &f.blocks[i]); |
| 450 | } |
| 451 | } |
| 452 | write(out, "}\n"); |
| 453 | } |
| 454 | |
| 455 | /////////////////// |
| 456 | // Data printing // |
| 457 | /////////////////// |
| 458 | |
| 459 | /// Write a data item. |
| 460 | unsafe fn writeDataItem(out: &mut sexpr::Output, item: super::DataItem) { |
| 461 | match item { |
| 462 | case super::DataItem::Val { typ, val } => { |
| 463 | writeType(out, typ); |
| 464 | write(out, " "); |
| 465 | writeI64(out, val); |
| 466 | } |
| 467 | case super::DataItem::Sym(name) => { |
| 468 | write(out, "sym "); |
| 469 | writeSymbol(out, name); |
| 470 | } |
| 471 | case super::DataItem::Fn(name) => { |
| 472 | write(out, "fn "); |
| 473 | writeSymbol(out, name); |
| 474 | } |
| 475 | case super::DataItem::Str(s) => { |
| 476 | write(out, "str "); |
| 477 | sexpr::printStringTo(out, s); |
| 478 | } |
| 479 | case super::DataItem::Undef => { |
| 480 | write(out, "undef"); |
| 481 | } |
| 482 | } |
| 483 | } |
| 484 | |
| 485 | /// Write a data value (item with optional repeat count). |
| 486 | unsafe fn writeDataValue(out: &mut sexpr::Output, value: super::DataValue) { |
| 487 | writeDataItem(out, value.item); |
| 488 | if value.count > 1 { |
| 489 | write(out, " * "); |
| 490 | writeU32(out, value.count); |
| 491 | } |
| 492 | } |
| 493 | |
| 494 | /// Write global data. |
| 495 | unsafe fn writeData(out: &mut sexpr::Output, d: super::Data) { |
| 496 | write(out, "data "); |
| 497 | if not d.readOnly { |
| 498 | write(out, "mut "); |
| 499 | } |
| 500 | writeSymbol(out, d.name); |
| 501 | write(out, " align "); |
| 502 | writeU32(out, d.alignment); |
| 503 | write(out, " {\n"); |
| 504 | |
| 505 | for v in d.values { |
| 506 | indent(out, 1); |
| 507 | writeDataValue(out, v); |
| 508 | write(out, ";\n"); |
| 509 | } |
| 510 | write(out, "}\n"); |
| 511 | } |
| 512 | |
| 513 | ////////////////////// |
| 514 | // Program printing // |
| 515 | ////////////////////// |
| 516 | |
| 517 | /// Print a program. |
| 518 | export unsafe fn printProgram(out: &mut sexpr::Output, program: &super::Program) { |
| 519 | // Data declarations. |
| 520 | for data, i in program.data { |
| 521 | writeData(out, data); |
| 522 | if i < program.data.len - 1 or program.fns.len > 0 { |
| 523 | write(out, "\n"); |
| 524 | } |
| 525 | } |
| 526 | // Functions. |
| 527 | for func, i in program.fns { |
| 528 | writeFn(out, func); |
| 529 | if i < program.fns.len - 1 { |
| 530 | write(out, "\n"); |
| 531 | } |
| 532 | } |
| 533 | } |
| 534 | |
| 535 | /// Print a program to a buffer, returning the written slice. |
| 536 | export unsafe fn printProgramToBuffer( |
| 537 | program: &super::Program, |
| 538 | buf: *mut [u8] |
| 539 | ) -> *[u8] { |
| 540 | let mut pos: u32 = 0; |
| 541 | let raw: *unsafe mut [u8] = &mut buf[..]; |
| 542 | let mut out = sexpr::Output::Buffer { buf: raw, pos: &mut pos }; |
| 543 | printProgram(&mut out, program); |
| 544 | |
| 545 | return &buf[..pos]; |
| 546 | } |