I am implementing a compiler using visitor pattern.
Here is the general algorithm I use.
regs
gives the number of registers needed and top
give the next free register.
generate(T,n) =
if T is a leaf write ``load top(), T"
if T is an internal node with children l and r then
if regs(r) = 0 then { generate(l); write ``op top(), r" }
else{
generate(l,n)//Stored in Rn
generate(r,n+1)//Stored in Rn+1
write "op Rn, Rn+1" // rresult is stored in Rn
push(R) }
But in the case where there's not enough registers, we need to spill variables. Suppose we use a greedy allocator that spills the last used register. It means that, when we generate(r)
if there's no register left to store the result, we push Rn to the stack and store result in Rn and then retrieve spilled variable to compute the operation.
The complete schema become:
generate(l,n)//Stored in Rn
push Rn // push to stack
generate(r,n)//Stored in Rn
mov Rn R0 // move
pop Rn // retrieve from stack
write ``op Rn, R0" // result is stored in Rn
What I tried is to modifty the function top
to make it return a register if any available and push last register before returning it if no-one is available so when generating r
children, we can save to stack if there's no space...
generate(l,top())//Stored in Rn
generate(r,top())//Spill if no space and store in Rn
mov Rn R0 <--- my problem is here
pop Rn <--- my problem is here
write ``op Rn, R0" <---my problem here
But after generation of l
and r
, how does the operation knows that it needs to unspill* some variable (if it has to) ?
My question is :
Programmatically, how can I implement it in the compiler to make it oblivious to operation code.
Ideally ... code should look like:
generate(l,something())//Stored in Rn
generate(r,something())
write ``op somethingelse(), againsomethingelse()" <--- taking into account when space is available or not
Aucun commentaire:
Enregistrer un commentaire