A Formally Veried Register Allocation Framework Kent D. Lee 1 Computer Science Luther College
Decorah, IowaUSA
Abstract
Whenusingformalmethodstogeneratecompilersitisdesirableforalllevelsofthe
compilerto beformallyspecied. Typically,registerallocationhasbeenthoughtto
be equivalent to graph coloring. Since graph coloring is NP-Complete most
algo-rithms for register allocation have been ad-hoc. This paperpresents a framework
for register allocation that has been formally veried using an inductive theorem
prover.
1 Introduction
Generating compilers from formal language descriptions means targeting a
specic machine architecture. Because of the nature of most high-level
lan-guages, stack-based target architectures are easy architectures for which to
generate code. However, most new architectures are RISC-based and
there-fore require operands tobe in registers.
To ease the assignment of operands to registers, register allocation has
typicallybeen performedby assumingan unlimitedsupply of symbolic
regis-ters. Once code generation is completed, ad-hoc methods are used to assign
these symbolic registers to the actual, physical registers of the machine [2].
This processresembles graphcoloringwhichisknown tobeNP-Complete [3].
Therefore it has been thought that register allocation is an NP-Complete
problem and hence mustbe done by ad-hocmethods.
This paperpresents analgorithm forregister allocationthat is not NP or
NP-Complete. In fact, it is O(jVj 2
) where V is the set of symbolic registers
being allocated.
The paper begins by presenting an example of a small program to
moti-vate the discussion of register allocation. Then, a formal description of that
1
Email: [email protected]
c
program using Action Semantics is presented. An assembly language
imple-mentationof the programisprovided aswell,showing howsymbolicregisters
are usedinthe targetcode. Next, aproperty ofregisterinterference graphsis
presented thatallows them tobeminimallycoloredin polynomialtime anda
proofofcorrectness isprovided. A frameworkforregisterallocation,basedon
the proofispresentedinthenext section. Anexampleofusingthe framework
togenerate code ispresented, andnally,the workis comparedtootherwork
on registerallocation. 2 A Motivating Example let fun f(x)=x+5 val i = ref 0 in i:=input(); output(1 + ((2 + 3) + f(!i))) end
Fig.1. a Smallprogram
Consider the program written using a small subset of ML in gure 1.
This language will be referred to as the Small language. Given an Action
Semantics [8] description for Small [5], the program's corresponding action
might look like the one given ingure 10.
Typically, formal semantic descriptions of programs are not directly
suit-able for code generation in low-level languages [6]. While Action Semantics
is chosen as the formal method for language description in this paper, the
register allocationframework presented here can beused in any compiler.
Regardless of the chosen formal method for language description,
trans-formationsonthe descriptionare usuallyrequiredtoget the program's
repre-sentationreadyforcode generation. Forinstance,bindingeliminationisoften
performed [9] on actions. Statically known information is usually applied to
the formalnotationtosimplifytheprogramdescription. Somecompilersmay
employpartialevaluationwithrespect tosomeknown (static)information[1]
to simplifythe source program.
Whichever formal language is used, the end result is a transformed
de-scriptionofaprogramthatisinsomewaymoresuitablefortranslationtothe
intended target architecture. In this paper, the action in gure 10 is
trans-formed by removing statically known bindings and by eliminating unneeded
transient values. The resultingaction, along with some sort (i.e. type)
infor-mationispresented ingure11. Theactioningure11isreadytobegiven to
a code generator. In the next section this action is given toa code generator
which must generate instructions involvingregisters toimplementit.
that resembles postx notation(i.e. operands are evaluated rst, followed by
the operation itself). For instance, the action in gure 11 is in postx form.
Postx actions are described in [6]. Stack machines are designed for postx
evaluation. Howevever, register machines are more prevalent. The register
allocationframework presented here can be used to emulate a stack machine
in registers. This is described in more detail insection 6.
3 Code Generation and Register Allocation
In this sectionthe actioningure11isgiven toacode generator tobe
trans-latedtoatargetarchitecture. SincemosttargetarchitecturestodayareRISC,
and most RISC architectures have very minor dierences (at least at the
in-struction set architecture level) the particular target architecture is
inconse-quential. For purposes of discussion consider the MIPS targetarchitecture.
The action given in gure 11 would typically be given to the code
gener-ator as an abstract syntax tree, so it is possible to think of generating code
incrementally forthis action.
Consider the task of generating code for this subaction:
give2
and then
give3
then
give the sumof the given data
As stated in the introduction, code generation is simpliedif it ispossible to
assume anunlimited supply of symbolicregisters. Symbolicregisters will be
denoted by R0, R1, R2, etc. To generate code for this action the number 2
could be loaded into register R7,3 could be loaded into R8, and R7 and R8
could be added together. This results inthe MIPS code:
li R7,2
li R8,3
add R7,R7,R8
Some things tonote about this code:
The symbolic registers R7 and R8 interfere with each other. That means
they cannot be assignedthe samephysical register.
At the end of this code R8 contains avalue that isn't needed again.
However, R7 contains avalue that willbe used later.
Thisinformationleadstotheconstructionof aregisterinterferencegraph.
Aregisterinterference graphcontainsverticesrepresenting symbolicregisters,
and edges existbetween vertices when their corresponding symbolicregisters
interferewith eachother. The complete MIPScode for thisexample program
R0
R1
R2
R3
R4
R5
R8
R6
R7
R9
Fig. 2. Theregister interference graphforthe code ingure 12
gure 2.
Tocompletethecodegeneration,thesymbolicregistersneedtobeassigned
tophysicalregistersof the machine. From the registerinterference graphitis
fairlyeasytoconcludethatthreephysicalregisters(inadditiontothephysical
registers alreadybeingused) are required toimplementthe code ingure 12.
In fact,three is the minimum number of registers required given the register
interferencegraphingure2. But,howcanthisbeformallyveriedespecially
when graphs become more complex?
4 Minimally Coloring Register Interference Graphs
In general, graph coloring is known to be NP-Complete. However, if it is
possible to consider only certain types of graphs there may exist polynomial
time algorithms to color them. This is the case with register interference
graphs, at least the type of register interference graphs considered in this
paper. PittmanandPeterssay intheirbookoncompilerdesignthatasimple
demand policyfor allocatingsymbolicregisters seems toworkbest [10]. This
paper helps to formalize that statement. A simple demand based register
allocation policy leads to register interference graphs that adhere to what is
called the lifetime property in graph theory. This property can be formally
stated as follows.
Denition: A simple undirected graph G = (V;E), exhibits the lifetime
propertyi 9:N !V sothat if ((i);(k))2E,then ((i);(j))2E,
i<j <k where is aone-to-one mappingfor N =f1;2;:::;jVjg.
Informally, this property means that there is an ordering of the vertices
suchthatiftwoverticeshaveanedgebetween them,thenallverticesbetween
the rst vertex and the last vertex also have edges to the rst vertex. In
terms ofsymbolicregisters,it meansthatsymbolicregistersare created,they
carry livevalues fora while,and thenthey aren't usedagain. Inother words,
register allocationpolicy.
Notethatndingthis mapping,ifitexists, foragraphwould take
expo-nentialtime in general,sothis doesnot provide apolynomialtime algorithm
tosolvethe graphcoloringproblemforallgraphsforwhichthe lifetime
prop-ertyholds. However, inthecaseofregisterinterferencegraphs,themappingis
ready toobtain. Itissimplythe orderinwhichthe symbolicregisterswere
re-quested. Forsimplicity,assume fromnowonthat themapping isimplicitly
given asthe indicesofverticesinV forany graphswith thelifetimeproperty,
that is, (i)=v
i
for any v
i 2V.
Proof that the register interference graphs considered here may be
min-imally colored in polynomial time is given below. The following proof was
rst given in a technicalreport authored by Kent Lee and Hantao Zhang [7].
The proof also leads to the framework for register allocation. The algorithm
is implicitly given in the proof of the following theorem, which is done by
induction on the number of verticesin V.
Theorem: A simple undirected graph, G = (V;E) exhibiting the lifetime
property can be minimallycolored intime O(jVj+jEj).
Proof: (by induction on the cardinality of V)
Base case: jVj = 0. Clearly, G can be colored by 0 colors in constant
time.
Inductive case: Assume,bytheinductionhypothesis,forallG=(V;E)
with jVj=n that G can beminimally coloredby k colors in time O(jEj+
jVj)if Gexhibitsthe lifetimeproperty. Consider G 0 =(V [fv n+1 g;E[E 0 ) withjV[fv n+1 gj=n+1whereE 0
containonlytheedgesfromverticesinV
tothe vertex v
n+1
. Wemust consider three cases for G 0
,which issupposed
toexhibit the lifetimeproperty.
Case 1: v
n+1
has less thank neighbors. Clearly,v
n+1
canbecoloredone
of the k colors of G that is not one of the colors of the neighbors of v
n+1 .
The neighbor colors of v
n+1
can be determined by examining the coloring
for G and the neighbors of v
n+1 in E
0
; this can be done in time O(jE 0
j).
Since nonew color is needed, G 0
is stillminimallycolored.
Case 2: v
n+1
haskneighbors. Then,sincethelifetimepropertyholdsfor
G 0 , if (v i ;v n+1 ) 2E 0 then (v i ;v j
) 2E, for i <j <n+1. This means that
v
n+1
forms a (k+1)-clique with its k neighbors in V. But we know from
graph theory that a simple undirected graph with a (k+1)-clique cannot
be colored in less than k +1 colors. Therefore, we have to assign a new
color tov
n+1 ; G
0
is thusstillminimally coloredwith k+1 colors. This can
be done again intime O(jE 0
j).
Case 3: v
n+1
has more than k neighbors. Then there exists at least a
(k+1)-cliqueinG by thesame argumentascase 2. But thismeans thatG
cannotbecoloredwithk colors. This contradictsour induction hypothesis,
sothis case is invalid.
Therefore, G 0
canbeminimallycoloredifGis minimallycolored,and we
lifetimeproperty maybe minimallycoloredin timeO(jVj+jEj)(assuming
we knowthe mappingof the vertices of V). 2
Theproofgivenhereisthecentralideaexploitedintheframeworkprovided
below. Inaddition,thetechnicalreportwithHantaoZhang[7]containsresults
fromprovingthe sametheoremusing anautomatedinductivetheorem prover
called RRL. The intended property for RRLto prove was: num colors(G)
maxclique(G). Afterproving 8 auxiliarylemmas,RRL was able toprove this
propertyinlessthan30seconds onaHP715 workstation(running theAKCL
lisp). Sinceany graphcannotbecoloredinlesscolorsthan themaximum size
clique, it is safeto conclude that the coloring isindeed minimal.
5 A Framework for Register Allocation
R6
R7
R8
R9
R0
R1
R2
R3
R4
R5
Fig. 3. Theregister interference graphredrawn
Thealgorithmforregisterallocationisbasedonthe simpledemandpolicy
described above. With any algorithmorframework, the rst step is toarrive
atarepresentationof theproblem. Inthis paper, symbolicregisternamesare
R0, R1, R2, etc. Therefore, they may be represented by integers (omitting
the R). Register interference graphs are represented by lists of pairs. Each
pair consists of two integers. The rst integer is the number of the symbolic
register. The second number indicates the physical register to which it is
assigned (i.e. itscolor) or negative one (i.e. -1)if the registeris stillalive.
The list representation is easy to visualize by looking at the graph of
g-ure 2. However, it may help if the graph is redrawn as in gure 3. The list
that represents this graph is given below.
[(9;2);(8;2);(7;1);(6;0);(5;1);(4;0);(3;1);(2;0);(1;1);(0;0)]
Notice that thesecond numberineachpair isnot onlythe colorof the vertex
(i.e. itsassigned physical register)butit isalsothe numberof neighborsthat
preceed itin the graph permutation.
Three functions implement this framework. The functions are written in
Standard ML. The graph is automatically constructed via the use of these
functions.
Therstfunction,createRegingure4,isgivenaregisterinterferencegraph
and createsanew registerbyreturning a newsymobolicregistername and
a new register interference graph that consists of the given graph with the
val regNum = ref 0;
fun createReg(regList) =
let val reg = !regNum
in
regNum := !regNum+1;
("R"^Int.toString(reg),(re g,~1 )::r egL ist)
end
Fig. 4. The createReg function
exception unknownReg; exception unFreedReg; fun unFreedCount([])=0 | unFreedCount((x,y)::t) = if y = ~1 then 1+unFreedCount(t) else unFreedCount(t) fun freeReg(r, []) = (TextIO.output(TextIO.std Out,
"Freeing non-existant register "^r^"!\n");
raise unknownReg) | freeReg(r, (regNum,color)::t) = if r = "R"^Int.toString(regNum) then (regNum,unFreedCount(t)):: t else if color = ~1 then (TextIO.output(TextIO.stdOu t,
"Illegally freeing register "^r^
" before R"^Int.toString(regNum)^"\ n");
raise unFreedReg)
else (regNum,color)::freeReg(r, t)
Fig.5. The freeReg function
fun offsetRegs([],offset) = []
| offsetRegs((regNum,color): :t,o ffse t) =
if color <> ~1
then (regNum,color+offset)::(o ffse tRe gs(t ,off set) )
else (regNum,~1)::(offsetRegs( t,of fse t))
fun concatRegs(g1, g2) = offsetRegs(g1,unFreedCou nt g2)@g2
freeRegingure 5is calledatthe end of thelifetimeof asymbolicregister.
It isgiven asymbolicregisterand aregisterinterference graphand returns
anew registerinterference graphwith the given symbolicregister freed.
concatRegs ingure 6 is called if two registerinterference graphs are to be
merged. This maybe needed ifcode generation has beenperformedontwo
parts of a computation (in a bottom-up fashion) and at some later point
duringcode generation the code ismerged intoone larger computation.
The createReg function uses the -1 (note that in ML -1 is written ~1) to
indicatethat the graphisnot yet completebecause registerswithcolor -1are
still alive. The freeReg function uses a result from the theorem. All registers
that followa given register, r, in the register list and are not yet freed must
be neighbors of r. Assume there are k such neighbors. Then, if they are
neighbors, by the lifetime property they must form a k-clique. But it was
proved thatthis issuÆcienttosaythat withthe additionof rthey mustform
a (k+1)-clique. Therefore r should be colored with color k+1. Since colors
begin with 0,the unFreedCount returnsthe next availablecolor.
The freeReg function checks that when a register, r, is freed there are
no other registers created after r that are still alive. If there are other live
registersthatwerecreatedafterr,freeingrwouldviolatethelifetimeproperty
andthereforeisnot allowed. Ineect, thisplacesarstin/rstoutrestriction
onregisterallocationwhichisanotherwayofthinkingofthelifetimeproperty.
Section 6uses the rst in/rst out property inits implementation.
The concatRegs function can be called to combine register interference
graphs at anytime in the future. This is especially useful if your compiler
can'tguaranteealefttorightbottom-uptraversalof theabstract syntaxtree.
Consideringthe computationalcomplexity of the functions abovethe
cre-ateReg function is O(1). The freeReg function must traverse the register list
once for each register that is freed. This is done by calling the unFreedCount
function, whichis then O(jVj)where V isthe set of symbolicregistersin the
register interference graph. The concatRegs function has similar complexity
asit alsocalls the unFreedCount function. Therefore, the framework performs
at least as well as O(jVj 2
). Since calls to freeReg occur interspersed with
the compilation of the code, inpractice there is nonoticable wait forregister
allocationtooccur duringcompilation.
6 An Example Use of the Framework
The framework presented inthispaperhas been successfullyused inpractice.
Inparticular,theActionSemantics-basedcompilergeneratorcalledGenesis[5]
uses an extension of this framework when targeting the MIPS architecture.
The program in gure 1 was compiled by a Genesis generated compiler and
the resulting MIPS code (slightly abbreviated due to length) is presented in
compilergenerationasitappliestosort inferenceandaction transformations.
Noattempthas beenmadetomakeGenesis'compilersproduceeÆcient code.
Parser
Generator
Generator
Scanner
Grammar
Semantic Specification
Action Semantic Definition (ASD)
ML−lex
ML−yacc
Assembly
Tokens
Action
Program
Annotated Action
Postfix
Action
Machine
Key:
Language Specific
MIPS Assembler
Code Generator
Common
Parser
Scanner
Sort Checker
Lexical Specification
Transformer
Language
Fig. 7. TheGenesis compilergenerator
ThestructureofGenesisisshowningure7. BecauseGenesisisanAction
Semantics-basedcompilergenerator,the programingure1isrsttranslated
to itsaction. Then the actionis sort checked (i.e. typechecking isperformed
onit)toverifythattheactioniswell-formed. Finally,theactionistransformed
beforeit isgiven as inputto the code generator.
Code generation is generally postx in nature. Operands are evaluated
before the operation is applied to the operands. When compiling actions it
makessensetogettheactioninaformasclosetotheformofthelow-levelcode
as possible. That is the responsibility of the action transformer in Genesis'
compilers. It insures that actions are postx in nature. Postx actions give
their operands and then operate onthem. For instance, the action
give2
and then
give3
then
give the sumof the given data
beginsbyevaluatingthetwooperands,bygiving2and3,andendsbyapplying
the sumoperationtothem. A stack machinetargetarchitectureiswellsuited
for postx evaluation. It would be convenient if a register machine could
emulate a stack machine, since stack architectures are easy to target when
val regStack = ref ([]:string list);
exception emptyRegStack;
fun pushReg r =
(regStack := (r::(!regStack)))
fun popReg() =
(if (!regStack = []) then raise emptyRegStack else ();
let val x as (r::rl) = !regStack
in
regStack:=rl;
r
end)
Fig.8. The pushRegand popReg functions
val regList = ref ([]:(int*int) list);
fun getReg() =
let val result as (r,rl) = createReg(!regList)
in
regList:=rl;
r
end
fun delReg(r) =
let val rl = freeReg(r,!regList)
in
regList:=rl;
()
end
Fig. 9. ThegetReg and delRegfunctions
The register framework presented in section 5 can be used to emuluate a
stack machine during code generation. Withthe additionof afew functions,
symbolicregisterscan be\pushed" and \popped" froma registerstack using
pushReg and popReg. These functions are presented in gure 8. In addition,
a couple of functions simplify calls for creating and freeing registers in the
framework. The functionsgetReg and delReg, ingure 9, imperativelycreate
and free registers usingthe createReg and freeRegfunctions.
TheregisterframeworkpresentedinthispaperisusedforgeneratingMIPS
code in Genesis' compilers. For instance, when the action give 2 is
encoun-tered, the code generator responds by calling the getReg function to allocate
pushingthe registerontothe registerstack usingpushReg. The actualcodein
the code generator, writtenin ML, looks like this
fun gen_action (give'(y, _)) env =
generate_yielder y env
and generate_yielder (intVal'(i,_)) env =
let val r = getReg()
in
TextIO.output(streamOf env,"\tli "^r^","^Int.toString(i)^"\n");
pushReg(r)
end
When givethe sumofthe givendata isencountered inthe action, thecode
generator knows that addition is a binary operation. The code generator's
code looks like this
fun generate_yielder (sum'(y,_)) env =
let val reg2 = popReg()
val reg1 = popReg()
in
delReg(reg2);
pushReg(reg1);
TextIO.output(streamOf env,"\tadd "^reg1^","^reg1^","^reg2^"\n")
end
There shouldbe two operands onthe simulated registerstack. Upon
rec-ognizingthe additionoperation,the code generator responds by popping two
registers fromthe registerstack, generating code toperform the required
ad-dition, and then pushing the register containing the result onto the register
stack. Notice the registerthat is nolonger needed is freed by callingdelReg.
7 Comparison to Other Work
Chaitin's work on register allocation [2] concentrates on allocating variables
toregisters. Thefocusofthatwork isstudyingthe livenessrangeofvariables.
More recent work by Hendren, et al [4] has extended that work by looking
at interval graphs and hierarchical cyclical interval graphs. In each of these
approaches nodes in the register interference graph correspond to variables
in the programand edges exist between nodes whosecorresponding variables
interfere with each other. Since variables can exist in virtually any order in
a program, there is nostructure tothe correspondinggraphs. The best
algo-rithms for coloringthese graphswill always be based onheuristics, assuming
that NP-complete is reallyNP and not P!
However, one possible advantage of these other approaches is that they
consider register allocation on a procedure or function level. The approach
presented here does not allocate variables to registers. Instead, it focuses on
a simplerproblemofallocatingtransient values(whichmay becopies of
vari-ables) to registers. The idea of allocatingregisters for transient values comes
an action. By limiting ourselves to values, the resulting register interference
graphs have a structure that can be exploited to minimally color them in
polynomialtime.
Putanotherway,whileotherworkinregisterallocationconcentrateson
al-locatingregistersforanentire procedureorfunction,theframeworkpresented
hereconcentratesonallocatingregistersatthestatementlevel,although
noth-ing ispreventing itfrom being used ona procedure orfunction level.
In comparing the two approaches several things can be said about each
approach. The primary advantage of Chaitin's and Hendren's work is in
re-ducing the number of memory accesses that are needed to place values in
registers. If a variable is assigned to a register for an entire function or
pro-cedure this certainly reduces the number of required loads. Without further
work, the approach presented here will certainly result in more load
instruc-tions. However, withthe adventof largedatacaches inRISCprocessors,load
instructions don't carry nearly the penalty of earlierCISC architectures.
Write operationsmay carry alarger penalty in a RISC architecture since
writes have the potential to invalidate data in the cache. When
consider-ing register allocationon a procedureor function level, spillcode willalmost
inevitablybegenerated, resultinginwritestomemory. However, withthe
ap-proachpresented inthis paper, registerspillingwillhappen very infrequently.
8 Conclusion
Registerallocationcanbeexpressed intermsofsymbolicregistersandregister
interference graphs. If a simple demand policy is employed, register
interfer-ence graphs adhere to the lifetimeproperty. This paperhas shown that such
graphs can be minimally coloredin polynomialtime.
In addition to the somewhat rigorous proof that was given in the paper,
the proof was veried by providing it to an automated inductive theorem
prover. Thetheorem proverconcludedthat graphsthatadheretothe lifetime
property can be minimally colored. The proof demonstrated the coloring
occurs inpolynomial time.
The framework presented inthis papertakesadvantage of the proof. The
graphsitproducesadheretothe lifetimeproperty,essentiallyenforcingarst
in/rst out allocation of registersof the machine.
Futureworkonthisframeworkincludesstudyinghowcodegeneratedwith
it performs when compared to other more conventional register allocation
techniques. If it is found that code generated using this framework performs
signicantly poorer due to an increased number of load instructions, register
loads mightbesuppressed by registercoalescing. Ifregisters canbecoalesced
insuchawaythatthelifetimepropertyispreserved,someregisterloadsmight
beeliminatedwhile stillbeing able tominimallycolor the resultinggraphsin
polynomialtime.
be studied in the context of preserving the lifetime property. By using this
framework as a basis for further research, researchers can either nd graph
transformations that preserve the lifetimeproperty or understand the
trade-os they are making when abandoning this property in register interference
graphs.
Acknowledgement
I would liketo thank Hantao Zhang for hishelp in gettingthe RRL theorem
prover to prove the theorem presented in this paper. Automated inductive
theorem proving is still something of an art and Hantao was responsible for
References
[1]A. Bondorf and J. Palsberg. Compiling actions by partial evaluation.
In Proceedings of Conference on Functional Programming Languages and
Computer Architecture (FCPA '93), Copenhagen,DK,1993.
[2]Chaitin, Auslander, Chandra, Cocke, Hopkins, and Markstein. Register
allocationvia coloring. Computer Languages, Vol. 6:47{57,1981.
[3]Garey and Johnson. A Guide to the Theory of NP-Completeness. Freeman,
1979.
[4]LaurieJ.Hendren,GuangR.Gao,ErikR.Altman,and ChandrikaMukerji. A
register allocation framework based on hierarchical cyclic interval graphs. In
Computational Complexity, pages176{191, 1992.
[5]K.D. Lee. Action Semantics-based Compiler Generation. PhD thesis,
Department of ComputerScience, Universityof Iowa, 1999.
[6]K.D. Lee. Postx transformations for action notation. In Proceedings of
AS2000. BRICSNotes SeriesNS-00-6, 2000.
[7]K.D. Leeand H.Zhang. Formal development of a minimal register allocation
algorithm. Technical Report 99-07, University of Iowa, Department of
ComputerScience, Iowa City,IA,1999.
[8]P.D. Mosses. Action Semantics: Cambridge Tracts in Theoretical Computer
Science 26. CambridgeUniversityPress, 1992.
[9]H. Moura. Action Notation Transformations. PhD thesis, Department of
ComputerScience, Universityof Glasgow, 1993.
[10]T. Pittman and J. Peters. The Art of Compiler Design. Prentice Hall,
bind\output"tonativeabstractionofanaction
before
bind\input"tonativeabstractionofanaction
hence
furthermore
recursively
bind\f"toclosureoftheabstractionof
furthermore
bind\x"tothegiven(integerjtruth-value)
thence
give(integerjtruth-value)boundto\x"
or
give[(integerjtruth-value)]cellboundto\x"
andthen
give5
then
givethesumofthegivendata
before
allocate[datum]cell
andthen
give0
then
storethegiven(integerjtruth-value)#2inthegiven[datum]cell#1
andthen
givethegiven[datum]cell#1
then
bind\i"tothegiven((integerjtruth-value)j[(integerjtru th -value)]cell)
hence
give(integerjtruth-value)boundto\i"
or
give[(integerjtruth-value)]cellboundto\i"
andthen
complete
then
enactapplicationoftheabstractionofanactionboundto\input"tothegivendata
then
storethegiven(integerjtruth-value)#2inthegiven[datum]cell#1
then give1 andthen give2 andthen give3 then
givethesumofthegivendata
andthen
give(integerjtruth-value)boundto\i"
or
give[(integerjtruth-value)]cellboundto\i"
then
givethe(integerjtruth-value)storedinthegiven[datum]cell
then
enactapplicationoftheabstractionofanactionboundto\f"tothegivendata
then
givethesumofthegivendata
then
givethesumofthegivendata
then
enactapplicationoftheabstractionofanactionboundto\output"tothegivendata
bind\f101"totheabstraction (integer),!(integer)0 of give[integer]cell (0;1) andthen
givethegiveninteger
then
storethegiveninteger#2inthegiven[integer]cell
(0;1) #1 then give[integer]cell (0;1) then
givetheintegerstoredinthegiven[integer]cell
(0;1)
andthen
give5
then
givethesumofthegivendata
andthen give[integer]cell (0;0) andthen give0 then
storethegiven0#2inthegiven[integer]cell
(0;0) #1 andthen give[integer]cell (0;0) andthen
enact\input"atdepth0
(),!(integer)
then
storethegiveninteger#2inthegiven[integer]cell
(0;0) #1 then give1 andthen give2 andthen give3 then
givethesumofthegivendata
andthen
give[integer]cell
(0;0)
then
givetheintegerstoredinthegiven[integer]cell
(0;0) then enact\f 101 "atdepth0 (integer),!(integer) then
givethesumofthegivendata
then
givethesumofthegivendata
then
enact\output"atdepth0
(integer),!()
.data prmpt: .asciiz"? " .text main: addi$sp,$sp,-40 sw$ra,4($sp) j f101 f101: addi$sp,$sp,-40
sw$ra,4($sp) #storethereturnaddress
sw$t0,0($sp) #storetheaccesslink
sw$s0,8($sp) #preservedacrossfunctioncalls
...
sw$a0,36($sp)
moveR0,$sp #accessingthecellinthiswaysowecanfollowaccesslinksifnecessary
addiR0,R0,36 #nowloadtheaddressofthecell
lwR0,0(R0)
liR1,5
addR0,R0,R1
move$v0,R0
lw$s0,8($sp) #restorepreservedregister
...
addi$sp,$sp,40
jr$ra #andreturn
f101: moveR2,$sp #accessingthecellinthiswaysowecanfollowaccesslinksifnecessary
addiR2,R2,36 #nowloadtheaddressofthecell
liR3,0
swR3,0(R2)
moveR4,$sp #accessingthecellinthiswaysowecanfollowaccesslinksifnecessary
addiR4,R4,36 #nowloadtheaddressofthecell
jal read moveR5,$v0 swR5,0(R4) liR6,1 liR7,2 liR8,3 addR7,R7,R8
moveR9,$sp #accessingthecellinthiswaysowecanfollowaccesslinksifnecessary
addiR9,R9,36 #nowloadtheaddressofthecell
lwR9,0(R9)
move$a0,R9 #puttheparameterintheparmregister
move$t0,$sp #settheaccesslink
jalf101 #callthefunction
moveR9,$v0 #savethefunctionresult
addR7,R7,R9
addR6,R6,R7
move$a0,R6
jal write
lw$ra,4($sp) #restorethereturnaddress
addi$sp,$sp,40 jr$ra write: li$v0,1 syscall jr$ra read: li$v0,4 la$a0,prmpt syscall li$v0,5 syscall jr$ra
equR0$s0 equR1$s1 equR2$s0 equR3$s1 equR4$s0
equR5$s1 equR6$s0 equR7$s1 equR8$s2 equR9$s2