compiler: Check control-flow target printing
cc046d6315be9ba6aba1df8f0831072802b02f2854770d08a5ab3e6704a78fc9
1 parent
084f3c7e
lib/std/lang/il/printer.rad
+19 -25
| 193 | 193 | writeVal(out, func); |
|
| 194 | 194 | writeArgs(out, args); |
|
| 195 | 195 | } |
|
| 196 | 196 | ||
| 197 | 197 | case super::Instr::Jmp { target, args } => { |
|
| 198 | - | write(out, "jmp @"); |
|
| 199 | - | write(out, blocks[target].label); |
|
| 200 | - | if args.len > 0 { |
|
| 201 | - | writeArgs(out, args); |
|
| 202 | - | } |
|
| 198 | + | write(out, "jmp "); |
|
| 199 | + | writeTarget(out, blocks, target, args); |
|
| 203 | 200 | } |
|
| 204 | 201 | case super::Instr::Br { op, typ, a: va, b: vb, thenTarget, thenArgs, elseTarget, elseArgs } => { |
|
| 205 | 202 | write(out, "br."); |
|
| 206 | 203 | match op { |
|
| 207 | 204 | case super::CmpOp::Eq => write(out, "eq"), |
| 213 | 210 | writeType(out, typ); |
|
| 214 | 211 | write(out, " "); |
|
| 215 | 212 | writeVal(out, va); |
|
| 216 | 213 | write(out, " "); |
|
| 217 | 214 | writeVal(out, vb); |
|
| 218 | - | write(out, " @"); |
|
| 219 | - | write(out, blocks[thenTarget].label); |
|
| 220 | - | if thenArgs.len > 0 { |
|
| 221 | - | writeArgs(out, thenArgs); |
|
| 222 | - | } |
|
| 223 | - | write(out, " @"); |
|
| 224 | - | write(out, blocks[elseTarget].label); |
|
| 225 | - | if elseArgs.len > 0 { |
|
| 226 | - | writeArgs(out, elseArgs); |
|
| 227 | - | } |
|
| 215 | + | write(out, " "); |
|
| 216 | + | writeTarget(out, blocks, thenTarget, thenArgs); |
|
| 217 | + | write(out, " "); |
|
| 218 | + | writeTarget(out, blocks, elseTarget, elseArgs); |
|
| 228 | 219 | } |
|
| 229 | 220 | case super::Instr::Switch { val, defaultTarget, defaultArgs, cases } => { |
|
| 230 | 221 | write(out, "switch "); |
|
| 231 | 222 | writeVal(out, val); |
|
| 232 | 223 | for c in cases { |
|
| 233 | 224 | write(out, " ("); |
|
| 234 | 225 | writeI64(out, c.value); |
|
| 235 | - | write(out, " @"); |
|
| 236 | - | write(out, blocks[c.target].label); |
|
| 237 | - | if c.args.len > 0 { |
|
| 238 | - | writeArgs(out, c.args); |
|
| 239 | - | } |
|
| 226 | + | write(out, " "); |
|
| 227 | + | writeTarget(out, blocks, c.target, c.args); |
|
| 240 | 228 | write(out, ")"); |
|
| 241 | 229 | } |
|
| 242 | - | write(out, " @"); |
|
| 243 | - | write(out, blocks[defaultTarget].label); |
|
| 244 | - | if defaultArgs.len > 0 { |
|
| 245 | - | writeArgs(out, defaultArgs); |
|
| 246 | - | } |
|
| 230 | + | write(out, " "); |
|
| 231 | + | writeTarget(out, blocks, defaultTarget, defaultArgs); |
|
| 247 | 232 | } |
|
| 248 | 233 | else => writeFixedInstr(out, inst), |
|
| 249 | 234 | } |
|
| 250 | 235 | } |
|
| 251 | 236 | ||
| 237 | + | /// Write a target label and its optional block arguments. |
|
| 238 | + | fn writeTarget(out: &mut opaque sexpr::Output, blocks: &[super::Block], target: u32, args: &[super::Val]) { |
|
| 239 | + | write(out, "@"); |
|
| 240 | + | write(out, blocks[target].label); |
|
| 241 | + | if args.len > 0 { |
|
| 242 | + | writeArgs(out, args); |
|
| 243 | + | } |
|
| 244 | + | } |
|
| 245 | + | ||
| 252 | 246 | /// Write an instruction whose operands need no graph traversal. |
|
| 253 | 247 | fn writeFixedInstr(out: &mut opaque sexpr::Output, inst: super::Instr) { |
|
| 254 | 248 | match inst { |
|
| 255 | 249 | // Memory operations. |
|
| 256 | 250 | case super::Instr::Reserve { dst, size, alignment } => { |
lib/std/lang/il/tests.rad
+35 -0
| 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 | + | /// Control-flow printing preserves target labels and empty or populated arguments. |
|
| 60 | + | @test unsafe fn testControlFlowPrinting() throws (testing::TestError) { |
|
| 61 | + | let value = super::Val::Reg(super::Reg { n: 3 }); |
|
| 62 | + | let other = super::Val::Imm(-7); |
|
| 63 | + | let mut args = [value, other]; |
|
| 64 | + | try checkPrintedInstruction(super::Instr::Jmp { target: 0, args: &mut [] }, "jmp @entry"); |
|
| 65 | + | try checkPrintedInstruction(super::Instr::Jmp { target: 0, args: &mut args[..] }, "jmp @entry(%3, -7)"); |
|
| 66 | + | try checkPrintedInstruction(super::Instr::Br { |
|
| 67 | + | op: super::CmpOp::Eq, typ: super::Type::W8, a: value, b: other, |
|
| 68 | + | thenTarget: 0, thenArgs: &mut [], elseTarget: 0, elseArgs: &mut [], |
|
| 69 | + | }, "br.eq w8 %3 -7 @entry @entry"); |
|
| 70 | + | try checkPrintedInstruction(super::Instr::Br { |
|
| 71 | + | op: super::CmpOp::Ne, typ: super::Type::W16, a: value, b: other, |
|
| 72 | + | thenTarget: 0, thenArgs: &mut args[..], elseTarget: 0, elseArgs: &mut [], |
|
| 73 | + | }, "br.ne w16 %3 -7 @entry(%3, -7) @entry"); |
|
| 74 | + | try checkPrintedInstruction(super::Instr::Br { |
|
| 75 | + | op: super::CmpOp::Slt, typ: super::Type::W32, a: value, b: other, |
|
| 76 | + | thenTarget: 0, thenArgs: &mut [], elseTarget: 0, elseArgs: &mut args[..], |
|
| 77 | + | }, "br.slt w32 %3 -7 @entry @entry(%3, -7)"); |
|
| 78 | + | try checkPrintedInstruction(super::Instr::Br { |
|
| 79 | + | op: super::CmpOp::Ult, typ: super::Type::W64, a: value, b: other, |
|
| 80 | + | thenTarget: 0, thenArgs: &mut args[..], elseTarget: 0, elseArgs: &mut args[..], |
|
| 81 | + | }, "br.ult w64 %3 -7 @entry(%3, -7) @entry(%3, -7)"); |
|
| 82 | + | let mut cases = [ |
|
| 83 | + | super::SwitchCase { value: -9223372036854775808, target: 0, args: &mut [] }, |
|
| 84 | + | super::SwitchCase { value: 9223372036854775807, target: 0, args: &mut args[..] }, |
|
| 85 | + | ]; |
|
| 86 | + | try checkPrintedInstruction(super::Instr::Switch { |
|
| 87 | + | val: value, defaultTarget: 0, defaultArgs: &mut [], cases: &mut [], |
|
| 88 | + | }, "switch %3 @entry"); |
|
| 89 | + | try checkPrintedInstruction(super::Instr::Switch { |
|
| 90 | + | val: value, defaultTarget: 0, defaultArgs: &mut args[..], cases: &mut cases[..], |
|
| 91 | + | }, "switch %3 (-9223372036854775808 @entry) (9223372036854775807 @entry(%3, -7)) @entry(%3, -7)"); |
|
| 92 | + | } |
|
| 93 | + | ||
| 59 | 94 | /// Buffered output truncates at capacity and preserves adjacent storage. |
|
| 60 | 95 | @test fn testSafePrintBuffer() throws (testing::TestError) { |
|
| 61 | 96 | let mut storage: [u8; 7] = [42; 7]; |
|
| 62 | 97 | let bytes: 'output = &mut storage[1..6] in { |
|
| 63 | 98 | let mut out = sexpr::Buffer 'output { buf: bytes, pos: 0 }; |