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 ILTi
, where<field>
is one of the flagsILIP
,PREV
, orNEXT
.
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 forp
)
sym
- 0
The reference is simple:
\*p
,\*(p+c)
wherec
is a constant; then cnst isc
(in units of bytes)- 65535
The reference is complex:
\*(p+i)
wherei
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 fors
.- 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 fora
.
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:
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.
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:
if the operand is any compare ILI, the LCJMPZ becomes an ICJMPZ and the ICJMPZ is optimized as above.
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.
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 searchesilip
for opportunities to fold incon
, 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 searchesilip
for opportunities to fold in the address constantcon
.
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 andop1
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: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.
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
andc$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 afteriltx
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:
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
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).
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