lib/std/lang/il/printer.rad 15.6 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
    }
103
}
104
105
/// Write a type.
106
fn writeType(out: *mut sexpr::Output, typ: super::Type) {
107
    write(out, typeStr(typ));
108
}
109
110
////////////////////////
111
// Operation printing //
112
////////////////////////
113
114
/// Get the string representation of a binary ALU operation.
115
fn binOpStr(op: super::BinOp) -> *[u8] {
116
    match op {
117
        case super::BinOp::Add => return "add",
118
        case super::BinOp::Sub => return "sub",
119
        case super::BinOp::Mul => return "mul",
120
        case super::BinOp::Sdiv => return "sdiv",
121
        case super::BinOp::Udiv => return "udiv",
122
        case super::BinOp::Srem => return "srem",
123
        case super::BinOp::Urem => return "urem",
124
        case super::BinOp::Eq => return "eq",
125
        case super::BinOp::Ne => return "ne",
126
        case super::BinOp::Slt => return "slt",
127
        case super::BinOp::Sge => return "sge",
128
        case super::BinOp::Ult => return "ult",
129
        case super::BinOp::Uge => return "uge",
130
        case super::BinOp::And => return "and",
131
        case super::BinOp::Or => return "or",
132
        case super::BinOp::Xor => return "xor",
133
        case super::BinOp::Shl => return "shl",
134
        case super::BinOp::Sshr => return "sshr",
135
        case super::BinOp::Ushr => return "ushr",
136
    }
137
}
138
139
/// Get the string representation of a unary ALU operation.
140
fn unOpStr(op: super::UnOp) -> *[u8] {
141
    match op {
142
        case super::UnOp::Neg => return "neg",
143
        case super::UnOp::Not => return "not",
144
    }
145
}
146
147
////////////////////
148
// Value printing //
149
////////////////////
150
151
/// Write a value (register, immediate, symbol, or undefined).
152
fn writeVal(out: *mut sexpr::Output, a: *mut alloc::Arena, val: super::Val) {
153
    match val {
154
        case super::Val::Reg(reg) => write(out, regStr(a, reg)),
155
        case super::Val::Imm(v) => write(out, formatI64(a, v)),
156
        case super::Val::DataSym(name) => writeSymbol(out, name),
157
        case super::Val::FnAddr(name) => writeSymbol(out, name),
158
        case super::Val::Undef => write(out, "undefined"),
159
    }
160
}
161
162
/// Write a register.
163
fn writeReg(out: *mut sexpr::Output, a: *mut alloc::Arena, reg: super::Reg) {
164
    write(out, regStr(a, reg));
165
}
166
167
/// Write a comma-separated argument list in parentheses.
168
fn writeArgs(out: *mut sexpr::Output, a: *mut alloc::Arena, args: *[super::Val]) {
169
    write(out, "(");
170
    for arg, i in args {
171
        if i > 0 {
172
            write(out, ", ");
173
        }
174
        writeVal(out, a, arg);
175
    }
176
    write(out, ")");
177
}
178
179
/// Write a typed parameter (e.g., `w32 %0`).
180
fn writeParam(out: *mut sexpr::Output, a: *mut alloc::Arena, param: super::Param) {
181
    writeType(out, param.type);
182
    write(out, " ");
183
    writeReg(out, a, param.value);
184
}
185
186
/// Write a comma-separated parameter list.
187
fn writeParams(out: *mut sexpr::Output, a: *mut alloc::Arena, params: *[super::Param]) {
188
    for param, i in params {
189
        if i > 0 {
190
            write(out, ", ");
191
        }
192
        writeParam(out, a, param);
193
    }
194
}
195
196
//////////////////////////
197
// Instruction printing //
198
//////////////////////////
199
200
/// Write an instruction.
201
fn writeInstr(out: *mut sexpr::Output, a: *mut alloc::Arena, blocks: *[super::Block], inst: super::Instr) {
202
    match inst {
203
        // Memory operations.
204
        case super::Instr::Reserve { dst, size, alignment } => {
205
            write(out, "reserve ");
206
            writeReg(out, a, dst);
207
            write(out, " ");
208
            writeVal(out, a, size);
209
            write(out, " ");
210
            write(out, formatU32(a, alignment));
211
        }
212
        case super::Instr::Load { dst, typ, src, offset } => {
213
            write(out, "load ");
214
            writeType(out, typ);
215
            write(out, " ");
216
            writeReg(out, a, dst);
217
            write(out, " ");
218
            writeReg(out, a, src);
219
            write(out, " ");
220
            write(out, formatI32(a, offset));
221
        }
222
        case super::Instr::Sload { dst, typ, src, offset } => {
223
            write(out, "sload ");
224
            writeType(out, typ);
225
            write(out, " ");
226
            writeReg(out, a, dst);
227
            write(out, " ");
228
            writeReg(out, a, src);
229
            write(out, " ");
230
            write(out, formatI32(a, offset));
231
        }
232
        case super::Instr::Store { typ, src, dst, offset } => {
233
            write(out, "store ");
234
            writeType(out, typ);
235
            write(out, " ");
236
            writeVal(out, a, src);
237
            write(out, " ");
238
            writeReg(out, a, dst);
239
            write(out, " ");
240
            write(out, formatI32(a, offset));
241
        }
242
        case super::Instr::Blit { dst, src, size } => {
243
            write(out, "blit ");
244
            writeReg(out, a, dst);
245
            write(out, " ");
246
            writeReg(out, a, src);
247
            write(out, " ");
248
            writeVal(out, a, size);
249
        }
250
        case super::Instr::Copy { dst, val } => {
251
            write(out, "copy ");
252
            writeReg(out, a, dst);
253
            write(out, " ");
254
            writeVal(out, a, val);
255
        }
256
257
        // ALU operations.
258
        case super::Instr::BinOp { op, dst, typ, a: va, b } =>
259
            writeTypedBinOp(out, a, binOpStr(op), typ, dst, va, b),
260
        case super::Instr::UnOp { op, dst, typ, a: va } =>
261
            writeTypedUnaryOp(out, a, unOpStr(op), typ, dst, va),
262
263
        // Conversion operations.
264
        case super::Instr::Zext { dst, typ, val } =>
265
            writeTypedUnaryOp(out, a, "zext", typ, dst, val),
266
        case super::Instr::Sext { dst, typ, val } =>
267
            writeTypedUnaryOp(out, a, "sext", typ, dst, val),
268
269
        // Call.
270
        case super::Instr::Call { dst, retTy, func, args } => {
271
            write(out, "call ");
272
            writeType(out, retTy);
273
            write(out, " ");
274
            if let d = dst {
275
                writeReg(out, a, d);
276
                write(out, " ");
277
            }
278
            writeVal(out, a, func);
279
            writeArgs(out, a, args);
280
        }
281
282
        // Terminators.
283
        case super::Instr::Ret { val } => {
284
            write(out, "ret");
285
            if let v = val {
286
                write(out, " ");
287
                writeVal(out, a, v);
288
            }
289
        }
290
        case super::Instr::Jmp { target, args } => {
291
            write(out, "jmp @");
292
            write(out, blocks[target].label);
293
            if args.len > 0 {
294
                writeArgs(out, a, args);
295
            }
296
        }
297
        case super::Instr::Br { op, typ, a: va, b: vb, thenTarget, thenArgs, elseTarget, elseArgs } => {
298
            write(out, "br.");
299
            match op {
300
                case super::CmpOp::Eq  => write(out, "eq"),
301
                case super::CmpOp::Ne  => write(out, "ne"),
302
                case super::CmpOp::Slt => write(out, "slt"),
303
                case super::CmpOp::Ult => write(out, "ult"),
304
            }
305
            write(out, " ");
306
            writeType(out, typ);
307
            write(out, " ");
308
            writeVal(out, a, va);
309
            write(out, " ");
310
            writeVal(out, a, vb);
311
            write(out, " @");
312
            write(out, blocks[thenTarget].label);
313
            if thenArgs.len > 0 {
314
                writeArgs(out, a, thenArgs);
315
            }
316
            write(out, " @");
317
            write(out, blocks[elseTarget].label);
318
            if elseArgs.len > 0 {
319
                writeArgs(out, a, elseArgs);
320
            }
321
        }
322
        case super::Instr::Switch { val, defaultTarget, defaultArgs, cases } => {
323
            write(out, "switch ");
324
            writeVal(out, a, val);
325
            for c in cases {
326
                write(out, " (");
327
                write(out, formatI64(a, c.value));
328
                write(out, " @");
329
                write(out, blocks[c.target].label);
330
                if c.args.len > 0 {
331
                    writeArgs(out, a, c.args);
332
                }
333
                write(out, ")");
334
            }
335
            write(out, " @");
336
            write(out, blocks[defaultTarget].label);
337
            if defaultArgs.len > 0 {
338
                writeArgs(out, a, defaultArgs);
339
            }
340
        }
341
        case super::Instr::Unreachable => {
342
            write(out, "unreachable");
343
        }
344
345
        // Intrinsics.
346
        case super::Instr::Ecall { dst, num, a0, a1, a2, a3 } => {
347
            write(out, "ecall ");
348
            writeReg(out, a, dst);
349
            write(out, " ");
350
            writeVal(out, a, num);
351
            write(out, " ");
352
            writeVal(out, a, a0);
353
            write(out, " ");
354
            writeVal(out, a, a1);
355
            write(out, " ");
356
            writeVal(out, a, a2);
357
            write(out, " ");
358
            writeVal(out, a, a3);
359
        }
360
        case super::Instr::Ebreak => {
361
            write(out, "ebreak");
362
        }
363
    }
364
}
365
366
/// Write a typed binary operation: `op type %dst %a %b`.
367
fn writeTypedBinOp(
368
    out: *mut sexpr::Output,
369
    a: *mut alloc::Arena,
370
    name: *[u8],
371
    typ: super::Type,
372
    dst: super::Reg,
373
    va: super::Val,
374
    vb: super::Val
375
) {
376
    write(out, name);
377
    write(out, " ");
378
    writeType(out, typ);
379
    write(out, " ");
380
    writeReg(out, a, dst);
381
    write(out, " ");
382
    writeVal(out, a, va);
383
    write(out, " ");
384
    writeVal(out, a, vb);
385
}
386
387
/// Write a typed unary operation: `op type %dst %val`.
388
fn writeTypedUnaryOp(
389
    out: *mut sexpr::Output,
390
    a: *mut alloc::Arena,
391
    name: *[u8],
392
    typ: super::Type,
393
    dst: super::Reg,
394
    val: super::Val
395
) {
396
    write(out, name);
397
    write(out, " ");
398
    writeType(out, typ);
399
    write(out, " ");
400
    writeReg(out, a, dst);
401
    write(out, " ");
402
    writeVal(out, a, val);
403
}
404
405
////////////////////
406
// Block printing //
407
////////////////////
408
409
/// Write a basic block.
410
fn writeBlock(out: *mut sexpr::Output, a: *mut alloc::Arena, blocks: *[super::Block], block: *super::Block) {
411
    // Block label.
412
    write(out, "  @");
413
    write(out, block.label);
414
415
    // Block parameters.
416
    if block.params.len > 0 {
417
        write(out, "(");
418
        writeParams(out, a, block.params);
419
        write(out, ")");
420
    }
421
    write(out, "\n");
422
423
    // Instructions.
424
    for instr in block.instrs {
425
        indent(out, 1);
426
        writeInstr(out, a, blocks, instr);
427
        write(out, ";\n");
428
    }
429
}
430
431
///////////////////////
432
// Function printing //
433
///////////////////////
434
435
/// Write a function.
436
fn writeFn(out: *mut sexpr::Output, a: *mut alloc::Arena, f: *super::Fn) {
437
    // Function signature.
438
    if f.isExtern {
439
        write(out, "extern ");
440
    }
441
    write(out, "fn ");
442
    writeType(out, f.returnType);
443
    write(out, " ");
444
    writeSymbol(out, f.name);
445
    write(out, "(");
446
    writeParams(out, a, f.params);
447
    write(out, ")");
448
449
    // Extern functions have no body.
450
    if f.isExtern {
451
        write(out, ";\n");
452
        return;
453
    }
454
    write(out, " {\n");
455
456
    // Blocks.
457
    for i in 0..f.blocks.len {
458
        if f.blocks[i].instrs.len > 0 {
459
            writeBlock(out, a, f.blocks, &f.blocks[i]);
460
        }
461
    }
462
    write(out, "}\n");
463
}
464
465
///////////////////
466
// Data printing //
467
///////////////////
468
469
/// Write a data item.
470
fn writeDataItem(out: *mut sexpr::Output, a: *mut alloc::Arena, item: super::DataItem) {
471
    match item {
472
        case super::DataItem::Val { typ, val } => {
473
            writeType(out, typ);
474
            write(out, " ");
475
            write(out, formatI64(a, val));
476
        }
477
        case super::DataItem::Sym(name) => {
478
            write(out, "sym ");
479
            writeSymbol(out, name);
480
        }
481
        case super::DataItem::Fn(name) => {
482
            write(out, "fn ");
483
            writeSymbol(out, name);
484
        }
485
        case super::DataItem::Str(s) => {
486
            write(out, "str ");
487
            sexpr::printStringTo(out, s);
488
        }
489
        case super::DataItem::Undef => {
490
            write(out, "undef");
491
        }
492
    }
493
}
494
495
/// Write a data value (item with optional repeat count).
496
fn writeDataValue(out: *mut sexpr::Output, a: *mut alloc::Arena, value: super::DataValue) {
497
    writeDataItem(out, a, value.item);
498
    if value.count > 1 {
499
        write(out, " * ");
500
        write(out, formatU32(a, value.count));
501
    }
502
}
503
504
/// Write global data.
505
fn writeData(out: *mut sexpr::Output, a: *mut alloc::Arena, d: super::Data) {
506
    write(out, "data ");
507
    if not d.readOnly {
508
        write(out, "mut ");
509
    }
510
    writeSymbol(out, d.name);
511
    write(out, " align ");
512
    write(out, formatU32(a, d.alignment));
513
    write(out, " {\n");
514
515
    for v in d.values {
516
        indent(out, 1);
517
        writeDataValue(out, a, v);
518
        write(out, ";\n");
519
    }
520
    write(out, "}\n");
521
}
522
523
//////////////////////
524
// Program printing //
525
//////////////////////
526
527
/// Print a program.
528
pub fn printProgram(out: *mut sexpr::Output, a: *mut alloc::Arena, program: *super::Program) {
529
    // Data declarations.
530
    for data, i in program.data {
531
        writeData(out, a, data);
532
        if i < program.data.len - 1 or program.fns.len > 0 {
533
            write(out, "\n");
534
        }
535
    }
536
    // Functions.
537
    for func, i in program.fns {
538
        writeFn(out, a, func);
539
        if i < program.fns.len - 1 {
540
            write(out, "\n");
541
        }
542
    }
543
}
544
545
/// Print a program to a buffer, returning the written slice.
546
pub fn printProgramToBuffer(
547
    program: *super::Program,
548
    arena: *mut alloc::Arena,
549
    buf: *mut [u8]
550
) -> *[u8] {
551
    let mut pos: u32 = 0;
552
    let mut out = sexpr::Output::Buffer { buf, pos: &mut pos };
553
    printProgram(&mut out, arena, program);
554
555
    return &buf[..pos];
556
}