github.com/cloudwego/frugal@v0.1.15/internal/binary/decoder/optimizer.go (about) 1 /* 2 * Copyright 2022 ByteDance Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package decoder 18 19 import ( 20 `fmt` 21 `sort` 22 `strings` 23 24 `github.com/cloudwego/frugal/internal/rt` 25 `github.com/oleiade/lane` 26 ) 27 28 type BasicBlock struct { 29 P Program 30 Src int 31 End int 32 Link []*BasicBlock 33 } 34 35 func (self *BasicBlock) Len() int { 36 return self.End - self.Src 37 } 38 39 func (self *BasicBlock) Free() { 40 q := lane.NewQueue() 41 m := make(map[*BasicBlock]struct{}) 42 43 /* traverse the graph with BFS */ 44 for q.Enqueue(self); !q.Empty(); { 45 v := q.Dequeue() 46 p := v.(*BasicBlock) 47 48 /* add branch to queue */ 49 for _, b := range p.Link { 50 q.Enqueue(b) 51 } 52 53 /* clear branch, and add to free list */ 54 m[p] = struct{}{} 55 p.Link = p.Link[:0] 56 } 57 58 /* reset and free all the nodes */ 59 for p := range m { 60 freeBasicBlock(p) 61 } 62 } 63 64 func (self *BasicBlock) String() string { 65 n := self.End - self.Src 66 v := make([]string, n + 1) 67 68 /* dump every instructions */ 69 for i := self.Src; i < self.End; i++ { 70 v[i - self.Src + 1] = " " + self.P[i].Disassemble() 71 } 72 73 /* add the entry label */ 74 v[0] = fmt.Sprintf("L_%d:", self.Src) 75 return strings.Join(v, "\n") 76 } 77 78 type GraphBuilder struct { 79 Pin map[int]bool 80 Graph map[int]*BasicBlock 81 } 82 83 func (self *GraphBuilder) scan(p Program) { 84 for _, v := range p { 85 if _OpBranches[v.Op] { 86 self.Pin[v.To] = true 87 } 88 } 89 } 90 91 func (self *GraphBuilder) block(p Program, i int, bb *BasicBlock) { 92 bb.Src = i 93 bb.End = i 94 95 /* traverse down until it hits a branch instruction */ 96 for i < len(p) && !_OpBranches[p[i].Op] { 97 i++ 98 bb.End++ 99 100 /* hit a merge point, merge with existing block */ 101 if self.Pin[i] { 102 bb.Link = append(bb.Link, self.branch(p, i)) 103 return 104 } 105 } 106 107 /* end of basic block */ 108 if i == len(p) { 109 return 110 } 111 112 /* also include the branch instruction */ 113 if bb.End++; p[i].Op != OP_struct_switch { 114 bb.Link = append(bb.Link, self.branch(p, p[i].To)) 115 } else { 116 for _, v := range p[i].IntSeq() { 117 if v >= 0 { 118 bb.Link = append(bb.Link, self.branch(p, v)) 119 } 120 } 121 } 122 123 /* GOTO instruction doesn't technically "branch", anything 124 * sits between it and the next branch target are unreachable. */ 125 if p[i].Op != OP_goto { 126 bb.Link = append(bb.Link, self.branch(p, i + 1)) 127 } 128 } 129 130 func (self *GraphBuilder) branch(p Program, i int) *BasicBlock { 131 var ok bool 132 var bb *BasicBlock 133 134 /* check for existing basic blocks */ 135 if bb, ok = self.Graph[i]; ok { 136 return bb 137 } 138 139 /* create a new block */ 140 bb = newBasicBlock() 141 bb.P, bb.Link = p, bb.Link[:0] 142 143 /* process the new block */ 144 self.Graph[i] = bb 145 self.block(p, i, bb) 146 return bb 147 } 148 149 func (self *GraphBuilder) Free() { 150 rt.MapClear(self.Pin) 151 rt.MapClear(self.Graph) 152 freeGraphBuilder(self) 153 } 154 155 func (self *GraphBuilder) Build(p Program) *BasicBlock { 156 self.scan(p) 157 return self.branch(p, 0) 158 } 159 160 func (self *GraphBuilder) BuildAndFree(p Program) (bb *BasicBlock) { 161 bb = self.Build(p) 162 self.Free() 163 return 164 } 165 166 type _OptimizerState struct { 167 buf []*BasicBlock 168 refs map[int]int 169 mask map[*BasicBlock]bool 170 } 171 172 func (self *_OptimizerState) visit(bb *BasicBlock) bool { 173 var mm bool 174 var ok bool 175 176 /* check for duplication */ 177 if mm, ok = self.mask[bb]; mm && ok { 178 return false 179 } 180 181 /* add to block buffer */ 182 self.buf = append(self.buf, bb) 183 self.mask[bb] = true 184 return true 185 } 186 187 func Optimize(p Program) Program { 188 acc := 0 189 ret := newProgram() 190 buf := lane.NewQueue() 191 ctx := newOptimizerState() 192 cfg := newGraphBuilder().BuildAndFree(p) 193 194 /* travel with BFS */ 195 for buf.Enqueue(cfg); !buf.Empty(); { 196 v := buf.Dequeue() 197 b := v.(*BasicBlock) 198 199 /* check for duplication, and then mark as visited */ 200 if !ctx.visit(b) { 201 continue 202 } 203 204 /* optimize each block */ 205 for _, pass := range _PassTab { 206 pass(b) 207 } 208 209 /* add conditional branches if any */ 210 for _, q := range b.Link { 211 buf.Enqueue(q) 212 } 213 } 214 215 /* sort the blocks by entry point */ 216 sort.Slice(ctx.buf, func(i int, j int) bool { 217 return ctx.buf[i].Src < ctx.buf[j].Src 218 }) 219 220 /* remap all the branch locations */ 221 for _, bb := range ctx.buf { 222 ctx.refs[bb.Src] = acc 223 acc += bb.End - bb.Src 224 } 225 226 /* adjust all the branch targets */ 227 for _, bb := range ctx.buf { 228 if end := bb.End; bb.Src != end { 229 if ins := &bb.P[end - 1]; _OpBranches[ins.Op] { 230 if ins.Op != OP_struct_switch { 231 ins.To = ctx.refs[ins.To] 232 } else { 233 for i, v := range ins.IntSeq() { 234 if v >= 0 { 235 ins.IntSeq()[i] = ctx.refs[v] 236 } 237 } 238 } 239 } 240 } 241 } 242 243 /* merge all the basic blocks */ 244 for _, bb := range ctx.buf { 245 ret = append(ret, bb.P[bb.Src:bb.End]...) 246 } 247 248 /* release the original program */ 249 p.Free() 250 freeOptimizerState(ctx) 251 return ret 252 } 253 254 var _PassTab = [...]func(p *BasicBlock) { 255 _PASS_SeekMerging, 256 _PASS_NopElimination, 257 _PASS_Compacting, 258 } 259 260 const ( 261 _NOP OpCode = 0xff 262 ) 263 264 func init() { 265 _OpNames[_NOP] = "(nop)" 266 } 267 268 // Seek Merging Pass: merges seeking instructions as much as possible. 269 func _PASS_SeekMerging(bb *BasicBlock) { 270 for i := bb.Src; i < bb.End; i++ { 271 if p := &bb.P[i]; p.Op == OP_seek { 272 for r, j := true, i + 1; r && j < bb.End; i, j = i + 1, j + 1 { 273 switch bb.P[j].Op { 274 case _NOP : break 275 case OP_seek : p.Iv += bb.P[j].Iv; bb.P[j].Op = _NOP 276 default : r = false 277 } 278 } 279 } 280 } 281 } 282 283 // NOP Elimination Pass: remove instructions that are effectively NOPs (`seek 0`) 284 func _PASS_NopElimination(bb *BasicBlock) { 285 for i := bb.Src; i < bb.End; i++ { 286 if bb.P[i].Iv == 0 && bb.P[i].Op == OP_seek { 287 bb.P[i].Op = _NOP 288 } 289 } 290 } 291 292 // Compacting Pass: remove all the placeholder NOP instructions inserted in the previous pass. 293 func _PASS_Compacting(bb *BasicBlock) { 294 var i int 295 var j int 296 297 /* copy instructins excluding NOPs */ 298 for i, j = bb.Src, bb.Src; i < bb.End; i++ { 299 if bb.P[i].Op != _NOP { 300 bb.P[j] = bb.P[i] 301 j++ 302 } 303 } 304 305 /* update basic block end if needed */ 306 if i != j { 307 bb.End = j 308 } 309 }