github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/ssa/decompose.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 "sort" 9 10 "github.com/go-asm/go/cmd/compile/types" 11 ) 12 13 // decompose converts phi ops on compound builtin types into phi 14 // ops on simple types, then invokes rewrite rules to decompose 15 // other ops on those types. 16 func decomposeBuiltIn(f *Func) { 17 // Decompose phis 18 for _, b := range f.Blocks { 19 for _, v := range b.Values { 20 if v.Op != OpPhi { 21 continue 22 } 23 decomposeBuiltInPhi(v) 24 } 25 } 26 27 // Decompose other values 28 // Note: Leave dead values because we need to keep the original 29 // values around so the name component resolution below can still work. 30 applyRewrite(f, rewriteBlockdec, rewriteValuedec, leaveDeadValues) 31 if f.Config.RegSize == 4 { 32 applyRewrite(f, rewriteBlockdec64, rewriteValuedec64, leaveDeadValues) 33 } 34 35 // Split up named values into their components. 36 // accumulate old names for aggregates (that are decomposed) in toDelete for efficient bulk deletion, 37 // accumulate new LocalSlots in newNames for addition after the iteration. This decomposition is for 38 // builtin types with leaf components, and thus there is no need to reprocess the newly create LocalSlots. 39 var toDelete []namedVal 40 var newNames []*LocalSlot 41 for i, name := range f.Names { 42 t := name.Type 43 switch { 44 case t.IsInteger() && t.Size() > f.Config.RegSize: 45 hiName, loName := f.SplitInt64(name) 46 newNames = maybeAppend2(f, newNames, hiName, loName) 47 for j, v := range f.NamedValues[*name] { 48 if v.Op != OpInt64Make { 49 continue 50 } 51 f.NamedValues[*hiName] = append(f.NamedValues[*hiName], v.Args[0]) 52 f.NamedValues[*loName] = append(f.NamedValues[*loName], v.Args[1]) 53 toDelete = append(toDelete, namedVal{i, j}) 54 } 55 case t.IsComplex(): 56 rName, iName := f.SplitComplex(name) 57 newNames = maybeAppend2(f, newNames, rName, iName) 58 for j, v := range f.NamedValues[*name] { 59 if v.Op != OpComplexMake { 60 continue 61 } 62 f.NamedValues[*rName] = append(f.NamedValues[*rName], v.Args[0]) 63 f.NamedValues[*iName] = append(f.NamedValues[*iName], v.Args[1]) 64 toDelete = append(toDelete, namedVal{i, j}) 65 } 66 case t.IsString(): 67 ptrName, lenName := f.SplitString(name) 68 newNames = maybeAppend2(f, newNames, ptrName, lenName) 69 for j, v := range f.NamedValues[*name] { 70 if v.Op != OpStringMake { 71 continue 72 } 73 f.NamedValues[*ptrName] = append(f.NamedValues[*ptrName], v.Args[0]) 74 f.NamedValues[*lenName] = append(f.NamedValues[*lenName], v.Args[1]) 75 toDelete = append(toDelete, namedVal{i, j}) 76 } 77 case t.IsSlice(): 78 ptrName, lenName, capName := f.SplitSlice(name) 79 newNames = maybeAppend2(f, newNames, ptrName, lenName) 80 newNames = maybeAppend(f, newNames, capName) 81 for j, v := range f.NamedValues[*name] { 82 if v.Op != OpSliceMake { 83 continue 84 } 85 f.NamedValues[*ptrName] = append(f.NamedValues[*ptrName], v.Args[0]) 86 f.NamedValues[*lenName] = append(f.NamedValues[*lenName], v.Args[1]) 87 f.NamedValues[*capName] = append(f.NamedValues[*capName], v.Args[2]) 88 toDelete = append(toDelete, namedVal{i, j}) 89 } 90 case t.IsInterface(): 91 typeName, dataName := f.SplitInterface(name) 92 newNames = maybeAppend2(f, newNames, typeName, dataName) 93 for j, v := range f.NamedValues[*name] { 94 if v.Op != OpIMake { 95 continue 96 } 97 f.NamedValues[*typeName] = append(f.NamedValues[*typeName], v.Args[0]) 98 f.NamedValues[*dataName] = append(f.NamedValues[*dataName], v.Args[1]) 99 toDelete = append(toDelete, namedVal{i, j}) 100 } 101 case t.IsFloat(): 102 // floats are never decomposed, even ones bigger than RegSize 103 case t.Size() > f.Config.RegSize: 104 f.Fatalf("undecomposed named type %s %v", name, t) 105 } 106 } 107 108 deleteNamedVals(f, toDelete) 109 f.Names = append(f.Names, newNames...) 110 } 111 112 func maybeAppend(f *Func, ss []*LocalSlot, s *LocalSlot) []*LocalSlot { 113 if _, ok := f.NamedValues[*s]; !ok { 114 f.NamedValues[*s] = nil 115 return append(ss, s) 116 } 117 return ss 118 } 119 120 func maybeAppend2(f *Func, ss []*LocalSlot, s1, s2 *LocalSlot) []*LocalSlot { 121 return maybeAppend(f, maybeAppend(f, ss, s1), s2) 122 } 123 124 func decomposeBuiltInPhi(v *Value) { 125 switch { 126 case v.Type.IsInteger() && v.Type.Size() > v.Block.Func.Config.RegSize: 127 decomposeInt64Phi(v) 128 case v.Type.IsComplex(): 129 decomposeComplexPhi(v) 130 case v.Type.IsString(): 131 decomposeStringPhi(v) 132 case v.Type.IsSlice(): 133 decomposeSlicePhi(v) 134 case v.Type.IsInterface(): 135 decomposeInterfacePhi(v) 136 case v.Type.IsFloat(): 137 // floats are never decomposed, even ones bigger than RegSize 138 case v.Type.Size() > v.Block.Func.Config.RegSize: 139 v.Fatalf("%v undecomposed type %v", v, v.Type) 140 } 141 } 142 143 func decomposeStringPhi(v *Value) { 144 types := &v.Block.Func.Config.Types 145 ptrType := types.BytePtr 146 lenType := types.Int 147 148 ptr := v.Block.NewValue0(v.Pos, OpPhi, ptrType) 149 len := v.Block.NewValue0(v.Pos, OpPhi, lenType) 150 for _, a := range v.Args { 151 ptr.AddArg(a.Block.NewValue1(v.Pos, OpStringPtr, ptrType, a)) 152 len.AddArg(a.Block.NewValue1(v.Pos, OpStringLen, lenType, a)) 153 } 154 v.reset(OpStringMake) 155 v.AddArg(ptr) 156 v.AddArg(len) 157 } 158 159 func decomposeSlicePhi(v *Value) { 160 types := &v.Block.Func.Config.Types 161 ptrType := v.Type.Elem().PtrTo() 162 lenType := types.Int 163 164 ptr := v.Block.NewValue0(v.Pos, OpPhi, ptrType) 165 len := v.Block.NewValue0(v.Pos, OpPhi, lenType) 166 cap := v.Block.NewValue0(v.Pos, OpPhi, lenType) 167 for _, a := range v.Args { 168 ptr.AddArg(a.Block.NewValue1(v.Pos, OpSlicePtr, ptrType, a)) 169 len.AddArg(a.Block.NewValue1(v.Pos, OpSliceLen, lenType, a)) 170 cap.AddArg(a.Block.NewValue1(v.Pos, OpSliceCap, lenType, a)) 171 } 172 v.reset(OpSliceMake) 173 v.AddArg(ptr) 174 v.AddArg(len) 175 v.AddArg(cap) 176 } 177 178 func decomposeInt64Phi(v *Value) { 179 cfgtypes := &v.Block.Func.Config.Types 180 var partType *types.Type 181 if v.Type.IsSigned() { 182 partType = cfgtypes.Int32 183 } else { 184 partType = cfgtypes.UInt32 185 } 186 187 hi := v.Block.NewValue0(v.Pos, OpPhi, partType) 188 lo := v.Block.NewValue0(v.Pos, OpPhi, cfgtypes.UInt32) 189 for _, a := range v.Args { 190 hi.AddArg(a.Block.NewValue1(v.Pos, OpInt64Hi, partType, a)) 191 lo.AddArg(a.Block.NewValue1(v.Pos, OpInt64Lo, cfgtypes.UInt32, a)) 192 } 193 v.reset(OpInt64Make) 194 v.AddArg(hi) 195 v.AddArg(lo) 196 } 197 198 func decomposeComplexPhi(v *Value) { 199 cfgtypes := &v.Block.Func.Config.Types 200 var partType *types.Type 201 switch z := v.Type.Size(); z { 202 case 8: 203 partType = cfgtypes.Float32 204 case 16: 205 partType = cfgtypes.Float64 206 default: 207 v.Fatalf("decomposeComplexPhi: bad complex size %d", z) 208 } 209 210 real := v.Block.NewValue0(v.Pos, OpPhi, partType) 211 imag := v.Block.NewValue0(v.Pos, OpPhi, partType) 212 for _, a := range v.Args { 213 real.AddArg(a.Block.NewValue1(v.Pos, OpComplexReal, partType, a)) 214 imag.AddArg(a.Block.NewValue1(v.Pos, OpComplexImag, partType, a)) 215 } 216 v.reset(OpComplexMake) 217 v.AddArg(real) 218 v.AddArg(imag) 219 } 220 221 func decomposeInterfacePhi(v *Value) { 222 uintptrType := v.Block.Func.Config.Types.Uintptr 223 ptrType := v.Block.Func.Config.Types.BytePtr 224 225 itab := v.Block.NewValue0(v.Pos, OpPhi, uintptrType) 226 data := v.Block.NewValue0(v.Pos, OpPhi, ptrType) 227 for _, a := range v.Args { 228 itab.AddArg(a.Block.NewValue1(v.Pos, OpITab, uintptrType, a)) 229 data.AddArg(a.Block.NewValue1(v.Pos, OpIData, ptrType, a)) 230 } 231 v.reset(OpIMake) 232 v.AddArg(itab) 233 v.AddArg(data) 234 } 235 236 func decomposeUser(f *Func) { 237 for _, b := range f.Blocks { 238 for _, v := range b.Values { 239 if v.Op != OpPhi { 240 continue 241 } 242 decomposeUserPhi(v) 243 } 244 } 245 // Split up named values into their components. 246 i := 0 247 var newNames []*LocalSlot 248 for _, name := range f.Names { 249 t := name.Type 250 switch { 251 case t.IsStruct(): 252 newNames = decomposeUserStructInto(f, name, newNames) 253 case t.IsArray(): 254 newNames = decomposeUserArrayInto(f, name, newNames) 255 default: 256 f.Names[i] = name 257 i++ 258 } 259 } 260 f.Names = f.Names[:i] 261 f.Names = append(f.Names, newNames...) 262 } 263 264 // decomposeUserArrayInto creates names for the element(s) of arrays referenced 265 // by name where possible, and appends those new names to slots, which is then 266 // returned. 267 func decomposeUserArrayInto(f *Func, name *LocalSlot, slots []*LocalSlot) []*LocalSlot { 268 t := name.Type 269 if t.NumElem() == 0 { 270 // TODO(khr): Not sure what to do here. Probably nothing. 271 // Names for empty arrays aren't important. 272 return slots 273 } 274 if t.NumElem() != 1 { 275 // shouldn't get here due to CanSSA 276 f.Fatalf("array not of size 1") 277 } 278 elemName := f.SplitArray(name) 279 var keep []*Value 280 for _, v := range f.NamedValues[*name] { 281 if v.Op != OpArrayMake1 { 282 keep = append(keep, v) 283 continue 284 } 285 f.NamedValues[*elemName] = append(f.NamedValues[*elemName], v.Args[0]) 286 } 287 if len(keep) == 0 { 288 // delete the name for the array as a whole 289 delete(f.NamedValues, *name) 290 } else { 291 f.NamedValues[*name] = keep 292 } 293 294 if t.Elem().IsArray() { 295 return decomposeUserArrayInto(f, elemName, slots) 296 } else if t.Elem().IsStruct() { 297 return decomposeUserStructInto(f, elemName, slots) 298 } 299 300 return append(slots, elemName) 301 } 302 303 // decomposeUserStructInto creates names for the fields(s) of structs referenced 304 // by name where possible, and appends those new names to slots, which is then 305 // returned. 306 func decomposeUserStructInto(f *Func, name *LocalSlot, slots []*LocalSlot) []*LocalSlot { 307 fnames := []*LocalSlot{} // slots for struct in name 308 t := name.Type 309 n := t.NumFields() 310 311 for i := 0; i < n; i++ { 312 fs := f.SplitStruct(name, i) 313 fnames = append(fnames, fs) 314 // arrays and structs will be decomposed further, so 315 // there's no need to record a name 316 if !fs.Type.IsArray() && !fs.Type.IsStruct() { 317 slots = maybeAppend(f, slots, fs) 318 } 319 } 320 321 makeOp := StructMakeOp(n) 322 var keep []*Value 323 // create named values for each struct field 324 for _, v := range f.NamedValues[*name] { 325 if v.Op != makeOp { 326 keep = append(keep, v) 327 continue 328 } 329 for i := 0; i < len(fnames); i++ { 330 f.NamedValues[*fnames[i]] = append(f.NamedValues[*fnames[i]], v.Args[i]) 331 } 332 } 333 if len(keep) == 0 { 334 // delete the name for the struct as a whole 335 delete(f.NamedValues, *name) 336 } else { 337 f.NamedValues[*name] = keep 338 } 339 340 // now that this f.NamedValues contains values for the struct 341 // fields, recurse into nested structs 342 for i := 0; i < n; i++ { 343 if name.Type.FieldType(i).IsStruct() { 344 slots = decomposeUserStructInto(f, fnames[i], slots) 345 delete(f.NamedValues, *fnames[i]) 346 } else if name.Type.FieldType(i).IsArray() { 347 slots = decomposeUserArrayInto(f, fnames[i], slots) 348 delete(f.NamedValues, *fnames[i]) 349 } 350 } 351 return slots 352 } 353 func decomposeUserPhi(v *Value) { 354 switch { 355 case v.Type.IsStruct(): 356 decomposeStructPhi(v) 357 case v.Type.IsArray(): 358 decomposeArrayPhi(v) 359 } 360 } 361 362 // decomposeStructPhi replaces phi-of-struct with structmake(phi-for-each-field), 363 // and then recursively decomposes the phis for each field. 364 func decomposeStructPhi(v *Value) { 365 t := v.Type 366 n := t.NumFields() 367 var fields [MaxStruct]*Value 368 for i := 0; i < n; i++ { 369 fields[i] = v.Block.NewValue0(v.Pos, OpPhi, t.FieldType(i)) 370 } 371 for _, a := range v.Args { 372 for i := 0; i < n; i++ { 373 fields[i].AddArg(a.Block.NewValue1I(v.Pos, OpStructSelect, t.FieldType(i), int64(i), a)) 374 } 375 } 376 v.reset(StructMakeOp(n)) 377 v.AddArgs(fields[:n]...) 378 379 // Recursively decompose phis for each field. 380 for _, f := range fields[:n] { 381 decomposeUserPhi(f) 382 } 383 } 384 385 // decomposeArrayPhi replaces phi-of-array with arraymake(phi-of-array-element), 386 // and then recursively decomposes the element phi. 387 func decomposeArrayPhi(v *Value) { 388 t := v.Type 389 if t.NumElem() == 0 { 390 v.reset(OpArrayMake0) 391 return 392 } 393 if t.NumElem() != 1 { 394 v.Fatalf("SSAable array must have no more than 1 element") 395 } 396 elem := v.Block.NewValue0(v.Pos, OpPhi, t.Elem()) 397 for _, a := range v.Args { 398 elem.AddArg(a.Block.NewValue1I(v.Pos, OpArraySelect, t.Elem(), 0, a)) 399 } 400 v.reset(OpArrayMake1) 401 v.AddArg(elem) 402 403 // Recursively decompose elem phi. 404 decomposeUserPhi(elem) 405 } 406 407 // MaxStruct is the maximum number of fields a struct 408 // can have and still be SSAable. 409 const MaxStruct = 4 410 411 // StructMakeOp returns the opcode to construct a struct with the 412 // given number of fields. 413 func StructMakeOp(nf int) Op { 414 switch nf { 415 case 0: 416 return OpStructMake0 417 case 1: 418 return OpStructMake1 419 case 2: 420 return OpStructMake2 421 case 3: 422 return OpStructMake3 423 case 4: 424 return OpStructMake4 425 } 426 panic("too many fields in an SSAable struct") 427 } 428 429 type namedVal struct { 430 locIndex, valIndex int // f.NamedValues[f.Names[locIndex]][valIndex] = key 431 } 432 433 // deleteNamedVals removes particular values with debugger names from f's naming data structures, 434 // removes all values with OpInvalid, and re-sorts the list of Names. 435 func deleteNamedVals(f *Func, toDelete []namedVal) { 436 // Arrange to delete from larger indices to smaller, to ensure swap-with-end deletion does not invalidate pending indices. 437 sort.Slice(toDelete, func(i, j int) bool { 438 if toDelete[i].locIndex != toDelete[j].locIndex { 439 return toDelete[i].locIndex > toDelete[j].locIndex 440 } 441 return toDelete[i].valIndex > toDelete[j].valIndex 442 443 }) 444 445 // Get rid of obsolete names 446 for _, d := range toDelete { 447 loc := f.Names[d.locIndex] 448 vals := f.NamedValues[*loc] 449 l := len(vals) - 1 450 if l > 0 { 451 vals[d.valIndex] = vals[l] 452 } 453 vals[l] = nil 454 f.NamedValues[*loc] = vals[:l] 455 } 456 // Delete locations with no values attached. 457 end := len(f.Names) 458 for i := len(f.Names) - 1; i >= 0; i-- { 459 loc := f.Names[i] 460 vals := f.NamedValues[*loc] 461 last := len(vals) 462 for j := len(vals) - 1; j >= 0; j-- { 463 if vals[j].Op == OpInvalid { 464 last-- 465 vals[j] = vals[last] 466 vals[last] = nil 467 } 468 } 469 if last < len(vals) { 470 f.NamedValues[*loc] = vals[:last] 471 } 472 if len(vals) == 0 { 473 delete(f.NamedValues, *loc) 474 end-- 475 f.Names[i] = f.Names[end] 476 f.Names[end] = nil 477 } 478 } 479 f.Names = f.Names[:end] 480 }