github.com/cloudwego/frugal@v0.1.15/internal/atm/ssa/pass_compact_amd64.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 ssa 18 19 import ( 20 `github.com/cloudwego/frugal/internal/atm/abi` 21 ) 22 23 type _MemAddr struct { 24 m Mem 25 n uint8 26 } 27 28 func memaddr(m Mem, n uint8) _MemAddr { 29 return _MemAddr { m, n } 30 } 31 32 type _MemTree struct { 33 next *_MemTree 34 mems map[Mem]Reg 35 } 36 37 func (self *_MemTree) add(mm Mem, rr Reg) { 38 if _, ok := self.mems[mm]; ok { 39 panic("memtree: memory operand conflict: " + mm.String()) 40 } else { 41 self.mems[mm] = rr 42 } 43 } 44 45 func (self *_MemTree) find(mm Mem) (Reg, bool) { 46 if rr, ok := self.mems[mm]; ok { 47 return rr, true 48 } else if self.next == nil { 49 return 0, false 50 } else { 51 return self.next.find(mm) 52 } 53 } 54 55 func (self *_MemTree) derive() *_MemTree { 56 return &_MemTree { 57 next: self, 58 mems: make(map[Mem]Reg), 59 } 60 } 61 62 // Compaction is like Fusion, but it performs the reverse action to reduce redundant operations. 63 type Compaction struct{} 64 65 func (self Compaction) dfs(cfg *CFG, bb *BasicBlock, mems *_MemTree, next *bool) { 66 id := bb.Id 67 vv := make(map[_MemAddr]Reg) 68 69 /* check every instructions, reuse memory addresses as much as possible */ 70 for i, v := range bb.Ins { 71 switch p := v.(type) { 72 default: { 73 break 74 } 75 76 /* load effective address */ 77 case *IrAMD64_LEA: { 78 mems.add(p.M, p.R) 79 } 80 81 /* lea {mem}, %r0; movx {mem}, %r1 --> lea {mem}, %r0; movx (%r0), %r1 */ 82 case *IrAMD64_MOV_load: { 83 if m, ok := mems.find(p.M); ok { 84 p.M, *next = Ptr(m, 0), true 85 } else if _, ok = vv[memaddr(p.M, p.N)]; !ok { 86 vv[memaddr(p.M, p.N)] = p.R 87 } 88 } 89 90 /* movx {mem}, %r0; movbex {mem}, %r1 --> movx {mem}, %r0; bswapx %r0, %r1 91 * leax {mem}, %r0; movbex {mem}, %r1 --> leax {mem}, %r0; movbex (%r0), %r1 */ 92 case *IrAMD64_MOV_load_be: { 93 if m, ok := mems.find(p.M); ok { 94 p.M, *next = Ptr(m, 0), true 95 } else if r, ok := vv[memaddr(p.M, p.N)]; ok { 96 bb.Ins[i], *next = &IrAMD64_BSWAP { R: p.R, V: r, N: p.N }, true 97 } 98 } 99 100 /* lea {mem}, %r0; movx %r1, {mem} --> lea {mem}, %r0; movx %r1, (%r0) */ 101 case *IrAMD64_MOV_store_r: { 102 if m, ok := mems.find(p.M); ok { 103 p.M, *next = Ptr(m, 0), true 104 delete(vv, memaddr(p.M, p.N)) 105 } 106 } 107 108 /* lea {mem}, %r0; movx {imm}, {mem} --> lea {mem}, %r0; movx {imm}, (%r0) */ 109 case *IrAMD64_MOV_store_i: { 110 if m, ok := mems.find(p.M); ok { 111 p.M, *next = Ptr(m, 0), true 112 delete(vv, memaddr(p.M, p.N)) 113 } 114 } 115 116 /* lea {mem}, %r0; movx {ptr}, {mem} --> lea {mem}, %r0; movx {ptr}, (%r0) */ 117 case *IrAMD64_MOV_store_p: { 118 if m, ok := mems.find(p.M); ok { 119 p.M, *next = Ptr(m, 0), true 120 delete(vv, memaddr(p.M, abi.PtrSize)) 121 } 122 } 123 124 /* lea {mem}, %r0; movbex %r1, {mem} --> lea {mem}, %r0; movbex %r1, (%r0) */ 125 case *IrAMD64_MOV_store_be: { 126 if m, ok := mems.find(p.M); ok { 127 p.M, *next = Ptr(m, 0), true 128 delete(vv, memaddr(p.M, p.N)) 129 } 130 } 131 132 /* movx {mem}, %r0; cmpx %r1, {mem} --> movx {mem}, %r0; cmpx %r1, %r0 133 * leaq {mem}, %r0; cmpx %r1, {mem} --> leaq {mem}, %r0; cmpx %r1, (%r0) */ 134 case *IrAMD64_CMPQ_rm: { 135 if m, ok := mems.find(p.Y); ok { 136 p.Y, *next = Ptr(m, 0), true 137 } else if v, ok := vv[memaddr(p.Y, p.N)]; ok { 138 bb.Ins[i], *next = &IrAMD64_CMPQ_rr { X: p.X, Y: v, Op: p.Op }, true 139 } 140 } 141 142 /* movx {mem}, %r0; cmpx {mem}, %r1 --> movx {mem}, %r0; cmpx %r0, %r1 143 * leaq {mem}, %r0; cmpx {mem}, %r1 --> leaq {mem}, %r0; cmpx (%r0), %r1 */ 144 case *IrAMD64_CMPQ_mr: { 145 if m, ok := mems.find(p.X); ok { 146 p.X, *next = Ptr(m, 0), true 147 } else if r, ok := vv[memaddr(p.X, p.N)]; ok { 148 bb.Ins[i], *next = &IrAMD64_CMPQ_rr { X: r, Y: p.Y, Op: p.Op }, true 149 } 150 } 151 152 /* movx {mem}, %r0; cmpx {mem}, {imm} --> movx {mem}, %r0; cmpx %r0, {imm} 153 * leaq {mem}, %r0; cmpx {mem}, {imm} --> leaq {mem}, %r0; cmpx (%r0), {imm} */ 154 case *IrAMD64_CMPQ_mi: { 155 if m, ok := mems.find(p.X); ok { 156 p.X, *next = Ptr(m, 0), true 157 } else if r, ok := vv[memaddr(p.X, p.N)]; ok { 158 bb.Ins[i], *next = &IrAMD64_CMPQ_ri { X: r, Y: p.Y, Op: p.Op }, true 159 } 160 } 161 162 /* movx {mem}, %r0; cmpx {mem}, {ptr} --> movx {mem}, %r0; cmpx %r0, {ptr} 163 * leaq {mem}, %r0; cmpx {mem}, {ptr} --> leaq {mem}, %r0; cmpx (%r0), {ptr} */ 164 case *IrAMD64_CMPQ_mp: { 165 if m, ok := mems.find(p.X); ok { 166 p.X, *next = Ptr(m, 0), true 167 } else if r, ok := vv[memaddr(p.X, abi.PtrSize)]; ok { 168 bb.Ins[i], *next = &IrAMD64_CMPQ_rp { X: r, Y: p.Y, Op: p.Op }, true 169 } 170 } 171 172 /* movx {mem}, %r0; cmpx {imm}, {mem} --> movx {mem}, %r0; cmpx {imm}, %r0 173 * leaq {mem}, %r0; cmpx {imm}, {mem} --> leaq {mem}, %r0; cmpx {imm}, (%r0) */ 174 case *IrAMD64_CMPQ_im: { 175 if m, ok := mems.find(p.Y); ok { 176 p.Y, *next = Ptr(m, 0), true 177 } else if r, ok := vv[memaddr(p.Y, p.N)]; ok { 178 bb.Ins[i], *next = &IrAMD64_CMPQ_ir { X: p.X, Y: r, Op: p.Op }, true 179 } 180 } 181 182 /* movx {mem}, %r0; cmpx {ptr}, {mem} --> movx {mem}, %r0; cmpx %r0, {ptr} 183 * leaq {mem}, %r0; cmpx {ptr}, {mem} --> leaq {mem}, %r0; cmpx (%r0), {ptr} */ 184 case *IrAMD64_CMPQ_pm: { 185 if m, ok := mems.find(p.Y); ok { 186 p.Y, *next = Ptr(m, 0), true 187 } else if r, ok := vv[memaddr(p.Y, abi.PtrSize)]; ok { 188 bb.Ins[i], *next = &IrAMD64_CMPQ_pr { X: p.X, Y: r, Op: p.Op }, true 189 } 190 } 191 } 192 } 193 194 /* check for terminators */ 195 switch p := bb.Term.(type) { 196 default: { 197 break 198 } 199 200 /* movx {mem}, %r0; cmpx %r1, {mem}; jcc --> movx {mem}, %r0; cmpx %r1, %r0; jcc 201 * leaq {mem}, %r0; cmpx %r1, {mem}; jcc --> leaq {mem}, %r0; cmpx %r1, (%r0); jcc */ 202 case *IrAMD64_Jcc_rm: { 203 if m, ok := mems.find(p.Y); ok { 204 p.Y, *next = Ptr(m, 0), true 205 } else if r, ok := vv[memaddr(p.Y, p.N)]; ok { 206 bb.Term, *next = &IrAMD64_Jcc_rr { X: p.X, Y: r, To: p.To, Ln: p.Ln, Op: p.Op }, true 207 } 208 } 209 210 /* movx {mem}, %r0; cmpx {mem}, %r1; jcc --> movx {mem}, %r0; cmpx %r0, %r1; jcc 211 * leaq {mem}, %r0; cmpx {mem}, %r1; jcc --> leaq {mem}, %r0; cmpx (%r0), %r1; jcc */ 212 case *IrAMD64_Jcc_mr: { 213 if m, ok := mems.find(p.X); ok { 214 p.X, *next = Ptr(m, 0), true 215 } else if r, ok := vv[memaddr(p.X, p.N)]; ok { 216 bb.Term, *next = &IrAMD64_Jcc_rr { X: r, Y: p.Y, To: p.To, Ln: p.Ln, Op: p.Op }, true 217 } 218 } 219 220 /* movx {mem}, %r0; cmpx {mem}, {imm}; jcc --> movx {mem}, %r0; cmpx %r0, {imm}; jcc 221 * leaq {mem}, %r0; cmpx {mem}, {imm}; jcc --> leaq {mem}, %r0; cmpx (%r0), {imm}; jcc */ 222 case *IrAMD64_Jcc_mi: { 223 if m, ok := mems.find(p.X); ok { 224 p.X, *next = Ptr(m, 0), true 225 } else if r, ok := vv[memaddr(p.X, p.N)]; ok { 226 bb.Term, *next = &IrAMD64_Jcc_ri { X: r, Y: p.Y, To: p.To, Ln: p.Ln, Op: p.Op }, true 227 } 228 } 229 230 /* movx {mem}, %r0; cmpx {mem}, {ptr}; jcc --> movx {mem}, %r0; cmpx %r0, {ptr}; jcc 231 * leaq {mem}, %r0; cmpx {mem}, {ptr}; jcc --> leaq {mem}, %r0; cmpx (%r0), {ptr}; jcc */ 232 case *IrAMD64_Jcc_mp: { 233 if m, ok := mems.find(p.X); ok { 234 p.X, *next = Ptr(m, 0), true 235 } else if r, ok := vv[memaddr(p.X, abi.PtrSize)]; ok { 236 bb.Term, *next = &IrAMD64_Jcc_rp { X: r, Y: p.Y, To: p.To, Ln: p.Ln, Op: p.Op }, true 237 } 238 } 239 240 /* movx {mem}, %r0; cmpx {imm}, {mem}; jcc --> movx {mem}, %r0; cmpx {imm}, %r0; jcc 241 * leaq {mem}, %r0; cmpx {imm}, {mem}; jcc --> leaq {mem}, %r0; cmpx {imm}, (%r0); jcc */ 242 case *IrAMD64_Jcc_im: { 243 if m, ok := mems.find(p.Y); ok { 244 p.Y, *next = Ptr(m, 0), true 245 } else if r, ok := vv[memaddr(p.Y, p.N)]; ok { 246 bb.Term, *next = &IrAMD64_Jcc_ir { X: p.X, Y: r, To: p.To, Ln: p.Ln, Op: p.Op }, true 247 } 248 } 249 250 /* movx {mem}, %r0; cmpx {ptr}, {mem}; jcc --> movx {mem}, %r0; cmpx %r0, {ptr}; jcc 251 * leaq {mem}, %r0; cmpx {ptr}, {mem}; jcc --> leaq {mem}, %r0; cmpx (%r0), {ptr}; jcc */ 252 case *IrAMD64_Jcc_pm: { 253 if m, ok := mems.find(p.Y); ok { 254 p.Y, *next = Ptr(m, 0), true 255 } else if r, ok := vv[memaddr(p.Y, abi.PtrSize)]; ok { 256 bb.Term, *next = &IrAMD64_Jcc_pr { X: p.X, Y: r, To: p.To, Ln: p.Ln, Op: p.Op }, true 257 } 258 } 259 } 260 261 /* DFS the dominator tree */ 262 for _, p := range cfg.DominatorOf[id] { 263 self.dfs(cfg, p, mems.derive(), next) 264 } 265 } 266 267 func (self Compaction) Apply(cfg *CFG) { 268 for next := true; next; { 269 next = false 270 self.dfs(cfg, cfg.Root, (*_MemTree).derive(nil), &next) 271 } 272 }