am HHHH H HI nswfl yn iltliflSlofi BHsliifl BKlffll H HI t USB SlftfiBm mWSSRmR LIBRARY OF THE UNIVERSITY OF ILLINOIS AT URBANA-CHAMPAIGN I 510. 84 lift- no. 661- G66 cop. Z CENTRAL CIRCULATION BOOKSTACKS The person charging this material is^re- sDonsible for its renewal or its return to the library from which it was borrowed on or beTore the Latest Dote stamped below. You moy be charged omm.mum fee of $75.00 for each lost book. for disciplinary ocHon «nd moy •»»«" " » JJ REN V E^L TELEPHONE CENTE* M3-S400 UIB 1 4 1996 At in 8 1996 When renewing by phone, write new due date below previous due date. IU >v T UIUCDCS-R- 7^-666 yjLt'fti THE CODER: A PROGRAM MODULE EOR CODE GENERATION IN HIGH-LEVEL IANGUAGE COMPILERS by Raymond P. Young July 197^ DEPARTMENT OF COMPUTER SCIENCE UNIVERSITY OF ILLINOIS AT URBANA-CHAMPAIGN URBANA, ILLINOIS ,.** THE LIBRARY OF THE AUG 1- 1974 UNIVERSITY OF ILLINOIS AT. URBANA-CHAMPAIGN UIUCDCS-R- 7^-666 THE CODER: A PROGRAM MODULE FOR CODE GENERATION IN HIGH-LEVEL LANGUAGE COMPILERS BY Raymond P. Young July 197^ Department of Computer Science University of Illinois at Urbana-Champaign Urbana, Illinois This work was supported in part by the Department of Computer Science and was submitted in partial fulfillment for the Master of Science degree in Computer Science, 197^. Digitized by the Internet Archive in 2013 http://archive.org/details/coderprogrammodu666youn Ill ACKNOWLEDGMENTS I would like to express my gratitude to my advisor. Professor Thomas R. Wilcox for his suggestions, patience, and guidance which helped make this thesis possible. I would also like to thank my parents and my friends for their encouragement and understanding- TABLE OF CONTENTS IV Page 1. INTRODUCTION 1 1.1 An Initial Look at the Coder 2 1.2 Review of Compilation................................ j I. ROLE OF THE CODER 9 2.1 A Discussion of Some Code Generation Systems 9 2.1.1 Intermediate Text..... 10 2.1.2 Code Generation Method................... 10 2.1.3 Template Implementation....................... 11 2.1.3.1 Example of a Template- 13 2.1.4 Sub-templates..... 14 2.1.5 Addressing.. 15 2.2 Summary of the Coding Process- 20 2.3 Coder Components... 21 2.3.1 Descriptor System............................. 21 2.3.1.1 Descriptor Information............... 23 2.3-1.2 Descriptor Types..................... 25 2.3.2 Coder Data Areas... 29 2.3.2.1 Translator Stack 30 2.3.2.2 Internal Coder Stack 30 2.3.2.3 Global Area 32 2.3.2.4 Register Area. 32 Page 2.3.2.5 Known Descriptor Chain... 33 2.3.3 Register Management............ 33 2.3-3.1 Register Management in ICL/360. . .. . . . 34 2.3.4 Storage Management.... 34 2.3.5 Address Computation 38 2.3.5.1 Addressing Modes.......... 38 2.3.5.2 Forward Referencing.................. 39 2.3.5.2.1 Example from ICL/360 40 2.3.6 Instruction Generation................... 44 2.3.6.1 Object Machine Simulation............ 45 2.3.6.2 Levels of Instruction Generating ICL.. 46 2.3.6.3 Generalized Object Machine...... 47 2.3.6.3.1 Tag Fields in the IBM/360 48 2.3.7 Flow of Control... 49 3. PROGRAMMING IN ICL . 51 3.1 Coding-time Computation.......... 51 3.2 Template Language. .................................. . 52 3.3 Error Handling....................................... 53 3.4 Supervisor Calls......................... 53 4. CONCLUSION 54 APPENDIX: IMPLEMENTATION OF ICL/360 56 LIST OF REFERENCES - 132 1. INTRODUCTION Problem solving on computers has moved away from assembly language and toward higher level languages which are more closely related to the problem being solved, less concerned with the actual machine, and much easier to use. Because of this, much research has been done in compiler construction. This work has been directed toward automating the compiler writing process and toward developing compilers for more complex languages. The function of code generation is less affected by the latter objective than the former. But little work has been done in making this portion of the compiler easier to build. For this reason, this thesis concerns itself with a program module, called a "coder", which relieves the compiler writer of many of the tedious details involved in generating machine code for a specific machine, while also allowing him to maintain control of the code being emitted. In particular, the coder for the IBM/360 [ 9 ] is proposed. A second reason for considering code generation is that, because software development is an increasing portion of the computer system cost, it is important that large scale programs be transportable. The partition of compilers into a language dependent tront-end and a machine dependent back-end lends itself to some degree of transportability. For a given compiler, an efficient tront-end should be written in a common language. Thus, it is transportable. This leaves only the machine dependent back-end to be rewritten for each individual object machine. The existence of the proper mechanism, the coder, will make the problem of machine dependence less severe. laJ. An_Initial_Look_at_the_Coder Assume that code will be generated from postfix, so that foe the expression: D = (A*B) +C code will be generated from the postfix string: D A B ♦ C ♦ =- For simplicity, assume that the location of each variable is represented by its symbolic name and that symbolic assembly language will ne generated. In short, code is generated by scanning the postfix string from left to right, placing the operands on a stack. When an operator is encountered in this scan, its operands are on the top of the stack and code is generated to perform the operation. The operands are then popped off the stack and the result (address) placed on tae stack . Note that in this method each postfix operator is essentially a trigger to generate code. The simplest way is to assume that all the operands, including temporaries, will always have their value in core after each operation at run-time. Then a fixed seguence of object code can always be used for each operator. For instance, the code for "♦" may have the form: LOAD R1, address of operand 2nd from the top of the stack ADD R1, address of operand on the top of the stack STORE R1, address of a temporary Then every time the "+" operator is encountered, the operand addresses are substituted and the code generated. Of course, the top two operands are popped and the temporary pushed onto the stack so as to be set up for the next operator. See Figure 1 for the steps in generating code for the example. It is apparent that this simple system generates very inefficient code: - assuming a multiple register machine, the use of storage instead of the unused registers to hold temporaries; - the unnecessary LOADs and STOREs. Without too much thought, one step toward improvement lies in leaving the intermediate results (T1, T2) wherever their values are generated and taking this into account when code which uses them as operands is generated. This implies that generating the code now becomes more complicated than merely inserting storage addresses into an operator "code skeleton". The nature of generating code has moved away from a "macro" type expansion to the execution or interpretation of a complex procedure. It is exactly this more complicated method of coding in which we are interested. U.2 Review _gf .Compilation Because the compilation model influences our concept of code generation, a brief description of the abstract model being used will be presented here. For further details, see Wilcox [17]. In any problem, a model or theory is needed as a foundation from which a solution is developed. Without any argument, it is asserted that the model described below is an abstraction of the compilation process as it appears in existing compilers. Of course. STACK POSTFIX GENERATED CODE (a) (b) (c) (d) (e) r I | D A B I t r 1 | D T1 I r I | D T1 C i r I | D T2 I r I J I • ♦ C + = C + = + = LOAD B1,A ADD R1,B STORE R1,T1 LOAD ADD STORE R1,T1 R1,C R1,T2 LOAD R1,T2 STORE R1,D Figure 1. Code Generation Using Address Substitution for D=(A+B)+C compilers are not partitioned exactly in this fashion, but the elements of each process and step are present. The compilation process can be divided into two distinct portions: recognition and code generation (see Figure 2) - Recognition is language dependent and machine independent, while code generation tends to be language independent and machine dependent. The recognition process consists of three subprocesses: 1. Lexical analysis reduces the character by character nature of the source program into a string of tokens. These tokens are, of course, the punctuation, identifiers, operators, keywords, etc., of the language. Tokens, which have not been previously defined, are placed into a symbol table where their attributes are recorded. 2- Sy ntac tic analy_sis is a check to ensure that the stream of tokens form a valid program according to the syntax rules of the language. 3. Semantic analysis unravels the "meaning" of the source program and creates an abstract parse tree (APT) . This APT is usually in the form of n-tuples or postfix which reflects the functional aspect of the source program. The APT is characterized by having flow of control implicit in the APT or embedded in the definition of a node of the APT, and by possibly having non-scalars for operands. Thus, the recognition portion of the compiler has transformed the original characters of the source program into a symbol table which contains the attributes of the various tokens of the program, and into the APT which reflects the partial ordering of the source Source Program I T Becognition r~ 1 Lexical Analysis | F r I Syntax Analysis | T r~ 1 Semantic Analysis | c + -J Y Intermediate Text Y Code Generation r~ 1 Storage Allocation 1 T r~ J Translation 1 1 T r _ ______ ____^ Intermediate Text 1 T i r Optimization T J 1 1 J 1 L J j 1 Y r"~ ~1 1 Coding 1 + 1 -J (Object Code) Figure 2. Steps in the Compilation Process operations on the defined tokens of the source program. Q.2d.e generation then takes the APT representation of the source program into the equivalent object program using the information contained in the symbol table. This transf ormation can be decomposed into four distinct steps; 1. storage allocation, I. translation, J. global optimization, and 4. coding. storage allocation consists of assigning to the source variables, offsets from a data area. Use of the base address of the storage area and the offset at run-time will specify the memory location being used for the variable. Once an address has been assigned to a variable, it will be possible to generate code to reference the variable if the coder knows where the data area's base address will be at run-time. I£a_lis_icit i i2n_ consists of mapping the APT into a sequence of source language machine (SLM) instructions, preserving the partial ordering of the source operations of the APT. It simultaneously expands the source operations involving aggregates into operations involving only scalars, and also makes explicit the instruction sequencing. The rationale for this comes from considering the SLM as a pseudo machine whose instruction set is ideally suited for implementing programs written in the source language. As a pseudo machine, it has the same characteristics as most other modern digital computers: sequential instruction execution, and operations involving scalar guantities only. Gl oba l o p^i mizatiop # if present, consists of rearranging the sequence of SLM instructions generated by the translator so as to perform the required computation in a aore efficient manner. (See Gries [8], chapter 18.) Finally, in the codinq £hase, the SLM is mapped into the real machine by expanding each SLM instruction into a sequence of object instructions. This sequence of object instructions when executed at run-time corresponds to the execution of the SLM instruction. In general, there will be several sequences which would have similar "effects", so that the efficient realization of this map is the domain of the coder. Note that there are really two levels of optimization in this compiler model: the global optimization of the SLM instructions which is object machine independent and the "low-level" optimization that is possible by considering only a few object instructions at a time. The former is handled in the global optimization step and the latter is a concern of the coding phase. 2. ROLE OF THE CODER The coder's functional tasks can be determined from the model being used: 1. Determining the various object code sequences which can implement a SLM instruction; 2. Selection of an "optimal" code sequence, usually based on the data items needed and where these items would be at run-time; 3. Allocation of resources so as to conform to the optimal sequence chosen; 4. Emission of the code sequence selected; and 5. Noting the run-time effect of the instruction sequence generated so that subsequent SLM instructions have relevant information for task 2. 2 t l A Discussion_of ..Some .Code_Generation. Systems In order to better understand what capabilities and features are desired and how they have been achieved in the past, a comparison of some existing code generation systems (MAD [1, 2], MAD/I [3, 6, 10, 15], PL/C [4, 16], an experimental PL/I compiler developed at Boulder, Colorado by IBM [5]) and compiler writing systems (MOBILE [11, 12, 13, 14] and CIL [7]) was made. The results of this survey and its implications for the coder can be summarized into five areas: 1. intermediate text. 10 2. code generation method, J. template implementation, U. sub-templates, and 5. addressing. 2._1.._1 Intermediate Text The torm of the coder input determines the complexity of the compiler's front-end and also some characteristics of the coder. MAD and MAD/I use n-tuples, whereas PL/C uses postfix with some prefix. If n-tuples are used, the semantic routines must manage an operand stack, thereby increasing the tasks of the recognizer. Postfix would require the stack be in the coder. The choice of intermediate text will also depend on the global optimization desired. Such optimization techniques are usually more suited for an n-tuple or tree representation than for a postfix form. because postfix and n-tuples are so commonly used, a coder should be capable of handling both efficiently. Trees are not used as frequently in compilers, so its implementation in a coder is likely to be of less practical use. 2±l-.2 Cod e .Genera ti o n_ Met hod Having decided on the input, the next area of concern must be the method of code generation. Code generation is a mapping of the intermediate text into an object machine sequence. There are two common ways of defining a mapping, and hence there are two general methods of code generation. Simple substitution into a code 11 skeleton is akin to a tabular representation of the map. As has already been seen, this normally leads to inadequate code- At the other extreme, a function may be specified by a complex algorithm which manipulates the arguments to produce the desired functional value. The interpreted template is the code generation analog. PL/I (Boulder) is really a template scheme designed for trees- CIL generates code through semantic routines with code brackets. These semantic routines are similar to templates but are simpler than the other template schemes for code generation since operand checking and such need not be done. The template scheme has the feature of breaking up the coding process into convenient sized steps- Each call to a template generates a code sequence that can be optimized on the low level. This is possible because each template call will alter the run-time environment by a noticeable amount, but not by so much that the individual changes themselves become obscured. In MAD, MAD/I, and PL/C, templates are chosen on the basis of both the operator and the operand modes. This is probably the easiest way of generating code if type checking must also be performed simultaneously. Templates can also be selected solely on the basis of the operator; and type checking, if it must be done at this time, can be handled within the templates. This serves to make the templates longer, but reduces the number of templates required. 2 -J. -. 3 Template I mplementation As was seen from the example in Section 1-1, simple "address" substitution into a code skeleton is inadeguate. Even a more \l sophisticated Ddcco processor such as STAGE2 of the MOBILE system is not sufficient. Although it has a very powerful conditional expansion capability and an associative memory, embedding any coder in such a system would reguire many additions to provide the facilities desired. What is wanted is the flexibility and power expected from a higher level language directed at the coding problem. However, in most of the schemes considered, the process of code generation occurs in a much larger environment. This means routines are written in a language which provide unneeded power tor code generation purposes and which freguently performs the coding tasks in an unnatural manner. In our model, coding occurs in a more restricted environment. Because of this, the capabilities that are reguired are more clearly defined and less confused with the other tasks that occur during compilation. As was mentioned earlier, higher level languages are being developed and used because they are more natural to the problem at hand. Then why not have a special language just for coding purposes? Carrying our model through, the coding language can oe viewed as nothing more than a machine, the coder, designed for just such a purpose! It is exactly this view which is taken here. The templates are programs written for the coder. Each time a template is called, the coder executes or interprets the instructions making up the template to perform the coding tasks. Viewing the coder in this manner, we have implicitly adopted tne characteristics and conventions associated with digital machines. More concretely, an assembler-type format (op-code, 13 operand fields, tag fields) is used for the coder's instruction set. 22.l-_3.j_l Example of a Template To better understand the coding mechanism, an example is given of a template which implements an ADD SLM instruction on the IBM/360. Suppose the SLM has only 32 bit two's complement integer arithmetic and uses a three address instruction format. Then ADD C,A,B would form A«-B and call the result C. A template to do this is shown in Figure 3. Because the SLM data type corresponds to the full word integer of the IBM/360, the fixed point instruction set is used to implement the ADD. Fixed point arithmetic can be done most conveniently using the general purpose registers (GPRs) , so that the operands of tne SLM instruction are checked to see if either is already in a GPR (lines 3 and 4) . If neither are in a GPR, one is acguired and code is generated to place the value of A into it (line 5). Local variables D and E are assigned values in such a way that D is the operand known to be in a GPR and E is the other operand (lines 6 to 10). Now since an RR-instr uction is preferable to an HX-instruction, one is generated, if possible (lines 11 and 12). The result at run-time will be in the register indicated by D. Since this result should be in C, C and D are associated with the same register (line 13). Because the operand D is no longer in the original register, the coder must be informed (line 14). 14 1 ADD TEMPLATE C,A,B 2 LOCAL D,E . CHECK TO SEE IF ANY OF THE OPERANDS ARE IN REGISTERS 3 BC 6000,A,AINR 4 BC 6000,B,BINR 5 LGPR A,NZ . OPERANDS NOT IN A GPil . SET D TO POINT TO AN OPERAND WHICH IS IN A GPR . E WILL BE THE OTHER OPERAND 6 AINR PTD A,D . A IS IN A GPR 7 d 9 BINR 10 11 ADD1 12 GRR AR,D,E . E IS IN A REGISTEB 13 ADD2 LINK D,C . RESULT IS IN GPR 14 15 16 ENR 17 18 Figure 3. Example of an ADD Template PTD A,D PTD B,E B ADD1 PTD B,D PTD A,E BC 6010, E, ENR GRR AR,D,E LINK D,C FREE D EXIT GRX A,D,E B ADD2 END ADD In this example, the detailed meaning of each statement of the template is not important. Rather, these statements are simplified from the coder proposed for the IBM/360 so as to make the general idea clear. As.lz.ii Sub-templates Certain coding language sequences may appear frequently in one or several templates. As in other languages, the subroutine equivalent is desirable for the modularity that can be derived froi its use. The reduction in template lengths, the consequent clarity and understandability of each template are all factors that cannot be ignored. Anyone who has programmed realizes the utility of 15 subroutines- Since creating a template is no more than programming in the coding language, the inclusion of sub-templates in the coder should evoke no protests. 2ili5 A ddre ssing A major problem to be handled is the matter of addressing. Operand addressing is handled automatically by all of the coding systems investigated. This is very important because it relieves the compiler writer of a very messy detail. The difficulty of the addressing function necessarily depends on both the source language and the object machine. For instance, MAD and MAD/I are not block structured, so that addressing can be simply determined from the address information in the symbol table. Because PL/C, PL/I (Boulder) , and CIL are faced with more general accessing functions, they all maintain a complex descriptor system in order to perform this task. PL/C and CIL, however, do not handle the various access functions cleanly. CIL permits only up to two levels of indirection and subscription. PL/C handles subscripting by performing the address computations and then manipulating the BASE field of the variable. It will be instructive to see exactly how PL/C handles addressing. The information to access the operands is embedded in the descriptors and is called the address word (AW) . If an operand is in a register, its AW (now called a RW) points to the corresponding register descriptor (RGSW) ; otherwise the AH has two fields, BASE and DISP, where BASE points to some other AW. 16 When storage is allocated to a variable this address is placed in the symbol table. As the variable is encountered by the expression translator in the intermediate text, a descriptor is placed on the coopile-time stack. For the AW, the DISP field is the address of this variable; that is, the offset from the beginning of some activation record. If the variable is STATIC, the BASE field points to the descriptor for register 11, a dedicated register pointing to the run-time location of the base address for the STATIC activation record. If the variable is of the AUTOMATIC storage class, the BASE field points to the descriptor for the base address of the activation record of the block in which the variable is declared. To illustrate this, consider the program segment in Figure 4a. When the block is first entered (during code generation) , the AW for the block will point to the RGSW for register 13, another dedicated register which contains the base address of the current activatioo record. As the internal blocks are entered, the AW of the bloc* will be changed to the dedicated register R9, the base address of the activation record one level down; then to the dedicated register R8, the base address of the activation record two levels down; and then finally to a location in the current activation record which contains the base address of the activation record. Thus, to use this technique effectively, a third field to indicate the index should be used along with the BASE and DISP fields already in PL/C. An alternative method would be the two-pass code generation system used in PL/I (Boulder) . Multi-pass code generation makes 17 STMT LEVEL NEST BLOCK PL/I SOURCE STATEMENT 1 M: PROCEDURE OPTIONS (MAI N) ; 2 1 1 DECLARE 1 S, 2 A, 2 B; 3 1 1 CALL P (S) ; 4 1 1 P: PROCEDURE (PS) ; 5 2 2 DECLARE 1 PS, 2 A, 2 B; 6 2 2 B1: BEGIN; 7 3 3 DECLARE C; 8 3 3 B2: BEGIN; 9 4 4 DECLARE D; 10 4 4 B3: BEGIN; 115 5 DECLARE E; 12 5 5 PS.B= (E-D) *C/S,B+PS.A; Figure 4. (a) Sample PL/I Program Segment LD F2, 09(0,813) (E SD F2,D6(0,R9) -D) MD F2,D4(0,R8) *C L R15 r D7 (0,R13) (R15 IS A PL/C SCRATCH REGISTER.) L R15,D2 (0,R15) DD F2,0(0,R15) /S. B L R1Q,D8 (0,R13) (R10 IS THE OTHER SCRATCH REGISTER.) L R10,D3 (0,R10) L RIO, 0(0, RIO) AD F2,0(0,R10) +PS.A L R15,D8 (0,R13) L R15,D3 (0,R15) L R15,4(0,R15) STD F2,0(0,R15) =PS. B Figure 4. (b) Code Generated for Statement 12 in (a) by PL/C 18 J Top of AR | Stack I- / / I / / +. • I 1 / / / / • PS L + > I I / / 1 B I I A / / I .+ » >l L + , i > I D7 D5 D4 D3 D1 I AR for I Block BJ D^ 1 I I 1 D8 | 1 I 1 1 1 1 1 i l — AR for 1 Block B2 D6 -+- AR f DE Block B1 1 1 - + - 1 1 AR f ar Proced ure P 1 1 -■*■- 1 1 1 1 I AR for Procedure M 1 D2 >c- Figure 4. (c) Run-time Environment While Executing Statement 12 19 Block B3 AR Base; Block B2 AB Base; Block B1 AR Base PS. B: PS. A: Dope Vector PS.B Dope Vector PS. A Dope Vector PS: PROC P»s AR Base: r - s.B: S. A: Dope Vector S.B: Dope Vector S.A: PROC M»s AR Base; r ■ -♦--. i L > r J (RFD r ■-♦ — i t > r J (RFD r L > r | (RFD «_ r • + - 1 i ». -f — i j r -♦--• i > r -♦ — • i —- — > r -♦ — • i > r - + — • L_ > r - + • L r — + — , i ■f — ♦ — I i r -♦ — • i > r - + — • i. > r -■f — • L > r D9 D6 DU D3 D8 D1 >l | Register 13 j I (RSD) | ^ j r i | Register 9 | I (HSD) { c j >l j Register 8 j J (RSD) | i j 4 4 | Register J I Status | | Descriptors) BASE and DISP Fields of Value Descriptors (SFD unless indicated otherwise) Figure 4. (d) Descriptor Structure While Compiling Statement 12 20 forward referencing easier to handle, sioce the addressinq code can be inserted later. It also helps remove the problems of register management from the initial code generation. Except for more efficient handling of forward references, there is no real reason for using multiple passes. If some inefficiency can be tolerated with forward references, then the one-pass scheme is acceptable. In a one-pass method, the forward references in a branch can be handled by the method CIL uses: branching to an always addressable area, generating the necessary addressing code, and theu performing the branch; or by some other "worst case" method. 2^2 Summary of the Co ding_Prgcess A characterization of the coding process has been developed by studying the compilation process and by a comparison of existing code generation systems: 1. The compiler writer determines the SLM instruction set, that is, creates the pseudo machine that is ideal for implementing the programming language. 2. Each SLM instruction from the compiler's front-end is nothing more than the name of a template which is to be "expanded" in order to generate the object code that implements it. The operands of the SLM instruction are the parameters of the template. 3. Each template is written in an interpretive coding language (ICL) which is the instruction set of the coder. Thus, it is the compiler writer who must decide the various alternatives which implement an SLM instruction. The ICL, because it is the instruction set of the coder, must reflect the coder and its 21 features. It provides: 1. Decision-aaking capability. 2. Sub-templates with local variables and parameters. J. Automatic handling of the various access functions for object machine addressing. 4. Input of either n-tuples or postfix, including the management of the stack. 5. Register management. 6. Temporaries management. 7. Descriptors for recording necessary information. 2 ..3 Coder _Co mp onents As in the original compiler, the coder may be decomposed into object machine independent and object machine dependent portions. This decomposition reflects the fact that certain ICL instructions should be present regardless of the object machine, while others are present because of the particular object machine. In the rest of this section, the coder is discussed in general terms so as to explain each components presence. (See Figure 5 for the overall coder structure.) Where necessary, the proposed coder for the IBM/360 will be used to illustrate its implementation. 2-.3_._l Descriptor_System In order to generate good object code, the coder must be able to keep track of the run-time state of the object machine. This object machine state consists of the register contents and the status of other program items, such as, source constants, variables. Symbol Table Descriptor Area <- Source Program I y r i >J Recognizer | l . J r i >\ I | Interface J | Module | >l I i j COHPILER FRONT-END CODER I 1 < >, ,< J I I r > | Control Unitj< t II 11 i I . >l l<— i J r r t >|Template Area| i j I Internal | | 1 Coder Stack |<— » | | Translator | »-- >j Stack | i j 1 Global Area J< * J Storage Areas | «- >j Known 1 t j l- 1 j Descriptor | | Chain | i j Figure 5. Overall Coder Structure 23 labels, and required temporaries. For this reason, a compile-time variable, called a descriptor, is used to indicate the status of a program item at run-time. Routines are also needed to manipulate these descriptors as code is generated. The information contained in the descriptor comes from the compiler writer (through explicit ICL statements) , from the coder itself, and from information contained in the symbol table when the descriptor is created. The ICL allows the compiler writer to specify only that information which he could be expected to know: 1. the location of the program item; 2. the object machine representation; 3. the value, if known; and 4. the number of uses. The descriptors can also be altered because of side effects of some ICL instructions. For instance, in generating an object instruction using program item A, execution of the instruction may result in the value of A becoming known (as in STORE R1,A where R1 is known to contain value 5). This fact is recorded within the descriptor for A. As a practical consideration, the descriptor must have a form efficient for the coder to use and which at the same time is clear to the compiler writer. laJiJU^ De sc riptor _Inf or mat ion The first three items of information are almost self- explanatory and little will be said about them- However, the USES 24 field probably requires some discussion. As implied by its name, the location of a program item merely indicates the location in the machine of the value at each moment of run-time; usually in a register or in storage someplace. The manipulation of this location information will be discussed in Section 2. 3. 5. The object machine representation of a program item is the data format of the object machine used to implement the program item. For instance, in the IBM/360 a full word integer would probably be used for a PL/I variable with attributes FIXED BINARY (31). Code optimization is based to a great degree upon the number ot times a program item is referenced. By this, we mean the number of times a program item appears as an operand of an SLM instruction. Thus, USES is just a count of the number of times a program item occurs as an SLM instruction operand. While this number is positive, the program item should, in general, retain the resources it possesses; otherwise, unnecessary STOBEs and LOADs will be generated. Because descriptors reflect the run-time status of the program, they are dynamically created and destroyed. Thus, this field is also used by the coder to determine if the descriptor itself should be destroyed. There is no need to have descriptors around which will never again be used. The actual manipulation of the USES field is somewhat more complicated than this. Since other descriptors, the coder registers, coder data areas, and forward references may have pointed to this program item, the program item is said to be pointer 25 referenced, it would be disastrous to destroy the descriptor before these pointer references are no longer needed. Hence, every time a descriptor is referenced in this manner its USES field is incremented, and every time a descriptor is no longer being pointer referenced, its USES field is decremented. Only if the field is zero can the descriptor be destroyed and its resources released. Occasionally, the compiler writer may want the descriptor to be around for the remainder of code generation regardless of the actual number of times it is referenced, that is, descriptors with an infinite number of uses. To maintain uniformity, the value of -1 is adopted to indicate this. 2 i 3 i J i 2_ Descriptor, Types There are four types of descriptors: 1. source value (SVD) , 2. storage area (SAD) , 3. register status (RSD) , and 4. register save area (RSAD) . A PL/I representation of these descriptor types for ICL/360 is given in Figure 6. The SVD is used to represent the program items of the source program. This normally includes labels, source constants, source variables, and temporaries. The SAD describes the various storage areas that are in existence. A storage area can be viewed as being a LABEL type program item and the "value" of a storage area is its base address. Like many program items, the value of a storage area is often not 26 1 SVD BASED(#SVD) , 2 D_MANAGEMENT, 3 D_TYPE BIT(2) INITIAL (» 00 • B) , 3 USES FIXED BINARY(31) INITIAL(1), 3 HEFEBENCE_CHAIN POINTER INITIAL (NULL) , /* HEADER TO THE CHAIN OF FORWARD REFERENCES TO THIS SVD */ 2 ACCESS, 3 BASE POINTER INITIAL (NULL) , 3 DISP POINTER INITIAL (NULL) , 3 INDEX POINTER INITIAL (NULL) , 2 LOCATION_INFO, 3 STORAGE BIT(1) INITI AL ( » 1 • B) , /* IS THE VALUE IN STORAGE? */ 3 REGISTER BIT (1) INITIAL (• • B) , /* IS THE VALUE IN A REGISTER? */ 3 TEMP BIT(1) INITIAL('O a B) , /* HAS A TEMPORARY BEEN ALLOCATED FOR THIS VALUE? */ 3 IMMEDADDR BIT(1) INIT( a 0»B), /* IF NOT ALLOCATED STORAGE YET, HILL THIS VALUE BE IMMEDIATELY ADDRESSABLE ON A FORWARD REFERENCE? V 2 ATTRIBUTES, 3 S360 BIT(U) INITIAL(*0001'B) , /* 0000 - BYTES 0011 - SFL 0110 - PACKED DECIMAL 0001 - HWI 0100 - LFL 0111 - ZONED DECIMAL 0010 - FWI 0101 - LABEL 1000 - CHARACTER */ 3 KNOWN BIT(1) INITIAL (» 1 B) , /* IS THE VALUE KNOWN? */ 3 DUMMY BIT (33) , 3 LENGTH POINTER, /* NUMBER OF BYTES ALLOCATED FOR THE VALUE */ 3 VALUE BIT (64) ; Figure 6. (a) PL/I Representation of the ICL/360 Source Value Descriptor 27 1 SAD BASED(#SAD), /* A SAD IS A REFORMATTED SVD */ 2 D_MANAGEMENT, 3 D_TYPE BIT (2) /* 01 */, 3 USES FIXED BINAR¥(31), 3 REFERENCE_CHAIN POINTER, /* HEADER TO THE CHAIN OF FORWARD REFERENCES TO THIS SAD */ 2 ACCESS, 3 BASE POINTER, 3 DISP POINTER, 3 INDEX POINTER, 2 LOCATION_INFO, 3 STORAGE BIT(1) , /* IS THE VALUE IN STORAGE? */ 3 REGISTER BIT{1) , /* IS THE VALUE IN A REGISTER? */ 3 TEMP BIT (1) , /* HAS A TEMPORARY BEEN ALLOCATED FOR THIS VALUE? */ 3 IMMEDADDR BIT(1) , /* IF NOT ALLOCATED STORAGE YET, WILL THIS VALUE BE IMMEDIATELY ADDRESSABLE ON A FORWARD REFERENCE? */ 2 ATTRIBUTES, 3 S360 BIT (4) /* 0101 */, 3 SA_TYPE BIT (2) , /* 00 - CODE AREA 01 - CONSTANT DATA AREA 10 - VARIABLE DATA AREA */ 3 MAXSIZE FIXED BINARY (31), /* NUMBER OF BYTES IN THE STORAGE AREA */ 3 FIRSTBYTE FIXED BINARY (31), /* INITIAL OFFSET OF THE STORAGE AREA */ 3 LC FIXED BINARY{31) , /* OFFSET OF THE NEXT FREE BYTE */ 3 IMAGE POINTER; /* AUXILIARY DATA STRUCTURE FOR THE STORAGE AREA */ Figure 6. (b) PL/I Representation of the ICL/360 Storage Area Descriptor 28 1 BSD BASED(#RSD), 2 D_HANAGEMENT, 3 D_TYPE BIT(2) INITIAL (• 10 • B) , J USES FIXED BINARY(31) INITIAL(-I), 3 DUMMY FIXED BINARY(31), 2 ACCESS, 3 BASE POINTEB INITIAL (NULL) , 3 DISP POINTER INITIAL (NULL) , 3 INDEX POINTER INITIAL (NU LL) , 2 REGISTER_INFO, 3 REG# BIT (4) , 3 PAIRED BIT(1) INITIAL (• 0« B) , /* IS THE REGISTER PAIRED? */ 3 REG_TYPE BIT(1) INITI AL ( • 1 • B) , /* - FPR 1 - GPR */ 3 DEDICATED BIT(1) INITIAL (• • B) , /* IS THE REGISTER DEDICATED? */ 3 INUSE BIT(1) INITIAL( , 0» B) r /* IS THE REGISTER IN USE? */ 3 REGISTER BIT(1) INITIAL (• 1 » B) , /* IS THE VALUE OF THE SVD (OR SAD) LINKED TO THIS RSD IN THE REGISTER? */ 3 KNOWN BIT(1) INITIAL (■ 0» B) , /* IS THE VALUE IN THE REGISTER KNOWN? */ 3 TIME FIXED BINARY(31) INITIAL(O), /* TIME OF LAST "FREE" FOR THIS REGISTER */ 3 CONTENT POINTER; /* POINTS TO THE SVD LINKED TO THE RSD */ Figure 6. (c) PL/I Representation of the ICL/360 Register Status Descriptor 1 RSAD BASED(#RSAD) , 2 D_MANAGEMENT, 3 DJTYPE BIT (2) INITIAL (• 1 1 » B) , 3 USES FIXED BINARY (31) INITIAL(1), 2 ATTRIBUTES, 3 KDC POINTER INITIAL (NULL) , 3 GPR(0:15,2) POINTER, 3 FPR (0:3,2) POINTER; /* COLUMN 1 POINTS TO A COPY OF THE RSD COLUMN 2 POINTS TO A COPY OF THE SVD OR SAD LINKED TO THE RSD */ Figure 6. (d) PL/I Representation of the ICL/360 Register Save Area Descriptor 29 known at compile-time so a location is usually specified indicating where it can be found at run-time- Thus, a SAD is a SVD which describes the base address of the storage area with the following additional information to describe the storage: 1. type of storage area, 2. offset value for the first byte of the storage area, and 3. size of the storage area, RSDs describe the object machine's registers and records: 1. the program item occupying the register (points to the descriptor) ; 2. the value in the register, if known; and 3. miscellaneous information which reflects the machine's idiosyncracies. The RSAD maintains a copy of the known descriptor chain {described in Section 2-3.2.5) and a partial copy of the run- time status of the registers and its contents for later use. It is used tor forward referencing where the program item-machine status must be saved so that patching can be done efficiently. 2 -J -.2 Coder Data_Areas Although all descriptors reside in a descriptor area, they are lot directly accessible to the coder programmer. Rather, access to lescriptors are through cells. A cell is nothing more than a jointer to a descriptor. These cells are partitioned into five :oder data areas: 1. translator stack, JO 2. internal coder stack, J. global area, U. current register area, and 5. known descriptor chain. Operands of ICL statements referencing descriptors specify a data area and an index specifier, which indicates a cell of the data area. The descriptor pointed to by that cell is the operand- i-.ia.2jj. Translator Stack To handle postfix input to the coder, a translator stack is present as are ICL statements to pop and push a pointer to the descriptor of an element on and off the stack. For n-tuple input the translator stack would never be specified. The index specifier in a translator stack access is the cell counting from the top of the translator stack. 2,3 -2-2 In terna l Coder .Stack To facilitate template calls within the coder, an internal coder stack and two associated inaccessible "registers", the INTERNAL_STACK_TOP and the LOC AL_DATA_POINTER, are used- The LOCAL_DATA_POINTER indicates the current template's activation record, while the INTERNAL_STACK_TOP indicates the current top of the stack- (See Figure 7.) In order to restore the environment on return from a template call, the calling template's return point and a pointer to its activation record is saved. Arguments for a template call are descriptors. Thus on a call, pointers to these descriptors are 31 I --- I CELLS I FOR I LOCAL I VARIABLES I CELLS I FOR j PARAMETERS | TEMPLATE RETURN POINT | OLD LOCAL_DATA_POINTER • * • < — INTERNAL STACK TOP < — LOCAL DATA POINTER Figure 7. Internal Coder Stack 32 placed on the internal coder stack. When called, the template will acquire any additional cells needed for local variables from this stack. During execution of the template, pointers to previously existing descriptors or newly created descriptors will be placed into these local variable cells. In accesses via the internal coder stack, the index specifier refers to the cell counting from the LOCAL_DATA_POINTER. iiii^sJ Global Area Cells are local, as described in Section 2.3-2.2, or they may be entirely global. This is accomplished through a global area of N cells. The index specifier indicates the particular cell of the global area, as in array subscription. 2._3._2.J± Register Area It is often necessary to refer to a particular register in code generation. For instance, conventions may require that register 1 contain the address of the parameter list in a subroutine call. Hence, a way to refer specifically to register 1 is needed. Since the ICL statements normally specify SVDs rather than the actual RSD linked to it (see Figure 3), we would like to remain consistent with this. Thus, although register 1 is specified, what we really have is the SVD which is linked to register 1. Since a register may be unoccupied, a "blank" SVD is needed which is linked to an empty RSD. Thus, an access of the register area results in a pointer to a SVD which is linked to the RSD for the register given by the index specifier. 33 2.3-2-5 Known D escriptor Chain The known descriptor chain (KDC) is a list of the program items whose run-time value is known at coding-time, for example, A in A = 1- The KDC is not a coder data area in the sense of the others: the coder programmer can only indicate that a program item is to be removed from the KDC or that at run-time the value will be known. The coder will subseguently insert or delete elements of the KDC as appropriate, depending on side effects from executing the instruction generating ICL statements. 2._3._3 Register Management In most machines, registers are a scarce commodity, so that one of the tasks in compiling into efficient object code, is the "optimal" use of the registers. In determining the register management facilities, a number of facts must be considered; among them: the number available, how they can be used (indexing only?, floating point arithmetic only?) , and the register configurations allowed in the object instructions. There are two types of registers: those which are managed by the coder and those which are not. Dedicated registers are not allocated nor released nor otherwise implicitly altered by the coder. The managed registers are used for the normal purposes of addressing, indexing, arithmetic, and logical operations. Clearly, the total number of registers reguired at any one time must not exceed the number of managed registers available. In the worst case, when a register allocation reguest is processed, all registers in the class are already in use. 34 Consequently, the register allocator must implement some sort of allocation scheme to select a register, generate code to place its c ontents into storage (a temporary if necessary), reallocate the register, and update the descriptors. 2-.2-.3-J. RegJ^ter_Manaaement_in_ICLZ3 60 in the proposed IBM/360 coder, the register management ICt statements try to reflect the characteristics of the machine: 1. There are two types of registers: general purpose registers (GPRs) and floating point registers (FPRs) ; 2. General purpose register RO cannot be used as an index or base register; and 3. The general purpose registers can be considered as even/odd pairs. The allocation algorithm for the proposed IBM/360 coder -as rbitrarily chosen to be: Select the managed register which was the least recently one FREE'd (an ICL statement which causes the resources associated with the descriptor to be released it the USES field is zero). Naturally, as knowledge is gained about the register allocation process, the algorithm used would be changed. This may also have an impact on the descriptors because additional information may be required. a 2.3.4 St^rag.e_Management There are three types of storage areas: 1. A code area (CA) is an initializable storage area whicl normally contains object code. 35 2. A c onst ant data area (CDA) is also an initializable storage area but is used by the coder to hold the constants necessary foe program execution. 3. The var iable data area (VDA) is used to hold variables and temporaries. A SAD must be created for each storage area that the compiler uses and it is up to the compiler writer to generate code to acguire the storage at run-time and place the base address in the register or storage location that has been indicated by the SAD's location field. During compile-time, there may be several storage areas of each type in existence. For instance, a separate code area might be used for the object code of each PL/I procedure. Because PL/I procedures may be nested inside one another, there will be several code areas. However, only one of each will be used by the coder; these are the ones pointed to by the coder registers CODE_AREA, CDA_AREA, and TEMP_AREA. Because CAs and CDAs are initialized at compile-time and will remain invariant with the execution of the object program, the coder need only be capable of allocating and initializing elements from these areas. Program elements that reguire initializable storage can never release them. If possible, the storage requirement of the object program itself should be kept minimal. Since there are two initializable components of the storage managed by the coder, it is natural to look at each of these in turn. 36 Code areas cannot be altered in any way without first knowing more about the algorithm the code is implementing. This information the coder does not have. Moreover, the actual code contained in a CA should not be manipulated by the coder since it is really the concern of the compiler writer (via templates) - For the CDAs, these arguments do not apply. The bit patterns inserted in a CDA are normally as a result of the coder requiring an addressing constant. Since the CDA image must be saved anyway, storage in this type of area can be minimized by using a locatioa already allocated which has the bit patterns (value) desired. There are at least two ways of doing this. The first requires a search of the CDA for the alignment and value desired. If none is found, then storage in the CDA will be allocated and initialized; otherwise, the storage already allocated is used. The second way consists of associating another data structure with each CDA. Then the value and alignment are hashed to access an entry of the data structure which will indicate the location in the storage area desired. The hashing scheme involves the overhead of another data structure and also does not check all the possibilites to reduce the storage used. Storage across consecutive CDA entries are not checked: it may be that the bit patterns of the last portion of one entry and the bit patterns of the next entry is just the value needed. Thus, although the linear search is very slow if the allocated CDA is very large, it is still preferred. On the other hand, VDAs are run-time dependent. Since source variables are usually given space by the storage allocator, the 37 primary problem faced here by the coder is in the allocation of storage in the VDA for the temporaries. In the simplest case, tha ranges of temporaries (their period of existence) are either disjoint or completely nested inside one another (Gries [8], page 300) and a simple compile-time stack can be used. However, code optimization may result in overlapping temporary ranges. In the general case, temporaries come into existence and depart at arbitrary points in time. Thus, a compile-time storage mechanism more complex than a simple stack is reguired. This is really a problem in "dynamic" storage allocation with an additional constraint of trying to minimize the amount of run- time storage that will be needed. Hence, technigues which allocate storage until none is left and then performing a garbage collection are unacceptable. Besides possibly using the entire storage area when it can be avoided, the problems of garbage collection in this particular environment are complex. Recall also that temporaries, being scalars, will normally require only one word of storage. Hence, any scheme which requires lists of available storage will tend to be inefficient with storage because of the overhead (for pointers). Normally, this is not tolerable. Perhaps the simplest technique meeting these constraints is the one known as block-bit. In this method each word of the storage area is mapped into a single bit. If the bit is on, the word is in use; otherwise, it is free to be allocated. This method suffers the overhead of a linear search through the bit table for a free word each time a temporary is allocated storage. Note that an allocation 38 algorithm will still be required to decide which word (s) will be allocated (first-f it?, best-fit?) . 2 ..3 ..5 Add£ess_Com£Utation A very fundamental task of the code generation process is the matter of operand addressing. If the instructions are such that all of core memory can be accessed without the need of a base register, then addressing is trivial and forward referencing reduces to a mere problem of patching up generated instructions. Hence, this discussion is very dependent on the object machine. For this reason, the addressing modes of the IBM/360 will be used as an example in the remainder of this section. 2._3.J>._1 Addressing Modes For a given machine, there are usually several addressing modes and the coder should be capable of expressing them all — preferably with a descriptor representation which encompasses them all as special cases. This is possible for the IBM/360. The allowable operand addressing schemes involve three quantities: 1. a base, normally thought of as the storage area base for the operand; 2. a displacement, a known constant offset from the base; and finally, 3. an index, which is a run-time offset from the address formed by the base-displacement. Each of these fields point to other descriptors because they are in actuality also program items. Naturally, this chain does not 39 continue indefinitely. At some point, a known program item or a known location (absolutely addressable) must be specified. Now given a particular instance of the operand's location, the coder can convert it to the form appropriate to the instruction that is to use it. This may involve generating code to set up base and/or index registers, and to get the displacement down to a legal value. 2i.3j.55_2 Forward Referencing The forward reference problem results when a reference is made to an operand whose location is not known at coding time t1. At that time, when code is being emitted, the amount of space ultimately required to address the operand is not yet known. There are several alternatives. A worst case assumption can be made so that for every forward reference, N words in the code area are reserved, where N will depend on the maximum length of the descriptor chain that is allowed. Needless to say this can become extremely costly in terms of space, depending on the number of forward references and the fraction of the N words that is actually used in each instance. Another method is to branch to an always addressable area where the necessary code can be inserted. A third method is proposed which requires an always addressable constant data area. The compiler writer can specify that no additional addressing code will be required. In that event, no space is reserved. otherwise, M words are reserved for addressing code, a scratch register Ri is obtained, and the status of the registers at time t1 is saved. Typically, the value of M will be 40 smaller than N because the descriptor chain length is not a consideration in this algorithm. At time t2 when the coder finds out that the operand is at location X, there are three possible cases of patching: 1. X is immediately addressable at time t1. By immediate addressability is meant that the operandi location can be encoded in the object instruction without need for additional addressing code. Hence, there is a base register Rj, such that the displacement D1 is in the allowable range. Then either no-op instructions or a branch around the reserved unused space is inserted in the M reserved words, whichever is more efficient. 2. X has displacement D1 outside the allowable range for displacements from any base register Rj at time t1. In this event, a storage element from the always addressable CDA is initialized with value D1. The N reserved words will then be filled with code which at run-time adds the value of D1 (if zero is an allowable displacement) to the value in Rj using the scratch register Ri. Thus, the desired location is displaced zero from register Ri. 3. X cannot be expressed as the displacement from any base register. In this case, a loader or linkage editor must be assumed which will resolve the reference after compilation. 2.3.5.2.1 Exampl e from _ ICL/36Q Consider the following ICL/360 instruction: GRX ST,0PND1,0PND2 which generates an RX-instruction to store the value whose contents are in the general purpose register indicated by 0PND1 into the 41 storage location given by OPND2. For def initeness, assume 0PN01 indicates GPR 3 and that the location of 0PND2 is displaced 20 bytes from the value of 0PND3. The value of OPND3 represents the base address of a storage area. Moreover, suppose the value and location of 0PND3 is not known. Now when the coder attempts to execute this ICL instruction at time t1, it finds that OPMD2's base field, 0PND3, is not known. The coder then applies the forward referencing algorithm to place the value of 0PND3 into a scratch register, say GPR 7, and then finally generates the STORE instruction. Since the coder is not told that 0PND3 will really be immediately addressable, three halfwords are skipped and the skeleton of the LOAD instruction is inserted into the current code area (see Figure 8a) . In addition, the following information is saved: 1. a pointer to the current code area descriptor, say OBJECT; 2. the displacement Z (=1080) in the code area; 3. a pointer to a RSD for the scratch register, GPR 7; 4. a pointer to the always addressable CDA, say ADDR, whose base address is in GPR 11; 5. the current contents of the registers (a pointer to an RSAD) ; 6. the number of halfwords skipped, in this case 3; 7. the instruction format where the forward referenced operand is being used, in this case RX; and 8. which operand this forward referenced operand is of the object instruction. In this case 0PND2 is the second operand of the RX-instruction. <*2 Code Area OBJECT: R10 > I Z = I 1080| I 7 Each indicated line is an empty halfvord L R7, ST R3, 20(87,0) Some of the GPRs and Their Contents: R7 - Scratch R9 - Base address of storage area VAR R10 - Base address of storage area OBJECT E11 - Base address of storage area ADDR Figure 8. (a) At Time of Forward Reference Code Area OBJECT: R10 > A Z = | 10801 BC 15,1086(R10) L : R7,3000 - A z = I • 1080j i 1 T • LB B7,B9 AH B7,20Q0 (R11,0) L R7,Q(R7,0) ST B3,20(R7,0) Constant Data Area ADDB; R11 > . A J 2000* I 1 5076 Figure 8- (c) At Time Reference is Besolved with D=5076 44 Now at tine t2, 0PND3*s location is defined to be displaced D from storage area VAR. It just so happens that the base address of VAB is in GPR 9 at time t1. If D is 3000, then this is case 1, and the empty halfwords are filled as shown in Figure 8b- If D=5076 and no other general register will make OPND2 immediately addressable, then we have case 2. A halfword is allocated from ADDR to contain the constant 5076 and the empty halfwords are filled as in Figure 8c. 2 ..3 ..6 Instruction Generation Generation of object code is an area where a balance between high-level convenience and low-level tedium is important. Generalization, if it is not natural, will remove the compiler writer one additional step from the generated code, reducing his control of the process by that much more. Generally, machine instructions are neatly partitioned into classes by format or operand addressability. This grouping will normally form a convenient basis for the instruction generating ICL statements. The location information of the descriptor would be used to specify the operand address. As an example, note that in line 16, Figure 3, the location information in the descriptors pointed to by cells D and E (of the internal coder stack) are used for the operand address fields of the ADD instruction. 45 2..3..6.J. Object .Machine Simulat ion One way in which the code can be optimized is in non-generation of object code if the result of the operation is known at coding- time. For example, in generating code for A = 10, the template may specify the instruction: LOAD A, TEN where A is the descriptor for source variable A and TEN is the descriptor for the source constant 10. Now, rather than generate this instruction, the known value for A is recorded. If a subsequent instruction were ADD A, TEN then again no code is generated and the new value of 20 is recorded for A. To do this, the coder maintains value fields in the SVDs and RSDs. When an ICL statement generates code, the operation can be simulated if the operands 1 values are known. The descriptors are then appropriately modified to indicate the resulting value and its "non-existant" location. If the actual value is needed later, then code will, of course, be generated. But if not, some code has been eliminated. Occasionally, the compiler writer may want to actually generate code even though the result is known. For instance, consider the following PL/I program segment: DECLARE A FIXED BINARY (31); A = 10; CALL F(A) ; When code is generated for the CALL, the value for A is known. If code for a CALL is predicated on the assumption that the value of 46 all arguments will always be in core, it would be easiest if code were actually generated to do this, rather than having the coder try to avoid doing it, only to be reguired to do so later- This is not to say that code should be generated exactly as specified. In these cases, information about the known values can still be used. Consider the ICL instruction which should cause the IBM/360 ADD RX-instr uction to be generated: GBX A,THB,TWO Assume the denotes a value in GPR 8 with a known value of 3 and TWO denotes a known value of 2. The result should be value 5 in GPR 8. But this is eguivalent in execution to LA 8,5(0,0) which has one less memory reference than the ADD instruction. This type of optimization by the coder is included only after a careful study of each instruction of the object machine to see how it couli be better implemented if its operands are known. Thus, it becomes clear that more than one type or level of instruction generation ICL is needed. The compiler writer will use the level which he deems best for the situation. 2 -.3 ..6 ..2 Levels of Instruction Generating ICL The coder has at least three conceptual levels of instruction- emitting ICL statements: 1. Universal, for operations which are common to most computers, such as, ADD, SHIFT LEFT; and which need not necessarily be generated. 47 2« Basic, for operations which are essentially in one to one correspondence with the object machined instruction set- Code is always generated which is equivalent to that specified. 3. Generalized machine, for operations peculiar to the object machined instruction set which are highly useful when further generalized. Of course, further levels of instruction-emitting ICL statements can be designed to handle generic operations, operand conversion to fit the operation, and so forth. But, if sufficiently low-level ICL statements are provided; for instance, being able to determine the operand*s data type; these types of statements can be performed by calls to templates written for the purpose. 2^3^.6 . _ 3 Gene ra lized Object Mach ine Normally, at the time code is generated, the values (bit patterns) making up the instruction are known: - the op-code, - the operand locations (registers or storage locations) , and - any tag fields required by the particular instruction. In attempting to have all ICL operands be descriptors, these fields, with the exception of the op-code, are specified by descriptors. The descriptors specifying machine instruction operands have already been mentioned. However, the miscellaneous tag fields of an instruction are different. Constants must be specified, not the run-time locations where the value will be. Moreover, these constants are restricted in value. For instance, consider the MVC instruction of the 48 IBH/360. It is used to move a byte string froa one location in core to another. Thus, MVC 20(13,2) ,16 (4) moves 13 bytes starting from the address displaced 16 bytes from GPB 4 into the area starting 20 bytes displaced froa GPB 2. The length of the string to be aoved is given in the instruction and cannot exceed 256 bytes. A consideration, is the use of a descriptor for specifying the tag field contents. Recall, that descriptors usually represent program iteas vhose values are not known until run-time. Thus, there are two alternatives: 1. All descriptors used to specify tag fields must always have Jen own values at the time the instruction generating ICL statement is executed by the coder; or 2- Allow run-time values for these tag fields. The first case represents a restriction whereas the second, if feasible, is a generalization of the object machine. For instance in the SVC instruction, by allowing a length greater than 256, the coder presents a machine a little more general than the original one. 2i.3..6._3.J Ta£_Fields_in_the_IBM^360 To show that this generalization of the tag fields is possible, consider where constants are needed in IBM/360 instructions: 1. As a fixed offset in the addressing of operands (BX, BS, SS instruction formats) : this always occurs in conjunction with an index register or a base register or both- Since arithmetic can be i 49 done with these registers, code can be generated to form a new index or base value. Then the object instruction can be generated with a displacement of zero and the new base or index value. 2. As length field indicators for instruction operands (SS instruction format): these fields always occur in bits 8-15, so that the EXECUTE instruction with a specified register can be used here. 3. As immediate operands; ie., for operands which are specified within the instruction itself (SI instruction format and the BC and BCR instructions) . In most of these instructions, there are alternative formats which permit an operand from storage or a register to be used: MVI D1(B1),I2 < > MVC D1 (1,B1) ,D2(B2) CLI D1(B1),I2 < > CLC D1 (1,B1) ,D2(B2) NI D1(B1),I2 < > NC Dl (1,B1) ,D2(B2) 01 D1(B1),I2 < > OC D1 (1,B1) ,D2(B2) XI D1(B1),I2 < > XC D1 <1,B1) ,D2(B2) . In the three remaining instructions: TM, BC, BCR, which reguire a mask, we note that these fields are specified in bit positions 8-15 of the instruction and again the EXECUTE instruction can be used. 2.J..7 Flow of Control Although the basis of the coder is the descriptor system, it would be useless without a control capability. For the coder, this is essential ia order to allow the selection of the "optimal" code sequence which implements an SLM instruction. For a given object machine, control ICL statements must reflect those conditions which will determine how code will be generated. 50 This ranges froa aachine dependence (is the program item in a register?, is it addressable?) to conditions dependent on the prograa item itself (is the value known?) . 51 3. PROGRAMMING IN ICL 3, 1 Codinq-time_Computation It is convenient to have variables around during the coding process and on which arithmetic and logical operations can be performed. This is useful especially if the value of the coding- time variable can then be used as a constant within the object code generated. For instance, in PL/I the bounds of an array are not known until run-time. To allocate storage for the array at that time, tne bounds information is kept in a dope vector, which can be allocated at compile-time. The size of the dope vector depends on the dimensionality of the array and, unless the same sized dope vector is used for all allowable dimension arrays, the amount of space allocated for the dope vector should depend on the dimensionality of the array. This means either distinct ICL sequences for each allowable array dimensionality, or one sequence which has as a parameter, the dimensions of the array. Bather than introduce an entirely new data structure and another set of ICL statements within the coder, the existing descriptor structure is used by considering the coding-time variables as program items with known values. Hence, they can be represented by ordinary SVDs and can be manipulated by the universal level of instruction generating ICL statements. Since the values are known, no code will be generated. 52 3 ..2. lea£late_L.an| COBBENT_IC=IC j IC=IC*instruction length+1 Branch on OP CODE I I I I I F V F F F Call appropriate routine to simulate ICL instruction J I I 1 I 57 I OP_CODE=TEMPLATE(IC) I On error I I F Call compiler writer supplied routine 1 i I F Figure 10- Coder Control Unit 58 NAME | PL/I ATTRIBUTE 1. TEMPLATE_POINTER 2. CURRENT IC 3. IC 4. MAXJTEMPLATE 5. L0CAL_DATA_P0INTEH 6. INTERNAL_STACK_TOP 7. MAX_INTERNAL_STACK 8. XRANSLATORJTOP 9. MAXJTRANSLATOR 10- MAXJ3LOBAL 11- KDC 12. CODE_AREA 13. CDA_AREA 14. TEMP AREA POINTER FIXED BINARY (31) FIXED BINARY (31) FIXED BINARY(31) FIXED BINARY (31) FIXED BINARY(31) FIXED BINARY (31) FIXED BINARY (31) FIXED BINARY (31) FIXED BINARY (31) POINTER POINTER POINTER POINTER REMARKS Template area base Location in the template area of the ICL instruction being interpreted Location of the next ICL instruction to be interpreted Template area size Current activation record in the internal coder stack Current top of the internal coder stack Internal coder stack size Current top of the translator stack Translator stack size Global area size Header for knovn descriptor chain Current code area Current constant data area Variable data area to be used for temporaries Figure 11. (a) Coder Registers and Their Implementation 59 NAME 1. GLOBALS 2. INTEHNAL_STACK 3. TRANSLATOR_STACK 4. KNOMN_CHAIN 5. TEMPLATE | PL/I ATTHIBUTE REMARKS (0:MAX_GLOBAL) POINTER (0:HAX_INTERNAL_STACK) POINTER (0:MAX_TRANSLATOR) POINTER Linked list of BASED structures (0:MAX_TEMPLATE) BIT(16) Global data area Internal coder stack Translator stack Known descriptor chain Template area Figure 11. (b) Coder Data Areas and Their Implementation SAD i IMAGE •-♦- j CORE TABLE >r 1 CORE i — > r -i Location 102<**i 1024* (i*1) -1 Figure 11. (c) Data Structure for Code and Constant Data Areas 60 SAD IMAGE •-♦• j hi, number of bytes in the storage area Block Bit Table L i J Real Offset Figure 11. (d) Data Structure for Variable Data Areas 61 GPH(0:15,2) POINTEB FPE(0: 3,2) POINTER Column 1 points to the current BSD for the register Column 2 points to the SVD or SAD linked to the register (a) Current Register RSDs and Their Contents 1 KDC_NODE BASE(#KDC_NODE) , 2 LINK POINTER INITIAL (NULL) , /* CHAIN FIELD FOR THE KNOWN DESCRIPTOR CHAIN */ 2 DESCRIPTOR POINTER INITIAL (NULL) # /* POINTS TO SVD FOR THE KNOWN VALUE */ 2 COPY POINTER INITIAL (NULL) , /* POINTS TO AN SVD CONTAINING THE SAVED INFORMATION WHEN THIS NODE IS PART OF A RSAD'S KDC */ (b) Node of the Known Descriptor Chain TIMER FIXED BINARY (31) INITIAL (0) Used to indicate the time a register has been FREE*d (c) Register Allocator Clock Figure 12. Internal Coder Hardware 62 A_ ti > ICL/360 Instruction Format There are four different ICL instruction formats (see Figure 13): 1. variable length with a modifier field, 2. variable length without a modifier field, 3. fixed length vith a modifier field, and 4. fixed length without a modifier field. In each of these formats, the fields within the instruction are 16 bits in length. The op~code is an encoded numeric representation of the ICL statement mnemonic. The op-code implicitly specifies the ICL format of the instruction. The length indicates the number of operands specified in the instruction. In the fixed length ICL statements, this field is not present because this information is implicitly contained in the op- code. The modifier field contains 16 bits of additional information used to more explicitly parameterize the ICL instruction. The meaning of each bit in this field depends on the ICL instruction. Each operand field consists of the 4 bit data area specifier and a 12 bit index into the data area discussed in Section 2.3.2. A ..3 Interface Module An interface module is required between the front-end and the coder to 1. Create and initialize descriptors. 63 | Op-code | Length | Modifier ) Operand 1| Operand 2] I I 1 J J 1. J (a) Variable Length with Modifier | Op-code | Length | Operand 1J Operand 2| I (b) Variable Length without Modifier j Op-code | Modifier | Operand 1| Operand 2| I Operand N| _1 I (c) Fixed Length with Modifier | Op-code | Operand 1| Operand 2\ . . | Operand NJ (d) Fixed Length without Modifier | Coder data area (4 bits) | Index specifier (12 bits) I (e) Operand Field Foraat Figure 13. ICL Instruction Formats 64 2. Set up the internal coder stack vith the SLfl operands on a coder call, and to 3, Initialize the coder's registers on a coder call. When the front-end generates SLM instructions, the operands will normally specify, in some manner, symbol table entries. But the coder does not have access to the symbol table, so an interface is required to map the symbol table information for an operand into a descriptor that the coder can manipulate. And since a call to the coder is really a call to a template, we would like to treat this initial call in a way consistent with any other call to a template. This means that for each SLfl operand, a pointer to the descriptor must be placed in the parameter cells of the internal coder stack. Also, the template return point and the saved LOCAL_DATA_POINTEE should be set to -1 to indicate that the coder was called when a return from this template is executed. Finally, the coder registers must be initialized. The IC must be set to the address of the template called by the SLfl instruction. Similarly, the LOCAL_DATA_POINTEB and INTEBNAL_STACK_TOP must also be properly set. Thus, when the coder begins execution with the ICL statement in the location given by the IC, the template is called because the coder has been placed into a state similar to that of a template call. Now as other templates are called and returned, the control unit merely checks to see if the template return point is -1. If it is not, the internal stack is popped and the coder returns to the calling template by restoring IC, INTERNAL_STACK_TOP, and LOCAL_DATA_POINTER. Otherwise, the control unit decrements the USES 65 field of the operands (depending on the SLH instruction) and returns control to the routine which called the coder originally. The interface nodule allows any SLM instruction format the compiler writer may wish to use in the front-end because it maps each SLM instruction into a location of the template area. This does not require extraordinary knowledge on the part of the interface module, since the compiler writer must construct the contents of the template area and the interface module anyway. Like the error handling routine, the interface module must be able to access the coder registers and data areas. For postfix input, this permits the interface module to push operands onto the translator stack without needing to call a PUSH template for each operand. i A t 4 Descriptor Area The descriptors are implemented as PL/I BASED variables so that the storage management tasks associated with the creation and destruction of descriptors is automatically handled by using the PL/I FREE and ALLOCATE statements. Additionally, the use of POINTER variables permits the data structures needed by the coder to indicate the location of the operands and permits the other references to descriptors made by the coder. AjJ> Storage Aj ed Mechanism The coder represents the two types of storage areas by using additional data structures (see Figure 11c, d) . For the initializable storage areas, the core image is maintained in blocks 66 of 1024 bytes. A core table is associated with each such storage area and is used to point to the blocks that make up the storage area. The variable data areas consist of a block-bit table which is used as discussed in Section 2.3.4. A. 6 Functional Description of ICL/36Q In the following subsections, the instructions of ICL/360 are listed and described. Also, the implementation of the interpretive routine for each ICL instruction is sketched in a PL/I-like language. The following notation and utility routines are used: 1. [...J denotes the enclosed entity is optional. 2. [...}i denotes the enclosed entity is to be repeated i-1 times. 3. {•-.} denotes the enclosed entity is to be repeated or more times. 4. (AjB]...|Z) denotes either A or B or ... or Z must be specified. 5. (A:B) denotes either A or A+1 or A+2 or . . . or B must be specified. 6. Variable Di denotes the ith descriptor operand (a cell) of the ICL instruction. It is also used to represent the pointer that is contained in the cell Di, or even the value that is pointed to. From the context, it will be clear which meaning is to be used. 7. Variables P, Q, R, S, T, U are used to denote PL/I POINTER variables. 8. ADD_REFEflENCE (pointer to SVD or SAD) is a procedure to increment the OSES field of the descriptor. 67 9. ALLOCATE_CA (pointer to SAD, 360 data type, number of elements to be allocated, number of bytes per element, OFFSET) is a procedure which allocates storage from a code area and returns the first location through OFFSET. 10. ALLOCATE_CDA (pointer to SAD, 360 data type, number of elements to be allocated, number of bytes per element, OFFSET) is a procedure which allocates storage from a constant data area and returns the first location through OFFSET. 11. ALLOCATE_VDA (pointer to SAD, 360 data type, number of elements to be allocated, number of bytes per element, OFFSET) is a procedure which allocates storage from a variable data area and returns the first location through OFFSET. 12. BDADDH (pointer to SVD or SAD, pointer to SAD, OFFSET) is a procedure which manipulates the access function of the first descriptor and returns its displacement from a SAD. The access function is not changed. 13. CELL (cell, pointer) is a procedure which places the pointer into the cell. 14. CONSTANT (value) is a function which returns a pointer to a SVD which has the value specified. 15. MAKERA (pointer to a SVD) is a procedure which may generate code so that the value of SVD is in a register. 16. MAKERXA (pointer to a SVD) is a procedure which may generate code so that the value of SVD is RX-addressable. 17. MAKERSA (pointer to a SVD) is a procedure which may generate code so that the value of SVD is RS-addressable. 68 18. OPEBAND (cell) is a function which returns the pointer contained in the cell. 19. PUTBB (op-code, operand 1: pointer to SVD, operand 2: pointer to SVD) is a procedure which eaits an BB-instruction. 20. PUTRX (op-code, operand 1: pointer to SVD, operand 2 index: pointer to SVD, operand 2 base: pointer to SVD, operand 2 displacement: pointer to SVD) is a procedure which eaits an RX- instruction. 21. PUTRS (op-code, operand 1: pointer to SVD, operand 3: pointer to SVD, operand 2 base: pointer to SVD, operand 2 displacement: pointer to SVD) is a procedure which emits an RS- instruction. 22. PUTSI (op-code, operand 1: pointer to SVD, operand 2: pointer to SVD) is a procedure which eaits an Si-instruction. 23. PUTSS1 (op-code, length: pointer to SVD, operand 1 base: pointer to SVD, operand 1 displacement: pointer to SVD, operand 2 base: pointer to SVD, operand 2 displacement: pointer to SVD) is a procedure which emits an SS-instruction containing only one length specifier. 24. PUTSS2 (op:code, operand 1 length: pointer to SVD, operand 2 length: pointer to SVD, operand 1 base: pointer to SVD, operand 1 displacement: pointer to SVD, operand 2 base: pointer to SVD, operand 2 displacement: pointer to SVD) is a procedure which emits an SS-instruction containing two length specifiers. 25. REMOVE_REf ERENCE (pointer to descriptor) is a procedure used to decrement the USES field of the descriptor. This routine will be responsible for releasing resources and destroying 69 descriptors when it is possible to do so. 70 1 If a bit is o P' resent: 1 - D2 2 - USES 3 - IMMED 4 - BASE 5 - DISP 6 - INDEX 7 - STORAGE b^b^l CREATE_SVD ASSEHBLEB FORMAT: CHESVD D1 [,D2] [ , USES=integer constant] [ ,IMHED=(0: 1) ] [,BASE=D3] [ # DISP=D4] [,INDEX=D5] [ ,STORAGE=(0: 1) ] [ ,BEGISTER=(0:1) ] [,TEMP=(0:1) ] [ # S360=(0:1) ] [,LEN=D6] [ # KNOtf M= (0: 1) ] [ ,VALUE=((0: 1)} 64] ICL FORMAT: OP-CODE: FORMAT TYPE: MODIFIER: If a bit is on, the indcated option is 8 - REGISTER 9 - TEMP 10 - S360 11 - LEN 12 - KNOWN 13 - VALUE 7 - STORAGE OPERAND 1: D1 Succeeding operands correspond to the next bit of the modifier vhich is on. If VALUE is specified, four operand fields are used, DESCRIPTION: A new SVD is created in the descriptor area and cell D1 points to it. It will be initialized from descriptor D2 and modified by the optional fields. The default length is 4 bytes. The descriptor for DISP must have a known value. IMPLEMENTATION: ALLOCATE S^D IF D2 specified THEN DO P=OPERAND(D2) #SVD->SVD=P->SVD END DO for all optional fields #SVD->SVD.field=optional field value END CALL ADD_REFERENCE ( #SVD->SVD. BASE) CALL ADD_REFERENCE ( #SVD->SVD. DISP) CALL ADD_REFERENCE (#SVD->SVD. INDEX) CALL ADD_REFERENCE ( #SVD->SVD. LENGTH) IF #SVD->SVD. LENGTH=NULL THEN #SVD->SVD. LENGr H=CONST ANT (4) CALL CELL (D1,#SVD) 71 A. 6.2 CLEAR KDC ASSEMBLER FORMAT: CLRKDC [D1 [,Di} J ICL FORMAT: OP-CODE: 1 FORMAT TYPE: 2 OPERANDS: As many as required DESCRIPTION: All the descriptors except those specified are removed from the KDC. Code is generated to put the value into core if not already there. IMPLEMENTATION: DO for every descriptor on KDC IF the descriptor is not an instruction operand THEN DO IF value not in core THEN Generate code to store the value, allocating a temporary if required CALL REMOVE_REFERENCE for the descriptor Remove the descriptor from KDC END END 72 A -_6._3 CHAIN TO KDC ASSEMBLER FORMAT: CHNKDC [D1 {,Di} ] ICL FORMAT: OP-CODE: 2 FORMAT TYPE: 2 OPERANDS: As many as required DESCRIPTION: The descriptors Di are placed on the KDC if not already there. The descriptor must have a known value. IMPLEMENTATION: DO for each Di P=OPERAND(Di) IF value for P->SVD known THEN DO Place P on KDC CALL ADD_REFERENCE(P) END ELSE Error END 73 4.6-4 DEFINE VALUE ASSEMBLER FORMAT: DVAL D1 ICL FORMAT: OP-CODE: 3 FORMAT TYPE: 4 OPERAND 1: D1 OPERAND 2: D2 DESCRIPTION: The current value of the descriptor D2 becomes the value of the descriptor D1. All forward references to the value of D2 are resolved. IMPLEMENTATION: P=0PERAND(D1) Q=OPERAND(D2) P->SVD. VALOE = Q->SVD. VALUE P->SVD.KNOSN=« 1»B DO for every entry on reference chain for P->SVD IF forward reference is for a value THEN DO Handle forward reference Delete entry from forward reference chain CALL REMOVE_REFERENCE(P) END END 7(4 A^sJ DEFINE_LO£AT.ION ASSEMBLES FORMAT: DLOC D1 [ f D2] [,BASE=D3] [,DISP=D4] [ r INDEX=D5] ICL FORMAT: OP-CODE: 4 FORMAT TYPE: 1 MODIFIER: If a bit is on, the indicated option is present: 1 - D2 3 - DISP 2 - BASE 4 - INDEX OPERAND 1: D1 Succeeding operands correspond to the next bit of the modifier which is on. DESCRIPTION: The access function given by descriptor D2, modified by the optional fields, becomes the access function of D1- If a field is not specified, is used. The descriptor used for DISP must have a known value. All location references to D1 are resolved. IMPLEMENTATION: P=OPERAND (D1) CALL REMOVE_REFERENCE(P->SVD.BASE) CALL REMOVE~REFERENCE(P->SVD. DISP) CALL REMOVE~REFERENCE(p->SVD. INDEX) IF D2 specified then P->SVD. ACCESS=OPERAND (D2) ->SVD. ACCESS DO for all optional fields P->SVD.field=optional field value END CALL ADD_REFERENCE (P->SVD. BASE) CALL ADD~REFERENCE (P->SVD.DISP) CALL ADD_REFERENCE (P->SVD. INDEX) DO for each NULL access field of P->SVD Set it to CONSTANT (0) END DO for every entry on reference chain for P->SFD IF forward reference entry is for the location of P->SVD THEN DO Handle forward reference Delete entry from reference chain CALL REMOVE_REFERENCESVD. LENGTH) CALL ADD_REFERENCE(P->SVD. LENGTH) END IF S360 option specified THEN DO CALL REM0VE_REFERENCE{0PERAND{D3) ) CALL CELL(D3,CONSTANT(P->SVD.S360) ) END 76 A. 6. 7 SET LENGTH FI ELD ASSEMBLES FOBMAT: SETLEN D1,D2 ICL FOBMAT: OP-CODE: 6 FOBMAT TYPE: 4 OPEBAND 1: D1 OPEBAND 2: D2 DESCBIPTION: The length field of descriptor D2 points to descriptor D1 only if D2 describes a S360 which can have varying length IMPLEMENTATION: P=0PEBAND(D1) Q=0PEBAND(D2) CALL B EM OVE_BEFEBENCE(Q->SVD. LENGTH) Q->SVD.LENGTH=P CALL ADD_BEFEBENCE{P) 77 A. 6. 8 ADD TO INDEX FIELD ASSEMBLER FORMAT: ADDNDX ICL FORMAT: OP-CODE: FORMAT TYPE: OPERAND 1: OPERAND 2: D1,D2 7 4 D1 D2 DESCRIPTION: If the INDEX field of descriptor D1 is null, descriptor D2 becomes INDEX. Otherwise code is generated, if required, to add the value of D2 to the INDEX value of descriptor D1. IMPLEMENTATION: P=OPERAND(D1) Q=OPERAND{D2) IF P->SVD.INDEX=NULL THEN DO P->SVD.INDEX=Q CALL ADD_REFERENCE(Q) END ELSE DO R=P->SVD. INDEX ALLOCATE SVD IF value for R->SVD and Q->SVD are known THEN DO #SVD->SVD.KNOwN=» 1»B Set #SVD->SVD. VALUE to sum of the values of R and Q END ELSE DO Get a scratch GPR Generate code to place value of R into scratch GPR Generate code to add value of Q into scratch GPR Link scratch register to #SVD->SVD CALL ADD_REFERENCE(#SVD) ♦ SVD-^VD.BEGISTER^ 1 *B END #SVD->SVD.STORAGE= , , B CALL REMOVE REFERENCE(R) P->SVD.INDEX=#SVD CALL ADD_REFERENCE(#SVD) END 78 A ..6 ..9 SET IMMEDIATE ADDRESS FLAG ASSEMBLES FORMAT: SETIMM D1 # (0: 1) ICL FORMAT: OP-CODE: 8 FORMAT TYPE: 4 OPERAND 1: D1 OPERAND 2: Flag value DESCRIPTION: The immediate flag of the descriptor D1 is set to the value indicated. IMPLEMENTATION: P=0PERAND(D1) P->SVD.IMMEDADDR=flag value 79 £.2.65.10 CREATE PRIVATE COPY ASSEMBLER FORMAT: PRIV Dl ICL FORMAT: OP-CODE: 9 FORMAT TYPE: 4 OPERAND 1: D1 DESCRIPTION: Cell Dl is set to point to a private copy of the SVD originally pointed to by Dl. The new descriptor may be modified without affecting the globally known description of the value. The value described by D1 will be protected from alteration if other uses for the value remain. IHPLEMENTATION: P=0PERAND(D1) IF P->SVD.USES is or 1 THEN RETURN ALLOCATE SVD #SVD->SVD.ATTRIBUTES=P->SVD. ATTRIBUTES CALL ADD_REFERENCE (P->SV D. LENGTH) Get a scratch register and set R to point to the RSD Link R->RSD and #SVD->SVD #SVD->SVD. REGISTERS 1»B *SVD->SVD.STORAGE= , , B B->RSD.TIME=2 147 483647 IF value of P->SVD was in a register THEN DO IF P->SVD was not in register R->RSD THEN Generate code to place value of P->SVD into R->RSD R->RSD. REGISTERS 1 • B Set #SVD->SVD. KNOWN Set R->RSD. KNOWN Set R->RSD. VALUE END ELSE DO Generate code to place value of P->SVD into R->RSD R->RSD. REGISTERS 1 ■ B Set #SVD->SVD. KNOWN Set R->RSD. KNOWN Set R->RSD. VALUE END 80 n «_u j__L J. rw xtif £ ^ _ ' ASSEMBLER FORMAT: PTD D1 ICL FORMAT: OP-CODE: 10 FORMAT TYPE: 4 OPERAND 1: D1 OPERAND 2: D2 DESCRIPTION: The cell D2 is set to point to the descriptor D1 IMPLEMENTATION: P=OPERAND (D1) Q=OPERAND(D2) CALL REMOVE_REFERENC£(Q) CALL CELL(D2,P) CALL ADD_REFERENCE(P) 81 A^kiil PUSH_ONTO_INTERNAL_STACK ASSEMBLER FORMAT: PUSHIN integer constant ICL FORMAT: OP-CODE: 11 FORMAT TYPE: 4 OPERAND 1: Number of elements to be pushed DESCRIPTION: The specified number of null entries are pushed onto the internal coder stack.. IMPLEMENTATION: J=INTERNAL_STACK_TOP ♦ number of elements to be pushed DO I=INTERNAL_STACK T0P*1 TO J INTERNAL_STACK*(I)=NULL END INTERNAL STACK TOP=J 82 Aj.6j.13 £USH_ONTO_IBANSIiATOR_SIACK [D1 {,D2} ] ASSEMBLER FORMAT PUSH ICL FORMAT: OP-CODE: FORMAT TYPE: OPERANDS: 12 2 As many as required DESCRIPTION: The indicated descriptors are pushed onto the translator stack. IMPLEMENTATION: DO for each Di P=OPERAND(Di) TRANSLATOR_T0P=TRANSLAT0R_T0P*1 TRANSLATOR_STACK(TRANSLATOR_TOP)=P CALL ADD_REFERENCE(P) END 83 As_6s.li POP FROM TRANSLATOR STACK ASSEMBLER FORMAT: POP D1 ICL FORMAT: OP-CODE: FORMAT TYPE: OPERAND 1: 13 D1 DESCRIPTION: The number of entries equal to the value of descriptor D1 is popped off the translator stack. The descriptors themselves are not explicitly FREE*d, IMPLEMENTATION: P=0PERAND(D1) J=P->SVD. VALUE DO 1=0 TO J-1 CALL REMOVE_REFERENCE(TRANSLATOR_STACK (TRANSLATOR_TOP-I) ) END TRANSLATOR TOP=TRANSLATOB TOP-J du A..6..A5 BRANCH ASSEMBLER FORMAT: B label ICL FORMAT: OP-CODE: 1U FORMAT TYPE: ** OPERAND 1: Template address ^^fnext ICL instruction interpreted will be fro. the location given by the template address. IMPLEMENTATION: IC=template address 85 A..6.J6 BRANCH ON CONDITION ASSEMBLER FORHAT: BC condition code, D1, label ICL FORMAT: OP-CODE: 15 FORHAT TYPE: 3 MODIFIER: If a bit is on, the indicated condition is specified: 1 - is in GPR 2 - is in an even GPR 3 - is in an odd GPR t - is in a GPR pair 5 - is in a FPR 6 - is RX-addressable 7 - is RS-addressable 8 - has a zero value 9 - has a known value 10 - has a known location 11 - AND of above conditions 12 - negation of above conditions OPERAND 1: D1 OPERAND 2: Template address DESCRIPTION: If the descriptor D1 meets all the conditions specified, then the next ICL instruction interpreted will be from the location given by the template address. For each bit 1 to 10 of the modifier which is on, descriptor D1 will either meet the condition (true) or it will not (false). A condition code is formed by ANDing the results of the individually specified conditions if bit 1 1 of the modifier is on, or by ORing them if bit 11 is off. And if bit 12 of the modifier is off, the condition code is inverted. Finally, the branch will occur if the condition code is true. IMPLEMENTATION: P=OPERAND(D1) ANDFLAG=' 1«B ORFLAG=«0»B DO for all specified conditions 1 through 9 IF P->SVD satisfies the condition THEN FLAG=»1«B ELSE FLAG=«0 , B ANDFLAG=ANDFLAG S FLAG ORFLAG=ORFLAG | FLAG END IF bit 11 on THEN CC=ANDFLAG ELSE CC=ORFLAG IF bit 12 on THEN CC=-»CC IF CC on THEN IC=template address 86 ASSEMBLER FORMAT: BAL label {,Di} ICL FORMAT: OP-CODE: 16 FORMAT TYPE: 2 OPERAND 1: Template address Succeeding operands correspond to the argument descriptors Di. DESCRIPTION: The template at the location specified is called with the indicated arguments. IMPLEMENTATION: UNSPEC (INTERNAL_STACK (INTERN AL_STACK_TOP+ 1) ) = UNSPEC (LOCAL_DATA POINTER) UNSPEC (INTERNAL_STACK(INTERNAL_STACK_T0P*2) ) =UNSPEC(IC) L0CAL_DATA_P0INTER=INTERNAL_STACK_T0P*1 IC=template address INTERNAL_STACK_TOP=INTERNAL_STACK_TOP+2 DO for every argument Di P=OPERAND(Di) INTERNAL_STACK_TOP=INTERNAL_STACK_TOP+1 INTERNAL_STACK (INTERNAL_STACK_TOP) =P CALL ADD_REFERENCE(P) END 87 Ai6-J8 RETURN FROM TEMPLATE ASSEMBLER FORMAT: RETURN ICL FORMAT: OP-CODE: 17 FORMAT TYPE: 4 DESCRIPTION: Return from a temp late IMPLEMENTATION: DO I=LOCAL_DATA_POINTER*2 TO INTERNAL_STACK TOP CALL REMOVE_REFERENCE (INTERNAL_STACK (I) ) END I=LOCAL_DATA_POINTER UNSPEC (LOCAL_DATA_POINTER) =UNSPEC (INTERNAL_STACK (I) ) UNSPEC (IC)=UNSP£C(INTERNAL_STACK(I*1) ) INTERNAL_STACKJTOP=I-1 IF IC=-1 THEN Return from the coder 88 A ..6 .J. 9 EXIT_FR0f1_XHE_C0DEB ASSEMBLER FORMAT: EXIT ICL FORMAT: OP-CODE: 18 FORMAT TYPE: a DESCRIPTION: Exit from the coder. Pop all entries from the internal coder stack. IMPLEMENTATION: DO WHILE (M'B) CALL RETURN END 89 4i.6-.20 S0PERVISOB CALL ASSEMBLER FORMAT: SVC integer constant ICL FORMAT: OP-CODE: 19 FORMAT TYPE: 4 OPERAND 1: SVC number DESCRIPTION: Call the compiler writer supplied SVC routine with the SVC number. IHPLEMENTATION: CALL SVC (SVC number) 90 kiA;_ll CREATE_STO£AGE_AHEA _ ASSEMBLER FORMAT: CSA D1,TYPE=(CA | COA | VDA) [ , M AXSIZ E=D2 ] [ ,START=D3] ICL FORMAT: OP-CODE: 20 FORMAT TYPE: 1 MODIFIER: 1 & 2 - 00 denotes a CA 01 denotes a CDA 10 denotes a VDA If the following bits are on, the indicated option is specified: 3 - MAXSIZE 4 - START OPERAND 1: D1 Succeeding operands correspond to the next bit of the modifier which is on. DESCRIPTION: Descriptor D1 is identified to the coder as a storage area base descriptor. The SVD will be reformatted as a SAD. MAXSIZE indicates the size of the storage area. Default size is 2147483647. START is the initial value for the location counter associated with the storage area. It is specified only for a CA or CDA. Default value is 0. Descriptors D2 and D3, if specified, must have known values. IMPLEMENTATION: P=0PERAND(D1) P->SVD.D_TYPE=»01 »B P->SVD.S360=»0101»B IF MAXSIZE specified THEN P->SAD. MAXSIZE=OPER AND (D2) ->SVD. VALUE ELSE P->SAD.MAXSIZE=2147783647 IF START specified THEN P->SAD.LC=OPERAND (D3) ->SVD. VALUE ELSE P->SAD.LC=0 P->SAD.FIRSTBYTE=P->SAD.LC P->SAD.SA_TYPE=type IF CA or CDA THEN DO ALLOCATE CORETABLE Link P->SAD TO CORETABLE #CORETABLE->CORETABLE=NULL END IF VDA THEN DO N=MIN (1024, P->S AD. MAXSIZE) ALLOCATE BLOCK BIT TABLE P->S AD. IMAGE=#BLOCK_BIT_TABLE #BLOCK_BIT_TABLE->BLOCK_BIT_TABLE. N=N #BLOCK_BIT_TABLE->BLOCK_BIT_TABLE.TABLE= , , B END 91 ^6^22 ESTABLISH STORAGE AREA ASSEMBLER FORMAT: ESA D1,TYPE=(CA | CDA | TEMP) ICL FORMAT: OP-CODE: 21 FORMAT TYPE: 3 MODIFIER: 1 8 2 - 00 denotes CA 01 denotes CDA 10 denotes TEMP OPERAND 1: D1 DESCRIPTION: The SAD D1 is now used as the current storage area for (code | constant data J temporary) . IMPLEMENTATION: P=0PERAND(D1) IF CA THEN DO CALL REMOVE_REFERENCE(CODE_AREA) CODE_AREA=P END IF CDA THEN DO CALL REMOVE_REFERENCE(CDA_AREA) CDA_AREA=P END IF TEMP THEN DO CALL REMOVE_REFERENCE(TEMP_AREA) TEMP_AREA=P END CALL ADD_REFERENCE(P) 92 A._b._23 ALLOCATE STORAGE ELEMENT ASSEMBLER FORMAT: ASE D1,D2,D3 ICL FORMAT: OP-CODE: 22 FORMAT TYPE: a OPERAND 1: D1 OPERAND 2: D2 OPERAND 3: D3 DESCRIPTION: The number of storage elements (defined by S360 of descriptor D2) equal to the value of descriptor D3, is allocated from storage area D1. The alignment of the storage elements will be consistent with S360 of descriptor D2. The access fields of D2 are set to represent the address of the first storage element allocated- IMPLEMENTATION: P=0PERAND(D1) Q=OPESAND (D2) R=OPERAND(D3) CALL REMOVE_BEFERENCE(Q->SVD.BASE) CALL REMOVE_REFERENCE(Q->SVD.DISP) CALL REMOVE~REFERENCE(Q->SVD. INDEX) IF P->SAD is a code area THEN CALL ALLOCATE_CA (P,Q->SVD. S360, R->SVD. VALUE, (Q->SVD. LENGTH) ->SVD. VALUE, DISP) IF P->SAD is a constant data area THEN CALL ALLOCATE_CDA (P, Q->SVD.S360,R->SVD. VALUE, (Q->SVD. LENGTH) ->SVD. VALUE, DISP) IF P->SVD is a variable data area THEN CALL ALLOCATE_VDA (P, Q->SVD. S360,R->SVD. VALUE, (Q->SVD. LENGTH) ->SVD. VALUE, DISP) Q->SVD.BASE=P Q->SVD.DISP=CONSTANT (DISP) Q->SVD.INDEX=CONSTANT (0) CALL ADD_REFEfiENCE(P) 93 A-.6 r 2ii FREE .ST0B4GE..ELEHENT ASSEMBLER FORMAT: FSE D1,D2 ICL FORMAT: OP-CODE: 23 FORMAT TYPE: 4 OPERAND 1: D1 OPERAND 2: D2 DESCRIPTION: The number of storage elements (defined by S360 of descriptor D1) equal to the value of D2 is released starting at the location given by descriptor D1. Only storage elements of a variable data area can be FSE»d. Descriptor D1's access fields will be nulled out. IMPLEMENTATION: P=0PERAND(D1) CALL BDADDR(P,T,DISP) Set the appropriate bits in the block-bit-table to M»B R=P IF P->SVD is in register Ri THEN R=pointer to RSD for Ri CALL REMOVE_REFERENCE(R->SVD.BASE) /*WE ARE USING THE FACT */ CALL REMOVE_REFERENCE{R->SVD-DISP) /*THAT THE ACCESS FIELDS */ CALL REMOVE_REFERENCE(R->SVD. INDEX) /*FOR RSD AND SVD OVERLAP*/ R->SVD-ACCESS=NULL P->SVD.STORAGE= , 0«B P->SVD.TEMP=»0'B *4 A 2.62.25 INITIALIZE STORAGE ELEMENT ASSEMBLER FORMAT: ISE D1 ICL FORMAT: OP-CODE: 24 FORMAT TYPE: 4 OPERAND 1 D1 DESCRIPTION: The storage location specified by descriptor D1 is initialized from its value field. Descriptor D1 must already have been allocated storage from either a code area or a constant data area. IMPLEMENTATION: P=0PERAND(D1) B=P IF value of P->SVD is in a register then R=P->SVD. BASE CALL BDADDR(R,T # DISP) /*THE OVERLAP OF THE ACCESS FIELDS */ /♦OF RSD AND SVD HAS BEEN USED */ IF P->SVD does not have a known value THEN DO CALL ADD_REFERENCE(P) Place an entry on the forward reference chain for P->SVD indicating that the value is needed. RETURN END ELSE Initialize the storage location with the value of P->SVD 95 As.6-.26 GET_GENERAL_P0RPOSE_REGISTER D1 [,ODD J ,EVEN] [,PAIR] [ , NZ ] ASSEMBLER FORMAT: GGPR D1 ICL FORMAT: OP-CODE: 25 FORMAT TYPE: 3 MODIFIER: 1 & 2-00 denotes neither ODD nor EVEN option specified 01 denotes ODD option specified 10 denotes EVEN option specified If the following bits are on, the indicated option is specified: 3 - PAIR 4 - NZ OPERAND 1: D1 DESCRIPTION: An [ODD J EVEN] GPR [PAIR] is obtained and linked to descriptor D1. If NZ is specified, GPR is not to be obtained. IHPLEMENTATION: P=0PERAND(D1) TIME=TIMER DO 1=0 TO 15 IF GPR(I,1)->RSD is dedicated THEN GO TO ENDLP IF GPR (I, 1) ->RSD meets the options THEN DO IF GPR (I,1)->RSD free THEN GO TO ALLOCATE IF GPR (1,1) ->RSD.TIME