1 /** 2 Copyright: Copyright (c) 2017-2019 Andrey Penechko. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 7 /// Convertion of function IR to textual representation 8 module vox.ir.ir_dump; 9 10 import std.stdio; 11 import std.format : formattedWrite; 12 13 import vox.all; 14 15 struct IrDumpContext 16 { 17 CompilationContext* context; 18 IrFunction* ir; 19 FuncDumpSettings* settings; // nullable, uses default if null 20 TextSink* sink; // nullable, writes to stdout if null 21 LivenessInfo* liveness; // nullable, doesn't print liveness if null 22 string passName; // string to printed after function signature. Optional 23 } 24 25 struct InstrPrintInfo 26 { 27 CompilationContext* context; 28 TextSink* sink; 29 IrFunction* ir; 30 IrIndex blockIndex; 31 IrBasicBlock* block; 32 IrIndex instrIndex; 33 IrInstrHeader* instrHeader; 34 FuncDumpSettings* settings; 35 IrDumpHandlers* handlers; 36 void dumpInstr() { handlers.instrDumper(this); } 37 } 38 39 struct IrDumpHandlers 40 { 41 InstructionDumper instrDumper; 42 IrIndexDumper indexDumper; 43 } 44 45 struct IrIndexDump 46 { 47 this(IrIndex index, ref InstrPrintInfo printInfo) { 48 this.index = index; 49 this.context = printInfo.context; 50 this.instrSet = printInfo.ir.instructionSet; 51 } 52 this(IrIndex index, CompilationContext* c, IrInstructionSet instrSet) { 53 this.index = index; 54 this.context = c; 55 this.instrSet = instrSet; 56 } 57 this(IrIndex index, CompilationContext* context, IrFunction* ir) { 58 this.index = index; 59 this.context = context; 60 this.instrSet = ir.instructionSet; 61 } 62 this(IrIndex index, IrBuilder* builder) { 63 this.index = index; 64 this.context = builder.context; 65 this.instrSet = builder.ir.instructionSet; 66 } 67 IrIndex index; 68 IrInstructionSet instrSet; 69 CompilationContext* context; 70 71 void toString(scope void delegate(const(char)[]) sink) { 72 instrSetDumpHandlers[instrSet].indexDumper(sink, *context, index); 73 } 74 } 75 76 alias InstructionDumper = void function(ref InstrPrintInfo p); 77 alias IrIndexDumper = void function(scope void delegate(const(char)[]) sink, ref CompilationContext, IrIndex); 78 79 struct FuncDumpSettings 80 { 81 bool printVars = false; 82 bool printBlockFlags = false; 83 bool printBlockIns = true; 84 bool printBlockOuts = false; 85 bool printBlockRefs = false; 86 bool printInstrIndexEnabled = true; 87 bool printLivenessLinearIndex = false; 88 bool printVregLiveness = false; 89 bool printPregLiveness = false; 90 bool printUses = true; 91 bool printLiveness = false; 92 bool printPassName = true; 93 bool escapeForDot = false; 94 } 95 96 IrDumpHandlers[] instrSetDumpHandlers = [ 97 IrDumpHandlers(&dumpIrInstr, &dumpIrIndex), 98 IrDumpHandlers(&dumpAmd64Instr, &dumpLirAmd64Index), 99 ]; 100 101 void dumpTypes(ref TextSink sink, ref CompilationContext ctx) 102 { 103 IrIndex type = ctx.types.firstType; 104 while (type.isDefined) 105 { 106 sink.putfln("%s", IrTypeDump(type, ctx)); 107 type = ctx.types.get!IrTypeHeader(type).nextType; 108 } 109 } 110 111 void dumpFunction(CompilationContext* context, IrFunction* ir, string passName = null) 112 { 113 IrDumpContext dumpCtx = { context : context, ir : ir, passName : passName }; 114 dumpFunction(&dumpCtx); 115 } 116 117 void dumpFunction(IrDumpContext* c) 118 { 119 assert(c.context, "context is null"); 120 assert(c.ir, "ir is null"); 121 122 bool defaultSink = false; 123 TextSink sink; 124 FuncDumpSettings settings; 125 126 if (c.sink is null) { 127 defaultSink = true; 128 c.sink = &sink; 129 } 130 131 if (c.settings is null) { 132 c.settings = &settings; 133 } 134 135 dumpFunctionImpl(c); 136 137 if (defaultSink) writeln(sink.text); 138 } 139 140 void dumpFunctionImpl(IrDumpContext* c) 141 { 142 IrFunction* ir = c.ir; 143 TextSink* sink = c.sink; 144 CompilationContext* ctx = c.context; 145 FuncDumpSettings* settings = c.settings; 146 LivenessInfo* liveness = c.liveness; 147 148 InstrPrintInfo printer; 149 printer.context = ctx; 150 printer.sink = sink; 151 printer.ir = ir; 152 printer.handlers = &instrSetDumpHandlers[ir.instructionSet]; 153 printer.settings = settings; 154 155 sink.put("func "); 156 // results 157 auto funcType = &ctx.types.get!IrTypeFunction(ir.type); 158 foreach(i, result; funcType.resultTypes) { 159 if (i > 0) sink.put(", "); 160 sink.putf("%s", IrIndexDump(result, printer)); 161 } 162 if (funcType.numResults > 0) sink.put(" "); 163 sink.put(ctx.idString(ir.name)); 164 165 // parameters 166 sink.put("("); 167 foreach(i, param; funcType.parameterTypes) { 168 if (i > 0) sink.put(", "); 169 sink.putf("%s", IrIndexDump(param, printer)); 170 } 171 sink.put(")"); 172 sink.putf(` %s bytes ir:"%s"`, ir.byteLength, instr_set_names[ir.instructionSet]); 173 if (settings.printPassName && c.passName.length) { 174 sink.put(` pass:"`); 175 sink.put(c.passName); 176 sink.put(`"`); 177 } 178 sink.putln(" {"); 179 180 int indexPadding = max(ir.numBasicBlocks, ir.numInstructions).numDigitsInNumber10; 181 int liveIndexPadding = 0; 182 if (liveness) liveIndexPadding = liveness.maxLinearIndex.numDigitsInNumber10; 183 184 void printInstrLiveness(IrIndex linearKeyIndex, IrIndex instrIndex) { 185 if (!settings.printLiveness) return; 186 if (liveness is null) return; 187 188 uint linearInstrIndex = liveness.linearIndices[linearKeyIndex]; 189 190 if (settings.printPregLiveness) 191 { 192 foreach(ref interval; liveness.physicalIntervals) 193 { 194 if (interval.coversPosition(linearInstrIndex)) 195 sink.put("|"); 196 else 197 sink.put(" "); 198 } 199 if (settings.printVregLiveness) sink.put("#"); 200 } 201 202 size_t[] blockLiveIn; 203 if (instrIndex.isBasicBlock) 204 { 205 blockLiveIn = liveness.bitmap.blockLiveInBuckets(instrIndex); 206 } 207 208 if (settings.printVregLiveness) 209 foreach(ref LiveInterval interval; liveness.virtualIntervals) 210 { 211 auto vreg = ir.getVirtReg(interval.definition); 212 213 if (interval.hasUseAt(linearInstrIndex)) 214 { 215 if (vreg.definition == instrIndex) // we are printing at definition site 216 sink.put("D"); // virtual register is defined by this instruction / phi 217 else 218 { 219 // some use 220 if (instrIndex.isPhi) 221 { 222 // some phi 223 if (vreg.users.contains(ir, instrIndex)) 224 sink.put("U"); // phi uses current vreg 225 else 226 sink.put("|"); // phi doesn't use current phi 227 } 228 else if (instrIndex.isBasicBlock) 229 { 230 // phi uses are located on the next basic block linear position 231 // this is a use in phi function 232 IrIndex prevBlock = ir.getBlock(instrIndex).prevBlock; 233 enum UseState : ubyte { 234 none = 0b00, 235 above = 0b01, 236 below = 0b10, 237 above_and_below = 0b11 238 } 239 UseState useState; 240 foreach (index, uint numUses; vreg.users.range(ir)) 241 { 242 if (index.isPhi) 243 { 244 IrPhi* phi = ir.getPhi(index); 245 IrIndex[] preds = ir.getBlock(phi.blockIndex).predecessors.data(ir); 246 foreach(size_t arg_i, IrIndex phiArg; phi.args(ir)) 247 { 248 // we only want phi functions that are in blocks that have this block as predecessor 249 if (preds[arg_i] == prevBlock && phiArg == interval.definition) 250 { 251 uint phiPos = liveness.linearIndices[phi.blockIndex]; 252 if (phiPos < linearInstrIndex) 253 useState |= UseState.above; // vreg is used by phi above 254 else 255 useState |= UseState.below; // vreg is used by phi below 256 } 257 } 258 } 259 } 260 final switch(useState) { 261 case UseState.none: sink.put(" "); break; 262 case UseState.above: sink.put("^"); break; 263 case UseState.below: sink.put("v"); break; 264 case UseState.above_and_below: sink.put("x"); break; 265 } 266 } 267 else 268 sink.put("U"); 269 } 270 } 271 else if (interval.coversPosition(linearInstrIndex)) 272 { 273 if (vreg.definition == instrIndex) 274 sink.put("D"); // virtual register is defined by this instruction / phi 275 else 276 { 277 if (instrIndex.isBasicBlock) 278 { 279 if (blockLiveIn.getBitAt(interval.definition.storageUintIndex)) 280 sink.put("+"); // virtual register is in "live in" of basic block 281 else 282 sink.put(" "); // phi result 283 } 284 else 285 sink.put("|"); // virtual register is live in this position 286 } 287 } 288 else 289 { 290 if (instrIndex.isPhi) 291 { 292 if (vreg.users.contains(ir, instrIndex)) 293 sink.put("U"); 294 else 295 sink.put(" "); 296 } 297 else 298 sink.put(" "); 299 } 300 } 301 302 if (settings.printLivenessLinearIndex) 303 { 304 sink.putf(" %*s| ", liveIndexPadding, linearInstrIndex); 305 } 306 } 307 308 void printInstrIndex(IrIndex someIndex) { 309 import std.range : repeat; 310 if (!settings.printInstrIndexEnabled) return; 311 if (someIndex.isInstruction) 312 sink.putf("%*s|", indexPadding, someIndex.storageUintIndex); 313 else 314 sink.putf("%s|", ' '.repeat(indexPadding)); 315 } 316 317 void printRegUses(IrIndex result) { 318 if (!result.isVirtReg) return; 319 auto vreg = ir.getVirtReg(result); 320 sink.put(" users ["); 321 uint i = 0; 322 foreach (IrIndex user, uint numUses; vreg.users.range(ir)) 323 { 324 if (i > 0) sink.put(", "); 325 sink.putf("%s", IrIndexDump(user, printer)); 326 if (numUses > 1) sink.putf(":%s", numUses); 327 ++i; 328 } 329 sink.put("]"); 330 } 331 332 IrIndex blockIndex = ir.entryBasicBlock; 333 IrBasicBlock* block; 334 while (blockIndex.isDefined) 335 { 336 if (!blockIndex.isBasicBlock) 337 { 338 sink.putfln(" invalid block %s", IrIndexDump(blockIndex, printer)); 339 break; 340 } 341 342 block = ir.getBlock(blockIndex); 343 scope(exit) blockIndex = block.nextBlock; 344 345 printer.blockIndex = blockIndex; 346 printer.block = block; 347 348 printInstrLiveness(blockIndex, blockIndex); 349 printInstrIndex(blockIndex); 350 sink.putf(" %s", IrIndexDump(blockIndex, printer)); 351 352 if (settings.printBlockFlags) 353 { 354 if (block.isSealed) sink.put(" S"); 355 else sink.put(" ."); 356 357 if (block.isFinished) sink.put("F"); 358 else sink.put("."); 359 360 if (block.isLoopHeader) sink.put("L"); 361 else sink.put("."); 362 } 363 364 if (settings.printBlockIns && block.predecessors.length > 0) 365 { 366 sink.putf(" in("); 367 foreach(i, predIndex; block.predecessors.range(ir)) { 368 if (i > 0) sink.put(", "); 369 sink.putf("%s", IrIndexDump(predIndex, printer)); 370 } 371 sink.put(")"); 372 } 373 if (settings.printBlockOuts && block.successors.length > 0) 374 { 375 sink.putf(" out("); 376 foreach(i, succIndex; block.successors.range(ir)) { 377 if (i > 0) sink.put(", "); 378 sink.putf("%s", IrIndexDump(succIndex, printer)); 379 } 380 sink.put(")"); 381 } 382 sink.putln; 383 384 // phis 385 IrIndex phiIndex = block.firstPhi; 386 IrPhi* phi; 387 while (phiIndex.isDefined) 388 { 389 if (!phiIndex.isPhi) 390 { 391 sink.putfln(" invalid phi %s", IrIndexDump(phiIndex, printer)); 392 break; 393 } 394 395 phi = ir.getPhi(phiIndex); 396 scope(exit) phiIndex = phi.nextPhi; 397 398 printInstrLiveness(blockIndex, phiIndex); 399 printInstrIndex(phiIndex); 400 sink.putf(" %s %s = %s(", 401 IrIndexDump(phi.result, printer), 402 IrIndexDump(ir.getVirtReg(phi.result).type, printer), 403 IrIndexDump(phiIndex, printer)); 404 IrIndex[] phiPreds = ir.getBlock(phi.blockIndex).predecessors.data(ir); 405 foreach(size_t arg_i, ref IrIndex phiArg; phi.args(ir)) 406 { 407 if (arg_i > 0) sink.put(", "); 408 sink.putf("%s", IrIndexDump(phiPreds[arg_i], printer)); 409 dumpArg(phiArg, printer); 410 } 411 sink.put(")"); 412 if (settings.printUses) printRegUses(phi.result); 413 sink.putln; 414 } 415 416 // instrs 417 foreach(IrIndex instrIndex, ref IrInstrHeader instrHeader; block.instructions(ir)) 418 { 419 printInstrLiveness(instrIndex, instrIndex); 420 printInstrIndex(instrIndex); 421 422 // print instr 423 printer.instrIndex = instrIndex; 424 printer.instrHeader = &instrHeader; 425 426 printer.dumpInstr(); 427 428 if (settings.printUses && instrHeader.hasResult) printRegUses(instrHeader.result(ir)); 429 sink.putln; 430 } 431 } 432 433 sink.putln("}"); 434 } 435 436 void dumpFunctionCFG(IrFunction* ir, ref TextSink sink, CompilationContext* ctx, ref FuncDumpSettings settings) 437 { 438 settings.escapeForDot = true; 439 sink.put(`digraph "`); 440 sink.put("function "); 441 sink.put(ctx.idString(ir.name)); 442 sink.putfln(`() %s bytes" {`, ir.byteLength * uint.sizeof); 443 int indexPadding = ir.numInstructions.numDigitsInNumber10; 444 445 InstrPrintInfo p; 446 p.context = ctx; 447 p.sink = &sink; 448 p.ir = ir; 449 p.settings = &settings; 450 451 foreach (IrIndex blockIndex, ref IrBasicBlock block; ir.blocks) 452 { 453 foreach(i, succIndex; block.successors.range(ir)) { 454 sink.putfln("node_%s -> node_%s;", 455 blockIndex.storageUintIndex, succIndex.storageUintIndex); 456 } 457 458 sink.putf(`node_%s [shape=record,label="{`, blockIndex.storageUintIndex); 459 460 p.blockIndex = blockIndex; 461 p.block = █ 462 463 sink.putf(` %s`, IrIndexDump(blockIndex, p)); 464 sink.put(`\l`); 465 466 // phis 467 foreach(IrIndex phiIndex, ref IrPhi phi; block.phis(ir)) 468 { 469 sink.putf(" %s %s = %s(", 470 IrIndexDump(phi.result, p), 471 IrIndexDump(ir.getVirtReg(phi.result).type, p), 472 IrIndexDump(phiIndex, p)); 473 IrIndex[] phiPreds = ir.getBlock(phi.blockIndex).predecessors.data(ir); 474 foreach(size_t arg_i, ref IrIndex phiArg; phi.args(ir)) 475 { 476 if (arg_i > 0) sink.put(", "); 477 sink.putf("%s %s", IrIndexDump(phiArg, p), IrIndexDump(phiPreds[arg_i], p)); 478 } 479 sink.put(")"); 480 sink.put(`\l`); 481 } 482 483 // instrs 484 foreach(IrIndex instrIndex, ref IrInstrHeader instrHeader; block.instructions(ir)) 485 { 486 // print instr 487 p.instrIndex = instrIndex; 488 p.instrHeader = &instrHeader; 489 p.dumpInstr(); 490 sink.put(`\l`); 491 } 492 sink.putfln(`}"];`); 493 } 494 495 sink.putln("}"); 496 } 497 498 void dumpIrIndex(scope void delegate(const(char)[]) sink, ref CompilationContext context, IrIndex index) 499 { 500 if (!index.isDefined) { 501 sink("<null>"); 502 return; 503 } 504 505 final switch(index.kind) with(IrValueKind) { 506 case none: sink.formattedWrite("0x%X", index.asUint); break; 507 case array: sink.formattedWrite("arr%s", index.storageUintIndex); break; 508 case instruction: sink.formattedWrite("i.%s", index.storageUintIndex); break; 509 case basicBlock: sink.formattedWrite("@%s", index.storageUintIndex); break; 510 case constant: 511 auto con = context.constants.get(index); 512 if (con.type.isTypeBasic) { 513 switch (con.type.basicType(&context)) { 514 case IrBasicType.i8: sink.formattedWrite("%s", con.i8); break; 515 case IrBasicType.i16: sink.formattedWrite("%s", con.i16); break; 516 case IrBasicType.i32: sink.formattedWrite("%s", con.i32); break; 517 case IrBasicType.i64: sink.formattedWrite("%s", con.i64); break; 518 case IrBasicType.f32: sink.formattedWrite("%s", con.f32); break; 519 case IrBasicType.f64: sink.formattedWrite("%s", con.f64); break; 520 default: sink.formattedWrite("%s", con.i64); break; 521 } 522 break; 523 } 524 sink.formattedWrite("%s", con.i64); 525 break; 526 case constantAggregate: 527 sink("{"); 528 foreach(i, m; context.constants.getAggregate(index).members) { 529 if (i > 0) sink(", "); 530 dumpIrIndex(sink, context, m); 531 } 532 sink("}"); 533 break; 534 case constantZero: 535 if (index.typeKind == IrTypeKind.basic) 536 sink("0"); 537 else 538 sink("zeroinit"); 539 break; 540 case global: sink.formattedWrite("g%s", index.storageUintIndex); break; 541 case phi: sink.formattedWrite("phi%s", index.storageUintIndex); break; 542 case stackSlot: sink.formattedWrite("s%s", index.storageUintIndex); break; 543 case virtualRegister: sink.formattedWrite("v%s", index.storageUintIndex); break; 544 case physicalRegister: sink.formattedWrite("r%s<c%s s%s>", index.physRegIndex, index.physRegClass, index.physRegSize); break; 545 case type: dumpIrType(sink, context, index); break; 546 case variable: assert(false); 547 case func: sink.formattedWrite("f%s", index.storageUintIndex); break; 548 } 549 } 550 551 struct IrTypeDump 552 { 553 this(IrIndex index, ref CompilationContext ctx) { 554 this.index = index; 555 this.ctx = &ctx; 556 } 557 IrIndex index; 558 CompilationContext* ctx; 559 560 void toString(scope void delegate(const(char)[]) sink) { 561 dumpIrType(sink, *ctx, index); 562 } 563 } 564 565 void dumpIrType(scope void delegate(const(char)[]) sink, ref CompilationContext ctx, IrIndex type, bool recurse = true) 566 { 567 if (type.isUndefined) { 568 sink("<null>"); 569 return; 570 } 571 final switch(type.typeKind) with(IrTypeKind) { 572 case basic: 573 final switch(cast(IrBasicType)type.typeIndex) with(IrBasicType) { 574 case noreturn_t: sink("noreturn"); break; 575 case void_t: sink("void"); break; 576 case i8: sink("i8"); break; 577 case i16: sink("i16"); break; 578 case i32: sink("i32"); break; 579 case i64: sink("i64"); break; 580 case f32: sink("f32"); break; 581 case f64: sink("f64"); break; 582 } 583 break; 584 case pointer: 585 dumpIrType(sink, ctx, ctx.types.get!IrTypePointer(type).baseType, false); 586 sink("*"); 587 break; 588 case array: 589 auto array = ctx.types.get!IrTypeArray(type); 590 sink.formattedWrite("[%s x ", array.numElements); 591 dumpIrType(sink, ctx, array.elemType); 592 sink("]"); 593 break; 594 case struct_t: 595 if (!recurse) { 596 sink("{...}"); 597 break; 598 } 599 IrTypeStruct* struct_t = &ctx.types.get!IrTypeStruct(type); 600 sink("{"); 601 foreach(i, IrTypeStructMember member; struct_t.members) 602 { 603 if (i > 0) { 604 if (struct_t.isUnion) sink(" | "); 605 else sink(", "); 606 } 607 dumpIrType(sink, ctx, member.type, false); 608 } 609 sink("}"); 610 break; 611 case func_t: 612 // results 613 auto funcType = &ctx.types.get!IrTypeFunction(type); 614 foreach(i, result; funcType.resultTypes) { 615 if (i > 0) sink(", "); 616 dumpIrType(sink, ctx, result); 617 } 618 619 // parameters 620 sink("("); 621 foreach(i, param; funcType.parameterTypes) { 622 if (i > 0) sink(", "); 623 dumpIrType(sink, ctx, param); 624 } 625 sink(")"); 626 break; 627 } 628 } 629 630 631 void dumpIrInstr(ref InstrPrintInfo p) 632 { 633 switch(p.instrHeader.op) 634 { 635 case IrOpcode.branch_unary: dumpUnBranch(p); break; 636 case IrOpcode.branch_binary: dumpBinBranch(p); break; 637 case IrOpcode.jump: dumpJmp(p); break; 638 case IrOpcode.branch_switch: dumpSwitch(p); break; 639 640 case IrOpcode.parameter: 641 uint paramIndex = p.ir.get!IrInstr_parameter(p.instrIndex).index(p.ir); 642 dumpOptionalResult(p); 643 p.sink.putf("parameter%s", paramIndex); 644 break; 645 646 case IrOpcode.ret: 647 p.sink.put(" return"); 648 break; 649 650 case IrOpcode.ret_val: 651 p.sink.put(" return"); 652 dumpArg(p.instrHeader.arg(p.ir, 0), p); 653 break; 654 655 default: 656 dumpOptionalResult(p); 657 p.sink.putf("%s", cast(IrOpcode)p.instrHeader.op); 658 dumpArgs(p); 659 break; 660 } 661 } 662 663 void dumpOptionalResult(ref InstrPrintInfo p) 664 { 665 if (p.instrHeader.hasResult) 666 { 667 if (p.instrHeader.result(p.ir).isVirtReg) 668 { 669 p.sink.putf(" %s %s = ", 670 IrIndexDump(p.instrHeader.result(p.ir), p), 671 IrIndexDump(p.ir.getVirtReg(p.instrHeader.result(p.ir)).type, p)); 672 } 673 else 674 { 675 p.sink.putf(" %s = ", IrIndexDump(p.instrHeader.result(p.ir), p)); 676 } 677 } 678 else 679 { 680 p.sink.put(" "); 681 } 682 } 683 684 void dumpArgs(ref InstrPrintInfo p) 685 { 686 foreach (i, IrIndex arg; p.instrHeader.args(p.ir)) 687 { 688 if (i > 0) p.sink.put(","); 689 dumpArg(arg, p); 690 } 691 } 692 693 void dumpArg(IrIndex arg, ref InstrPrintInfo p) 694 { 695 if (arg.isPhysReg) 696 { 697 p.sink.putf(" %s", IrIndexDump(arg, p)); 698 } 699 else if (arg.isFunction) 700 { 701 FunctionDeclNode* func = p.context.getFunction(arg); 702 p.sink.putf(" %s", p.context.idString(func.id)); 703 } 704 else 705 { 706 if (arg.isDefined) 707 p.sink.putf(" %s %s", 708 IrIndexDump(p.ir.getValueType(p.context, arg), p), 709 IrIndexDump(arg, p)); 710 else p.sink.put(" <null>"); 711 } 712 } 713 714 void dumpJmp(ref InstrPrintInfo p) 715 { 716 p.sink.put(" jmp "); 717 if (p.block.successors.length > 0) 718 p.sink.putf("%s", IrIndexDump(p.block.successors[0, p.ir], p)); 719 else 720 p.sink.put(p.settings.escapeForDot ? `\<null\>` : "<null>"); 721 } 722 723 void dumpSwitch(ref InstrPrintInfo p) 724 { 725 p.sink.put(" switch "); 726 IrIndex[] succ = p.block.successors.data(p.ir); 727 IrIndex[] args = p.instrHeader.args(p.ir); 728 729 if (args.length > 0) p.sink.putf("%s, ", IrIndexDump(args[0], p)); 730 else p.sink.put(p.settings.escapeForDot ? `\<null\>, ` : "<null>, "); 731 if (succ.length > 0) p.sink.putf("%s", IrIndexDump(succ[0], p)); 732 else p.sink.put(p.settings.escapeForDot ? `\<null\>` : "<null>"); 733 734 foreach(i; 1..max(succ.length, args.length)) 735 { 736 p.sink.put(", "); 737 if (succ.length > i) p.sink.putf("%s ", IrIndexDump(succ[i], p)); 738 else p.sink.put(p.settings.escapeForDot ? `\<null\> ` : "<null> "); 739 if (args.length > i) p.sink.putf("%s", IrIndexDump(args[i], p)); 740 else p.sink.put(p.settings.escapeForDot ? `\<null\>` : "<null>"); 741 } 742 } 743 744 void dumpUnBranch(ref InstrPrintInfo p) 745 { 746 p.sink.putf(" if%s", unaryCondStrings[p.instrHeader.cond]); 747 dumpArg(p.instrHeader.arg(p.ir, 0), p); 748 p.sink.put(" then "); 749 dumpBranchTargets(p); 750 } 751 752 void dumpBinBranch(ref InstrPrintInfo p) 753 { 754 string[] opStrings = p.settings.escapeForDot ? binaryCondStringsEscapedForDot : binaryCondStrings; 755 p.sink.put(" if"); 756 dumpArg(p.instrHeader.arg(p.ir, 0), p); 757 p.sink.putf(" %s", opStrings[p.instrHeader.cond]); 758 dumpArg(p.instrHeader.arg(p.ir, 1), p); 759 p.sink.put(" then "); 760 761 dumpBranchTargets(p); 762 } 763 764 void dumpBranchTargets(ref InstrPrintInfo p) 765 { 766 switch (p.block.successors.length) { 767 case 0: 768 p.sink.put(p.settings.escapeForDot ? `\<null\> else \<null\>` : "<null> else <null>"); 769 break; 770 case 1: 771 p.sink.putf(p.settings.escapeForDot ? `%s else \<null\>` : "%s else <null>", 772 IrIndexDump(p.block.successors[0, p.ir], p)); 773 break; 774 default: 775 p.sink.putf("%s else %s", 776 IrIndexDump(p.block.successors[0, p.ir], p), 777 IrIndexDump(p.block.successors[1, p.ir], p)); 778 break; 779 } 780 }