github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/ssa/expand_calls.go (about) 1 // Copyright 2020 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 10 "github.com/go-asm/go/cmd/compile/abi" 11 "github.com/go-asm/go/cmd/compile/base" 12 "github.com/go-asm/go/cmd/compile/ir" 13 "github.com/go-asm/go/cmd/compile/types" 14 "github.com/go-asm/go/cmd/src" 15 ) 16 17 func postExpandCallsDecompose(f *Func) { 18 decomposeUser(f) // redo user decompose to cleanup after expand calls 19 decomposeBuiltIn(f) // handles both regular decomposition and cleanup. 20 } 21 22 func expandCalls(f *Func) { 23 // Convert each aggregate arg to a call into "dismantle aggregate, store/pass parts" 24 // Convert each aggregate result from a call into "assemble aggregate from parts" 25 // Convert each multivalue exit into "dismantle aggregate, store/return parts" 26 // Convert incoming aggregate arg into assembly of parts. 27 // Feed modified AST to decompose. 28 29 sp, _ := f.spSb() 30 31 x := &expandState{ 32 f: f, 33 debug: f.pass.debug, 34 regSize: f.Config.RegSize, 35 sp: sp, 36 typs: &f.Config.Types, 37 wideSelects: make(map[*Value]*Value), 38 commonArgs: make(map[selKey]*Value), 39 commonSelectors: make(map[selKey]*Value), 40 memForCall: make(map[ID]*Value), 41 } 42 43 // For 32-bit, need to deal with decomposition of 64-bit integers, which depends on endianness. 44 if f.Config.BigEndian { 45 x.firstOp = OpInt64Hi 46 x.secondOp = OpInt64Lo 47 x.firstType = x.typs.Int32 48 x.secondType = x.typs.UInt32 49 } else { 50 x.firstOp = OpInt64Lo 51 x.secondOp = OpInt64Hi 52 x.firstType = x.typs.UInt32 53 x.secondType = x.typs.Int32 54 } 55 56 // Defer select processing until after all calls and selects are seen. 57 var selects []*Value 58 var calls []*Value 59 var args []*Value 60 var exitBlocks []*Block 61 62 var m0 *Value 63 64 // Accumulate lists of calls, args, selects, and exit blocks to process, 65 // note "wide" selects consumed by stores, 66 // rewrite mem for each call, 67 // rewrite each OpSelectNAddr. 68 for _, b := range f.Blocks { 69 for _, v := range b.Values { 70 switch v.Op { 71 case OpInitMem: 72 m0 = v 73 74 case OpClosureLECall, OpInterLECall, OpStaticLECall, OpTailLECall: 75 calls = append(calls, v) 76 77 case OpArg: 78 args = append(args, v) 79 80 case OpStore: 81 if a := v.Args[1]; a.Op == OpSelectN && !CanSSA(a.Type) { 82 if a.Uses > 1 { 83 panic(fmt.Errorf("Saw double use of wide SelectN %s operand of Store %s", 84 a.LongString(), v.LongString())) 85 } 86 x.wideSelects[a] = v 87 } 88 89 case OpSelectN: 90 if v.Type == types.TypeMem { 91 // rewrite the mem selector in place 92 call := v.Args[0] 93 aux := call.Aux.(*AuxCall) 94 mem := x.memForCall[call.ID] 95 if mem == nil { 96 v.AuxInt = int64(aux.abiInfo.OutRegistersUsed()) 97 x.memForCall[call.ID] = v 98 } else { 99 panic(fmt.Errorf("Saw two memories for call %v, %v and %v", call, mem, v)) 100 } 101 } else { 102 selects = append(selects, v) 103 } 104 105 case OpSelectNAddr: 106 call := v.Args[0] 107 which := v.AuxInt 108 aux := call.Aux.(*AuxCall) 109 pt := v.Type 110 off := x.offsetFrom(x.f.Entry, x.sp, aux.OffsetOfResult(which), pt) 111 v.copyOf(off) 112 } 113 } 114 115 // rewrite function results from an exit block 116 // values returned by function need to be split out into registers. 117 if isBlockMultiValueExit(b) { 118 exitBlocks = append(exitBlocks, b) 119 } 120 } 121 122 // Convert each aggregate arg into Make of its parts (and so on, to primitive types) 123 for _, v := range args { 124 var rc registerCursor 125 a := x.prAssignForArg(v) 126 aux := x.f.OwnAux 127 regs := a.Registers 128 var offset int64 129 if len(regs) == 0 { 130 offset = a.FrameOffset(aux.abiInfo) 131 } 132 auxBase := x.offsetFrom(x.f.Entry, x.sp, offset, types.NewPtr(v.Type)) 133 rc.init(regs, aux.abiInfo, nil, auxBase, 0) 134 x.rewriteSelectOrArg(f.Entry.Pos, f.Entry, v, v, m0, v.Type, rc) 135 } 136 137 // Rewrite selects of results (which may be aggregates) into make-aggregates of register/memory-targeted selects 138 for _, v := range selects { 139 if v.Op == OpInvalid { 140 continue 141 } 142 143 call := v.Args[0] 144 aux := call.Aux.(*AuxCall) 145 mem := x.memForCall[call.ID] 146 if mem == nil { 147 mem = call.Block.NewValue1I(call.Pos, OpSelectN, types.TypeMem, int64(aux.abiInfo.OutRegistersUsed()), call) 148 x.memForCall[call.ID] = mem 149 } 150 151 i := v.AuxInt 152 regs := aux.RegsOfResult(i) 153 154 // If this select cannot fit into SSA and is stored, either disaggregate to register stores, or mem-mem move. 155 if store := x.wideSelects[v]; store != nil { 156 // Use the mem that comes from the store operation. 157 storeAddr := store.Args[0] 158 mem := store.Args[2] 159 if len(regs) > 0 { 160 // Cannot do a rewrite that builds up a result from pieces; instead, copy pieces to the store operation. 161 var rc registerCursor 162 rc.init(regs, aux.abiInfo, nil, storeAddr, 0) 163 mem = x.rewriteWideSelectToStores(call.Pos, call.Block, v, mem, v.Type, rc) 164 store.copyOf(mem) 165 } else { 166 // Move directly from AuxBase to store target; rewrite the store instruction. 167 offset := aux.OffsetOfResult(i) 168 auxBase := x.offsetFrom(x.f.Entry, x.sp, offset, types.NewPtr(v.Type)) 169 // was Store dst, v, mem 170 // now Move dst, auxBase, mem 171 move := store.Block.NewValue3A(store.Pos, OpMove, types.TypeMem, v.Type, storeAddr, auxBase, mem) 172 move.AuxInt = v.Type.Size() 173 store.copyOf(move) 174 } 175 continue 176 } 177 178 var auxBase *Value 179 if len(regs) == 0 { 180 offset := aux.OffsetOfResult(i) 181 auxBase = x.offsetFrom(x.f.Entry, x.sp, offset, types.NewPtr(v.Type)) 182 } 183 var rc registerCursor 184 rc.init(regs, aux.abiInfo, nil, auxBase, 0) 185 x.rewriteSelectOrArg(call.Pos, call.Block, v, v, mem, v.Type, rc) 186 } 187 188 rewriteCall := func(v *Value, newOp Op, argStart int) { 189 // Break aggregate args passed to call into smaller pieces. 190 x.rewriteCallArgs(v, argStart) 191 v.Op = newOp 192 rts := abi.RegisterTypes(v.Aux.(*AuxCall).abiInfo.OutParams()) 193 v.Type = types.NewResults(append(rts, types.TypeMem)) 194 } 195 196 // Rewrite calls 197 for _, v := range calls { 198 switch v.Op { 199 case OpStaticLECall: 200 rewriteCall(v, OpStaticCall, 0) 201 case OpTailLECall: 202 rewriteCall(v, OpTailCall, 0) 203 case OpClosureLECall: 204 rewriteCall(v, OpClosureCall, 2) 205 case OpInterLECall: 206 rewriteCall(v, OpInterCall, 1) 207 } 208 } 209 210 // Rewrite results from exit blocks 211 for _, b := range exitBlocks { 212 v := b.Controls[0] 213 x.rewriteFuncResults(v, b, f.OwnAux) 214 b.SetControl(v) 215 } 216 217 } 218 219 func (x *expandState) rewriteFuncResults(v *Value, b *Block, aux *AuxCall) { 220 // This is very similar to rewriteCallArgs 221 // differences: 222 // firstArg + preArgs 223 // sp vs auxBase 224 225 m0 := v.MemoryArg() 226 mem := m0 227 228 allResults := []*Value{} 229 var oldArgs []*Value 230 argsWithoutMem := v.Args[:len(v.Args)-1] 231 232 for j, a := range argsWithoutMem { 233 oldArgs = append(oldArgs, a) 234 i := int64(j) 235 auxType := aux.TypeOfResult(i) 236 auxBase := b.NewValue2A(v.Pos, OpLocalAddr, types.NewPtr(auxType), aux.NameOfResult(i), x.sp, mem) 237 auxOffset := int64(0) 238 aRegs := aux.RegsOfResult(int64(j)) 239 if a.Op == OpDereference { 240 a.Op = OpLoad 241 } 242 var rc registerCursor 243 var result *[]*Value 244 if len(aRegs) > 0 { 245 result = &allResults 246 } else { 247 if a.Op == OpLoad && a.Args[0].Op == OpLocalAddr { 248 addr := a.Args[0] 249 if addr.MemoryArg() == a.MemoryArg() && addr.Aux == aux.NameOfResult(i) { 250 continue // Self move to output parameter 251 } 252 } 253 } 254 rc.init(aRegs, aux.abiInfo, result, auxBase, auxOffset) 255 mem = x.decomposeAsNecessary(v.Pos, b, a, mem, rc) 256 } 257 v.resetArgs() 258 v.AddArgs(allResults...) 259 v.AddArg(mem) 260 for _, a := range oldArgs { 261 if a.Uses == 0 { 262 if x.debug > 1 { 263 x.Printf("...marking %v unused\n", a.LongString()) 264 } 265 x.invalidateRecursively(a) 266 } 267 } 268 v.Type = types.NewResults(append(abi.RegisterTypes(aux.abiInfo.OutParams()), types.TypeMem)) 269 return 270 } 271 272 func (x *expandState) rewriteCallArgs(v *Value, firstArg int) { 273 if x.debug > 1 { 274 x.indent(3) 275 defer x.indent(-3) 276 x.Printf("rewriteCallArgs(%s; %d)\n", v.LongString(), firstArg) 277 } 278 // Thread the stores on the memory arg 279 aux := v.Aux.(*AuxCall) 280 m0 := v.MemoryArg() 281 mem := m0 282 allResults := []*Value{} 283 oldArgs := []*Value{} 284 argsWithoutMem := v.Args[firstArg : len(v.Args)-1] // Also strip closure/interface Op-specific args 285 286 sp := x.sp 287 if v.Op == OpTailLECall { 288 // For tail call, we unwind the frame before the call so we'll use the caller's 289 // SP. 290 sp = x.f.Entry.NewValue1(src.NoXPos, OpGetCallerSP, x.typs.Uintptr, mem) 291 } 292 293 for i, a := range argsWithoutMem { // skip leading non-parameter SSA Args and trailing mem SSA Arg. 294 oldArgs = append(oldArgs, a) 295 auxI := int64(i) 296 aRegs := aux.RegsOfArg(auxI) 297 aType := aux.TypeOfArg(auxI) 298 299 if a.Op == OpDereference { 300 a.Op = OpLoad 301 } 302 var rc registerCursor 303 var result *[]*Value 304 var aOffset int64 305 if len(aRegs) > 0 { 306 result = &allResults 307 } else { 308 aOffset = aux.OffsetOfArg(auxI) 309 } 310 if v.Op == OpTailLECall && a.Op == OpArg && a.AuxInt == 0 { 311 // It's common for a tail call passing the same arguments (e.g. method wrapper), 312 // so this would be a self copy. Detect this and optimize it out. 313 n := a.Aux.(*ir.Name) 314 if n.Class == ir.PPARAM && n.FrameOffset()+x.f.Config.ctxt.Arch.FixedFrameSize == aOffset { 315 continue 316 } 317 } 318 if x.debug > 1 { 319 x.Printf("...storeArg %s, %v, %d\n", a.LongString(), aType, aOffset) 320 } 321 322 rc.init(aRegs, aux.abiInfo, result, sp, aOffset) 323 mem = x.decomposeAsNecessary(v.Pos, v.Block, a, mem, rc) 324 } 325 var preArgStore [2]*Value 326 preArgs := append(preArgStore[:0], v.Args[0:firstArg]...) 327 v.resetArgs() 328 v.AddArgs(preArgs...) 329 v.AddArgs(allResults...) 330 v.AddArg(mem) 331 for _, a := range oldArgs { 332 if a.Uses == 0 { 333 x.invalidateRecursively(a) 334 } 335 } 336 337 return 338 } 339 340 func (x *expandState) decomposePair(pos src.XPos, b *Block, a, mem *Value, t0, t1 *types.Type, o0, o1 Op, rc *registerCursor) *Value { 341 e := b.NewValue1(pos, o0, t0, a) 342 pos = pos.WithNotStmt() 343 mem = x.decomposeAsNecessary(pos, b, e, mem, rc.next(t0)) 344 e = b.NewValue1(pos, o1, t1, a) 345 mem = x.decomposeAsNecessary(pos, b, e, mem, rc.next(t1)) 346 return mem 347 } 348 349 func (x *expandState) decomposeOne(pos src.XPos, b *Block, a, mem *Value, t0 *types.Type, o0 Op, rc *registerCursor) *Value { 350 e := b.NewValue1(pos, o0, t0, a) 351 pos = pos.WithNotStmt() 352 mem = x.decomposeAsNecessary(pos, b, e, mem, rc.next(t0)) 353 return mem 354 } 355 356 // decomposeAsNecessary converts a value (perhaps an aggregate) passed to a call or returned by a function, 357 // into the appropriate sequence of stores and register assignments to transmit that value in a given ABI, and 358 // returns the current memory after this convert/rewrite (it may be the input memory, perhaps stores were needed.) 359 // 'pos' is the source position all this is tied to 360 // 'b' is the enclosing block 361 // 'a' is the value to decompose 362 // 'm0' is the input memory arg used for the first store (or returned if there are no stores) 363 // 'rc' is a registerCursor which identifies the register/memory destination for the value 364 func (x *expandState) decomposeAsNecessary(pos src.XPos, b *Block, a, m0 *Value, rc registerCursor) *Value { 365 if x.debug > 1 { 366 x.indent(3) 367 defer x.indent(-3) 368 } 369 at := a.Type 370 if at.Size() == 0 { 371 return m0 372 } 373 if a.Op == OpDereference { 374 a.Op = OpLoad // For purposes of parameter passing expansion, a Dereference is a Load. 375 } 376 377 if !rc.hasRegs() && !CanSSA(at) { 378 dst := x.offsetFrom(b, rc.storeDest, rc.storeOffset, types.NewPtr(at)) 379 if x.debug > 1 { 380 x.Printf("...recur store %s at %s\n", a.LongString(), dst.LongString()) 381 } 382 if a.Op == OpLoad { 383 m0 = b.NewValue3A(pos, OpMove, types.TypeMem, at, dst, a.Args[0], m0) 384 m0.AuxInt = at.Size() 385 return m0 386 } else { 387 panic(fmt.Errorf("Store of not a load")) 388 } 389 } 390 391 mem := m0 392 switch at.Kind() { 393 case types.TARRAY: 394 et := at.Elem() 395 for i := int64(0); i < at.NumElem(); i++ { 396 e := b.NewValue1I(pos, OpArraySelect, et, i, a) 397 pos = pos.WithNotStmt() 398 mem = x.decomposeAsNecessary(pos, b, e, mem, rc.next(et)) 399 } 400 return mem 401 402 case types.TSTRUCT: 403 for i := 0; i < at.NumFields(); i++ { 404 et := at.Field(i).Type // might need to read offsets from the fields 405 e := b.NewValue1I(pos, OpStructSelect, et, int64(i), a) 406 pos = pos.WithNotStmt() 407 if x.debug > 1 { 408 x.Printf("...recur decompose %s, %v\n", e.LongString(), et) 409 } 410 mem = x.decomposeAsNecessary(pos, b, e, mem, rc.next(et)) 411 } 412 return mem 413 414 case types.TSLICE: 415 mem = x.decomposeOne(pos, b, a, mem, at.Elem().PtrTo(), OpSlicePtr, &rc) 416 pos = pos.WithNotStmt() 417 mem = x.decomposeOne(pos, b, a, mem, x.typs.Int, OpSliceLen, &rc) 418 return x.decomposeOne(pos, b, a, mem, x.typs.Int, OpSliceCap, &rc) 419 420 case types.TSTRING: 421 return x.decomposePair(pos, b, a, mem, x.typs.BytePtr, x.typs.Int, OpStringPtr, OpStringLen, &rc) 422 423 case types.TINTER: 424 mem = x.decomposeOne(pos, b, a, mem, x.typs.Uintptr, OpITab, &rc) 425 pos = pos.WithNotStmt() 426 // Immediate interfaces cause so many headaches. 427 if a.Op == OpIMake { 428 data := a.Args[1] 429 for data.Op == OpStructMake1 || data.Op == OpArrayMake1 { 430 data = data.Args[0] 431 } 432 return x.decomposeAsNecessary(pos, b, data, mem, rc.next(data.Type)) 433 } 434 return x.decomposeOne(pos, b, a, mem, x.typs.BytePtr, OpIData, &rc) 435 436 case types.TCOMPLEX64: 437 return x.decomposePair(pos, b, a, mem, x.typs.Float32, x.typs.Float32, OpComplexReal, OpComplexImag, &rc) 438 439 case types.TCOMPLEX128: 440 return x.decomposePair(pos, b, a, mem, x.typs.Float64, x.typs.Float64, OpComplexReal, OpComplexImag, &rc) 441 442 case types.TINT64: 443 if at.Size() > x.regSize { 444 return x.decomposePair(pos, b, a, mem, x.firstType, x.secondType, x.firstOp, x.secondOp, &rc) 445 } 446 case types.TUINT64: 447 if at.Size() > x.regSize { 448 return x.decomposePair(pos, b, a, mem, x.typs.UInt32, x.typs.UInt32, x.firstOp, x.secondOp, &rc) 449 } 450 } 451 452 // An atomic type, either record the register or store it and update the memory. 453 454 if rc.hasRegs() { 455 if x.debug > 1 { 456 x.Printf("...recur addArg %s\n", a.LongString()) 457 } 458 rc.addArg(a) 459 } else { 460 dst := x.offsetFrom(b, rc.storeDest, rc.storeOffset, types.NewPtr(at)) 461 if x.debug > 1 { 462 x.Printf("...recur store %s at %s\n", a.LongString(), dst.LongString()) 463 } 464 mem = b.NewValue3A(pos, OpStore, types.TypeMem, at, dst, a, mem) 465 } 466 467 return mem 468 } 469 470 // Convert scalar OpArg into the proper OpWhateverArg instruction 471 // Convert scalar OpSelectN into perhaps-differently-indexed OpSelectN 472 // Convert aggregate OpArg into Make of its parts (which are eventually scalars) 473 // Convert aggregate OpSelectN into Make of its parts (which are eventually scalars) 474 // Returns the converted value. 475 // 476 // - "pos" the position for any generated instructions 477 // - "b" the block for any generated instructions 478 // - "container" the outermost OpArg/OpSelectN 479 // - "a" the instruction to overwrite, if any (only the outermost caller) 480 // - "m0" the memory arg for any loads that are necessary 481 // - "at" the type of the Arg/part 482 // - "rc" the register/memory cursor locating the various parts of the Arg. 483 func (x *expandState) rewriteSelectOrArg(pos src.XPos, b *Block, container, a, m0 *Value, at *types.Type, rc registerCursor) *Value { 484 485 if at == types.TypeMem { 486 a.copyOf(m0) 487 return a 488 } 489 490 makeOf := func(a *Value, op Op, args []*Value) *Value { 491 if a == nil { 492 a = b.NewValue0(pos, op, at) 493 a.AddArgs(args...) 494 } else { 495 a.resetArgs() 496 a.Aux, a.AuxInt = nil, 0 497 a.Pos, a.Op, a.Type = pos, op, at 498 a.AddArgs(args...) 499 } 500 return a 501 } 502 503 if at.Size() == 0 { 504 // For consistency, create these values even though they'll ultimately be unused 505 if at.IsArray() { 506 return makeOf(a, OpArrayMake0, nil) 507 } 508 if at.IsStruct() { 509 return makeOf(a, OpStructMake0, nil) 510 } 511 return a 512 } 513 514 sk := selKey{from: container, size: 0, offsetOrIndex: rc.storeOffset, typ: at} 515 dupe := x.commonSelectors[sk] 516 if dupe != nil { 517 if a == nil { 518 return dupe 519 } 520 a.copyOf(dupe) 521 return a 522 } 523 524 var argStore [10]*Value 525 args := argStore[:0] 526 527 addArg := func(a0 *Value) { 528 if a0 == nil { 529 as := "<nil>" 530 if a != nil { 531 as = a.LongString() 532 } 533 panic(fmt.Errorf("a0 should not be nil, a=%v, container=%v, at=%v", as, container.LongString(), at)) 534 } 535 args = append(args, a0) 536 } 537 538 switch at.Kind() { 539 case types.TARRAY: 540 et := at.Elem() 541 for i := int64(0); i < at.NumElem(); i++ { 542 e := x.rewriteSelectOrArg(pos, b, container, nil, m0, et, rc.next(et)) 543 addArg(e) 544 } 545 a = makeOf(a, OpArrayMake1, args) 546 x.commonSelectors[sk] = a 547 return a 548 549 case types.TSTRUCT: 550 // Assume ssagen/ssa.go (in buildssa) spills large aggregates so they won't appear here. 551 for i := 0; i < at.NumFields(); i++ { 552 et := at.Field(i).Type 553 e := x.rewriteSelectOrArg(pos, b, container, nil, m0, et, rc.next(et)) 554 if e == nil { 555 panic(fmt.Errorf("nil e, et=%v, et.Size()=%d, i=%d", et, et.Size(), i)) 556 } 557 addArg(e) 558 pos = pos.WithNotStmt() 559 } 560 if at.NumFields() > 4 { 561 panic(fmt.Errorf("Too many fields (%d, %d bytes), container=%s", at.NumFields(), at.Size(), container.LongString())) 562 } 563 a = makeOf(a, StructMakeOp(at.NumFields()), args) 564 x.commonSelectors[sk] = a 565 return a 566 567 case types.TSLICE: 568 addArg(x.rewriteSelectOrArg(pos, b, container, nil, m0, at.Elem().PtrTo(), rc.next(x.typs.BytePtr))) 569 pos = pos.WithNotStmt() 570 addArg(x.rewriteSelectOrArg(pos, b, container, nil, m0, x.typs.Int, rc.next(x.typs.Int))) 571 addArg(x.rewriteSelectOrArg(pos, b, container, nil, m0, x.typs.Int, rc.next(x.typs.Int))) 572 a = makeOf(a, OpSliceMake, args) 573 x.commonSelectors[sk] = a 574 return a 575 576 case types.TSTRING: 577 addArg(x.rewriteSelectOrArg(pos, b, container, nil, m0, x.typs.BytePtr, rc.next(x.typs.BytePtr))) 578 pos = pos.WithNotStmt() 579 addArg(x.rewriteSelectOrArg(pos, b, container, nil, m0, x.typs.Int, rc.next(x.typs.Int))) 580 a = makeOf(a, OpStringMake, args) 581 x.commonSelectors[sk] = a 582 return a 583 584 case types.TINTER: 585 addArg(x.rewriteSelectOrArg(pos, b, container, nil, m0, x.typs.Uintptr, rc.next(x.typs.Uintptr))) 586 pos = pos.WithNotStmt() 587 addArg(x.rewriteSelectOrArg(pos, b, container, nil, m0, x.typs.BytePtr, rc.next(x.typs.BytePtr))) 588 a = makeOf(a, OpIMake, args) 589 x.commonSelectors[sk] = a 590 return a 591 592 case types.TCOMPLEX64: 593 addArg(x.rewriteSelectOrArg(pos, b, container, nil, m0, x.typs.Float32, rc.next(x.typs.Float32))) 594 pos = pos.WithNotStmt() 595 addArg(x.rewriteSelectOrArg(pos, b, container, nil, m0, x.typs.Float32, rc.next(x.typs.Float32))) 596 a = makeOf(a, OpComplexMake, args) 597 x.commonSelectors[sk] = a 598 return a 599 600 case types.TCOMPLEX128: 601 addArg(x.rewriteSelectOrArg(pos, b, container, nil, m0, x.typs.Float64, rc.next(x.typs.Float64))) 602 pos = pos.WithNotStmt() 603 addArg(x.rewriteSelectOrArg(pos, b, container, nil, m0, x.typs.Float64, rc.next(x.typs.Float64))) 604 a = makeOf(a, OpComplexMake, args) 605 x.commonSelectors[sk] = a 606 return a 607 608 case types.TINT64: 609 if at.Size() > x.regSize { 610 addArg(x.rewriteSelectOrArg(pos, b, container, nil, m0, x.firstType, rc.next(x.firstType))) 611 pos = pos.WithNotStmt() 612 addArg(x.rewriteSelectOrArg(pos, b, container, nil, m0, x.secondType, rc.next(x.secondType))) 613 if !x.f.Config.BigEndian { 614 // Int64Make args are big, little 615 args[0], args[1] = args[1], args[0] 616 } 617 a = makeOf(a, OpInt64Make, args) 618 x.commonSelectors[sk] = a 619 return a 620 } 621 case types.TUINT64: 622 if at.Size() > x.regSize { 623 addArg(x.rewriteSelectOrArg(pos, b, container, nil, m0, x.typs.UInt32, rc.next(x.typs.UInt32))) 624 pos = pos.WithNotStmt() 625 addArg(x.rewriteSelectOrArg(pos, b, container, nil, m0, x.typs.UInt32, rc.next(x.typs.UInt32))) 626 if !x.f.Config.BigEndian { 627 // Int64Make args are big, little 628 args[0], args[1] = args[1], args[0] 629 } 630 a = makeOf(a, OpInt64Make, args) 631 x.commonSelectors[sk] = a 632 return a 633 } 634 } 635 636 // An atomic type, either record the register or store it and update the memory. 637 638 // Depending on the container Op, the leaves are either OpSelectN or OpArg{Int,Float}Reg 639 640 if container.Op == OpArg { 641 if rc.hasRegs() { 642 op, i := rc.ArgOpAndRegisterFor() 643 name := container.Aux.(*ir.Name) 644 a = makeOf(a, op, nil) 645 a.AuxInt = i 646 a.Aux = &AuxNameOffset{name, rc.storeOffset} 647 } else { 648 key := selKey{container, rc.storeOffset, at.Size(), at} 649 w := x.commonArgs[key] 650 if w != nil && w.Uses != 0 { 651 if a == nil { 652 a = w 653 } else { 654 a.copyOf(w) 655 } 656 } else { 657 if a == nil { 658 aux := container.Aux 659 auxInt := container.AuxInt + rc.storeOffset 660 a = container.Block.NewValue0IA(container.Pos, OpArg, at, auxInt, aux) 661 } else { 662 // do nothing, the original should be okay. 663 } 664 x.commonArgs[key] = a 665 } 666 } 667 } else if container.Op == OpSelectN { 668 call := container.Args[0] 669 aux := call.Aux.(*AuxCall) 670 which := container.AuxInt 671 672 if at == types.TypeMem { 673 if a != m0 || a != x.memForCall[call.ID] { 674 panic(fmt.Errorf("Memories %s, %s, and %s should all be equal after %s", a.LongString(), m0.LongString(), x.memForCall[call.ID], call.LongString())) 675 } 676 } else if rc.hasRegs() { 677 firstReg := uint32(0) 678 for i := 0; i < int(which); i++ { 679 firstReg += uint32(len(aux.abiInfo.OutParam(i).Registers)) 680 } 681 reg := int64(rc.nextSlice + Abi1RO(firstReg)) 682 a = makeOf(a, OpSelectN, []*Value{call}) 683 a.AuxInt = reg 684 } else { 685 off := x.offsetFrom(x.f.Entry, x.sp, rc.storeOffset+aux.OffsetOfResult(which), types.NewPtr(at)) 686 a = makeOf(a, OpLoad, []*Value{off, m0}) 687 } 688 689 } else { 690 panic(fmt.Errorf("Expected container OpArg or OpSelectN, saw %v instead", container.LongString())) 691 } 692 693 x.commonSelectors[sk] = a 694 return a 695 } 696 697 // rewriteWideSelectToStores handles the case of a SelectN'd result from a function call that is too large for SSA, 698 // but is transferred in registers. In this case the register cursor tracks both operands; the register sources and 699 // the memory destinations. 700 // This returns the memory flowing out of the last store 701 func (x *expandState) rewriteWideSelectToStores(pos src.XPos, b *Block, container, m0 *Value, at *types.Type, rc registerCursor) *Value { 702 703 if at.Size() == 0 { 704 return m0 705 } 706 707 switch at.Kind() { 708 case types.TARRAY: 709 et := at.Elem() 710 for i := int64(0); i < at.NumElem(); i++ { 711 m0 = x.rewriteWideSelectToStores(pos, b, container, m0, et, rc.next(et)) 712 } 713 return m0 714 715 case types.TSTRUCT: 716 // Assume ssagen/ssa.go (in buildssa) spills large aggregates so they won't appear here. 717 for i := 0; i < at.NumFields(); i++ { 718 et := at.Field(i).Type 719 m0 = x.rewriteWideSelectToStores(pos, b, container, m0, et, rc.next(et)) 720 pos = pos.WithNotStmt() 721 } 722 return m0 723 724 case types.TSLICE: 725 m0 = x.rewriteWideSelectToStores(pos, b, container, m0, at.Elem().PtrTo(), rc.next(x.typs.BytePtr)) 726 pos = pos.WithNotStmt() 727 m0 = x.rewriteWideSelectToStores(pos, b, container, m0, x.typs.Int, rc.next(x.typs.Int)) 728 m0 = x.rewriteWideSelectToStores(pos, b, container, m0, x.typs.Int, rc.next(x.typs.Int)) 729 return m0 730 731 case types.TSTRING: 732 m0 = x.rewriteWideSelectToStores(pos, b, container, m0, x.typs.BytePtr, rc.next(x.typs.BytePtr)) 733 pos = pos.WithNotStmt() 734 m0 = x.rewriteWideSelectToStores(pos, b, container, m0, x.typs.Int, rc.next(x.typs.Int)) 735 return m0 736 737 case types.TINTER: 738 m0 = x.rewriteWideSelectToStores(pos, b, container, m0, x.typs.Uintptr, rc.next(x.typs.Uintptr)) 739 pos = pos.WithNotStmt() 740 m0 = x.rewriteWideSelectToStores(pos, b, container, m0, x.typs.BytePtr, rc.next(x.typs.BytePtr)) 741 return m0 742 743 case types.TCOMPLEX64: 744 m0 = x.rewriteWideSelectToStores(pos, b, container, m0, x.typs.Float32, rc.next(x.typs.Float32)) 745 pos = pos.WithNotStmt() 746 m0 = x.rewriteWideSelectToStores(pos, b, container, m0, x.typs.Float32, rc.next(x.typs.Float32)) 747 return m0 748 749 case types.TCOMPLEX128: 750 m0 = x.rewriteWideSelectToStores(pos, b, container, m0, x.typs.Float64, rc.next(x.typs.Float64)) 751 pos = pos.WithNotStmt() 752 m0 = x.rewriteWideSelectToStores(pos, b, container, m0, x.typs.Float64, rc.next(x.typs.Float64)) 753 return m0 754 755 case types.TINT64: 756 if at.Size() > x.regSize { 757 m0 = x.rewriteWideSelectToStores(pos, b, container, m0, x.firstType, rc.next(x.firstType)) 758 pos = pos.WithNotStmt() 759 m0 = x.rewriteWideSelectToStores(pos, b, container, m0, x.secondType, rc.next(x.secondType)) 760 return m0 761 } 762 case types.TUINT64: 763 if at.Size() > x.regSize { 764 m0 = x.rewriteWideSelectToStores(pos, b, container, m0, x.typs.UInt32, rc.next(x.typs.UInt32)) 765 pos = pos.WithNotStmt() 766 m0 = x.rewriteWideSelectToStores(pos, b, container, m0, x.typs.UInt32, rc.next(x.typs.UInt32)) 767 return m0 768 } 769 } 770 771 // TODO could change treatment of too-large OpArg, would deal with it here. 772 if container.Op == OpSelectN { 773 call := container.Args[0] 774 aux := call.Aux.(*AuxCall) 775 which := container.AuxInt 776 777 if rc.hasRegs() { 778 firstReg := uint32(0) 779 for i := 0; i < int(which); i++ { 780 firstReg += uint32(len(aux.abiInfo.OutParam(i).Registers)) 781 } 782 reg := int64(rc.nextSlice + Abi1RO(firstReg)) 783 a := b.NewValue1I(pos, OpSelectN, at, reg, call) 784 dst := x.offsetFrom(b, rc.storeDest, rc.storeOffset, types.NewPtr(at)) 785 m0 = b.NewValue3A(pos, OpStore, types.TypeMem, at, dst, a, m0) 786 } else { 787 panic(fmt.Errorf("Expected rc to have registers")) 788 } 789 } else { 790 panic(fmt.Errorf("Expected container OpSelectN, saw %v instead", container.LongString())) 791 } 792 return m0 793 } 794 795 func isBlockMultiValueExit(b *Block) bool { 796 return (b.Kind == BlockRet || b.Kind == BlockRetJmp) && b.Controls[0] != nil && b.Controls[0].Op == OpMakeResult 797 } 798 799 type Abi1RO uint8 // An offset within a parameter's slice of register indices, for abi1. 800 801 // A registerCursor tracks which register is used for an Arg or regValues, or a piece of such. 802 type registerCursor struct { 803 storeDest *Value // if there are no register targets, then this is the base of the store. 804 storeOffset int64 805 regs []abi.RegIndex // the registers available for this Arg/result (which is all in registers or not at all) 806 nextSlice Abi1RO // the next register/register-slice offset 807 config *abi.ABIConfig 808 regValues *[]*Value // values assigned to registers accumulate here 809 } 810 811 func (c *registerCursor) String() string { 812 dest := "<none>" 813 if c.storeDest != nil { 814 dest = fmt.Sprintf("%s+%d", c.storeDest.String(), c.storeOffset) 815 } 816 regs := "<none>" 817 if c.regValues != nil { 818 regs = "" 819 for i, x := range *c.regValues { 820 if i > 0 { 821 regs = regs + "; " 822 } 823 regs = regs + x.LongString() 824 } 825 } 826 827 // not printing the config because that has not been useful 828 return fmt.Sprintf("RCSR{storeDest=%v, regsLen=%d, nextSlice=%d, regValues=[%s]}", dest, len(c.regs), c.nextSlice, regs) 829 } 830 831 // next effectively post-increments the register cursor; the receiver is advanced, 832 // the (aligned) old value is returned. 833 func (c *registerCursor) next(t *types.Type) registerCursor { 834 c.storeOffset = types.RoundUp(c.storeOffset, t.Alignment()) 835 rc := *c 836 c.storeOffset = types.RoundUp(c.storeOffset+t.Size(), t.Alignment()) 837 if int(c.nextSlice) < len(c.regs) { 838 w := c.config.NumParamRegs(t) 839 c.nextSlice += Abi1RO(w) 840 } 841 return rc 842 } 843 844 // plus returns a register cursor offset from the original, without modifying the original. 845 func (c *registerCursor) plus(regWidth Abi1RO) registerCursor { 846 rc := *c 847 rc.nextSlice += regWidth 848 return rc 849 } 850 851 // at returns the register cursor for component i of t, where the first 852 // component is numbered 0. 853 func (c *registerCursor) at(t *types.Type, i int) registerCursor { 854 rc := *c 855 if i == 0 || len(c.regs) == 0 { 856 return rc 857 } 858 if t.IsArray() { 859 w := c.config.NumParamRegs(t.Elem()) 860 rc.nextSlice += Abi1RO(i * w) 861 return rc 862 } 863 if t.IsStruct() { 864 for j := 0; j < i; j++ { 865 rc.next(t.FieldType(j)) 866 } 867 return rc 868 } 869 panic("Haven't implemented this case yet, do I need to?") 870 } 871 872 func (c *registerCursor) init(regs []abi.RegIndex, info *abi.ABIParamResultInfo, result *[]*Value, storeDest *Value, storeOffset int64) { 873 c.regs = regs 874 c.nextSlice = 0 875 c.storeOffset = storeOffset 876 c.storeDest = storeDest 877 c.config = info.Config() 878 c.regValues = result 879 } 880 881 func (c *registerCursor) addArg(v *Value) { 882 *c.regValues = append(*c.regValues, v) 883 } 884 885 func (c *registerCursor) hasRegs() bool { 886 return len(c.regs) > 0 887 } 888 889 func (c *registerCursor) ArgOpAndRegisterFor() (Op, int64) { 890 r := c.regs[c.nextSlice] 891 return ArgOpAndRegisterFor(r, c.config) 892 } 893 894 // ArgOpAndRegisterFor converts an abi register index into an ssa Op and corresponding 895 // arg register index. 896 func ArgOpAndRegisterFor(r abi.RegIndex, abiConfig *abi.ABIConfig) (Op, int64) { 897 i := abiConfig.FloatIndexFor(r) 898 if i >= 0 { // float PR 899 return OpArgFloatReg, i 900 } 901 return OpArgIntReg, int64(r) 902 } 903 904 type selKey struct { 905 from *Value // what is selected from 906 offsetOrIndex int64 // whatever is appropriate for the selector 907 size int64 908 typ *types.Type 909 } 910 911 type expandState struct { 912 f *Func 913 debug int // odd values log lost statement markers, so likely settings are 1 (stmts), 2 (expansion), and 3 (both) 914 regSize int64 915 sp *Value 916 typs *Types 917 918 firstOp Op // for 64-bit integers on 32-bit machines, first word in memory 919 secondOp Op // for 64-bit integers on 32-bit machines, second word in memory 920 firstType *types.Type // first half type, for Int64 921 secondType *types.Type // second half type, for Int64 922 923 wideSelects map[*Value]*Value // Selects that are not SSA-able, mapped to consuming stores. 924 commonSelectors map[selKey]*Value // used to de-dupe selectors 925 commonArgs map[selKey]*Value // used to de-dupe OpArg/OpArgIntReg/OpArgFloatReg 926 memForCall map[ID]*Value // For a call, need to know the unique selector that gets the mem. 927 indentLevel int // Indentation for debugging recursion 928 } 929 930 // intPairTypes returns the pair of 32-bit int types needed to encode a 64-bit integer type on a target 931 // that has no 64-bit integer registers. 932 func (x *expandState) intPairTypes(et types.Kind) (tHi, tLo *types.Type) { 933 tHi = x.typs.UInt32 934 if et == types.TINT64 { 935 tHi = x.typs.Int32 936 } 937 tLo = x.typs.UInt32 938 return 939 } 940 941 // offsetFrom creates an offset from a pointer, simplifying chained offsets and offsets from SP 942 func (x *expandState) offsetFrom(b *Block, from *Value, offset int64, pt *types.Type) *Value { 943 ft := from.Type 944 if offset == 0 { 945 if ft == pt { 946 return from 947 } 948 // This captures common, (apparently) safe cases. The unsafe cases involve ft == uintptr 949 if (ft.IsPtr() || ft.IsUnsafePtr()) && pt.IsPtr() { 950 return from 951 } 952 } 953 // Simplify, canonicalize 954 for from.Op == OpOffPtr { 955 offset += from.AuxInt 956 from = from.Args[0] 957 } 958 if from == x.sp { 959 return x.f.ConstOffPtrSP(pt, offset, x.sp) 960 } 961 return b.NewValue1I(from.Pos.WithNotStmt(), OpOffPtr, pt, offset, from) 962 } 963 964 func (x *expandState) regWidth(t *types.Type) Abi1RO { 965 return Abi1RO(x.f.ABI1.NumParamRegs(t)) 966 } 967 968 // regOffset returns the register offset of the i'th element of type t 969 func (x *expandState) regOffset(t *types.Type, i int) Abi1RO { 970 // TODO maybe cache this in a map if profiling recommends. 971 if i == 0 { 972 return 0 973 } 974 if t.IsArray() { 975 return Abi1RO(i) * x.regWidth(t.Elem()) 976 } 977 if t.IsStruct() { 978 k := Abi1RO(0) 979 for j := 0; j < i; j++ { 980 k += x.regWidth(t.FieldType(j)) 981 } 982 return k 983 } 984 panic("Haven't implemented this case yet, do I need to?") 985 } 986 987 // prAssignForArg returns the ABIParamAssignment for v, assumed to be an OpArg. 988 func (x *expandState) prAssignForArg(v *Value) *abi.ABIParamAssignment { 989 if v.Op != OpArg { 990 panic(fmt.Errorf("Wanted OpArg, instead saw %s", v.LongString())) 991 } 992 return ParamAssignmentForArgName(x.f, v.Aux.(*ir.Name)) 993 } 994 995 // ParamAssignmentForArgName returns the ABIParamAssignment for f's arg with matching name. 996 func ParamAssignmentForArgName(f *Func, name *ir.Name) *abi.ABIParamAssignment { 997 abiInfo := f.OwnAux.abiInfo 998 ip := abiInfo.InParams() 999 for i, a := range ip { 1000 if a.Name == name { 1001 return &ip[i] 1002 } 1003 } 1004 panic(fmt.Errorf("Did not match param %v in prInfo %+v", name, abiInfo.InParams())) 1005 } 1006 1007 // indent increments (or decrements) the indentation. 1008 func (x *expandState) indent(n int) { 1009 x.indentLevel += n 1010 } 1011 1012 // Printf does an indented fmt.Printf on the format and args. 1013 func (x *expandState) Printf(format string, a ...interface{}) (n int, err error) { 1014 if x.indentLevel > 0 { 1015 fmt.Printf("%[1]*s", x.indentLevel, "") 1016 } 1017 return fmt.Printf(format, a...) 1018 } 1019 1020 func (x *expandState) invalidateRecursively(a *Value) { 1021 var s string 1022 if x.debug > 0 { 1023 plus := " " 1024 if a.Pos.IsStmt() == src.PosIsStmt { 1025 plus = " +" 1026 } 1027 s = a.String() + plus + a.Pos.LineNumber() + " " + a.LongString() 1028 if x.debug > 1 { 1029 x.Printf("...marking %v unused\n", s) 1030 } 1031 } 1032 lost := a.invalidateRecursively() 1033 if x.debug&1 != 0 && lost { // For odd values of x.debug, do this. 1034 x.Printf("Lost statement marker in %s on former %s\n", base.Ctxt.Pkgpath+"."+x.f.Name, s) 1035 } 1036 }