lib/std/lang/il/printer.rad 15.4 KiB 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
}