1MODULE OOC:X86:Debug; 2 3IMPORT 4 Object, Object:Boxed, ADT:Dictionary, IO:TextRider, IO:StdChannels, Out, 5 L := Log, OOC:C:Naming, S := OOC:X86:SSA, M := OOC:X86:Machine; 6 7TYPE 8 Output = POINTER TO OutputDesc; 9 OutputDesc = RECORD 10 b: S.Block; 11 name: STRING; 12 codeDict: Dictionary.Dictionary(S.Instr, STRING); 13 codeCount: LONGINT; 14 END; 15 16TYPE 17 State = POINTER TO StateDesc; 18 StateDesc = RECORD 19 (Object.ObjectDesc) 20 block: S.Block; 21 instr: S.Instr; 22 controlCP: S.Instr; 23 reg: ARRAY S.lastRegister+1 OF S.Result; 24 25 mergeCount: LONGINT; 26 undef: S.Instr; 27 next: State; 28 END; 29 30VAR 31 out: Output; 32 33CONST 34 writeAllInstructions = FALSE; 35 writeDesignator = TRUE; 36 resultPrefix = "("; 37 resultSuffix = ")"; 38 instrPrefix = "{"; 39 instrSuffix = "}"; 40 depPrefix = " ["; 41 depSuffix = "]"; 42 43PROCEDURE (out: Output) INIT*(b: S.Block; name: STRING); 44 BEGIN 45 out.b := b; 46 out.name := name; 47 out.codeDict := NEW(Dictionary.Dictionary(S.Instr, STRING)); 48 out.codeCount := 0; 49 END INIT; 50 51PROCEDURE (s: State) INIT*(block: S.Block); 52 BEGIN 53 s.block := block; 54 s.instr := block.instrList; 55 s.controlCP := block; 56 s.mergeCount := -1; 57 s.undef := NIL; 58 s.next := NIL; 59 END INIT; 60 61PROCEDURE (s: State) Copy*(): State; 62 VAR 63 t: State; 64 BEGIN 65 NEW(t); 66 t^ := s^; 67 RETURN t; 68 END Copy; 69 70PROCEDURE Type(type: S.Type): STRING; 71 BEGIN 72 CASE type OF 73 | S.noType: RETURN ""; 74 | S.signed8: RETURN "i8"; 75 | S.signed16: RETURN "i16"; 76 | S.signed32: RETURN "i32"; 77 | S.signed64: RETURN "i64"; 78 | S.unsigned8: RETURN "u8"; 79 | S.unsigned16: RETURN "u16"; 80 | S.unsigned32: RETURN "u32"; 81 | S.unsigned64: RETURN "u64"; 82 | S.address: RETURN "adr"; 83 | S.real32: RETURN "r32"; 84 | S.real64: RETURN "r64"; 85 END; 86 END Type; 87 88PROCEDURE Opcode*(instr: S.Instr): STRING; 89 VAR 90 str, type: STRING; 91 BEGIN 92 CASE instr.opcode OF 93 (* arithmetic *) 94 | S.const: str := "const"; 95 | S.add: str := "add"; 96 | S.sub: str := "sub"; 97 | S.mul: str := "mul"; 98 | S.div: str := "div"; 99 | S.neg: str := "neg"; 100 | S.asr: str := "asr"; 101 | S.asl: str := "asl"; 102 | S.lsr: str := "lsr"; 103 | S.lsl: str := "lsl"; 104 | S.rr: str := "rr"; 105 | S.rl: str := "rl"; 106 | S.indexed: str := "indexed"; 107 | S.eql: str := "eql"; 108 | S.neq: str := "neq"; 109 | S.lss: str := "lss"; 110 | S.leq: str := "leq"; 111 | S.gtr: str := "gtr"; 112 | S.geq: str := "geq"; 113 | S.setBool: str := "setBool"; 114 | S.bitTestAndSet: str := "bitTestAndSet"; 115 | S.bitTestAndClear: str := "bitTestAndClear"; 116 | S.typeConv: str := "typeConv"; 117 | S.fpLoad: str := "fpLoad"; 118 | S.fpStorePop: str := "fpStorePop"; 119 120 (* function framing *) 121 | S.enter: str := "enter"; 122 | S.exit: str := "exit"; 123 | S.return: str := "return"; 124 | S.call: str := "call"; 125 | S.bounceResult: str := "bounceResult"; 126 | S.function: str := "function"; 127 | S.allocateStack: str := "allocateStack"; 128 | S.standin: str := "standin"; 129 130 (* memory *) 131 | S.get: str := "get"; 132 | S.set: str := "set"; 133 | S.copy: str := "copy"; 134 | S.typeCast: str := "typeCast"; 135 | S.loadRegister: str := "loadRegister"; 136 | S.push: str := "push"; 137 | S.pop: str := "pop"; 138 | S.leave: str := "leave"; 139 | S.getLengthHeap: str := "getLengthHeap"; 140 | S.setStack: str := "setStack"; 141 | S.typeTag: str := "typeTag"; 142 143 (* control flow *) 144 | S.branch: str := "branch"; 145 | S.guard: 146 IF (instr(S.MergeInstr).jumps # NIL) THEN 147 str := "guard-or-merge"; 148 ELSE 149 str := "guard"; 150 END; 151 | S.merge: str := "merge"; 152 | S.jump: str := "jump"; 153 | S.gate: str := "gate"; 154 | S.designatorGate: str := "designatorGate"; 155 | S.loopStart: str := "loopStart"; 156 | S.loopEnd: str := "loopEnd"; 157 | S.loopExit: str := "loopExit"; 158 | S.loopBackedge: str := "loopBackedge"; 159 END; 160 IF (instr.resultList # NIL) & (instr.resultList.type # S.noType) THEN 161 type := Type(instr.resultList.type); 162 IF (instr.opcode = S.const) THEN 163 RETURN type; 164 ELSE 165 RETURN str+"-"+type; 166 END; 167 ELSE 168 RETURN str; 169 END; 170 END Opcode; 171 172PROCEDURE Register*(reg: S.Register): STRING; 173 BEGIN 174 CASE reg OF 175 | S.sp: RETURN "sp"; 176 | S.fp: RETURN "fp"; 177 | S.cc: RETURN "cc"; 178 | S.gp0: RETURN "gp0"; 179 | S.gp1: RETURN "gp1"; 180 | S.gp2: RETURN "gp2"; 181 | S.gp3: RETURN "gp3"; 182 | S.gp4: RETURN "gp4"; 183 | S.gp5: RETURN "gp5"; 184 | S.st0: RETURN "st0"; 185 | S.fp0: RETURN "fp0"; 186 | S.fp1: RETURN "fp1"; 187 | S.fp2: RETURN "fp2"; 188 | S.fp3: RETURN "fp3"; 189 | S.fp4: RETURN "fp4"; 190 | S.fp5: RETURN "fp5"; 191 | S.fp6: RETURN "fp6"; 192 | S.fp7: RETURN "fp7"; 193 ELSE 194 RETURN "???"; 195 END; 196 END Register; 197 198PROCEDURE (out: Output) GetInstrCode(instr: S.Instr): STRING; 199 VAR 200 str: STRING; 201 BEGIN 202 IF out.codeDict.HasKey(instr) THEN 203 RETURN out.codeDict.Get(instr); 204 ELSE 205 INC(out.codeCount); 206 str := Boxed.IntToString(out.codeCount); 207 out.codeDict.Set(instr, str); 208 RETURN str; 209 END; 210 END GetInstrCode; 211 212PROCEDURE (out: Output) Label(node: S.Node): STRING; 213 VAR 214 i: LONGINT; 215 res: S.Result; 216 str: STRING; 217 218 PROCEDURE Location(res: S.Result): STRING; 219 BEGIN 220 CASE res.adrMode OF 221 | S.register: 222 RETURN ":"+Register(res.register); 223 | S.indirect: 224 RETURN ":Indirect"; 225 | S.noLocation: 226 RETURN ""; 227 END; 228 END Location; 229 230 BEGIN 231 IF (node = NIL) THEN 232 RETURN "NIL"; 233 ELSE 234 WITH node: S.Instr DO 235 RETURN instrPrefix+out.GetInstrCode(node)+instrSuffix; 236 | node: S.Result DO 237 IF (node.instr.resultList.nextResult = NIL) THEN 238 (* single result: use short version *) 239 str := resultPrefix+out.GetInstrCode(node.instr)+resultSuffix ; 240 ELSE 241 i := 1; 242 res := node.instr.resultList; 243 WHILE (res # node) DO 244 INC(i); 245 res := res.nextResult; 246 END; 247 str := resultPrefix+out.GetInstrCode(node.instr)+"."+Boxed.IntToString(i)+resultSuffix ; 248 END; 249 RETURN str+Location(node); 250 | node: S.Opnd DO 251 RETURN out.GetInstrCode(node.instr)+"/"+ 252 Boxed.IntToString(node.OpndIndex())+"="+out.Label(node.arg); 253 END; 254 END; 255 END Label; 256 257PROCEDURE (out: Output) Result*(msg: STRING; res: S.Result); 258 BEGIN 259 Out.Object(msg); 260 Out.String(": "); 261 Out.Object(out.Label(res)); 262 Out.Ln; 263 END Result; 264 265PROCEDURE (out: Output) Instr*(msg: STRING; instr: S.Instr); 266 BEGIN 267 Out.Object(msg); 268 Out.String(": "); 269 Out.Object(out.Label(instr)); 270 Out.String(": "); 271 Out.Object(Opcode(instr)); 272 Out.Ln; 273 END Instr; 274 275PROCEDURE (out: Output) WriteFunction*(); 276 VAR 277 w: TextRider.Writer; 278 279 PROCEDURE WriteConst(selector: S.Selector); 280 BEGIN 281 WITH selector: S.Const DO 282 w.WriteObject(selector.value); 283 | selector: S.Var DO 284 IF (selector.decl = NIL) THEN 285 w.WriteObject("offset(.)"); 286 ELSIF selector.IsGlobalVar() THEN 287 w.WriteObject("adr("+Naming.NameOfDeclaration(selector.decl)+")"); 288 ELSE 289 w.WriteObject("offset("+selector.decl.Name()); 290 IF (selector.dim >= 0) THEN 291 w.WriteChar("."); 292 w.WriteLInt(selector.dim, 0); 293 END; 294 w.WriteObject(")"); 295 END; 296 | selector: S.Proc DO 297 w.WriteObject("adr("+selector.decl.Name()+")"); 298 | selector: S.ProcName DO 299 w.WriteObject('adr("'+selector.name+'")'); 300 | selector: S.TypeDescr DO 301 w.WriteObject('adr("'+Naming.NameOfTypeDescriptor(selector.type, NIL)+'")'); 302 | selector: S.ModuleDescr DO 303 w.WriteObject('_mid'); 304 END; 305 END WriteConst; 306 307 PROCEDURE Expr(instr: S.Instr); 308 VAR 309 opnd: S.Opnd; 310 s: S.Selector; 311 i: LONGINT; 312 jump: S.Instr; 313 BEGIN 314 w.WriteString("("); 315 IF writeDesignator & (instr.designator # NIL) THEN 316 w.WriteString("<"); 317 FOR s IN instr.designator^ DO 318 WITH s: S.Var DO 319 IF (s.decl = NIL) THEN 320 w.WriteString("offset(.)"); 321 ELSE 322 w.WriteString(s.decl.name.str^); 323 END; 324 | s: S.Const DO 325 w.WriteString("const"); 326 | s: S.Proc DO 327 w.WriteString(s.decl.name.str^); 328 | s: S.Index DO 329 w.WriteString("["); 330 w.WriteObject(out.Label(s.indexStandin.opndList)); 331 w.WriteString("]"); 332 | s: S.Field DO 333 w.WriteString("."); 334 w.WriteString(s.field.name.str^); 335 | s: S.ProcName DO 336 w.WriteObject('"'+s.name+'"'); 337 | s: S.TypeDescr DO 338 w.WriteObject(Naming.NameOfTypeDescriptor(s.type, NIL)); 339 | s: S.ModuleDescr DO 340 w.WriteString("_mid"); 341 | s: S.HeapObj DO 342 w.WriteObject("heap"); 343 END; 344 END; 345 w.WriteString("> "); 346 END; 347 348 w.WriteObject(Opcode(instr)); 349 FOR opnd IN instr.Operands() DO 350 w.WriteChar(" "); 351 IF opnd.immediate THEN 352 Expr(opnd.arg.instr); 353 ELSE 354 w.WriteObject(out.Label(opnd.arg)); 355 END; 356 END; 357 358 CASE instr.opcode OF 359 | S.const: 360 w.WriteChar(" "); 361 FOR i := 0 TO LEN(instr.designator^)-1 DO 362 IF (i # 0) THEN 363 w.WriteChar("+"); 364 END; 365 WriteConst(instr.designator[i]); 366 END; 367 ELSE 368 (* nothing *) 369 END; 370 371 WITH instr: S.MergeInstr DO 372 IF (instr.jumps = NIL) THEN 373 w.WriteString(" [no incoming jumps]"); 374 ELSE 375 FOR jump IN instr.jumps^ DO 376 w.WriteString(" ["); 377 w.WriteObject(out.Label(jump)); 378 w.WriteChar("]"); 379 END; 380 END; 381 ELSE 382 (* nothing *) 383 END; 384 w.WriteString(")"); 385 END Expr; 386 387 PROCEDURE WriteBlock(b: S.Block; indent: LONGINT); 388 VAR 389 instr: S.Instr; 390 dep: S.Dep; 391 i: LONGINT; 392 BEGIN 393 FOR instr IN b.Instructions() DO 394 IF writeAllInstructions OR 395 (instr.beforeList # NIL) OR (instr.afterList # NIL) OR 396 instr.IsLive() THEN 397 w.WriteLn; 398 FOR i := 1 TO indent DO 399 w.WriteString(" "); 400 END; 401 402 IF (instr.resultList = NIL) THEN 403 w.WriteObject(out.Label(instr)); 404 w.WriteChar(" "); 405 ELSE 406 w.WriteObject(out.Label(instr.resultList)); 407 w.WriteChar(" "); 408 IF (instr.resultList.nextResult # NIL) THEN 409 w.WriteObject(out.Label(instr.resultList.nextResult)); 410 w.WriteChar(" "); 411 IF (instr.resultList.nextResult.nextResult # NIL) THEN 412 w.WriteString("... "); 413 END; 414 END; 415 w.WriteString("<- "); 416 END; 417 Expr(instr); 418 419 IF (instr.beforeList # NIL) THEN 420 w.WriteString(depPrefix); 421 FOR dep IN instr.BeforeList(S.depAny) DO 422 IF (dep # instr.beforeList) THEN 423 w.WriteChar(" "); 424 END; 425 CASE dep.type OF 426 | S.depControl: w.WriteString("c:"); 427 | S.depMemory: w.WriteString("m:"); 428 | S.depRegion: w.WriteString("r:"); 429 END; 430 w.WriteObject(out.Label(dep.before)); 431 END; 432 w.WriteString(depSuffix); 433 END; 434 END; 435 436 IF (instr IS S.Block) THEN 437 WriteBlock(instr(S.Block), indent+1); 438 END; 439 END; 440 END WriteBlock; 441 442 BEGIN 443 w := TextRider.ConnectWriter(StdChannels.stderr); 444 w.WriteLn; 445 w.WriteObject("PROCEDURE "+out.name); 446 WriteBlock(out.b, 0); 447 w.WriteLn; 448 END WriteFunction; 449 450 451PROCEDURE UndefState(s: State); 452 VAR 453 i: LONGINT; 454 BEGIN 455 s.undef := NEW(S.Instr, -1); 456 FOR i := 0 TO S.lastRegister DO 457 s.reg[i] := s.undef.AddResult(S.signed32); 458 END; 459 END UndefState; 460 461PROCEDURE WriteFunctionAgain*(); 462 BEGIN 463 out.WriteFunction(); 464 END WriteFunctionAgain; 465 466PROCEDURE WriteFunction*(b: S.FunctionBlock); 467 VAR 468 name: STRING; 469 BEGIN 470 IF (b.procDecl.procAST = NIL) THEN 471 name := "MODULE_BODY"; 472 ELSE 473 name := Naming.NameOfDeclaration(b.procDecl); 474 END; 475 out := NEW(Output, b, name); 476 out.WriteFunction(); 477 END WriteFunction; 478 479PROCEDURE Label*(node: S.Node): STRING; 480 BEGIN 481 RETURN out.Label(node); 482 END Label; 483 484PROCEDURE Log*(msg: ARRAY OF CHAR; node: S.Node); 485 BEGIN 486 L.Object(msg, Label(node)); 487 END Log; 488 489PROCEDURE Log2*(msg1: ARRAY OF CHAR; node1: S.Node; 490 msg2: ARRAY OF CHAR; node2: S.Node); 491 BEGIN 492 L.Object(msg1, Label(node1)+", "+Object.NewLatin1(msg2)+": "+Label(node2)); 493 END Log2; 494 495PROCEDURE ValidateAllocation*(b: S.FunctionBlock); 496(**Checks that every read operation on a register, parameter, local variable, 497 or spill variable indeed retrieves the value it expects to get. This is one 498 way to check that the register allocator did not produce an invalid register 499 assignment. *) 500 VAR 501 s, ready: State; 502 block: S.Block; 503 dep: S.Dep; 504 waiting: Dictionary.Dictionary(S.Instr, State); 505 keys: Object.ObjectArrayPtr(S.Instr); 506 instr: S.Instr; 507 loopStart: S.LoopStartInstr; 508 509 PROCEDURE ValidateBlock(s: State); 510 (**Track changes to the state @oparam{s} performed by the sequence of 511 instructions in the block @ofield{s.block}, beginning with instruction 512 @ofield{s.instr}. Tracking ends either if control flow branches, or if 513 the end of the block is reached. In the former case, @ofield{s.instr} 514 indicates the branching instruction, in the latter case it is @code{NIL}. *) 515 VAR 516 instr: S.Instr; 517 res: S.Result; 518 inOutOpnd: S.Opnd; 519 520 PROCEDURE ValidateOpnds(instr: S.Instr); 521 VAR 522 opnd: S.Opnd; 523 arg: S.Result; 524 iter: S.IterOperands; 525 BEGIN 526 CASE instr.opcode OF 527 | S.jump: (* fold gate opnds into jump *) 528 iter := instr.GateOperands(); 529 | S.gate: (* opnds folded into jump *) 530 RETURN; 531 ELSE (* normal instruction *) 532 iter := instr.Operands(); 533 END; 534 FOR opnd IN iter DO 535 IF opnd.immediate THEN 536 ValidateOpnds(opnd.arg.instr); 537 ELSE 538 arg := opnd.arg; 539 ASSERT(arg.adrMode = S.register); 540 IF (arg # s.reg[arg.register]) THEN 541 L.Object("arg not in register", Label(opnd)); 542 L.Object("instead the register holds", 543 Label(s.reg[arg.register])); 544 L.Ptr("ptr", Label(s.reg[arg.register])); 545 L.Ptr("opcode", Opcode(s.reg[arg.register].instr)); 546 ASSERT(arg = s.reg[arg.register]); 547 END; 548 END; 549 END; 550 END ValidateOpnds; 551 552 PROCEDURE HasControlDep(instr: S.Instr): BOOLEAN; 553 VAR 554 dep: S.Dep; 555 iter: S.IterInstrAfterList; 556 BEGIN 557 iter := instr.AfterList(S.depControl); 558 RETURN iter.Next(dep); 559 END HasControlDep; 560 561 BEGIN 562 instr := s.instr; 563 WHILE (instr # NIL) DO 564 CASE instr.opcode OF 565 | S.branch, S.loopStart, S.loopExit: 566 s.instr := instr; 567 RETURN; 568 ELSE 569 IF instr.IsLive()THEN 570 IF HasControlDep(instr) THEN 571 s.controlCP := instr; 572 END; 573 574 ValidateOpnds(instr); 575 576 FOR res IN instr.Results() DO 577 IF (res.adrMode # S.register) THEN 578 Log("not in register", res); 579 ASSERT(res.adrMode = S.register); 580 END; 581 IF (s.reg[res.register] # NIL) THEN 582 ASSERT(s.reg[res.register].instr # instr); 583 END; 584 s.reg[res.register] := res; 585 END; 586 587 inOutOpnd := M.InOutOpnd(instr); 588 IF (inOutOpnd # NIL) THEN 589 IF (inOutOpnd.arg.register # instr.resultList.register) THEN 590 Log("in/out opnd mismatch", inOutOpnd); 591 ASSERT(inOutOpnd.arg.register = instr.resultList.register); 592 END; 593 END; 594 END; 595 END; 596 597 instr := instr.nextInstr; 598 END; 599 s.instr := NIL; 600 END ValidateBlock; 601 602 PROCEDURE PushState(s: State); 603 BEGIN 604 s.next := ready; 605 ready := s; 606 END PushState; 607 608 PROCEDURE PushBlock(s: State; block: S.Block); 609 VAR 610 t: State; 611 BEGIN 612 t := s.Copy(); 613 t.block := block; 614 t.instr := block.instrList; 615 t.controlCP := block; 616 PushState(t); 617 END PushBlock; 618 619 PROCEDURE PopState(): State; 620 VAR 621 s: State; 622 BEGIN 623 s := ready; 624 ready := ready.next; 625 RETURN s; 626 END PopState; 627 628 PROCEDURE MergedBlocks(merge: S.Instr): LONGINT; 629 VAR 630 c: LONGINT; 631 dep: S.Dep; 632 BEGIN 633 c := 0; 634 FOR dep IN merge.BeforeList(S.depControl) DO 635 INC(c); 636 END; 637 IF (merge.opcode = S.loopEnd) THEN 638 (* uncount dep loopEnd -> loopStart *) 639 DEC(c); 640 END; 641 RETURN c; 642 END MergedBlocks; 643 644 PROCEDURE MergeState(merge: S.MergeInstr; s: State); 645 VAR 646 i: LONGINT; 647 t: State; 648 BEGIN 649 IF waiting.HasKey(merge) THEN 650 t := waiting.Get(merge); 651 FOR i := 0 TO S.lastRegister DO 652 IF (t.reg[i] # s.reg[i]) THEN 653 t.reg[i] := NIL; 654 END; 655 END; 656 DEC(t.mergeCount); 657 IF (t.mergeCount = 0) THEN 658 PushState(t); 659 waiting.Delete(merge); 660 END; 661 ELSE 662 t := s.Copy(); 663 t.block := merge; 664 t.instr := merge.instrList; 665 t.controlCP := merge; 666 t.mergeCount := MergedBlocks(merge)-1; 667 IF (t.mergeCount = 0) THEN 668 PushState(t); 669 ELSE 670 waiting.Set(merge, t); 671 END; 672 END; 673 END MergeState; 674 675 BEGIN 676 waiting := NEW(Dictionary.Dictionary(S.Instr, State)); 677 s := NEW(State, b); 678 UndefState(s); 679 ready := NIL; 680 PushState(s); 681 682 WHILE (ready # NIL) DO 683 s := PopState(); 684 ValidateBlock(s); 685 686 IF (s.instr # NIL) THEN 687 CASE s.instr.opcode OF 688 | S.branch: 689 FOR block IN s.instr(S.BranchInstr).paths^ DO 690 MergeState(block(S.MergeInstr), s); 691 END; 692 | S.loopStart: 693 loopStart := s.instr(S.LoopStartInstr); 694 PushBlock(s, loopStart); 695 IF (MergedBlocks(loopStart.end) = 0) THEN 696 (* special case: if the loop has no exit, then the end of the 697 loop is never reached by normal means *) 698 PushBlock(s, loopStart.end); 699 END; 700 | S.loopExit: 701 (* add state to loopEnd *) 702 block := s.instr.MergingTarget(); 703 MergeState(block(S.MergeInstr), s); 704 (* and push instruction after the loopExit *) 705 s.instr := s.instr.nextInstr; 706 PushState(s); 707 END; 708 ELSIF (s.block.opcode = S.merge) THEN 709 (* continue after the branch in the block that contains the 710 merge's branch instruction *) 711 ASSERT(s.block.block.opcode = S.branch); 712 s.instr := s.block.block.nextInstr; 713 s.block := s.block.block.block; 714 s.controlCP := s.instr; 715 PushState(s); 716 ELSIF (s.block.opcode = S.loopEnd) THEN 717 (* continue after the loopEnd block *) 718 s.instr := s.block.nextInstr; 719 s.block := s.block.block; 720 s.controlCP := s.instr; 721 PushState(s); 722 ELSE 723 FOR dep IN s.controlCP.AfterList(S.depControl) DO 724 IF (dep.instr IS S.MergeInstr) & (dep.instr.opcode # S.loopEnd) THEN 725 MergeState(dep.instr(S.MergeInstr), s); 726 END; 727 END; 728 END; 729 END; 730 IF (waiting.Size() # 0) THEN 731 keys := waiting.Keys(); 732 FOR instr IN keys^ DO 733 Log("waiting", instr); 734 END; 735 ASSERT(waiting.Size() = 0); 736 END; 737 END ValidateAllocation; 738 739END OOC:X86:Debug. 740