lib/std/lang/il/printer.rad 15.7 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
    set 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
        case super::Instr::MemoryFence => {
364
            write(out, "memory-fence");
365
        }
366
    }
367
}
368
369
/// Write a typed binary operation: `op type %dst %a %b`.
370
fn writeTypedBinOp(
371
    out: *mut sexpr::Output,
372
    a: *mut alloc::Arena,
373
    name: *[u8],
374
    typ: super::Type,
375
    dst: super::Reg,
376
    va: super::Val,
377
    vb: super::Val
378
) {
379
    write(out, name);
380
    write(out, " ");
381
    writeType(out, typ);
382
    write(out, " ");
383
    writeReg(out, a, dst);
384
    write(out, " ");
385
    writeVal(out, a, va);
386
    write(out, " ");
387
    writeVal(out, a, vb);
388
}
389
390
/// Write a typed unary operation: `op type %dst %val`.
391
fn writeTypedUnaryOp(
392
    out: *mut sexpr::Output,
393
    a: *mut alloc::Arena,
394
    name: *[u8],
395
    typ: super::Type,
396
    dst: super::Reg,
397
    val: super::Val
398
) {
399
    write(out, name);
400
    write(out, " ");
401
    writeType(out, typ);
402
    write(out, " ");
403
    writeReg(out, a, dst);
404
    write(out, " ");
405
    writeVal(out, a, val);
406
}
407
408
////////////////////
409
// Block printing //
410
////////////////////
411
412
/// Write a basic block.
413
fn writeBlock(out: *mut sexpr::Output, a: *mut alloc::Arena, blocks: *[super::Block], block: *super::Block) {
414
    // Block label.
415
    write(out, "  @");
416
    write(out, block.label);
417
418
    // Block parameters.
419
    if block.params.len > 0 {
420
        write(out, "(");
421
        writeParams(out, a, block.params);
422
        write(out, ")");
423
    }
424
    write(out, "\n");
425
426
    // Instructions.
427
    for instr in block.instrs {
428
        indent(out, 1);
429
        writeInstr(out, a, blocks, instr);
430
        write(out, ";\n");
431
    }
432
}
433
434
///////////////////////
435
// Function printing //
436
///////////////////////
437
438
/// Write a function.
439
fn writeFn(out: *mut sexpr::Output, a: *mut alloc::Arena, f: *super::Fn) {
440
    // Function signature.
441
    if f.isExtern {
442
        write(out, "extern ");
443
    }
444
    write(out, "fn ");
445
    writeType(out, f.returnType);
446
    write(out, " ");
447
    writeSymbol(out, f.name);
448
    write(out, "(");
449
    writeParams(out, a, f.params);
450
    write(out, ")");
451
452
    // Extern functions have no body.
453
    if f.isExtern {
454
        write(out, ";\n");
455
        return;
456
    }
457
    write(out, " {\n");
458
459
    // Blocks.
460
    for i in 0..f.blocks.len {
461
        if f.blocks[i].instrs.len > 0 {
462
            writeBlock(out, a, f.blocks, &f.blocks[i]);
463
        }
464
    }
465
    write(out, "}\n");
466
}
467
468
///////////////////
469
// Data printing //
470
///////////////////
471
472
/// Write a data item.
473
fn writeDataItem(out: *mut sexpr::Output, a: *mut alloc::Arena, item: super::DataItem) {
474
    match item {
475
        case super::DataItem::Val { typ, val } => {
476
            writeType(out, typ);
477
            write(out, " ");
478
            write(out, formatI64(a, val));
479
        }
480
        case super::DataItem::Sym(name) => {
481
            write(out, "sym ");
482
            writeSymbol(out, name);
483
        }
484
        case super::DataItem::Fn(name) => {
485
            write(out, "fn ");
486
            writeSymbol(out, name);
487
        }
488
        case super::DataItem::Str(s) => {
489
            write(out, "str ");
490
            sexpr::printStringTo(out, s);
491
        }
492
        case super::DataItem::Undef => {
493
            write(out, "undef");
494
        }
495
    }
496
}
497
498
/// Write a data value (item with optional repeat count).
499
fn writeDataValue(out: *mut sexpr::Output, a: *mut alloc::Arena, value: super::DataValue) {
500
    writeDataItem(out, a, value.item);
501
    if value.count > 1 {
502
        write(out, " * ");
503
        write(out, formatU32(a, value.count));
504
    }
505
}
506
507
/// Write global data.
508
fn writeData(out: *mut sexpr::Output, a: *mut alloc::Arena, d: super::Data) {
509
    write(out, "data ");
510
    if not d.readOnly {
511
        write(out, "mut ");
512
    }
513
    writeSymbol(out, d.name);
514
    write(out, " align ");
515
    write(out, formatU32(a, d.alignment));
516
    write(out, " {\n");
517
518
    for v in d.values {
519
        indent(out, 1);
520
        writeDataValue(out, a, v);
521
        write(out, ";\n");
522
    }
523
    write(out, "}\n");
524
}
525
526
//////////////////////
527
// Program printing //
528
//////////////////////
529
530
/// Print a program.
531
export fn printProgram(out: *mut sexpr::Output, a: *mut alloc::Arena, program: *super::Program) {
532
    // Data declarations.
533
    for data, i in program.data {
534
        writeData(out, a, data);
535
        if i < program.data.len - 1 or program.fns.len > 0 {
536
            write(out, "\n");
537
        }
538
    }
539
    // Functions.
540
    for func, i in program.fns {
541
        writeFn(out, a, func);
542
        if i < program.fns.len - 1 {
543
            write(out, "\n");
544
        }
545
    }
546
}
547
548
/// Print a program to a buffer, returning the written slice.
549
export fn printProgramToBuffer(
550
    program: *super::Program,
551
    arena: *mut alloc::Arena,
552
    buf: *mut [u8]
553
) -> *[u8] {
554
    let mut pos: u32 = 0;
555
    let mut out = sexpr::Output::Buffer { buf, pos: &mut pos };
556
    printProgram(&mut out, arena, program);
557
558
    return &buf[..pos];
559
}