lib/std/lang/il/printer.rad 14.7 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
fn writeU32(out: *mut sexpr::Output, val: u32) {
12
    let mut digits: [u8; 10] = undefined;
13
    write(out, fmt::formatU32(val, &mut digits[..]));
14
}
15
16
/// Write an `i32` before its stack buffer leaves scope.
17
fn writeI32(out: *mut sexpr::Output, val: i32) {
18
    let mut digits: [u8; 12] = undefined;
19
    write(out, fmt::formatI32(val, &mut digits[..]));
20
}
21
22
/// Write an `i64` before its stack buffer leaves scope.
23
fn writeI64(out: *mut sexpr::Output, val: i64) {
24
    let mut digits: [u8; 20] = undefined;
25
    write(out, fmt::formatI64(val, &mut digits[..]));
26
}
27
28
/////////////////////
29
// Output helpers  //
30
/////////////////////
31
32
/// Write a string to the output.
33
fn write(out: *mut sexpr::Output, s: *[u8]) {
34
    sexpr::write(out, s);
35
}
36
37
/// Emit indentation.
38
fn indent(out: *mut sexpr::Output, depth: u32) {
39
    for _ in 0..depth {
40
        write(out, "    ");
41
    }
42
}
43
44
/// Check if name contains the path separator.
45
fn needsQuoting(name: *[u8]) -> bool {
46
    for ch in name {
47
        if ch == ':' {
48
            return true;
49
        }
50
    }
51
    return false;
52
}
53
54
/// Write a symbol name. Simple names use `$name`, qualified names use `$"name"`.
55
fn writeSymbol(out: *mut sexpr::Output, name: *[u8]) {
56
    write(out, "$");
57
    if needsQuoting(name) {
58
        write(out, "\"");
59
        write(out, name);
60
        write(out, "\"");
61
    } else {
62
        write(out, name);
63
    }
64
}
65
66
////////////////////
67
// Type printing  //
68
////////////////////
69
70
/// Get the string representation of a type.
71
fn typeStr(typ: super::Type) -> *[u8] {
72
    match typ {
73
        case super::Type::W8 => return "w8",
74
        case super::Type::W16 => return "w16",
75
        case super::Type::W32 => return "w32",
76
        case super::Type::W64 => return "w64",
77
    }
78
}
79
80
/// Write a type.
81
fn writeType(out: *mut sexpr::Output, typ: super::Type) {
82
    write(out, typeStr(typ));
83
}
84
85
////////////////////////
86
// Operation printing //
87
////////////////////////
88
89
/// Get the string representation of a binary ALU operation.
90
fn binOpStr(op: super::BinOp) -> *[u8] {
91
    match op {
92
        case super::BinOp::Add => return "add",
93
        case super::BinOp::Sub => return "sub",
94
        case super::BinOp::Mul => return "mul",
95
        case super::BinOp::Sdiv => return "sdiv",
96
        case super::BinOp::Udiv => return "udiv",
97
        case super::BinOp::Srem => return "srem",
98
        case super::BinOp::Urem => return "urem",
99
        case super::BinOp::Eq => return "eq",
100
        case super::BinOp::Ne => return "ne",
101
        case super::BinOp::Slt => return "slt",
102
        case super::BinOp::Sge => return "sge",
103
        case super::BinOp::Ult => return "ult",
104
        case super::BinOp::Uge => return "uge",
105
        case super::BinOp::And => return "and",
106
        case super::BinOp::Or => return "or",
107
        case super::BinOp::Xor => return "xor",
108
        case super::BinOp::Shl => return "shl",
109
        case super::BinOp::Sshr => return "sshr",
110
        case super::BinOp::Ushr => return "ushr",
111
    }
112
}
113
114
/// Get the string representation of a unary ALU operation.
115
fn unOpStr(op: super::UnOp) -> *[u8] {
116
    match op {
117
        case super::UnOp::Neg => return "neg",
118
        case super::UnOp::Not => return "not",
119
    }
120
}
121
122
////////////////////
123
// Value printing //
124
////////////////////
125
126
/// Write a value (register, immediate, symbol, or undefined).
127
fn writeVal(out: *mut sexpr::Output, val: super::Val) {
128
    match val {
129
        case super::Val::Reg(reg) => writeReg(out, reg),
130
        case super::Val::Imm(v) => writeI64(out, v),
131
        case super::Val::DataSym(name) => writeSymbol(out, name),
132
        case super::Val::FnAddr(name) => writeSymbol(out, name),
133
        case super::Val::Undef => write(out, "undefined"),
134
    }
135
}
136
137
/// Write a register in one operation before its stack buffer leaves scope.
138
fn writeReg(out: *mut sexpr::Output, reg: super::Reg) {
139
    let mut buffer: [u8; 11] = undefined;
140
    let digits = fmt::formatU32(reg.n, &mut buffer[1..]);
141
    let start = buffer.len - digits.len - 1;
142
    set buffer[start] = '%';
143
    write(out, &buffer[start..]);
144
}
145
146
/// Write a comma-separated argument list in parentheses.
147
fn writeArgs(out: *mut sexpr::Output, args: *[super::Val]) {
148
    write(out, "(");
149
    for arg, i in args {
150
        if i > 0 {
151
            write(out, ", ");
152
        }
153
        writeVal(out, arg);
154
    }
155
    write(out, ")");
156
}
157
158
/// Write a typed parameter (e.g., `w32 %0`).
159
fn writeParam(out: *mut sexpr::Output, param: super::Param) {
160
    writeType(out, param.type);
161
    write(out, " ");
162
    writeReg(out, param.value);
163
}
164
165
/// Write a comma-separated parameter list.
166
fn writeParams(out: *mut sexpr::Output, params: *[super::Param]) {
167
    for param, i in params {
168
        if i > 0 {
169
            write(out, ", ");
170
        }
171
        writeParam(out, param);
172
    }
173
}
174
175
//////////////////////////
176
// Instruction printing //
177
//////////////////////////
178
179
/// Write an instruction.
180
fn writeInstr(out: *mut sexpr::Output, blocks: *[super::Block], inst: super::Instr) {
181
    match inst {
182
        // Memory operations.
183
        case super::Instr::Reserve { dst, size, alignment } => {
184
            write(out, "reserve ");
185
            writeReg(out, dst);
186
            write(out, " ");
187
            writeVal(out, size);
188
            write(out, " ");
189
            writeU32(out, alignment);
190
        }
191
        case super::Instr::Load { dst, typ, src, offset } => {
192
            write(out, "load ");
193
            writeType(out, typ);
194
            write(out, " ");
195
            writeReg(out, dst);
196
            write(out, " ");
197
            writeReg(out, src);
198
            write(out, " ");
199
            writeI32(out, offset);
200
        }
201
        case super::Instr::Sload { dst, typ, src, offset } => {
202
            write(out, "sload ");
203
            writeType(out, typ);
204
            write(out, " ");
205
            writeReg(out, dst);
206
            write(out, " ");
207
            writeReg(out, src);
208
            write(out, " ");
209
            writeI32(out, offset);
210
        }
211
        case super::Instr::Store { typ, src, dst, offset } => {
212
            write(out, "store ");
213
            writeType(out, typ);
214
            write(out, " ");
215
            writeVal(out, src);
216
            write(out, " ");
217
            writeReg(out, dst);
218
            write(out, " ");
219
            writeI32(out, offset);
220
        }
221
        case super::Instr::Blit { dst, src, size, alignment } => {
222
            write(out, "blit ");
223
            writeReg(out, dst);
224
            write(out, " ");
225
            writeReg(out, src);
226
            write(out, " ");
227
            writeVal(out, size);
228
            write(out, " ");
229
            writeU32(out, alignment);
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
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
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
fn writeBlock(out: *mut sexpr::Output, blocks: *[super::Block], block: *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
fn writeFn(out: *mut sexpr::Output, f: *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
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
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
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 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 fn printProgramToBuffer(
529
    program: *super::Program,
530
    buf: *mut [u8]
531
) -> *[u8] {
532
    let mut pos: u32 = 0;
533
    let mut out = sexpr::Output::Buffer { buf, pos: &mut pos };
534
    printProgram(&mut out, program);
535
536
    return &buf[..pos];
537
}