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 := ®Allocator{ 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 }