mardi 20 décembre 2022

How write a compiler to handle operations using interpreter design pattern?

I am writing a simple compiler using the interpreter design pattern. My problem is how to handle how variables are stored in the compiler.

The syntax tree is compiled recursively using the method compile having the compiler as context to provide registers and spill used registers if necessary.

Let's take an example (the function compile of the class Add representing an addition)

def compile(compiler):
    this.leftOperand.compile(compiler) #compile left operand 
    this.rightOperand().compile(compiler) # compile right operand 
    compiler.add(ADDINSTRUCTION(valueleft,valueright))

The use cases are:

  • both the left and right operand are complex instructions thus stored in registers
  • left operand is a simple value (float or int) and not saved in register but used directly
  • there's not enough registers left for "right operand" so left operand result is spilled and retrieved later for for addition

My question is:

Is there an elegant scheme to apply so that the compiler should know what are valueleft and valueright (taking into account those use-cases) ? I am looking for the data structures I must use either in expressions themselves or the compiler.

The solutions I thought about are :

#1

make compile return where (in which register) the computation has been stored. But this doesn't work in case a previously stored computation is spilled to the stack (then unspilled in R0).

#2

Saving the two most recent used registers (it would take into account unspilling) but I doubt this scheme would last.

ex:

valueright = compiler.lastUsedRegister 
valueleft = compiler.beforelastUsedRegister 

but I doubt this scheme will last.

Is there some elegant solution for this problem ?

Aucun commentaire:

Enregistrer un commentaire