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