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