github.com/cloudwego/frugal@v0.1.15/internal/atm/ssa/block.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 22 `github.com/cloudwego/frugal/internal/atm/abi` 23 `github.com/cloudwego/frugal/internal/atm/hir` 24 `github.com/cloudwego/frugal/internal/atm/rtx` 25 ) 26 27 var _MemSize = [...]uint8 { 28 hir.OP_lb: 1, 29 hir.OP_lw: 2, 30 hir.OP_ll: 4, 31 hir.OP_lq: 8, 32 hir.OP_sb: 1, 33 hir.OP_sw: 2, 34 hir.OP_sl: 4, 35 hir.OP_sq: 8, 36 } 37 38 var _UnaryOps = [...]IrUnaryOp { 39 hir.OP_swapw : IrOpSwap16, 40 hir.OP_swapl : IrOpSwap32, 41 hir.OP_swapq : IrOpSwap64, 42 hir.OP_sxlq : IrOpSx32to64, 43 } 44 45 var _BinaryOps = [...]IrBinaryOp { 46 hir.OP_add : IrOpAdd, 47 hir.OP_sub : IrOpSub, 48 hir.OP_addi : IrOpAdd, 49 hir.OP_muli : IrOpMul, 50 hir.OP_andi : IrOpAnd, 51 hir.OP_xori : IrOpXor, 52 hir.OP_shri : IrOpShr, 53 } 54 55 var _ConstnessMap = [...]Constness { 56 hir.Const : Const, 57 hir.Volatile : Volatile, 58 } 59 60 type BasicBlock struct { 61 Id int 62 Phi []*IrPhi 63 Ins []IrNode 64 Pred []*BasicBlock 65 Term IrTerminator 66 } 67 68 func (self *BasicBlock) String() string { 69 return fmt.Sprintf("bb_%d", self.Id) 70 } 71 72 func (self *BasicBlock) addPred(p *BasicBlock) { 73 for _, b := range self.Pred { if b == p { return } } 74 self.Pred = append(self.Pred, p) 75 } 76 77 func (self *BasicBlock) addInstr(p *hir.Ir) { 78 switch p.Op { 79 default: { 80 panic("invalid instruction: " + p.Disassemble(nil)) 81 } 82 83 /* no operation */ 84 case hir.OP_nop: { 85 break 86 } 87 88 /* ptr(Pr) -> Pd */ 89 case hir.OP_ip: { 90 self.Ins = append( 91 self.Ins, 92 &IrConstPtr { 93 P: p.Pr, 94 R: Rv(p.Pd), 95 M: _ConstnessMap[p.Constness()], 96 }, 97 ) 98 } 99 100 /* *(Ps + Iv) -> Rx */ 101 case hir.OP_lb, hir.OP_lw, hir.OP_ll, hir.OP_lq: { 102 self.Ins = append( 103 self.Ins, 104 &IrConstInt { 105 R: Tr(0), 106 V: p.Iv, 107 }, 108 &IrLEA { 109 R : Pr(0), 110 Mem : Rv(p.Ps), 111 Off : Tr(0), 112 }, 113 &IrLoad { 114 R : Rv(p.Rx), 115 Mem : Pr(0), 116 Size : _MemSize[p.Op], 117 }, 118 ) 119 } 120 121 /* *(Ps + Iv) -> Pd */ 122 case hir.OP_lp: { 123 self.Ins = append( 124 self.Ins, 125 &IrConstInt { 126 R: Tr(0), 127 V: p.Iv, 128 }, 129 &IrLEA { 130 R : Pr(0), 131 Mem : Rv(p.Ps), 132 Off : Tr(0), 133 }, 134 &IrLoad { 135 R : Rv(p.Pd), 136 Mem : Pr(0), 137 Size : abi.PtrSize, 138 }, 139 ) 140 } 141 142 /* Rx -> *(Pd + Iv) */ 143 case hir.OP_sb, hir.OP_sw, hir.OP_sl, hir.OP_sq: { 144 self.Ins = append( 145 self.Ins, 146 &IrConstInt { 147 R: Tr(0), 148 V: p.Iv, 149 }, 150 &IrLEA { 151 R : Pr(0), 152 Mem : Rv(p.Pd), 153 Off : Tr(0), 154 }, 155 &IrStore { 156 R : Rv(p.Rx), 157 Mem : Pr(0), 158 Size : _MemSize[p.Op], 159 }, 160 ) 161 } 162 163 /* Ps -> *(Pd + Iv) */ 164 case hir.OP_sp: { 165 self.Ins = append( 166 self.Ins, 167 &IrConstInt { 168 R: Tr(0), 169 V: p.Iv, 170 }, 171 &IrLEA { 172 R : Pr(0), 173 Mem : Rv(p.Pd), 174 Off : Tr(0), 175 }, 176 &IrConstPtr { 177 R: Pr(1), 178 P: rtx.V_pWriteBarrier, 179 M: Volatile, 180 }, 181 &IrConstPtr { 182 R: Pr(2), 183 P: rtx.F_gcWriteBarrier, 184 }, 185 &IrWriteBarrier { 186 R : Rv(p.Ps), 187 M : Pr(0), 188 Fn : Pr(2), 189 Var : Pr(1), 190 }, 191 ) 192 } 193 194 /* arg[Iv] -> Rx */ 195 case hir.OP_ldaq: { 196 self.Ins = append( 197 self.Ins, 198 &IrLoadArg { 199 R: Rv(p.Rx), 200 I: int(p.Iv), 201 }, 202 ) 203 } 204 205 /* arg[Iv] -> Pd */ 206 case hir.OP_ldap: { 207 self.Ins = append( 208 self.Ins, 209 &IrLoadArg { 210 R: Rv(p.Pd), 211 I: int(p.Iv), 212 }, 213 ) 214 } 215 216 /* Ps + Rx -> Pd */ 217 case hir.OP_addp: { 218 self.Ins = append( 219 self.Ins, 220 &IrLEA { 221 R : Rv(p.Pd), 222 Mem : Rv(p.Ps), 223 Off : Rv(p.Rx), 224 }, 225 ) 226 } 227 228 /* Ps - Rx -> Pd */ 229 case hir.OP_subp: { 230 self.Ins = append( 231 self.Ins, 232 &IrUnaryExpr { 233 R : Tr(0), 234 V : Rv(p.Rx), 235 Op : IrOpNegate, 236 }, 237 &IrLEA { 238 R : Rv(p.Pd), 239 Mem : Rv(p.Ps), 240 Off : Tr(0), 241 }, 242 ) 243 } 244 245 /* Ps + Iv -> Pd */ 246 case hir.OP_addpi: { 247 self.Ins = append( 248 self.Ins, 249 &IrConstInt { 250 R: Tr(0), 251 V: p.Iv, 252 }, 253 &IrLEA { 254 R : Rv(p.Pd), 255 Mem : Rv(p.Ps), 256 Off : Tr(0), 257 }, 258 ) 259 } 260 261 /* Rx ± Ry -> Rz */ 262 case hir.OP_add, hir.OP_sub: { 263 self.Ins = append( 264 self.Ins, 265 &IrBinaryExpr { 266 R : Rv(p.Rz), 267 X : Rv(p.Rx), 268 Y : Rv(p.Ry), 269 Op : _BinaryOps[p.Op], 270 }, 271 ) 272 } 273 274 /* Ry & (1 << (Rx % PTR_BITS)) != 0 -> Rz 275 * Ry |= 1 << (Rx % PTR_BITS) */ 276 case hir.OP_bts: { 277 self.Ins = append( 278 self.Ins, 279 &IrBitTestSet { 280 T: Rv(p.Rz), 281 S: Rv(p.Ry), 282 X: Rv(p.Ry), 283 Y: Rv(p.Rx), 284 }, 285 ) 286 } 287 288 /* Rx {+,*,&,^,>>} Iv -> Ry */ 289 case hir.OP_addi, hir.OP_muli, hir.OP_andi, hir.OP_xori, hir.OP_shri: { 290 self.Ins = append( 291 self.Ins, 292 &IrConstInt { 293 R: Tr(0), 294 V: p.Iv, 295 }, 296 &IrBinaryExpr { 297 R : Rv(p.Ry), 298 X : Rv(p.Rx), 299 Y : Tr(0), 300 Op : _BinaryOps[p.Op], 301 }, 302 ) 303 } 304 305 /* Rx | (1 << Iv) -> Ry */ 306 case hir.OP_bsi: { 307 self.Ins = append( 308 self.Ins, 309 &IrConstInt { 310 R: Tr(0), 311 V: 1 << p.Iv, 312 }, 313 &IrBinaryExpr { 314 R : Rv(p.Ry), 315 X : Rv(p.Rx), 316 Y : Tr(0), 317 Op : IrOpOr, 318 }, 319 ) 320 } 321 322 /* {bswap{16/32/64}/sign_extend_32_to_64}(Rx) -> Ry */ 323 case hir.OP_swapw, hir.OP_swapl, hir.OP_swapq, hir.OP_sxlq: { 324 self.Ins = append( 325 self.Ins, 326 &IrUnaryExpr { 327 R : Rv(p.Ry), 328 V : Rv(p.Rx), 329 Op : _UnaryOps[p.Op], 330 }, 331 ) 332 } 333 334 /* memset(Pd, 0, Iv) */ 335 case hir.OP_bzero: { 336 r := Rv(p.Pd) 337 d := uintptr(0) 338 n := uintptr(p.Iv) 339 340 /* call memory zero for large blocks */ 341 for ; n >= rtx.MaxZeroSize; n -= rtx.MaxZeroSize { 342 self.zeroBlock(r, d, rtx.MaxZeroSize) 343 d += rtx.MaxZeroSize 344 } 345 346 /* call memory zero for smaller blocks */ 347 if n >= rtx.ZeroStep { 348 self.zeroBlock(r, d, n) 349 d += n / rtx.ZeroStep * rtx.ZeroStep 350 n %= rtx.ZeroStep 351 } 352 353 /* use scalar code for remaining bytes */ 354 for _, v := range []uintptr { 8, 4, 2, 1 } { 355 for n >= v { 356 self.zeroUnit(r, d, v) 357 d += v 358 n -= v 359 } 360 } 361 } 362 363 /* memcpy(Pd, Ps, Rx) */ 364 case hir.OP_bcopy: { 365 self.Ins = append( 366 self.Ins, 367 &IrConstPtr { 368 R: Pr(0), 369 P: rtx.F_memmove, 370 }, 371 &IrCallFunc { 372 R : Pr(0), 373 In : []Reg { Rv(p.Pd), Rv(p.Ps), Rv(p.Rx) }, 374 Func : rtx.S_memmove, 375 }, 376 ) 377 } 378 379 /* C subroutine calls */ 380 case hir.OP_ccall: { 381 self.Ins = append( 382 self.Ins, 383 &IrConstPtr { 384 R: Pr(0), 385 P: hir.LookupCall(p.Iv).Func, 386 }, 387 &IrCallNative { 388 R : Pr(0), 389 In : ri2regs(p.Ar[:p.An]), 390 Out : ri2regz(p.Rr[:p.Rn]), 391 }, 392 ) 393 } 394 395 /* Go subroutine calls */ 396 case hir.OP_gcall: { 397 self.Ins = append( 398 self.Ins, 399 &IrConstPtr { 400 R: Pr(0), 401 P: hir.LookupCall(p.Iv).Func, 402 }, 403 &IrCallFunc { 404 R : Pr(0), 405 In : ri2regs(p.Ar[:p.An]), 406 Out : ri2regs(p.Rr[:p.Rn]), 407 Func : abi.ABI.GetLayout(hir.LookupCall(p.Iv).Id), 408 }, 409 ) 410 } 411 412 /* interface method calls */ 413 case hir.OP_icall: { 414 self.Ins = append( 415 self.Ins, 416 &IrCallMethod { 417 T : Rv(p.Ps), 418 V : Rv(p.Pd), 419 In : ri2regs(p.Ar[:p.An]), 420 Out : ri2regs(p.Rr[:p.Rn]), 421 Slot : hir.LookupCall(p.Iv).Slot, 422 Func : abi.ABI.GetLayout(hir.LookupCall(p.Iv).Id), 423 }, 424 ) 425 } 426 427 /* trigger a debugger breakpoint */ 428 case hir.OP_break: { 429 self.Ins = append( 430 self.Ins, 431 new(IrBreakpoint), 432 ) 433 } 434 } 435 } 436 437 func (self *BasicBlock) zeroUnit(r Reg, d uintptr, n uintptr) { 438 self.Ins = append(self.Ins, 439 &IrConstInt { 440 R: Tr(0), 441 V: int64(d), 442 }, 443 &IrLEA { 444 R : Pr(0), 445 Mem : r, 446 Off : Tr(0), 447 }, 448 &IrStore { 449 R : Rz, 450 Mem : Pr(0), 451 Size : uint8(n), 452 }, 453 ) 454 } 455 456 func (self *BasicBlock) zeroBlock(r Reg, d uintptr, n uintptr) { 457 self.Ins = append(self.Ins, 458 &IrConstInt { 459 R: Tr(0), 460 V: int64(d), 461 }, 462 &IrLEA { 463 R : Pr(0), 464 Mem : r, 465 Off : Tr(0), 466 }, 467 &IrConstPtr { 468 R: Pr(1), 469 P: rtx.MemZero.ForSize(n), 470 }, 471 &IrCallNative { 472 R : Pr(1), 473 In : []Reg { Pr(0) }, 474 }, 475 ) 476 } 477 478 func (self *BasicBlock) termReturn(p *hir.Ir) { 479 self.Term = &IrReturn { 480 R: ri2regs(p.Rr[:p.Rn]), 481 } 482 } 483 484 func (self *BasicBlock) termBranch(to *BasicBlock) { 485 to.addPred(self) 486 self.Term = &IrSwitch { Ln: IrLikely(to) } 487 } 488 489 func (self *BasicBlock) termCondition(p *hir.Ir, t *BasicBlock, f *BasicBlock) { 490 var cmp IrBinaryOp 491 var lhs hir.Register 492 var rhs hir.Register 493 494 /* check for OpCode */ 495 switch p.Op { 496 case hir.OP_beq : cmp, lhs, rhs = IrCmpEq , p.Rx, p.Ry 497 case hir.OP_bne : cmp, lhs, rhs = IrCmpNe , p.Rx, p.Ry 498 case hir.OP_blt : cmp, lhs, rhs = IrCmpLt , p.Rx, p.Ry 499 case hir.OP_bltu : cmp, lhs, rhs = IrCmpLtu , p.Rx, p.Ry 500 case hir.OP_bgeu : cmp, lhs, rhs = IrCmpGeu , p.Rx, p.Ry 501 case hir.OP_beqp : cmp, lhs, rhs = IrCmpEq , p.Ps, p.Pd 502 case hir.OP_bnep : cmp, lhs, rhs = IrCmpNe , p.Ps, p.Pd 503 default : panic("invalid branch: " + p.Disassemble(nil)) 504 } 505 506 /* construct the instruction */ 507 ins := &IrBinaryExpr { 508 R : Tr(0), 509 X : Rv(lhs), 510 Y : Rv(rhs), 511 Op : cmp, 512 } 513 514 /* add predecessors */ 515 t.addPred(self) 516 f.addPred(self) 517 518 /* create branch targets */ 519 to := IrUnlikely(t) 520 br := IrUnlikely(f) 521 522 /* assign the correct likeliness */ 523 if p.Likeliness() == hir.Likely { 524 to.Likeliness = Likely 525 } else { 526 br.Likeliness = Likely 527 } 528 529 /* attach to the block */ 530 self.Ins = append(self.Ins, ins) 531 self.Term = &IrSwitch { V: Tr(0), Ln: to, Br: map[int32]*IrBranch { 0: br } } 532 }