ILI

Overview

ILIs (intermediate language instructions) are the second internal representation of a source program. Each ILI corresponds to a sequence of SC micro-ops. The ILIs are translated from the ILMs by the Expander and are the representation of the source seen by the optimizer and code scheduler.

ILIs are maintained in a single memory area and are shared for the entire source file. That is, multiple occurrences of an expression will be represented by a single ILI. See the section ILI Structure for a complete description of an ILI.

The ILIs are grouped into basic blocks. An ILM block may produce several ILI blocks. Associated with each ILI block is a block information header (BIH) which defines what labels and variables are referenced in the block and describes certain attributes of the block. See the section BIH Structure for more information.

An ILI block consists of a sequence of ILI terminal nodes (ILTs) which are linked together and represent ILI “statements”. Each ILT consists of previous and next ILT links, a pointer to the ILI tree which represents the ILI statement, and flags which describe the ILI statement (see the section, ILT Structure). The Expander buffers one block of ILT at a time in a dynamic storage area. When the block is completed, it is written out to a temporary file in binary format by the routine wrilts, and will be read by the optimizer and/or code scheduler by the routine rdilts. ILIs are added to the ILI area by the routine srcili.

Static Tables

ILI Attributes

Associated with each ILI opcode (located by the value of the opcode) is information describing the ILI (its attributes). This information is defined symbolically in a file processed by the utility ILITP which creates the data definition files needed to define the information including the above mentioned macros for the ILI opcodes. See APPENDIX V for the current symbolic ILI definitions.

The attributes for a given ILI opcode are defined by the following structure:

typedef struct {
   char \*name;
   char type;
   short oprs;
   long oprflag;
   short attr;
}  ILIINFO;

where,

name

pointer to a null terminated character string for the ILI name. This is needed only for an ILI debug dump.

type

type of the ILI. The allowed values are:

&’a’

arithmetic

&’i’

intrinsic

&’b’

branch

&’l’

load

&’c’

constant

&’s’

store

&’p’

procedure

&’d’

register define

&’m’

register move

&’o’

other

oprs

number of operands (1-4).

oprflag

bit fields defining the type of each operand:

_

op\*<n\*>

op\*<2\*>

op\*<1\*>

_

op\*<i\*> is 4 bit field defining the type of operand i:

ILIO_SYM

symbol table pointer

ILIO_STC

short constant

ILIO_OFF

symbol table pointer to a constant of type DT_CPTR

ILIO_NME

names entry pointer

ILIO_LNK

ILI link (the value is not actually used by the ILI; it is just defining a dependency on that operand)

ILIO_IRLNK

ILI link whose value is type integer register

ILIO_ARLNK

ILI link whose value is type address register

ILIO_SPLNK

ILI link whose value is type single precision register

ILIO_DPLNK

ILI link whose value is type double precision register

ILIO_IR

operand i is an integer register

ILIO_AR

operand i is an address register

ILIO_DP

operand i is a single precision register

ILIO_SP

operand i is a double precision register

attr

bit fields describing other attributes for the ILI

_

res

comm

_

comm

1 bit field denoting if the operands of the ILI are commutative:

0

operands are not commutative

ILIA_COMM

operands are commutative

res

3 bit field defining the type of the result of the ILI:

ILIA_TRM

does not define a result - this ILI is a terminal ILI

ILIA_LNK

the ILI may be pointed to by other ILI but does not produce a result

ILIA_IR

dr result

ILIA_AR

ar result

ILIA_SP

sp result

ILIA_DP

dp result

The attributes for the ILIs are represented by an array of ILIINFO structures called ilis. The array is indexed by the value of an ILI opcode. Extracting information other than the packed bit fields is done by using a construct of the form ilis[opc].member. Macros exist in the file ili.h which provide access to the operand flags and attribute flags. These are:

IL_OPRFLAG(opc, opn)
IL_ISLINK(opc, opn)
IL_COMM(opc)
IL_REG(opc)

where opc is the opcode of the ILI and opn is the operand number.

ILI Scheduling Information

The ILI Scheduling Information is static data created by the ILITP utility when processing ILI template definitions, and used by the Scheduler to schedule ILI.

The information is stored in a single static array, silinfo, which is indexed by ILI opcode number and which contains entries of the following structure:

struct {
   short cycles;
   short rsc_vs;
   short opr_info;
   short r_reads;
   short r_writes;
   short latch;
   short first_rs;
   short rs_avail;
   short ovlap;
   short attrs;
}
cycles

Number of template cycles, including those containing only result definition micro-ops, of this ILI. It (1) is used to compute the depth of an ili when building the dependency graph, (2) defines how many resource vector masks there are for this ili, (3) is used to determine when an address register input to an ili is free for other uses, (4) is used to determine if an ili needs to be written to the Scheduled Ili file.

rsc_vs

Relative pointer into array resources containing the static resource bit masks for this ili (the bit masks are shared by the various ili).

opr_info

Currently not used.

r_reads

Relative pointer into array r_reads containing register read information. r_reads consists of (shared) blocks of records, one record for each data register or double precision read in a template. Each record is a 3-tuple with the following fields:

cyclno

cycle relative to the beginning of the template on which the read occurs (0, 1, …).

iliopr

number (1, 2, …) of ili operand which is being read.

motype

0

default.

1

first multiplier operand.

2

second multiplier operand.

3

operand which is read on more than one cycle and which therefore may not be allowed to latch with input.

r_writes

Relative pointer into array r_writes containing register write information. r_writes consists of (shared) blocks of records, one record for each result definition micro-op (e.g. rs=xxx) in the template. Each record is a tuple containing the following two values:

cyclno

cycle number relative to beginning of the template, on which the write occurs.

src

source of the write: 0 - 7 latch id. 8 - 11 opr1, opr2, … respectively.

latch

For ili whose result is available in a latch this is the latch id (1 - 7), and 0 for other ili.

first_rs

Number of first template cycle which contains a result definition (rs= ) micro-op. This is used to determine on what cycle the result register(s) allocated for this ili must be free and available.

rs_avail

Template cycle of the last result definition micro-op. If the result type of the ili is ar or lnk, this is the number of the first cycle after the end of the template. If the last result definition micro-op is of the form “rs=opn”, this value is incremented by one.

This value is used to (1) compute the first cycle on which to attempt scheduling a link successor of this ili, and (2) determine if a link successor will be able to latch to the result of this ili.

suc_sched

Number of cycles in template. Used to compute the first cycle on which to attempt scheduling a non-link successor of this ili.

ILI Template Definitions

The Template Definitions consist of two arrays which define for each ILI opcode, the micro-ops which make up its template.

The first array, ilitp, is indexed by ILI opcode and contains relative pointers into the second array, tmops.

Figure 13-1 Template Definitions

The array tmops consists of a segment for each ILI template. The first element of a segment is the depth in cycles, d, of the template. Following the depth are d pairs of micro-op numbers which define which micro-ops appear on the consecutive cycles of the template. Note that this implies a maximum of two micro-ops per template cycle, but this limit should not cause a problem since any two micro-ops can always be merged to form a third. If more than two microps are desired for each template line, then the MAXTMOPS define should be set to the desired value for both the code (“benddep.h”) and for the microp and ilitp utilities machine dependent include file.

If one of the cycles only contains a single micro-op, the succeeding microp values are 0.

Result definition micro-ops are not included in the template.

Dynamic Structures

ILI Structure

An ILI has the form:

typedef struct {
   unsigned short opc;
   unsigned short hshlnk;
   unsigned short count;
   unsigned short opnd[];
}  ILI;

where,

opc

value denoting the ILI operation (its opcode)

hshlnk

hash link field which is used for linking together ILIs whose hash values are identical

count

a count of the number of times the ILI is used (used for reclaiming ILI space)

opnd[]

operands of the ILI where the number of operands ranges from 1 to 4 and depends on the ILI opcode. The size of the operand array is statically set to a constant which is sufficient to hold the operands of all ILIs. Note that the ILITP utility will ensure that this size is sufficent.

A pointer to an ILI is just an integer value which is an offset from the beginning of the ILI area, and, this value is represented by an unsigned short int. Since an ILI’s operand field can locate (link to) an ILI, the number of ILIs allowed for a source program is limited to 65535.

ILI opcodes are non-zero positive integers referenced by macros whose names begin with IL\_. These names appear in the file iliatt.h which is produced by the utility, ILITP (see the section ILITP Utility). In the file ili.h, are the macros which are used to access an ILI and a typedef for the ILI structure.

The macros used to access the field <field> of ILI i, where <field> is one of OPC, HSHLNK, or COUNT, are of the form: ILI_<field>(i) The macro used to access the n``th operand of ILI ``i is ILI_OPND(i, n) All of these macros can be used to assign a value to or fetch a value from an ILI field.

Each ILI is hashed into the common ILI area. The hash tables are logically divided into 4 tables where a table is used for the ILI with the same number of operands. Each hash table is of identical size and the hashing function uses the values of the opcodes and operands to compute the hash index. The routine srcili is used to search the ILI area. ILIs whose hash values are identical are linked together using the HSHLNK field.

ILT Structure

An ILT is the terminal node of an ILI statement which roughly corresponds to a source language statement. The ILI statement may be a store, an unconditional or conditional branch, or a procedure call. The ILTs are maintained in a single dynamic memory area. The following external variable is used to represent the ILT area:

struct {
    ILT \*stg_base;          /\* pointer to memory space \*/
    unsigned short stg_size; /\* size in ILT units \*/
    unsigned short stg_avail;        /\* index to the available ILT \*/
} iltb;

The structure of an ILT is:

_

ilip

flags

_

prev

next

_

where,

ilip

ILI pointer to the “tree” of ILIs representing the statement

flags

various flags of the ILT (a value of one indicates that the flag is set).

EX

ILI tree contains an external reference (either a procedure or a function)

ST

ILI is a store

BR

ILI is a branch

prev

pointer to the previous ILT (a value of zero indicates that the ILT is the first in the block

next

pointer to the next ILT (a value of zero indicates that the ILT is the last in the block

An ILI block is doubly linked list of ILTs. A block of ILTs is maintained in a dynamic storage area by the Expander. This dynamic area is divided into consecutive ILT nodes. The free ILT nodes are linked together to manage unused nodes. A pointer to an ILT node is an integer value which is a relative pointer from the area’s base address.

Macros, found in the include file ili.h, provide access to the fields of an ILT. These macros are of the form:

ILT_<field>(i)

access field <field> of ILT i, where <field> is one of the flags ILIP, PREV, or NEXT.

BIH Structure

The block information header for an ILT/ILI block contains a list of all the labels to which control may flow from this block, and a list of the variables and constants referenced in the block. In addition, a BIH contains information such as the line number of the first statement in the block, where the block is filed away, flags, storage management information, and fields used by the optimizer. The information is generated by the Expander (except for the optimizer fields) and used by the optimizer and/or code scheduler.

The BIHs are maintained in a dynamic memory area and a pointer (the block id) to a BIH is a relative pointer from the beginning of the area. The following external variable is used to represent the BIH area:

struct {
    BIH \*stg_base;            /\* pointer to memory space \*/
    unsigned short stg_size;  /\* size in BIH units \*/
    unsigned short stg_avail; /\* index to the available BIH \*/
} bihb;

The structure of a BIH is:

_

label

lineno

_

flags

assn

_

filoff /

iltfst

iltlst

_

prev

next

_

where,

label

symbol table pointer to the label defined by the block (at the beginning). A value of zero indicates no lable is defined.

lineno

source line number of the first statement in the block

flags

various bit flags of the block. A value of one indicates that the flag is true

RD

the block has been read into the ILT area

FT

the block’s control falls through to its immediate successor

EN

the block is an entry to a procedure - label locates the procedure entry symbol

EX

the block references an external. If the block is the entry of the function, this flag is for the entire function.

PL

the block is a pipelinable loop

assn

assigned register and temporary lists (to be completed)

filoff

locates where the block of ILTs is in the ILT file. This field is shared with the fields iltfirst and iltlast.

iltfirst

pointer to the first ILT in the block

iltlast

pointer to the last ILT in the block

prev

pointer to the previous block’s BIH

next

pointer to the next block’s BIH

Macros are provided in file ili.h which allow access to the fields of a BIH. The macros are of the form:

BIH_<field>( blkid )

where blkid is the block id for the BIH.

Names Table

The names table consists of entries which denote the references that have occurred in the ILI. An entry will provide information as to its type (scalar, array, etc.). The entries are located by various expander/optimizer data structures and the load and store ILI’s.

The names table is maintained in a single dynmamic area. The following external variable is used to represent the NME area:

struct {
    NME \*stg_base;          /\* pointer to memory space \*/
    unsigned short stg_size; /\* size in NME units \*/
    unsigned short stg_avail;        /\* index to the available NME \*/
} nmeb;

A names (NME) entry has the form:

typedef struct {
    char type;
    char inlarr;
    unsigned short sym;
    unsigned short nm;
    unsigned short rfptr;
    unsigned short hshlnk;
    unsigned short f6;
    INT cnst;
}  NME;

where,

type

indicates the type of the entry:

NT_UNK

The reference is unknown (i.e., *(f())).

NT_IND

indirection, i.e., *p

NT_VAR

variable, (array, structure, or scalar)

NT_MEM

structure member

NT_ARR

array element

inlarr

Reference is a subscripted reference of an array which has been substituted for a dummy array argument of a function which has been inlined.

hshlnk

hash link field used for linking up names entries whose hash values are identical

f6

field used by the optimizer to record the definitions for a symbol

The meanings of the remaining fields depend on type: .uh indirection

rfptr

back pointer (exact use depends on the expander/optimizer). If no reference exists, this field is zero.

nm

base reference (for \*p, nm is the name entry for p)

sym

0

The reference is simple: \*p, \*(p+c) where c is a constant; then cnst is c (in units of bytes)

65535

The reference is complex: \*(p+i) where i is a variable, \*f(), etc.

.uh variable

rfptr

back pointer (exact use depends on the expander/optimizer). If no reference exists, this field is zero.

nm

0

sym

symbol table pointer .uh member

rfptr

back pointer (exact use depends on the expander/optimizer). If no reference exists, this field is zero.

nm

parent of member reference (this is a pointer to a NME item): for s.x, this locates the NME for s.

sym

symbol table pointer to the member .uh array

rfptr

back pointer (exact use depends on the expander/optimizer). If no reference exists, this field is zero.

nm

NME item for the array: for a[i], this locates the NME for a.

sym

0

the reference has constant subscripts; then cnst = constant offset in byte units

65535

the reference has variable subscripts.

Examples: The notation <type, rfptr, sym, nm, cnst> represents a NME item. '--' indicates that the field is not used. 'x' indicates a ST item for x. '(i)' indicates the i-th NME entry.

struct {
    struct {
     int             y, z;
    }               x;
    int             a[10];
    struct {
     int             c;
    }               b;
}               s, \*p;
(1)  <  NT_VAR , -- , 's' ,   0 , -- >       "s"
(2)  <  NT_MEM , -- , 'x' , (1) , -- >       "s.x"
(3)  <  NT_MEM , -- , 'y' , (2) , -- >       "s.x.y"
(4)  <  NT_MEM , -- , 'z' , (2) , -- >       "s.x.z"
(5)  <  NT_MEM , -- , 'a' , (1) , -- >       "s.a"
(6)  <  NT_ARR , -- ,   0 , (5) ,  1 >       "s.a[1]"
(7)  <  NT_ARR , -- ,  -1 , (5) , -- >       "s.a[i]"
(8)  <  NT_VAR , -- , 'p' ,   0 , -- >       "p"
(9)  <  NT_IND , -- ,   0 , (8) ,  0 >       "\*p"
(10) <  NT_MEM , -- , 'b' , (9) , -- >       "p->b"
(11) <  NT_MEM , -- , 'c' , (10), -- >       "p->b.c"

Macros exist in the file ili.h which allow access to the fields and are of the form:

NME_<field>(item)

Processing

ILI Processing

The ILIs (intermediate language instructions) are maintained in a single dynamic memory area and are shared for the entire source program. The following external structure variable is used to locate the memory area, give the size of the area, and to indicate where the available area begins :

struct {
    ILI \*stg_base;   /\* pointer to memory space \*/
    int  stg_size;   /\* size in units of ILIs   \*/
    int  stg_avail;  /\* index to the available ILI in the memory
                      \* space \*/
}        ilib;

Multiple occurrences of an expression will be represented by a single ILI. Although the number of operands depend on the ILI, each ILI entry in the ILI area consists of a fixed size. ILI pointers are just integers (greater than zero) which are offsets from the beginning of the ILI area.

The operands of an ILI may be pointers to other ILI, immediate values, pointers (index values) to names entries, and symbol table pointers. For an ILI whose operands are commutative (the operands are two ILI pointers), the ILI pointers are ordered such that the index of operand 1 is less than or equal to the index of operand 2. This provides an easy mechanism to ensure that expressions such as a \* b and b \* a are represented by the same ILI. If one of the operands is a pointer to a constant ILI, the second operand will always locate the constant ILI. Note that this is an exception to the rule that the index value of the first operand is not greater than the index value of the second operand.

The ILI module contains two levels of ILI processing. At the lower level is the routine srcili. At the higher level is the routine addili which is called by the various expand and optimizer routines whenever an ILI needs to be added. srcili is called by addili when the ILI is to be entered into the ILI area.

srcili uses a hashing mechanism based on the ILI’s opcode and operands. Figure 13-1 gives a description of the srcili algorithm. Note that for a new entry, its count is set to 0.

val = HASH(ili);         /\* compute hash value \*/
for (p=ILI in the hash chain for val)
   if (p==ili) return(p);

/\*  create new entry in the ILI area  \*/

p = new ILI;
copy fields of ili to p;
ILI_COUNT(p) = 0;
return(p);

Figure 13-1. srcili Algorithm

The ILI module is entered through the routine addili. ILIs to be added to the ILI area are first handled by addili. Other routines are provided which call addili given a specific ILI and its operands to be added. addili processes the ILI according to its type and performs certain optimizations such as constant folding and strength reduction. For those cases discussed previously, addili re-arranges operands. Depending on the complexity involved, separate routines are called by addili (although they are logically a part of addili) to handle certain cases. Figure 13-2 gives an overview of addili.

given ilip (a pointer to the ILI to be added);
opc = ilip->opc;
select processing depending on type of ILI {

case ilip is arithmetic:
   return addarth(ilip);
case ilip is constant, load, store, procedure, define:
   return srcili(ilip);
case ilip is move:
   process move ILI;
case ilip is branch:
   return addbran(ilip);
case ilip is other:
   return srcili(ilip);

}

Figure 13-2. addili

Optimizations are performed on the ILI (mainly on the arithmetics) and include both machine independent and dependent optimizations. Briefly, the optimizations are:

  • constant folding

  • identities

    i \* 1  \(->  i
    i / 1  \(->  i
    i + 0  \(->  i
    
  • integer and address association

    i - c          \(->  i + (-c),  c is a constant
    (i + c1) + c2  \(->  i + (c1 + c2),  c1 and c2 are constants
    (c1 - i) + c2  \(->  (c1 + c2) - i
    
  • comparison with zero

    i :: 0  \(->      uses ILI which does not require a memory
                      reference of the constant 0
    i + c :: 0  \(->  i :: c, where c is a constant
    
  • branch optimizations - see section entitled Branch Optimizations.

Move Processing

The move ILI are divided into two types:

  1. The move ILI whose link is type IR, DP, SP, or AR to a data register, double precision register, or address register, respectively. These ILIs (MVIR, MVDP, MVSP, and MVAR) are terminal ILIs and also specify the destination register.

  2. The move ILIs which convert type AR to IR or IR to AR (AIMV or IAMV, respectively). These ILIs are non-terminal ILIs and do not specify a register.

The terminal move ILIs are simply added to the ILI area by srcili unless the register specified is (mi1. If this occurs, the ILI linked to by the move ILI is returned. This is provided for certain expansions which need to simply pass up the result of another ILI.

The convert register ILIs may be constant folded. That is, if the value (ILI) being converted is a constant, the appropriate constant and constant ILI are created. Also, the following identities may be performed:

AIMV  j  \(->  i    if j = IAMV  i
IAMV  j  \(->  i    if j = IAMV  i

Branch Optimizations

Certain sequences of ILIs are optimized which will allow better code to be produced. The optimizations include changing a compare ILI followed by a branch ILI to a single ILI which combines the compare and branch operations, and “constant folding” branches (changing a compare and branch to an unconditional branch).

These optimizations center around the ICJMPZ ILI and LCJMPZ ILI. Depending on the language, the ILMs BRT and BRF expand to one of these ILI. For C, the notion of true and false is integer non-zero and zero, respectively; therefore, the ICJMPZ would be used (ICJMPZ means comparing its operand to zero and branching if the specified condition is true). For Fortran, the notion of logical true and false is odd and even, respectively; LCJMPZ means checking the low bit of its operand and branching if the specified condition is true).

The ICJMPZ ILI is of the form:

ICJMPZ drlnk cond sym

where,

drlnk

is a pointer to an integer ILI

cond

is a condition value: 1 = equal 2 = not equal 3 = less than 4 = greater than or equal 5 = less than or equal 6 = greater than

sym

is a symbol table pointer to the label.

The ILI LCJMPZ is of the form:

LCJMPZ drlnk cond sym

where,

drlnk

is a pointer to an ILI producing a logical result

cond

is a condition value: 1 = equal (branch if false) 2 = not equal (branch if true)

sym

is a symbol table pointer to the label.

For conditional branching, the semantic analyzer outputs a sequence of ILMs corresponding to:

(1) compare op1 op2 (this ILM is based on data type)
(2) relop (1)
(2) branch true (or false) (1) label

For example, comparing two integer values and branching to label L in C if they are equal results in the following sequence of two ILMs:

(1) ICMP op1 op2
(2) EQ (1)
(3) BRT (1) L

These ILMs, before any optimizations occur, expand to the following ILI:

(1) ICMP op1 op2 eq
(2) ICJMPZ (1) ne L

The desired ILI in this example is:

(1) ICJMP op1 op2 eq L

Optimizing the ILI in the ICJMPZ context involves looking at the operand of ICJMPZ and creating new ILI depending on what it is. If the ILI is a compare, a compare and branch ILI is created according to the following table:

operand

created ILI

ACMP[Z]

(-> ICJMPZ (->

ACJMP[Z] *

ICMP[Z]

^

ICJMP[Z]

FCMP[Z]

^

FCJMP[Z]

DCMP[Z]

^

DCJMP[Z]

*

ACJMP becomes ACJMPEQ if the condition is equals

NOTE

The (optional) Z indicates that the comparison is with zero.

If the ILI is a constant, then either an unconditional branch is generated or no branch is required.

Additional optimizations performed on the compare and branch ILIs (non-Z variety) are:

  • If the second operand is a constant of value zero, the ILI becomes the corresponding Z variety whose link operand is the first operand of the original ILI and whose condition and and sym fields are copied from the original ILI.

  • If the first operand is a constant of value zero, the ILI becomes the corresponding Z variety whose link operand is the second operand of the original ILI. The comparison is reversed if the condition is not 1 (equal) or 2 (not equal) (i.e., less than becomes greater than, etc.) For example,

    (1) ICON '0'
    (2) ICJMP (1) i ge L
    

    becomes

    (1) ICJMPZ i le L
    

For Fortran, the BRT/BRF ILMs expand to the LCJMPZ ILI. This ILI is optimized according to the following, recursive, rules:

  1. if the operand is any compare ILI, the LCJMPZ becomes an ICJMPZ and the ICJMPZ is optimized as above.

  2. if the operand is a NOT ILI, the not is deleted and the condition in the LCJMPZ is complemented. The operand of the NOT becomes the operand of the new LCJMPZ.

  3. otherwise, the low bit of the operand’s value needs to be checked. The LCJMPZ is passed through.

Program Units

The C module file iliutil.c contains the following routines:

int addili (ilip)
    ILI \*ilip;
  • Main add ili routine; returns the index where the ILI, described by ilip, was added.

int addarth (ilip)
    ILI \*ilip;
  • Adds the arithmetic ili. Performs the operand switching if the ILI has commutative operands. Performs various arithmetic optimizations including constant folding. The index to the ILI added is returned.

int red_iadd (ilix, con)
    int ilix;
    INT con;
  • Adds the integer add ILI (IADD) of the form ilip+con. This routine recursively searches ilip for opportunities to fold in con, e.g.,

    (i + 2) + j + 4  -->  (i + 6) + j
    
int red_aadd (ilix, con)
    int ilix;
    INT con[2];
  • Adds the address add ILI (AADD) of the form ilip+con. This routine recursively searches ilip for opportunities to fold in the address constant con.

int addbran (ilip)
    ILI \*ilip;
  • Adds the branch ili. This routine checks for the various branch optimizations.

INT icmp(val1, val2)
    INT val1, val2;
  • Compares two INT values and returns:

    (mi1

    val1 < val2

    0

    val1 = val2

    1

    val1 > val2

int ad1ili(opc, op1 )
    int opc, op1;
  • Adds an ILI with one operand. opc is the opcode of the ILI and op1 its operand.

int ad2ili(opc, op1, op2)
    int opc, op1, op2;
  • Adds an ILI with two operands.

int ad3ili(opc, op1, op2, op3)
    int opc, op1, op2, op3;
  • Adds an ILI with three operands.

int ad4ili(opc, op1, op2, op3, op4)
    int opc, op1, op2, op3, op4;
  • Adds an ILI with four operands.

int ad_icon(val)
    INT val;
  • Adds the ICON ili for the integer constant whose value is val.

int ad_aconi(val)
  • Adds the ACON ili for an integer constant instead of an address constant.

int srcili(ilip)
    ILI \*ilip;
  • Enters the ILI into the ILI area. If it already exists, the index is returned. If it is a new entry, its fields are set, and its index is returned.

void dmpili()
  • For compiler debugging purposes, writes to the debug file the ILI hash table and/or the ILI area. This dump is controlled by vale of debug flag 10.

The following routines are contained in the C module file, bihutil.c^:

bih_init()
  • Initializes the bih area. This involves creating a list of free nodes in the area beginning from its available index to the end of the storage (its storage size). The available index and storage size are set when it is allocated or reallocated.

int addbih(bihx)
    int bihx;
  • Creates a new bih, initializes its fields, and links it in after bihx. The index to the new bih is returned.

int chk_terminal_func(entbihx, exitbihx)
    int entbihx, exitbihx;
  • Determines if the function’s linkage can be changed in order to speed it up. The function is defined by the entry bih index, entbihx, and the exit bih index, exitbihx. The conditions and their corresponding actions are:

    1. The function does not make any function calls (the function is a terminal function). No entry and exit routines are needed. A special static array (.STACK) is used to contain any automatic data required by the function. The appropriate ILI replace the ENTRY and EXIT ILI which will load and restore the stack pointer.

    2. The function does not require any save frame space in the stack header (globar registers are not used and the exception register is not modified). The routines referenced in the ENTRY and EXIT ILI are replaced with c$i_qentry and c$i_qexit, respectively. These routines do not check the functions’s PED to determine if the AR and EXCSTAT fields are used.

The C module file iltutil.c contains the following routines:

ilt_init()
  • Initializes the ILT area. This involves creating a list of free ILT nodes in the available area. This area begins at the available index and ends at the value denoted by storage size.

int addilt(iltx, ilix)
    int iltx, ilix;
  • Creates a new ilt for the ili ilix. The ilt is inserted after iltx and its index is returned.

int reduce_ilt(iltx)
    int iltx;
  • Recursively searches the ili of the ilt located by iltx for any function ILIs. When one is found, a new ilt is created for it. Returns the index to the last ilt created.

wrilts(bihx)
    int bihx;
  • Writes out an ilt/ili block given its bih.

rdilts(bihx)
    int bihx;
  • Reads in an ilt/ili block given its bih.

dmpilt(bihx)
    int bihx;
  • For debugging purposes, the ilt/ili block is written out to the debug file. This dump is controlled by the value in debug flag 10.

The C module file nmeutil.c contains the following routines:

int addnme(type, sym, nm, cnst)
    int type, sym, nm;
    INT cnst;
  • Main add NME routine.

void dmpnme()
  • For compiler debugging purposes, writes to the debug file the names table area. This dump is controlled by debug flag 10.

ILITP Utility

NOTE: This section needs to be revised!!

ILITP reads the file which symbolically defines the ILI opcodes, their attributes, and their micro-op templates. It also reads a file of micro-op information created by the MICROP utility. ILITP writes a number of files, containing C macros and data definitions for the ILI attributes, templates, etc. ILITP must be run when an ILI is deleted or added, or when its attributes or templates are modified.

Inputs

The first file read by ILITP is the file of micro-op information created by MICROP. This information is stored up in a table indexed by micro-op number for later use when processing the ILI template definitions.

The main input to ILITP is the ILI definition file, which is the first file specified on the command line. For an ILI, there are three types of lines processed:

  1. The .IL line defines the name, the number of operands, and operand types of the ILI. The ILI definition line has the form:

    .IL <name> [ <opr>i ] ...
    

    where,

    <name>

    is the name of the ILI

    <opr>i

    is the type of the ith operand and is one of:

    sym

    operand locates a symbol table node

    off

    operand locates an address constant (a symbol table node)

    nme

    operand locates a names table entry

    lnk

    operand locates an ILI

    irlnk

    operand locates a IR ILI

    splnk

    operand locates a SP ILI

    dplnk

    operand locates a DP ILI

    arlnk

    operand locates a AR ILI

    stc

    operand is a short constant (an immediate value)

    dr

    operand is data register

    sp

    operand is a single precision register

    dp

    operand is a double precision register

    ar

    operand is an address register

    x87

    operand is an x87 floating-point stack register

  2. The .AT line defines the type of the ILI, the register type of the ILI, and whether or not the ILI operands are commutative. The attribute line is of the form:

    .AT <type> [ <comm> [ <res> ] ]
    

    where,

    <type>

    is the type of the ILI: “arth”, “intr”, “branch”, “load”, “cons”, “store”, “proc”, “define”, “move”, or “other”.

    <comm>

    is the commutative flag for the operands: “null” (operands are not commutative) or “comm” (operands are commutative).

    <res>

    is the result type of the ILI: “null” or “trm” (no result and a terminal ILI), “lnk” (the ILI has no result but can be linked to), “dr” (data register), “ar” (address register), “dp” (double precision register), or “x87” (x87 floating-point stack register).

  3. The .TP line defines the micro-operations for an instruction word. There may be zero or more .TP lines required for an ILI. The .TP line is of the form: .. code-block:: none

    .TP <micro-ops> …

    At most two micro-ops can appear on a single line. The text of a micro-op must match exactly one of the micro-ops defined in the Micro-op Definition File (input to MICROP).

Outputs

Four output files are written by ILITP: iliatdf.h, iliatt.h, ilidf.h, and ilitpdf.h.

Summary

Include Files

iliatdf.h

Contains the C initializations for the ILI attributes produced by ILITP

ilitpdf.h

Contains the C initializations for the ILI templates produced by ILITP

iliatt.h

Contains the macros which define the C symbol names for the ILI and their corresponding opcode values. This file is produced by ILITP.

ili.h

Contains the declarations for the ILI data structures, ILI attributes, and the ILT data structures. The necessary external data declarations are in this file including the storage management structures for the ILI and ILT areas. Macros defined in the file provide access to the ILI and ILT fields, and provide the values of the various fields. Also, this file contains the declarations for the BIH data structure and storage management structure. Macros reside in the file which provide access to the fields of these structures.

Macros

IL_

ILI attributes and names

ILIO_

ILI operand types

ILIA_

ILI attribute flag values

ILI_

ILI structure

ILT_

ILT structure

BIH_

BIH structure