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.
8 "cmd/compile/internal/abi"
9 "cmd/compile/internal/base"
10 "cmd/compile/internal/ir"
11 "cmd/compile/internal/types"
18 from *Value // what is selected from
19 offsetOrIndex int64 // whatever is appropriate for the selector
24 type Abi1RO uint8 // An offset within a parameter's slice of register indices, for abi1.
26 func isBlockMultiValueExit(b *Block) bool {
27 return (b.Kind == BlockRet || b.Kind == BlockRetJmp) && b.Controls[0] != nil && b.Controls[0].Op == OpMakeResult
30 func badVal(s string, v *Value) error {
31 return fmt.Errorf("%s %s", s, v.LongString())
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 {
44 if t.IsStruct() && t.NumFields() == 1 {
48 if t.IsArray() && t.NumElem() == 1 {
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
64 regValues *[]*Value // values assigned to registers accumulate here
67 func (rc *registerCursor) String() string {
69 if rc.storeDest != nil {
70 dest = rc.storeDest.String()
73 if rc.regValues != nil {
75 for i, x := range *rc.regValues {
79 regs = regs + x.LongString()
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)
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 {
90 if int(c.nextSlice) < c.regsLen {
91 w := c.config.NumParamRegs(t)
92 c.nextSlice += Abi1RO(w)
97 // plus returns a register cursor offset from the original, without modifying the original.
98 func (c *registerCursor) plus(regWidth Abi1RO) registerCursor {
100 rc.nextSlice += regWidth
105 // Register offsets for fields of built-in aggregate types; the ones not listed are zero.
113 func (x *expandState) regWidth(t *types.Type) Abi1RO {
114 return Abi1RO(x.abi1.NumParamRegs(t))
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.
124 return Abi1RO(i) * x.regWidth(t.Elem())
128 for j := 0; j < i; j++ {
129 k += x.regWidth(t.FieldType(j))
133 panic("Haven't implemented this case yet, do I need to?")
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 {
140 if i == 0 || c.regsLen == 0 {
144 w := c.config.NumParamRegs(t.Elem())
145 rc.nextSlice += Abi1RO(i * w)
149 for j := 0; j < i; j++ {
150 rc.next(t.FieldType(j))
154 panic("Haven't implemented this case yet, do I need to?")
157 func (c *registerCursor) init(regs []abi.RegIndex, info *abi.ABIParamResultInfo, result *[]*Value, storeDest *Value) {
158 c.regsLen = len(regs)
161 c.storeDest = storeDest // only save this if there are no registers, will explode if misused.
164 c.config = info.Config()
168 func (c *registerCursor) addArg(v *Value) {
169 *c.regValues = append(*c.regValues, v)
172 func (c *registerCursor) hasRegs() bool {
176 type expandState struct {
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
189 namedSelects map[*Value][]namedVal
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
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) {
202 if et == types.TINT64 {
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) {
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())))
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 {
229 // This captures common, (apparently) safe cases. The unsafe cases involve ft == uintptr
230 if (ft.IsPtr() || ft.IsUnsafePtr()) && pt.IsPtr() {
234 // Simplify, canonicalize
235 for from.Op == OpOffPtr {
236 offset += from.AuxInt
240 return x.f.ConstOffPtrSP(pt, offset, x.sp)
242 return b.NewValue1I(from.Pos.WithNotStmt(), OpOffPtr, pt, offset, from)
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
249 locs = append(locs, x.f.SplitSlot(ls[i], sfx, offset, ty))
254 // prAssignForArg returns the ABIParamAssignment for v, assumed to be an OpArg.
255 func (x *expandState) prAssignForArg(v *Value) *abi.ABIParamAssignment {
257 panic(badVal("Wanted OpArg, instead saw", v))
259 return ParamAssignmentForArgName(x.f, v.Aux.(*ir.Name))
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 {
271 panic(fmt.Errorf("Did not match param %v in prInfo %+v", name, abiInfo.InParams()))
274 // indent increments (or decrements) the indentation.
275 func (x *expandState) indent(n int) {
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, "")
284 return fmt.Printf(format, a...)
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.
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.
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.
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.
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 {
308 x.Printf("rewriteSelect(%s; %s; memOff=%d; regOff=%d)\n", leaf.LongString(), selector.LongString(), offset, regOffset)
310 var locs []*LocalSlot
311 leafType := leaf.Type
312 if len(selector.Args) > 0 {
313 w := selector.Args[0]
318 selector.SetArg(0, w)
322 case OpArgIntReg, OpArgFloatReg:
323 if leafType == selector.Type { // OpIData leads us here, sometimes.
324 leaf.copyOf(selector)
326 x.f.Fatalf("Unexpected %s type, selector=%s, leaf=%s\n", selector.Op.String(), selector.LongString(), leaf.LongString())
329 x.Printf("---%s, break\n", selector.Op.String())
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)
336 x.f.Fatalf("Unexpected OpArg type, selector=%s, leaf=%s\n", selector.LongString(), leaf.LongString())
339 x.Printf("---OpArg, break\n")
344 case OpIData, OpStructSelect, OpArraySelect:
345 leafType = removeTrivialWrapperTypes(leaf.Type)
347 x.newArgToMemOrRegs(selector, leaf, offset, regOffset, leafType, leaf.Pos)
349 for _, s := range x.namedSelects[selector] {
350 locs = append(locs, x.f.Names[s.locIndex])
353 case OpLoad: // We end up here because of IData of immediate structures.
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).
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
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
365 // v71 (+143) = IData <Nodes> v22 (v[Nodes])
366 // v73 (+146) = StaticLECall <[]*Node,mem> {AuxCall{"".Nodes.Slice([Nodes,0])[[]*Node,8]}} [32] v71 v21
368 // translates (w/o the "case OpLoad:" above) to:
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
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() {
385 x.Printf("---OpLoad, break\n")
387 break // softfloat pass will take care of that
389 x.f.Fatalf("Unexpected Load as selector, leaf=%s, selector=%s\n", leaf.LongString(), selector.LongString())
391 leaf.copyOf(selector)
392 for _, s := range x.namedSelects[selector] {
393 locs = append(locs, x.f.Names[s.locIndex])
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]
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)
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()))
416 if aux.abiInfo == nil {
417 panic(badVal("aux.abiInfo nil for call", call))
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
424 selector.copyOf(existing)
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()))
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
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))
450 reg := int64(regOffset + Abi1RO(firstReg))
451 if leaf.Block == call.Block {
452 leaf.reset(OpSelectN)
456 x.transformedSelects[leaf.ID] = true // leaf, rewritten to use post-expansion indexing.
458 w := call.Block.NewValue1I(leaf.Pos, OpSelectN, leafType, reg, call0)
459 x.transformedSelects[w.ID] = true // select, using post-expansion indexing.
463 off := x.offsetFrom(x.f.Entry, x.sp, offset+aux.OffsetOfResult(which), pt)
464 if leaf.Block == call.Block {
466 leaf.SetArgs2(off, call)
469 w := call.Block.NewValue2(leaf.Pos, OpLoad, leafType, off, call)
472 x.Printf("---new %s\n", w.LongString())
476 for _, s := range x.namedSelects[selector] {
477 locs = append(locs, x.f.Names[s.locIndex])
480 x.f.Fatalf("Should not have non-SSA-able OpSelectN, selector=%s", selector.LongString())
485 w := selector.Args[0]
487 if w.Type.Kind() != types.TSTRUCT { // IData artifact
488 ls = x.rewriteSelect(leaf, w, offset, regOffset)
490 fldi := int(selector.AuxInt)
491 ls = x.rewriteSelect(leaf, w, offset+w.Type.FieldOff(fldi), regOffset+x.regOffset(w.Type, fldi))
493 for _, l := range ls {
494 locs = append(locs, x.f.SplitStruct(l, int(selector.AuxInt)))
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)))
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)
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)
515 ls := x.rewriteSelect(leaf, selector.Args[0], offset, regOffset)
516 locs = x.splitSlots(ls, ".ptr", 0, x.typs.BytePtr)
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()))
524 w := selector.Args[0]
525 ls := x.rewriteSelect(leaf, w, offset, regOffset)
527 if w.Type.IsEmptyInterface() {
530 locs = x.splitSlots(ls, sfx, 0, x.typs.Uintptr)
533 ls := x.rewriteSelect(leaf, selector.Args[0], offset, regOffset)
534 locs = x.splitSlots(ls, ".real", 0, selector.Type)
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)
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)
545 ls := x.rewriteSelect(leaf, selector.Args[0], offset+x.ptrSize, regOffset+RO_iface_data)
546 locs = x.splitSlots(ls, ".data", x.ptrSize, leafType)
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)
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])
560 // Ignore dead ends. These can occur if this phase is run before decompose builtin (which is not intended, but allowed).
566 func (x *expandState) rewriteDereference(b *Block, base, a, mem *Value, offset, size int64, typ *types.Type, pos src.XPos) *Value {
568 dst := x.offsetFrom(b, base, offset, source.Type)
569 if a.Uses == 1 && a.Block == b {
572 a.Type = types.TypeMem
575 a.SetArgs3(dst, source, mem)
578 mem = b.NewValue3A(pos, OpMove, types.TypeMem, typ, dst, source, mem)
584 var indexNames [1]string = [1]string{"[0]"}
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() {
596 switch container.Kind() {
598 container = container.Elem()
599 if container.Size() == 0 {
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]
608 for i := 0; i < container.NumFields(); i++ {
609 fld := container.Field(i)
610 if fld.Offset+fld.Type.Size() > offset {
612 path += "." + fld.Sym.Name
618 case types.TINT64, types.TUINT64:
619 if container.Size() == x.regSize {
622 if offset == x.hiOffset {
628 return path + ".data"
630 if container.IsEmptyInterface() {
631 return path + ".type"
633 return path + ".itab"
636 if offset == 2*x.regSize {
645 case types.TCOMPLEX64, types.TCOMPLEX128:
647 return path + ".real"
649 return path + ".imag"
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.
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 {
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])
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++ {
686 fmt.Printf("rt=%s, off=%d, rt.Width=%d, rt.Align=%d\n", rt.String(), off, rt.Size(), uint8(rt.Alignment()))
688 panic(fmt.Errorf("offset %d of requested register %d should be zero, source=%s", offs[loadRegOffset], loadRegOffset, source.LongString()))
692 x.Printf("decompose arg %s has %d locs\n", source.LongString(), len(locs))
695 for i := loadRegOffset; i < last; i++ {
698 w := x.commonArgs[selKey{source, off, rt.Size(), rt}]
700 w = x.newArgToMemOrRegs(source, w, off, i, rt, pos)
701 suffix := x.pathTo(source.Type, rt, off)
703 x.splitSlotsIntoNames(locs, suffix, off, rt, w)
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)
714 mem = x.storeArgOrLoad(pos, b, w, mem, rt, storeOffset+off, i, storeRc.next(rt))
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()
732 for i := 0; i < u.NumFields(); 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()
739 case types.TINT64, types.TUINT64:
740 if t.Size() == x.regSize {
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))
749 if u.IsEmptyInterface() {
752 return storeTwoArg(x, pos, b, locs, sfx, ".idata", source, mem, x.typs.Uintptr, x.typs.BytePtr, 0, storeOffset, loadRegOffset, storeRc)
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)
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)
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)
772 x.f.Names = append(x.f.Names, l)
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.
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.
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 {
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()
807 for i := 0; i < u.NumFields(); 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()
814 case types.TINT64, types.TUINT64:
815 if t.Size() == x.regSize {
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))
823 return storeTwoLoad(x, pos, b, source, mem, x.typs.Uintptr, x.typs.BytePtr, 0, storeOffset, loadRegOffset, storeRc)
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)
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)
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 {
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())
847 w := x.commonArgs[selKey{source, argOffset, t.Size(), t}]
849 w = x.newArgToMemOrRegs(source, w, argOffset, loadRegOffset, t, pos)
850 x.splitSlotsIntoNames(locs, suffix, argOffset, t, w)
852 return x.storeArgOrLoad(pos, b, w, mem, t, storeOffset, loadRegOffset, storeRc)
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)
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()
866 return storeOneArg(x, pos, b, locs, suffix2, source, mem, t2, offArg+t1Size, offStore+t1Size, loadRegOffset+1, storeRc)
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()
875 return storeOneLoad(x, pos, b, source, mem, t2, offArg+t1Size, offStore+t1Size, loadRegOffset+1, storeRc)
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 {
885 x.Printf("storeArgOrLoad(%s; %s; %s; %d; %s)\n", source.LongString(), mem.String(), t.String(), storeOffset, storeRc.String())
888 // Start with Opcodes that can be disassembled
891 return x.storeArgOrLoad(pos, b, source.Args[0], mem, t, storeOffset, loadRegOffset, storeRc)
893 case OpLoad, OpDereference:
894 ret := x.decomposeLoad(pos, b, source, mem, t, storeOffset, loadRegOffset, storeRc)
900 ret := x.decomposeArg(pos, b, source, mem, t, storeOffset, loadRegOffset, storeRc)
905 case OpArrayMake0, OpStructMake0:
906 // TODO(register args) is this correct for registers?
909 case OpStructMake1, OpStructMake2, OpStructMake3, OpStructMake4:
910 for i := 0; i < t.NumFields(); 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()
918 return x.storeArgOrLoad(pos, b, source.Args[0], mem, t.Elem(), storeOffset, 0, storeRc.at(t, 0))
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)
927 tPart := x.typs.Float32
928 wPart := t.Size() / 2
930 tPart = x.typs.Float64
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)
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)
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)
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)
953 // For nodes that cannot be taken apart -- OpSelectN, other structure selectors.
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)
962 eltRO := x.regWidth(elt)
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()
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
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)
998 for i := 0; i < t.NumFields(); 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()
1007 case types.TINT64, types.TUINT64:
1008 if t.Size() == x.regSize {
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))
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)
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)
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)
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)
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)
1058 if source.Op == OpDereference {
1059 source.Op = OpLoad // For purposes of parameter passing expansion, a Dereference is a Load.
1061 if storeRc.hasRegs() {
1062 storeRc.addArg(source)
1064 dst := x.offsetFrom(b, storeRc.storeDest, storeOffset, types.NewPtr(t))
1065 s = b.NewValue3A(pos, OpStore, types.TypeMem, t, dst, source, mem)
1068 x.Printf("-->storeArg returns %s, storeRc=%s\n", s.LongString(), storeRc.String())
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) {
1081 x.Printf("rewriteArgs(%s; %d)\n", v.LongString(), firstArg)
1083 // Thread the stores on the memory arg
1084 aux := v.Aux.(*AuxCall)
1087 newArgs := []*Value{}
1088 oldArgs := []*Value{}
1090 if v.Op == OpTailLECall {
1091 // For tail call, we unwind the frame before the call so we'll use the caller's
1093 sp = x.f.Entry.NewValue1(src.NoXPos, OpGetCallerSP, x.typs.Uintptr, mem)
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)
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())
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.
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 {
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)
1120 var rc registerCursor
1121 var result *[]*Value
1126 aOffset = aux.OffsetOfArg(auxI)
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 {
1137 x.Printf("...storeArg %s, %v, %d\n", a.LongString(), aType, aOffset)
1139 rc.init(aRegs, aux.abiInfo, result, sp)
1140 mem = x.storeArgOrLoad(a.Pos, v.Block, a, mem, aType, aOffset, 0, rc)
1143 var preArgStore [2]*Value
1144 preArgs := append(preArgStore[:0], v.Args[0:firstArg]...)
1146 v.AddArgs(preArgs...)
1147 v.AddArgs(newArgs...)
1149 for _, a := range oldArgs {
1151 x.invalidateRecursively(a)
1158 func (x *expandState) invalidateRecursively(a *Value) {
1162 if a.Pos.IsStmt() == src.PosIsStmt {
1165 s = a.String() + plus + a.Pos.LineNumber() + " " + a.LongString()
1167 x.Printf("...marking %v unused\n", s)
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)
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.
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.
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.
1194 debug: f.pass.debug,
1195 canSSAType: f.fe.CanSSA,
1196 regSize: f.Config.RegSize,
1198 typs: &f.Config.Types,
1199 ptrSize: f.Config.PtrSize,
1200 namedSelects: make(map[*Value][]namedVal),
1202 commonArgs: make(map[selKey]*Value),
1203 memForCall: make(map[ID]*Value),
1204 transformedSelects: make(map[ID]bool),
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
1212 x.lowOffset, x.hiOffset = 0, 4
1213 x.loRo, x.hiRo = 0, 1
1217 x.Printf("\nexpandsCalls(%s)\n", f.Name)
1220 for i, name := range f.Names {
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})
1232 // TODO if too slow, whole program iteration can be replaced w/ slices of appropriate values, accumulated in first loop here.
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 {
1239 case OpStaticLECall, OpTailLECall:
1242 case OpClosureLECall:
1247 x.rewriteArgs(v, firstArg)
1249 if isBlockMultiValueExit(b) {
1251 // Very similar to code in rewriteArgs, but results instead of args.
1256 allResults := []*Value{}
1258 x.Printf("multiValueExit rewriting %s\n", v.LongString())
1260 var oldArgs []*Value
1261 for j, a := range v.Args[:len(v.Args)-1] {
1262 oldArgs = append(oldArgs, a)
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
1278 mem = x.rewriteDereference(v.Block, auxBase, a, mem, auxOffset, auxSize, auxType, a.Pos)
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) {
1286 var rc registerCursor
1287 var result *[]*Value
1289 result = &allResults
1291 rc.init(aRegs, aux.abiInfo, result, auxBase)
1292 mem = x.storeArgOrLoad(v.Pos, b, a, mem, aux.TypeOfResult(i), auxOffset, 0, rc)
1296 v.AddArgs(allResults...)
1298 v.Type = types.NewResults(append(abi.RegisterTypes(aux.abiInfo.OutParams()), types.TypeMem))
1300 for _, a := range oldArgs {
1303 x.Printf("...marking %v unused\n", a.LongString())
1305 x.invalidateRecursively(a)
1309 x.Printf("...multiValueExit new result %s\n", v.LongString())
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)
1323 iAEATt := x.isAlreadyExpandedAggregateType(t)
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)
1333 dst, mem := v.Args[0], v.Args[2]
1334 mem = x.storeArgOrLoad(v.Pos, b, source, mem, t, 0, 0, registerCursor{storeDest: dst})
1340 val2Preds := make(map[*Value]int32) // Used to accumulate dependency graph of selection operations for topological ordering.
1342 // Step 2: transform or accumulate selection operations for rewrite in topological order.
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),
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
1352 case OpStructSelect, OpArraySelect,
1354 OpStringPtr, OpStringLen,
1355 OpSlicePtr, OpSliceLen, OpSliceCap, OpSlicePtrUnchecked,
1356 OpComplexReal, OpComplexImag,
1357 OpInt64Hi, OpInt64Lo:
1360 case OpStructSelect, OpArraySelect, OpSelectN, OpArg:
1363 x.Printf("v2p[%s] = %d\n", w.LongString(), val2Preds[w])
1369 if _, ok := val2Preds[v]; !ok {
1372 x.Printf("v2p[%s] = %d\n", v.LongString(), val2Preds[v])
1377 if !x.isAlreadyExpandedAggregateType(v.Type) {
1380 if _, ok := val2Preds[v]; !ok {
1383 x.Printf("v2p[%s] = %d\n", v.LongString(), val2Preds[v])
1388 // Do these directly, there are no chains of selectors.
1391 aux := call.Aux.(*AuxCall)
1393 off := x.offsetFrom(x.f.Entry, x.sp, aux.OffsetOfResult(which), pt)
1399 // Step 3: Compute topological order of selectors,
1400 // then process it in reverse to eliminate duplicates,
1401 // then forwards to rewrite selectors.
1403 // All chains of selectors end up in same block as the call.
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
1413 return vi.ID < vj.ID
1415 return x.sdom.domorder(bi) > x.sdom.domorder(bj) // reverse the order to put dominators last.
1418 // Accumulate order in allOrdered
1419 var allOrdered []*Value
1420 for v, n := range val2Preds {
1422 allOrdered = append(allOrdered, v)
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)
1433 continue // no Args[0], hence done.
1436 n, ok := val2Preds[w]
1441 allOrdered = append(allOrdered, w)
1442 delete(val2Preds, w)
1445 val2Preds[w] = n - 1
1449 x.commonSelectors = make(map[selKey]*Value)
1450 // Rewrite duplicate selectors as copies where possible.
1451 for i := len(allOrdered) - 1; i >= 0; i-- {
1458 for w.Op == OpCopy {
1465 continue // handled elsewhere, not an indexable result
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())
1477 offset = size * v.AuxInt
1479 offset = v.AuxInt // offset is just a key, really.
1483 offset = x.lowOffset
1484 case OpStringLen, OpSliceLen, OpIData:
1487 offset = 2 * x.ptrSize
1491 sk := selKey{from: w, size: size, offsetOrIndex: offset, typ: typ}
1492 dupe := x.commonSelectors[sk]
1494 x.commonSelectors[sk] = v
1495 } else if x.sdom.IsAncestorEq(dupe.Block, v.Block) {
1497 x.Printf("Duplicate, make %s copy of %s\n", v, dupe)
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
1507 // Indices of entries in f.Names that need to be deleted.
1508 var toDelete []namedVal
1510 // Rewrite selectors.
1511 for i, v := range allOrdered {
1514 x.Printf("allOrdered[%d] = b%d, %s, uses=%d\n", i, b.ID, v.LongString(), v.Uses)
1517 x.invalidateRecursively(v)
1523 locs := x.rewriteSelect(v, v, 0, 0)
1524 // Install new names.
1525 if v.Type.IsMemory() {
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)
1534 f.NamedValues[*l] = append(f.NamedValues[*l], v)
1538 if ns, ok := x.namedSelects[v]; ok {
1539 // Not-leaf types that had debug locations need to lose them.
1541 toDelete = append(toDelete, ns...)
1545 deleteNamedVals(f, toDelete)
1547 // Step 4: rewrite the calls themselves, correcting the type.
1548 for _, b := range f.Blocks {
1549 for _, v := range b.Values {
1552 x.rewriteArgToMemOrRegs(v)
1553 case OpStaticLECall:
1555 rts := abi.RegisterTypes(v.Aux.(*AuxCall).abiInfo.OutParams())
1556 v.Type = types.NewResults(append(rts, types.TypeMem))
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))
1567 rts := abi.RegisterTypes(v.Aux.(*AuxCall).abiInfo.OutParams())
1568 v.Type = types.NewResults(append(rts, types.TypeMem))
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 {
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())
1586 if w.Type.IsUnsafePtr() && !v.Type.IsUnsafePtr() {
1587 // Update unsafe.Pointer type if we know the actual pointer type.
1590 // TODO: don't dedup pointer and scalar? Rewrite to OpConvert? Can it happen?
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)
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 {
1615 for a.Op == OpCopy {
1622 for _, b := range f.Blocks {
1623 for _, v := range b.Values {
1624 for i, a := range v.Args {
1632 x.invalidateRecursively(a)
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 {
1646 if a.Type.IsMemory() {
1649 if a.Pos.Line() != v.Pos.Line() {
1652 if !a.Pos.SameFile(v.Pos) {
1656 case OpArgIntReg, OpArgFloatReg, OpSelectN:
1657 v.Pos = v.Pos.WithIsStmt()
1658 a.Pos = a.Pos.WithDefaultStmt()
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 {
1671 x.Printf("rewriteArgToMemOrRegs(%s)\n", v.LongString())
1673 pa := x.prAssignForArg(v)
1674 switch len(pa.Registers) {
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()))
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
1689 r := pa.Registers[0]
1691 v.Op, i = ArgOpAndRegisterFor(r, x.f.ABISelf)
1692 v.Aux = &AuxNameOffset{v.Aux.(*ir.Name), 0}
1694 x.commonArgs[key] = v
1697 panic(badVal("Saw unexpanded OpArg", v))
1700 x.Printf("-->%s\n", v.LongString())
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 {
1712 x.Printf("newArgToMemOrRegs(base=%s; toReplace=%s; t=%s; memOff=%d; regOff=%d)\n", baseArg.String(), toReplace.LongString(), t.String(), offset, regOffset)
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 {
1720 x.Printf("...replace %s\n", toReplace.LongString())
1724 x.Printf("-->%s\n", w.LongString())
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()))
1737 auxInt := baseArg.AuxInt + offset
1738 if toReplace != nil && toReplace.Block == baseArg.Block {
1739 toReplace.reset(OpArg)
1741 toReplace.AuxInt = auxInt
1745 w = baseArg.Block.NewValue0IA(baseArg.Pos, OpArg, t, auxInt, aux)
1747 x.commonArgs[key] = w
1748 if toReplace != nil {
1752 x.Printf("-->%s\n", w.LongString())
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()))
1765 if baseArg.AuxInt != 0 {
1766 base.Fatalf("BaseArg %s bound to registers has non-zero AuxInt", baseArg.LongString())
1768 aux := &AuxNameOffset{baseArg.Aux.(*ir.Name), offset}
1769 if toReplace != nil && toReplace.Block == baseArg.Block {
1772 toReplace.AuxInt = auxInt
1776 w = baseArg.Block.NewValue0IA(baseArg.Pos, op, t, auxInt, aux)
1778 x.commonArgs[key] = w
1779 if toReplace != nil {
1783 x.Printf("-->%s\n", w.LongString())
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
1796 return OpArgIntReg, int64(r)