github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/ssa/value.go (about) 1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package ssa 6 7 import ( 8 "fmt" 9 "math" 10 "sort" 11 "strings" 12 13 "github.com/go-asm/go/cmd/compile/ir" 14 "github.com/go-asm/go/cmd/compile/types" 15 "github.com/go-asm/go/cmd/src" 16 ) 17 18 // A Value represents a value in the SSA representation of the program. 19 // The ID and Type fields must not be modified. The remainder may be modified 20 // if they preserve the value of the Value (e.g. changing a (mul 2 x) to an (add x x)). 21 type Value struct { 22 // A unique identifier for the value. For performance we allocate these IDs 23 // densely starting at 1. There is no guarantee that there won't be occasional holes, though. 24 ID ID 25 26 // The operation that computes this value. See op.go. 27 Op Op 28 29 // The type of this value. Normally this will be a Go type, but there 30 // are a few other pseudo-types, see ../types/type.go. 31 Type *types.Type 32 33 // Auxiliary info for this value. The type of this information depends on the opcode and type. 34 // AuxInt is used for integer values, Aux is used for other values. 35 // Floats are stored in AuxInt using math.Float64bits(f). 36 // Unused portions of AuxInt are filled by sign-extending the used portion, 37 // even if the represented value is unsigned. 38 // Users of AuxInt which interpret AuxInt as unsigned (e.g. shifts) must be careful. 39 // Use Value.AuxUnsigned to get the zero-extended value of AuxInt. 40 AuxInt int64 41 Aux Aux 42 43 // Arguments of this value 44 Args []*Value 45 46 // Containing basic block 47 Block *Block 48 49 // Source position 50 Pos src.XPos 51 52 // Use count. Each appearance in Value.Args and Block.Controls counts once. 53 Uses int32 54 55 // wasm: Value stays on the WebAssembly stack. This value will not get a "register" (WebAssembly variable) 56 // nor a slot on Go stack, and the generation of this value is delayed to its use time. 57 OnWasmStack bool 58 59 // Is this value in the per-function constant cache? If so, remove from cache before changing it or recycling it. 60 InCache bool 61 62 // Storage for the first three args 63 argstorage [3]*Value 64 } 65 66 // Examples: 67 // Opcode aux args 68 // OpAdd nil 2 69 // OpConst string 0 string constant 70 // OpConst int64 0 int64 constant 71 // OpAddcq int64 1 amd64 op: v = arg[0] + constant 72 73 // short form print. Just v#. 74 func (v *Value) String() string { 75 if v == nil { 76 return "nil" // should never happen, but not panicking helps with debugging 77 } 78 return fmt.Sprintf("v%d", v.ID) 79 } 80 81 func (v *Value) AuxInt8() int8 { 82 if opcodeTable[v.Op].auxType != auxInt8 && opcodeTable[v.Op].auxType != auxNameOffsetInt8 { 83 v.Fatalf("op %s doesn't have an int8 aux field", v.Op) 84 } 85 return int8(v.AuxInt) 86 } 87 88 func (v *Value) AuxUInt8() uint8 { 89 if opcodeTable[v.Op].auxType != auxUInt8 { 90 v.Fatalf("op %s doesn't have a uint8 aux field", v.Op) 91 } 92 return uint8(v.AuxInt) 93 } 94 95 func (v *Value) AuxInt16() int16 { 96 if opcodeTable[v.Op].auxType != auxInt16 { 97 v.Fatalf("op %s doesn't have an int16 aux field", v.Op) 98 } 99 return int16(v.AuxInt) 100 } 101 102 func (v *Value) AuxInt32() int32 { 103 if opcodeTable[v.Op].auxType != auxInt32 { 104 v.Fatalf("op %s doesn't have an int32 aux field", v.Op) 105 } 106 return int32(v.AuxInt) 107 } 108 109 // AuxUnsigned returns v.AuxInt as an unsigned value for OpConst*. 110 // v.AuxInt is always sign-extended to 64 bits, even if the 111 // represented value is unsigned. This undoes that sign extension. 112 func (v *Value) AuxUnsigned() uint64 { 113 c := v.AuxInt 114 switch v.Op { 115 case OpConst64: 116 return uint64(c) 117 case OpConst32: 118 return uint64(uint32(c)) 119 case OpConst16: 120 return uint64(uint16(c)) 121 case OpConst8: 122 return uint64(uint8(c)) 123 } 124 v.Fatalf("op %s isn't OpConst*", v.Op) 125 return 0 126 } 127 128 func (v *Value) AuxFloat() float64 { 129 if opcodeTable[v.Op].auxType != auxFloat32 && opcodeTable[v.Op].auxType != auxFloat64 { 130 v.Fatalf("op %s doesn't have a float aux field", v.Op) 131 } 132 return math.Float64frombits(uint64(v.AuxInt)) 133 } 134 func (v *Value) AuxValAndOff() ValAndOff { 135 if opcodeTable[v.Op].auxType != auxSymValAndOff { 136 v.Fatalf("op %s doesn't have a ValAndOff aux field", v.Op) 137 } 138 return ValAndOff(v.AuxInt) 139 } 140 141 func (v *Value) AuxArm64BitField() arm64BitField { 142 if opcodeTable[v.Op].auxType != auxARM64BitField { 143 v.Fatalf("op %s doesn't have a ValAndOff aux field", v.Op) 144 } 145 return arm64BitField(v.AuxInt) 146 } 147 148 // long form print. v# = opcode <type> [aux] args [: reg] (names) 149 func (v *Value) LongString() string { 150 if v == nil { 151 return "<NIL VALUE>" 152 } 153 s := fmt.Sprintf("v%d = %s", v.ID, v.Op) 154 s += " <" + v.Type.String() + ">" 155 s += v.auxString() 156 for _, a := range v.Args { 157 s += fmt.Sprintf(" %v", a) 158 } 159 if v.Block == nil { 160 return s 161 } 162 r := v.Block.Func.RegAlloc 163 if int(v.ID) < len(r) && r[v.ID] != nil { 164 s += " : " + r[v.ID].String() 165 } 166 if reg := v.Block.Func.tempRegs[v.ID]; reg != nil { 167 s += " tmp=" + reg.String() 168 } 169 var names []string 170 for name, values := range v.Block.Func.NamedValues { 171 for _, value := range values { 172 if value == v { 173 names = append(names, name.String()) 174 break // drop duplicates. 175 } 176 } 177 } 178 if len(names) != 0 { 179 sort.Strings(names) // Otherwise a source of variation in debugging output. 180 s += " (" + strings.Join(names, ", ") + ")" 181 } 182 return s 183 } 184 185 func (v *Value) auxString() string { 186 switch opcodeTable[v.Op].auxType { 187 case auxBool: 188 if v.AuxInt == 0 { 189 return " [false]" 190 } else { 191 return " [true]" 192 } 193 case auxInt8: 194 return fmt.Sprintf(" [%d]", v.AuxInt8()) 195 case auxInt16: 196 return fmt.Sprintf(" [%d]", v.AuxInt16()) 197 case auxInt32: 198 return fmt.Sprintf(" [%d]", v.AuxInt32()) 199 case auxInt64, auxInt128: 200 return fmt.Sprintf(" [%d]", v.AuxInt) 201 case auxUInt8: 202 return fmt.Sprintf(" [%d]", v.AuxUInt8()) 203 case auxARM64BitField: 204 lsb := v.AuxArm64BitField().getARM64BFlsb() 205 width := v.AuxArm64BitField().getARM64BFwidth() 206 return fmt.Sprintf(" [lsb=%d,width=%d]", lsb, width) 207 case auxFloat32, auxFloat64: 208 return fmt.Sprintf(" [%g]", v.AuxFloat()) 209 case auxString: 210 return fmt.Sprintf(" {%q}", v.Aux) 211 case auxSym, auxCall, auxTyp: 212 if v.Aux != nil { 213 return fmt.Sprintf(" {%v}", v.Aux) 214 } 215 return "" 216 case auxSymOff, auxCallOff, auxTypSize, auxNameOffsetInt8: 217 s := "" 218 if v.Aux != nil { 219 s = fmt.Sprintf(" {%v}", v.Aux) 220 } 221 if v.AuxInt != 0 || opcodeTable[v.Op].auxType == auxNameOffsetInt8 { 222 s += fmt.Sprintf(" [%v]", v.AuxInt) 223 } 224 return s 225 case auxSymValAndOff: 226 s := "" 227 if v.Aux != nil { 228 s = fmt.Sprintf(" {%v}", v.Aux) 229 } 230 return s + fmt.Sprintf(" [%s]", v.AuxValAndOff()) 231 case auxCCop: 232 return fmt.Sprintf(" {%s}", Op(v.AuxInt)) 233 case auxS390XCCMask, auxS390XRotateParams: 234 return fmt.Sprintf(" {%v}", v.Aux) 235 case auxFlagConstant: 236 return fmt.Sprintf("[%s]", flagConstant(v.AuxInt)) 237 case auxNone: 238 return "" 239 default: 240 // If you see this, add a case above instead. 241 return fmt.Sprintf("[auxtype=%d AuxInt=%d Aux=%v]", opcodeTable[v.Op].auxType, v.AuxInt, v.Aux) 242 } 243 } 244 245 // If/when midstack inlining is enabled (-l=4), the compiler gets both larger and slower. 246 // Not-inlining this method is a help (*Value.reset and *Block.NewValue0 are similar). 247 // 248 //go:noinline 249 func (v *Value) AddArg(w *Value) { 250 if v.Args == nil { 251 v.resetArgs() // use argstorage 252 } 253 v.Args = append(v.Args, w) 254 w.Uses++ 255 } 256 257 //go:noinline 258 func (v *Value) AddArg2(w1, w2 *Value) { 259 if v.Args == nil { 260 v.resetArgs() // use argstorage 261 } 262 v.Args = append(v.Args, w1, w2) 263 w1.Uses++ 264 w2.Uses++ 265 } 266 267 //go:noinline 268 func (v *Value) AddArg3(w1, w2, w3 *Value) { 269 if v.Args == nil { 270 v.resetArgs() // use argstorage 271 } 272 v.Args = append(v.Args, w1, w2, w3) 273 w1.Uses++ 274 w2.Uses++ 275 w3.Uses++ 276 } 277 278 //go:noinline 279 func (v *Value) AddArg4(w1, w2, w3, w4 *Value) { 280 v.Args = append(v.Args, w1, w2, w3, w4) 281 w1.Uses++ 282 w2.Uses++ 283 w3.Uses++ 284 w4.Uses++ 285 } 286 287 //go:noinline 288 func (v *Value) AddArg5(w1, w2, w3, w4, w5 *Value) { 289 v.Args = append(v.Args, w1, w2, w3, w4, w5) 290 w1.Uses++ 291 w2.Uses++ 292 w3.Uses++ 293 w4.Uses++ 294 w5.Uses++ 295 } 296 297 //go:noinline 298 func (v *Value) AddArg6(w1, w2, w3, w4, w5, w6 *Value) { 299 v.Args = append(v.Args, w1, w2, w3, w4, w5, w6) 300 w1.Uses++ 301 w2.Uses++ 302 w3.Uses++ 303 w4.Uses++ 304 w5.Uses++ 305 w6.Uses++ 306 } 307 308 func (v *Value) AddArgs(a ...*Value) { 309 if v.Args == nil { 310 v.resetArgs() // use argstorage 311 } 312 v.Args = append(v.Args, a...) 313 for _, x := range a { 314 x.Uses++ 315 } 316 } 317 func (v *Value) SetArg(i int, w *Value) { 318 v.Args[i].Uses-- 319 v.Args[i] = w 320 w.Uses++ 321 } 322 func (v *Value) SetArgs1(a *Value) { 323 v.resetArgs() 324 v.AddArg(a) 325 } 326 func (v *Value) SetArgs2(a, b *Value) { 327 v.resetArgs() 328 v.AddArg(a) 329 v.AddArg(b) 330 } 331 func (v *Value) SetArgs3(a, b, c *Value) { 332 v.resetArgs() 333 v.AddArg(a) 334 v.AddArg(b) 335 v.AddArg(c) 336 } 337 338 func (v *Value) resetArgs() { 339 for _, a := range v.Args { 340 a.Uses-- 341 } 342 v.argstorage[0] = nil 343 v.argstorage[1] = nil 344 v.argstorage[2] = nil 345 v.Args = v.argstorage[:0] 346 } 347 348 // reset is called from most rewrite rules. 349 // Allowing it to be inlined increases the size 350 // of cmd/compile by almost 10%, and slows it down. 351 // 352 //go:noinline 353 func (v *Value) reset(op Op) { 354 if v.InCache { 355 v.Block.Func.unCache(v) 356 } 357 v.Op = op 358 v.resetArgs() 359 v.AuxInt = 0 360 v.Aux = nil 361 } 362 363 // invalidateRecursively marks a value as invalid (unused) 364 // and after decrementing reference counts on its Args, 365 // also recursively invalidates any of those whose use 366 // count goes to zero. It returns whether any of the 367 // invalidated values was marked with IsStmt. 368 // 369 // BEWARE of doing this *before* you've applied intended 370 // updates to SSA. 371 func (v *Value) invalidateRecursively() bool { 372 lostStmt := v.Pos.IsStmt() == src.PosIsStmt 373 if v.InCache { 374 v.Block.Func.unCache(v) 375 } 376 v.Op = OpInvalid 377 378 for _, a := range v.Args { 379 a.Uses-- 380 if a.Uses == 0 { 381 lost := a.invalidateRecursively() 382 lostStmt = lost || lostStmt 383 } 384 } 385 386 v.argstorage[0] = nil 387 v.argstorage[1] = nil 388 v.argstorage[2] = nil 389 v.Args = v.argstorage[:0] 390 391 v.AuxInt = 0 392 v.Aux = nil 393 return lostStmt 394 } 395 396 // copyOf is called from rewrite rules. 397 // It modifies v to be (Copy a). 398 // 399 //go:noinline 400 func (v *Value) copyOf(a *Value) { 401 if v == a { 402 return 403 } 404 if v.InCache { 405 v.Block.Func.unCache(v) 406 } 407 v.Op = OpCopy 408 v.resetArgs() 409 v.AddArg(a) 410 v.AuxInt = 0 411 v.Aux = nil 412 v.Type = a.Type 413 } 414 415 // copyInto makes a new value identical to v and adds it to the end of b. 416 // unlike copyIntoWithXPos this does not check for v.Pos being a statement. 417 func (v *Value) copyInto(b *Block) *Value { 418 c := b.NewValue0(v.Pos.WithNotStmt(), v.Op, v.Type) // Lose the position, this causes line number churn otherwise. 419 c.Aux = v.Aux 420 c.AuxInt = v.AuxInt 421 c.AddArgs(v.Args...) 422 for _, a := range v.Args { 423 if a.Type.IsMemory() { 424 v.Fatalf("can't move a value with a memory arg %s", v.LongString()) 425 } 426 } 427 return c 428 } 429 430 // copyIntoWithXPos makes a new value identical to v and adds it to the end of b. 431 // The supplied position is used as the position of the new value. 432 // Because this is used for rematerialization, check for case that (rematerialized) 433 // input to value with position 'pos' carried a statement mark, and that the supplied 434 // position (of the instruction using the rematerialized value) is not marked, and 435 // preserve that mark if its line matches the supplied position. 436 func (v *Value) copyIntoWithXPos(b *Block, pos src.XPos) *Value { 437 if v.Pos.IsStmt() == src.PosIsStmt && pos.IsStmt() != src.PosIsStmt && v.Pos.SameFileAndLine(pos) { 438 pos = pos.WithIsStmt() 439 } 440 c := b.NewValue0(pos, v.Op, v.Type) 441 c.Aux = v.Aux 442 c.AuxInt = v.AuxInt 443 c.AddArgs(v.Args...) 444 for _, a := range v.Args { 445 if a.Type.IsMemory() { 446 v.Fatalf("can't move a value with a memory arg %s", v.LongString()) 447 } 448 } 449 return c 450 } 451 452 func (v *Value) Logf(msg string, args ...interface{}) { v.Block.Logf(msg, args...) } 453 func (v *Value) Log() bool { return v.Block.Log() } 454 func (v *Value) Fatalf(msg string, args ...interface{}) { 455 v.Block.Func.fe.Fatalf(v.Pos, msg, args...) 456 } 457 458 // isGenericIntConst reports whether v is a generic integer constant. 459 func (v *Value) isGenericIntConst() bool { 460 return v != nil && (v.Op == OpConst64 || v.Op == OpConst32 || v.Op == OpConst16 || v.Op == OpConst8) 461 } 462 463 // ResultReg returns the result register assigned to v, in github.com/go-asm/go/cmd/obj/$ARCH numbering. 464 // It is similar to Reg and Reg0, except that it is usable interchangeably for all Value Ops. 465 // If you know v.Op, using Reg or Reg0 (as appropriate) will be more efficient. 466 func (v *Value) ResultReg() int16 { 467 reg := v.Block.Func.RegAlloc[v.ID] 468 if reg == nil { 469 v.Fatalf("nil reg for value: %s\n%s\n", v.LongString(), v.Block.Func) 470 } 471 if pair, ok := reg.(LocPair); ok { 472 reg = pair[0] 473 } 474 if reg == nil { 475 v.Fatalf("nil reg0 for value: %s\n%s\n", v.LongString(), v.Block.Func) 476 } 477 return reg.(*Register).objNum 478 } 479 480 // Reg returns the register assigned to v, in github.com/go-asm/go/cmd/obj/$ARCH numbering. 481 func (v *Value) Reg() int16 { 482 reg := v.Block.Func.RegAlloc[v.ID] 483 if reg == nil { 484 v.Fatalf("nil register for value: %s\n%s\n", v.LongString(), v.Block.Func) 485 } 486 return reg.(*Register).objNum 487 } 488 489 // Reg0 returns the register assigned to the first output of v, in github.com/go-asm/go/cmd/obj/$ARCH numbering. 490 func (v *Value) Reg0() int16 { 491 reg := v.Block.Func.RegAlloc[v.ID].(LocPair)[0] 492 if reg == nil { 493 v.Fatalf("nil first register for value: %s\n%s\n", v.LongString(), v.Block.Func) 494 } 495 return reg.(*Register).objNum 496 } 497 498 // Reg1 returns the register assigned to the second output of v, in github.com/go-asm/go/cmd/obj/$ARCH numbering. 499 func (v *Value) Reg1() int16 { 500 reg := v.Block.Func.RegAlloc[v.ID].(LocPair)[1] 501 if reg == nil { 502 v.Fatalf("nil second register for value: %s\n%s\n", v.LongString(), v.Block.Func) 503 } 504 return reg.(*Register).objNum 505 } 506 507 // RegTmp returns the temporary register assigned to v, in github.com/go-asm/go/cmd/obj/$ARCH numbering. 508 func (v *Value) RegTmp() int16 { 509 reg := v.Block.Func.tempRegs[v.ID] 510 if reg == nil { 511 v.Fatalf("nil tmp register for value: %s\n%s\n", v.LongString(), v.Block.Func) 512 } 513 return reg.objNum 514 } 515 516 func (v *Value) RegName() string { 517 reg := v.Block.Func.RegAlloc[v.ID] 518 if reg == nil { 519 v.Fatalf("nil register for value: %s\n%s\n", v.LongString(), v.Block.Func) 520 } 521 return reg.(*Register).name 522 } 523 524 // MemoryArg returns the memory argument for the Value. 525 // The returned value, if non-nil, will be memory-typed (or a tuple with a memory-typed second part). 526 // Otherwise, nil is returned. 527 func (v *Value) MemoryArg() *Value { 528 if v.Op == OpPhi { 529 v.Fatalf("MemoryArg on Phi") 530 } 531 na := len(v.Args) 532 if na == 0 { 533 return nil 534 } 535 if m := v.Args[na-1]; m.Type.IsMemory() { 536 return m 537 } 538 return nil 539 } 540 541 // LackingPos indicates whether v is a value that is unlikely to have a correct 542 // position assigned to it. Ignoring such values leads to more user-friendly positions 543 // assigned to nearby values and the blocks containing them. 544 func (v *Value) LackingPos() bool { 545 // The exact definition of LackingPos is somewhat heuristically defined and may change 546 // in the future, for example if some of these operations are generated more carefully 547 // with respect to their source position. 548 return v.Op == OpVarDef || v.Op == OpVarLive || v.Op == OpPhi || 549 (v.Op == OpFwdRef || v.Op == OpCopy) && v.Type == types.TypeMem 550 } 551 552 // removeable reports whether the value v can be removed from the SSA graph entirely 553 // if its use count drops to 0. 554 func (v *Value) removeable() bool { 555 if v.Type.IsVoid() { 556 // Void ops (inline marks), must stay. 557 return false 558 } 559 if opcodeTable[v.Op].nilCheck { 560 // Nil pointer checks must stay. 561 return false 562 } 563 if v.Type.IsMemory() { 564 // We don't need to preserve all memory ops, but we do need 565 // to keep calls at least (because they might have 566 // synchronization operations we can't see). 567 return false 568 } 569 if v.Op.HasSideEffects() { 570 // These are mostly synchronization operations. 571 return false 572 } 573 return true 574 } 575 576 // AutoVar returns a *Name and int64 representing the auto variable and offset within it 577 // where v should be spilled. 578 func AutoVar(v *Value) (*ir.Name, int64) { 579 if loc, ok := v.Block.Func.RegAlloc[v.ID].(LocalSlot); ok { 580 if v.Type.Size() > loc.Type.Size() { 581 v.Fatalf("spill/restore type %s doesn't fit in slot type %s", v.Type, loc.Type) 582 } 583 return loc.N, loc.Off 584 } 585 // Assume it is a register, return its spill slot, which needs to be live 586 nameOff := v.Aux.(*AuxNameOffset) 587 return nameOff.Name, nameOff.Offset 588 } 589 590 // CanSSA reports whether values of type t can be represented as a Value. 591 func CanSSA(t *types.Type) bool { 592 types.CalcSize(t) 593 if t.Size() > int64(4*types.PtrSize) { 594 // 4*Widthptr is an arbitrary constant. We want it 595 // to be at least 3*Widthptr so slices can be registerized. 596 // Too big and we'll introduce too much register pressure. 597 return false 598 } 599 switch t.Kind() { 600 case types.TARRAY: 601 // We can't do larger arrays because dynamic indexing is 602 // not supported on SSA variables. 603 // TODO: allow if all indexes are constant. 604 if t.NumElem() <= 1 { 605 return CanSSA(t.Elem()) 606 } 607 return false 608 case types.TSTRUCT: 609 if t.NumFields() > MaxStruct { 610 return false 611 } 612 for _, t1 := range t.Fields() { 613 if !CanSSA(t1.Type) { 614 return false 615 } 616 } 617 return true 618 default: 619 return true 620 } 621 }