lib/std/lang/il/printer.rad 16.0 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
fn writeU32(out: &mut opaque sexpr::Output, val: u32) {
12
    let mut digits: [u8; 10] = [0; 10];
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
fn writeI32(out: &mut opaque sexpr::Output, val: i32) {
19
    let mut digits: [u8; 12] = [0; 12];
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
fn writeI64(out: &mut opaque sexpr::Output, val: i64) {
26
    let mut digits: [u8; 20] = [0; 20];
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
fn write(out: &mut opaque sexpr::Output, s: &[u8]) {
37
    sexpr::write(out, s);
38
}
39
40
/// Emit indentation.
41
fn indent(out: &mut opaque 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
fn writeSymbol(out: &mut opaque 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
fn writeType(out: &mut opaque 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
fn writeVal(out: &mut opaque 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
fn writeReg(out: &mut opaque sexpr::Output, reg: super::Reg) {
142
    let mut buffer: [u8; 11] = [0; 11];
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
fn writeArgs(out: &mut opaque sexpr::Output, args: &[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
fn writeParam(out: &mut opaque 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
fn writeParams(out: &mut opaque sexpr::Output, params: &[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 opaque sexpr::Output, blocks: &[super::Block], inst: super::Instr) {
183
    match inst {
184
        case super::Instr::Call { args, .. } => writeOperandInstr(out, inst, args),
185
186
        case super::Instr::Jmp { target, args } => {
187
            write(out, "jmp ");
188
            writeTarget(out, blocks, target, args);
189
        }
190
        case super::Instr::Br { thenArgs, elseArgs, .. } =>
191
            writeBranch(out, blocks, inst, thenArgs, elseArgs),
192
        case super::Instr::Switch { val, defaultTarget, defaultArgs, cases } => {
193
            write(out, "switch ");
194
            writeVal(out, val);
195
            for c in cases {
196
                write(out, " (");
197
                writeI64(out, c.value);
198
                write(out, " ");
199
                writeTarget(out, blocks, c.target, c.args);
200
                write(out, ")");
201
            }
202
            write(out, " ");
203
            writeTarget(out, blocks, defaultTarget, defaultArgs);
204
        }
205
        else => writeOperandInstr(out, inst, &[]),
206
    }
207
}
208
209
/// Write a comparison and both of its control-flow edges.
210
fn writeBranch(
211
    out: &mut opaque sexpr::Output,
212
    blocks: &[super::Block],
213
    inst: super::Instr,
214
    thenArgs: &[super::Val],
215
    elseArgs: &[super::Val],
216
) {
217
    let case super::Instr::Br { op, typ, a: va, b: vb, thenTarget, elseTarget, .. } = inst
218
        else panic "writeBranch: expected branch";
219
    write(out, "br.");
220
    match op {
221
        case super::CmpOp::Eq  => write(out, "eq"),
222
        case super::CmpOp::Ne  => write(out, "ne"),
223
        case super::CmpOp::Slt => write(out, "slt"),
224
        case super::CmpOp::Ult => write(out, "ult"),
225
    }
226
    write(out, " ");
227
    writeType(out, typ);
228
    write(out, " ");
229
    writeVal(out, va);
230
    write(out, " ");
231
    writeVal(out, vb);
232
    write(out, " ");
233
    writeTarget(out, blocks, thenTarget, thenArgs);
234
    write(out, " ");
235
    writeTarget(out, blocks, elseTarget, elseArgs);
236
}
237
238
/// Write a target label and its optional block arguments.
239
fn writeTarget(out: &mut opaque sexpr::Output, blocks: &[super::Block], target: u32, args: &[super::Val]) {
240
    write(out, "@");
241
    write(out, blocks[target].label);
242
    if args.len > 0 {
243
        writeArgs(out, args);
244
    }
245
}
246
247
/// Write fixed operands and borrowed call arguments.
248
fn writeOperandInstr(out: &mut opaque sexpr::Output, inst: super::Instr, args: &[super::Val]) {
249
    match inst {
250
        // Call.
251
        case super::Instr::Call { dst, retTy, func, .. } => {
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
        // Memory operations.
264
        case super::Instr::Reserve { dst, size, alignment } => {
265
            write(out, "reserve ");
266
            writeReg(out, dst);
267
            write(out, " ");
268
            writeVal(out, size);
269
            write(out, " ");
270
            writeU32(out, alignment);
271
        }
272
        case super::Instr::Load { dst, typ, src, offset } => {
273
            write(out, "load ");
274
            writeType(out, typ);
275
            write(out, " ");
276
            writeReg(out, dst);
277
            write(out, " ");
278
            writeReg(out, src);
279
            write(out, " ");
280
            writeI32(out, offset);
281
        }
282
        case super::Instr::Sload { dst, typ, src, offset } => {
283
            write(out, "sload ");
284
            writeType(out, typ);
285
            write(out, " ");
286
            writeReg(out, dst);
287
            write(out, " ");
288
            writeReg(out, src);
289
            write(out, " ");
290
            writeI32(out, offset);
291
        }
292
        case super::Instr::Store { typ, src, dst, offset } => {
293
            write(out, "store ");
294
            writeType(out, typ);
295
            write(out, " ");
296
            writeVal(out, src);
297
            write(out, " ");
298
            writeReg(out, dst);
299
            write(out, " ");
300
            writeI32(out, offset);
301
        }
302
        case super::Instr::Blit { dst, src, size } => {
303
            write(out, "blit ");
304
            writeReg(out, dst);
305
            write(out, " ");
306
            writeReg(out, src);
307
            write(out, " ");
308
            writeVal(out, size);
309
        }
310
        case super::Instr::Copy { dst, val } => {
311
            write(out, "copy ");
312
            writeReg(out, dst);
313
            write(out, " ");
314
            writeVal(out, val);
315
        }
316
317
        // ALU operations.
318
        case super::Instr::BinOp { op, dst, typ, a: va, b } =>
319
            writeTypedBinOp(out, binOpStr(op), typ, dst, va, b),
320
        case super::Instr::UnOp { op, dst, typ, a: va } =>
321
            writeTypedUnaryOp(out, unOpStr(op), typ, dst, va),
322
323
        // Conversion operations.
324
        case super::Instr::Zext { dst, typ, val } =>
325
            writeTypedUnaryOp(out, "zext", typ, dst, val),
326
        case super::Instr::Sext { dst, typ, val } =>
327
            writeTypedUnaryOp(out, "sext", typ, dst, val),
328
329
        // Terminators.
330
        case super::Instr::Ret { val } => {
331
            write(out, "ret");
332
            if let v = val {
333
                write(out, " ");
334
                writeVal(out, v);
335
            }
336
        }
337
        case super::Instr::Unreachable => {
338
            write(out, "unreachable");
339
        }
340
341
        // Intrinsics.
342
        case super::Instr::Ecall { dst, num, a0, a1, a2, a3 } => {
343
            write(out, "ecall ");
344
            writeReg(out, dst);
345
            write(out, " ");
346
            writeVal(out, num);
347
            write(out, " ");
348
            writeVal(out, a0);
349
            write(out, " ");
350
            writeVal(out, a1);
351
            write(out, " ");
352
            writeVal(out, a2);
353
            write(out, " ");
354
            writeVal(out, a3);
355
        }
356
        case super::Instr::DeviceRead { typ, dst, handle, offset } => {
357
            write(out, "device-read "); write(out, typeStr(typ)); write(out, " ");
358
            writeReg(out, dst); write(out, " "); writeVal(out, handle); write(out, " "); writeVal(out, offset);
359
        }
360
        case super::Instr::DeviceWrite { typ, handle, offset, value } => {
361
            write(out, "device-write "); write(out, typeStr(typ)); write(out, " ");
362
            writeVal(out, handle); write(out, " "); writeVal(out, offset); write(out, " "); writeVal(out, value);
363
        }
364
        case super::Instr::Ebreak => {
365
            write(out, "ebreak");
366
        }
367
        case super::Instr::MemoryFence => {
368
            write(out, "memory-fence");
369
        }
370
        case super::Instr::Jmp { .. },
371
             super::Instr::Br { .. }, super::Instr::Switch { .. } =>
372
            panic "writeOperandInstr: expected value operands",
373
    }
374
}
375
376
/// Write a typed binary operation: `op type %dst %a %b`.
377
fn writeTypedBinOp(
378
    out: &mut opaque sexpr::Output,
379
    name: *[u8],
380
    typ: super::Type,
381
    dst: super::Reg,
382
    va: super::Val,
383
    vb: super::Val
384
) {
385
    write(out, name);
386
    write(out, " ");
387
    writeType(out, typ);
388
    write(out, " ");
389
    writeReg(out, dst);
390
    write(out, " ");
391
    writeVal(out, va);
392
    write(out, " ");
393
    writeVal(out, vb);
394
}
395
396
/// Write a typed unary operation: `op type %dst %val`.
397
fn writeTypedUnaryOp(
398
    out: &mut opaque sexpr::Output,
399
    name: *[u8],
400
    typ: super::Type,
401
    dst: super::Reg,
402
    val: super::Val
403
) {
404
    write(out, name);
405
    write(out, " ");
406
    writeType(out, typ);
407
    write(out, " ");
408
    writeReg(out, dst);
409
    write(out, " ");
410
    writeVal(out, val);
411
}
412
413
////////////////////
414
// Block printing //
415
////////////////////
416
417
/// Write a basic block.
418
unsafe fn writeBlock(out: &mut opaque sexpr::Output, blocks: &[super::Block], block: &super::Block) {
419
    // Block label.
420
    write(out, "  @");
421
    write(out, block.label);
422
423
    // Block parameters.
424
    if block.params.len > 0 {
425
        write(out, "(");
426
        writeParams(out, block.params);
427
        write(out, ")");
428
    }
429
    write(out, "\n");
430
431
    // Instructions.
432
    for instr in block.instrs {
433
        indent(out, 1);
434
        writeInstr(out, blocks, instr);
435
        write(out, ";\n");
436
    }
437
}
438
439
///////////////////////
440
// Function printing //
441
///////////////////////
442
443
/// Write a function.
444
unsafe fn writeFn(out: &mut opaque sexpr::Output, f: &super::Fn) {
445
    // Function signature.
446
    if f.isExtern {
447
        write(out, "extern ");
448
    }
449
    write(out, "fn ");
450
    writeType(out, f.returnType);
451
    write(out, " ");
452
    writeSymbol(out, f.name);
453
    write(out, "(");
454
    writeParams(out, f.params);
455
    write(out, ")");
456
457
    // Extern functions have no body.
458
    if f.isExtern {
459
        write(out, ";\n");
460
        return;
461
    }
462
    write(out, " {\n");
463
464
    // Blocks.
465
    for i in 0..f.blocks.len {
466
        if f.blocks[i].instrs.len > 0 {
467
            writeBlock(out, f.blocks, &f.blocks[i]);
468
        }
469
    }
470
    write(out, "}\n");
471
}
472
473
///////////////////
474
// Data printing //
475
///////////////////
476
477
/// Write a data item.
478
fn writeDataItem(out: &mut opaque sexpr::Output, item: super::DataItem) {
479
    match item {
480
        case super::DataItem::Val { typ, val } => {
481
            writeType(out, typ);
482
            write(out, " ");
483
            writeI64(out, val);
484
        }
485
        case super::DataItem::Sym(name) => {
486
            write(out, "sym ");
487
            writeSymbol(out, name);
488
        }
489
        case super::DataItem::Fn(name) => {
490
            write(out, "fn ");
491
            writeSymbol(out, name);
492
        }
493
        case super::DataItem::Str(s) => {
494
            write(out, "str ");
495
            sexpr::printStringTo(out, s);
496
        }
497
        case super::DataItem::Undef => {
498
            write(out, "undef");
499
        }
500
    }
501
}
502
503
/// Write a data value (item with optional repeat count).
504
fn writeDataValue(out: &mut opaque sexpr::Output, value: super::DataValue) {
505
    writeDataItem(out, value.item);
506
    if value.count > 1 {
507
        write(out, " * ");
508
        writeU32(out, value.count);
509
    }
510
}
511
512
/// Write global data.
513
fn writeData(out: &mut opaque sexpr::Output, d: super::Data) {
514
    write(out, "data ");
515
    if not d.readOnly {
516
        write(out, "mut ");
517
    }
518
    writeSymbol(out, d.name);
519
    write(out, " align ");
520
    writeU32(out, d.alignment);
521
    write(out, " {\n");
522
523
    for v in d.values {
524
        indent(out, 1);
525
        writeDataValue(out, v);
526
        write(out, ";\n");
527
    }
528
    write(out, "}\n");
529
}
530
531
//////////////////////
532
// Program printing //
533
//////////////////////
534
535
/// Print a program.
536
export unsafe fn printProgram(out: &mut opaque sexpr::Output, program: &super::Program) {
537
    // Data declarations.
538
    for data, i in program.data {
539
        writeData(out, data);
540
        if i < program.data.len - 1 or program.fns.len > 0 {
541
            write(out, "\n");
542
        }
543
    }
544
    // Functions.
545
    for func, i in program.fns {
546
        writeFn(out, func);
547
        if i < program.fns.len - 1 {
548
            write(out, "\n");
549
        }
550
    }
551
}
552
553
/// Print a program to a buffer, returning the written slice.
554
export unsafe fn printProgramToBuffer(
555
    program: &super::Program,
556
    buf: *mut [u8]
557
) -> *[u8] {
558
    let mut pos: u32 = 0;
559
    let bytes: 'output = &mut buf[..] in {
560
        let mut out = sexpr::Buffer 'output { buf: bytes, pos: 0 };
561
        printProgram(&mut out, program);
562
        set pos = out.pos;
563
    }
564
565
    return &buf[..pos];
566
}