github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/ir/fold.go (about) 1 package ir 2 3 import ( 4 "github.com/arnodel/golua/ops" 5 ) 6 7 // FoldConstants uses the given FoldFunc to fold the code items in the given 8 // constant slice. 9 func FoldConstants(consts []Constant, f FoldFunc) []Constant { 10 fConsts := make([]Constant, len(consts)) 11 for i, k := range consts { 12 if c, ok := k.(*Code); ok { 13 fc := FoldCode(*c, f) 14 fConsts[i] = &fc 15 } else { 16 fConsts[i] = k 17 } 18 } 19 return fConsts 20 } 21 22 // FoldCode uses the given FoldFunc to fold the instructions in the given code. 23 // Returns a new Code instande with folded code. 24 func FoldCode(c Code, f FoldFunc) Code { 25 if f == nil { 26 return c 27 } 28 var s foldStack 29 var i1 Instruction 30 var l1 int 31 for i, i2 := range c.Instructions { 32 l2 := c.Lines[i] 33 if i1 != nil { 34 i1, i2 = f(i1, i2, c.Registers) 35 switch { 36 case i1 == nil && i2 == nil: 37 // Folded to nothing, pop from the stack to be able to fold the 38 // next instruction. 39 l2, i2 = s.pop() 40 case i1 == nil: 41 // Folded to i2 42 l2 = mergeLines(l1, l2) 43 case i2 == nil: 44 // Folded to i1 45 i1, i2 = nil, i1 46 l2 = mergeLines(l1, l2) 47 default: 48 // Not folded 49 } 50 } 51 if i1 != nil { 52 s.push(l1, i1) 53 } 54 i1 = i2 55 l1 = l2 56 } 57 if i1 != nil { 58 s.push(l1, i1) 59 } 60 c.Lines = s.lines 61 c.Instructions = s.instructions 62 return c 63 } 64 65 // A FoldFunc can turn 2 instructions into fewer instructions (in which case 66 // some of the returned instructions are nil). 67 type FoldFunc func(i1, i2 Instruction, regs []RegData) (Instruction, Instruction) 68 69 // DefaultFold applies a few simple folds 70 var DefaultFold FoldFunc = nil 71 72 // FoldTakeRelease folds the following 73 // 74 // (take r1; release r1) ==> () 75 func FoldTakeRelease(i1, i2 Instruction, regs []RegData) (Instruction, Instruction) { 76 t1, ok := i1.(TakeRegister) 77 if !ok { 78 return i1, i2 79 } 80 r2, ok := i2.(ReleaseRegister) 81 if !ok { 82 return i1, i2 83 } 84 if t1.Reg != r2.Reg { 85 return i1, i2 86 } 87 return nil, nil 88 } 89 90 // FoldMoveReg folds the following 91 // 92 // (r1 <- X; r2 <- r1) ==> r2 <- X 93 func FoldMoveReg(i1, i2 Instruction, regs []RegData) (Instruction, Instruction) { 94 sr1, ok := i1.(SetRegInstruction) 95 if !ok { 96 return i1, i2 97 } 98 dst := sr1.DestReg() 99 if regs[dst].IsCell { 100 return i1, i2 101 } 102 tr2, ok := i2.(Transform) 103 if !ok { 104 return i1, i2 105 } 106 if tr2.Op != ops.OpId || dst != tr2.Src { 107 return i1, i2 108 } 109 return nil, sr1.WithDestReg(tr2.Dst) 110 } 111 112 // ComposeFolds takes several fold functions are composes them into one single 113 // function applying all the folds. 114 func ComposeFolds(fs ...FoldFunc) FoldFunc { 115 return func(i1, i2 Instruction, regs []RegData) (Instruction, Instruction) { 116 for _, f := range fs { 117 i1, i2 = f(i1, i2, regs) 118 if i1 == nil || i2 == nil { 119 break 120 } 121 } 122 return i1, i2 123 } 124 } 125 126 type foldStack struct { 127 lines []int 128 instructions []Instruction 129 } 130 131 func (s *foldStack) push(l int, i Instruction) { 132 s.lines = append(s.lines, l) 133 s.instructions = append(s.instructions, i) 134 } 135 136 func (s *foldStack) empty() bool { 137 return len(s.instructions) == 0 138 } 139 140 func (s *foldStack) pop() (l int, i Instruction) { 141 last := len(s.instructions) - 1 142 if last < 0 { 143 return 144 } 145 l = s.lines[last] 146 i = s.instructions[last] 147 s.lines = s.lines[:last] 148 s.instructions = s.instructions[:last] 149 return 150 } 151 152 func mergeLines(l1, l2 int) int { 153 if l1 != 0 { 154 return l1 155 } 156 return l2 157 }