github.com/cloudwego/frugal@v0.1.15/internal/atm/ssa/pass_constprop.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 `fmt` 21 `math/bits` 22 `unsafe` 23 24 `github.com/cloudwego/frugal/internal/atm/abi` 25 ) 26 27 // ConstProp propagates constant through the expression tree. 28 type ConstProp struct{} 29 30 func (ConstProp) unary(v int64, op IrUnaryOp) int64 { 31 switch op { 32 case IrOpNegate : return -v 33 case IrOpSwap16 : return int64(bits.ReverseBytes16(uint16(v))) 34 case IrOpSwap32 : return int64(bits.ReverseBytes32(uint32(v))) 35 case IrOpSwap64 : return int64(bits.ReverseBytes64(uint64(v))) 36 case IrOpSx32to64 : return int64(int32(v)) 37 default : panic(fmt.Sprintf("constprop: invalid unary operator: %d", op)) 38 } 39 } 40 41 func (ConstProp) binary(x int64, y int64, op IrBinaryOp) int64 { 42 switch op { 43 case IrOpAdd : return x + y 44 case IrOpSub : return x - y 45 case IrOpMul : return x * y 46 case IrOpAnd : return x & y 47 case IrOpOr : return x | y 48 case IrOpXor : return x ^ y 49 case IrOpShr : return x >> y 50 case IrCmpEq : if x == y { return 1 } else { return 0 } 51 case IrCmpNe : if x != y { return 1 } else { return 0 } 52 case IrCmpLt : if x < y { return 1 } else { return 0 } 53 case IrCmpLtu : if uint64(x) < uint64(y) { return 1 } else { return 0 } 54 case IrCmpGeu : if uint64(x) >= uint64(y) { return 1 } else { return 0 } 55 default : panic(fmt.Sprintf("constprop: invalid binary operator: %d", op)) 56 } 57 } 58 59 func (ConstProp) testandset(x int64, y int64) (int64, int64) { 60 if bv := int64(1 << y); x & bv == 0 { 61 return 0, x | bv 62 } else { 63 return 1, x | bv 64 } 65 } 66 67 func (self ConstProp) Apply(cfg *CFG) { 68 done := false 69 consts := make(map[Reg]_ConstData) 70 71 /* constant zero registers */ 72 consts[Rz] = constint(0) 73 consts[Pn] = constptr(nil, Const) 74 75 /* const adder */ 76 addconst := func(r Reg, v _ConstData) { 77 if _, ok := consts[r]; !ok { 78 done = false 79 consts[r] = v 80 } 81 } 82 83 /* evaluate const expression until no modifications were made */ 84 for !done { 85 done = true 86 for _, bb := range cfg.PostOrder().Reversed() { 87 phi := make([]*IrPhi, 0, len(bb.Phi)) 88 ins := make([]IrNode, 0, len(bb.Ins)) 89 isconst := false 90 91 /* check every Phi node */ 92 for _, p := range bb.Phi { 93 var first bool 94 var cdata _ConstData 95 96 /* assume it's a const */ 97 first = true 98 isconst = true 99 100 /* a Phi node is a const iff all it's arguments are the same const */ 101 for _, r := range p.V { 102 if *r != p.R { 103 if cc, ok := consts[*r]; !ok { 104 isconst = false 105 break 106 } else if first { 107 cdata = cc 108 first = false 109 } else if cdata != cc { 110 isconst = false 111 break 112 } 113 } 114 } 115 116 /* not constant, keep it as is */ 117 if !isconst { 118 phi = append(phi, p) 119 continue 120 } 121 122 /* registers declared by Phi nodes can never be zero registers */ 123 if p.R.Kind() == K_zero { 124 panic("constprop: assignment to zero registers in Phi node: " + p.String()) 125 } 126 127 /* replace the Phi node with a Const node */ 128 if cdata.i { 129 ins = append(ins, &IrConstInt { R: p.R, V: cdata.v }) 130 } else { 131 ins = append(ins, &IrConstPtr { R: p.R, P: cdata.p, M: cdata.c }) 132 } 133 134 /* mark as constant */ 135 done = false 136 consts[p.R] = cdata 137 } 138 139 /* check every instructions */ 140 for _, v := range bb.Ins { 141 switch p := v.(type) { 142 default: { 143 ins = append(ins, p) 144 } 145 146 /* value loads */ 147 case *IrLoad: { 148 var ok bool 149 var iv int64 150 var cc _ConstData 151 var pv unsafe.Pointer 152 153 /* must be constant memory address */ 154 if cc, ok = consts[p.Mem]; !ok { 155 ins = append(ins, p) 156 break 157 } 158 159 /* address must be a pointer */ 160 if cc.i { 161 panic(fmt.Sprintf("constprop: value load on integer value %#x: %s", cc.v, p)) 162 } 163 164 /* do not load a volatile pointer */ 165 if cc.c != Const { 166 ins = append(ins, p) 167 break 168 } 169 170 /* constant pointer */ 171 if p.R.Ptr() { 172 if p.Size != abi.PtrSize { 173 panic("constprop: pointer load of invalid size: " + p.String()) 174 } else { 175 pv = *(*unsafe.Pointer)(cc.p) 176 ins = append(ins, &IrConstPtr { R: p.R, P: pv, M: Volatile }) 177 addconst(p.R, constptr(pv, Volatile)) 178 break 179 } 180 } 181 182 /* read the integer */ 183 switch p.Size { 184 case 1 : iv = int64(*(*uint8)(cc.p)) 185 case 2 : iv = int64(*(*uint16)(cc.p)) 186 case 4 : iv = int64(*(*uint32)(cc.p)) 187 case 8 : iv = int64(*(*uint64)(cc.p)) 188 default : panic("constprop: integer load of invalid size: " + p.String()) 189 } 190 191 /* replace the instruction */ 192 ins = append(ins, &IrConstInt { R: p.R, V: iv }) 193 addconst(p.R, constint(iv)) 194 } 195 196 /* integer constant */ 197 case *IrConstInt: { 198 ins = append(ins, p) 199 addconst(p.R, constint(p.V)) 200 } 201 202 /* pointer constant */ 203 case *IrConstPtr: { 204 ins = append(ins, p) 205 addconst(p.R, constptr(p.P, p.M)) 206 } 207 208 /* pointer arithmetics */ 209 case *IrLEA: { 210 if mem, ok := consts[p.Mem]; !ok { 211 ins = append(ins, p) 212 } else if off, ok := consts[p.Off]; !ok { 213 ins = append(ins, p) 214 } else if mem.i { 215 panic(fmt.Sprintf("constprop: pointer operation on integer value %#x: %s", mem.v, p)) 216 } else if !off.i { 217 panic(fmt.Sprintf("constprop: pointer operation with pointer offset %p: %s", off.p, p)) 218 } else { 219 r := addptr(mem.p, off.v) 220 ins = append(ins, &IrConstPtr { R: p.R, P: r, M: Volatile }) 221 addconst(p.R, constptr(r, mem.c)) 222 } 223 } 224 225 /* unary expressions */ 226 case *IrUnaryExpr: { 227 if cc, ok := consts[p.V]; !ok { 228 ins = append(ins, p) 229 } else if !cc.i { 230 panic(fmt.Sprintf("constprop: integer operation on pointer value %p: %s", cc.p, p)) 231 } else { 232 r := self.unary(cc.v, p.Op) 233 ins = append(ins, &IrConstInt { R: p.R, V: r }) 234 addconst(p.R, constint(r)) 235 } 236 } 237 238 /* binary expressions */ 239 case *IrBinaryExpr: { 240 if x, ok := consts[p.X]; !ok { 241 ins = append(ins, p) 242 } else if y, ok := consts[p.Y]; !ok { 243 ins = append(ins, p) 244 } else if !x.i { 245 panic(fmt.Sprintf("constprop: integer operation on pointer value %p: %s", x.p, p)) 246 } else if !y.i { 247 panic(fmt.Sprintf("constprop: integer operation on pointer value %p: %s", y.p, p)) 248 } else { 249 r := self.binary(x.v, y.v, p.Op) 250 ins = append(ins, &IrConstInt { R: p.R, V: r }) 251 addconst(p.R, constint(r)) 252 } 253 } 254 255 /* bit test and set operation */ 256 case *IrBitTestSet: { 257 if x, ok := consts[p.X]; !ok { 258 ins = append(ins, p) 259 } else if y, ok := consts[p.Y]; !ok { 260 ins = append(ins, p) 261 } else if !x.i { 262 panic(fmt.Sprintf("constprop: integer operation on pointer value %p: %s", x.p, p)) 263 } else if !y.i { 264 panic(fmt.Sprintf("constprop: integer operation on pointer value %p: %s", y.p, p)) 265 } else { 266 t, s := self.testandset(x.v, y.v) 267 ins = append(ins, &IrConstInt { R: p.T, V: t }, &IrConstInt { R: p.S, V: s }) 268 addconst(p.T, constint(t)) 269 addconst(p.S, constint(s)) 270 } 271 } 272 } 273 } 274 275 /* rebuild the basic block */ 276 bb.Phi = phi 277 bb.Ins = ins 278 } 279 } 280 }