• 沒有找到結果。

2 Static Single Assignment

N/A
N/A
Protected

Academic year: 2022

Share "2 Static Single Assignment"

Copied!
10
0
0

加載中.... (立即查看全文)

全文

(1)

A Correspondence between Continuation Passing Style and Static Single Assignment Form

Richard A. Kelsey NEC Research Institute kelsey@research.nj.nec.com

Abstract

We de ne syntactic transformations that con- vert continuation passing style (CPS) programs into static single assignment form (SSA) and vice versa. Some CPS programs cannot be converted to SSA, but these are not produced by the usual CPS transformation. The CPS!SSA transforma- tion is especially helpful for compiling functional programs. Many optimizations that normally re- quire ow analysis can be performed directly on functional CPS programs by viewing them as SSA programs. We also present a simple program transformation that merges CPS procedures to- gether and by doing so greatly increases the scope of the SSA ow information. This transformation is useful for analyzing loops expressed as recursive procedures.

Permission to copy without fee all or part of this material is granted provided that the copies are not made or distributed for direct commercial advantage, the ACM copyright notice and the title of the publication and its date appear, and notice is given that copying is by permission of the Association for Computing Machinery. To copy otherwise, or to republish, requires a fee and/or speci c permission.

IR'95-1/95 San Francisco, California USA (proceedings in ACM SIGPLAN Notices 30(3), March 1995)

c

1995ACM

1 Introduction

Continuation-passing style has been used as an intermediate language in a number of compilers for functional languages [1, 8, 12]. Static single assignment form has been used in optimizations targeted towards imperative languages, for exam- ple eliminating induction variables [13] and par- tial redundancies [2]. In this paper we de ne syn- tactic transformations for converting continuation passing style (CPS) programs into static single as- signment form (SSA) and vice versa.

The similarities between CPS and SSA have been noted by others [1, 9]. In CPS there is exactly one binding form for every variable and variable uses are lexically scoped. In SSA there is exactly one assignment statement for every vari- able, and that statement dominates all uses of the variable. This is also the main di erence between the two: the restriction on variable references in CPS is lexical, while in SSA it is dynamic.

The two forms have generally been used in very di erent contexts. CPS has been used in com- pilers for functional languages, and SSA for im- perative ones. As a result, the problem of ow analysis has come to be viewed as more dicult in CPS. This is really an artifact of the programs

(2)

being compiled and not a problem with the CPS intermediate language. Functional programs ex- press control ow through the use of procedures, resulting in large collections of small procedures, as opposed to small collections of large procedures in imperative languages (by `large' procedure we mean one large enough to contain a loop). The writers of compilers for imperative languages have been quite successful with using SSA to express the results of intraprocedural ow-analysis and then analyzing the SSA program. CPS uses pro- cedures to express practically everything, so any- thing but the most local optimization appears to require interprocedural analysis, which is hard in any language.

In this paper we are not going to concern our- selves with interprocedural ow analysis. What we will do is restrict our notion of what consti- tutes a procedure in CPS. The  forms in CPS programs will be annotated to indicate which rep- resent full procedures and which are continua- tions. This reduces the number of full procedures and greatly simpli es analyzing the program.

Throughout the paper we will assume that any use of lexical scoping in the source program has been implemented by the introducing explicit en- vironments, as described in [1, 7]. We further assume that in the CPS programs continuations are created and used in a last-in/ rst-out man- ner (see section 6 for a discussion). The lat- ter restriction only a ects the way in which non- local returns, such as longjumps in C or call-with- current-continuation in Scheme, are expressed in CPS.The paper proceeds as follows. Section 2 con- tains the de nition of an SSA language. Section 3 de nes a source language, an annotated CPS lan- guage, and an algorithm for converting source programs into CPS programs. The following two sections de ne functions that convert CPS proce- dures into SSA procedures and vice versa. The remainder of the paper is a discussion of practical di erences between SSA and CPS, followed by a

comparison to previous work.

2 Static Single Assignment

SSA is an imperative form in which there is ex- actly one assignment for every variable and that assignment dominates all uses of the variable (see [4] for a good overview of SSA).

To make the control- ow graph explicit, control ow is expressed entirely in terms ofifandgoto. We allow expressions in some nonstandard places, for example as the arguments to the -functions;

removing these only requires introducing a few additional assignment statements. The syntax of expressions does not matter and is left unspeci- ed, with the restriction that they may not con- tain procedure calls (and they typically don't in SSA languages).

The grammar for a procedure in our SSA lan- guage is:

P ::= proc(x*)f B L* g L ::= l : I* B

I ::= x (E*);

B ::= x E;Bjx E(E*); Bj

gotoli; j

returnE;jreturnE(E*); j

if E thenBelseB E ::= xjE+ Ej:::

where x2 variables l 2labels

The semantics is the `obvious' one. is as- signment, E(E, :::) is a procedure call, and

return returns from the current procedure. The

-functions at the beginning of a block each take one argument for each of thegoto's that jump to that block. The i'th argument is returned when control reaches the block fromgotoli.

Cytron et al. [4] describe a translation al- gorithm that eciently converts programs into SSA while introducing the minimum number of

-functions.

(3)

Because every variable has a unique assign- ment, de nition$use chains are trivial to com- pute. Many analyses and optimizations are sim- pler when applied to a program in SSA form than to the original source program.

Below is an example SSA procedure that counts the number of times zero appears in the sequence

f(0) :::f(limit-1) for some function fand in- teger limit. Note that some of the computation occurs in the -functions. In particular the sec- ond if makes sense only in the context of the

-function forc0 at labelj. We will be using this example throughout the paper.

proc (f limit) f goto l0;

l:i (0, i+1);

c (0, c0);

if i = limit then return c;

else

x f(i);

if x = 0 then goto j0; else

goto j1; g j:c0 (c+1, c);

goto l1; g

3 Annotated CPS

In continuation passing style procedures do not return. Instead they are passed an additional ar- gument, a continuation, which is applied to the procedure's return value (see chapter 8 of [5] for a full discussion).

The correspondence between CPS and SSA re- quires a slightly modi ed algorithm for converting programs into CPS. The modi ed algorithm an- notates theforms in the CPS code to show how they are used. The annotations have no semantic content and, if not introduced by the CPS algo- rithm, could be added to the program via some

suitable analysis (assuming the CPS program is in a suitable form; see section 6 below).

Our source language is a subset of Scheme [3], with the subset chosen as a compromise between simplicity and realism. For simplicity we will as- sume that the source and CPS languages use the same expressions as in the SSA language of the previous section. In the context of Scheme pro- grams we will refer to the equivalents of the SSA expressions as trivial expressions, to keep from confusing them with other Scheme expressions.

As described above, the main restriction on (triv- ial) expressions is that they cannot contain pro- cedure calls.

To simplify the CPS algorithm the source lan- guage is restricted to allow non-trivial expressions only in tail position or as the bound value in a

let. In an actual compiler the source program could be put in this form either by a pre-pass or as part of a more complex CPS algorithm. We also assume that every identi er is unique.

A loop expression is a version of Scheme's named-let with the restriction that calls to the label may only occur in tail position. It is in- cluded to show how iterative constructs, such as

forandwhileloops, may be converted into CPS.

The grammar for this Scheme subset is:

M ::= Ej(E E*)j (ifE M M)j

(let ((xM)) M)j

(loop l((xE)*) M)j

(l E*)

E ::= xj(+ E E)j ::: P ::= ((x*)M) where x2 variables

l 2labels

The semantics of the source language is that of Scheme. This subset is sucient to implement most of Scheme, with explicit cells added for any variables that are the targets of set!expressions in the source program.

As was done in the Rabbit compiler [12] our

(4)

F : MC !M0

F([[E, k]]) =(kE)

F([[E, (cont(x)M0)]]) =(let ((xE)) M0)

F([[(E:::), C]]) =(let ((v(E :::))) (j v))if C = jand j is bound byletrec

=(E ::: C) otherwise

F([[(let ((xM1)) M2), C]]) =F([[M1,(cont(x)F([[M2, C]]))]])

F([[(ifE M1 M2),k]]) =(ifE F([[M1,k]]) F([[M2,k]]))

F([[(ifE M1 M2),(cont(x)M0)]]) =

(letrec ((j (jump (j)M0))) (ifE F([[M1,j]])F([[M2, j]]))) wherej is new

F([[(loop l((xEinitial):::)M), C]]) =

(letrec ((l (jump (x:::)F([[M, C]])))) (l Einitial :::))

F([[(lE :::), C]]) = (l E:::)

V : P! P0

V([[((x:::)M)]]) = (proc (x:::k)F([[M, k]])) Figure 1: Conversion to CPS conversion to CPS will treat trivial expressions as

values and not introduce continuations for them.

In the CPS grammar all 's are annotated as being eitherproc,cont, orjump. The annotations indicate how the 's are used, and, equivalently, how they can be compiled.

Proc is used as the translation of the  forms in the source program. These are full procedures that eventually return a value and for this reason take a continuation as an argument. The cont and jump forms are continuations that are used in slightly di erent ways. The contcontinuations are return points for calls to proc's. Jump indi- cates that the continuation is called within the current procedure instead of being passed to an- other one. The CPS algorithm introduces jump

continuations when the two arms of a conditional have to rejoin at a common point and for the bod- ies ofloop's.

In terms of compilation strategy,cont's are re- turn points,jump's can be compiled as gotos, and proc's require a complete procedure-call mecha- nism.

The grammar for the CPS language is as fol- lows:

M0 ::=(E E* C)j

(kE*)j

(if E M0 M0)j

(let ((xE))M0)j

(letrec ((xP0)*)M0) C ::=kj(cont(x)M0) E ::=x j(+ E E) j::: P0 ::=(proc (x*k)M0)j

(jump (x*)M0) where x,k 2variables

The semantics is the obvious call-by-value se- mantics. The annotated 's are all just , letis syntactic sugar for, and so on.

The identi ers x and k used in the grammar are members of the same syntactic class. Mak- ing them syntactically distinct, as is sometimes done [8], restricts how continuations are used and makes CPS and SSA entirely equivalent (see Sec- tion 6).

The function de ned in Figure 1 translates

(5)

(proc (f limit k)

(letrec ((l (jump (i c)

(if (= i limit) (k c)

(f i (cont (x)

(letrec ((j (jump (c0)

(l (+ i 1) c0)))) (if (= x 0)

(j (+ c 1)) (j c))))))))) (l 0 0)))

Figure 2: Example program in CPS source expressions M to CPS expressions M0. It

takes a continuation C, which is either an identi- er or a cont, as its second argument.

F has two rules for applications. The rst rule is used when the continuation argument is an identi er bound by a letrec in the rule for if and ensures that all uses of such identi ers are called directly. The second is used in all other cases. For let a new continuation is created for the value. Identi er continuations are propagated throughif's; to avoid code expansion continu- ations (as opposed to continuations that are just an identi er) are marked as jump and bound to an identi er. Loop is translated into a recursive continuation. The loop begins by calling the con- tinuation on the initial values of the iterative vari- ables. Calls to the loop's label become calls to the recursive continuation, ignoring the current con- tinuation.

As we are not interested in interprocedural analysis we will treat eachproc as a separate pro- gram (here we depend on the assumption that ex- plicit environments have been introduced to take care of lexical scoping for any nestedproc's). Be- cause they are called at the point they are created,

cont's can be considered as inlined procedures.

The call sites of jump's are easily found: the

jump's only occur as the bound value inletrec's.

Furthermore, all references to the variables to which the jump's are bound are calls to those variables.

The following is the sample SSA procedure from above written in the source language for the CPS transformation:

( (f limit)

(loop l ((i 0) (c 0)) (if (= i limit)

c

(let ((x (f i)))

(let ((c0 (if (= x 0) (+ c 1) c))) (l (+ i 1) c0))))))

Figure 2 presents the CPS version of the same procedure. A cont is introduced as the continu- ation for the call to fandjump's are used as the join point for theif and for the loop.

4 Converting CPS to SSA

In this section we de ne a syntactic translation that converts CPS procedures into SSA proce- dures.

The function G in Figure 3 translates non- trivial CPS expressions to SSA statements. The

(6)

G : M0 !B

G([[(let ((xE))M0)]]) =x E; G([[M0]])

G([[(E :::(cont (x)M0))]]) =x E(:::); G([[M0]])

G([[(E :::k)]]) =returnE(:::);

G([[(kE)]]) =returnE;

G([[(j E0;i E1;i :::)]]) =gotoji;

G([[(if E M01 M02)]]) = if EthenG([[M01]]) elseG([[M02]])

G([[(letrec ( :::)M0)]]) =G([[M0]])

Gproc : P0 ! P

Gproc([[(proc (x:::) M0)]]) = proc(x::: k)f G([[M0]])Gjump([[(jump ::: )]]):::g

Gjump : j (jump (x :::)M0)! L

Gjump([[j,(jump (x:::)M0)]]) =j : x (E0;0, E0;1,:::); ::: G([[M0]]))

Figure 3: Translation of CPS to SSA. The Ei;j on the right-hand side of the de nition of Gjump are those from the left-hand side of the de nition ofG([[(j:::)]]).

simple binding forms, let and cont, become as- signments. Ifis essentially the same in both syn- taxes. Uses of a proc's continuation variable be- come return's while calls to letrec-bound con- tinuation variables are translated asgotos.

The jump's in the program are ignored when found by G. Eachjump is instead lifted up to be- come a labeled block in the SSA procedure. The arguments to the jump's, which are also ignored by G, become the arguments to the -function that de nes the value of the corresponding vari- able in the SSA program. In the de nition of

Gjump, E0;1 is the rst argument to the second call (in some arbitrary ordering) to j, the vari- able bound to (jump (x::: ) M0). That call is translated as gotoj1.

The translated program is syntactically correct, and it obeys the SSA restriction that there is exactly one assignment per variable (since each variable is bound exactly once in the CPS pro- gram). Variables in the translated program are assigned the values of the same expressions they were bound to in the CPS program, and evalua- tion order is preserved, so the two programs pro- duce identical results.

Applying Gproc to the CPS example produces

the original SSA example from above.

5 Converting SSA to CPS

We would like to produce an inverse of G to con- vert SSA programs to CPS. The function H in Figure 4, produced by a simple editing of the def- inition of G, is almost an inverse of G. The di- culty is the rule for theletrec's that bindjump's.

In translating jump's to labeled blocksG ignores their position in the CPS program. Translating a labeled block back into ajump is straightforward, but we need to bind the jump with a letrec somewhere in the newly created CPS program.

The jump's need to be placed such that no vari- able is referred to outside the form that binds it.

Our placement algorithm uses the dominator tree of the SSA program. Statement M0 is said to dominate M1 if M0 appears in every execution path between the start of the program and M1. If M2 also dominates M1 then either M2 dominates M0 or vice versa. The dominator relation thus organizes a program's statements into a tree. The immediate dominator of M is M's parent in the dominator tree.

Given a labeled block, J : I* B, in the SSA

(7)

H: B ! M0

H([[x E;B]]) =(let ((xE)) H([[B]]))

H([[x E(:::);B]]) =(E:::(cont (x)H([[B]])))

H([[ifE thenB1 elseB2]]) = (ifE H([[B1]])H([[B2]]))

H([[returnE(:::);]]) = (E :::k)

H([[returnE;]]) = (kE)

H([[gotoji;]]) = (j E0;i E1;i :::)

Hproc : P! P0

Hproc([[proc(x::: k)fB L*g]]) = (proc (x:::)G([[B]]))

Hjump : L! (jump (x:::)M0)

Hjump([[j: x (E0;0, E0;1,:::); ::: B]]) =(jump (x:::)H([[B]]))

Figure 4: Translation of SSA to CPS. The Ei;j on the right-hand side of the de nition ofH([[gotoJi;]]) are those from the left-hand side of the de nition of Hjump.

program, with M the immediate dominator of I*

B, we will replaceH([[M]]) with

(letrec ((jHjump([[I* B]]))) H([[M]])) in the CPS procedure. If two or more labels have the same immediate dominator their jump's are placed in a singleletrec.

Theorem:

all uses of variables in the CPS pro- gram produced byH are properly in scope.

Proof:

We know that every use of a variable in the SSA program is dominated by the variable's assignment statement. Assignment statements in the SSA program correspond to binding forms in the CPS program. By inspection Hpreserves the dominator tree, so it is sucient to show that every statement in the CPS program is lexically inferior to its immediate dominator, and thus to all its dominators, including the binding forms for any referenced variables.

Again inspecting the de nition of H, we can see that when the M0 produced byH is the body of either a LET, a proc, or a cont, or if it is ei- ther arm of an if, then it is lexically inferior to its immediate dominator. The remaining possible location for an M0 is as the body of a jump, and each jump is by construction lexically inferior to the dominators of its immediate dominator. This would lead to a problem if the immediate domina-

tor were a binding form, but this cannot happen.

If (let ((x E)) M0) or (E :::(cont (x) M0)) dominates a jump, then M0 does as well.

The arguments to the -functions are also placed so as to be lexically inferior to their dom- inators. QED.

Using H to convert the original SSA example to CPS produces a program identical to the CPS program shown in Figure 2.

6 Why CPS and SSA Are Not Identical

The functionGcannot be applied to all CPS pro- grams. It depends on the 's being correctly an- notated, which in turn depends on continuations being used in a somewhat restricted fashion. If non-local returns, such as longjumps in C or call- with-current-continuation in Scheme, are trans- lated into uses of continuations in the CPS pro- gram there is no way to label the continuations so used. They are neither simply passed as a proce- dure's continuation, nor are all of their calls tail- recursive calls within the procedure in which they were created, so neither cont or jump is appro- priate. A fourth annotation could be introduced

(8)

(proc (f limit k)

(letrec ((l (proc (i c k0)

(if (= i limit) (k0 c)

(f i (cont (x)

(letrec ((j (jump (c0)

(l (+ i 1) c0 k0)))) (if (= x 0)

(j (+ c 1)) (j c))))))))) (l 0 0 k)))

Figure 5: Example program using Scheme's named-lettranslated into CPS.

for continuations that are created in one proce- dure and called in another. There would still be no direct way to represent the program in SSA.

7 Compiling Imperative Pro- grams

We have shown that programs can be translated into CPS and from there to SSA programs by a series of syntactic transformations. Producing an SSA program normally requires ow-analysis, as the program directly expresses use$de nition chaining. Where is the ow information in the CPS program coming from?

The source program for the CPS transforma- tion is a functional program, and as such con- tained the required ow information. If we had started with an imperative program, meaning one using set! to modify the values of variables, translation into our CPS source language would have required adding explicit cells to hold the val- ues of all variables that were the targets of set!

expressions. Thejump's in the resulting CPS pro- gram would take no arguments and there would be no -functions in the corresponding SSA pro- gram, only a lot of stores and fetches. Transform- ing such a program into a more useful form would require doing some ow-analysis, similar to that done in translating imperative programs to SSA.

8 Compiling (Mostly) Func- tional Programs

For programs that do not side-a ect the values of variables, viewing CPS procedures as SSA pro- cedures shows that CPS nicely re ects intrapro- cedural de nition$use associations without any need for ow analysis. There is still a problem.

Programs written in languages such as ML and Scheme tend to have few side-a ected variables, making data ow visible as discussed above. The diculty is that these same languages use recur- sion to express iteration. They do not use itera- tive constructs like loop in the CPS source lan- guage used above. So while we get intraprocedu- ral ow information more or less for free, what we really want is interprocedural information.

Interprocedural analysis is dicult, but we can instead take the approach of increasing the size of the procedures. The idea is to nd a set of proce- dures all of which are always called with the same continuation, and then to substitute that contin- uation for the procedures' continuation variables.

The procedures are then themselves continuations nested within a single large procedure, removing any need for interprocedural information.

More precisely, given a set procedures P0 :::Pn bound by one or moreletrecforms to identi ers f0 :::fn and a continuation C such that every use off is either a tail-recursive call from within P or

(9)

a non-tail-recursive call being passed continuation C, we can perform the following transformation.

Let Pi =(proc (:::ki)Mi). If C is(cont :::), let jbe a new identi er; if C is an identi er, let j be that identi er.

1. Replace all references to the ki with j and remove them from the variable lists of the Pi, changing the Pi fromproc's to jump's.

2. Remove the continuation from all calls to the fi.

3. Let M be the smallest form containing every non-tail-recursive call to the fi. Replace M with(letrec (:::)M)where (:::)bindsj to C if C = (cont :::) and also binds fi to Pi for every fi whose original binding form is lexically apparent at M.

Because we restricted ourselves to having thefi be bound byletrecand being called directly (as opposed to being passed to another procedure and then being called, for example), this is a purely syntactic transformation. More sophisticated ver- sions are clearly possible. The simple syntactic version presented here applies to many common uses of recursive procedures.

If our CPS source program used Scheme's named-let syntax instead of the restricted loop version used above, applying G to it would pro- duce the program shown in Figure 5. The recur- sive procedure is now a full procedure. Applying the above transformation to this program, with P0 = (proc (i c k0) :::) and C = j = l, pro- duces the procedure shown in Figure 2.

We need to show that the transformation is correct and preserves the map to SSA. Substitut- ing the value C (or an identi er bound to C) for the variables ki is safe, because theki are always bound to that value. Scoping is preserved, be- cause any free identi ers in C are in scope at M, otherwise they would be out of scope at some oc- currence of C in the original program. Moving the Pi to a lexically inferior form also preserves

scoping, and all uses of thefi are at or below the insertedletrecthat now binds them.

The function G can be applied to the trans- formed program to produce an equivalent SSA version. We have created more jump's, but they obey the same restrictions as the ones created by the CPS algorithm: the jump's are bound by

letrec, all uses of the variables to which they are bound are tail calls, and the bindings and uses all lie within a single proc (because any outlying Pi were moved in step 3 of the transformation).

9 Related Work and Conclu- sion

The similarity between CPS and SSA has been noted by others. Appel [1] and O'Donnell [9]

both brie y describe the relationship between - functions and parameters to procedures whose call sites are known. Here we have shown that the correspondence is exact, and, more importantly, goes both ways. The main di erence between a CPS procedure and an SSA procedure is the syn- tax. Compiler writers can use a single represen- tation and take advantage of both the work on intraprocedural optimizations that has been done using SSA and the work on interprocedural opti- mizations done using CPS.

The other contribution of this paper is that the correspondence can be made to be useful. As long as procedures are too small to contain loops, con- sidering them as SSA procedures does not help much. We have shown how to merge looping pro- cedures together such that the result can treated as a single procedure (a restricted and somewhat more complex version of this transformation is mentioned in two of our earlier papers, [6, 7], but without any discussion of the implications for pro- gram analysis). Without this transformation ana- lyzing loops that are expressed as recursive proce- dures requires interprocedural analysis, as in [11], and in this case using CPS may be more dicult than necessary [10].

(10)

References

[1] Andrew W. Appel. Compiling with

Continuations. Cambridge University Press, Cambridge, 1992.

[2] Preston Briggs and Keith D. Cooper.

E ective partial redundancy elimination. In Proceedings ACM SIGPLAN '94 Conference on Programming Language Design and Implementation, pages 159{170, 1994.

[3] William Clinger and Jonathan Rees.

Revised4 report on the algorithmic language Scheme. ACM Lisp Pointers, 4(3):1{55, 1991.

[4] Ron Cytron, Jeanne Ferrante, Barry K.

Rosen, Mark N. Wegman, and F. Kenneth Zadeck. Eciently computing static single assignment form and the control

dependence graph. ACM Transactions on Programming Languages and Systems, 13(4):451{490, 1991.

[5] Daniel P. Friedman, Mitchell Wand, and Christopher T. Haynes. Essentials of Programming Languages. MIT Press, Cambridge, MA, 1992. Also McGraw-Hill, Chicago, 1992.

[6] Richard Kelsey. Compilation by program transformation. Technical Report

YALEU/DCS/TR-702, Department of Computer Science, Yale University, New Haven, CT, 1989.

[7] Richard Kelsey and Paul Hudak. Realistic compilation by program transformation. In Conf. Rec. 16 ACM Symposium on

Principles of Programming Languages, pages 281{292, 1989.

[8] David A. Kranz, Richard Kelsey, Jonathan A. Rees, Paul Hudak, James Philbin, and Norman I. Adams. Orbit: An

optimizing compiler for scheme. In Proceedings SIGPLAN '86 Symposium on Compiler Construction, 1986. SIGPLAN Notices 21(7), July, 1986, 219-223.

[9] Ciaran O'Donnell. High level compiling for low level machines. PhD thesis, Ecole Nationale Superieure des

Telecommunications, 1994.

[10] Amr Sabry and Matthias Felleisen. Is continutation-passing useful for data ow analysis? InProceedings of the ACM SIGPLAN '94 Conference on Programming Language Design and Implementation, pages 1{12, 1994.

[11] Olin Shivers. Control-Flow Analysis of Higher-Order Languages or Taming Lambda. PhD thesis, School of Computer Science, Carnegie-Mellon University, 1991.

[12] Guy L. Steele. Rabbit: A compiler for Scheme. Technical Report 474,

Massachusetts Institute of Technology, Cambridge, MA, May 1978.

[13] Michael Wolfe. Beyond induction variables.

InProceedings of the ACM SIGPLAN '92 Conference on Programming Language Design and Implementation, pages 162{174, 1992.

參考文獻

相關文件

Let D(t/s) be a standard open subset in Spec(S −1 A)... Here h is the induced map of the

Let σ be the restriction of s to

Solution: S is said to have the least-upper-bound property if every nonempty, bounded above subset E ⊂ S has the least upper bound in

• It is easy to translate into Morse code than reverse

Practice: What is the largest unsigned integer that may be stored in 20 bits. Practice: What is the largest unsigned integer that may be stored in

Example: 14A3 would cause the contents of the memory cell located at address A3 to be placed in register 4.. 2 RXY LOAD the register R with the bit

A simple plane region can be decom- posed into a finite union of non-overlapping rectangles.. Two rectangles are called non-overlapping if their intersection is contained in

Students may not be familiar with