| CSDL Overview | RTLs | Lambda-RTL | Basic RTL Operator Library | SPARC Description | Pentium excerpts |
This chapter describes RTL operators and functions that we expect to be useful for describing many different machines. As we do throughout this document, we use the noweb literate-programming tool [cite ramsey:simplified] to present examples of lambda-RTL. Noweb extracts the code from the same files used to produce this document, so we can run the examples through the lambda-RTL compiler and make sure everything compiles.
A noweb file contains explanatory text interleaved with named ``code chunks,'' which contain source code and references to other code chunks. The names of chunks appear italicized and in angle brackets, as in the following C code:
<summarize the problem>= [D->]
printf("Inputs are: ");
<for every i that is an input, print i and a blank>
printf("\n");
<for everyithat is an input, printiand a blank>= (<-U) for (i = 1; i < argc; i++) printf("%s ", argv[i]);
The === sign indicates the definition of a chunk. Definitions of a chunk can be continued in a later chunk; noweb concatenates their contents. Such a concatenation is indicated by a +=== sign in the definition:
<summarize the problem>+= [<-D]
printf("Must process %d files\n", argc-1);
noweb adds navigational aids to the document. Each chunk name ends with the number of the page on which the chunk's definition begins. When more than one definition appears on a page, they are distinguished by appending lower-case letters to the page number. When a chunk's definition is continued, noweb includes pointers to the previous and next definitions, written ``<-D'' and ``D->.'' The notation ``<-U'' shows where a chunk is used.
The layout of the module is as follows:
<basic.rtl>= module StdOperators is import RTL <definitions of standard RTL operators and functions> end
Because lambda-RTL does not yet have modules, we use noweb to
include the chunk <definitions of standard RTL operators and functions> in our descriptions of the SPARC
and the Pentium.
lambda-RTL is intended for building RTLs, not analying them, so it
doesn't expose all the structure of RTLs;
single effects, guarded effects, and full RTLs (lists of guarded
effects) all have the same type rtl.
The initial basis provides ways to create and
combine effects.
We define a more compact, infix notation for parts of the basis.
<definitions of standard RTL operators and functions>= (<-U) [D->] val do_nothing is RTL.SKIP : rtl val --> is RTL.GUARD : bool * rtl -> rtl val := is RTL.STORE -- type is #n loc * #n bits -> rtl infixr 1 --> infixn 2 :=
Defines-->,:=,do_nothing(links are to index).
In the underlying representation, the semantics of these operations
is extended to full RTLs in the obvious way.
For example, RTL.STORE produces an RTL containing a single effect
whose guard is true.
We don't need an explicit FETCH, because fetches are inserted
automatically, and we don't need an explicit CONST, because lambda-RTL
compiles literal constants into RTL CONST nodes.
We use a vertical bar to combine simultaneous effects, and a semicolon for sequential composition. One day, the lambda-RTL translator will translate sequential composition to simultaneous composition, using forward substitution.
<definitions of standard RTL operators and functions>+= (<-U) [<-D->] val | is RTL.PAR : rtl * rtl -> rtl val ; is RTL.SEQ : rtl * rtl -> rtl infixl 0 | infixl ~1 ;
true, false, and RTL.NOT are the Boolean operators in
lambda-RTL's initial basis.
We prefer to use
not to stand for Boolean negation.
<definitions of standard RTL operators and functions>+= (<-U) [<-D->] val not is RTL.NOT
Definesnot(links are to index).
For writing other Boolean functions
lambda-RTL has only if-then-else-fi, which lambda-RTL rewrites
into suitable guards.
We would like to have connectives andalso and orelse, but
introducing them presents a choice about level of detail:
should they be introduced as new RTL operators, whose semantics must
be defined outside of lambda-RTL, or should they be defined in terms of the
existing lambda-RTL primitives?
We would like to try both alternatives, because they provide different
levels of detail in the generated RTLs.
Eventually, lambda-RTL will have mechanisms to support multiple levels of abstraction, probably by using some sort of parameterized modules. For now, we resort to some literate-programming tricks that are equivalent to conditional compilation. noweb can extract this code in two versions, designated simple and full. Code chunks tagged ((simple)) and ((full)) appear only in the corresponding versions, and untagged code chunks appear in both versions.
In the simple version, the boolean connectives are left abstract. In the full version, we give them their standard definitions. The simple version has the advantage that the RTLs are smaller and easier to read, but the disadvantage that the semantics have to be specified elsewhere. In the full version, these connectives are expanded to primitives, so they automatically get their proper semantics.
<definitions of standard RTL operators and functions ((simple))>= [D->] rtlop [andalso orelse] : bool * bool -> bool
Definesandalso,orelse(links are to index).
<definitions of standard RTL operators and functions ((full))>= [D->] fun andalso (p, q) is if p then q else false fi fun orelse (p, q) is if p then true else q fi
Definesandalso,orelse(links are to index).
No matter what their definitions, we want these connectives to be left-associative infix operators.
<definitions of standard RTL operators and functions>+= (<-U) [<-D->] infixl 3 orelse infixl 4 andalso
In the long run, equality and inequality will probably have to have some built-in semantics, but for now, we leave them abstract.
<definitions of standard RTL operators and functions>+= (<-U) [<-D->] rtlop [eq ne] : #n bits * #n bits -> bool val [= <>] is [eq ne] infixn 5 [= <>]
Defines<>,eq,ne(links are to index).
These operators are shown in TeX as = and !=.
We have chosen to make inequality a primitive RTL operator, although
we could have defined it in terms of equality by writing
val <> is \(x,y). not (x = y).
We can convert between booleans and bits. Again we have the choice of making things abstract or concrete.
<definitions of standard RTL operators and functions ((simple))>+= [<-D->] rtlop bit : bool -> #1 bits -- turn boolean into bit rtlop bool : #1 bits -> bool -- turn bit into boolean
Definesbit,bool(links are to index).
<definitions of standard RTL operators and functions ((full))>+= [<-D->] fun bit p is if p then 1 else 0 fi fun bool n is n <> 0
Definesbit,bool(links are to index).
<definitions of standard RTL operators and functions>+= (<-U) [<-D->] local rtlop [add sub] : #n bits * #n bits -> #n bits in val [+ -] is [add sub] end rtlop [carry borrow] : #n bits * #n bits * #1 bits -> #1 bits rtlop neg : #n bits -> #n bits -- two's-complement negation
Defines+,-,add,borrow,carry,neg,sub(links are to index).
Note that this definition hides the names add and sub, using
+ and - instead.
To define combined versions add and subtract, we need sign
extension and zero extension.
These operators are polymorphic in the sizes of the arguments
and results.[*]
<definitions of standard RTL operators and functions>+= (<-U) [<-D->] rtlop [sx zx] : #n bits -> #m bits -- must have m > n
Definessx,zx(links are to index).
Now we can define add and subtract to combine result and carry/borrow.
<definitions of standard RTL operators and functions>+= (<-U) [<-D->]
infixl 6 [+ -]
fun add (n, m, c) is { result is n + m + zx c, carry is carry (n, m, c) }
fun subtract (n, m, b) is { result is n - m - zx b, borrow is borrow (n, m, b) }
Definesadd,subtract(links are to index).
The intended semantics of add and subtract is ``add with
carry'' and ``subtract with borrow.''
Integer multiplication and division may be signed or unsigned.
There are two sets of signed division operators because
division may truncate towards -infinity (divs and mods) or
towards 0 (quots and rems).
There is, of course, no such distinction for unsigned division.
<definitions of standard RTL operators and functions>+= (<-U) [<-D->]
local
rtlop [mul mulUn] : #n bits * #n bits -> #m bits -- m = 2 * n
rtlop [divRz divRn divUn modRz modRn modUn] : #m bits * #n bits -> #n bits -- m = 2 * n
in
val [mulu muls divu divs quot modu mods rem] is
[mulUn mul divUn divRn divRz modUn modRn modRz]
end
DefinesdivRn,divRz,divs,divu,divUn,modRn,modRz,mods,modu,modUn,mul,muls,mulu,mulUn,quot,rem(links are to index).
The lambda-RTL type system is unable to express the
constraints on the types of multiplication and division.
We could play some games with the definitions (e.g., return two words
and merge them with bitInsert), but it's not worth it at this
time.
This code highlights the unpleasant question of naming RTL
operators.
As of July 1999, the names used in rtlop declarations are
consistent with the names used in the VPOI code-generation interfaces.
These names are, however, re-bound to other names, which are used in
the actual machine descriptions.
Eventually, it is hoped that VPO, lambda-RTL, C--, and the
Princeton proof-carrying code project will agree on a set of names for
operators.
Two's-complement signed integer comparison.
<definitions of standard RTL operators and functions>+= (<-U) [<-D->] local rtlop [lt le gt ge] : #n bits * #n bits -> bool in val [< <= > >=] is [lt le gt ge] end
Defines<,<=,>,>=,ge,gt,le,lt(links are to index).
Comparisons are left abstract instead of being defined in terms
of < and =.
<definitions of standard RTL operators and functions>+= (<-U) [<-D->] local rtlop [ltUn leUn gtUn geUn] : #n bits * #n bits -> bool in val [ult ule ugt uge] is [ltUn leUn gtUn geUn] end
DefinesgeUn,gtUn,leUn,ltUn,uge,ugt,ule,ult(links are to index).
The arithmetic operators obey the usual rules of precedence.
<definitions of standard RTL operators and functions>+= (<-U) [<-D->] infixl 7 [divs mods quots rems divu modu quotu remu] infixl 6 [+ -] infixn 5 [< <= > >=]
<definitions of standard RTL operators and functions>+= (<-U) [<-D->]
rtlop [and or xor] : #n bits * #n bits -> #n bits
rtlop com : #n bits -> #n bits -- `complement' because `not' is boolean
rtlop bitInsert : {lsb : #k bits, wide : #w bits} * #n bits -> #w bits
val bitInsert is \x.\y.bitInsert (x, y) -- curry
Definesand,bitInsert,com,or,xor(links are to index).
bitInsert inserts into a
w-bit (``wide'') value.
The thing inserted or extracted is n bits (``narrow''), and
the widths of both wide and narrow values are normally inferred by the
type system.
We don't need a corresponding bitExtract because we can use
lambda-RTL's built-in slicing operator instead.
There are instructions that manipulate bit fields whose widths aren't
known statically.
These computations can't be expressed with bitInsert and slicing,
because we can't assign a type (width) to the results.
But we can define bitTransfer as a combination extraction and
insertion, and in fact, this is the way such instructions must behave.
Even an instruction that extracts k bits from a 32-bit word, where
k is not known until run time, must still insert the result into
some value of known size---typically a 32-bit word of all zeros.
<definitions of standard RTL operators and functions>+= (<-U) [<-D->]
rtlop bitTransfer :
{ src : {value : #w bits, lsb : #k bits}
, dst : {value : #w bits, lsb : #k bits}
, width : #k bits
} -> #w bits
-- extract width bits from src at lsb, insert into dst at lsb
DefinesbitTransfer(links are to index).
The width and lsb specifiers are assumed to have the same
type.
Most processors have shift instructions. We could make the shift operations primitive RTL operators, or we could define them in terms of bit transfer. If shift is specified in terms of bit transfer, the width of the value shifted must be specified. This means for compatibility, we must specify the width for the primitive versions also. This situation is very unsatisfying.
<definitions of standard RTL operators and functions ((simple))>+= [<-D] local rtlop [lshift rshift rshiftA] : #k bits * #n bits * #k bits -> #n bits in val [shl shrl shra] is [lshift rshift rshiftA] end
Defineslshift,rshift,rshiftA,shl,shra,shrl(links are to index).
<definitions of standard RTL operators and functions ((full))>+= [<-D]
fun shl (w, n, k) is
bitTransfer {src is {value is n, lsb is 0},
dst is {value is 0, lsb is k},
width is w - k}
fun shrl (w, n, k) is
bitTransfer {src is {value is n, lsb is k},
dst is {value is 0, lsb is 0},
width is w - k}
fun shra (w, n, k) is
bitTransfer {src is {value is n, lsb is k},
dst is {value is sx n@bits[1 bit at w-1], lsb is 0},
width is w - k}
Definesshl,shra,shrl(links are to index).
One alternative might be to provide a primitive to convert a static width
parameter #n into an ordinary value parameter n.
This alternative would require a lot of extra machinery, since at
present, static width parameters to functions are all handled
implicitly by the lambda-RTL translator.
As of July 199, the lambda-RTL translator does not support the kill
effects.
In a later implementation, we plan to use the question mark to stand
for an ``undefined'' value.
All lambda-RTL functions and RTL operators will be strict in this value,
and RTL.STORE(loc, ?) will become RTL.KILL loc.
Because
we would like to use the question mark in our example descriptions,
even though lambda-RTL doesn't yet have the machinery to give it its
eventual meaning, we introduce it as a nullary RTL operator.
<definitions of standard RTL operators and functions>+= (<-U) [<-D->] local rtlop undefined : #k bits in val ? is undefined end
Defines?,undefined(links are to index).
<definitions of standard RTL operators and functions>+= (<-U) [<-D] module IEEE754 is <IEEE 754 floating point> end
Many floating-point operations that we normally think of as binary can't be treated as binary in lambda-RTL, because their results depend on the rounding mode, which must be passed as an additional argument.
<IEEE 754 floating point>= (<-U) [D->]
local
rtlop [negF fabs] : #k bits -> #k bits -- exact (no rounding)
rtlop [fsqrt] : #k bits * #2 bits -> #k bits -- rounded
rtlop [addF subF mulF divF] : #k bits * #k bits * #2 bits -> #k bits
rtlop [fmulx] : #k bits * #k bits -> #n bits -- where #n = 2 #k
in
val [fneg fabs fsqrt fadd fsub fmul fdiv fmulx] is
[negF fabs fsqrt addF subF mulF divF fmulx]
end
DefinesaddF,divF,fabs,fadd,fdiv,fmul,fmulx,fneg,fsqrt,fsub,mulF,negF,subF(links are to index).
All these operators are polymorphic in the size
of their operands, so they can be used for single-, double-, and
extended-precision floats.
fmulx (for ``exact'') produces an exact, double-size result,
without rounding.
There are four rounding modes required by the standard. It's unfortunate that we seem to have to pick integer representations for these rounding modes, instead of leaving them more abstract, but lambda-RTL does not currently permit users to introduce abstract types.
<IEEE 754 floating point>+= (<-U) [<-D->] module Round is rtlop [nearest zero down up] : #2 bits -- what to round toward end
Definesdown,nearest,up,zero(links are to index).
Conversions are polymorphic in two
widths: the argument and result widths.
The argument of type #2 bits represents the rounding mode.
<IEEE 754 floating point>+= (<-U) [<-D->] rtlop [i2f f2i f2f] : #k bits * #2 bits -> #n bits
Definesf2f,f2i,i2f(links are to index).
There are the special values +/-0 and +/-infinity.
<IEEE 754 floating point>+= (<-U) [<-D->] rtlop [pzero mzero] : #n bits rtlop [pinf minf] : #n bits
Definesminf,mzero,pinf,pzero(links are to index).
<IEEE 754 floating point>+= (<-U) [<-D->] rtlop NaN : #k bits -> #n bits -- function from significand to value
DefinesNaN(links are to index).
A floating-point comparison produces one of four results: less, equal, greater, or unordered. Because we don't want to fix particular 2-bit representations of these results, we create RTL operators to describe them.
<IEEE 754 floating point>+= (<-U) [<-D] module Comparison is rtlop [float_lt float_eq float_gt unordered] : #2 bits val [< = >] is [float_lt float_eq float_gt] end local rtlop cmpF : #k bits * #k bits -> #2 bits in val fcmp is cmpF end
Defines<,>,cmpF,fcmp,float_eq,float_gt,float_lt,unordered(links are to index).
| CSDL Overview | RTLs | Lambda-RTL | Basic RTL Operator Library | SPARC Description | Pentium excerpts |
i that is an input, print i and a blank>: U1, D2
| CSDL Overview | RTLs | Lambda-RTL | Basic RTL Operator Library | SPARC Description | Pentium excerpts |