AAA RRReee---UUUsssaaabbbllleee CCCooodddeee GGGeeennneeerrraaatttooorrr fffooorrr PPPrrriiimmmeee 555000---SSSeeerrriiieeesss CCCooommmpppuuuttteeerrrsss UUUssseeerrr'''sss GGGuuuiiidddeee T. Allen Akin School of Information and Computer Science Georgia Institute of Technology Atlanta, Georgia 30332 March, 1983 ___FFF___ooo___rrr___eee___www___ooo___rrr___ddd Although the School of Information and Computer Science has operated Prime 400 and 550 computers for over four years, as yet there has been no successful local attempt to produce a compiler for them. The main reasons for this failure are the irregularity of the architecture and existing system software, the complexity of Prime's standard object code format, and the lack of documentation on matters of importance to compiler writers. This paper discusses the design, implementation, and usage of a re-usable code generator. This program can serve as a com- mon "back-end" for a number of language translators, producing 64V-mode assembly language code suitable for execution on the P400 and higher numbered processors in Prime's "50" series. Furthermore, it could be tailored to match specific front-ends, when needs for special optimizations arise. A preliminary version of the code generator is available for general use. ___HHH___ooo___www___ ___ttt___ooo___ ___UUU___sss___eee___ ___TTT___hhh___iii___sss___ ___GGG___uuu___iii___ddd___eee The first chapter of this GGGuuuiiidddeee is the _O_v_e_r_v_i_e_w. The _O_v_e_r_v_i_e_w is a brief summary of the design and construction of the code generator. This chapter may be of general interest, but it is not necessary to read it in order to learn to use the code generator. The _C_o_d_e _G_e_n_e_r_a_t_o_r _U_s_a_g_e chapter describes the location of the code generator and its associated run-time support libraries, as well as the Software Tools Subsystem commands necessary to access them. Recommended procedure is to study this section, then generate command language programs to do the low-level file access operations. _I_n_p_u_t _D_a_t_a _S_t_r_e_a_m _F_o_r_m_a_t_s gives a bird's-eye view of the formats of the three code generator input streams. This chapter merits some study, although it is supplemented by the _E_x_t_e_n_d_e_d _E_x_a_m_p_l_e_s. The three operator definitions chapters (_O_p_e_r_a_t_o_r_s _U_s_e_f_u_l _i_n _t_h_e _S_t_a_t_i_c _D_a_t_a _S_t_r_e_a_m, _O_p_e_r_a_t_o_r_s _U_s_e_f_u_l _i_n _t_h_e _P_r_o_c_e_d_u_r_e _D_e_f_i_n_i_t_i_o_n _S_t_r_e_a_m, _O_p_e_r_a_t_o_r_s _U_s_e_f_u_l _i_n _P_r_o_c_e_d_u_r_e _D_e_f_i_n_i_t_i_o_n_s) provide a detailed reference for the intermediate form operators interpreted by the code generator. One or two readings through this chapter are desirable; thereafter, it can be used as a reference with the _O_p_e_r_a_t_o_r/_F_u_n_c_t_i_o_n _I_n_d_e_x and the Table of Contents used as entry points. The _E_x_t_e_n_d_e_d _E_x_a_m_p_l_e_s are comprised of several short (but complete) programs written in the language C. These examples include the original C code, annotated versions of the three code generator input streams, and an annotated listing of the code generator's assembly language output. The chapter should be useful in learning how the various intermediate form operators work together, and may be used as a reference when building a new front end. 'Drift' is a very small expression-based language whose structure closely mimics the code generator's internal world- model. _T_h_e _'_D_r_i_f_t_' _C_o_m_p_i_l_e_r is a complete, working compiler using the code generator as a back-end. It serves as an example of one way to construct a front-end for the VCG. For ease of reference, all the intermediate form operators have been organized by subject in the _I_n_t_e_r_m_e_d_i_a_t_e _F_o_r_m _O_p_e_r_a_t_o_r/_F_u_n_c_t_i_o_n _I_n_d_e_x. Typically, one would look up some func- tion (e.g., "subscripting") in the _I_n_d_e_x, find the name of the appropriate intermediate form operator (e.g., INDEX_OP), then look up that operator in the table of contents to find its com- plete description. - 1 - ___OOO___vvv___eee___rrr___vvv___iii___eee___www PPPhhhiiilllooosssoooppphhhyyy DDDeeesssiiigggnnn CCCooonnnsssiiidddeeerrraaatttiiiooonnnsss The design of the code generator (hereinafter referred to as VCG, for "V-mode Code Generator") was driven by a number of considerations: ... For experimental language translators, code generation should be fast and straightforward. This is necessary both for fast turnaround and ease of debugging in the development stage, and for fast turnaround in typical educational applications. ... The VCG should insulate front-ends from details of storage allocation and data format selection, as well as instruction generation. This encourages inter-language compatibility at the object code level, as well as providing a framework for easily retargetable front-ends. ... The intermediate form (IMF) that is processed by the VCG should be simple to generate and display (for debugging purposes). Furthermore, it should not unduly restrict extension for additional functionality or optimization. ... The output object code should conform to Prime's current standards, and should include at least minimal provisions for separate compilation and run-time debugging. IIImmmpppllleeemmmeeennntttaaatttiiiooonnn AAApppppprrroooaaaccchhheeesss After some time, consideration of the goals above led to the following approaches to the implementation of the VCG: ... The basic IMF handled by both the front end and the VCG should be a tree structure. A tree is easily generated from the information available on the semantic stack during a bottom-up parse, and can be generated directly without an explicit stack during a top-down parse. A number of operations like constant folding, reordering of operands of commutative operators, and global context propagation are readily performed on a tree structure. Furthermore, use of a tree can eliminate the need for generation and tracking of temporary variables in the front end. ... The IMF operators should be close to the constructs used in an algorithmic language of the level of, say, Pascal. This permits straightforward translation of most algorithmic - 2 - languages, and provides enough additional context to sim- plify many optimization tasks. For example, the IMF resem- bles the program's flow graph closely enough that simple global register allocation can be performed without graph reduction. ... One of the basic functions of the VCG is the mapping of data descriptions supplied by the front end into physical storage layouts. The goal of complete machine data structure independence in the front end cannot be met without com- promising the code generator's utility for languages that allow storage layout specification (C and Ada are notable examples). Therefore, the IMF should contain descriptions of data structures in terms of a small set of primitive data modes that can easily be parameterized in front-end code. Simple variables, structures, and arrays defined in the front end must be converted to single or multiple instances of the following basic machine data modes: 16-bit signed integer, 16-bit unsigned integer, 32-bit signed integer, 32- bit unsigned integer, 32-bit floating point, and 64-bit floating point. ... The IMF tree should be linearized and passed to the VCG as a stream of data in prefix Polish notation. The linearized form partly reflects the usual Software Tools methodology of expressing even complex data transformations as "filters." However, there are other advantages, particularly in storing and interpreting the IMF for debugging. Prefix Polish was chosen because it can be generated easily from the internal representation of the tree, and because it minimizes the amount of state information that must be explicitly maintained by both the front end and the VCG in order to output or input the IMF. ... The final output of the VCG should be a stream of Prime Macro Assembly Language source code. Although the time required to assemble this source imposes a significant penalty on code generator performance, it appears to be unavoidable if the compiler writer is to be insulated from Prime's object code format. (In addition, Prime has scheduled object code format changes, and it would not be wise to invest heavily in the present format.) SSStttrrruuuccctttuuurrreee The VCG "main loop" simply reads each module present on its input, rebuilds the tree represented by the input, transforms the tree to a linked list of machine instructions, performs register tracking optimizations on that list, and finally converts the list to assembly language and outputs it. The input and output routines are straightforward and relatively uninteresting. - 3 - The optimization routines amount to about 13 pages of Ratfor code, and work by simulating the effect of each machine instruc- tion on the contents of the six registers that are tracked. At the moment, three types of optimization are performed: redundant loads are eliminated, some memory references are eliminated in favor of register-to-register transfers, and general instruction sequences are replaced with special-case code. The heart of the code generator is the set of transformation routines that convert the tree representation to the doubly- linked list of machine instructions. The transformation routines exhibit a great deal of knowledge about the machine architecture, but actually employ only very simple algorithms for code generation. IMF operators may appear in one of several "contexts," identified internally by the following terms: RRReeeaaaccchhh. An operator evaluated in reach context yields the address of a word in memory containing the result of the operation, if possible. At present, only the object, constant, pointer dereferencing, array indexing, and struc- ture member selection operators yield addresses. All other operators behave as if they were evaluated in "load" context. LLLoooaaaddd. An operator evaluated in load context yields a value in a machine register. The particular register used depends only on the basic machine data mode of the operation. Most IMF operators are evaluated only in this context. VVVoooiiiddd. An operator evaluated in void context yields side effects only. In a very few cases, this results in an opportunity to exploit special-case machine instructions that perform some calculation without making the result available in a register (incrementing a memory location, for example). FFFlllooowww. An operator evaluated in flow context yields a change in flow-of-control rather than a value. For example, a "test for equality" operator would return 1 or 0 in a load context, but in flow context would cause a jump to a given label depending on the outcome of the test. AAAPPP. An operator evaluated in AP context yields an "argument pointer" rather than a value. Argument pointers are used to pass parameters to procedures. Context information is propagated top-down by the code generator as it scans the IMF tree. Additional information in the form of register requirements is propagated from the bottom up during the same scan. Together, context and register usage determine with fair accuracy the optimal code sequence to be generated for a given operator. - 4 - IIInnnpppuuuttt///OOOuuutttpppuuuttt SSSeeemmmaaannntttiiicccsss IIInnnpppuuuttt SSStttrrruuuccctttuuurrreee The IMF passed to the VCG consists of a sequence of _m_o_d_u_l_e_s. A module is a sequence of procedure definitions, static data definitions, and entry point declarations. The static data definitions build a data area that is shared by all procedures in the module, while the procedure definitions build code and data areas that are strictly local to each procedure, and the entry point declarations make the static data area or procedures visible to Prime's link editor. Prime's Fortran compilers currently generate code that is equivalent to one procedure per module under this scheme; Prime's PL/I and Pascal compilers generate code that is equivalent to a single IMF module. The VCG module structure permits com- patibility with either of these alternatives, as well as com- promise forms that are more suitable for other languages. Note: Separate compilation capability directly affects module structure. At present, there is no way for separately compiled procedures to share a static data area. Furthermore, separately-compiled static objects must be referenced by a unique 8 or fewer character name made visible to the loader. A Fortran COMMON block definition can be used to reduce the number of such external symbols, but COMMON definitions must match exactly in all separately-maintained modules. In addition, note that Prime's current loader software requires that external objects be referenced through an indirect address, which can cause a significant reduc- tion in performance. Each _s_t_a_t_i_c_ _d_a_t_a_ _d_e_f_i_n_i_t_i_o_n allocates space for an object and may specify an initial value for the object. A _s_t_a_t_i_c_ _d_a_t_a_ _d_e_c_l_a_r_a_t_i_o_n names an object that is defined outside the current module, but provides no other information about the object. Each _p_r_o_c_e_d_u_r_e_ _d_e_f_i_n_i_t_i_o_n consists of information associated with a closed routine defined by the front end. In particular, the procedure's argument types and code tree are included. The bulk of the IMF will be in subtrees defining the code associated with procedures. Most storage allocators, arithmetic operators, and flow controllers are straightforwardly expressed in tree form; a description of these IMF components is available elsewhere. OOOuuutttpppuuuttt SSStttrrruuuccctttuuurrreee Each VCG input module generates a single PMA input module, terminated by an END pseudo-op. The PMA input module may be - 5 - assembled, link-edited, and subsequently executed. The concatenation of all static data definitions and declarations forms a _l_i_n_k_ _f_r_a_m_e that is shared by all procedures in the module. Each procedure definition yields an entry control block (ECB) and a chunk of machine code that implements the function of the procedure, including the allocation of space in the procedure's _s_t_a_c_k _f_r_a_m_e for local variables. - 6 - ___CCC___ooo___ddd___eee___ ___GGG___eee___nnn___eee___rrr___aaa___ttt___ooo___rrr___ ___UUU___sss___aaa___ggg___eee The code generator currently resides in the file =bin=/vcg. The three input streams can be read from the three standard inputs, or from three files (if a standard naming convention is used). The PMA output stream is produced on standard output 1, and should be redirected to a file for assembly. Assume temporary files will be used for communications between the front end and the code generator. The temporary files must have names of the form "xxx.ct1" (for IMF stream 1), "xxx.ct2" (for IMF stream 2), and "xxx.ct3" (for IMF stream 3), where "xxx" is completely arbitrary but must be the same for all of the three temporary files in a given run. When the code generator is invoked, the string "xxx" must be passed to it as a command line argument. To use the code generator, first run the front end to produce the temporary files: front_end Say, for example, this produces files "temp.ct1", "temp.ct2", and "temp.ct3". Next, run the code generator and produce the assem- bly language output: vcg temp >temp.s Run the assembler to convert the PMA source to relocatable binary code: pmac temp.s Finally, run the link editor to load the VCG main program, the binary code for your program, and all required library routines: ld =lib=/vcg_main temp.b =lib=/vcglib -o program This produces an object program (in the file "program") which may be executed simply by typing its name: program All run-time support routines called by the output of the code generator are available in the library =lib=/vcglib. The stub main program in =lib=/vcg_main calls a procedure named MAIN; therefore, the user's main program must be named MAIN. (This is the usual case in C environments.) One miscellaneous note: if the front end is being written in Ratfor, the complete set of macro definitions for the intermediate form operators can be obtained by simply including - 7 - the file "=incl=/vcg_defs.r.i". If the front end is being writ- ten in Pascal, the complete set of constant definitions for the intermediate form operators can be obtained by including the file "=incl=/vcg_defs.p.i". - 8 - ___III___nnn___ppp___uuu___ttt___ ___DDD___aaa___ttt___aaa___ ___SSS___ttt___rrr___eee___aaa___mmm___ ___FFF___ooo___rrr___mmm___aaa___ttt___sss This section describes the formats of the three code generator input streams. Note that all three have the same basic format: _ 32 MODULE_OP | _ | 59 SEQ_OP | Repeat for | Repeat for each ... Item of information _| each item | module | 39 NULL_OP _| 39 NULL_OP Stream termination Detailed examples of the code generator input can be found in the "Extended Examples" section of this guide. SSStttrrreeeaaammm 111 --------- EEEnnntttrrryyy PPPoooiiinnnttt DDDeeeccclllaaarrraaatttiiiooonnnsss The first intermediate form stream consists of one or more _m_o_d_u_l_e_s. Each module consists of a MODULE_OP, a list of _e_n_t_r_y _p_o_i_n_t _d_e_c_l_a_r_a_t_i_o_n_s separated by SEQ_OPs, and a NULL_OP terminat- ing the list of entry point declarations. The list of modules is terminated by a final NULL_OP. Each entry point declaration is an object identification number followed by a character string, expressed as the length of the string followed by the ASCII character codes for the charac- ters in the string. Each such string is assumed to be the name of a location defined in the current input module, and is made available to the link editor for resolving references made by other modules. A template for stream 1 would look something like this: _ 32 MODULE_OP | _ | 59 SEQ_OP | Repeat for each | Repeat for each Entry object id | entry point | module ... Entry point name _| | | 39 NULL_OP _| 39 NULL_OP Terminate stream - 9 - SSStttrrreeeaaammm 222 --------- SSStttaaatttiiiccc DDDaaatttaaa DDDeeeccclllaaarrraaatttiiiooonnnsss///DDDeeefffiiinnniiitttiiiooonnnsss In C terminology, a data "definition" reserves storage space for an object and possibly initializes that space, whereas a data "declaration" simply indicates that the storage space for an object resides outside the current module. The second intermediate form input stream defines or declares static data (objects that are not automatically allocated on the stack when a procedure is entered). The input stream consists of a series of _m_o_d_u_l_e_s, terminated by a NULL_OP. Each module contains a sequence of _D_E_F_I_N_E__S_T_A_T__O_Ps and _D_E_C_L_A_R_E__S_T_A_T__O_Ps, terminated by a NULL_OP. A template for the static data stream would look something like this: _ 32 MODULE_OP | _ | 59 SEQ_OP | Repeat for | Repeat for 14/11 DEFINE/DECLARE_STAT_OP | each defn/decl | each module ... with associated info _| | | 39 NULL_OP _| 39 NULL_OP Terminate stream SSStttrrreeeaaammm 333 --------- PPPrrroooccceeeddduuurrreee DDDeeefffiiinnniiitttiiiooonnnsss The third intermediate form input stream consists of one or more _m_o_d_u_l_e_s, terminated by a NULL_OP. Each module contains a list of _P_R_O_C__D_E_F_N__O_Ps, separated by SEQ_OPs and terminated with a NULL_OP. Each PROC_DEFN_OP causes a procedure to be defined and code for it to be generated. A template for stream 3 would look something like this: _ 32 MODULE_OP | _ | 59 SEQ_OP | Repeat for | Repeat for 50 PROC_DEFN_OP | each procedure | each module ... with associated info _| | | 39 NULL_OP _| 39 NULL_OP Terminate stream - 10 - ___PPP___rrr___iii___mmm___iii___ttt___iii___vvv___eee___ ___DDD___aaa___ttt___aaa___ ___MMM___ooo___ddd___eee___sss The following primitive data modes are presently handled by the code generator: IIINNNTTT___MMMOOODDDEEE 111 Integer objects are one 16-bit word in size. They have integral values in the range (-2**15) to (2**15 - 1), inclusive. LLLOOONNNGGG___IIINNNTTT___MMMOOODDDEEE 222 Long integer objects are two 16-bit words in size. They have integral values in the range (-2**31) to (2**31 - 1), inclusive. UUUNNNSSS___MMMOOODDDEEE 333 Unsigned objects are nominally one 16-bit word in size. They have integral values in the range 0 to (2**16 - 1). Bit fields (see FIELD_OP) can be of mode UNSIGNED, and may range from 1 bit to 16 bits in length (with consequent change in the range of values they can represent). LLLOOONNNGGG___UUUNNNSSS___MMMOOODDDEEE 444 Long unsigned objects are nominally two 16-bit words in size. They have integral values in the range 0 to (2**32 - 1). Machine addresses (pointers) are represented as long unsigned quantities. Bit fields (see FIELD_OP) can be of mode LONG UNSIGNED, and may range from 1 bit to 32 bits in length (with consequent change in the range of values they can represent). FFFLLLOOOAAATTT___MMMOOODDDEEE 555 Floating point objects are two 16-bit words in size. LLLOOONNNGGG FFFLLLOOOAAATTT___MMMOOODDDEEE 666 Long floating point objects are four 16-bit words in size. - 11 - SSSTTTOOOWWWEEEDDD___MMMOOODDDEEE 777 STOWED mode is the mode assigned to structured objects like arrays and structs (Pascal "records"). STOWED objects may be any size from 1 to 65536 16-bit words; IMF operators that need to know the size of a STOWED object invariably have a "length" or "size" parameter to carry that information. - 12 - ___OOO___ppp___eee___rrr___aaa___ttt___ooo___rrr___sss___ ___UUU___sss___eee___fff___uuu___lll___ ___iii___nnn___ ___ttt___hhh___eee___ ___SSS___ttt___aaa___ttt___iii___ccc___ ___DDD___aaa___ttt___aaa___ ___SSS___ttt___rrr___eee___aaa___mmm DDDEEECCCLLLAAARRREEE___SSSTTTAAATTT___OOOPPP 111111 int 11 int object_id string external_name DECLARE_STAT informs the code generator that an object defined outside the current module will be referenced by a given integer object id. The parameter 'external_name' is a character string, represented in the IMF by a length followed by a stream of ASCII characters (one per word, right justified, zero filled). The external name is used by the link editor and the loader to resolve actual references to the object. Example: extern int abc where 'abc' is assigned the object id 6 11 DECLARE_STAT_OP 6 Object id of 'abc' 3 Length of name 'abc' 225 character 'a' 226 character 'b' 227 character 'c' DDDEEEFFFIIINNNEEE___SSSTTTAAATTT___OOOPPP 111444 int 14 int object_id tree init_list int size This operator causes storage for the object identified by 'object_id' to be allocated in the current link frame (static data area). 'Object_id' must be used in all subsequent references to the object, and the object's definition with DEFINE_STAT must precede all such references. The init_list is a list of initializers whose values will be assigned to successive portions of the newly-declared object. The size parameter specifies the amount of storage to be reserved for the object, in words. (Slightly fewer than 65,535 words are available for static storage in each module.) Example: static int abc[100] where abc is assigned the object id 6 14 DEFINE_STAT_OP 6 Object id for 'abc' 39 NULL_OP (no initializers present) - 13 - 100 Object is 100 words long - 14 - ___OOO___ppp___eee___rrr___aaa___ttt___ooo___rrr___sss___ ___UUU___sss___eee___fff___uuu___lll___ ___iii___nnn___ ___ttt___hhh___eee___ ___PPP___rrr___ooo___ccc___eee___ddd___uuu___rrr___eee___ ___DDD___eee___fff___iii___nnn___iii___ttt___iii___ooo___nnn___ ___SSS___ttt___rrr___eee___aaa___mmm PPPRRROOOCCC___DDDEEEFFFNNN___OOOPPP 555000 int 50 int object_id int number_of_args string proc_name tree argument_list tree code Each procedure to be generated by the code generator is defined by a PROC_DEFN_OP. The 'object_id' is an integer identifier that must be used on calls to the procedure and other references to its entry control block (for example, pointers to functions as used in C). 'Number_of_args' should be self-explanatory. 'Proc_name' is a string (in the IMF, a length followed by ASCII character values) giving the internal name of the procedure. (This information is used to print trace information during debugging.) Each formal parameter (argument) is described by a PROC_DEFN_ARG_OP; 'argument_list' is simply a linked list of those descriptions. 'Code' is a subtree containing the body of the procedure: local variable definitions and expressions to be evaluated. Example: the following C function main (argc, argv) int argc; char **argv; { int i; i = 4; } 50 PROC_DEFN_OP 1 Procedure is object number 1 2 Procedure has 2 arguments 4 Procedure name is 4 characters long 237 m 225 a 233 i 238 n 49 PROC_DEFN_ARG_OP 2 Argument is object number 2 1 INT_MODE 0 VAL_DISP; pass argument by value 1 Argument is 1 word long 49 PROC_DEFN_ARG_OP 3 Argument is object number 3 4 LONG_UNS_MODE (a pointer) 1 REF_DISP; pass argument by reference 2 Argument is 2 words long - 15 - 39 NULL_OP; end of argument description list 59 SEQ_OP; beginning of procedure code 13 DEFINE_DYNM_OP 4 Object id is 4 39 NULL_OP; no initializers 1 Object is 1 word long 59 SEQ_OP; procedure code continues 5 ASSIGN_OP 1 INT_MODE 40 OBJECT_OP 1 INT_MODE 4 Object id is 4 9 CONST_OP 1 INT_MODE 1 Constant is 1 word long 4 Constant has value 4 1 Assignment transfers 1 word 39 NULL_OP; end of procedure code PPPRRROOOCCC___DDDEEEFFFNNN___AAARRRGGG___OOOPPP 444999 int 49 int object_id int mode int disposition int length tree next_argument Formal parameters to procedures are described by this operator. The 'object_id' is an integer identifier that must be supplied on subsequent references to the parameter (see OBJECT_OP). The 'mode' is the machine data type of the parameter. 'Disposition' indicates how the argument is to be treated on the call; the two alternatives at the moment are 0 (VALUE_DISP) for pass-by-value (aka copy in) and 1 (REF_DISP) for pass-by-reference. 'Length' gives the size of the argument in 16-bit words; it is primarily necessary for handling of STOWED arguments that are passed by value. 'Next_argument' is simply a link to the next PROC_DEFN_ARG_OP in a procedure's argument descriptor list, or a NULL_OP. See PROC_DEFN_OP for examples of PROC_DEFN_ARG_OP. - 16 - ___OOO___ppp___eee___rrr___aaa___ttt___ooo___rrr___sss___ ___UUU___sss___eee___fff___uuu___lll___ ___iii___nnn___ ___PPP___rrr___ooo___ccc___eee___ddd___uuu___rrr___eee___ ___DDD___eee___fff___iii___nnn___iii___ttt___iii___ooo___nnn___sss AAADDDDDDAAAAAA___OOOPPP 111 int 1 int mode tree left tree right The result of this operator is an rvalue, the sum of the values of the left and right operands. As a side effect, the sum is stored back into the left operand. The left operand must be an lvalue or a bit field (see FIELD_OP). Both operands must have the same mode as the ADDAA operation. The operation mode may not be STOWED. ADDAA stands for "add and assign." This operator is normally used to implement the addition assignment operator ("+=" in C, "+:=" in Algol 68). Example: i += 1 (where i is an integer object with object id 12) 1 ADDAA_OP 1 INT_MODE 40 OBJECT_OP 1 INT_MODE 12 Object id 12 9 CONST_OP 1 INT_MODE 1 length is 1 word 1 value of first word is 1 AAADDDDDD___OOOPPP 222 int 2 int mode tree left tree right The result of this operator is an rvalue, the sum of the values of the left and right operands. Both operands must have the same mode as the ADD, and STOWED mode is not allowed. ADD is used to implement simple addition of fixed or floating point values. Example: i + 1 (where i is an integer object with object id 12) 2 ADD_OP 1 INT_MODE 40 OBJECT_OP 1 INT_MODE - 17 - 12 Object id 12 9 CONST_OP 1 INT_MODE 1 length is 1 word 1 value of first word is 1 AAANNNDDDAAAAAA___OOOPPP 333 int 3 int mode tree left tree right The result of this operator is an rvalue, the bitwise logical "and" of the values of the left and right operands. As a side effect, the conjunction is stored back into the left operand. The left operand must be an lvalue or a bit field (see FIELD_OP). Both operands must have the same mode as the ANDAA operation; the only allowable modes are INT, UNSIGNED, LONG INT, and LONG UNSIGNED. ANDAA stands for "'and' and assign." ANDAA_OP is used to implement the logical-and assignment operator ("&=" in C). Example: i &= 1 (where i is an integer object with object id 12) 3 ANDAA_OP 1 INT_MODE 40 OBJECT_OP 1 INT_MODE 12 Object id 12 9 CONST_OP 1 INT_MODE 1 length is 1 word 1 value of first word is 1 AAANNNDDD___OOOPPP 444 int 4 int mode tree left tree right The result of this operator is an rvalue, the bitwise logical- "and" of the values of the left and right operands. Both operands must have the same mode as the AND operation; the only allowable modes are INT, LONG INT, UNSIGNED, and LONG UNSIGNED. AND_OP is normally used to implement the bitwise logical conjunc- tion of integers ("&" in C). Although AND_OP can be used to implement conjunction in Boolean expressions, the short-circuit - 18 - conjunction operator (SAND_OP) is probably a better choice, since it guarantees evaluation order and prevents undesirable side effects. Example: i & 1 (where i is an integer object with object id 12) 4 AND_OP 1 INT_MODE 40 OBJECT_OP 1 INT_MODE 12 Object id 12 9 CONST_OP 1 INT_MODE 1 length is 1 word 1 value of first word is 1 AAASSSSSSIIIGGGNNN___OOOPPP 555 int 5 int mode tree left tree right int length The result of this operator is an rvalue, namely the value of the right operand. As a side effect, the result is stored into the left operand. The left operand must be an lvalue or a bit field (see FIELD_OP). Both operands must have the same mode as the ASSIGN operation. Any mode is allowable, but the parameter 'length' must be set to the operand length, in 16-bit words. ASSIGN implements the semantics of assignment statements in most algorithmic languages. Note that STOWED mode values are allowed, so things like Pascal record assignment can be handled. Example: i = 1 (where i is anDDDDAAAAAA___OOOPPP 111 int 1 int mode tree left tree right The result of this operator is an rvalue, the sum of the values of the left and right operands. As a side effect, the sum is stored back into the left operand. The left operand must be an lvalue or a bit field (see FIELD_OP). Both operands must have the same mode as the ADDAA operation. The operation mode may not be STOWED. ADDAA stands for "add and assign." This operator is normally used to implement the addition assignment operator ("+=" in C, "+:=" in Algol 68). Example: i += 1 (where i is an integer object with object id 12) 1 ADDAA_OP 1 INT_MODE 40 OBJECT_OP 1 INT_MODE 12 Object id 12 9 CONST_OP 1 INT_MODE 1 length is 1 word 1 value of first word is 1 AAADDDDDD___OOOPPP 222 int 2 int mode tree left tree right The result of this operator is an rvalue, the sum of the values of the left and right operands. Both operands must have the same mode as the ADD, and STOWED mode is not allowed. ADD is used to implement simple addition of fixed or floating point values. Example: i + 1 (where i is an integer object with object id 12) 2 ADD_OP 1 INT_MODE 40 OBJECT_OP 1 INT_MODE - 17 - 12 Object id 12 9 CONST_OP 1 INT_MODE 1 length is 1 word 1 value of first word is 1 AAANNNDDDAAAAAA___OOOPPP 333 int 3 int mode tree left tree right The result of this operator is an rvalue, the bitwise logical "and" of the values of the left and right operands. As a side effect, the conjunction is stored back into the left operand. The left operand must be an lvalue or a bit field (see FIELD_OP). Both operands must have the same mode as the ANDAA operation; the only allowable modes are INT, UNSIGNED, LONG INT, and LONG UNSIGNED. ANDAA stands for "'and' and assign." ANDAA_OP is used to implement the logical-and assignment operator ("&=" in C). Example: i &= 1 (where i is an integer object with object id 12) 3 ANDAA_OP 1 INT_MODE 40 OBJECT_OP 1 INT_MODE 12 Object id 12 9 CONST_OP 1 INT_MODE 1 length is 1 word 1 value of first word is 1 AAANNNDDD___OOOPPP 444 int 4 int mode tree left tree right The result of this operator is an rvalue, the bitwise logical- "and" of the values of the left and right operands. Both operands must have the same mode as the AND operation; the only allowable modes are INT, LONG INT, UNSIGNED, and LONG UNSIGNED. AND_OP is normally used to implement the bitwise logical conjunc- tion of integers ("&" in C). Although AND_OP can be used to implement conjunction in Boolean expressions, the short-circuit - 18 - conjunction operator (SAND_OP) is probably a better choice, since it guarantees evaluation order and prevents undesirable side effects. Example: i & 1 (where i is an integer object with object id 12) 4 AND_OP 1 INT_MODE 40 OBJECT_OP 1 INT_MODE 12 Object id 12 9 CONST_OP 1 INT_MODE 1 length is 1 word 1 value of first word is 1 AAASSSSSSIIIGGGNNN___OOOPPP 555 int 5 int mode tree left tree right int length The result of this operator is an rvalue, namely the value of the right operand. As a side effect, the result is stored into the left operand. The left operand must be an lvalue or a bit field (see FIELD_OP). Both operands must have the same mode as the ASSIGN operation. Any mode is allowable, but the parameter 'length' must be set to the operand length, in 16-bit words. ASSIGN implements the semantics of assignment statements in most algorithmic languages. Note that STOWED mode values are allowed, so things like Pascal record assignment can be handled. Example: i = 1 (where i is an