compiler: Check call instruction printing

b1a85855d70c0606ec50c5b0ab7edbe6dfad45199c8778109dbbf6b7e2e2abaf
Alexis Sellier committed ago 1 parent cc046d63
lib/std/lang/il/printer.rad +19 -17
179 179
//////////////////////////
180 180
181 181
/// Write an instruction.
182 182
unsafe fn writeInstr(out: &mut opaque sexpr::Output, blocks: *unsafe [super::Block], inst: super::Instr) {
183 183
    match inst {
184 -
        // Call.
185 -
        case super::Instr::Call { dst, retTy, func, args } => {
186 -
            write(out, "call ");
187 -
            writeType(out, retTy);
188 -
            write(out, " ");
189 -
            if let d = dst {
190 -
                writeReg(out, d);
191 -
                write(out, " ");
192 -
            }
193 -
            writeVal(out, func);
194 -
            writeArgs(out, args);
195 -
        }
184 +
        case super::Instr::Call { args, .. } => writeOperandInstr(out, inst, args),
196 185
197 186
        case super::Instr::Jmp { target, args } => {
198 187
            write(out, "jmp ");
199 188
            writeTarget(out, blocks, target, args);
200 189
        }
228 217
                write(out, ")");
229 218
            }
230 219
            write(out, " ");
231 220
            writeTarget(out, blocks, defaultTarget, defaultArgs);
232 221
        }
233 -
        else => writeFixedInstr(out, inst),
222 +
        else => writeOperandInstr(out, inst, &[]),
234 223
    }
235 224
}
236 225
237 226
/// Write a target label and its optional block arguments.
238 227
fn writeTarget(out: &mut opaque sexpr::Output, blocks: &[super::Block], target: u32, args: &[super::Val]) {
241 230
    if args.len > 0 {
242 231
        writeArgs(out, args);
243 232
    }
244 233
}
245 234
246 -
/// Write an instruction whose operands need no graph traversal.
247 -
fn writeFixedInstr(out: &mut opaque sexpr::Output, inst: super::Instr) {
235 +
/// Write fixed operands and borrowed call arguments.
236 +
fn writeOperandInstr(out: &mut opaque sexpr::Output, inst: super::Instr, args: &[super::Val]) {
248 237
    match inst {
238 +
        // Call.
239 +
        case super::Instr::Call { dst, retTy, func, .. } => {
240 +
            write(out, "call ");
241 +
            writeType(out, retTy);
242 +
            write(out, " ");
243 +
            if let d = dst {
244 +
                writeReg(out, d);
245 +
                write(out, " ");
246 +
            }
247 +
            writeVal(out, func);
248 +
            writeArgs(out, args);
249 +
        }
250 +
249 251
        // Memory operations.
250 252
        case super::Instr::Reserve { dst, size, alignment } => {
251 253
            write(out, "reserve ");
252 254
            writeReg(out, dst);
253 255
            write(out, " ");
351 353
            write(out, "ebreak");
352 354
        }
353 355
        case super::Instr::MemoryFence => {
354 356
            write(out, "memory-fence");
355 357
        }
356 -
        case super::Instr::Call { .. }, super::Instr::Jmp { .. },
358 +
        case super::Instr::Jmp { .. },
357 359
             super::Instr::Br { .. }, super::Instr::Switch { .. } =>
358 -
            panic "writeFixedInstr: expected fixed operands",
360 +
            panic "writeOperandInstr: expected value operands",
359 361
    }
360 362
}
361 363
362 364
/// Write a typed binary operation: `op type %dst %a %b`.
363 365
fn writeTypedBinOp(
lib/std/lang/il/tests.rad +24 -1
1 -
//! Tests for RIL source register iteration.
1 +
//! Tests for RIL formatting and source register iteration.
2 2
3 3
use std::testing;
4 4
use std::lang::sexpr;
5 5
use std::lang::il::printer;
6 6
54 54
    try checkPrintedInstruction(super::Instr::DeviceWrite { typ: super::Type::W64, handle: source, offset: value, value: source }, "device-write w64 %2 -9223372036854775808 %2");
55 55
    try checkPrintedInstruction(super::Instr::Ebreak, "ebreak");
56 56
    try checkPrintedInstruction(super::Instr::MemoryFence, "memory-fence");
57 57
}
58 58
59 +
/// Call printing preserves destinations, callee forms, and argument order.
60 +
@test unsafe fn testCallPrinting() throws (testing::TestError) {
61 +
    let dst = super::Reg { n: 7 };
62 +
    let direct = super::Val::FnAddr("callee");
63 +
    let indirect = super::Val::Reg(super::Reg { n: 2 });
64 +
    let args = [super::Val::Imm(-1), super::Val::Reg(dst), super::Val::FnAddr("pkg::callback")];
65 +
    try checkPrintedInstruction(super::Instr::Call {
66 +
        retTy: super::Type::W8, dst: nil, func: direct, args: &[],
67 +
    }, "call w8 $callee()");
68 +
    try checkPrintedInstruction(super::Instr::Call {
69 +
        retTy: super::Type::W16, dst, func: direct, args: &[],
70 +
    }, "call w16 %7 $callee()");
71 +
    try checkPrintedInstruction(super::Instr::Call {
72 +
        retTy: super::Type::W32, dst: nil, func: indirect, args: &args[..1],
73 +
    }, "call w32 %2(-1)");
74 +
    try checkPrintedInstruction(super::Instr::Call {
75 +
        retTy: super::Type::W64, dst, func: indirect, args: &args[..],
76 +
    }, "call w64 %7 %2(-1, %7, $\"pkg::callback\")");
77 +
    try checkPrintedInstruction(super::Instr::Call {
78 +
        retTy: super::Type::W64, dst, func: super::Val::FnAddr("pkg::callee"), args: &args[..],
79 +
    }, "call w64 %7 $\"pkg::callee\"(-1, %7, $\"pkg::callback\")");
80 +
}
81 +
59 82
/// Control-flow printing preserves target labels and empty or populated arguments.
60 83
@test unsafe fn testControlFlowPrinting() throws (testing::TestError) {
61 84
    let value = super::Val::Reg(super::Reg { n: 3 });
62 85
    let other = super::Val::Imm(-7);
63 86
    let mut args = [value, other];