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