github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/ircomp/ircomp.go (about)

     1  package ircomp
     2  
     3  import (
     4  	"github.com/arnodel/golua/code"
     5  	"github.com/arnodel/golua/ir"
     6  )
     7  
     8  type ConstantCompiler struct {
     9  	builder       *code.Builder
    10  	constants     []ir.Constant
    11  	constantMap   map[uint]int
    12  	compiledCount int
    13  	queue         []uint
    14  	offset        int
    15  }
    16  
    17  var _ ir.ConstantProcessor = (*ConstantCompiler)(nil)
    18  
    19  func NewConstantCompiler(constants []ir.Constant, cc *code.Builder) *ConstantCompiler {
    20  	kc := &ConstantCompiler{
    21  		builder:     cc,
    22  		constants:   constants,
    23  		constantMap: make(map[uint]int),
    24  	}
    25  	return kc
    26  }
    27  
    28  // ProcessFloat compiles a Float.
    29  func (kc *ConstantCompiler) ProcessFloat(k ir.Float) {
    30  	kc.addCompiled(code.Float(k))
    31  }
    32  
    33  // ProcessInt compiles a Int.
    34  func (kc *ConstantCompiler) ProcessInt(k ir.Int) {
    35  	kc.addCompiled(code.Int(k))
    36  }
    37  
    38  // ProcessBool compiles a Bool.
    39  func (kc *ConstantCompiler) ProcessBool(k ir.Bool) {
    40  	kc.addCompiled(code.Bool(k))
    41  }
    42  
    43  // ProcessString compiles a String.
    44  func (kc *ConstantCompiler) ProcessString(k ir.String) {
    45  	kc.addCompiled(code.String(k))
    46  }
    47  
    48  // ProcessNil compiles a Nil.
    49  func (kc *ConstantCompiler) ProcessNil(k ir.NilType) {
    50  	kc.addCompiled(code.NilType{})
    51  }
    52  
    53  // ProcessCode compiles a Code.
    54  func (kc *ConstantCompiler) ProcessCode(c ir.Code) {
    55  	start := kc.builder.Offset()
    56  	regAllocator := &regAllocator{
    57  		registers:   c.Registers,
    58  		allocations: make([]regAllocation, len(c.Registers)),
    59  	}
    60  	// First allocate all the registers that will take upvalues.
    61  	for _, r := range c.UpvalueDests {
    62  		regAllocator.takeRegister(r)
    63  	}
    64  	ic := instrCompiler{
    65  		ConstantCompiler: kc,
    66  		regAllocator:     regAllocator,
    67  	}
    68  	for i, instr := range c.Instructions {
    69  		ic.line = c.Lines[i]
    70  		instr.ProcessInstr(ic)
    71  	}
    72  	end := kc.builder.Offset()
    73  	kc.addCompiled(code.Code{
    74  		Name:         c.Name,
    75  		StartOffset:  start,
    76  		EndOffset:    end,
    77  		UpvalueCount: int16(len(c.UpvalueDests)),
    78  		CellCount:    int16(len(regAllocator.cells)),
    79  		UpNames:      c.UpNames,
    80  		RegCount:     int16(len(regAllocator.regs)),
    81  	})
    82  }
    83  
    84  func (kc *ConstantCompiler) compileConstant(ki uint) {
    85  	kc.constants[ki].ProcessConstant(kc)
    86  }
    87  
    88  func (kc *ConstantCompiler) addCompiled(ck code.Constant) {
    89  	kc.builder.AddConstant(ck)
    90  	kc.compiledCount++
    91  }
    92  
    93  func (kc *ConstantCompiler) GetConstant(ki uint) ir.Constant {
    94  	return kc.constants[ki]
    95  }
    96  
    97  func (kc *ConstantCompiler) QueueConstant(ki uint) int {
    98  	if cki, ok := kc.constantMap[ki]; ok {
    99  		return cki
   100  	}
   101  	kc.constantMap[ki] = kc.offset
   102  	kc.offset++
   103  	kc.queue = append(kc.queue, ki)
   104  	return kc.offset - 1
   105  }
   106  
   107  func (kc *ConstantCompiler) CompileQueue() (unit *code.Unit, err error) {
   108  	defer func() {
   109  		if r := recover(); r != nil {
   110  			cp, ok := r.(*CompilationPanic)
   111  			if !ok {
   112  				panic(r)
   113  			}
   114  			// Try to recover where we were, this is not always accurate but a
   115  			// refactor would be required to get accurate info
   116  			//
   117  			// TODO: make more accurate
   118  			unit := kc.builder.GetUnit()
   119  			for i := len(unit.Lines) - 1; i >= 0; i-- {
   120  				if line := unit.Lines[i]; line > 0 {
   121  					cp.line = line
   122  					break
   123  				}
   124  			}
   125  			err = cp
   126  		}
   127  	}()
   128  	for kc.queue != nil {
   129  		queue := kc.queue
   130  		kc.queue = nil
   131  		for _, ki := range queue {
   132  			kc.compileConstant(ki)
   133  			if kc.constantMap[ki] != kc.compiledCount-1 {
   134  				panic("Inconsistent constant indexes :(")
   135  			}
   136  		}
   137  	}
   138  	return kc.builder.GetUnit(), nil
   139  }