|
|
1.1 ! root 1: .de Ip ! 2: .IP \\$1 ! 3: .br ! 4: .. ! 5: .nr $1 3 ! 6: .nr $2 7 ! 7: .nr $3 0 ! 8: .NH 3 ! 9: \*Miconx/start.s\fR ! 10: .SH ! 11: Overview ! 12: .PP ! 13: The routine \*Mmstart\fP in \*Mstart.s\fP is used to get Icon started. ! 14: When the Icon interpreter is executed,\^ the C routine \*Mmain\fP passes ! 15: control to \*Mmstart\fP,\^ and merely serves as a front-end for \*Mmstart\fP. ! 16: .SH ! 17: Generic Operation ! 18: .Ls ! 19: .Np ! 20: Call the routine \*Minit\fR with the name of the ! 21: file to interpret as its argument. ! 22: .Np ! 23: Make an Icon list out of the command line arguments using ! 24: the \*Mllist\fR function. ! 25: .Np ! 26: Invoke the main procedure of the Icon program. ! 27: .Le ! 28: .SH ! 29: \*Mstart\fP on the VAX ! 30: .PP ! 31: There is a short main program in \*Miconx/main.c\fP that calls \*Mmstart\fP ! 32: with two arguments: ! 33: .Ds ! 34: .S1 ! 35: main(argc, argv) ! 36: int argc; ! 37: char **argv; ! 38: { ! 39: mstart(argc, argv); ! 40: } ! 41: .De ! 42: .PP ! 43: The number of command line arguments is in \*Margc\fP, and \*Margv\fP is a ! 44: pointer to an array of pointers to strings representing the arguments. ! 45: \*Margv\^[0]\fP is the command used to invoke the interpreter and \*Margv\^[1]\fP ! 46: is the name of the file being interpreted. Additional command line arguments ! 47: are passed along to the main procedure of the Icon program. When ! 48: \*Mmstart\fP gets control, \*M4(ap)\fP is the \*Margc\fP value and ! 49: \*M8(ap)\fP is the argv value. ! 50: .PP ! 51: The first action taken by \*Mmstart\fP is to call \*Minit\fR to initialize the ! 52: Icon run-time system. \*Minit\fR loads the header and code portions of ! 53: the interpretable file into memory,\^ so \*Minit\fR needs the ! 54: name of the interpretable file. The word at \*M8(ap)\fP is loaded into ! 55: \*Mr9\fP, pointing it at \*Margv\^[0]\fP. Then the name of the file to interpret ! 56: (\*Margv\^[1]\fP), residing at \*M4(r9)\fP, is pushed on the stack as the ! 57: argument for \*Minit\fP, which is then called. ! 58: .PP ! 59: In order to provide conformity with the usual execution environment ! 60: of Icon procedures,\^ an expression frame is created for the execution ! 61: of the main procedure. Both the old expression frame pointer and the ! 62: old generator frame pointer are set to be 0 in the expression frame ! 63: for \*Mmain\fP. The failure label must point to an interpreter opcode that ! 64: will terminate execution of the program. The opcode 0 is used ! 65: for this purpose. A word of storage,\^ \*Mflab\fR,\^ is declared and ! 66: initialized to 0. The failure label points to \*Mflab\fR. Thus,\^ if ! 67: \*Mmain\fP fails,\^ the interpreter executes the \*Mquit\fR opcode. ! 68: .PP ! 69: The next task is to push the descriptor for the procedure main ! 70: on the stack for later use by \*Minvoke\fR. The variable ! 71: \*M_globals\fR contains the address of the list of global variable ! 72: descriptors. ! 73: The first global variable descriptor is always the one for the ! 74: procedure main; if no main procedure was found when the program ! 75: was linked,\^ the descriptor ! 76: will be \*M&null\fR. The value of \*M_globals\fR is loaded into ! 77: \*Mr0\fR and the word then referenced by \*Mr0\fR is checked to see if it ! 78: is equal to \*MD_PROC\fR. (The first word of a descriptor for a procedure ! 79: is always equal to \*MD_PROC\fR.) ! 80: .\"\^[? not sure if we really need to check for D_PROC,\^ ! 81: .\"I think just checking for ~= 0 will suffice.] ! 82: If the word is not equal ! 83: to \*MD_PROC\fR,\^ a branch is made to \*Mnomain\fR which generates ! 84: the appropriate run-time error. Otherwise,\^ the descriptor ! 85: for \*Mmain\fP is pushed onto the stack. (The effect of the instruction ! 86: \*Mmovq\ (r0),\^\-(sp)\fR ! 87: is to move 8 bytes (the size of a quadword) ! 88: starting at the address referenced by \*Mr0\fR to the ! 89: 8 bytes referenced by the \*Msp\fR after subtracting 8 from the ! 90: \*Msp\fR.) ! 91: .PP ! 92: The main procedure is to be invoked with a list consisting of the command ! 93: line arguments (if any). The Icon run-time routine \*Mllist\fR is used to ! 94: make the list that is passed to the main procedure. \*Mllist\fR stores ! 95: the descriptor for the list that it creates in the descriptor above its ! 96: first argument descriptor,\^ so to accommodate the result,\^ a null descriptor ! 97: is pushed on the stack using the \*Mclrq\fR instruction. Note that ! 98: because \*Mllist\fP calls \*Msetbound\fP and \*Mclrbound\fP,\^ it is not ! 99: possible to execute all of \*Mstart\fP until \*Mrt/setbound.s\fP has been ! 100: completed. ! 101: .PP ! 102: At the beginning of this routine,\^ \*Mr9\fR was set to point ! 103: at the first word of the argument list. ! 104: Neither the name of the Icon interpreter nor the name of the interpretable ! 105: file is desired in the argument list passed to main,\^ so \*Mr9\fR is ! 106: twice incremented by 4 (the size in bytes of a word) ! 107: to point it at the first actual program argument. ! 108: .PP ! 109: The next step is to construct the argument list for \*Mllist\fR. For ! 110: each command line argument,\^ the address of the string and then its ! 111: length are pushed on the stack. The length and address pairs form ! 112: descriptors that \*Mllist\fP makes an Icon list from. ! 113: \*Mr8\fR is used to count the arguments. ! 114: After the addresses and lengths of each argument have been pushed,\^ the ! 115: number of arguments is pushed on the stack. ! 116: At this point,\^ the stack looks like this: ! 117: .Ds ! 118: .ft R ! 119: .S1 ! 120: descriptor for main procedure (2 4-byte words) ! 121: a null descriptor (2 words containing 0) ! 122: address of first argument to Icon program ! 123: length of first argument ! 124: \*(El ! 125: address of last argument to Icon program ! 126: length of last argument ! 127: \*Msp\fR \*(ar number of arguments ! 128: .De ! 129: .LP ! 130: All addresses and lengths are one word in size. The \*Mcalls\fR ! 131: instruction needs to be told how many words are in its argument list. ! 132: (This is necessary because when a return is made from the subroutine,\^ ! 133: the specified number of words are removed from the stack.) There ! 134: are two words for each argument and an additional word for the number ! 135: of arguments. (Do not confuse the argument count for the \*Mllist\fR ! 136: subroutine and the argument list size.) ! 137: \*Mr8*2+1\fP is calculated in \*Mr8\fP. This value is used ! 138: as the argument list length for the \*Mcalls\fR instruction. ! 139: When \*Mllist\fR returns,\^ the arguments are stripped from the ! 140: stack and the stack looks like this: ! 141: .Ds ! 142: .ft R ! 143: .S1 ! 144: descriptor for main procedure ! 145: \*Msp\fR \*(ar descriptor for list of command line arguments ! 146: .De ! 147: .LP ! 148: Note that the null descriptor pushed earlier received the result of ! 149: the \*Mllist\fR function. ! 150: .PP ! 151: At this point,\^ the main procedure is ready to be invoked. The descriptor for ! 152: the main procedure is \*(a0 and the descriptor for the list of ! 153: command line arguments is \*(a1. Before invoking the main procedure,\^ ! 154: the procedure frame pointer and the generator frame pointer are cleared. ! 155: The main procedure is being invoked with one argument,\^ so a constant ! 156: 1 is pushed on the stack. The \*Mcalls\fR instruction is given an ! 157: argument of 3 because a word is used for the number of arguments and ! 158: two additional words are used for the descriptor for the list of command ! 159: line arguments. ! 160: .PP ! 161: If the main procedure fails,\^ the interpreter will encounter the ! 162: 0 opcode discussed earlier. If the main procedure returns (this ! 163: usually isn't done),\^ the return will manifest itself as \f3invoke\fR ! 164: returning. If this happens,\^ \*M_c_exit\fR is called with an ! 165: argument of 0. Incidentally,\^ this also happens when the interpreter ! 166: hits the 0 opcode. ! 167: .PP ! 168: There is a block of code labeled \*Mnomain\fR that is executed when ! 169: no main procedure is found. This calls the routine \*Mrunerr\fR to ! 170: produce an error message. The actual call made is \*Mrunerr(117,\^0)\fR. ! 171: The number 117 is looked up in a table of run-time errors. If the second ! 172: argument to \*Mrunerr\fR is non-zero,\^ it is interpreted as being the ! 173: address of a descriptor and the descriptor is examined to produce ! 174: an ``offending value'' to accompany the run-time error. ! 175: .PP ! 176: The last portion of executable code in \f3start.s\fR is the subroutine ! 177: \*M_c_exit\fR. If the variable \*M_monres\fR is non-zero,\^ it indicates ! 178: that profiling is on,\^ and it must be turned off. This is accomplished ! 179: by calling \*M_monitor(0)\fR. The routine \*M__cleanup\fR is then ! 180: called to shut down the i/o system. Finally,\^ \*M_exit\fR is called ! 181: with the argument of \*M_c_exit\fR to terminate execution of the ! 182: Icon interpreter. ! 183: .PP ! 184: There are several data declarations in \*Mstart.s\fR. The first data ! 185: declaration is a \*M.space 60\fR. This is an accommodation for the ! 186: garbage collector. It ! 187: insures that enough of the start of the data section ! 188: is used up to force the addresses of other data objects to be ! 189: greater than the defined constant \*MMAXTYPE\fR in \f3h/rt.h\fR. ! 190: .PP ! 191: Some assorted declarations are next. \*Mflab\fR is referenced ! 192: by the interpreter if the main procedure fails. It must be at ! 193: least a byte long and contain a 0. \*M_boundary\fR must be a ! 194: word long and contain a 0. \*M_environ\fR must be a word long; ! 195: its contents are unimportant as it is written into at the beginning ! 196: of \*Mstart.s\fR. ! 197: .PP ! 198: The \*M_tended\fR array is also used in conjunction with garbage ! 199: collection. It must declare space for five descriptors (two words ! 200: per descriptor) that are initialized to 0. ! 201: .\"\^[?] ! 202: The label \*M_etended\fR ! 203: is used to mark the end of the \*M_tended\fR array. ! 204: .ne 1i ! 205: .NH 3 ! 206: \*Mrt/setbound.s\fR ! 207: .SH ! 208: Overview ! 209: .PP ! 210: \*Msetbound.s\fP contains code for \*Msetbound\fP and \*Mclrbound\fP. ! 211: \*Msetbound\fP sets \*M_boundary\fP under appropriate conditions and ! 212: \*Mclrbound\fP clears \*M_boundary\fP under appropriate conditions. ! 213: .SH ! 214: Generic Operation ! 215: .PP ! 216: When a call is made from Icon into C,\^ \*M_boundary\fP must be set to point ! 217: at the procedure frame on the top of the stack. C routines that can ! 218: be called from Icon have a call to \*Msetbound\fP as their first ! 219: operation. If \*Msetbound\fP is called and \*M_boundary\fP is not ! 220: set,\^ that is,\^ \*M_boundary\fP has a value of zero,\^ \*Mboundary\fP is ! 221: set to value of the \*Mfp\fP of the calling procedure. ! 222: .PP ! 223: When a C routine returns to Icon,\^ \*M_boundary\fP must be cleared. ! 224: If \*M_boundary\fP has the same value as \*Mfp\fP,\^ the routine was ! 225: called from Icon and thus when it returns,\^ it returns to Icon. ! 226: At appropriate return points in routines,\^ a call to \*Mclrbound\fP is ! 227: made. If the return takes control back to Icon,\^ \*M_boundary\fP ! 228: is cleared. ! 229: .SH ! 230: \*Msetbound\fP on the VAX ! 231: .PP ! 232: The value of \*M_boundary\fP is tested. If \*Mboundary\fP is not set,\^ ! 233: that is,\^ if it has a value of zero,\^ it is set to the value of the ! 234: \*Mfp\fP in the calling procedure. Because the call to ! 235: \*Msetbound\fP creates a procedure frame,\^ the \*Mfp\fP value saved in ! 236: the frame is used. If \*M_boundary\fP is already set,\^ it is not ! 237: changed. ! 238: .SH ! 239: \*Mclrbound\fP on the VAX ! 240: .PP ! 241: The value of \*M_boundary\fP is compared to the \*Mfp\fP in the ! 242: calling procedure. If the values are the same,\^ the calling procedure ! 243: was called from Icon and when it returns it returns to Icon. ! 244: If this is the case,\^ \*M_boundary\fP is cleared. ! 245: .SH ! 246: An Alternative Approach ! 247: .PP ! 248: If the C system on the target machine uses calls at the beginning and ! 249: end of C routines to save and restore registers,\^ it is possible to ! 250: modify these routines to set and clear the boundary. This approach is ! 251: used on the PDP-11. ! 252: .PP ! 253: The file \*Mrt/csv.s\fP contains replacement ! 254: routines for \*Mcsv\fP and \*Mcret\fP. When \*Mcsv\fP is called to ! 255: save registers,\^ if \*M_boundary\fP is 0,\^ \*Msp\fP is saved in ! 256: \*M_boundary\fP. This insures that the first call from Icon into C ! 257: leaves \*M_boundary\fP at the top of the stack at that point. ! 258: When \*Mcret\fP is called to restore registers,\^ if the procedure ! 259: frame pointer is equal to \*M_boundary\fP,\^ the return is taking ! 260: control back to Icon code and \*M_boundary\fP is cleared. ! 261: .PP ! 262: Note the resemblance between calling \*Msetbound\fP at the start of a ! 263: C routine and having \*Mcsv\fP set \*M_boundary\fP whenever a C routine ! 264: is entered. Similarly,\^ there is a resemblance between calling ! 265: \*Mclrbound\fP at appropriate points and having \*Mcret\fP clear ! 266: \*M_boundary\fP when necessary. ! 267: .PP ! 268: The initial implementation of Icon was on the PDP-11 and the modified ! 269: \*Mcsv\fP and \*Mcret\fP approach was used. When the system was ! 270: ported to the VAX,\^ the subsumption of \*Mcsv\fP and \*Mcret\fP by the ! 271: hardware required that a software approach be taken. The trade-offs ! 272: are marginal if the target machine uses save and restore routines. ! 273: Having a distinct \*Msetbound\fP and \*Mclrbound\fP may be easier to ! 274: implement,\^ but using modified save and restore routines is ! 275: definitely faster. ! 276: .PP ! 277: If this approach is used,\^ then \*MSetBound\fP and \*MClearBound\fP in ! 278: \*Mrt.h\fP should be defined,\^ but given no value. ! 279: .NH 3 ! 280: \*Mlib/invoke.s\fR ! 281: .SH ! 282: Overview ! 283: .LP ! 284: \*Minvoke.s\fP handles four specific tasks. These are ! 285: .Ds ! 286: .ft R ! 287: call a built-in function ! 288: call an Icon procedure ! 289: create a record ! 290: perform mutual evaluation ! 291: .De ! 292: .LP ! 293: Note that all of these operations rise from a source code ! 294: expression of the form ! 295: .Ds ! 296: \*(e0(\*(e1,\^\*(El,\^\*(en) ! 297: .De ! 298: where each \*(ai is an expression of some type. ! 299: Icon has strict left-to-right evaluation and thus,\^ for the preceding ! 300: expression,\^ \*(e0 is evaluated first,\^ then \*(e1,\^ and so forth ! 301: through \*(en. As each expression is evaluated,\^ its result ! 302: is pushed on the stack. After the expressions have been evaluated ! 303: (and assuming none failed),\^ the stack looks something like ! 304: .Ds ! 305: .ft R ! 306: .S1 ! 307: value from \*(e0 ! 308: value from \*(e1 ! 309: \*(El ! 310: value from \*(ei ! 311: \*(El ! 312: \*Msp\fR \*(ar value from \*(en ! 313: .De ! 314: Then,\^ \*(e0 is \fIinvoked\fP. ! 315: .PP ! 316: Recall that stacks are represented as growing downward. Thus,\^ ! 317: \*(en is on the ``top'' of the stack. Also note that each ! 318: ``value'' on the stack is actually a two-word descriptor. ! 319: .\"The various \*(ei may be referred to as \fIarguments\fP. ! 320: .SH ! 321: Generic Operation ! 322: .PP ! 323: \*Minvoke\fP is an interpreter opcode that takes a single operand ! 324: specifying how many \*(ei are present (\*(e0 is not counted). ! 325: \*Minvoke\fP is also called from \*Mstart.s\fP to invoke the \*Mmain\fP ! 326: procedure. ! 327: .Ls ! 328: .Np ! 329: \*Minvoke\fP must determine whether it is to call a built-in function,\^ ! 330: call an Icon procedure,\^ create a record,\^ or perform mutual evaluation. If ! 331: \*(e0 is not something that can be invoked,\^ ! 332: \*Minvoke\fP notes this as a run-time error. ! 333: .Np ! 334: If mutual evaluation is to be done,\^ \*Minvoke\fP selects the ! 335: value of \*(ei that corresponds to the value of \*(e0. That is,\^ ! 336: if \*(e0 is 2,\^ then \*(e2 is selected and returned. If \*(e0 ! 337: references a value that is out of range,\^ \*Minvoke\fP fails. ! 338: .Np ! 339: If an Icon procedure or built-in procedure ! 340: is being called,\^ the argument list is adjusted ! 341: to conform to the number of arguments that the procedure or function ! 342: is expecting. ! 343: This may mean supplying \*M&null\fP for missing values,\^ or discarding ! 344: the last few \*(ei. Some built-in functions take a variable number of ! 345: arguments,\^ but all Icon procedures take a fixed number of arguments. ! 346: If the desired operation is creation of a record,\^ \*Minvoke\fP treats ! 347: this just like invocation of a built-in procedure. ! 348: .Le ! 349: .LP ! 350: Built-in functions and Icon procedures are handled in very different ways. ! 351: If a built-in function is being invoked,\^ it is simply called. (The ! 352: calling sequence is somewhat convoluted on the VAX and is ! 353: described later.) ! 354: Calling an Icon procedure is more involved; the following actions ! 355: are taken. ! 356: .Ls ! 357: .Np ! 358: Each \*(ei in the (adjusted) argument list is dereferenced. ! 359: .Np ! 360: If \*M&trace\fP has a non-zero value,\^ the function \*Mctrace\fP is ! 361: called with appropriate arguments. \*Mctrace\fP produces output ! 362: that includes the name of the procedure being called and the arguments ! 363: that are being passed to it. ! 364: .Np ! 365: The remainder of the procedure frame (partially constructed by the ! 366: call itself) is built. This includes pushing values for \*M_file\fP ! 367: and \*M_line\fP on the stack. \*M_file\fP is a pointer to a string ! 368: that names the source file from which the code currently being ! 369: executed came. \*M_line\fP is the number of the source line ! 370: that is currently being executed. A descriptor for ! 371: \*M&null\fP is pushed on the ! 372: stack for each dynamic local of the procedure. ! 373: .Np ! 374: The generator frame pointer is cleared (because a new expression ! 375: context is being entered). \*Mipc\fP ! 376: is loaded with the entry point of the procedure being called. ! 377: Control is then passed back to the interpreter using a jump ! 378: instruction. ! 379: .Le ! 380: .SH ! 381: \*Minvoke\fP on the VAX ! 382: .PP ! 383: On the VAX,\^ immediately after \*Minvoke\fP has been entered,\^ the stack is ! 384: .Ds ! 385: .ft R ! 386: .St ! 387: value from \*(e0 ! 388: value from \*(e1 ! 389: \*(El ! 390: 8 value from \*(en ! 391: 4 number of expressions \- 1 (\*Mnargs\fR) ! 392: \*Map\fR \*(ar 0 number of words in argument list (\*Mnwords\fR) ! 393: -4 saved \*Mr11\fR ! 394: -8 saved \*Mr10\fR ! 395: \*(El ! 396: saved \*Mr1\fR ! 397: saved \*Mpc\fR ! 398: 12 saved \*Mfp\fR ! 399: 8 saved \*Map\fR ! 400: 4 program status word and register mask ! 401: \*Msp\fR,\^ \*Mfp\fR \*(ar 0 0 (condition handler address) ! 402: .De ! 403: .PP ! 404: The first action of \*Minvoke\fP is to set \*M_boundary\fP to the ! 405: value of \*Mfp\fP. This is done because \*Minvoke\fP may be ! 406: invoking a built-in procedure. ! 407: .PP ! 408: The number of arguments with which \*(e0 is to be invoked is contained ! 409: in the \fInargs\fP word,\^ which resides at \*M4(ap)\fP. This value ! 410: is frequently used and is put into \*Mr8\fP. ! 411: .PP ! 412: \*Minvoke\fP makes ! 413: frequent use of \*(e0,\^ but its address is not a fixed distance ! 414: from any known point. Rather,\^ the address of \*(e0 must be calculated ! 415: using the address of \*(en and the number of arguments. The VAX ! 416: \*Mmovaq\fP instruction makes this calculation easy. The desired ! 417: calculation is ! 418: .Ds ! 419: r11 = 8 + ap + (r8 * 8) ! 420: .De ! 421: and is performed by ! 422: .Ds ! 423: movaq 8(ap)\^[r8],\^r11 ! 424: .De ! 425: \*(e0 may be a ! 426: variable and if so,\^ it needs to be dereferenced. ! 427: \*Mr11\fP,\^ which contains the address of \*(e0 is pushed on ! 428: the stack and \*Mderef\fP is called. The dereferencing is done ! 429: ``in place''; the previous value of \*(e0 is replaced with the ! 430: dereferenced value. The dereferenced value is a descriptor ! 431: whose first word contains type information and whose second word ! 432: (in some cases) contains the address of a data block which holds ! 433: the actual value of the object. Note that \*Mr11\fP points to ! 434: the first word of this descriptor. ! 435: .PP ! 436: Recall that the first task of \*Minvoke\fP is to determine what ! 437: \*(e0 is and to act accordingly. The simplest case is when \*(e0 ! 438: is a procedure. That is checked for by comparing \*M0(r11)\fP with ! 439: \*MD_PROC\fP. If \*(e0 is a procedure,\^ a forward jump is made to ! 440: \*Mdoinvk\fP. ! 441: .PP ! 442: It is more interesting if \*(e0 is not a procedure. The first ! 443: alternative investigated is mutual evaluation. mutual evaluation is similar to a procedure ! 444: call,\^ but rather than \*(e0 being a procedure,\^ it is an integer ! 445: that selects one of the \*(ei. The selected \*(ei is the outcome of ! 446: the mutual evaluation. The routine \*Mcvint\fP is used to ! 447: try to convert \*(e0 to an integer. If \*(e0 cannot be converted to ! 448: an integer,\^ a forward branch is taken to \*Mtrystr\fP to explore another ! 449: possibility. ! 450: For mutual evaluation,\^ a non-positive value of \*(e0 ! 451: is acceptable and is converted to a positive value using the ! 452: \*Mcvpos\fP routine. (Expressions in the argument list are indexed ! 453: the same way that characters in a string are indexed.) If the ! 454: position is greater than the number of expressions in the list,\^ that ! 455: is,\^ if the reference is out of range,\^ the mutual evaluation fails by calling ! 456: the routine \*Mfail\fP. If the position is in range,\^ the selected ! 457: \*(ei must be returned as the result of the invocation (and the ! 458: result of the mutual evaluation). The \*(ei to return is selected by multiplying ! 459: the position by 8 (each \*(ei is a descriptor) and subtracting ! 460: that from \*Mr11\fP,\^ which points at \*(e0. The descriptor thus ! 461: referenced is copied into the location of \*(e0. (Recall that \*(e0 ! 462: is used to receive the result of an operation.) \*M_boundary\fP ! 463: is then cleared and \*Minvoke\fP returns. ! 464: .PP ! 465: If \*(e0 is not convertible to an integer,\^ conversion to a ! 466: procedure is attempted. (Note that this is an experimental extension ! 467: to Icon.) \*(e0 is first converted to a string ! 468: using \*Mcvstr\fP. If the conversion is successful,\^ the routine ! 469: \*Mstrprc\fP is called to see if the string ``names'' a procedure. ! 470: The conversion performed by \*Mstrprc\fP is ``in place'',\^ i.e.,\^ ! 471: \*(e0 becomes a descriptor for a procedure. If either the ! 472: conversion in \*Mcvstr\fP or \*Mstrprc\fP fails,\^ \*(e0 is deemed ! 473: to be uninvocable and this is noted by run-time Error 106. ! 474: .PP ! 475: At this point (the label \*Mdoinvk\fP),\^ \*(e0 is a descriptor to a procedure ! 476: to be invoked and \*Mr11\fP points to \*(e0. ! 477: .PP ! 478: The next operation is to make the number of arguments supplied ! 479: conform to the number of arguments that the procedure is expecting. ! 480: The number of arguments that a procedure expects is in the fifth ! 481: word of its procedure block. This value for the procedure being ! 482: invoked is obtained and placed in \*Mr10\fP. ! 483: If the value is negative,\^ the number ! 484: of arguments that the procedure expects is variable. Only built-in ! 485: procedures can have a variable number of arguments,\^ so if the ! 486: desired argument count is negative,\^ control passes to the label ! 487: \*Mbuiltin\fP. ! 488: .PP ! 489: \*Mr8\fP contains the number of arguments given and \*Mr10\fP contains ! 490: the number of arguments desired. The value of \*Mr10\fP is subtracted ! 491: from \*Mr8\fP,\^ leaving \*Mr8\fP with the difference. If the ! 492: number of arguments supplied is the same as the number expected,\^ ! 493: no adjustment is needed and a forward jump is made to ! 494: \*Mdoderef\fP. Otherwise,\^ the stack must be adjusted. ! 495: .PP ! 496: First,\^ \fInwords\fR and \fInargs\fR on the stack are adjusted. Recall ! 497: that \fInargs\fR ! 498: is used by Icon and is the number of arguments for a procedure. ! 499: \fInwords\fR is used by the VAX hardware and is the number of words that ! 500: the argument list for a subroutine occupies. \fInargs\fR resides at ! 501: \*M4(ap)\fP and is updated by storing \*Mr10\fP in \*M4(ap)\fP. ! 502: \fInwords\fR is trickier because only the low-order byte of the \fInwords\fR word ! 503: is to be used for the word count. The low-order byte of \*Mr10\fP is ! 504: stored in \*M0(ap)\fP,\^ doubled,\^ and then incremented by one. (The ! 505: increment by one is to allow for the \fInargs\fR word that is part of ! 506: the argument list.) ! 507: .PP ! 508: The deletion of excess arguments or addition of \*M&null\fP for ! 509: missing arguments is accomplished by moving the lower portion of ! 510: the stack up or down to overwrite excess arguments or to leave ! 511: space for missing arguments. Consider the following: A procedure ! 512: that expects one argument has been invoked with three arguments. ! 513: The stack is ! 514: .Ds ! 515: .ft R ! 516: .St ! 517: 128 \*(e0 ! 518: 120 \*(e1 ! 519: 112 \*(e2 ! 520: 104 \*(e3 ! 521: 100 \*Mnargs\fR (3 at call,\^ 1 after adjustment) ! 522: \*Map\fR \*(ar 96 \*Mnwords\fR (7 at call,\^ 3 after adjustment) ! 523: 92 \*Mr11\fR ! 524: \*(El ! 525: 48 \*Mr1\fR ! 526: 44 \*Mpc\fR ! 527: 40 \*Mfp\fR ! 528: 36 \*Map\fR ! 529: 32 \*Mpsw\fR ! 530: \*Msp\fR,\^ \*Mfp\fR \*(ar 28 0 ! 531: .De ! 532: The situation desired is ! 533: .Ds ! 534: .ft R ! 535: 128 \*(e0 ! 536: 120 \*(e1 ! 537: 116 \*Mnargs\fR (1) ! 538: \*Map\fR \*(ar 112 \*Mnwords\fR (3) ! 539: 108 \*Mr11\fR ! 540: \*(El ! 541: 64 \*Mr1\fR ! 542: 60 \*Mpc\fR ! 543: 56 \*Mfp\fR ! 544: 52 \*Map\fR ! 545: 48 \*Mpsw\fR ! 546: \*Msp\fR,\^ \*Mfp\fR \*(ar 44 0 ! 547: .De ! 548: Note the address field (which has been arbitrarily assigned). ! 549: Observe that \*(e0 and \*(e1 are in ! 550: the same place,\^ but that the lower portion of the stack has moved up. ! 551: Consider what would be desired if the ! 552: procedure being invoked requires five arguments and only three are ! 553: supplied. The desired stack configuration is ! 554: .Ds ! 555: .ft R ! 556: .St ! 557: 128 \*(e0 ! 558: 120 \*(e1 ! 559: 112 \*(e2 ! 560: 104 \*(e3 ! 561: 96 \*M&null\fP (\*(e4) ! 562: 88 \*M&null\fP (\*(e5) ! 563: 84 \*Mnargs\fR (5) ! 564: \*Map\fR \*(ar 80 \*Mnwords\fR (11) ! 565: 76 \*Mr11\fR ! 566: \*(El ! 567: 32 \*Mr1\fR ! 568: 28 \*Mpc\fR ! 569: 24 \*Mfp\fR ! 570: 20 \*Map\fR ! 571: 16 \*Mpsw\fR ! 572: \*Msp\fR,\^ \*Mfp\fR \*(ar 12 0 ! 573: .De ! 574: As before,\^ the ``good'' arguments are in the same place,\^ but the ! 575: stack has moved down to make room for \*(e4 and \*(e5 and a value ! 576: of \*M&null\fP is supplied for them. ! 577: .PP ! 578: The VAX hardware makes these stack manipulations easy. Recall that ! 579: \*Mr8\fP contains ! 580: .Ds ! 581: .ft R ! 582: number of arguments expected \- number of arguments supplied ! 583: .De ! 584: Thus,\^ in the first case \*Mr8\fP has a value of 2,\^ while in the second ! 585: case \*Mr8\fP is \-2. The value of \*Mr8\fP is multiplied by ! 586: 8 to turn the argument count into a byte count. This byte count ! 587: is added to \*Msp\fP,\^ effectively moving it to where it should ! 588: be. Now,\^ a block of memory starting at where \*Mfp\fP points ! 589: must be moved to where \*Msp\fP points. The size of the ! 590: block needs to be considered. It starts at the fp and contains ! 591: the condition handler,\^ the old psw,\^ \*Map\fP,\^ \*Mfp\fP and \*Mpc\fP ! 592: \(em five words. ! 593: Eleven saved registers are included; eleven more words. (A constant,\^ ! 594: \*MINVREGS\fP,\^ is used to represent the number of saved registers.) ! 595: Finally,\^ two words for \fInargs\fR and \fInwords\fR \(em a total of 18 ! 596: words. The VAX ! 597: instruction that makes the move is ! 598: .Ds ! 599: movc3 $(INVREGS+7)*4,\^(fp),\^(sp) ! 600: .De ! 601: which moves 18*4 (=72) bytes from where \*Mfp\fP is pointing to ! 602: where \*Msp\fP pointing. The VAX allows the source and ! 603: destination fields to overlap without producing ``curious'' results. ! 604: .PP ! 605: Some housekeeping must be performed after the move is made. ! 606: \*Mfp\fP is set to point to the same word \*Msp\fP points to,\^ ! 607: and the boundary is set to point to the same place. \*Map\fP is ! 608: adjusted to point to the \fInwords\fR word. ! 609: .PP ! 610: If arguments were deleted,\^ the adjustment process is done. If ! 611: arguments were added,\^ \*M&null\fP must be supplied as the value ! 612: for each argument that was added. Because the descriptor for ! 613: \*M&null\fP consists of two words containing 0,\^ the task amounts to filling ! 614: in null bytes in the ``hole'' that was made. \*Mr8\fP contains ! 615: the number of bytes in the ``hole''. \*Mr8\fP is negative and it ! 616: is negated to obtain a positive byte count. The instruction ! 617: .Ds ! 618: movc5 $0,\^(r0),\^$0,\^r8,\^(INVREGS+7)*4(sp) ! 619: .De ! 620: does all the work. Specifically,\^ this instruction moves bytes of zeroes ! 621: starting at \*M(r0)\fP to a field that starts at \*M72(sp)\fP and ! 622: extends for \*Mr8\fP bytes. The third operand of the instruction ! 623: is a ``fill character'' that is used in the event that the destination ! 624: field is longer than the source field. In this case,\^ the source ! 625: field is 0 bytes long,\^ and a null-byte fill character is moved into ! 626: each byte of the destination field. This is the usual way to zero ! 627: out a block of memory on the VAX. ! 628: .PP ! 629: At this point,\^ the correct number of arguments for the procedure ! 630: are on the stack. ! 631: .PP ! 632: For an Icon procedure,\^ all arguments must be dereferenced before ! 633: the procedure is called. Procedure blocks have a field that tells ! 634: how many dynamic local variables the procedure has. For built-in ! 635: procedures this field has a value of \-1. If a built-in procedure ! 636: is to be invoked,\^ control jumps to the label \*Mbuiltin\fP. ! 637: If an Icon procedure is to be invoked,\^ but it has no arguments (and ! 638: thus they do not need to be dereferenced),\^ a forward jump is made ! 639: to \*Mcktrace\fP. ! 640: .PP ! 641: \*Mr11\fP points to the descriptor for \*(e0. The address of \*(e1 ! 642: is used later in \*Minvoke\fP,\^ and its address is calculated using \*Mr11\fP ! 643: because it's handy. \*Mr10\fP contains the number of arguments and ! 644: this value is stored in \*Mr5\fP for subsequent use of \*Mr5\fP as a ! 645: counter. The arguments must be dereferenced,\^ this is done by calling ! 646: \*Mderef\fP with the address of each argument. The instruction ! 647: .Ds ! 648: pushaq -(r11) ! 649: .De ! 650: is used to decrement \*Mr11\fP by 8 and then push the value of \*Mr11\fP ! 651: on the stack. Because \*Mr11\fP initially references \*(e0,\^ the first ! 652: time through \*Mr11\fP is decremented to point at \*(e1 and ! 653: \*Mderef\fP is called with the address of \*(e1. \*Mr11\fP is backed ! 654: down through the expression list,\^ with each \*(ei being dereferenced ! 655: in turn. Note that the \*Msobgeq\fP instruction is used as a loop ! 656: controller,\^ decrementing \*Mr5\fP and jumping back to \*Mnxtarg\fP as ! 657: long as \*Mr5\fP is not 0. ! 658: .PP ! 659: At this point,\^ an Icon procedure is being ! 660: invoked; it has the correct number of arguments and they ! 661: have been dereferenced. ! 662: .PP ! 663: If tracing is on,\^ (indicated by a non-zero value for \*M_k_trace\fP),\^ ! 664: a trace message must be produced at this point. ! 665: The routine \*Mctrace\fP does all the work. It needs to be called ! 666: with the appropriate arguments. \*Mctrace\fP requires three ! 667: arguments: procedure block address,\^ number of arguments,\^ and the ! 668: address of the first argument. These are pushed on the stack and ! 669: \*Mctrace\fP is called. ! 670: .PP ! 671: The portion of the stack from \*(e0 on down constitutes a partial ! 672: procedure frame and it must be completed. \*M_line\fP and \*M_file\fP ! 673: are pushed on the stack. To complete the frame,\^ the local variables ! 674: must be pushed on the stack. Local variables have an initial value ! 675: of \*M&null\fP,\^ and the \*Mmovc5\fP idiom used previously is used ! 676: again to zero out the space required for the local variables. ! 677: .PP ! 678: Because an Icon procedure is being invoked,\^ the boundary is cleared and ! 679: \*M_k_level\fP (the \*M&level\fP keyword) is incremented. The entry ! 680: point for a procedure is stored in the third word of the procedure ! 681: block. This value is loaded into \*Mipc\fP. ! 682: A new expression context is being entered,\^ and both ! 683: \*Mgfp\fP and \*Mefp\fP are cleared using a \*Mclrq\fP instruction. ! 684: .PP ! 685: Control is passed back to the main loop of the interpreter ! 686: by jumping to \*Minterp\fP. ! 687: The Icon procedure is now being executed. The procedure ! 688: eventually terminates by use of \*Mpret\fP or \*Mpfail\fP. ! 689: .LP ! 690: The section of code following \*Mbuiltin:\fP ! 691: handles the case where a built-in procedure is to be invoked. ! 692: .PP ! 693: If nothing is changed,\^ when a built-in function returns,\^ it would ! 694: return to the instruction after the call to \*Minvoke\fP. This ! 695: is unsatisfactory,\^ since the boundary needs to be cleared because ! 696: of the transition from the C environment to the Icon environment. ! 697: Rather than having a call to \*Mclrbound\fP at ! 698: the end of each built-in procedure,\^ the boundary is cleared at ! 699: a common return point. ! 700: .PP ! 701: The \*Mpc\fP value that is stored at \*M16(fp)\fP is ``hidden'' ! 702: at \*M20(fp)\fP where the value of \*Mr1\fP should be saved. ! 703: \*M16(fp)\fP is replaced with the address of the forward label ! 704: \*Mbprtn\fP. Thus,\^ when the built-in procedure returns,\^ it goes ! 705: right to \*Mbprtn\fP. Because a C environment is being entered,\^ ! 706: the boundary is set to the current value of \*Mfp\fP. The third ! 707: word of the procedure block contains the entry point of the built-in ! 708: procedure and it is jumped to. It is important to understand that ! 709: the procedure is not called because the call frame has already been ! 710: constructed. Also,\^ the entry point address stored in the procedure ! 711: block \fImust\fR be past any instructions that are used to establish ! 712: the stack environment for the routine because this environment ! 713: should already be on the stack. ! 714: .PP ! 715: When the built-in procedure returns,\^ it comes to \*Mbprtn\fP. ! 716: The boundary is cleared because execution is back in an Icon ! 717: environment. The procedure return restores \*Mr1\fP,\^ which contains ! 718: the \*Mpc\fP value that \*Minvoke\fP should return to. Because ! 719: the return has already swept off the old frame,\^ \*M0(r1)\fP is ! 720: jumped to and \*Minvoke\fP is finished. Note that the built-in ! 721: procedure has left its result in \*(e0,\^ which is left on the ! 722: stack. Thus,\^ the direct result of \*Minvoke\fP is an additional ! 723: value on the stack. ! 724: .SH ! 725: Some Comments on \*Minvoke\fR ! 726: .PP ! 727: It is important to understand the purpose of \*Minvoke\fP. Unless ! 728: mutual evaluation is being performed,\^ ! 729: the task of \*Minvoke\fP is to create a procedure frame for an Icon or ! 730: built-in procedure and then transfer control to the procedure. ! 731: .PP ! 732: On the VAX and the PDP-11,\^ the call to \*Minvoke\fP partially ! 733: creates the procedure frame. \*Minvoke\fP then completes the ! 734: frame. For Icon procedures,\^ control eventually passes out of ! 735: \*Minvoke\fP and goes back to the interpreter loop. However,\^ ! 736: for built-in procedures,\^ after the frame is built,\^ \*Minvoke\fP ! 737: \fIbranches into\fP the C code for the procedure. Thus,\^ it appears ! 738: that the built-in procedure was called directly. This scheme is ! 739: facilitated by the fact that entry points of C routines on the ! 740: VAX and PDP-11 are a constant distance from the start of the routine. ! 741: (The \*MEntryPoint\fP macro in \*Mrt.h\fP specifies the distance.) ! 742: On some machines this may not be practical and other schemes may need ! 743: to be developed. ! 744: .PP ! 745: Another point that needs to be addressed is that of argument removal. ! 746: When a built-in procedure,\^ Icon procedure,\^ or built-in operation is ! 747: performed,\^ the net result almost always is ! 748: simply the addition of a descriptor to the stack. More precisely,\^ ! 749: the new descriptor is actually a replacement for \*(a0,\^ which ! 750: is the descriptor for the procedure being called,\^ or in the case ! 751: of a built-in operation is \*M&null\fP. Recall that arguments are below \*(a0 ! 752: on the stack. The \*(a0 word must be on top of the ! 753: stack when a procedure or operation is complete. The VAX does ! 754: this via hardware,\^ which manages the stack and removes arguments when ! 755: a procedure call is complete. The PDP-11 does not have such hardware ! 756: facilities and thus the arguments must be removed manually. The ! 757: problem is compounded by the fact that for built-in operations,\^ ! 758: \*Minvoke\fP is never called; rather,\^ the interpreter loop calls the ! 759: appropriate subroutine directly. ! 760: .PP ! 761: The method employed on the PDP-11 uses the \*Mcret\fP routine to ! 762: remove arguments. \*Mcret\fP is called at the end of each C routine ! 763: to restore registers. Icon has a replacement for \*Mcret\fP that ! 764: restores registers but also removes arguments when appropriate. ! 765: \*Mrt/csv.s\fP contains the replacement routine. When \*Mcret\fP is ! 766: called,\^ if \*Mr5\fP (the \*Mpfp\fP on the PDP-11) is equal to ! 767: \*M_boundary\fP,\^ then \*Mcret\fP is returning to Icon code and any ! 768: arguments on the stack are removed,\^ leaving \*(a0 on the top of the ! 769: stack. ! 770: A similar technique may be needed on the target machine. ! 771: .PP ! 772: \*Mrt/csv.s\fP also contains a replacement for the \*Mcsv\fP routine ! 773: which saves registers upon entry to C functions ! 774: on the PDP-11. Both \*Mcsv\fP and \*Mcret\fP also contain code that ! 775: is used to set and clear the boundary at appropriate times. See ! 776: the description of \*Mrt/setbound.s\fP for more details. ! 777: .NH 3 ! 778: \*Miconx/interp.s\fR ! 779: .SH ! 780: Overview ! 781: .PP ! 782: \*Minterp.s\fP is the main loop for the interpreter. ! 783: The execution of an Icon program is stack based. ! 784: As the interpreter executes an Icon program,\^ it ! 785: fetches instructions and accompanying operands out of the instruction ! 786: stream of the interpretable file. Operands for ! 787: interpreter instructions are pushed on the stack and results ! 788: accumulate on the stack as operands for other instructions. ! 789: In addition to simple incremental and decremental stack changes,\^ ! 790: the expression evaluation mechanism may cause portions of the ! 791: stack to be duplicated and may also cause the top portion of ! 792: the stack to be removed. ! 793: .SH ! 794: Generic Operation ! 795: .PP ! 796: An Icon program is executed by interpreting the interpretable file ! 797: produced by the linker. The interpretation process itself is fairly ! 798: simple. \*Mipc\fP points at the next ! 799: instruction to be executed. (Recall that the interpretable file is ! 800: loaded into memory.) The opcode of the instruction is fetched ! 801: and the corresponding ! 802: word in the jump table is taken as the address of a sequence of ! 803: instructions that perform the desired operations. A branch is ! 804: taken to the referenced location and the operation is performed. ! 805: The operation may require operands; if so,\^ they appear in the ! 806: instruction stream following the opcode. The segment of code that ! 807: performs a particular operation is responsible for fetching the ! 808: appropriate operands out of the stream. When the ! 809: operation is complete,\^ a jump is taken to the top of the interpreter ! 810: loop and the process continues. ! 811: .PP ! 812: Interpreter operations are of two types. Operations of the first type ! 813: call a routine to perform a task. Operations of the second type are ! 814: executed entirely by the interpreter; no subroutine call is necessary. ! 815: .PP ! 816: Operations that require a call to be made call routines in the ! 817: \*Moperators\fP or \*Mlib\fP directories. The routine being called ! 818: may require one or more arguments. If arguments are required,\^ they ! 819: appear on the stack. When the routine returns,\^ it removes ! 820: any arguments that it was called with from the stack and leaves its ! 821: result on the top of the stack. ! 822: .PP ! 823: To facilitate the calling of ! 824: routines,\^ a table known as \*Moptab\fP parallels the jump table. ! 825: An opcode \fIn\fP references the \fIn\fPth word of the jump table. ! 826: If the operation designated by the opcode requires a call,\^ the ! 827: \fIn\fPth word of \*Moptab\fP contains the address of the routine ! 828: that should be called. ! 829: .PP ! 830: The interpreter saves space in its ! 831: instruction stream by encoding operand information in some opcodes. ! 832: For example,\^ the \*Mline\fP instruction has one operand that ! 833: is used to set the value of \*M_line\fP,\^ the current source line ! 834: number. The \*Mlinex\fP instruction is an alternate form of ! 835: \*Mline\fP which encodes the line number as the low order bits of the ! 836: opcode. Specifically,\^ the opcodes from 192 to 256 are \*Mlinex\fP ! 837: opcodes. For example,\^ opcode 195 is equivalent to a \*Mline\fP ! 838: opcode with an operand of 3. ! 839: .SH ! 840: Implementing the Interpreter Loop ! 841: .PP ! 842: \*Minterp.s\fP stands alone among the assembly language files as one that is ! 843: well suited to coding in a macro fashion. Most of the interpreter loop is ! 844: written in terms of \fIcpp\fP macros and thus porting it is largely a ! 845: matter of writing the macros for the target machine. ! 846: .LP ! 847: The following \*M#define\fPs must be made. ! 848: .Ip \*MOp\ \ \ \ \ \fP ! 849: .br ! 850: The operand register. Any general purpose register will do. The ! 851: value of the register need not preserved between instructions; its ! 852: lifetime is only from the time that an operand is fetched until the ! 853: next opcode is fetched or a routine is called. ! 854: .Ip \*MGetOp\fP ! 855: This must expand into code that fetches the next operand out of ! 856: the instruction stream and places it in the register \*MOp\fP. ! 857: Recall that operand size is determined by ! 858: the \*M#define\fP for \*MOPNDSIZE\fP in the linker. ! 859: On the VAX,\^ \*MGetOp\fP is merely ! 860: .Ds ! 861: .ta 0.6i +0.6i +0.6i +0.6i ! 862: movl (ipc)+,\^Op ! 863: .De ! 864: This is because operands are one word long and can begin on any byte ! 865: boundary. If the VAX did not support word fetching from arbitrary ! 866: boundaries,\^ it would be necessary to get the bytes from the ! 867: instruction stream one at a time and make a word out of them using ! 868: boolean operations. If such were the case,\^ a reasonable alternative ! 869: would be to make opcodes one word in size and thus all instruction ! 870: stream objects (opcodes,\^ operands,\^ and words),\^ would be of the same ! 871: size and lie on word boundaries. ! 872: .\".IP \*MGetWord\fP ! 873: .\"\*MGetWord\fP is similar to \*MGetOp\fP,\^ but rather than loading the ! 874: .\"next operand,\^ it loads the next \fIword\fP from the instruction stream into ! 875: .\"the \*MOp\fP register. Recall that the size of a word is defined by ! 876: .\"the \*MWORDSIZE\fP in the linker. If words are the same size as ! 877: .\"operands on the target machine,\^ \*MGetWord\fP should be identical to ! 878: .\"\*MGetOp\fP. ! 879: .Ip \*MPushOp\fP ! 880: Push the \*MOp\fP register on the stack. The VAX uses ! 881: .ta .6i ! 882: .Ds ! 883: pushl Op ! 884: .De ! 885: .Ip \*MPushNull\fP ! 886: Push a descriptor for \*M&null\fP on the stack. That is,\^ push two ! 887: words of zeroes. The VAX \*Mclrq\fP instruction does the trick: ! 888: .Ds ! 889: clrq -(sp) ! 890: .De ! 891: .Ip \*MPush_R(x)\fP,\^\ \*MPush_S(x)\fP,\^\ \*MPush_K(x)\fP ! 892: Push the value of \*Mx\fP on the stack. To accommodate machines with ! 893: non-orthogonal instruction sets,\^ \*MPush_R\fP is used to push a register ! 894: value,\^ and \*MPush_S\fP is used to push the contents of a storage ! 895: location. \*MPush_K\fP is used to push a constant value. ! 896: The VAX uses ! 897: .Ds ! 898: pushl x ! 899: .De ! 900: for both \*MPush_R(x)\fP and \*MPush_S(x)\fP,\^ while ! 901: .Ds ! 902: pushl $x ! 903: .De ! 904: is used for \*MPush_K(x)\fP. ! 905: .Ip \*MPushOpSum_R(x)\fP,\^\ \*MPushOpSum_S(x)\fP ! 906: \*MPushOpSum_R(x)\fP adds the value ! 907: of the register \*Mx\fP to \*MOp\fP and pushes the result on the stack. ! 908: \*MPushOpSum_S(x)\fP is similar,\^ adding the value in the memory ! 909: location \*Mx\fP to \*MOp\fP and pushing the result. On the VAX,\^ ! 910: .Ds ! 911: addl3 Op,\^x,\^-(sp) ! 912: .De ! 913: is used for both. ! 914: .Ip \*MNextInst\fP ! 915: Branch to the top of the interpreter loop. The VAX uses ! 916: .Ds ! 917: jmp _interp ! 918: .De ! 919: .Ip \*MCallN(n)\fP ! 920: Call the routine corresponding to the current opcode with \*Mn\fP ! 921: arguments. On the VAX,\^ the opcode fetching segment loads \*Mr0\fP ! 922: with a byte offset into the jump table. This same byte offset ! 923: references the location in \*Moptab\fP which contains the address ! 924: of the routine which corresponds to the current opcode. ! 925: \*MCallN(n)\fP expands to ! 926: .Ds ! 927: pushl $n ! 928: calls $((n*2)+1),\^*optab(r0) ! 929: .De ! 930: \*Mpushl $n\fP pushes the number of arguments on the stack. This ! 931: word becomes the \fInargs\fP word of the procedure frame. The ! 932: first of operand of the \*Mcalls\fP instruction is the length of ! 933: words in the argument list,\^ since each argument is a two word ! 934: descriptor and the \fInargs\fP word occupies another word,\^ ! 935: \*Mn*2+1\fP is used as the length of the argument list. The ! 936: address contained in the \*Moptab\fP word referenced by \*Mr0\fP is ! 937: the routine to call. ! 938: .Ip \*MCallNameN(n,\^f)\fP ! 939: Call the routine named \*Mf\fP with \*Mn\fP arguments. This is very ! 940: similar to \*MCallN\fP,\^ the only difference being that the routine ! 941: to call is explicitly named rather than being implicitly determined ! 942: from the opcode value. The VAX uses ! 943: .Ds ! 944: pushl $n ! 945: calls $((n*2)+1),\^f ! 946: .De ! 947: .Ip \*MBitClear(m)\fP ! 948: The constant value \*Mm\fP designates bits in the \*MOp\fP register ! 949: to leave on. All other bits in \*MOp\fP should be turned off. That ! 950: is,\^ the complement of \*Mm\fP is \*MAND\fPed with the contents of ! 951: \*MOp\fP and the result is placed in \*MOp\fP. This is used to ! 952: decode opcodes with encoded operands. The VAX uses ! 953: .Ds ! 954: bicl2 $0!m,\^Op ! 955: .De ! 956: .Ip \*MJump(lab)\fP ! 957: Branch to the label \*Mlab\fP. The destination label is close to the ! 958: jump,\^ so a short jump of some type may be used. The VAX uses ! 959: .Ds ! 960: jbr lab ! 961: .De ! 962: .Ip \*MLongJump(lab)\fP ! 963: \*MLongJump\fP is like \*MJump\fP with the exception that \*Mlab\fP ! 964: may be quite distant. The VAX uses ! 965: .Ds ! 966: jmp lab ! 967: .De ! 968: .Ip \*MLabel(lab)\fP ! 969: Generate a label declaration for \*Mlab\fP. The VAX uses ! 970: .Ds ! 971: lab: ! 972: .De ! 973: .SH ! 974: VAX Specific Sections of \*Minterp\fR ! 975: .PP ! 976: Several sections of \*Minterp\fP are machine specific and must be ! 977: coded on a per-machine basis. The sections in question are explained ! 978: on an individual basis: ! 979: .Ip \*M_interp\fP ! 980: The next opcode is fetched and loaded into \*Mr0\fP. \*Mmovzbl\fP ! 981: moves a byte and zero extends it to a word value. Because a byte was ! 982: fetched,\^ \*Mipc\fP is incremented by 1. The opcode is saved in ! 983: \*MOp\fP in case it contains an encoded operand. \*Mr0\fP is ! 984: multiplied by 4 to turn it into a byte offset. A jump is made to the ! 985: address indexed by \*Mr0\fP in \*Mjumptab\fP to perform the desired ! 986: operation. Eventually,\^ a jump returns control to the label ! 987: \*M_interp\fP to fetch and execute the next instruction. ! 988: .Ip \*Mop_bscan\fP ! 989: A descriptor for \*M_k_subject\fP is pushed on the stack. Then ! 990: the value of \*M_k_pos\fP is pushed,\^ followed by the constant ! 991: \*MD_INTEGER\fP. The routine corresponding to \*Mop_bscan\fP,\^ ! 992: \*M_bscan\fP,\^ is called with 0 arguments. (This causes the ! 993: descriptors for \*M_k_subject\fP and the value of \*M_k_pos\fP to be ! 994: left on the stack.) When \*M_bscan\fP returns,\^ a branch is made to ! 995: \*M_interp\fP. ! 996: .Ip \*Mop_ccase\fP ! 997: A null descriptor is pushed on the stack. The word immediately ! 998: above the current expression frame is then pushed on the stack. ! 999: .Ip \*Mop_chfail\fP ! 1000: The operand of \*Mchfail\fP is fetched into \*MOp\fP. \*MOp\fP and ! 1001: \*Mipc\fP are added together and the result replaces the failure ! 1002: address in the current expression frame. ! 1003: .Ip \*Mop_dup\fP ! 1004: A null descriptor is pushed on the stack. The value that was on top ! 1005: of the stack is now at \*M8(sp)\fP,\^ and it is copied to the top of the ! 1006: stack using a \*Mmovq\fP. ! 1007: .Ip \*Mop_eret\fP ! 1008: \*Meret\fP gets the value on top of the stack,\^ removes the current ! 1009: expression frame and puts the previous top of stack value back on the ! 1010: top of the stack. First of all,\^ ! 1011: .Ds ! 1012: movq (sp)+,\^r0 ! 1013: .De ! 1014: moves the descriptor on the top of the stack into the \*Mr0\-r1\fP ! 1015: register pair and increments the stack pointer by 8. The \*Mgfp\fP ! 1016: is loaded with the \*Mgfp\fP value stored in the expression frame ! 1017: marker. \*Msp\fP is loaded from \*Mefp\fP,\^ bringing the expression ! 1018: marker to the top of the stack. The old \*Mefp\fP value from the ! 1019: marker is loaded into \*Mefp\fP. Finally,\^ the value stored in the ! 1020: \*Mr0\-r1\fP pair is pushed on the stack. ! 1021: .Ip \*Mop_file\fP ! 1022: The operand of \*Mfile\fP is loaded into \*MOp\fP. \*MOp\fP and the ! 1023: value of \*M_ident\fP are added and the result in placed in ! 1024: \*M_file\fP. ! 1025: .Ip \*Mop_goto\fP ! 1026: The operand is loaded into \*MOp\fP and then added to \*Mipc\fP. ! 1027: .Ip \*Mop_incres\fP ! 1028: The eighth word of the co-expression heap block for the current ! 1029: expression is incremented by one. ! 1030: .Ip \*Mop_init\fP ! 1031: This one is tricky. The \*Minit\fP instruction arises from the ! 1032: \*Minitial\fP statement in Icon and is used to effect one-time ! 1033: execution of a segment of code. The operand of \*Minit\fP is the ! 1034: address of the first instruction after the segment that is to be ! 1035: executed once. The instruction ! 1036: .Ds ! 1037: movb $59,\^-(ipc) ! 1038: .De ! 1039: decrements \*Mipc\fP by 1 and then stores the constant 59 in the byte ! 1040: that \*Mipc\fP references,\^ which is the \*Minit\fP opcode. The magic ! 1041: number 59 is the opcode for \*Mgoto\fP,\^ so in effect,\^ the \*Minit\fP ! 1042: had been made into a goto that skips a section of code. By adding 5 ! 1043: to \*Mipc\fP,\^ it leaves \*Mipc\fP pointing at the first instruction ! 1044: of the \*Minitial\fP code. The constant 5 is derived from the ! 1045: width of the opcode and associated operand,\^ i.e.,\^ ! 1046: \*MOPSIZE+OPNDSIZE\fP. ! 1047: .Ip \*Mop_invoke\fP ! 1048: This section has two entry points: \*Mop_invoke\fP gets control if ! 1049: \*Minvoke\fP has an operand,\^ and \*Mop_invkx\fP gets control if the ! 1050: operand is encoded in the opcode. If an operand is specified,\^ it is ! 1051: fetched into \*MOp\fP. If the operand is encoded,\^ \*MBitClear(7)\fP is ! 1052: used to isolate the operand in \*MOp\fP. Control converges at ! 1053: \*Mdoinvoke\fP. The operand is the number of arguments to ! 1054: invoke the procedure with and it is pushed on the stack as ! 1055: the \fInargs\fP word. (The arguments are already on the stack.) ! 1056: The \*Mcalls\fP instruction needs the length of the argument list,\^ so ! 1057: \*MOp\fP is multiplied by 2 and then incremented by 1. \*Minvoke\fP ! 1058: is called. ! 1059: .Ip \*Mop_int\fP ! 1060: As in \*Mop_invoke\fP,\^ \*Mop_int\fP has a secondary entry point,\^ ! 1061: \*Mop_intx\fP,\^ for operands encoded in the opcode. At the \*Mop_int\fP ! 1062: entry point,\^ a \*MWORDSIZE\fP value is fetched out of the instruction ! 1063: stream and loaded into \*MOp\fP. At the \*Mop_intx\fP entry point,\^ ! 1064: the \*MOp\fP value is decoded from the operand. The \*MOp\fP value is ! 1065: pushed on the stack and is followed by a \*MD_INTEGER\fP word,\^ forming ! 1066: an integer descriptor. ! 1067: .Ip \*Mop_line\fP ! 1068: Like \*Mop_invoke\fP and \*Mop_int\fP,\^ \*Mop_line\fP has a secondary ! 1069: entry point. The operand value is obtained and then moved into \*M_line\fP. ! 1070: .Ip \*Mop_llist\fP ! 1071: \*Mllist\fP is similar to \*Minvoke\fP in that it has a number of ! 1072: arguments already on the stack and that the operand specifies the ! 1073: number. The operand is fetched into \*MOp\fP and pushed on the ! 1074: stack to become the \fInargs\fP argument of \*Mllist\fP. \*MOp\fP is ! 1075: then multiplied by 2 and incremented by 1 to serve as an argument ! 1076: list size for \*Mcalls\fP. ! 1077: .Ip \*Mop_mark\fP ! 1078: The operand is fetched into \*MOp\fP and \*Mipc\fP is added to it. ! 1079: \*Mefp\fP is pushed on the stack and the new \*Msp\fP value is put in ! 1080: \*Mefp\fP. \*Mgfp\fP is pushed on the stack and cleared. \*MOp\fP ! 1081: is pushed on the stack. ! 1082: .Ip \*Mop_mark0\fP ! 1083: Like \*Mop_mark\fP,\^ with an implicit operand value of zero. ! 1084: .Ip \*Mop_pop\fP ! 1085: The two \*Mtstl\fP instructions serve to add 8 to \*Msp\fP which ! 1086: removes the top value from the stack. ! 1087: .Ip \*Mop_sdup\fP ! 1088: The descriptor on the top of the stack is pushed on the stack,\^ ! 1089: duplicating it. ! 1090: .Ip \*Mop_unmark\fP ! 1091: The operand,\^ the number of expression frames to remove from the ! 1092: stack,\^ is fetched into \*MOp\fP. \*Mefp\fP is ! 1093: restored from the current expression frame. The instruction ! 1094: .Ds ! 1095: sobgtr Op,\^unmkjmp ! 1096: .De ! 1097: decrements \*MOp\fP and then branches to \*Mdounmark\fP if \*MOp\fP is not ! 1098: zero. This chains through the number of expression frames specified ! 1099: by the operand. \*Mgfp\fP is restored from the current expression ! 1100: marker. \*Mefp\fP is loaded into \*Msp\fP to move the expression ! 1101: marker to the top of the stack. Finally,\^ \*Mefp\fP is restored ! 1102: from the marker and \*Msp\fP is incremented to remove the last word ! 1103: of the marker. ! 1104: .Ip \*Mop_unmk1-7\fP ! 1105: Similar to \*Munmark\fP,\^ but uses successive restorations of ! 1106: \*Mefp\fP rather than a loop. ! 1107: .Ip \*Mop_global\fP ! 1108: Dual entry points are used to deal with possible operand encoding. ! 1109: The operand,\^ which is a number of a variable in the global region,\^ ! 1110: is multiplied by 8 to provide a byte offset from the start of ! 1111: the global region. The sum of \*MOp\fP and the value of \*Mglobals\fP ! 1112: is pushed on the stack to provide a descriptor address. The constant ! 1113: \*MD_VAR\fP is pushed on the stack to complete the descriptor for the ! 1114: global variable. ! 1115: .Ip \*Mop_static\fP ! 1116: Identical to \*Mop_global\fP except that \*Mstatics\fP is used ! 1117: instead of \*Mglobals\fP. ! 1118: .Ip \*Mop_local\fP ! 1119: The operand value is the number of a local variable for which a ! 1120: variable descriptor is to be pushed on the stack. Recall that the ! 1121: local variables lie below the procedure frame and,\^ on the VAX,\^ the ! 1122: descriptor for the first one is at \*M\-16(fp)\fP. \*MOp\fP is ! 1123: negated. The instruction ! 1124: .Ds ! 1125: pushaq -16(fp)\^[Op] ! 1126: .De ! 1127: performs the calculation ! 1128: .Ds ! 1129: -16 + fp + (Op * 8) ! 1130: .De ! 1131: which computes the address of the descriptor of the desired variable ! 1132: and pushes it on the stack. The variable descriptor is completed by ! 1133: pushing \*MD_VAR\fP on the stack. ! 1134: .Ip \*Mop_arg\fP ! 1135: Like \*Mop_local\fP,\^ but it uses \*M8(ap)\fP as the base for the ! 1136: address calculation and the operand value is not negated. ! 1137: .Ip \*Mquit\ \ \ \ \ \fP ! 1138: Push a 0 on the stack and call the routine \*M_c_exit\fP to terminate ! 1139: execution of the Icon program. ! 1140: .Ip \*Merr\ \ \ \ \ \fP ! 1141: \*Merr\fP should never be encountered during normal execution. ! 1142: Reaching it indicates that an invalid opcode was encountered. ! 1143: It need not do anything more than abort execution. On the VAX,\^ ! 1144: it calls \*Msprintf\fP to create a string containing the invalid ! 1145: opcode and the \*Mipc\fP where it was encountered and then calls ! 1146: \*Msyserr\fP with the string as an argument. ! 1147: .NH 3 ! 1148: \*Mlib/efail.s\fR ! 1149: .SH ! 1150: Overview ! 1151: .PP ! 1152: \*Mefail\fP handles the failure of an expression. When ! 1153: Icon evaluates an expression,\^ it tries to produce a result from it. ! 1154: If at some point in the evaluation of an expression the expression ! 1155: fails,\^ Icon resumes inactive generators in the expression in ! 1156: an attempt to make the expression succeed. \*Mefail\fP is at the ! 1157: heart of this activity. ! 1158: .LP ! 1159: \*Mefail\fP has three distinct outcomes: ! 1160: .Ls ! 1161: .Np ! 1162: Resumption of the newest inactive generator in the current ! 1163: expression frame. ! 1164: .Np ! 1165: Failure of the current expression with execution continuing ! 1166: at the failure address contained in the expression marker. ! 1167: .Np ! 1168: Failure of the current expression with propagation of failure ! 1169: to the enclosing expression frame. This is similar to (2),\^ ! 1170: but occurs when the failure address is 0. After the current ! 1171: expression fails,\^ \*Mefail\fP loops back to its entry point ! 1172: to fail again. ! 1173: .Le ! 1174: .PP ! 1175: \*Mefail\fP is branched to rather than being ! 1176: called. This is because it ! 1177: serves as a ``back-end'' for several failure ! 1178: actions that may occur during the course of execution: ! 1179: .Ls ! 1180: .Np ! 1181: When a built-in procedure fails,\^ it calls the routine \*Mfail\fP,\^ which ! 1182: in turn branches to \*Mefail\fP. ! 1183: .Np ! 1184: When an Icon procedure fails via the \*Mpfail\fP routine,\^ \*Mpfail\fP ! 1185: terminates by branching to \*Mefail\fP. ! 1186: .Np ! 1187: When the \*Mefail\fP opcode is executed by the interpreter,\^ \*Mefail\fP ! 1188: is branched to. ! 1189: .Np ! 1190: The generator frames built by \*Mesusp\fP and \*Mlsusp\fP use \*Mefail\fP ! 1191: as a return address. This is explained in detail later. ! 1192: .Le ! 1193: .SH ! 1194: Generic Operation ! 1195: .PP ! 1196: \*Mefail\fP is essentially a simple routine. There are two separate ! 1197: paths of execution that \*Mefail\fP may take. The first is to ! 1198: resume an inactive generator. The second is to cause failure of ! 1199: the expression in lieu of an inactive generator. ! 1200: .PP ! 1201: If there is an inactive generator in the current expression frame,\^ ! 1202: it must be resumed. ! 1203: If the generator is an Icon procedure and tracing is in on,\^ ! 1204: \*Matrace\fP is called with appropriate arguments. \*M_k_level\fP,\^ ! 1205: \*M_line\fP,\^ and \*M_file\fP are restored from the generator frame. A return ! 1206: is performed and the net result is that the stack is restored to ! 1207: the state that it was in before the suspension that created the ! 1208: generator. ! 1209: .PP ! 1210: If there are no inactive generators that can be resumed,\^ the ! 1211: expression being evaluated must fail. This is done by popping ! 1212: the stack back through the current expression frame and resuming ! 1213: execution at the point indicated by the failure address in ! 1214: the expression marker. This is a two-step process. The first ! 1215: is to pop the frame and the second is to resume execution. ! 1216: When the frame is popped,\^ the expression has failed. The ! 1217: failure address in the expression marker is saved before ! 1218: the frame is popped. If this address is not zero,\^ execution ! 1219: is continued by branching to the address. If the address is ! 1220: zero,\^ the failure is propagated to the enclosing expression by ! 1221: branching to efail. ! 1222: .PP ! 1223: Zero failure addresses are generated by the ucode instruction ! 1224: .Ds ! 1225: mark L0 ! 1226: .De ! 1227: Such instructions are used to avoid a special case during code ! 1228: generation and the only purpose they serve during program execution ! 1229: is to create an expression marker for the corresponding ! 1230: \*Munmark\fP instruction to remove. (\*Mmark\fP and \*Munmark\fP ! 1231: instructions are paired.) ! 1232: Thus,\^ whenever \*Mefail\fP pops an expression whose marker has a ! 1233: zero failure address,\^ \*Mefail\fP causes failure in the enclosing ! 1234: expression. ! 1235: .SH ! 1236: \*Mefail\fP on the VAX ! 1237: .PP ! 1238: The first action is to determine if there is an inactive generator ! 1239: that can be reactivated. If the generator frame pointer ! 1240: is non-zero,\^ it points to the newest inactive generator. ! 1241: Note that whenever a new expression frame is created,\^ the generator ! 1242: frame pointer is zeroed. Thus,\^ if \*Mgfp\fP is non-zero,\^ the ! 1243: generator frame that it points to belongs to a generator in the ! 1244: current expression frame. ! 1245: .PP ! 1246: If an inactive generator is available,\^ it must be reactivated. ! 1247: First,\^ \*M_boundary\fP is restored from the generator frame. ! 1248: The stack is popped back to the generator frame by loading ! 1249: \*Mfp\fP from \*Mgfp\fP. But,\^ before \*Mfp\fP is loaded,\^ ! 1250: its value is saved in \*Mr0\fP. \*Mfp\fP now points at ! 1251: word 0 of the generator frame,\^ but that is a word below the ! 1252: actual stack frame that it should be pointing at,\^ so \*Mfp\fP is ! 1253: incremented by 4 using a \*Mtstl\fP. ! 1254: .LP ! 1255: There are three types of generators that may be encountered ! 1256: by \*Mefail\fP. ! 1257: .Ls ! 1258: .Np ! 1259: An Icon procedure that did a \*Msuspend\fP. In such cases,\^ the ! 1260: routine \*Mpsusp\fP handled the suspension. ! 1261: .Np ! 1262: A built-in procedure that called the C function \*Msuspend()\fP. ! 1263: .Np ! 1264: A generator created by an \*Mesusp\fP or \*Mlsusp\fP instruction. ! 1265: Such generators ! 1266: arise from source code constructs like \*M\*(x1 |\ \^\*(x2\fP,\^ \*M|\*(xx\fR,\^ and ! 1267: \*M\*(x1 \e\ \^\*(x2\fR,\^ which are referred to as \fIcontrol regimes\fP. ! 1268: .Le ! 1269: .PP ! 1270: The generators may be treated the same way as far as resumption goes. ! 1271: However,\^ if an Icon procedure is being resumed,\^ a tracing message must ! 1272: be generated if \*M_k_trace\fP is not 0. ! 1273: .PP ! 1274: If the value of \*M_boundary\fP is not the same as \*Mfp\fP,\^ the ! 1275: generator is a built-in procedure and tracing is not done. ! 1276: If the \*Mfp\fP saved in the current frame is the same as the ! 1277: \*Mfp\fP was upon entry to \*Mefail\fP (the value was saved in ! 1278: \*Mr0\fP),\^ the generator was made by an \*Mesusp\fP or an \*Mlsusp\fP ! 1279: and tracing is not done. ! 1280: .PP ! 1281: Otherwise,\^ the generator is an Icon procedure,\^ and \*Matrace\fP must ! 1282: be called. \*Matrace\fP takes one argument,\^ the address of the ! 1283: procedure block for the procedure being resumed. Recall that \*(e0 on the ! 1284: stack is a descriptor for the procedure block. The address of ! 1285: \*(e0 is calculated using ! 1286: .Ds ! 1287: &\*(e0 = ap + 8 + (8 * nargs) ! 1288: .De ! 1289: The resulting address is used as the single argument for \*Matrace\fP. ! 1290: .PP ! 1291: The generator is now ready to be resumed. ! 1292: \*M_k_level\fP,\^ \*M_line\fP,\^ and \*M_file\fP are restored by popping them ! 1293: from the generator frame. If the generator is a built-in procedure,\^ ! 1294: \*M_boundary\fP is cleared. A return is performed to activate the ! 1295: generator. ! 1296: .PP ! 1297: The return has different effects depending on the type of generator ! 1298: being resumed. ! 1299: .PP ! 1300: If the generator is a built-in procedure,\^ the return ! 1301: restores the stack to the state it was in before \*Msuspend\fP was ! 1302: called,\^ and execution proceeds at the point just after \*Msuspend()\fP. ! 1303: In this case the \*Mpc\fP value being returned to references an instruction ! 1304: in the built-in procedure. ! 1305: .PP ! 1306: If the generator is an Icon procedure,\^ the stack is restored to ! 1307: the state it was in before the \*Mpsusp\fP ucode instruction ! 1308: was executed. The \*Mpc\fP value being returned to references an ! 1309: instruction in the interpreter loop. Execution of the program ! 1310: continues with the interpreter instruction following the \*Mpsusp\fP. ! 1311: .PP ! 1312: If the generator is a control regime,\^ the stack is restored to ! 1313: the state it was in before the \*Mesusp\fP or \*Mlsusp\fP that ! 1314: created the generator was performed. The ! 1315: return \*Mpc\fP points to \*Mefail\fP itself. Thus,\^ when the ! 1316: return is done,\^ the stack is cleared,\^ and an \*Mefail\fP is performed. ! 1317: This has the effect of transferring control to the failure label ! 1318: in the expression marker of the bounding expression frame. ! 1319: .\"(The failure label lies at the start of code for the alternative.) ! 1320: .PP ! 1321: If there is no generator to reactivate,\^ the expression must fail. ! 1322: This is handled at the label \*Mnogen\fP. \*Mefp\fP points to the ! 1323: expression frame marker. \*Mipc\fP is loaded ! 1324: from \*M\-8(efp)\fP which contains the address to go to in the ! 1325: event that the current expression fails. (As it has.) ! 1326: \*Mgfp\fP is restored from the expression marker. ! 1327: \*Mefp\fP is restored from the marker and the marker is popped off the stack. ! 1328: .PP ! 1329: If the failure address in \*Mipc\fP is non-zero,\^ control is passed ! 1330: back to the interpreter via a branch and execution of the ucode ! 1331: resumes at the failure address. If \*Mipc\fP is zero,\^ the expression ! 1332: failure is transmitted to the surrounding expression frame ! 1333: by a branch to \*Mefail\fP. (Recall that a zero failure address ! 1334: comes from a \*Mmark L0\fP instruction and that a failure that ! 1335: reaches a \*Mmark L0\fP marker must be propagated to the next ! 1336: expression marker.) ! 1337: .NH 3 ! 1338: \*Mlib/pfail.s\fR ! 1339: .SH ! 1340: Overview ! 1341: .LP ! 1342: \*Mpfail\fP handles the failure of an Icon procedure. An Icon ! 1343: procedure can fail by ! 1344: .Ds ! 1345: .ft R ! 1346: executing a \*Mfail\fP expression ! 1347: executing \*Mreturn \fIexpr\fR when \fIexpr\fR fails ! 1348: allowing the flow of control to reach the end of a procedure ! 1349: .De ! 1350: \*Mpfail\fP is entered via a branch when ! 1351: the interpreter encounters the \*Mpfail\fP instruction. ! 1352: .SH ! 1353: Generic Operation ! 1354: .PP ! 1355: The task of \*Mpfail\fP is to signal failure in the expression that ! 1356: contains the procedure call being evaluated. This is done ! 1357: by removing the Icon procedure frame from the stack,\^ restoring ! 1358: appropriate registers and values,\^ and calling \*Mefail\fP. The key is that ! 1359: all \*Mpfail\fP needs to do is to remove the procedure frame ! 1360: from the stack and from then on things can be handled just like expression ! 1361: failure. ! 1362: Thus,\^ \*Mefail\fP does most of the work. ! 1363: .PP ! 1364: \*Mpfail\fP calls \*Mftrace\fP to produce a trace message if ! 1365: tracing is on. \*Mpfail\fP also decrements \*M_k_level\fP because ! 1366: a procedure is being exited. ! 1367: .PP ! 1368: Note that the procedure frame on the stack is a frame that ! 1369: was created by \*Minvoke\fP. ! 1370: .SH ! 1371: \*Mpfail\fP on the VAX ! 1372: .PP ! 1373: After \*M_k_level\fP is decremented,\^ \*M_k_trace\fP is checked to ! 1374: see if a trace message should be produced. If tracing is on,\^ ! 1375: \*Mftrace\fP must be called. \*Mftrace\fP takes one argument,\^ ! 1376: the address of the procedure block for the failing procedure. ! 1377: \*(e0 is the descriptor for the procedure block,\^ and the address ! 1378: of \*(e0 is calculated using ! 1379: .Ds ! 1380: &\*(e0 = (\fInargs\fP * 8) + 8 + ap ! 1381: .De ! 1382: The resulting address is pushed on the stack and \*Mftrace\fP is ! 1383: called. ! 1384: .PP ! 1385: Execution continues at \*Mdofail\fP to remove the procedure frame ! 1386: from the stack. The frame cannot be merely popped because it ! 1387: contains state information that must be restored. \*M_line\fP and ! 1388: \*M_file\fP are extracted from the frame. \*Mefp\fP,\^ \*Mgfp\fP,\^ and ! 1389: \*Mipc\fP are restored from the frame using addresses ! 1390: relative to \*Map\fP. Note that this works because \*Minvoke\fP saves ! 1391: these registers and their location is known. ! 1392: .PP ! 1393: \*Map\fP and \*Mfp\fP are restored from the frame. When \*Mfp\fP is ! 1394: restored,\^ it serves to remove the procedure frame (made by ! 1395: \*Minvoke\fP) from the stack. At this point,\^ the stack is the ! 1396: same state it was in before the interpreter performed the ! 1397: \*Minvoke\fP instruction. A branch is made to \*Mefail\fP to ! 1398: cause failure in the enclosing expression. ! 1399: .NH 2 ! 1400: Testing the Basis ! 1401: .PP ! 1402: At this point,\^ enough of the system has been written to run some ! 1403: very simple Icon ! 1404: programs. \*Mtest/hello.icn\fP should be functional ! 1405: and more complete testing is in order. Refer to \fITesting the ! 1406: Basis\fP in \^[5].
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.