|
|
1.1 ! root 1: .NH 3 ! 2: \*Mrt/arith.s\fR ! 3: .SH ! 4: Overview ! 5: .PP ! 6: \*Marith.s\fP contains code for routines that add,\^ subtract,\^ and ! 7: multiply integers and check for overflow. If overflow occurs,\^ ! 8: run-time error 203 is produced. ! 9: .PP ! 10: The arguments to \*Mckadd\fP,\^ \*Mcksub\fP,\^ and \*Mckmul\fP are ! 11: two C \*Mlong\fP integers on which to operate. For example,\^ ! 12: if \*Mckadd\fP were written in C,\^ it would be declared ! 13: .Ds ! 14: long ckadd(a,\^b) ! 15: long a,\^b; ! 16: { ! 17: \&\*(El ! 18: } ! 19: .De ! 20: The routines return the result of the operation ! 21: using standard C return conventions. ! 22: .SH ! 23: \*Marith\fP on the VAX ! 24: .PP ! 25: The two arguments appear on the stack; \*Ma\fP is at \*M4(ap)\fP and ! 26: \*Mb\fP is at \*M8(ap)\fP. The appropriate 3-operand VAX instruction ! 27: is used to perform the operation and the result is placed in \*Mr0\fP ! 28: in accordance with C return conventions. If overflow occurs ! 29: during the operation,\^ the overflow bit in the program status word ! 30: is set. ! 31: .PP ! 32: After the operation is performed,\^ the overflow bit is ! 33: checked. If it is on,\^ indicating that an overflow occurred,\^ a branch ! 34: is taken to \*Moflow\fP,\^ where \*Mrunerr(203,\^0)\fP is called. ! 35: If overflow did not occur,\^ the routine returns and the value in ! 36: \*Mr0\fP is the value returned to the calling expression. ! 37: .PP ! 38: \*Marith.s\fP is trivial on the VAX because the hardware supports ! 39: operations on C \*Mlong\fP integers. This may not be the case on the ! 40: target machine. If so,\^ \*Marith.s\fP will be considerably more ! 41: complicated. However,\^ it usually is not difficult to locate routines ! 42: that perform these functions. As a last resort,\^ look at the code the C ! 43: compiler generates for the various arithmetic operations on long ! 44: integers. ! 45: .NH 3 ! 46: \*Mrt/fail.s\fR ! 47: .SH ! 48: Overview ! 49: .PP ! 50: \*Mfail\fP handles the failure of built-in procedures and operations. ! 51: Built-in procedures and operations are C routines and they signal ! 52: failure by calling \*Mfail()\fP. When a failure of this type ! 53: occurs,\^ the failure must be transmitted to the Icon expression ! 54: whose evaluation is in progress and that requires the services of ! 55: a C routine. ! 56: .SH ! 57: Generic Operation ! 58: .PP ! 59: \*Mfail\fP itself does very little,\^ the real work is done by ! 60: \*Mefail\fP. \*Mfail\fP pops the stack ! 61: back to where it was before the C routine was called and then ! 62: branches to \*Mefail\fP to make the enclosing expression fail. ! 63: .PP ! 64: \*Mfail\fP is akin to \*Mpfail\fP in that it pops the stack back ! 65: to a state that it was in when an expression was being evaluated ! 66: and then causes failure of the expression. The primary difference ! 67: is that an Icon procedure frame is being removed in \*Mpfail\fP and ! 68: it contains extra information that must be restored. ! 69: .SH ! 70: \*Mfail\fP on the VAX ! 71: .PP ! 72: \*M_boundary\fP points to the procedure frame for the first C routine ! 73: that was called from Icon. \*Mfp\fP is loaded from \*M_boundary\fP and ! 74: this puts the stack back to the state that it was in when the ! 75: C routine was entered. The C routine is for either a built-in procedure ! 76: such as \*Mread\fP or for an operator such as \*M+\fP. For a ! 77: built-in procedure,\^ the procedure frame now on the top of the stack ! 78: (after loading \*Mfp\fP from \*M_boundary\fP) is the frame constructed ! 79: in \*Minvoke\fP. For an operator,\^ the frame on the stack is the ! 80: one constructed when the interpreter loop called the C routine ! 81: for the operator. ! 82: .PP ! 83: The task at hand is to remove the procedure frame and restore the ! 84: registers that were saved in the frame. The mask/psw word of the ! 85: frame is manipulated so that the mask portion of the word resides ! 86: in bits 0:11 of \*Mr0\fP and the remaining bits of \*Mr0\fP are 0. ! 87: The VAX \*Mpopr\fP instruction takes a register mask as an operand ! 88: and pops words from the stack into the registers indicated by ! 89: the mask. For example,\^ ! 90: .Ds ! 91: popr $0x0005 ! 92: .De ! 93: moves the top word of the stack into \*Mr0\fP and the second stack ! 94: word into \*Mr2\fP. \*Msp\fP is then incremented by 8. ! 95: .PP ! 96: The saved registers start at \*M20(fp)\fP and \*Msp\fP is loaded ! 97: with this address. Then \*Mpopr r0\fP restores the registers ! 98: that are saved in the frame. Note that the manipulations of the ! 99: mask/psw are necessary because it is not known \fIa priori\fP which ! 100: registers were saved. In particular,\^ \*Mpopr $0x0fff\fP would ! 101: be disastrous. ! 102: .PP ! 103: After the registers have been restored,\^ \*Map\fP and \*Mfp\fP are ! 104: restored from the saved \*Map\fP and \*Mfp\fP values in the ! 105: frame. ! 106: .PP ! 107: At this point,\^ the stack is as it was before the frame for ! 108: the built-in procedure or operator was created. All that ! 109: remains is to signal failure in the expression being evaluated ! 110: and this is done by branching to \*Mefail\fP. ! 111: .ne 1i ! 112: .NH 3 ! 113: \*Mlib/pret.s\fR ! 114: .SH ! 115: Overview ! 116: .PP ! 117: \*Mpret\fP handles the return of a value from an Icon procedure. ! 118: \*Mpret\fP is called from the interpreter loop with a single ! 119: argument (which is on the stack) ! 120: that is the value being returned. The value is dereferenced ! 121: if necessary. If tracing is on,\^ a trace message is produced. The ! 122: return value is copied over \*(e0 in the frame of the procedure that ! 123: is returning a value. The procedure frame is removed,\^ leaving ! 124: the result on the stack,\^ and \*Mpret\fP returns. ! 125: .SH ! 126: Generic Operation ! 127: .LP ! 128: .Ls ! 129: .Np ! 130: \*M&level\fP is decremented because a procedure is being exited. ! 131: .Np ! 132: The stack address where the return value is ! 133: to be placed is calculated. Recall that when a procedure is invoked,\^ the ! 134: return value (if any) ultimately replaces \*(e0,\^ the descriptor ! 135: for the procedure returning the value. ! 136: .Np ! 137: The value being returned must be dereferenced if it is a local ! 138: variable or argument. This is because local variables and arguments ! 139: are on the stack and the portion of the stack associated with ! 140: a procedure ``goes away'' when a procedure returns. If the ! 141: return value is a variable (is of type \*MT_VAR\fP) ! 142: and its address ! 143: is between the base of the current expression stack*,\^ ! 144: and the stack pointer,\^ it is dereferenced. ! 145: If it is a substring trapped variable (is of type \*MT_TVAR\fP ! 146: and points to a block of type \*MT_TVSUBS\fP),\^ and the address of the ! 147: variable containing the substring ! 148: is between the base of the current expression stack ! 149: and the stack pointer,\^ it is dereferenced. ! 150: .FS ! 151: *For purposes of uniformity,\^ the system stack is treated ! 152: as if it were a co-expression stack. The global variable ! 153: \*Mcurrent\fP is a pointer to the descriptor for the co-expression ! 154: stack block for the current co-expression. Co-expressions ! 155: need not be implemented; it is only important that \*Mcurrent\fP ! 156: and the descriptor that it points to be initialized correctly. ! 157: This is done in \*Miconx/init.c\fP. ! 158: .FE ! 159: .Np ! 160: If \*M&trace\fP is non-zero,\^ \*Mrtrace\fP is called with the ! 161: address of the block for the returning procedure and the ! 162: address of the value being returned. ! 163: .Np ! 164: \*Mfp\fP,\^ \*M_line\fP,\^ and ! 165: \*M_file\fP are restored from the returning procedure. ! 166: Because the impending return restores control to the Icon ! 167: environment,\^ \*M_boundary\fP is cleared. ! 168: .Np ! 169: \*Mpret\fP returns from the Icon procedure by executing ! 170: a return instruction. Because the current \*Mfp\fP points to the ! 171: procedure frame for the Icon procedure,\^ and the frame was ! 172: built by \*Minvoke\fP,\^ the return is effectively a return ! 173: from \*Minvoke\fP and the net result is the return value ! 174: left on the stack. ! 175: .Le ! 176: .SH ! 177: \*Mpret\fP on the VAX ! 178: .PP ! 179: \*Mpret\fP does not save any registers because the frame built ! 180: upon entry to \*Mpret\fP is discarded. ! 181: .PP ! 182: \*M_boundary\fP is set because \*Mderef\fP may be called and \*Mderef\fP ! 183: can cause a garbage collection. ! 184: .PP ! 185: \*M_k_level\fP is decremented because a procedure is being exited. ! 186: .PP ! 187: \*Mpret\fP needs to know the address of the descriptor for the Icon ! 188: procedure that is returning. Before \*Mpret\fP was called,\^ \*Mfp\fP ! 189: pointed at the procedure frame for the current Icon procedure (which ! 190: is now returning),\^ and \*Map\fP pointed at its argument list. The ! 191: call to \*Mpret\fP created a procedure frame which contains the old ! 192: \*Map\fP. This value is extracted and used in conjunction with ! 193: \fInargs\fP to calculate the address of the descriptor for the ! 194: procedure. This value is stored in \*Mr11\fP for future use. ! 195: .PP ! 196: As described,\^ the value being returned needs to be dereferenced ! 197: in certain cases. The return value is a descriptor and is the ! 198: argument to \*Mpret\fP. The first word of this descriptor ! 199: lies at \*M8(ap)\fP and contains type and flag information. This ! 200: word is placed in \*Mr1\fP for further examination. ! 201: .PP ! 202: The VAX \*Mbitl\fP instruction tests a set of bits. The two ! 203: operand values are ANDed together and the condition codes ! 204: are set according to the value of the result. The result itself ! 205: is discared. The instruction ! 206: .Ds ! 207: bitl $F_NQUAL,\^r1 ! 208: .De ! 209: ANDs the type and flags word with the \*MF_NQUAL\fP mask. ! 210: The \*MF_NQUAL\fP bit is set if a descriptor is \fInot\fP a ! 211: string qualifier. ! 212: If the \*MF_NQUAL\fP bit is not on,\^ the result of the ! 213: AND is a 0. The test is followed by ! 214: .Ds ! 215: beql chktrace ! 216: .De ! 217: Thus,\^ if the return value \fIis\fP a string qualifier,\^ a branch ! 218: is taken to \*Mchktrace\fP,\^ and no dereferencing is performed. ! 219: .PP ! 220: If the return value does have the \*MF_NQUAL\fP attribute,\^ it ! 221: is checked to see if it is a variable. The \*MF_VAR\fP bit is tested. ! 222: If it is not on,\^ the return value is not a variable and does ! 223: not have to be dereferenced. A branch is made to \*Mchktrace\fP if ! 224: this is the case. ! 225: .PP ! 226: If a variable is in hand,\^ the \*MF_TVAR\fP bit is checked to see ! 227: if it is a trapped variable. If it is not a trapped variable,\^ the ! 228: address field of the return value's descriptor is moved into \*Mr1\fP for ! 229: further testing and a branch is taken to \*Mchkloc\fP. ! 230: .PP ! 231: If the return value is a substring trapped variable,\^ it may reference ! 232: a local variable or an argument. The type bits of the descriptor ! 233: are isolated by ORing it with \*MTYPEMASK\fP. If the type ! 234: is not \*MT_TVSUBS\fP,\^ no dereferencing is needed and a branch ! 235: is taken to \*Mchktrace\fP. If it is a substring trapped variable,\^ ! 236: the address of the variable containing the substring is obtained ! 237: from the trapped variable's data block and is loaded into \*Mr1\fP. ! 238: .PP ! 239: At this point (\*Mchkloc\fP),\^ \*Mr1\fP points to a descriptor ! 240: that is directly or indirectly referenced by the return value. ! 241: If the descriptor is in the current expression stack,\^ the return ! 242: value must be dereferenced. \*Mr1\fP is first compared to ! 243: \*Msp\fP. If it is less than \*Msp\fP,\^ the descriptor is below ! 244: the stack and a branch is made to \*Mchktrace\fP. Otherwise,\^ ! 245: \*Mr1\fP is compared to the base address of the current expression ! 246: stack. If \*Mr1\fP is greater than the base of stack,\^ it is ! 247: above the stack and a branch is made to \*Mchktrace\fP. ! 248: .PP ! 249: It is now certain that the return value must be ! 250: dereferenced,\^ lest it ``disappear'' when the portion of the stack ! 251: it is in is re-used. The address of the return value is pushed ! 252: on the stack and \*Mderef\fP is called. Note that \*Mderef\fP completely ! 253: handles dereferencing of substring trapped variables and thus no ! 254: special provisions need to be made. ! 255: .PP ! 256: At \*Mchktrace\fP,\^ the return value has been dereferenced if ! 257: necessary and it is time to produce a tracing message if \*M&trace\fP ! 258: is non-zero. \*Mrtrace\fP does the work and it ! 259: requires two arguments: the address of the block for the returning ! 260: procedure,\^ and the address of the return value. Earlier,\^ the ! 261: address of descriptor for the procedure block was calculated and left ! 262: in \*Mr11\fP. The second word of this descriptor is pushed on ! 263: the stack along with the address of the return value. ! 264: .PP ! 265: \*Mpret\fP ``returns'' the designated ! 266: value by overwriting the procedure's descriptor ! 267: with the descriptor of the return value. \*Mr11\fP points at the ! 268: descriptor for the procedure and \*M8(ap)\fP still points at the ! 269: descriptor for the return value,\^ so ! 270: .Ds ! 271: movq 8(ap),\^(r11) ! 272: .De ! 273: does the trick. ! 274: .PP ! 275: Everything is set,\^ the actual return must be performed. The \*Mfp\fP ! 276: saved in the current frame is the procedure frame pointer of the Icon ! 277: procedure and the saved value is loaded into \*Mfp\fP to bring ! 278: the Icon procedure frame to the top of the stack. \*M_line\fP and ! 279: \*M_file\fP are restored from the Icon procedure frame. ! 280: \*M_boundary\fP is cleared because the return will take execution back ! 281: into an Icon realm. ! 282: .PP ! 283: A \*Mret\fP is executed. The return goes through the procedure ! 284: frame built by \*Minvoke\fP. Thus,\^ control is returned to the point ! 285: just after the call to \*Minvoke\fP and it appears as if \*Minvoke\fP ! 286: itself had just returned. ! 287: .NH 3 ! 288: \*Mlib/esusp.s\fR ! 289: .SH ! 290: Overview ! 291: .LP ! 292: \*Mesusp\fP suspends a value from an expression. \*Mesusp\fP is ! 293: called from the interpreter loop and the value to suspend appears ! 294: as an argument. A generator frame hiding the current expression ! 295: is created. The surrounding expression frame is duplicated. ! 296: \*Mesusp\fP leaves the value being suspended on the top of the stack. ! 297: .PP ! 298: The \*Mesusp\fP operation arises from the alternation ! 299: (\*M\*(x1\ |\ \^\*(x2\fR) control ! 300: structure. For example ! 301: .Ds ! 302: p(5 | 10) ! 303: .De ! 304: indicates that the call \*Mp(5)\fP should be made and if it fails,\^ then ! 305: \*Mp(10)\fP should be called. ! 306: .PP ! 307: The function of \*Mesusp\fP is best explained using an example. ! 308: The following ucode is generated for \*Mp(5 | 10)\fP ! 309: .Ds ! 310: .ta .8i +.8i +.8i +.8i ! 311: mark L1 ! 312: var 0 \fR(the variable \*Mp\fR)\*M ! 313: mark L2 ! 314: int 0 \fR(constant 5)\*M ! 315: esusp ! 316: goto L3 ! 317: lab L2 ! 318: int 1 \fR(constant 10)\*M ! 319: lab L3 ! 320: invoke 1 ! 321: unmark 1 ! 322: lab L1 ! 323: .De ! 324: When execution reaches \*Mesusp\fP,\^ the stack looks like ! 325: .Ds ! 326: .ft R ! 327: .ta 1i ! 328: expression marker with \*ML1\fR as failure address ! 329: descriptor for variable \*Mp\fR ! 330: \*Mefp\fR \*(ar expression marker with \*ML2\fR as failure address ! 331: \*Msp\fR \*(ar descriptor for constant 5 ! 332: .De ! 333: \*Mgfp\fP is zero at this point. ! 334: After the \*Mesusp\fP is performed,\^ the stack is ! 335: .Ds ! 336: .ft R ! 337: .ta 1i ! 338: \*Mefp\fR \*(ar expression marker with \*ML1\fR as failure address ! 339: descriptor for variable \*Mp\fR ! 340: expression marker with \*ML2\fR as failure address ! 341: descriptor for constant 5 ! 342: \*Mgfp\fR \*(ar generator frame built by \*Mesusp\fR ! 343: .ta 1i 3.3i ! 344: descriptor for variable \*Mp\fR } duplicated region ! 345: \*Msp\fR \*(ar descriptor for constant 5 ! 346: .De ! 347: A branch is taken to \*ML3\fP,\^ where \*Minvoke 1\fP is performed. ! 348: This invokes \*Mp\fP with one argument,\^ the constant 5 on the ! 349: stack. If \*Mp(5)\fP succeeds,\^ the \*Munmark 1\fP is performed ! 350: and the stack is popped back through the \*ML1\fP expression frame,\^ ! 351: the current location of \*Mefp\fP. ! 352: .PP ! 353: Suppose that instead of succeeding,\^ \*Mp(5)\fP fails. ! 354: \*Mp\fP fails by calling \*Mpfail\fP,\^ which removes the ! 355: procedure frame from the stack and then calls \*Mefail\fP. The ! 356: previous stack picture shows what the stack looks like after the ! 357: procedure frame has been removed. \*Mefail\fP finds that ! 358: \*Mgfp\fP is not null and restores certain values that are saved ! 359: in the generator frame. The frame,\^ which was created by \*Mesusp\fP,\^ ! 360: contains a return address that points to \*Mefail\fP. Thus,\^ when ! 361: \*Mefail\fP removes the frame by returning through it,\^ control goes ! 362: back to the start of \*Mefail\fP and the stack is ! 363: .Ds ! 364: .ft R ! 365: .ta 1i ! 366: expression marker with \*ML1\fR as failure address ! 367: descriptor for variable \*Mp\fR ! 368: \*Mefp\fR \*(ar expression marker with \*ML2\fR as failure address ! 369: \*Msp\fR \*(ar descriptor for constant 5 ! 370: .De ! 371: This time around,\^ \*Mgfp\fP is zero,\^ so \*Mefail\fP must remove ! 372: the current expression frame and branch to the failure address in ! 373: the frame's marker. When the expression frame is removed,\^ the ! 374: stack looks like ! 375: .Ds ! 376: .ft R ! 377: .ta 1i ! 378: expression marker with \*ML1\fR as failure address ! 379: \*Msp\fR \*(ar descriptor for variable \*Mp\fR ! 380: .De ! 381: The failure address in the expression frame was \*ML2\fP,\^ so control ! 382: is transferred to \*Mlabel L2\fP in the ucode. (Note how much went ! 383: on as the result of the \*Minvoke\fP being executed.) The ! 384: instruction \*Mint 1\fP is executed and a descriptor for the constant ! 385: 10 is pushed on the stack giving: ! 386: .Ds ! 387: .ft R ! 388: .ta 1i ! 389: expression marker with \*ML1\fR as failure address ! 390: descriptor for variable \*Mp\fR ! 391: \*Msp\fR \*(ar descriptor for constant 10 ! 392: .De ! 393: \*Minvoke 1\fP is performed again,\^ which does \*Mp(10)\fP. ! 394: .PP ! 395: If \*Mp(10)\fP succeeds,\^ the \*Munmark 1\fP is executed,\^ which ! 396: removes the \*ML1\fP marker and transfers control to \*ML1\fP. ! 397: If \*Mp(10)\fP fails,\^ the same thing happens,\^ but \*Mefail\fP ! 398: does the work rather than \*Munmark\fP. ! 399: .SH ! 400: Generic Operation ! 401: .LP ! 402: .Ls ! 403: .Np ! 404: The procedure frame created by the call to \*Mesusp\fP partially ! 405: forms the generator frame. The frame is completed by pushing ! 406: \*M_boundary\fP,\^ \*M_k_level\fP,\^ \*M_line\fP,\^ and \*M_file\fP. ! 407: The generator frame pointer is set to point at the word of the ! 408: frame which contains the boundary. ! 409: .Np ! 410: The bounds of the expression frame to be duplicated are determined. ! 411: The lower bound is the stack word above the current expression frame ! 412: marker. ! 413: The upper bound is dependent on \*Mefp\fP and \*Mgfp\fP ! 414: values saved in the current expression marker. If the saved \*Mgfp\fP is ! 415: non-zero,\^ the upper bound is the first word below the generator ! 416: frame marker. If the saved \*Mgfp\fP is zero,\^ the upper bound is ! 417: the first word below the expression frame marker referenced by the ! 418: saved \*Mefp\fP. In the example,\^ this region only contains ! 419: the descriptor for the variable \*Mp\fP. ! 420: The region is copied to the top of the stack. The stack pointer is ! 421: adjusted to point to the new top of stack. ! 422: .Np ! 423: The value being suspended is pushed on the stack. ! 424: .Np ! 425: The return address in the new generator frame is replaced by the address of ! 426: \*Mefail\fP so that when \*Mefail\fP removes the frame by returning through ! 427: it,\^ \*Mefail\fP regains control. The old return address is ! 428: momentarily retained. The procedure frame pointer and argument ! 429: pointer are restored. \*M_boundary\fP is cleared because control is ! 430: returning to Icon code. ! 431: .Np ! 432: \*Mefp\fP in the current expression ! 433: marker replaces the expression frame pointer. Thus,\^ if an \*Munmark\fP ! 434: is performed,\^ the entire expression frame is removed. In the example,\^ ! 435: this happens if \*Mp(5)\fP or \*Mp(10)\fP succeeds. ! 436: .Np ! 437: The return \*Mpc\fP value which was saved is jumped to. This is in ! 438: effect a return from \*Mesusp\fP,\^ but the stack is untouched. ! 439: .Le ! 440: .SH ! 441: \*Mesusp\fP on the VAX ! 442: .PP ! 443: When \*Mesusp\fP is entered,\^ the generator frame is partially ! 444: constructed. \*Mipc\fP,\^ \*Mgfp\fP,\^ and \*Mefp\fP are saved in ! 445: the frame. \*M_boundary\fP is set to the current \*Mfp\fP value ! 446: and is pushed on the stack. The generator frame pointer,\^ is pointed at ! 447: the word containing the boundary. ! 448: The frame is completed by pushing \*M_k_level\fP,\^ \*M_line\fP,\^ ! 449: and \*M_file\fP on the stack. ! 450: .PP ! 451: The lower bound of the region to copy is the first word above the ! 452: current expression frame marker. Recall that an expression frame ! 453: looks like ! 454: .Ds ! 455: .ft R ! 456: .St ! 457: \*Mefp\fR \*(ar 0 old expression frame pointer ! 458: -4 old generator frame pointer ! 459: -8 failure address ! 460: .De ! 461: Thus,\^ ! 462: .Ds ! 463: addl3 $4,\^efp,\^r0 ! 464: .De ! 465: points \*Mr0\fP at the lower end of the region to copy. ! 466: .PP ! 467: The upper bound of the region to copy is the low word of the marker ! 468: for the enclosing generator or expression frame. If \*Mgfp\fP ! 469: is non-zero the generator frame marker is used. ! 470: Otherwise,\^ the expression frame marker is used. Recall that a ! 471: generator frame looks like ! 472: .Ds ! 473: .ft R ! 474: .St ! 475: saved registers ! 476: 20 reactivation address (saved \*Mpc\fR) ! 477: 16 saved \*Mfp\fR ! 478: 12 saved \*Map\fR ! 479: 8 \*Mpsw\fR and register mask ! 480: 4 0 ! 481: \*Mgfp\fR \*(ar 0 boundary ! 482: -4 saved \*M_k_level\fR ! 483: -8 saved \*M_line\fR ! 484: -12 saved \*M_file\fR ! 485: .De ! 486: So,\^ if the saved \*Mgfp\fP is non-zero,\^ the upper bound of the region ! 487: to copy is ! 488: .Ds ! 489: .ft R ! 490: saved \*Mgfp\fR - 12 ! 491: .De ! 492: Otherwise,\^ it is ! 493: .Ds ! 494: .ft R ! 495: saved \*Mefp\fR - 8 ! 496: .De ! 497: The appropriate calculation is performed and \*Mr2\fP pointed at the ! 498: bounding word. ! 499: .LP ! 500: At this point,\^ the stack looks something like ! 501: .Ds ! 502: .ft R ! 503: .St ! 504: \*Mr2\fR \*(ar low word of expression or generator frame marker ! 505: last word of region to copy ! 506: \*(El ! 507: \*Mr0\fR \*(ar 4 first word of region to copy ! 508: \*Mefp\fR \*(ar 0 saved expression frame pointer ! 509: -4 saved generator frame pointer expression marker ! 510: -8 failure label ! 511: 8 descriptor for value to suspend ! 512: 4 \*Mnargs\fR (1) ! 513: \*Map\fR \*(ar 0 \*Mnwords\fR (3) ! 514: -4 saved \*Mr11\fR (\*Mefp\fR) ! 515: saved \*Mr10\fR (\*Mgfp\fR) ! 516: 24 saved \*Mr9\fR (\*Mipc\fR) ! 517: 20 reactivation address (saved \*Mpc\fR) ! 518: 16 saved \*Mfp\fR ! 519: 12 saved \*Map\fR generator marker ! 520: 8 \*Mpsw\fR and register mask ! 521: 4 0 ! 522: 0 boundary (\*Mfp\fR at entry to \*Mesusp\fR) ! 523: -4 \*M_k_level\fR ! 524: -8 \*M_line\fR ! 525: \*Msp\fR \*(ar -12 \*M_file\fR ! 526: .De ! 527: .PP ! 528: The region starting at \*Mr0\fP and extending to \*Mr2\fP is to be ! 529: copied to the top of the stack. The length of the region in bytes is ! 530: calculated in \*Mr2\fP. ! 531: The value of \*Mr2\fP is subtracted from \*Msp\fP,\^ moving \*Msp\fP ! 532: down to accommodate the region. The region is then copied using ! 533: .Ds ! 534: movc3 r2,\^(r0),\^(sp) ! 535: .De ! 536: which moves \*Mr2\fP bytes starting at \*M0(r0)\fP to \*M0(sp)\fP. ! 537: .PP ! 538: The descriptor for the value to suspend is at \*M8(ap)\fP and it ! 539: is pushed on the stack using ! 540: .Ds ! 541: movq 8(ap),\^(sp) ! 542: .De ! 543: .LP ! 544: The stack now looks like ! 545: .Ds ! 546: .ft R ! 547: .ta 0.90i ! 548: \*Mr2\fR \*(ar low word of expression or generator frame marker ! 549: last word of region to copy ! 550: \*(El ! 551: \*Mr0\fR \*(ar first word of region to copy ! 552: \*Mefp\fR \*(ar expression frame marker ! 553: \*M8(ap)\fR \*(ar descriptor for value to suspend ! 554: \*(El ! 555: \*Mgfp\fR \*(ar generator frame marker ! 556: last word of copied region ! 557: \*(El ! 558: first word of copied region ! 559: \*Msp\fR \*(ar descriptor for value to suspend ! 560: .De ! 561: .PP ! 562: The reactivation address that is saved in the generator frame is moved ! 563: into \*Mr1\fP for later use. It is then replaced by the address ! 564: of \*Mefail\fP so that when the frame is returned through,\^ control ! 565: will go to \*Mefail\fP. ! 566: .PP ! 567: \*Mfp\fP and \*Map\fP are restored from the generator frame. \*M_boundary\fP ! 568: is cleared because control is being returned to Icon code. ! 569: .PP ! 570: \*Mefp\fP is pointed at the previous expression frame. That is,\^ \*Mefp\fP ! 571: is moved back one step in the expression frame chain. ! 572: .PP ! 573: Control is returned to the interpreter loop by branching to ! 574: \*M0(r1)\fP,\^ the reactivation address originally saved in the generator frame. ! 575: .NH 3 ! 576: \*Mlib/lsusp.s\fR ! 577: .SH ! 578: Overview ! 579: .PP ! 580: \*Mlsusp\fP suspends a value from a limited expression. A limited ! 581: expression arises from a source code expression of the form ! 582: .Ds ! 583: \*(x1\ \e\ \^\*(x2 ! 584: .De ! 585: This limits \*(x1 to at most \*(x2 results. (\*(x2 ! 586: must be a non-negative integer.) ! 587: .PP ! 588: \*Mlsusp\fP is just like \*Mesusp\fP except that it has provisions for ! 589: checking and decrementing the limit counter and taking the appropriate ! 590: action when the counter reaches zero. As a simple example,\^ consider ! 591: .Ds ! 592: p(x\ \e\ \^2) ! 593: .De ! 594: which generates the ucode ! 595: .Ds ! 596: .ta .8i +.8i +.8i ! 597: mark L1 ! 598: var 0 \fR(variable \*Mp\fR)\*M ! 599: int 0 \fR(constant 2)\*M ! 600: limit ! 601: mark L0 ! 602: var 1 \fR(variable \*Mx\fR)\*M ! 603: lsusp ! 604: invoke 1 ! 605: unmark 1 ! 606: \*(El ! 607: .De ! 608: When control reaches the \*Mlsusp\fP,\^ the stack looks like ! 609: .Ds ! 610: .ft R ! 611: .S1 ! 612: expression marker with \*ML1\fR as failure label ! 613: descriptor for variable \*Mp\fR ! 614: descriptor for integer with value of 2 ! 615: \*Mefp\fR \*(ar expression marker with \*ML0\fR as failure label ! 616: \*Msp\fR \*(ar descriptor for variable \*Mx\fR ! 617: .De ! 618: The \*Mlimit\fP instruction insures that the value on the top of ! 619: the stack (its argument) is a non-negative integer. After \*Mlsusp\fP,\^ the ! 620: stack is ! 621: .Ds ! 622: .ft R ! 623: .ta 0.75i 3.3i ! 624: \*Mefp\fR \*(ar expression marker with \*ML1\fR as failure label ! 625: descriptor for variable \*Mp\fR ! 626: descriptor for integer with value of 2 ! 627: expression marker with \*ML0\fR as failure label ! 628: descriptor for variable \*Mx\fR ! 629: \*Mgfp\fR \*(ar generator frame built by \*Mlsusp\fR ! 630: descriptor for variable \*Mp\fR } duplicated region ! 631: \*Msp\fR \*(ar descriptor for variable \*Mx\fR ! 632: .De ! 633: This is the same thing that \*Mesusp\fP would do,\^ with the ! 634: exception that the limit counter,\^ the integer descriptor,\^ is not ! 635: part of the duplicated region. ! 636: .SH ! 637: Generic Operation ! 638: .LP ! 639: .Ls ! 640: .Np ! 641: The procedure frame created by the call to \*Mesusp\fP partially ! 642: forms the generator frame. ! 643: .Np ! 644: The limit counter is decremented and if it is zero,\^ no suspension ! 645: is performed. Instead,\^ the current expression frame is removed ! 646: and the limit counter is replaced by the value that would have been ! 647: suspended had the limitation not been in effect. \*Mlsusp\fP returns,\^ ! 648: leaving the value on the top of the stack. ! 649: .Np ! 650: If the limit counter is not zero,\^ execution proceeds exactly ! 651: as it does for \*Mesusp\fP with the exception that the determination ! 652: of the region to copy takes the limit counter into consideration and ! 653: does not include it in the region that is copied. ! 654: .Le ! 655: .SH ! 656: \*Mlsusp\fP on the VAX ! 657: .PP ! 658: When \*Mlsusp\fP is entered,\^ the generator frame is partially ! 659: constructed. \*Mipc\fP,\^ \*Mgfp\fP,\^ and \*Mefp\fP are saved in ! 660: the frame. ! 661: .PP ! 662: The expression frame and associated limit counter have the following ! 663: layout: ! 664: .Ds ! 665: .ft R ! 666: .St ! 667: 8 number of results left ! 668: \u limit counter\d ! 669: .sp -1 ! 670: 4 \*MD_INTEGER\fR (type and flags word) ! 671: \*Mefp\fR \*(ar 0 old expression frame pointer ! 672: -4 old generator frame pointer expression frame ! 673: -8 failure label ! 674: .De ! 675: .PP ! 676: The limit counter is decremented and if it is not zero,\^ control ! 677: passes to \*Mdosusp:\fP and from then on execution proceeds ! 678: exactly as it does in \*Mesusp\fP. Specifically,\^ ! 679: the code beginning at \*Mdosusp:\fP ! 680: is an exact duplicate of that in \*Mesusp\fP with the ! 681: exception of the instruction that determines the lower bound of ! 682: the region to be duplicated. \*Mesusp\fP uses ! 683: .Ds ! 684: addl3 $4,\^efp,\^r0 ! 685: .De ! 686: which points \*Mr0\fP at the word immediately above the expression ! 687: frame. \*Mlsusp\fP uses ! 688: .Ds ! 689: addl3 $12,\^efp,\^r0 ! 690: .De ! 691: which points \*Mr0\fP at the word above the limit counter that is ! 692: directly above the expression frame. ! 693: .PP ! 694: If the limit counter is zero,\^ the counter is to be replaced with the ! 695: value which was to be suspended. The value appears as an argument to ! 696: \*Mlsusp\fP. This is accomplished with ! 697: .Ds ! 698: movq 8(ap),\^4(efp) ! 699: .De ! 700: .PP ! 701: The value of \*Mgfp\fP that is stored in the ! 702: expression frame is restored. ! 703: .PP ! 704: The saved \*Mpc\fP in \*Mlsusp\fP's frame is moved into \*Mr0\fP for ! 705: later use. ! 706: .PP ! 707: The expression frame is removed by moving \*Mefp\fP into \*Msp\fP,\^ ! 708: which leaves the expression frame marker word that contains the old ! 709: \*Mefp\fP on the top of the stack. This word is popped of the stack ! 710: and moved into \*Mefp\fP,\^ restoring \*Mefp\fP and leaving the ! 711: return (would-be suspended) value on the top of the stack. ! 712: .PP ! 713: \*Map\fP and \*Mfp\fP are restored from the procedure frame made upon ! 714: entry to \*Mlsusp\fP. ! 715: .PP ! 716: \*Mlsusp\fP ``returns'' by jumping to \*M0(r0)\fP,\^ the return point ! 717: that was saved in the frame. The value that was to be suspended,\^ but ! 718: was not because of the limitation,\^ is left on the top of the stack. ! 719: .NH 3 ! 720: \*Mlib/psusp.s\fR ! 721: .SH ! 722: Overview ! 723: .PP ! 724: \*Mpsusp\fP suspends a result from an Icon procedure. \*Mpsusp\fP is ! 725: called from the interpreter loop and the value to suspend appears ! 726: as an argument to \*Mpsusp\fP. A generator frame is created and ! 727: the generator or expression frame immediately containing the frame ! 728: for the suspending procedure ! 729: is duplicated on top of the stack. \*Mpsusp\fP returns through ! 730: the duplicated frame,\^ leaving the suspending value on top ! 731: of the stack. A return from \*Mpsusp\fP is manifested as a return ! 732: from \*Minvoke\fP. ! 733: .PP ! 734: The \*Mpsusp\fP operation arises from the \*Msuspend \*(xx\fR statement. ! 735: .PP ! 736: \*Mpsusp\fP is conceptually similar to \*Mesusp\fP,\^ the difference ! 737: being that a procedure frame is part of the expression frame ! 738: being duplicated and that requires some extra work. ! 739: To get a feel for what \*Mpsusp\fP does,\^ consider a simple example: ! 740: .PP ! 741: .Ds ! 742: .ta .5i +.5i +.5i +.5i +.5i ! 743: procedure main() ! 744: f(p(3)) ! 745: end ! 746: ! 747: procedure p(a) ! 748: suspend a ! 749: end ! 750: .De ! 751: .LP ! 752: The generated ucode for \*Mmain\fP is ! 753: .Ds ! 754: .ta .8i +.8i +.8i +.8i +.8i ! 755: \*(El ! 756: mark L1 ! 757: var 0 \fR(the variable \*Mf\fR)\*M ! 758: var 1 \fR(the variable \*Mp\fR)\*M ! 759: int 0 \fR(the constant 3)\*M ! 760: invoke 1 ! 761: invoke 1 ! 762: \*(El ! 763: lab L1 ! 764: .De ! 765: and the generated code for \*Mp\fP is ! 766: .Ds ! 767: mark p.L1 ! 768: mark L0 ! 769: var 0 \fR(the argument \*Ma\fR)\*M ! 770: psusp ! 771: \*(El ! 772: lab p.L1 ! 773: .De ! 774: .LP ! 775: When control reaches the \*Minvoke\fP instruction,\^ the stack resembles ! 776: .Ds ! 777: .ft R ! 778: .S1 ! 779: \*Mefp\fR \*(ar expression marker with \*ML1\fR as failure address ! 780: descriptor for variable \*Mf\fR ! 781: descriptor for variable \*Mp\fR ! 782: \*Msp\fR \*(ar descriptor for constant 3 ! 783: .De ! 784: after \*Mp\fP has been invoked,\^ just before the \*Mpsusp\fP is ! 785: executed the stack is ! 786: .Ds ! 787: .ft R ! 788: .S1 ! 789: expression marker with \*ML1\fR as failure address ! 790: descriptor for variable \*Mf\fR ! 791: descriptor for variable \*Mp\fR ! 792: descriptor for constant 3 (becomes argument \*Ma\fR) ! 793: procedure frame for \*Mp\fR (created by \*Minvoke\fR) ! 794: expression marker with \*Mp.L1\fR as failure address ! 795: \*Mefp\fR \*(ar expression marker with \*ML0\fR as failure address ! 796: \*Msp\fR \*(ar descriptor for argument \*Ma\fR ! 797: .De ! 798: Just before control returns from \*Mpsusp\fP,\^ the stack is ! 799: .Ds ! 800: .ta 0.75i 3.25i ! 801: .ft R ! 802: expression marker with \*ML1\fR as failure address ! 803: descriptor for variable \*Mf\fR ! 804: descriptor for variable \*Mp\fR ! 805: descriptor for constant 3 ! 806: procedure frame for \*Mp\fR ! 807: expression marker with \*Mp.L1\fR as failure address ! 808: expression marker with \*ML0\fR as failure address ! 809: descriptor for argument \*Ma\fR (being suspended) ! 810: \*Mgfp\fR \*(ar generator frame built by \*Mpsusp\fR ! 811: descriptor for variable \*Mf\fR ! 812: descriptor for variable \*Mp\fR duplicated region ! 813: descriptor for constant 3 ! 814: \*Msp\fR \*(ar procedure frame for \*Mp\fR ! 815: .De ! 816: After \*Mpsusp\fP returns,\^ the situation is ! 817: .Ds ! 818: \*Mefp\fR \*(ar expression marker with \*ML1\fR as failure address ! 819: descriptor for variable \*Mf\fR ! 820: descriptor for variable \*Mp\fR ! 821: descriptor for constant 3 ! 822: procedure frame for \*Mp\fR ! 823: expression marker with \*Mp.L1\fR as failure address ! 824: expression marker with \*ML0\fR as failure address ! 825: descriptor for argument \*Ma\fR ! 826: \*Mgfp\fR \*(ar generator frame built by \*Mpsusp\fR ! 827: descriptor for variable \*Mf\fR ! 828: \*Msp\fR \*(ar descriptor for constant 3 (the suspended value) ! 829: .De ! 830: .PP ! 831: The return from \*Mpsusp\fP goes to the second \*Minvoke\fP,\^ which ! 832: calls \*Mf\fP with one argument,\^ the constant 3 that was ! 833: suspended. If \*Mf(3)\fP fails,\^ the procedure frame for ! 834: \*Mf\fP is removed. \*Mefail\fP takes control and returns through ! 835: the generator frame built by \*Mpsusp\fP. This leaves the ! 836: descriptor for \*Ma\fP on top of the stack. Execution continues ! 837: by \*Mp\fP failing,\^ and then \*Mmain\fP failing. ! 838: .SH ! 839: Generic Operation ! 840: .LP ! 841: .Ls ! 842: .Np ! 843: The procedure frame created by the call to \*Mpsusp\fP partially forms ! 844: the generator frame. \*M_boundary\fP is set as the current location ! 845: of \*Mfp\fP and it is added to the generator frame. ! 846: .Np ! 847: As in \*Mpret\fP,\^ the value being suspended must be dereferenced ! 848: in certain cases. For example,\^ if the value is a local variable ! 849: or an argument,\^ it is dereferenced. The same code that handles ! 850: dereferencing in \*Mpret\fP appears in \*Mpsusp\fP as well. ! 851: Note that while suspension leaves the local variables and arguments ! 852: of a procedure intact,\^ if the enclosing expression frame should ! 853: be removed by an \*Munmark\fP,\^ the procedure frame would be destroyed,\^ ! 854: leaving undeferenced values pointing at meaningless data. ! 855: .Np ! 856: The generator frame is completed by pointing \*Mgfp\fP at the boundary ! 857: value already in the frame and by adding \*M_k_level\fP,\^ \*M_line\fP,\^ ! 858: and \*M_file\fP. ! 859: .Np ! 860: The bounds of the expression frame to be duplicated are determined. ! 861: The lower bound is the low word of the procedure frame for the ! 862: suspending procedure and the upper bound is the marker for the ! 863: expression frame or generator frame which is just prior to the ! 864: procedure frame. As in \*Mesusp\fP,\^ if \*Mgfp\fP is non-zero,\^ ! 865: the marker it points to is used. Otherwise,\^ the marker referenced ! 866: by \*Mefp\fP is used. The \*Mgfp\fP and \*Mefp\fP values used ! 867: are those found in the procedure frame of the suspending procedure. ! 868: The region is copied to the top of the stack. ! 869: In the example,\^ the duplicated region contained the procedure ! 870: frame marker for \*Mp\fP,\^ and the descriptors for the constant 3,\^ ! 871: and the variables \*Mp\fP and \*Mf\fP. ! 872: .Np ! 873: If \*M&trace\fP is non-zero,\^ \*Mstrace\fP is called to produce ! 874: a trace message noting that the procedure is suspending a value. ! 875: \*Mstrace\fP requires the address of the block for the suspending ! 876: procedure and the address of the descriptor for the value being ! 877: suspended. ! 878: .Np ! 879: \*M_line\fP and \*M_file\fP are restored from the frame of the ! 880: suspending procedure. This is done because when \*Mpsusp\fP is ! 881: finished,\^ it is as if the Icon procedure had returned. Thus,\^ ! 882: the line number and file name need to be what they were before ! 883: the procedure was called. ! 884: .Np ! 885: The generator frame pointer saved in the duplicated procedure frame ! 886: on the top of the stack is replaced by the current value of ! 887: \*Mgfp\fP,\^ which points to the newly created generator frame. ! 888: When \*Mpsusp\fP returns through the frame on the top of the stack,\^ ! 889: \*Mgfp\fP is restored from the value in the frame and \*Mgfp\fP ! 890: then references the new generator frame. ! 891: .Np ! 892: The descriptor for \*(a0 in the argument list of the Icon procedure ! 893: that is suspending is a descriptor for the procedure itself. This ! 894: descriptor is replaced with the descriptor for the value being ! 895: suspended. When \*Mpsusp\fP is done,\^ this descriptor is left on the ! 896: top of the stack. ! 897: .Np ! 898: \*M_boundary\fP is cleared because control is returning to Icon code. ! 899: .Np ! 900: \*Mpsusp\fP returns. The return uses the duplicated procedure frame ! 901: on the top of the stack. The result is that it appears as if the ! 902: \*Minvoke\fP that originally called the suspending procedure has ! 903: returned. ! 904: .Le ! 905: .SH ! 906: \*Mpsusp\fP on the VAX ! 907: .PP ! 908: When \*Mpsusp\fP is entered,\^ the generator frame is partially ! 909: constructed as a result of the call. \*M_boundary\fP is set to the ! 910: current value of \*Mfp\fP and this value is pushed on the stack ! 911: as part of the generator frame. ! 912: .PP ! 913: The value being suspended needs to be dereferenced if it is a local ! 914: variable or an argument. This operation is the same as is done in ! 915: \*Mpret\fP; consult the section on it for details of the actions ! 916: taken. ! 917: .PP ! 918: The generator frame is completed by pointing \*Mgfp\fP at the frame ! 919: word containing the boundary value and by adding \*M_k_level\fP,\^ ! 920: \*M_line\fP,\^ and \*M_file\fP to the frame. ! 921: .PP ! 922: The region to be duplicated is determined. The low word to be copied ! 923: is the low word of the procedure frame of the suspending procedure. ! 924: (The word that contains the 0.) This is readily accessible as the ! 925: \*Mfp\fP saved the procedure frame of \*Mpsusp\fP and is placed in \*Mr7\fP. ! 926: .PP ! 927: The high word to be copied is dependent upon the expression and generator ! 928: environment of the suspending procedure. If the \*Mgfp\fP ! 929: in the suspender's environment is not zero,\^ the word just below the ! 930: generator frame marker is the highest word to be copied. If ! 931: \*Mgfp\fP is zero,\^ the word just below the expression marker pointed at ! 932: by \*Mefp\fP in the suspender's environment is the highest word to ! 933: be copied. ! 934: .PP ! 935: The fact that the saved \*Mefp\fP and \*Mgfp\fP ! 936: appear on the stack just below the \fInwords\fP word (referenced by ! 937: \*M0(ap)\fP) is used to retrieve and test them. As in \*Mesusp\fP,\^ ! 938: if the saved \*Mgfp\fP is non-zero,\^ ! 939: .Ds ! 940: .ft R ! 941: saved \*Mgfp\fR - 12 ! 942: .De ! 943: is used for the lower bound,\^ otherwise ! 944: .Ds ! 945: .ft R ! 946: saved \*Mefp\fR - 8 ! 947: .De ! 948: is the lower bound. \*Mr4\fP is pointed at the appropriate word on ! 949: the upper end. As in \*Mesusp\fP,\^ \*Msp\fP is moved down to ! 950: accommodate the region to be duplicated and the region is copied to ! 951: the top of the stack using a \*Mmovc3\fP. ! 952: .PP ! 953: After \*M_k_level\fP is decremented,\^ \*M_k_trace\fP is checked to see ! 954: if a trace message should be produced. If so,\^ \*Mstrace\fP is called with ! 955: pointers to the descriptors for the suspending procedure and the ! 956: value being suspended. The address of the value being suspended is ! 957: \*M8(ap)\fP and the address of the descriptor for the procedure is ! 958: determined using the standard ! 959: .Ds ! 960: &\*(a0 = (nargs * 8) + 8 + ap ! 961: .De ! 962: calculation. ! 963: .PP ! 964: The values of \*M_line\fP and \*M_file\fP are restored from the ! 965: suspender's frame. ! 966: .PP ! 967: The saved \*Mgfp\fP in the duplicated procedure frame on the top of ! 968: the stack must be replaced by the current value of \*Mgfp\fP. Finding the ! 969: location of the saved \*Mgfp\fP is a little tricky. The distance ! 970: between \*Mfp\fP and \*Map\fP in the duplicated frame is calculated ! 971: by subtracting the value of \*Mfp\fP from the \*Map\fP value and putting ! 972: the result in \*Mr0\fP. The value in \*Mr0\fP represents the distance ! 973: from the top of the stack to \*M0(ap)\fP. Adding the ! 974: current \*Msp\fP (which points at the low word of the duplicated ! 975: procedure frame) to \*Mr0\fP points \*Mr0\fP at the \fInwords\fP word ! 976: of the new frame. \*Map\fP normally points at the \fInwords\fP word,\^ ! 977: so \*Mr0\fP serves as a pseudo-\*Map\fP. It is known that the saved \*Mgfp\fP ! 978: is the second word below the \fInwords\fP word; thus the new \*Mgfp\fP ! 979: is stored in \*M\-8(r0)\fP,\^ replacing the old value. Thus,\^ when \*Mpsusp\fP ! 980: returns through the duplicated frame,\^ the value just stored is ! 981: the restored value of \*Mgfp\fP. ! 982: .PP ! 983: The descriptor for the suspending procedure must be replaced by the ! 984: descriptor for the return value. The previous calculation left ! 985: \*Mr0\fP pointing at \*M0(ap)\fP in the duplicated frame. The ! 986: address of \*(a0 is calculated using ! 987: .Ds ! 988: &\*(a0 = (nwords * 4) + 4 + r0 ! 989: .De ! 990: Note that \*M+ 4\fP accounts for the four bytes that the \fInwords\fP ! 991: word itself occupies. The descriptor for the return value is put in ! 992: place using a \*Mmovq\fP. ! 993: .PP ! 994: \*Msp\fP is moved into \*Mfp\fP so that the pending return uses the ! 995: new frame on the top of the stack. ! 996: .PP ! 997: \*M_boundary\fP is cleared because control is going back into Icon ! 998: code. ! 999: .PP ! 1000: A \*Mret\fP is executed to return from \*Mpsusp\fP. This return uses ! 1001: the duplicated procedure frame and thus the duplicated frame is ! 1002: removed. The final result is that it looks like the original call to ! 1003: \*Minvoke\fP that started the Icon procedure has returned and ! 1004: the suspended value is left on the top of the stack. ! 1005: .ne 3v ! 1006: .NH 3 ! 1007: \*Mrt/suspend.s\fR ! 1008: .SH ! 1009: Overview ! 1010: .PP ! 1011: \*Msuspend\fP suspends a value from a built-in procedure or operator. ! 1012: \*Msuspend\fP is similar to \*Mpsusp\fP and amounts to little ! 1013: more than a simplified version of it. Recall that built-in ! 1014: procedures are C functions; thus,\^ \*Msuspend\fP is directly called ! 1015: from C. ! 1016: .PP ! 1017: A generator frame is created and the generator or expression frame ! 1018: immediately containing the frame for the suspending procedure ! 1019: is duplicated on the top of the stack. \*Msuspend\fP returns through ! 1020: the duplicated frame,\^ leaving the value being suspended on the top ! 1021: of the stack. When \*Msuspend\fP returns,\^ it appears as a return ! 1022: from the original call to the built-in procedure. ! 1023: .PP ! 1024: Note that \*Msuspend\fP handles the suspension of values from both ! 1025: built-in procedures such as \*Mupto\fP and from operators such as ! 1026: the element generation operator,\^ \*M!\fP. For built-in procedures,\^ ! 1027: the procedure frame is built by \*Minvoke\fP,\^ while for operators,\^ ! 1028: the procedure frame is built directly by the call to the appropriate ! 1029: function from the interpreter loop. ! 1030: The value being suspended by the C function is represented ! 1031: by the \*(a0 descriptor in the argument list. When ! 1032: \*Msuspend\fP is called,\^ the value to suspend is in place in ! 1033: \*Marg0\fP. ! 1034: .SH ! 1035: Generic Operation ! 1036: .PP ! 1037: \*Msuspend\fP can be considered as a ``subset'' of \*Mpsusp\fP. The ! 1038: descriptions of operations in this section and the next are excerpts ! 1039: from \*Mpsusp\fP. ! 1040: .LP ! 1041: The actions of \*Mpsusp\fP that are \fInot\fP taken by \*Msuspend\fP are: ! 1042: .Ls ! 1043: .Np ! 1044: The return value does not need to be dereferenced because the ! 1045: suspending function has already taken care of that. ! 1046: .Np ! 1047: No tracing message is produced because tracing is only done ! 1048: for Icon procedures. ! 1049: .Np ! 1050: The return value does not need to be moved into the duplicated ! 1051: procedure frame because it is already in place in the original procedure ! 1052: frame and when the frame is duplicated,\^ the return value is ! 1053: duplicated along with it. ! 1054: .Np ! 1055: \*M_k_level\fP is not decremented because \*M&level\fP keeps track of ! 1056: Icon procedure calls and \*Msuspend\fP is returning from a C routine. ! 1057: \*M_line\fP,\^ and \*M_file\fP are not restored because they are ! 1058: not part of the procedure frame of the built-in procedure. ! 1059: .Le ! 1060: .LP ! 1061: The operations that are performed by \*Msuspend\fP are: ! 1062: .Ls ! 1063: .Np ! 1064: The procedure frame created by the call to \*Msuspend\fP partially forms ! 1065: the generator frame. \*M_boundary\fP is set as the current location ! 1066: of \*Mfp\fP and it is added to the generator frame. ! 1067: .Np ! 1068: The bounds of the expression frame to be duplicated are determined. ! 1069: The lower bound is the low word of the procedure frame for the ! 1070: suspending procedure and the upper bound is the marker for the ! 1071: expression frame or generator frame which is just prior to the ! 1072: procedure frame. As in \*Mesusp\fP,\^ if \*Mgfp\fP is non-zero,\^ ! 1073: the marker it points to is used. Otherwise,\^ the marker referenced ! 1074: by \*Mefp\fP is used. The \*Mgfp\fP and \*Mefp\fP values used ! 1075: are those found in the procedure frame of the suspending procedure. ! 1076: The region is copied to the top of the stack. ! 1077: .Np ! 1078: The generator frame pointer saved in the duplicated procedure frame ! 1079: on the top of the stack is replaced by the current value of ! 1080: \*Mgfp\fP,\^ which points to the newly created generator frame. ! 1081: When \*Msuspend\fP returns through the frame on the top of the stack,\^ ! 1082: \*Mgfp\fP is restored from the value in the frame and \*Mgfp\fP ! 1083: then references the new generator frame. ! 1084: .Np ! 1085: \*M_boundary\fP is cleared because control is returning to Icon code. ! 1086: .Np ! 1087: \*Msuspend\fP returns. The return uses the duplicated procedure frame ! 1088: on the top of the stack. The result is that it appears as if the ! 1089: original call to the suspending procedure has returned. ! 1090: .Le ! 1091: .SH ! 1092: \*Msuspend\fP on the VAX ! 1093: .PP ! 1094: When \*Msuspend\fP is entered,\^ the generator frame is partially ! 1095: constructed as a result of the call. \*M_boundary\fP is set to the ! 1096: current value of \*Mfp\fP and this value is pushed on the stack ! 1097: as part of the generator frame. ! 1098: The generator frame is completed by pointing \*Mgfp\fP at the frame ! 1099: word containing the boundary value and by adding \*M_k_level\fP,\^ ! 1100: \*M_line\fP,\^ and \*M_file\fP to the frame. ! 1101: .PP ! 1102: The region to be duplicated is determined. The low word to be copied ! 1103: is the low word of the procedure frame of the suspending function. ! 1104: (The word that contains the 0.) This is readily accessible as the ! 1105: \*Mfp\fP saved in the procedure frame of \*Msuspend\fP and \*Mr7\fP is ! 1106: pointed at the word containing the saved \*Mfp\fP. ! 1107: .PP ! 1108: The high word to be copied is dependent upon the expression and generator ! 1109: environment of the suspending function. If the \*Mgfp\fP ! 1110: in the suspender's environment is not zero,\^ the word just below the ! 1111: generator frame marker is the highest word to be copied. If ! 1112: \*Mgfp\fP is zero,\^ the just word below the expression marker pointed at ! 1113: by \*Mefp\fP in the suspender's environment is the highest word to ! 1114: be copied. ! 1115: .PP ! 1116: The fact that the saved \*Mefp\fP and \*Mgfp\fP ! 1117: appear on the stack just below the \fInwords\fP word (referenced by ! 1118: \*M0(ap)\fP) is used to retrieve and test them. As in \*Mesusp\fP,\^ ! 1119: if the saved \*Mgfp\fP is non-zero,\^ ! 1120: .Ds ! 1121: .ft R ! 1122: saved \*Mgfp\fR - 12 ! 1123: .De ! 1124: is used for the lower bound,\^ otherwise ! 1125: .Ds ! 1126: .ft R ! 1127: saved \*Mefp\fR - 8 ! 1128: .De ! 1129: is the lower bound. \*Mr4\fP is pointed at the appropriate word on ! 1130: the upper end. As in \*Mesusp\fP,\^ \*Msp\fP is moved down to ! 1131: accommodate the region to be duplicated and the region is copied to ! 1132: the top of the stack using a \*Mmovc3\fP. ! 1133: .PP ! 1134: The saved \*Mgfp\fP in the duplicated procedure frame on the top of ! 1135: the stack must be replaced by the current value of \*Mgfp\fP. Finding the ! 1136: location of the saved \*Mgfp\fP is a little tricky. The distance ! 1137: between \*Mfp\fP and \*Map\fP in the duplicated frame is calculated ! 1138: by subtracting the \*Mfp\fP value from the \*Map\fP value and putting ! 1139: the result in \*Mr0\fP. The value in \*Mr0\fP represents the distance ! 1140: from the top of the stack to \*M0(ap)\fP. Adding the ! 1141: current \*Msp\fP (which points at the low word of the duplicated ! 1142: procedure frame) to \*Mr0\fP points \*Mr0\fP at the \fInwords\fP word ! 1143: of the new frame. \*Map\fP normally points at the \fInwords\fP word,\^ ! 1144: so \*Mr0\fP serves as a pseudo-\*Map\fP. It is known that the saved \*Mgfp\fP ! 1145: is the second word below the \fInwords\fP word and thus the new \*Mgfp\fP ! 1146: is stored in \*M\-8(r0)\fP,\^ replacing the old value. Thus,\^ when \*Msuspend\fP ! 1147: returns through the duplicated frame,\^ the value just stored is ! 1148: the restored value of \*Mgfp\fP. ! 1149: .PP ! 1150: A \*Mret\fP is executed to return from \*Msuspend\fP. This return uses ! 1151: the duplicated procedure frame and thus the duplicated frame is ! 1152: removed. The final result is that it looks like the original call to ! 1153: function has returned and ! 1154: the suspended value is left on the top of the stack. ! 1155: .ne 2i ! 1156: .NH 3 ! 1157: \*Mfunctions/display.c\fR ! 1158: .SH ! 1159: Overview ! 1160: .PP ! 1161: \*Mdisplay.c\fP implements the Icon function \*Mdisplay()\fP. ! 1162: \*Mdisplay\fP traces back through Icon procedure frames printing various ! 1163: sorts of information. Therefore,\^ some of the code in \*Mdisplay\fP ! 1164: is machine dependent. ! 1165: .SH ! 1166: Generic Operation ! 1167: .PP ! 1168: \*Mdisplay\fP makes one calculation that is machine dependent. The ! 1169: calculation is to take a frame whose address is contained in the ! 1170: variable \*Mfp\fP and calculate the address of the procedure ! 1171: descriptor in the frame that is pointed at by the \*Mfp\fP value ! 1172: saved in the frame that \*Mfp\fP references. ! 1173: .SH ! 1174: \*Mdisplay\fP on the VAX ! 1175: .PP ! 1176: \*Map\fP and \*Mfp\fP are restored from the frame referenced by ! 1177: \*Mfp\fP. The number of arguments to the procedure is contained in ! 1178: \*Map\^[1]\fP. This is loaded into the variable \*Mn\fP. The address ! 1179: of the procedure descriptor (\*(a0) is calculated using: ! 1180: .Ds ! 1181: dp = ap + 2 + 2*n ! 1182: .De ! 1183: Note that this is the same computation that is made at several points ! 1184: in the assembly language routines. As in \*Msweep\fP,\^ the calculations are ! 1185: being made using \*Mint *\fP variables and thus the constants ! 1186: represent word counts instead of byte counts as they do in the ! 1187: assembly language routines. ! 1188: ! 1189: .NH 3 ! 1190: \*Mrt/gcollect.s\fR ! 1191: .SH ! 1192: Overview ! 1193: .PP ! 1194: \*Mgcollect\fP is a simple routine that insures that garbage ! 1195: collections are done using the stack for the main co-expression. ! 1196: This done by saving certain values in the co-expression block of the ! 1197: current co-expression,\^ restoring values from the co-expression block ! 1198: for \*M&main\fP,\^ calling the garbage collector,\^ and then restoring ! 1199: the original values. ! 1200: \*Mgcollect\fP takes a single argument that is passed directly ! 1201: to \*Mcollect\fP. ! 1202: .PP ! 1203: If co-expressions are not implemented,\^ \*Mgcollect\fP need only ! 1204: consist of a call to \*Mcollect\fP,\^ being sure to pass its argument ! 1205: on through. ! 1206: .SH ! 1207: \*Mgcollect\fP on the VAX ! 1208: .PP ! 1209: \*Mr0\fP is pointed at the heap block for the current co-expression. ! 1210: \*Msp\fP,\^ \*Map\fP,\^ and \*M_boundary\fP are saved in the appropriate ! 1211: words of the block. ! 1212: .PP ! 1213: \*Mr0\fP is pointed at the heap block for \*M&main\fP,\^ the ! 1214: co-expression that is initially active. \*Msp\fP,\^ \*Map\fP,\^ and \*M_boundary\fP ! 1215: are restored from values saved in the block. ! 1216: .PP ! 1217: The argument to \*Mgcollect\fP is pushed on the stack,\^ and ! 1218: \*Mcollect\fP is called with one argument. ! 1219: .PP ! 1220: \*Mr0\fP is pointed at the heap block for the current co-expression ! 1221: and the \*Msp\fP,\^ \*Map\fP,\^ and \*M_boundary\fP values saved at the ! 1222: start of the routine are restored. ! 1223: .PP ! 1224: \*Mgcollect\fP returns. ! 1225: .NH 3 ! 1226: \*Mrt/sweep.c\fR ! 1227: .SH ! 1228: Overview ! 1229: .PP ! 1230: \*Msweep\fP is used during garbage collection to sweep a stack,\^ ! 1231: marking all the descriptors in the stack. \*Msweep\fP begins at ! 1232: the low word (the top) of a stack and moves up through the stack,\^ ! 1233: looking for descriptors and marking them. ! 1234: A stack is composed of four kinds of ! 1235: objects: descriptors,\^ and markers for procedure,\^ ! 1236: generator,\^ and expression frames. \*Msweep\fP uses knowledge of ! 1237: frame marker formats to skip over markers and to process the ! 1238: intervening descriptors. ! 1239: .PP ! 1240: Although \*Msweep\fP is written in C,\^ the knowledge of frame formats that it ! 1241: employs requires that it be written on a per-machine basis. ! 1242: .SH ! 1243: Generic Operation ! 1244: .PP ! 1245: There are three places that descriptors can appear on the stack: ! 1246: above an expression marker,\^ in an argument list,\^ and above an ! 1247: argument list. This can be considered as only two places because ! 1248: descriptors above the argument list can be considered as part of the ! 1249: argument list. ! 1250: .PP ! 1251: \*Msweep\fP is called with a single argument that is the address of ! 1252: the first word of a stack to mark. For purposes of discussion assume ! 1253: that \*Msp\fP references the stack word of current interest. ! 1254: \*Msweep\fP has a loop and each time through the loop,\^ one of four actions ! 1255: is taken based on the word that \*Msp\fP is pointing at: ! 1256: .Ls ! 1257: .Np ! 1258: If \*Msp\fP is pointing at the low word of a procedure frame marker,\^ \*Msp\fP ! 1259: is moved to point at the low word of the argument list of the ! 1260: procedure. \*Mefp\fP,\^ \*Mgfp\fP,\^ and \*Mpfp\fP are restored from the ! 1261: procedure frame. The number of arguments to the procedure is placed ! 1262: in \fInargs\fP. ! 1263: .Np ! 1264: If \*Msp\fP is pointing at the low word of a generator frame marker,\^ ! 1265: \*Mfp\fP is restored from the boundary word of the generator frame ! 1266: and \*Msp\fP is pointed at the low word of the frame referenced by \*Mfp\fP. ! 1267: .Np ! 1268: If \*Msp\fP is pointing at the low word of an expression frame ! 1269: marker,\^ \*Mgfp\fP and \*Mefp\fP are restored from the marker and ! 1270: \*Msp\fP is pointed at the word above the marker. ! 1271: .Np ! 1272: If none of the preceding conditions are true,\^ the word that \*Msp\fP ! 1273: points at is assumed to be the low word of a descriptor and that ! 1274: descriptor is marked. \*Msp\fP is incremented by 2 to move past ! 1275: the descriptor. If \fInargs\fP is not zero,\^ it is decremented. ! 1276: .Le ! 1277: .PP ! 1278: This process continues as long as \*Mfp\fP and \fInargs\fP are not ! 1279: both zero. \fInargs\fP is used so that the arguments in the very ! 1280: last frame are processed. The \*Mfp\fP at that point is 0. ! 1281: .SH ! 1282: \*Msweep\fP on the VAX ! 1283: .PP ! 1284: The routine \*Mgetap\fP is used by \*Msweep\fP. \*Mgetap\fP ! 1285: takes the address of a frame and returns the address of \*M0(ap)\fP ! 1286: in that frame. That is,\^ it returns the address of the start of the ! 1287: argument list for the frame. ! 1288: .PP ! 1289: Note that the C code uses \*Mint *\fP variables for the various ! 1290: calculations that are performed. Thus,\^ a calculation such as ! 1291: \*Mx + 2\fP is actually performing \*Mx + 8\fP. Similarly,\^ ! 1292: \*Mx\^[\-1]\fP would be the address \*Mx \- 4\fP. ! 1293: .PP ! 1294: \*Msweep\fP is called with a single parameter,\^ \*Mfp\fP. \*Mfp\fP ! 1295: holds the address of the frame with which to start the marking process. This ! 1296: address is a \*M_boundary\fP value,\^ and thus it ! 1297: points to the 0 (condition handler) word of a procedure frame. ! 1298: .PP ! 1299: \*Msp\fP is set to \*Mfp \- FRAMELIMIT\fP,\^ so that the first time ! 1300: throughout the loop,\^ the procedure frame on the top of the stack ! 1301: is processed. This gets the ball rolling,\^ so to speak. ! 1302: .PP ! 1303: \*Msweep\fP loops while \*Mfp\fP and \fInargs\fP are not both zero. ! 1304: It should be noted that the variables used in \*Msweep\fP have no ! 1305: connection to actual registers other than having the same name. ! 1306: .PP ! 1307: If \*Msp\fP is equal to \*Mfp \- FRAMELIMIT\fP,\^ it indicates that \*Msp\fP ! 1308: is pointing at a procedure frame marker. ! 1309: \*MFRAMELIMIT\fP is 2 on the VAX because ! 1310: there are two words,\^ the saved values of \*M_line\fP and \*M_file\fP,\^ ! 1311: that lie below the word in the frame that \*Mfp\fP points at. ! 1312: .PP ! 1313: When a procedure frame marker is encountered,\^ \*Mefp\fP and \*Mgfp\fP values ! 1314: are restored using negative displacements from \*Map\fP. \*Map\fP ! 1315: points at the \fInwords\fP word of the frame,\^ and \*Msp\fP is set to ! 1316: \*Map + 2\fP so that it points at the descriptor for the first ! 1317: argument. \fInargs\fP is loaded from the argument list. \*Map\fP ! 1318: and \*Mfp\fP are restored from the frame ! 1319: .PP ! 1320: A generator frame is indicated by \*Msp\fP being equal to \*Mgfp \- ! 1321: 3\fP. This is because there are three words,\^ \*M_line\fP,\^ ! 1322: \*M_file\fP,\^ and \*M_k_level\fP in the generator frame below the ! 1323: word that generator frame pointer points at. \*Mfp\fP is restored ! 1324: from the frame. A new \*Map\fP value is calculated from \*Mfp\fP ! 1325: using \*Mgetap\fP. \*Msp\fP is set to \*Mfp \- FRAMELIMIT\fP to cause ! 1326: recognition of a procedure frame the next time around. ! 1327: .PP ! 1328: An expression frame marker is indicated by \*Msp\fP being equal to \*Mefp \- ! 1329: 2\fP. \*Mefp\fP and \*Mgfp\fP are restored from the marker. ! 1330: \*Msp\fP is incremented by 3 which leaves it pointing at the word ! 1331: above the marker,\^ which may be a descriptor. ! 1332: .PP ! 1333: If \*Msp\fP suits none of the preceding criteria,\^ it is assumed to ! 1334: point at a descriptor. \*Mmark\fP is called with the value of \*Msp\fP as ! 1335: its argument. \*Msp\fP is incremented by 2 to move past the ! 1336: descriptor just marked. If \fInargs\fP is non-zero,\^ it is ! 1337: decremented. ! 1338: .bp ! 1339: .SH ! 1340: Acknowledgements ! 1341: .PP ! 1342: Ralph Griswold patiently suffered through a number of drafts of this ! 1343: document and made innumerable suggestions about grammar,\^ form,\^ and content. ! 1344: Steve Wampler graciously answered a number of questions about the internal ! 1345: workings of Icon and also made a number of comments on a late draft. ! 1346: .SH ! 1347: References ! 1348: .LP ! 1349: .IP 1. ! 1350: R. E. Griswold,\^ R. K. McConeghy,\^ and W. H. Mitchell,\^ \fIA Tour Through the ! 1351: C Implementation of Icon; Version 5.9\fR,\^ ! 1352: Technical Report 84-11, Department of Computer Science, The University of ! 1353: Arizona, August 1984. ! 1354: .IP 2. ! 1355: \fIVAX Architecture Handbook\fP,\^ Digital Equipment Corporation,\^ ! 1356: Maynard,\^ Massachusetts,\^ 1982. ! 1357: .IP 3. ! 1358: D. M. Ritchie,\^ A Tour Through the UNIX C Compiler,\^ \fIUNIX Programmers ! 1359: Manual,\^ Volume 2B\fP,\^ Bell Telephone Laboratories,\^ Inc.,\^ Murray Hill,\^ ! 1360: New Jersey,\^ 1979. ! 1361: .IP 4. ! 1362: S. C. Johnson,\^ A Tour Through the Portable C Compiler,\^ \fIUNIX Programmers ! 1363: Manual,\^ Volume 2B\fP,\^ Bell Telephone Laboratories,\^ Inc.,\^ Murray Hill,\^ ! 1364: New Jersey,\^ 1979. ! 1365: .IP 5. ! 1366: R. E. Griswold,\^ \fIAn Overview of the Porting Process for Version 5.9 ! 1367: of Icon\fR, ! 1368: Department of Computer Science,\^ The University of Arizona,\^ August 1984.
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.