github.com/bir3/gocompiler@v0.3.205/src/cmd/compile/internal/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 "github.com/bir3/gocompiler/src/cmd/compile/internal/abi" 9 "github.com/bir3/gocompiler/src/cmd/compile/internal/base" 10 "github.com/bir3/gocompiler/src/cmd/compile/internal/ir" 11 "github.com/bir3/gocompiler/src/cmd/compile/internal/types" 12 "github.com/bir3/gocompiler/src/cmd/internal/src" 13 "fmt" 14 "sort" 15 ) 16 17 type selKey struct { 18 from *Value // what is selected from 19 offsetOrIndex int64 // whatever is appropriate for the selector 20 size int64 21 typ *types.Type 22 } 23 24 type Abi1RO uint8 // An offset within a parameter's slice of register indices, for abi1. 25 26 func isBlockMultiValueExit(b *Block) bool { 27 return (b.Kind == BlockRet || b.Kind == BlockRetJmp) && b.Controls[0] != nil && b.Controls[0].Op == OpMakeResult 28 } 29 30 func badVal(s string, v *Value) error { 31 return fmt.Errorf("%s %s", s, v.LongString()) 32 } 33 34 // removeTrivialWrapperTypes unwraps layers of 35 // struct { singleField SomeType } and [1]SomeType 36 // until a non-wrapper type is reached. This is useful 37 // for working with assignments to/from interface data 38 // fields (either second operand to OpIMake or OpIData) 39 // where the wrapping or type conversion can be elided 40 // because of type conversions/assertions in source code 41 // that do not appear in SSA. 42 func removeTrivialWrapperTypes(t *types.Type) *types.Type { 43 for { 44 if t.IsStruct() && t.NumFields() == 1 { 45 t = t.Field(0).Type 46 continue 47 } 48 if t.IsArray() && t.NumElem() == 1 { 49 t = t.Elem() 50 continue 51 } 52 break 53 } 54 return t 55 } 56 57 // A registerCursor tracks which register is used for an Arg or regValues, or a piece of such. 58 type registerCursor struct { 59 // TODO(register args) convert this to a generalized target cursor. 60 storeDest *Value // if there are no register targets, then this is the base of the store. 61 regsLen int // the number of registers available for this Arg/result (which is all in registers or not at all) 62 nextSlice Abi1RO // the next register/register-slice offset 63 config *abi.ABIConfig 64 regValues *[]*Value // values assigned to registers accumulate here 65 } 66 67 func (rc *registerCursor) String() string { 68 dest := "<none>" 69 if rc.storeDest != nil { 70 dest = rc.storeDest.String() 71 } 72 regs := "<none>" 73 if rc.regValues != nil { 74 regs = "" 75 for i, x := range *rc.regValues { 76 if i > 0 { 77 regs = regs + "; " 78 } 79 regs = regs + x.LongString() 80 } 81 } 82 // not printing the config because that has not been useful 83 return fmt.Sprintf("RCSR{storeDest=%v, regsLen=%d, nextSlice=%d, regValues=[%s]}", dest, rc.regsLen, rc.nextSlice, regs) 84 } 85 86 // next effectively post-increments the register cursor; the receiver is advanced, 87 // the old value is returned. 88 func (c *registerCursor) next(t *types.Type) registerCursor { 89 rc := *c 90 if int(c.nextSlice) < c.regsLen { 91 w := c.config.NumParamRegs(t) 92 c.nextSlice += Abi1RO(w) 93 } 94 return rc 95 } 96 97 // plus returns a register cursor offset from the original, without modifying the original. 98 func (c *registerCursor) plus(regWidth Abi1RO) registerCursor { 99 rc := *c 100 rc.nextSlice += regWidth 101 return rc 102 } 103 104 const ( 105 // Register offsets for fields of built-in aggregate types; the ones not listed are zero. 106 RO_complex_imag = 1 107 RO_string_len = 1 108 RO_slice_len = 1 109 RO_slice_cap = 2 110 RO_iface_data = 1 111 ) 112 113 func (x *expandState) regWidth(t *types.Type) Abi1RO { 114 return Abi1RO(x.abi1.NumParamRegs(t)) 115 } 116 117 // regOffset returns the register offset of the i'th element of type t 118 func (x *expandState) regOffset(t *types.Type, i int) Abi1RO { 119 // TODO maybe cache this in a map if profiling recommends. 120 if i == 0 { 121 return 0 122 } 123 if t.IsArray() { 124 return Abi1RO(i) * x.regWidth(t.Elem()) 125 } 126 if t.IsStruct() { 127 k := Abi1RO(0) 128 for j := 0; j < i; j++ { 129 k += x.regWidth(t.FieldType(j)) 130 } 131 return k 132 } 133 panic("Haven't implemented this case yet, do I need to?") 134 } 135 136 // at returns the register cursor for component i of t, where the first 137 // component is numbered 0. 138 func (c *registerCursor) at(t *types.Type, i int) registerCursor { 139 rc := *c 140 if i == 0 || c.regsLen == 0 { 141 return rc 142 } 143 if t.IsArray() { 144 w := c.config.NumParamRegs(t.Elem()) 145 rc.nextSlice += Abi1RO(i * w) 146 return rc 147 } 148 if t.IsStruct() { 149 for j := 0; j < i; j++ { 150 rc.next(t.FieldType(j)) 151 } 152 return rc 153 } 154 panic("Haven't implemented this case yet, do I need to?") 155 } 156 157 func (c *registerCursor) init(regs []abi.RegIndex, info *abi.ABIParamResultInfo, result *[]*Value, storeDest *Value) { 158 c.regsLen = len(regs) 159 c.nextSlice = 0 160 if len(regs) == 0 { 161 c.storeDest = storeDest // only save this if there are no registers, will explode if misused. 162 return 163 } 164 c.config = info.Config() 165 c.regValues = result 166 } 167 168 func (c *registerCursor) addArg(v *Value) { 169 *c.regValues = append(*c.regValues, v) 170 } 171 172 func (c *registerCursor) hasRegs() bool { 173 return c.regsLen > 0 174 } 175 176 type expandState struct { 177 f *Func 178 abi1 *abi.ABIConfig 179 debug int // odd values log lost statement markers, so likely settings are 1 (stmts), 2 (expansion), and 3 (both) 180 canSSAType func(*types.Type) bool 181 regSize int64 182 sp *Value 183 typs *Types 184 ptrSize int64 185 hiOffset int64 186 lowOffset int64 187 hiRo Abi1RO 188 loRo Abi1RO 189 namedSelects map[*Value][]namedVal 190 sdom SparseTree 191 commonSelectors map[selKey]*Value // used to de-dupe selectors 192 commonArgs map[selKey]*Value // used to de-dupe OpArg/OpArgIntReg/OpArgFloatReg 193 memForCall map[ID]*Value // For a call, need to know the unique selector that gets the mem. 194 transformedSelects map[ID]bool // OpSelectN after rewriting, either created or renumbered. 195 indentLevel int // Indentation for debugging recursion 196 } 197 198 // intPairTypes returns the pair of 32-bit int types needed to encode a 64-bit integer type on a target 199 // that has no 64-bit integer registers. 200 func (x *expandState) intPairTypes(et types.Kind) (tHi, tLo *types.Type) { 201 tHi = x.typs.UInt32 202 if et == types.TINT64 { 203 tHi = x.typs.Int32 204 } 205 tLo = x.typs.UInt32 206 return 207 } 208 209 // isAlreadyExpandedAggregateType returns whether a type is an SSA-able "aggregate" (multiple register) type 210 // that was expanded in an earlier phase (currently, expand_calls is intended to run after decomposeBuiltin, 211 // so this is all aggregate types -- small struct and array, complex, interface, string, slice, and 64-bit 212 // integer on 32-bit). 213 func (x *expandState) isAlreadyExpandedAggregateType(t *types.Type) bool { 214 if !x.canSSAType(t) { 215 return false 216 } 217 return t.IsStruct() || t.IsArray() || t.IsComplex() || t.IsInterface() || t.IsString() || t.IsSlice() || 218 (t.Size() > x.regSize && (t.IsInteger() || (x.f.Config.SoftFloat && t.IsFloat()))) 219 } 220 221 // offsetFrom creates an offset from a pointer, simplifying chained offsets and offsets from SP 222 // TODO should also optimize offsets from SB? 223 func (x *expandState) offsetFrom(b *Block, from *Value, offset int64, pt *types.Type) *Value { 224 ft := from.Type 225 if offset == 0 { 226 if ft == pt { 227 return from 228 } 229 // This captures common, (apparently) safe cases. The unsafe cases involve ft == uintptr 230 if (ft.IsPtr() || ft.IsUnsafePtr()) && pt.IsPtr() { 231 return from 232 } 233 } 234 // Simplify, canonicalize 235 for from.Op == OpOffPtr { 236 offset += from.AuxInt 237 from = from.Args[0] 238 } 239 if from == x.sp { 240 return x.f.ConstOffPtrSP(pt, offset, x.sp) 241 } 242 return b.NewValue1I(from.Pos.WithNotStmt(), OpOffPtr, pt, offset, from) 243 } 244 245 // splitSlots splits one "field" (specified by sfx, offset, and ty) out of the LocalSlots in ls and returns the new LocalSlots this generates. 246 func (x *expandState) splitSlots(ls []*LocalSlot, sfx string, offset int64, ty *types.Type) []*LocalSlot { 247 var locs []*LocalSlot 248 for i := range ls { 249 locs = append(locs, x.f.SplitSlot(ls[i], sfx, offset, ty)) 250 } 251 return locs 252 } 253 254 // prAssignForArg returns the ABIParamAssignment for v, assumed to be an OpArg. 255 func (x *expandState) prAssignForArg(v *Value) *abi.ABIParamAssignment { 256 if v.Op != OpArg { 257 panic(badVal("Wanted OpArg, instead saw", v)) 258 } 259 return ParamAssignmentForArgName(x.f, v.Aux.(*ir.Name)) 260 } 261 262 // ParamAssignmentForArgName returns the ABIParamAssignment for f's arg with matching name. 263 func ParamAssignmentForArgName(f *Func, name *ir.Name) *abi.ABIParamAssignment { 264 abiInfo := f.OwnAux.abiInfo 265 ip := abiInfo.InParams() 266 for i, a := range ip { 267 if a.Name == name { 268 return &ip[i] 269 } 270 } 271 panic(fmt.Errorf("Did not match param %v in prInfo %+v", name, abiInfo.InParams())) 272 } 273 274 // indent increments (or decrements) the indentation. 275 func (x *expandState) indent(n int) { 276 x.indentLevel += n 277 } 278 279 // Printf does an indented fmt.Printf on te format and args. 280 func (x *expandState) Printf(format string, a ...interface{}) (n int, err error) { 281 if x.indentLevel > 0 { 282 fmt.Printf("%[1]*s", x.indentLevel, "") 283 } 284 return fmt.Printf(format, a...) 285 } 286 287 // Calls that need lowering have some number of inputs, including a memory input, 288 // and produce a tuple of (value1, value2, ..., mem) where valueK may or may not be SSA-able. 289 290 // With the current ABI those inputs need to be converted into stores to memory, 291 // rethreading the call's memory input to the first, and the new call now receiving the last. 292 293 // With the current ABI, the outputs need to be converted to loads, which will all use the call's 294 // memory output as their input. 295 296 // rewriteSelect recursively walks from leaf selector to a root (OpSelectN, OpLoad, OpArg) 297 // through a chain of Struct/Array/builtin Select operations. If the chain of selectors does not 298 // end in an expected root, it does nothing (this can happen depending on compiler phase ordering). 299 // The "leaf" provides the type, the root supplies the container, and the leaf-to-root path 300 // accumulates the offset. 301 // It emits the code necessary to implement the leaf select operation that leads to the root. 302 // 303 // TODO when registers really arrive, must also decompose anything split across two registers or registers and memory. 304 func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64, regOffset Abi1RO) []*LocalSlot { 305 if x.debug > 1 { 306 x.indent(3) 307 defer x.indent(-3) 308 x.Printf("rewriteSelect(%s; %s; memOff=%d; regOff=%d)\n", leaf.LongString(), selector.LongString(), offset, regOffset) 309 } 310 var locs []*LocalSlot 311 leafType := leaf.Type 312 if len(selector.Args) > 0 { 313 w := selector.Args[0] 314 if w.Op == OpCopy { 315 for w.Op == OpCopy { 316 w = w.Args[0] 317 } 318 selector.SetArg(0, w) 319 } 320 } 321 switch selector.Op { 322 case OpArgIntReg, OpArgFloatReg: 323 if leafType == selector.Type { // OpIData leads us here, sometimes. 324 leaf.copyOf(selector) 325 } else { 326 x.f.Fatalf("Unexpected %s type, selector=%s, leaf=%s\n", selector.Op.String(), selector.LongString(), leaf.LongString()) 327 } 328 if x.debug > 1 { 329 x.Printf("---%s, break\n", selector.Op.String()) 330 } 331 case OpArg: 332 if !x.isAlreadyExpandedAggregateType(selector.Type) { 333 if leafType == selector.Type { // OpIData leads us here, sometimes. 334 x.newArgToMemOrRegs(selector, leaf, offset, regOffset, leafType, leaf.Pos) 335 } else { 336 x.f.Fatalf("Unexpected OpArg type, selector=%s, leaf=%s\n", selector.LongString(), leaf.LongString()) 337 } 338 if x.debug > 1 { 339 x.Printf("---OpArg, break\n") 340 } 341 break 342 } 343 switch leaf.Op { 344 case OpIData, OpStructSelect, OpArraySelect: 345 leafType = removeTrivialWrapperTypes(leaf.Type) 346 } 347 x.newArgToMemOrRegs(selector, leaf, offset, regOffset, leafType, leaf.Pos) 348 349 for _, s := range x.namedSelects[selector] { 350 locs = append(locs, x.f.Names[s.locIndex]) 351 } 352 353 case OpLoad: // We end up here because of IData of immediate structures. 354 // Failure case: 355 // (note the failure case is very rare; w/o this case, make.bash and run.bash both pass, as well as 356 // the hard cases of building {syscall,math,math/cmplx,math/bits,go/constant} on ppc64le and mips-softfloat). 357 // 358 // GOSSAFUNC='(*dumper).dump' go build -gcflags=-l -tags=math_big_pure_go cmd/compile/internal/gc 359 // cmd/compile/internal/gc/dump.go:136:14: internal compiler error: '(*dumper).dump': not lowered: v827, StructSelect PTR PTR 360 // b2: ← b1 361 // v20 (+142) = StaticLECall <interface {},mem> {AuxCall{reflect.Value.Interface([reflect.Value,0])[interface {},24]}} [40] v8 v1 362 // v21 (142) = SelectN <mem> [1] v20 363 // v22 (142) = SelectN <interface {}> [0] v20 364 // b15: ← b8 365 // v71 (+143) = IData <Nodes> v22 (v[Nodes]) 366 // v73 (+146) = StaticLECall <[]*Node,mem> {AuxCall{"".Nodes.Slice([Nodes,0])[[]*Node,8]}} [32] v71 v21 367 // 368 // translates (w/o the "case OpLoad:" above) to: 369 // 370 // b2: ← b1 371 // v20 (+142) = StaticCall <mem> {AuxCall{reflect.Value.Interface([reflect.Value,0])[interface {},24]}} [40] v715 372 // v23 (142) = Load <*uintptr> v19 v20 373 // v823 (142) = IsNonNil <bool> v23 374 // v67 (+143) = Load <*[]*Node> v880 v20 375 // b15: ← b8 376 // v827 (146) = StructSelect <*[]*Node> [0] v67 377 // v846 (146) = Store <mem> {*[]*Node} v769 v827 v20 378 // v73 (+146) = StaticCall <mem> {AuxCall{"".Nodes.Slice([Nodes,0])[[]*Node,8]}} [32] v846 379 // i.e., the struct select is generated and remains in because it is not applied to an actual structure. 380 // The OpLoad was created to load the single field of the IData 381 // This case removes that StructSelect. 382 if leafType != selector.Type { 383 if x.f.Config.SoftFloat && selector.Type.IsFloat() { 384 if x.debug > 1 { 385 x.Printf("---OpLoad, break\n") 386 } 387 break // softfloat pass will take care of that 388 } 389 x.f.Fatalf("Unexpected Load as selector, leaf=%s, selector=%s\n", leaf.LongString(), selector.LongString()) 390 } 391 leaf.copyOf(selector) 392 for _, s := range x.namedSelects[selector] { 393 locs = append(locs, x.f.Names[s.locIndex]) 394 } 395 396 case OpSelectN: 397 // TODO(register args) result case 398 // if applied to Op-mumble-call, the Aux tells us which result, regOffset specifies offset within result. If a register, should rewrite to OpSelectN for new call. 399 // TODO these may be duplicated. Should memoize. Intermediate selectors will go dead, no worries there. 400 call := selector.Args[0] 401 call0 := call 402 aux := call.Aux.(*AuxCall) 403 which := selector.AuxInt 404 if x.transformedSelects[selector.ID] { 405 // This is a minor hack. Either this select has had its operand adjusted (mem) or 406 // it is some other intermediate node that was rewritten to reference a register (not a generic arg). 407 // This can occur with chains of selection/indexing from single field/element aggregates. 408 leaf.copyOf(selector) 409 break 410 } 411 if which == aux.NResults() { // mem is after the results. 412 // rewrite v as a Copy of call -- the replacement call will produce a mem. 413 if leaf != selector { 414 panic(fmt.Errorf("Unexpected selector of memory, selector=%s, call=%s, leaf=%s", selector.LongString(), call.LongString(), leaf.LongString())) 415 } 416 if aux.abiInfo == nil { 417 panic(badVal("aux.abiInfo nil for call", call)) 418 } 419 if existing := x.memForCall[call.ID]; existing == nil { 420 selector.AuxInt = int64(aux.abiInfo.OutRegistersUsed()) 421 x.memForCall[call.ID] = selector 422 x.transformedSelects[selector.ID] = true // operand adjusted 423 } else { 424 selector.copyOf(existing) 425 } 426 427 } else { 428 leafType := removeTrivialWrapperTypes(leaf.Type) 429 if x.canSSAType(leafType) { 430 pt := types.NewPtr(leafType) 431 // Any selection right out of the arg area/registers has to be same Block as call, use call as mem input. 432 // Create a "mem" for any loads that need to occur. 433 if mem := x.memForCall[call.ID]; mem != nil { 434 if mem.Block != call.Block { 435 panic(fmt.Errorf("selector and call need to be in same block, selector=%s; call=%s", selector.LongString(), call.LongString())) 436 } 437 call = mem 438 } else { 439 mem = call.Block.NewValue1I(call.Pos.WithNotStmt(), OpSelectN, types.TypeMem, int64(aux.abiInfo.OutRegistersUsed()), call) 440 x.transformedSelects[mem.ID] = true // select uses post-expansion indexing 441 x.memForCall[call.ID] = mem 442 call = mem 443 } 444 outParam := aux.abiInfo.OutParam(int(which)) 445 if len(outParam.Registers) > 0 { 446 firstReg := uint32(0) 447 for i := 0; i < int(which); i++ { 448 firstReg += uint32(len(aux.abiInfo.OutParam(i).Registers)) 449 } 450 reg := int64(regOffset + Abi1RO(firstReg)) 451 if leaf.Block == call.Block { 452 leaf.reset(OpSelectN) 453 leaf.SetArgs1(call0) 454 leaf.Type = leafType 455 leaf.AuxInt = reg 456 x.transformedSelects[leaf.ID] = true // leaf, rewritten to use post-expansion indexing. 457 } else { 458 w := call.Block.NewValue1I(leaf.Pos, OpSelectN, leafType, reg, call0) 459 x.transformedSelects[w.ID] = true // select, using post-expansion indexing. 460 leaf.copyOf(w) 461 } 462 } else { 463 off := x.offsetFrom(x.f.Entry, x.sp, offset+aux.OffsetOfResult(which), pt) 464 if leaf.Block == call.Block { 465 leaf.reset(OpLoad) 466 leaf.SetArgs2(off, call) 467 leaf.Type = leafType 468 } else { 469 w := call.Block.NewValue2(leaf.Pos, OpLoad, leafType, off, call) 470 leaf.copyOf(w) 471 if x.debug > 1 { 472 x.Printf("---new %s\n", w.LongString()) 473 } 474 } 475 } 476 for _, s := range x.namedSelects[selector] { 477 locs = append(locs, x.f.Names[s.locIndex]) 478 } 479 } else { 480 x.f.Fatalf("Should not have non-SSA-able OpSelectN, selector=%s", selector.LongString()) 481 } 482 } 483 484 case OpStructSelect: 485 w := selector.Args[0] 486 var ls []*LocalSlot 487 if w.Type.Kind() != types.TSTRUCT { // IData artifact 488 ls = x.rewriteSelect(leaf, w, offset, regOffset) 489 } else { 490 fldi := int(selector.AuxInt) 491 ls = x.rewriteSelect(leaf, w, offset+w.Type.FieldOff(fldi), regOffset+x.regOffset(w.Type, fldi)) 492 if w.Op != OpIData { 493 for _, l := range ls { 494 locs = append(locs, x.f.SplitStruct(l, int(selector.AuxInt))) 495 } 496 } 497 } 498 499 case OpArraySelect: 500 w := selector.Args[0] 501 index := selector.AuxInt 502 x.rewriteSelect(leaf, w, offset+selector.Type.Size()*index, regOffset+x.regOffset(w.Type, int(index))) 503 504 case OpInt64Hi: 505 w := selector.Args[0] 506 ls := x.rewriteSelect(leaf, w, offset+x.hiOffset, regOffset+x.hiRo) 507 locs = x.splitSlots(ls, ".hi", x.hiOffset, leafType) 508 509 case OpInt64Lo: 510 w := selector.Args[0] 511 ls := x.rewriteSelect(leaf, w, offset+x.lowOffset, regOffset+x.loRo) 512 locs = x.splitSlots(ls, ".lo", x.lowOffset, leafType) 513 514 case OpStringPtr: 515 ls := x.rewriteSelect(leaf, selector.Args[0], offset, regOffset) 516 locs = x.splitSlots(ls, ".ptr", 0, x.typs.BytePtr) 517 518 case OpSlicePtr, OpSlicePtrUnchecked: 519 w := selector.Args[0] 520 ls := x.rewriteSelect(leaf, w, offset, regOffset) 521 locs = x.splitSlots(ls, ".ptr", 0, types.NewPtr(w.Type.Elem())) 522 523 case OpITab: 524 w := selector.Args[0] 525 ls := x.rewriteSelect(leaf, w, offset, regOffset) 526 sfx := ".itab" 527 if w.Type.IsEmptyInterface() { 528 sfx = ".type" 529 } 530 locs = x.splitSlots(ls, sfx, 0, x.typs.Uintptr) 531 532 case OpComplexReal: 533 ls := x.rewriteSelect(leaf, selector.Args[0], offset, regOffset) 534 locs = x.splitSlots(ls, ".real", 0, selector.Type) 535 536 case OpComplexImag: 537 ls := x.rewriteSelect(leaf, selector.Args[0], offset+selector.Type.Size(), regOffset+RO_complex_imag) // result is FloatNN, width of result is offset of imaginary part. 538 locs = x.splitSlots(ls, ".imag", selector.Type.Size(), selector.Type) 539 540 case OpStringLen, OpSliceLen: 541 ls := x.rewriteSelect(leaf, selector.Args[0], offset+x.ptrSize, regOffset+RO_slice_len) 542 locs = x.splitSlots(ls, ".len", x.ptrSize, leafType) 543 544 case OpIData: 545 ls := x.rewriteSelect(leaf, selector.Args[0], offset+x.ptrSize, regOffset+RO_iface_data) 546 locs = x.splitSlots(ls, ".data", x.ptrSize, leafType) 547 548 case OpSliceCap: 549 ls := x.rewriteSelect(leaf, selector.Args[0], offset+2*x.ptrSize, regOffset+RO_slice_cap) 550 locs = x.splitSlots(ls, ".cap", 2*x.ptrSize, leafType) 551 552 case OpCopy: // If it's an intermediate result, recurse 553 locs = x.rewriteSelect(leaf, selector.Args[0], offset, regOffset) 554 for _, s := range x.namedSelects[selector] { 555 // this copy may have had its own name, preserve that, too. 556 locs = append(locs, x.f.Names[s.locIndex]) 557 } 558 559 default: 560 // Ignore dead ends. These can occur if this phase is run before decompose builtin (which is not intended, but allowed). 561 } 562 563 return locs 564 } 565 566 func (x *expandState) rewriteDereference(b *Block, base, a, mem *Value, offset, size int64, typ *types.Type, pos src.XPos) *Value { 567 source := a.Args[0] 568 dst := x.offsetFrom(b, base, offset, source.Type) 569 if a.Uses == 1 && a.Block == b { 570 a.reset(OpMove) 571 a.Pos = pos 572 a.Type = types.TypeMem 573 a.Aux = typ 574 a.AuxInt = size 575 a.SetArgs3(dst, source, mem) 576 mem = a 577 } else { 578 mem = b.NewValue3A(pos, OpMove, types.TypeMem, typ, dst, source, mem) 579 mem.AuxInt = size 580 } 581 return mem 582 } 583 584 var indexNames [1]string = [1]string{"[0]"} 585 586 // pathTo returns the selection path to the leaf type at offset within container. 587 // e.g. len(thing.field[0]) => ".field[0].len" 588 // this is for purposes of generating names ultimately fed to a debugger. 589 func (x *expandState) pathTo(container, leaf *types.Type, offset int64) string { 590 if container == leaf || offset == 0 && container.Size() == leaf.Size() { 591 return "" 592 } 593 path := "" 594 outer: 595 for { 596 switch container.Kind() { 597 case types.TARRAY: 598 container = container.Elem() 599 if container.Size() == 0 { 600 return path 601 } 602 i := offset / container.Size() 603 offset = offset % container.Size() 604 // If a future compiler/ABI supports larger SSA/Arg-able arrays, expand indexNames. 605 path = path + indexNames[i] 606 continue 607 case types.TSTRUCT: 608 for i := 0; i < container.NumFields(); i++ { 609 fld := container.Field(i) 610 if fld.Offset+fld.Type.Size() > offset { 611 offset -= fld.Offset 612 path += "." + fld.Sym.Name 613 container = fld.Type 614 continue outer 615 } 616 } 617 return path 618 case types.TINT64, types.TUINT64: 619 if container.Size() == x.regSize { 620 return path 621 } 622 if offset == x.hiOffset { 623 return path + ".hi" 624 } 625 return path + ".lo" 626 case types.TINTER: 627 if offset != 0 { 628 return path + ".data" 629 } 630 if container.IsEmptyInterface() { 631 return path + ".type" 632 } 633 return path + ".itab" 634 635 case types.TSLICE: 636 if offset == 2*x.regSize { 637 return path + ".cap" 638 } 639 fallthrough 640 case types.TSTRING: 641 if offset == 0 { 642 return path + ".ptr" 643 } 644 return path + ".len" 645 case types.TCOMPLEX64, types.TCOMPLEX128: 646 if offset == 0 { 647 return path + ".real" 648 } 649 return path + ".imag" 650 } 651 return path 652 } 653 } 654 655 // decomposeArg is a helper for storeArgOrLoad. 656 // It decomposes a Load or an Arg into smaller parts and returns the new mem. 657 // If the type does not match one of the expected aggregate types, it returns nil instead. 658 // Parameters: 659 // 660 // pos -- the location of any generated code. 661 // b -- the block into which any generated code should normally be placed 662 // source -- the value, possibly an aggregate, to be stored. 663 // mem -- the mem flowing into this decomposition (loads depend on it, stores updated it) 664 // t -- the type of the value to be stored 665 // storeOffset -- if the value is stored in memory, it is stored at base (see storeRc) + storeOffset 666 // loadRegOffset -- regarding source as a value in registers, the register offset in ABI1. Meaningful only if source is OpArg. 667 // storeRc -- storeRC; if the value is stored in registers, this specifies the registers. 668 // StoreRc also identifies whether the target is registers or memory, and has the base for the store operation. 669 func (x *expandState) decomposeArg(pos src.XPos, b *Block, source, mem *Value, t *types.Type, storeOffset int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value { 670 671 pa := x.prAssignForArg(source) 672 var locs []*LocalSlot 673 for _, s := range x.namedSelects[source] { 674 locs = append(locs, x.f.Names[s.locIndex]) 675 } 676 677 if len(pa.Registers) > 0 { 678 // Handle the in-registers case directly 679 rts, offs := pa.RegisterTypesAndOffsets() 680 last := loadRegOffset + x.regWidth(t) 681 if offs[loadRegOffset] != 0 { 682 // Document the problem before panicking. 683 for i := 0; i < len(rts); i++ { 684 rt := rts[i] 685 off := offs[i] 686 fmt.Printf("rt=%s, off=%d, rt.Width=%d, rt.Align=%d\n", rt.String(), off, rt.Size(), uint8(rt.Alignment())) 687 } 688 panic(fmt.Errorf("offset %d of requested register %d should be zero, source=%s", offs[loadRegOffset], loadRegOffset, source.LongString())) 689 } 690 691 if x.debug > 1 { 692 x.Printf("decompose arg %s has %d locs\n", source.LongString(), len(locs)) 693 } 694 695 for i := loadRegOffset; i < last; i++ { 696 rt := rts[i] 697 off := offs[i] 698 w := x.commonArgs[selKey{source, off, rt.Size(), rt}] 699 if w == nil { 700 w = x.newArgToMemOrRegs(source, w, off, i, rt, pos) 701 suffix := x.pathTo(source.Type, rt, off) 702 if suffix != "" { 703 x.splitSlotsIntoNames(locs, suffix, off, rt, w) 704 } 705 } 706 if t.IsPtrShaped() { 707 // Preserve the original store type. This ensures pointer type 708 // properties aren't discarded (e.g, notinheap). 709 if rt.Size() != t.Size() || len(pa.Registers) != 1 || i != loadRegOffset { 710 b.Func.Fatalf("incompatible store type %v and %v, i=%d", t, rt, i) 711 } 712 rt = t 713 } 714 mem = x.storeArgOrLoad(pos, b, w, mem, rt, storeOffset+off, i, storeRc.next(rt)) 715 } 716 return mem 717 } 718 719 u := source.Type 720 switch u.Kind() { 721 case types.TARRAY: 722 elem := u.Elem() 723 elemRO := x.regWidth(elem) 724 for i := int64(0); i < u.NumElem(); i++ { 725 elemOff := i * elem.Size() 726 mem = storeOneArg(x, pos, b, locs, indexNames[i], source, mem, elem, elemOff, storeOffset+elemOff, loadRegOffset, storeRc.next(elem)) 727 loadRegOffset += elemRO 728 pos = pos.WithNotStmt() 729 } 730 return mem 731 case types.TSTRUCT: 732 for i := 0; i < u.NumFields(); i++ { 733 fld := u.Field(i) 734 mem = storeOneArg(x, pos, b, locs, "."+fld.Sym.Name, source, mem, fld.Type, fld.Offset, storeOffset+fld.Offset, loadRegOffset, storeRc.next(fld.Type)) 735 loadRegOffset += x.regWidth(fld.Type) 736 pos = pos.WithNotStmt() 737 } 738 return mem 739 case types.TINT64, types.TUINT64: 740 if t.Size() == x.regSize { 741 break 742 } 743 tHi, tLo := x.intPairTypes(t.Kind()) 744 mem = storeOneArg(x, pos, b, locs, ".hi", source, mem, tHi, x.hiOffset, storeOffset+x.hiOffset, loadRegOffset+x.hiRo, storeRc.plus(x.hiRo)) 745 pos = pos.WithNotStmt() 746 return storeOneArg(x, pos, b, locs, ".lo", source, mem, tLo, x.lowOffset, storeOffset+x.lowOffset, loadRegOffset+x.loRo, storeRc.plus(x.loRo)) 747 case types.TINTER: 748 sfx := ".itab" 749 if u.IsEmptyInterface() { 750 sfx = ".type" 751 } 752 return storeTwoArg(x, pos, b, locs, sfx, ".idata", source, mem, x.typs.Uintptr, x.typs.BytePtr, 0, storeOffset, loadRegOffset, storeRc) 753 case types.TSTRING: 754 return storeTwoArg(x, pos, b, locs, ".ptr", ".len", source, mem, x.typs.BytePtr, x.typs.Int, 0, storeOffset, loadRegOffset, storeRc) 755 case types.TCOMPLEX64: 756 return storeTwoArg(x, pos, b, locs, ".real", ".imag", source, mem, x.typs.Float32, x.typs.Float32, 0, storeOffset, loadRegOffset, storeRc) 757 case types.TCOMPLEX128: 758 return storeTwoArg(x, pos, b, locs, ".real", ".imag", source, mem, x.typs.Float64, x.typs.Float64, 0, storeOffset, loadRegOffset, storeRc) 759 case types.TSLICE: 760 mem = storeOneArg(x, pos, b, locs, ".ptr", source, mem, x.typs.BytePtr, 0, storeOffset, loadRegOffset, storeRc.next(x.typs.BytePtr)) 761 return storeTwoArg(x, pos, b, locs, ".len", ".cap", source, mem, x.typs.Int, x.typs.Int, x.ptrSize, storeOffset+x.ptrSize, loadRegOffset+RO_slice_len, storeRc) 762 } 763 return nil 764 } 765 766 func (x *expandState) splitSlotsIntoNames(locs []*LocalSlot, suffix string, off int64, rt *types.Type, w *Value) { 767 wlocs := x.splitSlots(locs, suffix, off, rt) 768 for _, l := range wlocs { 769 old, ok := x.f.NamedValues[*l] 770 x.f.NamedValues[*l] = append(old, w) 771 if !ok { 772 x.f.Names = append(x.f.Names, l) 773 } 774 } 775 } 776 777 // decomposeLoad is a helper for storeArgOrLoad. 778 // It decomposes a Load into smaller parts and returns the new mem. 779 // If the type does not match one of the expected aggregate types, it returns nil instead. 780 // Parameters: 781 // 782 // pos -- the location of any generated code. 783 // b -- the block into which any generated code should normally be placed 784 // source -- the value, possibly an aggregate, to be stored. 785 // mem -- the mem flowing into this decomposition (loads depend on it, stores updated it) 786 // t -- the type of the value to be stored 787 // storeOffset -- if the value is stored in memory, it is stored at base (see storeRc) + offset 788 // loadRegOffset -- regarding source as a value in registers, the register offset in ABI1. Meaningful only if source is OpArg. 789 // storeRc -- storeRC; if the value is stored in registers, this specifies the registers. 790 // StoreRc also identifies whether the target is registers or memory, and has the base for the store operation. 791 // 792 // TODO -- this needs cleanup; it just works for SSA-able aggregates, and won't fully generalize to register-args aggregates. 793 func (x *expandState) decomposeLoad(pos src.XPos, b *Block, source, mem *Value, t *types.Type, storeOffset int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value { 794 u := source.Type 795 switch u.Kind() { 796 case types.TARRAY: 797 elem := u.Elem() 798 elemRO := x.regWidth(elem) 799 for i := int64(0); i < u.NumElem(); i++ { 800 elemOff := i * elem.Size() 801 mem = storeOneLoad(x, pos, b, source, mem, elem, elemOff, storeOffset+elemOff, loadRegOffset, storeRc.next(elem)) 802 loadRegOffset += elemRO 803 pos = pos.WithNotStmt() 804 } 805 return mem 806 case types.TSTRUCT: 807 for i := 0; i < u.NumFields(); i++ { 808 fld := u.Field(i) 809 mem = storeOneLoad(x, pos, b, source, mem, fld.Type, fld.Offset, storeOffset+fld.Offset, loadRegOffset, storeRc.next(fld.Type)) 810 loadRegOffset += x.regWidth(fld.Type) 811 pos = pos.WithNotStmt() 812 } 813 return mem 814 case types.TINT64, types.TUINT64: 815 if t.Size() == x.regSize { 816 break 817 } 818 tHi, tLo := x.intPairTypes(t.Kind()) 819 mem = storeOneLoad(x, pos, b, source, mem, tHi, x.hiOffset, storeOffset+x.hiOffset, loadRegOffset+x.hiRo, storeRc.plus(x.hiRo)) 820 pos = pos.WithNotStmt() 821 return storeOneLoad(x, pos, b, source, mem, tLo, x.lowOffset, storeOffset+x.lowOffset, loadRegOffset+x.loRo, storeRc.plus(x.loRo)) 822 case types.TINTER: 823 return storeTwoLoad(x, pos, b, source, mem, x.typs.Uintptr, x.typs.BytePtr, 0, storeOffset, loadRegOffset, storeRc) 824 case types.TSTRING: 825 return storeTwoLoad(x, pos, b, source, mem, x.typs.BytePtr, x.typs.Int, 0, storeOffset, loadRegOffset, storeRc) 826 case types.TCOMPLEX64: 827 return storeTwoLoad(x, pos, b, source, mem, x.typs.Float32, x.typs.Float32, 0, storeOffset, loadRegOffset, storeRc) 828 case types.TCOMPLEX128: 829 return storeTwoLoad(x, pos, b, source, mem, x.typs.Float64, x.typs.Float64, 0, storeOffset, loadRegOffset, storeRc) 830 case types.TSLICE: 831 mem = storeOneLoad(x, pos, b, source, mem, x.typs.BytePtr, 0, storeOffset, loadRegOffset, storeRc.next(x.typs.BytePtr)) 832 return storeTwoLoad(x, pos, b, source, mem, x.typs.Int, x.typs.Int, x.ptrSize, storeOffset+x.ptrSize, loadRegOffset+RO_slice_len, storeRc) 833 } 834 return nil 835 } 836 837 // storeOneArg creates a decomposed (one step) arg that is then stored. 838 // pos and b locate the store instruction, source is the "base" of the value input, 839 // mem is the input mem, t is the type in question, and offArg and offStore are the offsets from the respective bases. 840 func storeOneArg(x *expandState, pos src.XPos, b *Block, locs []*LocalSlot, suffix string, source, mem *Value, t *types.Type, argOffset, storeOffset int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value { 841 if x.debug > 1 { 842 x.indent(3) 843 defer x.indent(-3) 844 x.Printf("storeOneArg(%s; %s; %s; aO=%d; sO=%d; lrO=%d; %s)\n", source.LongString(), mem.String(), t.String(), argOffset, storeOffset, loadRegOffset, storeRc.String()) 845 } 846 847 w := x.commonArgs[selKey{source, argOffset, t.Size(), t}] 848 if w == nil { 849 w = x.newArgToMemOrRegs(source, w, argOffset, loadRegOffset, t, pos) 850 x.splitSlotsIntoNames(locs, suffix, argOffset, t, w) 851 } 852 return x.storeArgOrLoad(pos, b, w, mem, t, storeOffset, loadRegOffset, storeRc) 853 } 854 855 // storeOneLoad creates a decomposed (one step) load that is then stored. 856 func storeOneLoad(x *expandState, pos src.XPos, b *Block, source, mem *Value, t *types.Type, offArg, offStore int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value { 857 from := x.offsetFrom(source.Block, source.Args[0], offArg, types.NewPtr(t)) 858 w := source.Block.NewValue2(source.Pos, OpLoad, t, from, mem) 859 return x.storeArgOrLoad(pos, b, w, mem, t, offStore, loadRegOffset, storeRc) 860 } 861 862 func storeTwoArg(x *expandState, pos src.XPos, b *Block, locs []*LocalSlot, suffix1 string, suffix2 string, source, mem *Value, t1, t2 *types.Type, offArg, offStore int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value { 863 mem = storeOneArg(x, pos, b, locs, suffix1, source, mem, t1, offArg, offStore, loadRegOffset, storeRc.next(t1)) 864 pos = pos.WithNotStmt() 865 t1Size := t1.Size() 866 return storeOneArg(x, pos, b, locs, suffix2, source, mem, t2, offArg+t1Size, offStore+t1Size, loadRegOffset+1, storeRc) 867 } 868 869 // storeTwoLoad creates a pair of decomposed (one step) loads that are then stored. 870 // the elements of the pair must not require any additional alignment. 871 func storeTwoLoad(x *expandState, pos src.XPos, b *Block, source, mem *Value, t1, t2 *types.Type, offArg, offStore int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value { 872 mem = storeOneLoad(x, pos, b, source, mem, t1, offArg, offStore, loadRegOffset, storeRc.next(t1)) 873 pos = pos.WithNotStmt() 874 t1Size := t1.Size() 875 return storeOneLoad(x, pos, b, source, mem, t2, offArg+t1Size, offStore+t1Size, loadRegOffset+1, storeRc) 876 } 877 878 // storeArgOrLoad converts stores of SSA-able potentially aggregatable arguments (passed to a call) into a series of primitive-typed 879 // stores of non-aggregate types. It recursively walks up a chain of selectors until it reaches a Load or an Arg. 880 // If it does not reach a Load or an Arg, nothing happens; this allows a little freedom in phase ordering. 881 func (x *expandState) storeArgOrLoad(pos src.XPos, b *Block, source, mem *Value, t *types.Type, storeOffset int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value { 882 if x.debug > 1 { 883 x.indent(3) 884 defer x.indent(-3) 885 x.Printf("storeArgOrLoad(%s; %s; %s; %d; %s)\n", source.LongString(), mem.String(), t.String(), storeOffset, storeRc.String()) 886 } 887 888 // Start with Opcodes that can be disassembled 889 switch source.Op { 890 case OpCopy: 891 return x.storeArgOrLoad(pos, b, source.Args[0], mem, t, storeOffset, loadRegOffset, storeRc) 892 893 case OpLoad, OpDereference: 894 ret := x.decomposeLoad(pos, b, source, mem, t, storeOffset, loadRegOffset, storeRc) 895 if ret != nil { 896 return ret 897 } 898 899 case OpArg: 900 ret := x.decomposeArg(pos, b, source, mem, t, storeOffset, loadRegOffset, storeRc) 901 if ret != nil { 902 return ret 903 } 904 905 case OpArrayMake0, OpStructMake0: 906 // TODO(register args) is this correct for registers? 907 return mem 908 909 case OpStructMake1, OpStructMake2, OpStructMake3, OpStructMake4: 910 for i := 0; i < t.NumFields(); i++ { 911 fld := t.Field(i) 912 mem = x.storeArgOrLoad(pos, b, source.Args[i], mem, fld.Type, storeOffset+fld.Offset, 0, storeRc.next(fld.Type)) 913 pos = pos.WithNotStmt() 914 } 915 return mem 916 917 case OpArrayMake1: 918 return x.storeArgOrLoad(pos, b, source.Args[0], mem, t.Elem(), storeOffset, 0, storeRc.at(t, 0)) 919 920 case OpInt64Make: 921 tHi, tLo := x.intPairTypes(t.Kind()) 922 mem = x.storeArgOrLoad(pos, b, source.Args[0], mem, tHi, storeOffset+x.hiOffset, 0, storeRc.next(tHi)) 923 pos = pos.WithNotStmt() 924 return x.storeArgOrLoad(pos, b, source.Args[1], mem, tLo, storeOffset+x.lowOffset, 0, storeRc) 925 926 case OpComplexMake: 927 tPart := x.typs.Float32 928 wPart := t.Size() / 2 929 if wPart == 8 { 930 tPart = x.typs.Float64 931 } 932 mem = x.storeArgOrLoad(pos, b, source.Args[0], mem, tPart, storeOffset, 0, storeRc.next(tPart)) 933 pos = pos.WithNotStmt() 934 return x.storeArgOrLoad(pos, b, source.Args[1], mem, tPart, storeOffset+wPart, 0, storeRc) 935 936 case OpIMake: 937 mem = x.storeArgOrLoad(pos, b, source.Args[0], mem, x.typs.Uintptr, storeOffset, 0, storeRc.next(x.typs.Uintptr)) 938 pos = pos.WithNotStmt() 939 return x.storeArgOrLoad(pos, b, source.Args[1], mem, x.typs.BytePtr, storeOffset+x.ptrSize, 0, storeRc) 940 941 case OpStringMake: 942 mem = x.storeArgOrLoad(pos, b, source.Args[0], mem, x.typs.BytePtr, storeOffset, 0, storeRc.next(x.typs.BytePtr)) 943 pos = pos.WithNotStmt() 944 return x.storeArgOrLoad(pos, b, source.Args[1], mem, x.typs.Int, storeOffset+x.ptrSize, 0, storeRc) 945 946 case OpSliceMake: 947 mem = x.storeArgOrLoad(pos, b, source.Args[0], mem, x.typs.BytePtr, storeOffset, 0, storeRc.next(x.typs.BytePtr)) 948 pos = pos.WithNotStmt() 949 mem = x.storeArgOrLoad(pos, b, source.Args[1], mem, x.typs.Int, storeOffset+x.ptrSize, 0, storeRc.next(x.typs.Int)) 950 return x.storeArgOrLoad(pos, b, source.Args[2], mem, x.typs.Int, storeOffset+2*x.ptrSize, 0, storeRc) 951 } 952 953 // For nodes that cannot be taken apart -- OpSelectN, other structure selectors. 954 switch t.Kind() { 955 case types.TARRAY: 956 elt := t.Elem() 957 if source.Type != t && t.NumElem() == 1 && elt.Size() == t.Size() && t.Size() == x.regSize { 958 t = removeTrivialWrapperTypes(t) 959 // it could be a leaf type, but the "leaf" could be complex64 (for example) 960 return x.storeArgOrLoad(pos, b, source, mem, t, storeOffset, loadRegOffset, storeRc) 961 } 962 eltRO := x.regWidth(elt) 963 source.Type = t 964 for i := int64(0); i < t.NumElem(); i++ { 965 sel := source.Block.NewValue1I(pos, OpArraySelect, elt, i, source) 966 mem = x.storeArgOrLoad(pos, b, sel, mem, elt, storeOffset+i*elt.Size(), loadRegOffset, storeRc.at(t, 0)) 967 loadRegOffset += eltRO 968 pos = pos.WithNotStmt() 969 } 970 return mem 971 972 case types.TSTRUCT: 973 if source.Type != t && t.NumFields() == 1 && t.Field(0).Type.Size() == t.Size() && t.Size() == x.regSize { 974 // This peculiar test deals with accesses to immediate interface data. 975 // It works okay because everything is the same size. 976 // Example code that triggers this can be found in go/constant/value.go, function ToComplex 977 // v119 (+881) = IData <intVal> v6 978 // v121 (+882) = StaticLECall <floatVal,mem> {AuxCall{"".itof([intVal,0])[floatVal,8]}} [16] v119 v1 979 // This corresponds to the generic rewrite rule "(StructSelect [0] (IData x)) => (IData x)" 980 // Guard against "struct{struct{*foo}}" 981 // Other rewriting phases create minor glitches when they transform IData, for instance the 982 // interface-typed Arg "x" of ToFloat in go/constant/value.go 983 // v6 (858) = Arg <Value> {x} (x[Value], x[Value]) 984 // is rewritten by decomposeArgs into 985 // v141 (858) = Arg <uintptr> {x} 986 // v139 (858) = Arg <*uint8> {x} [8] 987 // because of a type case clause on line 862 of go/constant/value.go 988 // case intVal: 989 // return itof(x) 990 // v139 is later stored as an intVal == struct{val *big.Int} which naively requires the fields of 991 // of a *uint8, which does not succeed. 992 t = removeTrivialWrapperTypes(t) 993 // it could be a leaf type, but the "leaf" could be complex64 (for example) 994 return x.storeArgOrLoad(pos, b, source, mem, t, storeOffset, loadRegOffset, storeRc) 995 } 996 997 source.Type = t 998 for i := 0; i < t.NumFields(); i++ { 999 fld := t.Field(i) 1000 sel := source.Block.NewValue1I(pos, OpStructSelect, fld.Type, int64(i), source) 1001 mem = x.storeArgOrLoad(pos, b, sel, mem, fld.Type, storeOffset+fld.Offset, loadRegOffset, storeRc.next(fld.Type)) 1002 loadRegOffset += x.regWidth(fld.Type) 1003 pos = pos.WithNotStmt() 1004 } 1005 return mem 1006 1007 case types.TINT64, types.TUINT64: 1008 if t.Size() == x.regSize { 1009 break 1010 } 1011 tHi, tLo := x.intPairTypes(t.Kind()) 1012 sel := source.Block.NewValue1(pos, OpInt64Hi, tHi, source) 1013 mem = x.storeArgOrLoad(pos, b, sel, mem, tHi, storeOffset+x.hiOffset, loadRegOffset+x.hiRo, storeRc.plus(x.hiRo)) 1014 pos = pos.WithNotStmt() 1015 sel = source.Block.NewValue1(pos, OpInt64Lo, tLo, source) 1016 return x.storeArgOrLoad(pos, b, sel, mem, tLo, storeOffset+x.lowOffset, loadRegOffset+x.loRo, storeRc.plus(x.hiRo)) 1017 1018 case types.TINTER: 1019 sel := source.Block.NewValue1(pos, OpITab, x.typs.BytePtr, source) 1020 mem = x.storeArgOrLoad(pos, b, sel, mem, x.typs.BytePtr, storeOffset, loadRegOffset, storeRc.next(x.typs.BytePtr)) 1021 pos = pos.WithNotStmt() 1022 sel = source.Block.NewValue1(pos, OpIData, x.typs.BytePtr, source) 1023 return x.storeArgOrLoad(pos, b, sel, mem, x.typs.BytePtr, storeOffset+x.ptrSize, loadRegOffset+RO_iface_data, storeRc) 1024 1025 case types.TSTRING: 1026 sel := source.Block.NewValue1(pos, OpStringPtr, x.typs.BytePtr, source) 1027 mem = x.storeArgOrLoad(pos, b, sel, mem, x.typs.BytePtr, storeOffset, loadRegOffset, storeRc.next(x.typs.BytePtr)) 1028 pos = pos.WithNotStmt() 1029 sel = source.Block.NewValue1(pos, OpStringLen, x.typs.Int, source) 1030 return x.storeArgOrLoad(pos, b, sel, mem, x.typs.Int, storeOffset+x.ptrSize, loadRegOffset+RO_string_len, storeRc) 1031 1032 case types.TSLICE: 1033 et := types.NewPtr(t.Elem()) 1034 sel := source.Block.NewValue1(pos, OpSlicePtr, et, source) 1035 mem = x.storeArgOrLoad(pos, b, sel, mem, et, storeOffset, loadRegOffset, storeRc.next(et)) 1036 pos = pos.WithNotStmt() 1037 sel = source.Block.NewValue1(pos, OpSliceLen, x.typs.Int, source) 1038 mem = x.storeArgOrLoad(pos, b, sel, mem, x.typs.Int, storeOffset+x.ptrSize, loadRegOffset+RO_slice_len, storeRc.next(x.typs.Int)) 1039 sel = source.Block.NewValue1(pos, OpSliceCap, x.typs.Int, source) 1040 return x.storeArgOrLoad(pos, b, sel, mem, x.typs.Int, storeOffset+2*x.ptrSize, loadRegOffset+RO_slice_cap, storeRc) 1041 1042 case types.TCOMPLEX64: 1043 sel := source.Block.NewValue1(pos, OpComplexReal, x.typs.Float32, source) 1044 mem = x.storeArgOrLoad(pos, b, sel, mem, x.typs.Float32, storeOffset, loadRegOffset, storeRc.next(x.typs.Float32)) 1045 pos = pos.WithNotStmt() 1046 sel = source.Block.NewValue1(pos, OpComplexImag, x.typs.Float32, source) 1047 return x.storeArgOrLoad(pos, b, sel, mem, x.typs.Float32, storeOffset+4, loadRegOffset+RO_complex_imag, storeRc) 1048 1049 case types.TCOMPLEX128: 1050 sel := source.Block.NewValue1(pos, OpComplexReal, x.typs.Float64, source) 1051 mem = x.storeArgOrLoad(pos, b, sel, mem, x.typs.Float64, storeOffset, loadRegOffset, storeRc.next(x.typs.Float64)) 1052 pos = pos.WithNotStmt() 1053 sel = source.Block.NewValue1(pos, OpComplexImag, x.typs.Float64, source) 1054 return x.storeArgOrLoad(pos, b, sel, mem, x.typs.Float64, storeOffset+8, loadRegOffset+RO_complex_imag, storeRc) 1055 } 1056 1057 s := mem 1058 if source.Op == OpDereference { 1059 source.Op = OpLoad // For purposes of parameter passing expansion, a Dereference is a Load. 1060 } 1061 if storeRc.hasRegs() { 1062 storeRc.addArg(source) 1063 } else { 1064 dst := x.offsetFrom(b, storeRc.storeDest, storeOffset, types.NewPtr(t)) 1065 s = b.NewValue3A(pos, OpStore, types.TypeMem, t, dst, source, mem) 1066 } 1067 if x.debug > 1 { 1068 x.Printf("-->storeArg returns %s, storeRc=%s\n", s.LongString(), storeRc.String()) 1069 } 1070 return s 1071 } 1072 1073 // rewriteArgs replaces all the call-parameter Args to a call with their register translation (if any). 1074 // Preceding parameters (code pointers, closure pointer) are preserved, and the memory input is modified 1075 // to account for any parameter stores required. 1076 // Any of the old Args that have their use count fall to zero are marked OpInvalid. 1077 func (x *expandState) rewriteArgs(v *Value, firstArg int) { 1078 if x.debug > 1 { 1079 x.indent(3) 1080 defer x.indent(-3) 1081 x.Printf("rewriteArgs(%s; %d)\n", v.LongString(), firstArg) 1082 } 1083 // Thread the stores on the memory arg 1084 aux := v.Aux.(*AuxCall) 1085 m0 := v.MemoryArg() 1086 mem := m0 1087 newArgs := []*Value{} 1088 oldArgs := []*Value{} 1089 sp := x.sp 1090 if v.Op == OpTailLECall { 1091 // For tail call, we unwind the frame before the call so we'll use the caller's 1092 // SP. 1093 sp = x.f.Entry.NewValue0(src.NoXPos, OpGetCallerSP, x.typs.Uintptr) 1094 } 1095 for i, a := range v.Args[firstArg : len(v.Args)-1] { // skip leading non-parameter SSA Args and trailing mem SSA Arg. 1096 oldArgs = append(oldArgs, a) 1097 auxI := int64(i) 1098 aRegs := aux.RegsOfArg(auxI) 1099 aType := aux.TypeOfArg(auxI) 1100 if len(aRegs) == 0 && a.Op == OpDereference { 1101 aOffset := aux.OffsetOfArg(auxI) 1102 if a.MemoryArg() != m0 { 1103 x.f.Fatalf("Op...LECall and OpDereference have mismatched mem, %s and %s", v.LongString(), a.LongString()) 1104 } 1105 if v.Op == OpTailLECall { 1106 // It's common for a tail call passing the same arguments (e.g. method wrapper), 1107 // so this would be a self copy. Detect this and optimize it out. 1108 a0 := a.Args[0] 1109 if a0.Op == OpLocalAddr { 1110 n := a0.Aux.(*ir.Name) 1111 if n.Class == ir.PPARAM && n.FrameOffset()+x.f.Config.ctxt.Arch.FixedFrameSize == aOffset { 1112 continue 1113 } 1114 } 1115 } 1116 // "Dereference" of addressed (probably not-SSA-eligible) value becomes Move 1117 // TODO(register args) this will be more complicated with registers in the picture. 1118 mem = x.rewriteDereference(v.Block, sp, a, mem, aOffset, aux.SizeOfArg(auxI), aType, a.Pos) 1119 } else { 1120 var rc registerCursor 1121 var result *[]*Value 1122 var aOffset int64 1123 if len(aRegs) > 0 { 1124 result = &newArgs 1125 } else { 1126 aOffset = aux.OffsetOfArg(auxI) 1127 } 1128 if v.Op == OpTailLECall && a.Op == OpArg && a.AuxInt == 0 { 1129 // It's common for a tail call passing the same arguments (e.g. method wrapper), 1130 // so this would be a self copy. Detect this and optimize it out. 1131 n := a.Aux.(*ir.Name) 1132 if n.Class == ir.PPARAM && n.FrameOffset()+x.f.Config.ctxt.Arch.FixedFrameSize == aOffset { 1133 continue 1134 } 1135 } 1136 if x.debug > 1 { 1137 x.Printf("...storeArg %s, %v, %d\n", a.LongString(), aType, aOffset) 1138 } 1139 rc.init(aRegs, aux.abiInfo, result, sp) 1140 mem = x.storeArgOrLoad(a.Pos, v.Block, a, mem, aType, aOffset, 0, rc) 1141 } 1142 } 1143 var preArgStore [2]*Value 1144 preArgs := append(preArgStore[:0], v.Args[0:firstArg]...) 1145 v.resetArgs() 1146 v.AddArgs(preArgs...) 1147 v.AddArgs(newArgs...) 1148 v.AddArg(mem) 1149 for _, a := range oldArgs { 1150 if a.Uses == 0 { 1151 x.invalidateRecursively(a) 1152 } 1153 } 1154 1155 return 1156 } 1157 1158 func (x *expandState) invalidateRecursively(a *Value) { 1159 var s string 1160 if x.debug > 0 { 1161 plus := " " 1162 if a.Pos.IsStmt() == src.PosIsStmt { 1163 plus = " +" 1164 } 1165 s = a.String() + plus + a.Pos.LineNumber() + " " + a.LongString() 1166 if x.debug > 1 { 1167 x.Printf("...marking %v unused\n", s) 1168 } 1169 } 1170 lost := a.invalidateRecursively() 1171 if x.debug&1 != 0 && lost { // For odd values of x.debug, do this. 1172 x.Printf("Lost statement marker in %s on former %s\n", base.Ctxt.Pkgpath+"."+x.f.Name, s) 1173 } 1174 } 1175 1176 // expandCalls converts LE (Late Expansion) calls that act like they receive value args into a lower-level form 1177 // that is more oriented to a platform's ABI. The SelectN operations that extract results are rewritten into 1178 // more appropriate forms, and any StructMake or ArrayMake inputs are decomposed until non-struct values are 1179 // reached. On the callee side, OpArg nodes are not decomposed until this phase is run. 1180 // TODO results should not be lowered until this phase. 1181 func expandCalls(f *Func) { 1182 // Calls that need lowering have some number of inputs, including a memory input, 1183 // and produce a tuple of (value1, value2, ..., mem) where valueK may or may not be SSA-able. 1184 1185 // With the current ABI those inputs need to be converted into stores to memory, 1186 // rethreading the call's memory input to the first, and the new call now receiving the last. 1187 1188 // With the current ABI, the outputs need to be converted to loads, which will all use the call's 1189 // memory output as their input. 1190 sp, _ := f.spSb() 1191 x := &expandState{ 1192 f: f, 1193 abi1: f.ABI1, 1194 debug: f.pass.debug, 1195 canSSAType: f.fe.CanSSA, 1196 regSize: f.Config.RegSize, 1197 sp: sp, 1198 typs: &f.Config.Types, 1199 ptrSize: f.Config.PtrSize, 1200 namedSelects: make(map[*Value][]namedVal), 1201 sdom: f.Sdom(), 1202 commonArgs: make(map[selKey]*Value), 1203 memForCall: make(map[ID]*Value), 1204 transformedSelects: make(map[ID]bool), 1205 } 1206 1207 // For 32-bit, need to deal with decomposition of 64-bit integers, which depends on endianness. 1208 if f.Config.BigEndian { 1209 x.lowOffset, x.hiOffset = 4, 0 1210 x.loRo, x.hiRo = 1, 0 1211 } else { 1212 x.lowOffset, x.hiOffset = 0, 4 1213 x.loRo, x.hiRo = 0, 1 1214 } 1215 1216 if x.debug > 1 { 1217 x.Printf("\nexpandsCalls(%s)\n", f.Name) 1218 } 1219 1220 for i, name := range f.Names { 1221 t := name.Type 1222 if x.isAlreadyExpandedAggregateType(t) { 1223 for j, v := range f.NamedValues[*name] { 1224 if v.Op == OpSelectN || v.Op == OpArg && x.isAlreadyExpandedAggregateType(v.Type) { 1225 ns := x.namedSelects[v] 1226 x.namedSelects[v] = append(ns, namedVal{locIndex: i, valIndex: j}) 1227 } 1228 } 1229 } 1230 } 1231 1232 // TODO if too slow, whole program iteration can be replaced w/ slices of appropriate values, accumulated in first loop here. 1233 1234 // Step 0: rewrite the calls to convert args to calls into stores/register movement. 1235 for _, b := range f.Blocks { 1236 for _, v := range b.Values { 1237 firstArg := 0 1238 switch v.Op { 1239 case OpStaticLECall, OpTailLECall: 1240 case OpInterLECall: 1241 firstArg = 1 1242 case OpClosureLECall: 1243 firstArg = 2 1244 default: 1245 continue 1246 } 1247 x.rewriteArgs(v, firstArg) 1248 } 1249 if isBlockMultiValueExit(b) { 1250 x.indent(3) 1251 // Very similar to code in rewriteArgs, but results instead of args. 1252 v := b.Controls[0] 1253 m0 := v.MemoryArg() 1254 mem := m0 1255 aux := f.OwnAux 1256 allResults := []*Value{} 1257 if x.debug > 1 { 1258 x.Printf("multiValueExit rewriting %s\n", v.LongString()) 1259 } 1260 var oldArgs []*Value 1261 for j, a := range v.Args[:len(v.Args)-1] { 1262 oldArgs = append(oldArgs, a) 1263 i := int64(j) 1264 auxType := aux.TypeOfResult(i) 1265 auxBase := b.NewValue2A(v.Pos, OpLocalAddr, types.NewPtr(auxType), aux.NameOfResult(i), x.sp, mem) 1266 auxOffset := int64(0) 1267 auxSize := aux.SizeOfResult(i) 1268 aRegs := aux.RegsOfResult(int64(j)) 1269 if len(aRegs) == 0 && a.Op == OpDereference { 1270 // Avoid a self-move, and if one is detected try to remove the already-inserted VarDef for the assignment that won't happen. 1271 if dAddr, dMem := a.Args[0], a.Args[1]; dAddr.Op == OpLocalAddr && dAddr.Args[0].Op == OpSP && 1272 dAddr.Args[1] == dMem && dAddr.Aux == aux.NameOfResult(i) { 1273 if dMem.Op == OpVarDef && dMem.Aux == dAddr.Aux { 1274 dMem.copyOf(dMem.MemoryArg()) // elide the VarDef 1275 } 1276 continue 1277 } 1278 mem = x.rewriteDereference(v.Block, auxBase, a, mem, auxOffset, auxSize, auxType, a.Pos) 1279 } else { 1280 if a.Op == OpLoad && a.Args[0].Op == OpLocalAddr { 1281 addr := a.Args[0] // This is a self-move. // TODO(register args) do what here for registers? 1282 if addr.MemoryArg() == a.MemoryArg() && addr.Aux == aux.NameOfResult(i) { 1283 continue 1284 } 1285 } 1286 var rc registerCursor 1287 var result *[]*Value 1288 if len(aRegs) > 0 { 1289 result = &allResults 1290 } 1291 rc.init(aRegs, aux.abiInfo, result, auxBase) 1292 mem = x.storeArgOrLoad(v.Pos, b, a, mem, aux.TypeOfResult(i), auxOffset, 0, rc) 1293 } 1294 } 1295 v.resetArgs() 1296 v.AddArgs(allResults...) 1297 v.AddArg(mem) 1298 v.Type = types.NewResults(append(abi.RegisterTypes(aux.abiInfo.OutParams()), types.TypeMem)) 1299 b.SetControl(v) 1300 for _, a := range oldArgs { 1301 if a.Uses == 0 { 1302 if x.debug > 1 { 1303 x.Printf("...marking %v unused\n", a.LongString()) 1304 } 1305 x.invalidateRecursively(a) 1306 } 1307 } 1308 if x.debug > 1 { 1309 x.Printf("...multiValueExit new result %s\n", v.LongString()) 1310 } 1311 x.indent(-3) 1312 } 1313 } 1314 1315 // Step 1: any stores of aggregates remaining are believed to be sourced from call results or args. 1316 // Decompose those stores into a series of smaller stores, adding selection ops as necessary. 1317 for _, b := range f.Blocks { 1318 for _, v := range b.Values { 1319 if v.Op == OpStore { 1320 t := v.Aux.(*types.Type) 1321 source := v.Args[1] 1322 tSrc := source.Type 1323 iAEATt := x.isAlreadyExpandedAggregateType(t) 1324 1325 if !iAEATt { 1326 // guarding against store immediate struct into interface data field -- store type is *uint8 1327 // TODO can this happen recursively? 1328 iAEATt = x.isAlreadyExpandedAggregateType(tSrc) 1329 if iAEATt { 1330 t = tSrc 1331 } 1332 } 1333 dst, mem := v.Args[0], v.Args[2] 1334 mem = x.storeArgOrLoad(v.Pos, b, source, mem, t, 0, 0, registerCursor{storeDest: dst}) 1335 v.copyOf(mem) 1336 } 1337 } 1338 } 1339 1340 val2Preds := make(map[*Value]int32) // Used to accumulate dependency graph of selection operations for topological ordering. 1341 1342 // Step 2: transform or accumulate selection operations for rewrite in topological order. 1343 // 1344 // Aggregate types that have already (in earlier phases) been transformed must be lowered comprehensively to finish 1345 // the transformation (user-defined structs and arrays, slices, strings, interfaces, complex, 64-bit on 32-bit architectures), 1346 // 1347 // Any select-for-addressing applied to call results can be transformed directly. 1348 for _, b := range f.Blocks { 1349 for _, v := range b.Values { 1350 // Accumulate chains of selectors for processing in topological order 1351 switch v.Op { 1352 case OpStructSelect, OpArraySelect, 1353 OpIData, OpITab, 1354 OpStringPtr, OpStringLen, 1355 OpSlicePtr, OpSliceLen, OpSliceCap, OpSlicePtrUnchecked, 1356 OpComplexReal, OpComplexImag, 1357 OpInt64Hi, OpInt64Lo: 1358 w := v.Args[0] 1359 switch w.Op { 1360 case OpStructSelect, OpArraySelect, OpSelectN, OpArg: 1361 val2Preds[w] += 1 1362 if x.debug > 1 { 1363 x.Printf("v2p[%s] = %d\n", w.LongString(), val2Preds[w]) 1364 } 1365 } 1366 fallthrough 1367 1368 case OpSelectN: 1369 if _, ok := val2Preds[v]; !ok { 1370 val2Preds[v] = 0 1371 if x.debug > 1 { 1372 x.Printf("v2p[%s] = %d\n", v.LongString(), val2Preds[v]) 1373 } 1374 } 1375 1376 case OpArg: 1377 if !x.isAlreadyExpandedAggregateType(v.Type) { 1378 continue 1379 } 1380 if _, ok := val2Preds[v]; !ok { 1381 val2Preds[v] = 0 1382 if x.debug > 1 { 1383 x.Printf("v2p[%s] = %d\n", v.LongString(), val2Preds[v]) 1384 } 1385 } 1386 1387 case OpSelectNAddr: 1388 // Do these directly, there are no chains of selectors. 1389 call := v.Args[0] 1390 which := v.AuxInt 1391 aux := call.Aux.(*AuxCall) 1392 pt := v.Type 1393 off := x.offsetFrom(x.f.Entry, x.sp, aux.OffsetOfResult(which), pt) 1394 v.copyOf(off) 1395 } 1396 } 1397 } 1398 1399 // Step 3: Compute topological order of selectors, 1400 // then process it in reverse to eliminate duplicates, 1401 // then forwards to rewrite selectors. 1402 // 1403 // All chains of selectors end up in same block as the call. 1404 1405 // Compilation must be deterministic, so sort after extracting first zeroes from map. 1406 // Sorting allows dominators-last order within each batch, 1407 // so that the backwards scan for duplicates will most often find copies from dominating blocks (it is best-effort). 1408 var toProcess []*Value 1409 less := func(i, j int) bool { 1410 vi, vj := toProcess[i], toProcess[j] 1411 bi, bj := vi.Block, vj.Block 1412 if bi == bj { 1413 return vi.ID < vj.ID 1414 } 1415 return x.sdom.domorder(bi) > x.sdom.domorder(bj) // reverse the order to put dominators last. 1416 } 1417 1418 // Accumulate order in allOrdered 1419 var allOrdered []*Value 1420 for v, n := range val2Preds { 1421 if n == 0 { 1422 allOrdered = append(allOrdered, v) 1423 } 1424 } 1425 last := 0 // allOrdered[0:last] has been top-sorted and processed 1426 for len(val2Preds) > 0 { 1427 toProcess = allOrdered[last:] 1428 last = len(allOrdered) 1429 sort.SliceStable(toProcess, less) 1430 for _, v := range toProcess { 1431 delete(val2Preds, v) 1432 if v.Op == OpArg { 1433 continue // no Args[0], hence done. 1434 } 1435 w := v.Args[0] 1436 n, ok := val2Preds[w] 1437 if !ok { 1438 continue 1439 } 1440 if n == 1 { 1441 allOrdered = append(allOrdered, w) 1442 delete(val2Preds, w) 1443 continue 1444 } 1445 val2Preds[w] = n - 1 1446 } 1447 } 1448 1449 x.commonSelectors = make(map[selKey]*Value) 1450 // Rewrite duplicate selectors as copies where possible. 1451 for i := len(allOrdered) - 1; i >= 0; i-- { 1452 v := allOrdered[i] 1453 if v.Op == OpArg { 1454 continue 1455 } 1456 w := v.Args[0] 1457 if w.Op == OpCopy { 1458 for w.Op == OpCopy { 1459 w = w.Args[0] 1460 } 1461 v.SetArg(0, w) 1462 } 1463 typ := v.Type 1464 if typ.IsMemory() { 1465 continue // handled elsewhere, not an indexable result 1466 } 1467 size := typ.Size() 1468 offset := int64(0) 1469 switch v.Op { 1470 case OpStructSelect: 1471 if w.Type.Kind() == types.TSTRUCT { 1472 offset = w.Type.FieldOff(int(v.AuxInt)) 1473 } else { // Immediate interface data artifact, offset is zero. 1474 f.Fatalf("Expand calls interface data problem, func %s, v=%s, w=%s\n", f.Name, v.LongString(), w.LongString()) 1475 } 1476 case OpArraySelect: 1477 offset = size * v.AuxInt 1478 case OpSelectN: 1479 offset = v.AuxInt // offset is just a key, really. 1480 case OpInt64Hi: 1481 offset = x.hiOffset 1482 case OpInt64Lo: 1483 offset = x.lowOffset 1484 case OpStringLen, OpSliceLen, OpIData: 1485 offset = x.ptrSize 1486 case OpSliceCap: 1487 offset = 2 * x.ptrSize 1488 case OpComplexImag: 1489 offset = size 1490 } 1491 sk := selKey{from: w, size: size, offsetOrIndex: offset, typ: typ} 1492 dupe := x.commonSelectors[sk] 1493 if dupe == nil { 1494 x.commonSelectors[sk] = v 1495 } else if x.sdom.IsAncestorEq(dupe.Block, v.Block) { 1496 if x.debug > 1 { 1497 x.Printf("Duplicate, make %s copy of %s\n", v, dupe) 1498 } 1499 v.copyOf(dupe) 1500 } else { 1501 // Because values are processed in dominator order, the old common[s] will never dominate after a miss is seen. 1502 // Installing the new value might match some future values. 1503 x.commonSelectors[sk] = v 1504 } 1505 } 1506 1507 // Indices of entries in f.Names that need to be deleted. 1508 var toDelete []namedVal 1509 1510 // Rewrite selectors. 1511 for i, v := range allOrdered { 1512 if x.debug > 1 { 1513 b := v.Block 1514 x.Printf("allOrdered[%d] = b%d, %s, uses=%d\n", i, b.ID, v.LongString(), v.Uses) 1515 } 1516 if v.Uses == 0 { 1517 x.invalidateRecursively(v) 1518 continue 1519 } 1520 if v.Op == OpCopy { 1521 continue 1522 } 1523 locs := x.rewriteSelect(v, v, 0, 0) 1524 // Install new names. 1525 if v.Type.IsMemory() { 1526 continue 1527 } 1528 // Leaf types may have debug locations 1529 if !x.isAlreadyExpandedAggregateType(v.Type) { 1530 for _, l := range locs { 1531 if _, ok := f.NamedValues[*l]; !ok { 1532 f.Names = append(f.Names, l) 1533 } 1534 f.NamedValues[*l] = append(f.NamedValues[*l], v) 1535 } 1536 continue 1537 } 1538 if ns, ok := x.namedSelects[v]; ok { 1539 // Not-leaf types that had debug locations need to lose them. 1540 1541 toDelete = append(toDelete, ns...) 1542 } 1543 } 1544 1545 deleteNamedVals(f, toDelete) 1546 1547 // Step 4: rewrite the calls themselves, correcting the type. 1548 for _, b := range f.Blocks { 1549 for _, v := range b.Values { 1550 switch v.Op { 1551 case OpArg: 1552 x.rewriteArgToMemOrRegs(v) 1553 case OpStaticLECall: 1554 v.Op = OpStaticCall 1555 rts := abi.RegisterTypes(v.Aux.(*AuxCall).abiInfo.OutParams()) 1556 v.Type = types.NewResults(append(rts, types.TypeMem)) 1557 case OpTailLECall: 1558 v.Op = OpTailCall 1559 rts := abi.RegisterTypes(v.Aux.(*AuxCall).abiInfo.OutParams()) 1560 v.Type = types.NewResults(append(rts, types.TypeMem)) 1561 case OpClosureLECall: 1562 v.Op = OpClosureCall 1563 rts := abi.RegisterTypes(v.Aux.(*AuxCall).abiInfo.OutParams()) 1564 v.Type = types.NewResults(append(rts, types.TypeMem)) 1565 case OpInterLECall: 1566 v.Op = OpInterCall 1567 rts := abi.RegisterTypes(v.Aux.(*AuxCall).abiInfo.OutParams()) 1568 v.Type = types.NewResults(append(rts, types.TypeMem)) 1569 } 1570 } 1571 } 1572 1573 // Step 5: dedup OpArgXXXReg values. Mostly it is already dedup'd by commonArgs, 1574 // but there are cases that we have same OpArgXXXReg values with different types. 1575 // E.g. string is sometimes decomposed as { *int8, int }, sometimes as { unsafe.Pointer, uintptr }. 1576 // (Can we avoid that?) 1577 var IArg, FArg [32]*Value 1578 for _, v := range f.Entry.Values { 1579 switch v.Op { 1580 case OpArgIntReg: 1581 i := v.AuxInt 1582 if w := IArg[i]; w != nil { 1583 if w.Type.Size() != v.Type.Size() { 1584 f.Fatalf("incompatible OpArgIntReg [%d]: %s and %s", i, v.LongString(), w.LongString()) 1585 } 1586 if w.Type.IsUnsafePtr() && !v.Type.IsUnsafePtr() { 1587 // Update unsafe.Pointer type if we know the actual pointer type. 1588 w.Type = v.Type 1589 } 1590 // TODO: don't dedup pointer and scalar? Rewrite to OpConvert? Can it happen? 1591 v.copyOf(w) 1592 } else { 1593 IArg[i] = v 1594 } 1595 case OpArgFloatReg: 1596 i := v.AuxInt 1597 if w := FArg[i]; w != nil { 1598 if w.Type.Size() != v.Type.Size() { 1599 f.Fatalf("incompatible OpArgFloatReg [%d]: %v and %v", i, v, w) 1600 } 1601 v.copyOf(w) 1602 } else { 1603 FArg[i] = v 1604 } 1605 } 1606 } 1607 1608 // Step 6: elide any copies introduced. 1609 // Update named values. 1610 for _, name := range f.Names { 1611 values := f.NamedValues[*name] 1612 for i, v := range values { 1613 if v.Op == OpCopy { 1614 a := v.Args[0] 1615 for a.Op == OpCopy { 1616 a = a.Args[0] 1617 } 1618 values[i] = a 1619 } 1620 } 1621 } 1622 for _, b := range f.Blocks { 1623 for _, v := range b.Values { 1624 for i, a := range v.Args { 1625 if a.Op != OpCopy { 1626 continue 1627 } 1628 aa := copySource(a) 1629 v.SetArg(i, aa) 1630 for a.Uses == 0 { 1631 b := a.Args[0] 1632 x.invalidateRecursively(a) 1633 a = b 1634 } 1635 } 1636 } 1637 } 1638 1639 // Rewriting can attach lines to values that are unlikely to survive code generation, so move them to a use. 1640 for _, b := range f.Blocks { 1641 for _, v := range b.Values { 1642 for _, a := range v.Args { 1643 if a.Pos.IsStmt() != src.PosIsStmt { 1644 continue 1645 } 1646 if a.Type.IsMemory() { 1647 continue 1648 } 1649 if a.Pos.Line() != v.Pos.Line() { 1650 continue 1651 } 1652 if !a.Pos.SameFile(v.Pos) { 1653 continue 1654 } 1655 switch a.Op { 1656 case OpArgIntReg, OpArgFloatReg, OpSelectN: 1657 v.Pos = v.Pos.WithIsStmt() 1658 a.Pos = a.Pos.WithDefaultStmt() 1659 } 1660 } 1661 } 1662 } 1663 } 1664 1665 // rewriteArgToMemOrRegs converts OpArg v in-place into the register version of v, 1666 // if that is appropriate. 1667 func (x *expandState) rewriteArgToMemOrRegs(v *Value) *Value { 1668 if x.debug > 1 { 1669 x.indent(3) 1670 defer x.indent(-3) 1671 x.Printf("rewriteArgToMemOrRegs(%s)\n", v.LongString()) 1672 } 1673 pa := x.prAssignForArg(v) 1674 switch len(pa.Registers) { 1675 case 0: 1676 frameOff := v.Aux.(*ir.Name).FrameOffset() 1677 if pa.Offset() != int32(frameOff+x.f.ABISelf.LocalsOffset()) { 1678 panic(fmt.Errorf("Parameter assignment %d and OpArg.Aux frameOffset %d disagree, op=%s", 1679 pa.Offset(), frameOff, v.LongString())) 1680 } 1681 case 1: 1682 t := v.Type 1683 key := selKey{v, 0, t.Size(), t} 1684 w := x.commonArgs[key] 1685 if w != nil && w.Uses != 0 { // do not reuse dead value 1686 v.copyOf(w) 1687 break 1688 } 1689 r := pa.Registers[0] 1690 var i int64 1691 v.Op, i = ArgOpAndRegisterFor(r, x.f.ABISelf) 1692 v.Aux = &AuxNameOffset{v.Aux.(*ir.Name), 0} 1693 v.AuxInt = i 1694 x.commonArgs[key] = v 1695 1696 default: 1697 panic(badVal("Saw unexpanded OpArg", v)) 1698 } 1699 if x.debug > 1 { 1700 x.Printf("-->%s\n", v.LongString()) 1701 } 1702 return v 1703 } 1704 1705 // newArgToMemOrRegs either rewrites toReplace into an OpArg referencing memory or into an OpArgXXXReg to a register, 1706 // or rewrites it into a copy of the appropriate OpArgXXX. The actual OpArgXXX is determined by combining baseArg (an OpArg) 1707 // with offset, regOffset, and t to determine which portion of it to reference (either all or a part, in memory or in registers). 1708 func (x *expandState) newArgToMemOrRegs(baseArg, toReplace *Value, offset int64, regOffset Abi1RO, t *types.Type, pos src.XPos) *Value { 1709 if x.debug > 1 { 1710 x.indent(3) 1711 defer x.indent(-3) 1712 x.Printf("newArgToMemOrRegs(base=%s; toReplace=%s; t=%s; memOff=%d; regOff=%d)\n", baseArg.String(), toReplace.LongString(), t.String(), offset, regOffset) 1713 } 1714 key := selKey{baseArg, offset, t.Size(), t} 1715 w := x.commonArgs[key] 1716 if w != nil && w.Uses != 0 { // do not reuse dead value 1717 if toReplace != nil { 1718 toReplace.copyOf(w) 1719 if x.debug > 1 { 1720 x.Printf("...replace %s\n", toReplace.LongString()) 1721 } 1722 } 1723 if x.debug > 1 { 1724 x.Printf("-->%s\n", w.LongString()) 1725 } 1726 return w 1727 } 1728 1729 pa := x.prAssignForArg(baseArg) 1730 if len(pa.Registers) == 0 { // Arg is on stack 1731 frameOff := baseArg.Aux.(*ir.Name).FrameOffset() 1732 if pa.Offset() != int32(frameOff+x.f.ABISelf.LocalsOffset()) { 1733 panic(fmt.Errorf("Parameter assignment %d and OpArg.Aux frameOffset %d disagree, op=%s", 1734 pa.Offset(), frameOff, baseArg.LongString())) 1735 } 1736 aux := baseArg.Aux 1737 auxInt := baseArg.AuxInt + offset 1738 if toReplace != nil && toReplace.Block == baseArg.Block { 1739 toReplace.reset(OpArg) 1740 toReplace.Aux = aux 1741 toReplace.AuxInt = auxInt 1742 toReplace.Type = t 1743 w = toReplace 1744 } else { 1745 w = baseArg.Block.NewValue0IA(baseArg.Pos, OpArg, t, auxInt, aux) 1746 } 1747 x.commonArgs[key] = w 1748 if toReplace != nil { 1749 toReplace.copyOf(w) 1750 } 1751 if x.debug > 1 { 1752 x.Printf("-->%s\n", w.LongString()) 1753 } 1754 return w 1755 } 1756 // Arg is in registers 1757 r := pa.Registers[regOffset] 1758 op, auxInt := ArgOpAndRegisterFor(r, x.f.ABISelf) 1759 if op == OpArgIntReg && t.IsFloat() || op == OpArgFloatReg && t.IsInteger() { 1760 fmt.Printf("pa=%v\nx.f.OwnAux.abiInfo=%s\n", 1761 pa.ToString(x.f.ABISelf, true), 1762 x.f.OwnAux.abiInfo.String()) 1763 panic(fmt.Errorf("Op/Type mismatch, op=%s, type=%s", op.String(), t.String())) 1764 } 1765 if baseArg.AuxInt != 0 { 1766 base.Fatalf("BaseArg %s bound to registers has non-zero AuxInt", baseArg.LongString()) 1767 } 1768 aux := &AuxNameOffset{baseArg.Aux.(*ir.Name), offset} 1769 if toReplace != nil && toReplace.Block == baseArg.Block { 1770 toReplace.reset(op) 1771 toReplace.Aux = aux 1772 toReplace.AuxInt = auxInt 1773 toReplace.Type = t 1774 w = toReplace 1775 } else { 1776 w = baseArg.Block.NewValue0IA(baseArg.Pos, op, t, auxInt, aux) 1777 } 1778 x.commonArgs[key] = w 1779 if toReplace != nil { 1780 toReplace.copyOf(w) 1781 } 1782 if x.debug > 1 { 1783 x.Printf("-->%s\n", w.LongString()) 1784 } 1785 return w 1786 1787 } 1788 1789 // ArgOpAndRegisterFor converts an abi register index into an ssa Op and corresponding 1790 // arg register index. 1791 func ArgOpAndRegisterFor(r abi.RegIndex, abiConfig *abi.ABIConfig) (Op, int64) { 1792 i := abiConfig.FloatIndexFor(r) 1793 if i >= 0 { // float PR 1794 return OpArgFloatReg, i 1795 } 1796 return OpArgIntReg, int64(r) 1797 }