]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/ssa/expand_calls.go
[dev.regabi] all: merge master (dab3e5a) into dev.regabi
[gostls13.git] / src / cmd / compile / internal / ssa / expand_calls.go
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         "cmd/compile/internal/types"
9         "cmd/internal/src"
10         "fmt"
11         "sort"
12 )
13
14 type selKey struct {
15         from   *Value
16         offset int64
17         size   int64
18         typ    *types.Type
19 }
20
21 type offsetKey struct {
22         from   *Value
23         offset int64
24         pt     *types.Type
25 }
26
27 func isBlockMultiValueExit(b *Block) bool {
28         return (b.Kind == BlockRet || b.Kind == BlockRetJmp) && len(b.Controls) > 0 && b.Controls[0].Op == OpMakeResult
29 }
30
31 // expandCalls converts LE (Late Expansion) calls that act like they receive value args into a lower-level form
32 // that is more oriented to a platform's ABI.  The SelectN operations that extract results are rewritten into
33 // more appropriate forms, and any StructMake or ArrayMake inputs are decomposed until non-struct values are
34 // reached.  On the callee side, OpArg nodes are not decomposed until this phase is run.
35 // TODO results should not be lowered until this phase.
36 func expandCalls(f *Func) {
37         // Calls that need lowering have some number of inputs, including a memory input,
38         // and produce a tuple of (value1, value2, ..., mem) where valueK may or may not be SSA-able.
39
40         // With the current ABI those inputs need to be converted into stores to memory,
41         // rethreading the call's memory input to the first, and the new call now receiving the last.
42
43         // With the current ABI, the outputs need to be converted to loads, which will all use the call's
44         // memory output as their input.
45         if !LateCallExpansionEnabledWithin(f) {
46                 return
47         }
48         debug := f.pass.debug > 0
49
50         if debug {
51                 fmt.Printf("\nexpandsCalls(%s)\n", f.Name)
52         }
53
54         canSSAType := f.fe.CanSSA
55         regSize := f.Config.RegSize
56         sp, _ := f.spSb()
57         typ := &f.Config.Types
58         ptrSize := f.Config.PtrSize
59
60         // For 32-bit, need to deal with decomposition of 64-bit integers, which depends on endianness.
61         var hiOffset, lowOffset int64
62         if f.Config.BigEndian {
63                 lowOffset = 4
64         } else {
65                 hiOffset = 4
66         }
67
68         namedSelects := make(map[*Value][]namedVal)
69
70         sdom := f.Sdom()
71
72         common := make(map[selKey]*Value)
73
74         // intPairTypes returns the pair of 32-bit int types needed to encode a 64-bit integer type on a target
75         // that has no 64-bit integer registers.
76         intPairTypes := func(et types.Kind) (tHi, tLo *types.Type) {
77                 tHi = typ.UInt32
78                 if et == types.TINT64 {
79                         tHi = typ.Int32
80                 }
81                 tLo = typ.UInt32
82                 return
83         }
84
85         // isAlreadyExpandedAggregateType returns whether a type is an SSA-able "aggregate" (multiple register) type
86         // that was expanded in an earlier phase (currently, expand_calls is intended to run after decomposeBuiltin,
87         // so this is all aggregate types -- small struct and array, complex, interface, string, slice, and 64-bit
88         // integer on 32-bit).
89         isAlreadyExpandedAggregateType := func(t *types.Type) bool {
90                 if !canSSAType(t) {
91                         return false
92                 }
93                 return t.IsStruct() || t.IsArray() || t.IsComplex() || t.IsInterface() || t.IsString() || t.IsSlice() ||
94                         t.Size() > regSize && t.IsInteger()
95         }
96
97         offsets := make(map[offsetKey]*Value)
98
99         // offsetFrom creates an offset from a pointer, simplifying chained offsets and offsets from SP
100         // TODO should also optimize offsets from SB?
101         offsetFrom := func(from *Value, offset int64, pt *types.Type) *Value {
102                 if offset == 0 && from.Type == pt { // this is not actually likely
103                         return from
104                 }
105                 // Simplify, canonicalize
106                 for from.Op == OpOffPtr {
107                         offset += from.AuxInt
108                         from = from.Args[0]
109                 }
110                 if from == sp {
111                         return f.ConstOffPtrSP(pt, offset, sp)
112                 }
113                 key := offsetKey{from, offset, pt}
114                 v := offsets[key]
115                 if v != nil {
116                         return v
117                 }
118                 v = from.Block.NewValue1I(from.Pos.WithNotStmt(), OpOffPtr, pt, offset, from)
119                 offsets[key] = v
120                 return v
121         }
122
123         // splitSlots splits one "field" (specified by sfx, offset, and ty) out of the LocalSlots in ls and returns the new LocalSlots this generates.
124         splitSlots := func(ls []LocalSlot, sfx string, offset int64, ty *types.Type) []LocalSlot {
125                 var locs []LocalSlot
126                 for i := range ls {
127                         locs = append(locs, f.fe.SplitSlot(&ls[i], sfx, offset, ty))
128                 }
129                 return locs
130         }
131
132         // removeTrivialWrapperTypes unwraps layers of
133         // struct { singleField SomeType } and [1]SomeType
134         // until a non-wrapper type is reached.  This is useful
135         // for working with assignments to/from interface data
136         // fields (either second operand to OpIMake or OpIData)
137         // where the wrapping or type conversion can be elided
138         // because of type conversions/assertions in source code
139         // that do not appear in SSA.
140         removeTrivialWrapperTypes := func(t *types.Type) *types.Type {
141                 for {
142                         if t.IsStruct() && t.NumFields() == 1 {
143                                 t = t.Field(0).Type
144                                 continue
145                         }
146                         if t.IsArray() && t.NumElem() == 1 {
147                                 t = t.Elem()
148                                 continue
149                         }
150                         break
151                 }
152                 return t
153         }
154
155         // Calls that need lowering have some number of inputs, including a memory input,
156         // and produce a tuple of (value1, value2, ..., mem) where valueK may or may not be SSA-able.
157
158         // With the current ABI those inputs need to be converted into stores to memory,
159         // rethreading the call's memory input to the first, and the new call now receiving the last.
160
161         // With the current ABI, the outputs need to be converted to loads, which will all use the call's
162         // memory output as their input.
163
164         // rewriteSelect recursively walks from leaf selector to a root (OpSelectN, OpLoad, OpArg)
165         // through a chain of Struct/Array/builtin Select operations.  If the chain of selectors does not
166         // end in an expected root, it does nothing (this can happen depending on compiler phase ordering).
167         // The "leaf" provides the type, the root supplies the container, and the leaf-to-root path
168         // accumulates the offset.
169         // It emits the code necessary to implement the leaf select operation that leads to the root.
170         //
171         // TODO when registers really arrive, must also decompose anything split across two registers or registers and memory.
172         var rewriteSelect func(leaf *Value, selector *Value, offset int64) []LocalSlot
173         rewriteSelect = func(leaf *Value, selector *Value, offset int64) []LocalSlot {
174                 if debug {
175                         fmt.Printf("rewriteSelect(%s, %s, %d)\n", leaf.LongString(), selector.LongString(), offset)
176                 }
177                 var locs []LocalSlot
178                 leafType := leaf.Type
179                 if len(selector.Args) > 0 {
180                         w := selector.Args[0]
181                         if w.Op == OpCopy {
182                                 for w.Op == OpCopy {
183                                         w = w.Args[0]
184                                 }
185                                 selector.SetArg(0, w)
186                         }
187                 }
188                 switch selector.Op {
189                 case OpArg:
190                         if !isAlreadyExpandedAggregateType(selector.Type) {
191                                 if leafType == selector.Type { // OpIData leads us here, sometimes.
192                                         leaf.copyOf(selector)
193                                 } else {
194                                         f.Fatalf("Unexpected OpArg type, selector=%s, leaf=%s\n", selector.LongString(), leaf.LongString())
195                                 }
196                                 if debug {
197                                         fmt.Printf("\tOpArg, break\n")
198                                 }
199                                 break
200                         }
201                         switch leaf.Op {
202                         case OpIData, OpStructSelect, OpArraySelect:
203                                 leafType = removeTrivialWrapperTypes(leaf.Type)
204                         }
205                         aux := selector.Aux
206                         auxInt := selector.AuxInt + offset
207                         if leaf.Block == selector.Block {
208                                 leaf.reset(OpArg)
209                                 leaf.Aux = aux
210                                 leaf.AuxInt = auxInt
211                                 leaf.Type = leafType
212                         } else {
213                                 w := selector.Block.NewValue0IA(leaf.Pos, OpArg, leafType, auxInt, aux)
214                                 leaf.copyOf(w)
215                                 if debug {
216                                         fmt.Printf("\tnew %s\n", w.LongString())
217                                 }
218                         }
219                         for _, s := range namedSelects[selector] {
220                                 locs = append(locs, f.Names[s.locIndex])
221                         }
222
223                 case OpLoad: // We end up here because of IData of immediate structures.
224                         // Failure case:
225                         // (note the failure case is very rare; w/o this case, make.bash and run.bash both pass, as well as
226                         // the hard cases of building {syscall,math,math/cmplx,math/bits,go/constant} on ppc64le and mips-softfloat).
227                         //
228                         // GOSSAFUNC='(*dumper).dump' go build -gcflags=-l -tags=math_big_pure_go cmd/compile/internal/gc
229                         // cmd/compile/internal/gc/dump.go:136:14: internal compiler error: '(*dumper).dump': not lowered: v827, StructSelect PTR PTR
230                         // b2: ← b1
231                         // v20 (+142) = StaticLECall <interface {},mem> {AuxCall{reflect.Value.Interface([reflect.Value,0])[interface {},24]}} [40] v8 v1
232                         // v21 (142) = SelectN <mem> [1] v20
233                         // v22 (142) = SelectN <interface {}> [0] v20
234                         // b15: ← b8
235                         // v71 (+143) = IData <Nodes> v22 (v[Nodes])
236                         // v73 (+146) = StaticLECall <[]*Node,mem> {AuxCall{"".Nodes.Slice([Nodes,0])[[]*Node,8]}} [32] v71 v21
237                         //
238                         // translates (w/o the "case OpLoad:" above) to:
239                         //
240                         // b2: ← b1
241                         // v20 (+142) = StaticCall <mem> {AuxCall{reflect.Value.Interface([reflect.Value,0])[interface {},24]}} [40] v715
242                         // v23 (142) = Load <*uintptr> v19 v20
243                         // v823 (142) = IsNonNil <bool> v23
244                         // v67 (+143) = Load <*[]*Node> v880 v20
245                         // b15: ← b8
246                         // v827 (146) = StructSelect <*[]*Node> [0] v67
247                         // v846 (146) = Store <mem> {*[]*Node} v769 v827 v20
248                         // v73 (+146) = StaticCall <mem> {AuxCall{"".Nodes.Slice([Nodes,0])[[]*Node,8]}} [32] v846
249                         // i.e., the struct select is generated and remains in because it is not applied to an actual structure.
250                         // The OpLoad was created to load the single field of the IData
251                         // This case removes that StructSelect.
252                         if leafType != selector.Type {
253                                 f.Fatalf("Unexpected Load as selector, leaf=%s, selector=%s\n", leaf.LongString(), selector.LongString())
254                         }
255                         leaf.copyOf(selector)
256                         for _, s := range namedSelects[selector] {
257                                 locs = append(locs, f.Names[s.locIndex])
258                         }
259
260                 case OpSelectN:
261                         // TODO these may be duplicated. Should memoize. Intermediate selectors will go dead, no worries there.
262                         call := selector.Args[0]
263                         aux := call.Aux.(*AuxCall)
264                         which := selector.AuxInt
265                         if which == aux.NResults() { // mem is after the results.
266                                 // rewrite v as a Copy of call -- the replacement call will produce a mem.
267                                 leaf.copyOf(call)
268                         } else {
269                                 leafType := removeTrivialWrapperTypes(leaf.Type)
270                                 if canSSAType(leafType) {
271                                         pt := types.NewPtr(leafType)
272                                         off := offsetFrom(sp, offset+aux.OffsetOfResult(which), pt)
273                                         // Any selection right out of the arg area/registers has to be same Block as call, use call as mem input.
274                                         if leaf.Block == call.Block {
275                                                 leaf.reset(OpLoad)
276                                                 leaf.SetArgs2(off, call)
277                                                 leaf.Type = leafType
278                                         } else {
279                                                 w := call.Block.NewValue2(leaf.Pos, OpLoad, leafType, off, call)
280                                                 leaf.copyOf(w)
281                                                 if debug {
282                                                         fmt.Printf("\tnew %s\n", w.LongString())
283                                                 }
284                                         }
285                                         for _, s := range namedSelects[selector] {
286                                                 locs = append(locs, f.Names[s.locIndex])
287                                         }
288                                 } else {
289                                         f.Fatalf("Should not have non-SSA-able OpSelectN, selector=%s", selector.LongString())
290                                 }
291                         }
292
293                 case OpStructSelect:
294                         w := selector.Args[0]
295                         var ls []LocalSlot
296                         if w.Type.Kind() != types.TSTRUCT { // IData artifact
297                                 ls = rewriteSelect(leaf, w, offset)
298                         } else {
299                                 ls = rewriteSelect(leaf, w, offset+w.Type.FieldOff(int(selector.AuxInt)))
300                                 if w.Op != OpIData {
301                                         for _, l := range ls {
302                                                 locs = append(locs, f.fe.SplitStruct(l, int(selector.AuxInt)))
303                                         }
304                                 }
305                         }
306
307                 case OpArraySelect:
308                         w := selector.Args[0]
309                         rewriteSelect(leaf, w, offset+selector.Type.Size()*selector.AuxInt)
310
311                 case OpInt64Hi:
312                         w := selector.Args[0]
313                         ls := rewriteSelect(leaf, w, offset+hiOffset)
314                         locs = splitSlots(ls, ".hi", hiOffset, leafType)
315
316                 case OpInt64Lo:
317                         w := selector.Args[0]
318                         ls := rewriteSelect(leaf, w, offset+lowOffset)
319                         locs = splitSlots(ls, ".lo", lowOffset, leafType)
320
321                 case OpStringPtr:
322                         ls := rewriteSelect(leaf, selector.Args[0], offset)
323                         locs = splitSlots(ls, ".ptr", 0, typ.BytePtr)
324
325                 case OpSlicePtr:
326                         w := selector.Args[0]
327                         ls := rewriteSelect(leaf, w, offset)
328                         locs = splitSlots(ls, ".ptr", 0, types.NewPtr(w.Type.Elem()))
329
330                 case OpITab:
331                         w := selector.Args[0]
332                         ls := rewriteSelect(leaf, w, offset)
333                         sfx := ".itab"
334                         if w.Type.IsEmptyInterface() {
335                                 sfx = ".type"
336                         }
337                         locs = splitSlots(ls, sfx, 0, typ.Uintptr)
338
339                 case OpComplexReal:
340                         ls := rewriteSelect(leaf, selector.Args[0], offset)
341                         locs = splitSlots(ls, ".real", 0, leafType)
342
343                 case OpComplexImag:
344                         ls := rewriteSelect(leaf, selector.Args[0], offset+leafType.Width) // result is FloatNN, width of result is offset of imaginary part.
345                         locs = splitSlots(ls, ".imag", leafType.Width, leafType)
346
347                 case OpStringLen, OpSliceLen:
348                         ls := rewriteSelect(leaf, selector.Args[0], offset+ptrSize)
349                         locs = splitSlots(ls, ".len", ptrSize, leafType)
350
351                 case OpIData:
352                         ls := rewriteSelect(leaf, selector.Args[0], offset+ptrSize)
353                         locs = splitSlots(ls, ".data", ptrSize, leafType)
354
355                 case OpSliceCap:
356                         ls := rewriteSelect(leaf, selector.Args[0], offset+2*ptrSize)
357                         locs = splitSlots(ls, ".cap", 2*ptrSize, leafType)
358
359                 case OpCopy: // If it's an intermediate result, recurse
360                         locs = rewriteSelect(leaf, selector.Args[0], offset)
361                         for _, s := range namedSelects[selector] {
362                                 // this copy may have had its own name, preserve that, too.
363                                 locs = append(locs, f.Names[s.locIndex])
364                         }
365
366                 default:
367                         // Ignore dead ends. These can occur if this phase is run before decompose builtin (which is not intended, but allowed).
368                 }
369
370                 return locs
371         }
372
373         // storeArgOrLoad converts stores of SSA-able aggregate arguments (passed to a call) into a series of primitive-typed
374         // stores of non-aggregate types.  It recursively walks up a chain of selectors until it reaches a Load or an Arg.
375         // If it does not reach a Load or an Arg, nothing happens; this allows a little freedom in phase ordering.
376         var storeArgOrLoad func(pos src.XPos, b *Block, base, source, mem *Value, t *types.Type, offset int64) *Value
377
378         // decomposeArgOrLoad is a helper for storeArgOrLoad.
379         // It decomposes a Load or an Arg into smaller parts, parameterized by the decomposeOne and decomposeTwo functions
380         // passed to it, and returns the new mem. If the type does not match one of the expected aggregate types, it returns nil instead.
381         decomposeArgOrLoad := func(pos src.XPos, b *Block, base, source, mem *Value, t *types.Type, offset int64,
382                 decomposeOne func(pos src.XPos, b *Block, base, source, mem *Value, t1 *types.Type, offArg, offStore int64) *Value,
383                 decomposeTwo func(pos src.XPos, b *Block, base, source, mem *Value, t1, t2 *types.Type, offArg, offStore int64) *Value) *Value {
384                 u := source.Type
385                 switch u.Kind() {
386                 case types.TARRAY:
387                         elem := u.Elem()
388                         for i := int64(0); i < u.NumElem(); i++ {
389                                 elemOff := i * elem.Size()
390                                 mem = decomposeOne(pos, b, base, source, mem, elem, source.AuxInt+elemOff, offset+elemOff)
391                                 pos = pos.WithNotStmt()
392                         }
393                         return mem
394                 case types.TSTRUCT:
395                         for i := 0; i < u.NumFields(); i++ {
396                                 fld := u.Field(i)
397                                 mem = decomposeOne(pos, b, base, source, mem, fld.Type, source.AuxInt+fld.Offset, offset+fld.Offset)
398                                 pos = pos.WithNotStmt()
399                         }
400                         return mem
401                 case types.TINT64, types.TUINT64:
402                         if t.Width == regSize {
403                                 break
404                         }
405                         tHi, tLo := intPairTypes(t.Kind())
406                         mem = decomposeOne(pos, b, base, source, mem, tHi, source.AuxInt+hiOffset, offset+hiOffset)
407                         pos = pos.WithNotStmt()
408                         return decomposeOne(pos, b, base, source, mem, tLo, source.AuxInt+lowOffset, offset+lowOffset)
409                 case types.TINTER:
410                         return decomposeTwo(pos, b, base, source, mem, typ.Uintptr, typ.BytePtr, source.AuxInt, offset)
411                 case types.TSTRING:
412                         return decomposeTwo(pos, b, base, source, mem, typ.BytePtr, typ.Int, source.AuxInt, offset)
413                 case types.TCOMPLEX64:
414                         return decomposeTwo(pos, b, base, source, mem, typ.Float32, typ.Float32, source.AuxInt, offset)
415                 case types.TCOMPLEX128:
416                         return decomposeTwo(pos, b, base, source, mem, typ.Float64, typ.Float64, source.AuxInt, offset)
417                 case types.TSLICE:
418                         mem = decomposeTwo(pos, b, base, source, mem, typ.BytePtr, typ.Int, source.AuxInt, offset)
419                         return decomposeOne(pos, b, base, source, mem, typ.Int, source.AuxInt+2*ptrSize, offset+2*ptrSize)
420                 }
421                 return nil
422         }
423
424         // storeOneArg creates a decomposed (one step) arg that is then stored.
425         // pos and b locate the store instruction, base is the base of the store target, source is the "base" of the value input,
426         // mem is the input mem, t is the type in question, and offArg and offStore are the offsets from the respective bases.
427         storeOneArg := func(pos src.XPos, b *Block, base, source, mem *Value, t *types.Type, offArg, offStore int64) *Value {
428                 w := common[selKey{source, offArg, t.Width, t}]
429                 if w == nil {
430                         w = source.Block.NewValue0IA(source.Pos, OpArg, t, offArg, source.Aux)
431                         common[selKey{source, offArg, t.Width, t}] = w
432                 }
433                 return storeArgOrLoad(pos, b, base, w, mem, t, offStore)
434         }
435
436         // storeOneLoad creates a decomposed (one step) load that is then stored.
437         storeOneLoad := func(pos src.XPos, b *Block, base, source, mem *Value, t *types.Type, offArg, offStore int64) *Value {
438                 from := offsetFrom(source.Args[0], offArg, types.NewPtr(t))
439                 w := source.Block.NewValue2(source.Pos, OpLoad, t, from, mem)
440                 return storeArgOrLoad(pos, b, base, w, mem, t, offStore)
441         }
442
443         storeTwoArg := func(pos src.XPos, b *Block, base, source, mem *Value, t1, t2 *types.Type, offArg, offStore int64) *Value {
444                 mem = storeOneArg(pos, b, base, source, mem, t1, offArg, offStore)
445                 pos = pos.WithNotStmt()
446                 t1Size := t1.Size()
447                 return storeOneArg(pos, b, base, source, mem, t2, offArg+t1Size, offStore+t1Size)
448         }
449
450         storeTwoLoad := func(pos src.XPos, b *Block, base, source, mem *Value, t1, t2 *types.Type, offArg, offStore int64) *Value {
451                 mem = storeOneLoad(pos, b, base, source, mem, t1, offArg, offStore)
452                 pos = pos.WithNotStmt()
453                 t1Size := t1.Size()
454                 return storeOneLoad(pos, b, base, source, mem, t2, offArg+t1Size, offStore+t1Size)
455         }
456
457         storeArgOrLoad = func(pos src.XPos, b *Block, base, source, mem *Value, t *types.Type, offset int64) *Value {
458                 if debug {
459                         fmt.Printf("\tstoreArgOrLoad(%s;  %s;  %s;  %s; %d)\n", base.LongString(), source.LongString(), mem.String(), t.String(), offset)
460                 }
461
462                 switch source.Op {
463                 case OpCopy:
464                         return storeArgOrLoad(pos, b, base, source.Args[0], mem, t, offset)
465
466                 case OpLoad:
467                         ret := decomposeArgOrLoad(pos, b, base, source, mem, t, offset, storeOneLoad, storeTwoLoad)
468                         if ret != nil {
469                                 return ret
470                         }
471
472                 case OpArg:
473                         ret := decomposeArgOrLoad(pos, b, base, source, mem, t, offset, storeOneArg, storeTwoArg)
474                         if ret != nil {
475                                 return ret
476                         }
477
478                 case OpArrayMake0, OpStructMake0:
479                         return mem
480
481                 case OpStructMake1, OpStructMake2, OpStructMake3, OpStructMake4:
482                         for i := 0; i < t.NumFields(); i++ {
483                                 fld := t.Field(i)
484                                 mem = storeArgOrLoad(pos, b, base, source.Args[i], mem, fld.Type, offset+fld.Offset)
485                                 pos = pos.WithNotStmt()
486                         }
487                         return mem
488
489                 case OpArrayMake1:
490                         return storeArgOrLoad(pos, b, base, source.Args[0], mem, t.Elem(), offset)
491
492                 case OpInt64Make:
493                         tHi, tLo := intPairTypes(t.Kind())
494                         mem = storeArgOrLoad(pos, b, base, source.Args[0], mem, tHi, offset+hiOffset)
495                         pos = pos.WithNotStmt()
496                         return storeArgOrLoad(pos, b, base, source.Args[1], mem, tLo, offset+lowOffset)
497
498                 case OpComplexMake:
499                         tPart := typ.Float32
500                         wPart := t.Width / 2
501                         if wPart == 8 {
502                                 tPart = typ.Float64
503                         }
504                         mem = storeArgOrLoad(pos, b, base, source.Args[0], mem, tPart, offset)
505                         pos = pos.WithNotStmt()
506                         return storeArgOrLoad(pos, b, base, source.Args[1], mem, tPart, offset+wPart)
507
508                 case OpIMake:
509                         mem = storeArgOrLoad(pos, b, base, source.Args[0], mem, typ.Uintptr, offset)
510                         pos = pos.WithNotStmt()
511                         return storeArgOrLoad(pos, b, base, source.Args[1], mem, typ.BytePtr, offset+ptrSize)
512
513                 case OpStringMake:
514                         mem = storeArgOrLoad(pos, b, base, source.Args[0], mem, typ.BytePtr, offset)
515                         pos = pos.WithNotStmt()
516                         return storeArgOrLoad(pos, b, base, source.Args[1], mem, typ.Int, offset+ptrSize)
517
518                 case OpSliceMake:
519                         mem = storeArgOrLoad(pos, b, base, source.Args[0], mem, typ.BytePtr, offset)
520                         pos = pos.WithNotStmt()
521                         mem = storeArgOrLoad(pos, b, base, source.Args[1], mem, typ.Int, offset+ptrSize)
522                         return storeArgOrLoad(pos, b, base, source.Args[2], mem, typ.Int, offset+2*ptrSize)
523                 }
524
525                 // For nodes that cannot be taken apart -- OpSelectN, other structure selectors.
526                 switch t.Kind() {
527                 case types.TARRAY:
528                         elt := t.Elem()
529                         if source.Type != t && t.NumElem() == 1 && elt.Width == t.Width && t.Width == regSize {
530                                 t = removeTrivialWrapperTypes(t)
531                                 // it could be a leaf type, but the "leaf" could be complex64 (for example)
532                                 return storeArgOrLoad(pos, b, base, source, mem, t, offset)
533                         }
534                         for i := int64(0); i < t.NumElem(); i++ {
535                                 sel := source.Block.NewValue1I(pos, OpArraySelect, elt, i, source)
536                                 mem = storeArgOrLoad(pos, b, base, sel, mem, elt, offset+i*elt.Width)
537                                 pos = pos.WithNotStmt()
538                         }
539                         return mem
540
541                 case types.TSTRUCT:
542                         if source.Type != t && t.NumFields() == 1 && t.Field(0).Type.Width == t.Width && t.Width == regSize {
543                                 // This peculiar test deals with accesses to immediate interface data.
544                                 // It works okay because everything is the same size.
545                                 // Example code that triggers this can be found in go/constant/value.go, function ToComplex
546                                 // v119 (+881) = IData <intVal> v6
547                                 // v121 (+882) = StaticLECall <floatVal,mem> {AuxCall{"".itof([intVal,0])[floatVal,8]}} [16] v119 v1
548                                 // This corresponds to the generic rewrite rule "(StructSelect [0] (IData x)) => (IData x)"
549                                 // Guard against "struct{struct{*foo}}"
550                                 // Other rewriting phases create minor glitches when they transform IData, for instance the
551                                 // interface-typed Arg "x" of ToFloat in go/constant/value.go
552                                 //   v6 (858) = Arg <Value> {x} (x[Value], x[Value])
553                                 // is rewritten by decomposeArgs into
554                                 //   v141 (858) = Arg <uintptr> {x}
555                                 //   v139 (858) = Arg <*uint8> {x} [8]
556                                 // because of a type case clause on line 862 of go/constant/value.go
557                                 //      case intVal:
558                                 //                 return itof(x)
559                                 // v139 is later stored as an intVal == struct{val *big.Int} which naively requires the fields of
560                                 // of a *uint8, which does not succeed.
561                                 t = removeTrivialWrapperTypes(t)
562                                 // it could be a leaf type, but the "leaf" could be complex64 (for example)
563                                 return storeArgOrLoad(pos, b, base, source, mem, t, offset)
564                         }
565
566                         for i := 0; i < t.NumFields(); i++ {
567                                 fld := t.Field(i)
568                                 sel := source.Block.NewValue1I(pos, OpStructSelect, fld.Type, int64(i), source)
569                                 mem = storeArgOrLoad(pos, b, base, sel, mem, fld.Type, offset+fld.Offset)
570                                 pos = pos.WithNotStmt()
571                         }
572                         return mem
573
574                 case types.TINT64, types.TUINT64:
575                         if t.Width == regSize {
576                                 break
577                         }
578                         tHi, tLo := intPairTypes(t.Kind())
579                         sel := source.Block.NewValue1(pos, OpInt64Hi, tHi, source)
580                         mem = storeArgOrLoad(pos, b, base, sel, mem, tHi, offset+hiOffset)
581                         pos = pos.WithNotStmt()
582                         sel = source.Block.NewValue1(pos, OpInt64Lo, tLo, source)
583                         return storeArgOrLoad(pos, b, base, sel, mem, tLo, offset+lowOffset)
584
585                 case types.TINTER:
586                         sel := source.Block.NewValue1(pos, OpITab, typ.BytePtr, source)
587                         mem = storeArgOrLoad(pos, b, base, sel, mem, typ.BytePtr, offset)
588                         pos = pos.WithNotStmt()
589                         sel = source.Block.NewValue1(pos, OpIData, typ.BytePtr, source)
590                         return storeArgOrLoad(pos, b, base, sel, mem, typ.BytePtr, offset+ptrSize)
591
592                 case types.TSTRING:
593                         sel := source.Block.NewValue1(pos, OpStringPtr, typ.BytePtr, source)
594                         mem = storeArgOrLoad(pos, b, base, sel, mem, typ.BytePtr, offset)
595                         pos = pos.WithNotStmt()
596                         sel = source.Block.NewValue1(pos, OpStringLen, typ.Int, source)
597                         return storeArgOrLoad(pos, b, base, sel, mem, typ.Int, offset+ptrSize)
598
599                 case types.TSLICE:
600                         et := types.NewPtr(t.Elem())
601                         sel := source.Block.NewValue1(pos, OpSlicePtr, et, source)
602                         mem = storeArgOrLoad(pos, b, base, sel, mem, et, offset)
603                         pos = pos.WithNotStmt()
604                         sel = source.Block.NewValue1(pos, OpSliceLen, typ.Int, source)
605                         mem = storeArgOrLoad(pos, b, base, sel, mem, typ.Int, offset+ptrSize)
606                         sel = source.Block.NewValue1(pos, OpSliceCap, typ.Int, source)
607                         return storeArgOrLoad(pos, b, base, sel, mem, typ.Int, offset+2*ptrSize)
608
609                 case types.TCOMPLEX64:
610                         sel := source.Block.NewValue1(pos, OpComplexReal, typ.Float32, source)
611                         mem = storeArgOrLoad(pos, b, base, sel, mem, typ.Float32, offset)
612                         pos = pos.WithNotStmt()
613                         sel = source.Block.NewValue1(pos, OpComplexImag, typ.Float32, source)
614                         return storeArgOrLoad(pos, b, base, sel, mem, typ.Float32, offset+4)
615
616                 case types.TCOMPLEX128:
617                         sel := source.Block.NewValue1(pos, OpComplexReal, typ.Float64, source)
618                         mem = storeArgOrLoad(pos, b, base, sel, mem, typ.Float64, offset)
619                         pos = pos.WithNotStmt()
620                         sel = source.Block.NewValue1(pos, OpComplexImag, typ.Float64, source)
621                         return storeArgOrLoad(pos, b, base, sel, mem, typ.Float64, offset+8)
622                 }
623
624                 dst := offsetFrom(base, offset, types.NewPtr(t))
625                 x := b.NewValue3A(pos, OpStore, types.TypeMem, t, dst, source, mem)
626                 if debug {
627                         fmt.Printf("\t\tstoreArg returns %s\n", x.LongString())
628                 }
629                 return x
630         }
631
632         rewriteDereference := func(b *Block, base, a, mem *Value, offset, size int64, typ *types.Type, pos src.XPos) *Value {
633                 source := a.Args[0]
634                 dst := offsetFrom(base, offset, source.Type)
635                 if a.Uses == 1 && a.Block == b {
636                         a.reset(OpMove)
637                         a.Pos = pos
638                         a.Type = types.TypeMem
639                         a.Aux = typ
640                         a.AuxInt = size
641                         a.SetArgs3(dst, source, mem)
642                         mem = a
643                 } else {
644                         mem = b.NewValue3A(pos, OpMove, types.TypeMem, typ, dst, source, mem)
645                         mem.AuxInt = size
646                 }
647                 return mem
648         }
649
650         // rewriteArgs removes all the Args from a call and converts the call args into appropriate
651         // stores (or later, register movement).  Extra args for interface and closure calls are ignored,
652         // but removed.
653         rewriteArgs := func(v *Value, firstArg int) *Value {
654                 // Thread the stores on the memory arg
655                 aux := v.Aux.(*AuxCall)
656                 pos := v.Pos.WithNotStmt()
657                 m0 := v.MemoryArg()
658                 mem := m0
659                 for i, a := range v.Args {
660                         if i < firstArg {
661                                 continue
662                         }
663                         if a == m0 { // mem is last.
664                                 break
665                         }
666                         auxI := int64(i - firstArg)
667                         if a.Op == OpDereference {
668                                 if a.MemoryArg() != m0 {
669                                         f.Fatalf("Op...LECall and OpDereference have mismatched mem, %s and %s", v.LongString(), a.LongString())
670                                 }
671                                 // "Dereference" of addressed (probably not-SSA-eligible) value becomes Move
672                                 // TODO this will be more complicated with registers in the picture.
673                                 mem = rewriteDereference(v.Block, sp, a, mem, aux.OffsetOfArg(auxI), aux.SizeOfArg(auxI), aux.TypeOfArg(auxI), pos)
674                         } else {
675                                 if debug {
676                                         fmt.Printf("storeArg %s, %v, %d\n", a.LongString(), aux.TypeOfArg(auxI), aux.OffsetOfArg(auxI))
677                                 }
678                                 mem = storeArgOrLoad(pos, v.Block, sp, a, mem, aux.TypeOfArg(auxI), aux.OffsetOfArg(auxI))
679                         }
680                 }
681                 v.resetArgs()
682                 return mem
683         }
684
685         // TODO if too slow, whole program iteration can be replaced w/ slices of appropriate values, accumulated in first loop here.
686
687         // Step 0: rewrite the calls to convert incoming args to stores.
688         for _, b := range f.Blocks {
689                 for _, v := range b.Values {
690                         switch v.Op {
691                         case OpStaticLECall:
692                                 mem := rewriteArgs(v, 0)
693                                 v.SetArgs1(mem)
694                         case OpClosureLECall:
695                                 code := v.Args[0]
696                                 context := v.Args[1]
697                                 mem := rewriteArgs(v, 2)
698                                 v.SetArgs3(code, context, mem)
699                         case OpInterLECall:
700                                 code := v.Args[0]
701                                 mem := rewriteArgs(v, 1)
702                                 v.SetArgs2(code, mem)
703                         }
704                 }
705                 if isBlockMultiValueExit(b) {
706                         // Very similar to code in rewriteArgs, but results instead of args.
707                         v := b.Controls[0]
708                         m0 := v.MemoryArg()
709                         mem := m0
710                         aux := f.OwnAux
711                         pos := v.Pos.WithNotStmt()
712                         for j, a := range v.Args {
713                                 i := int64(j)
714                                 if a == m0 {
715                                         break
716                                 }
717                                 auxType := aux.TypeOfResult(i)
718                                 auxBase := b.NewValue2A(v.Pos, OpLocalAddr, types.NewPtr(auxType), aux.results[i].Name, sp, mem)
719                                 auxOffset := int64(0)
720                                 auxSize := aux.SizeOfResult(i)
721                                 if a.Op == OpDereference {
722                                         // Avoid a self-move, and if one is detected try to remove the already-inserted VarDef for the assignment that won't happen.
723                                         if dAddr, dMem := a.Args[0], a.Args[1]; dAddr.Op == OpLocalAddr && dAddr.Args[0].Op == OpSP &&
724                                                 dAddr.Args[1] == dMem && dAddr.Aux == aux.results[i].Name {
725                                                 if dMem.Op == OpVarDef && dMem.Aux == dAddr.Aux {
726                                                         dMem.copyOf(dMem.MemoryArg()) // elide the VarDef
727                                                 }
728                                                 continue
729                                         }
730                                         mem = rewriteDereference(v.Block, auxBase, a, mem, auxOffset, auxSize, auxType, pos)
731                                 } else {
732                                         if a.Op == OpLoad && a.Args[0].Op == OpLocalAddr {
733                                                 addr := a.Args[0]
734                                                 if addr.MemoryArg() == a.MemoryArg() && addr.Aux == aux.results[i].Name {
735                                                         continue
736                                                 }
737                                         }
738                                         mem = storeArgOrLoad(v.Pos, b, auxBase, a, mem, aux.TypeOfResult(i), auxOffset)
739                                 }
740                         }
741                         b.SetControl(mem)
742                         v.reset(OpInvalid) // otherwise it can have a mem operand which will fail check(), even though it is dead.
743                 }
744         }
745
746         for i, name := range f.Names {
747                 t := name.Type
748                 if isAlreadyExpandedAggregateType(t) {
749                         for j, v := range f.NamedValues[name] {
750                                 if v.Op == OpSelectN || v.Op == OpArg && isAlreadyExpandedAggregateType(v.Type) {
751                                         ns := namedSelects[v]
752                                         namedSelects[v] = append(ns, namedVal{locIndex: i, valIndex: j})
753                                 }
754                         }
755                 }
756         }
757
758         // Step 1: any stores of aggregates remaining are believed to be sourced from call results or args.
759         // Decompose those stores into a series of smaller stores, adding selection ops as necessary.
760         for _, b := range f.Blocks {
761                 for _, v := range b.Values {
762                         if v.Op == OpStore {
763                                 t := v.Aux.(*types.Type)
764                                 source := v.Args[1]
765                                 tSrc := source.Type
766                                 iAEATt := isAlreadyExpandedAggregateType(t)
767
768                                 if !iAEATt {
769                                         // guarding against store immediate struct into interface data field -- store type is *uint8
770                                         // TODO can this happen recursively?
771                                         iAEATt = isAlreadyExpandedAggregateType(tSrc)
772                                         if iAEATt {
773                                                 t = tSrc
774                                         }
775                                 }
776                                 if iAEATt {
777                                         if debug {
778                                                 fmt.Printf("Splitting store %s\n", v.LongString())
779                                         }
780                                         dst, mem := v.Args[0], v.Args[2]
781                                         mem = storeArgOrLoad(v.Pos, b, dst, source, mem, t, 0)
782                                         v.copyOf(mem)
783                                 }
784                         }
785                 }
786         }
787
788         val2Preds := make(map[*Value]int32) // Used to accumulate dependency graph of selection operations for topological ordering.
789
790         // Step 2: transform or accumulate selection operations for rewrite in topological order.
791         //
792         // Aggregate types that have already (in earlier phases) been transformed must be lowered comprehensively to finish
793         // the transformation (user-defined structs and arrays, slices, strings, interfaces, complex, 64-bit on 32-bit architectures),
794         //
795         // Any select-for-addressing applied to call results can be transformed directly.
796         for _, b := range f.Blocks {
797                 for _, v := range b.Values {
798                         // Accumulate chains of selectors for processing in topological order
799                         switch v.Op {
800                         case OpStructSelect, OpArraySelect,
801                                 OpIData, OpITab,
802                                 OpStringPtr, OpStringLen,
803                                 OpSlicePtr, OpSliceLen, OpSliceCap,
804                                 OpComplexReal, OpComplexImag,
805                                 OpInt64Hi, OpInt64Lo:
806                                 w := v.Args[0]
807                                 switch w.Op {
808                                 case OpStructSelect, OpArraySelect, OpSelectN, OpArg:
809                                         val2Preds[w] += 1
810                                         if debug {
811                                                 fmt.Printf("v2p[%s] = %d\n", w.LongString(), val2Preds[w])
812                                         }
813                                 }
814                                 fallthrough
815
816                         case OpSelectN:
817                                 if _, ok := val2Preds[v]; !ok {
818                                         val2Preds[v] = 0
819                                         if debug {
820                                                 fmt.Printf("v2p[%s] = %d\n", v.LongString(), val2Preds[v])
821                                         }
822                                 }
823
824                         case OpArg:
825                                 if !isAlreadyExpandedAggregateType(v.Type) {
826                                         continue
827                                 }
828                                 if _, ok := val2Preds[v]; !ok {
829                                         val2Preds[v] = 0
830                                         if debug {
831                                                 fmt.Printf("v2p[%s] = %d\n", v.LongString(), val2Preds[v])
832                                         }
833                                 }
834
835                         case OpSelectNAddr:
836                                 // Do these directly, there are no chains of selectors.
837                                 call := v.Args[0]
838                                 which := v.AuxInt
839                                 aux := call.Aux.(*AuxCall)
840                                 pt := v.Type
841                                 off := offsetFrom(sp, aux.OffsetOfResult(which), pt)
842                                 v.copyOf(off)
843                         }
844                 }
845         }
846
847         // Step 3: Compute topological order of selectors,
848         // then process it in reverse to eliminate duplicates,
849         // then forwards to rewrite selectors.
850         //
851         // All chains of selectors end up in same block as the call.
852
853         // Compilation must be deterministic, so sort after extracting first zeroes from map.
854         // Sorting allows dominators-last order within each batch,
855         // so that the backwards scan for duplicates will most often find copies from dominating blocks (it is best-effort).
856         var toProcess []*Value
857         less := func(i, j int) bool {
858                 vi, vj := toProcess[i], toProcess[j]
859                 bi, bj := vi.Block, vj.Block
860                 if bi == bj {
861                         return vi.ID < vj.ID
862                 }
863                 return sdom.domorder(bi) > sdom.domorder(bj) // reverse the order to put dominators last.
864         }
865
866         // Accumulate order in allOrdered
867         var allOrdered []*Value
868         for v, n := range val2Preds {
869                 if n == 0 {
870                         allOrdered = append(allOrdered, v)
871                 }
872         }
873         last := 0 // allOrdered[0:last] has been top-sorted and processed
874         for len(val2Preds) > 0 {
875                 toProcess = allOrdered[last:]
876                 last = len(allOrdered)
877                 sort.SliceStable(toProcess, less)
878                 for _, v := range toProcess {
879                         delete(val2Preds, v)
880                         if v.Op == OpArg {
881                                 continue // no Args[0], hence done.
882                         }
883                         w := v.Args[0]
884                         n, ok := val2Preds[w]
885                         if !ok {
886                                 continue
887                         }
888                         if n == 1 {
889                                 allOrdered = append(allOrdered, w)
890                                 delete(val2Preds, w)
891                                 continue
892                         }
893                         val2Preds[w] = n - 1
894                 }
895         }
896
897         common = make(map[selKey]*Value)
898         // Rewrite duplicate selectors as copies where possible.
899         for i := len(allOrdered) - 1; i >= 0; i-- {
900                 v := allOrdered[i]
901                 if v.Op == OpArg {
902                         continue
903                 }
904                 w := v.Args[0]
905                 if w.Op == OpCopy {
906                         for w.Op == OpCopy {
907                                 w = w.Args[0]
908                         }
909                         v.SetArg(0, w)
910                 }
911                 typ := v.Type
912                 if typ.IsMemory() {
913                         continue // handled elsewhere, not an indexable result
914                 }
915                 size := typ.Width
916                 offset := int64(0)
917                 switch v.Op {
918                 case OpStructSelect:
919                         if w.Type.Kind() == types.TSTRUCT {
920                                 offset = w.Type.FieldOff(int(v.AuxInt))
921                         } else { // Immediate interface data artifact, offset is zero.
922                                 f.Fatalf("Expand calls interface data problem, func %s, v=%s, w=%s\n", f.Name, v.LongString(), w.LongString())
923                         }
924                 case OpArraySelect:
925                         offset = size * v.AuxInt
926                 case OpSelectN:
927                         offset = w.Aux.(*AuxCall).OffsetOfResult(v.AuxInt)
928                 case OpInt64Hi:
929                         offset = hiOffset
930                 case OpInt64Lo:
931                         offset = lowOffset
932                 case OpStringLen, OpSliceLen, OpIData:
933                         offset = ptrSize
934                 case OpSliceCap:
935                         offset = 2 * ptrSize
936                 case OpComplexImag:
937                         offset = size
938                 }
939                 sk := selKey{from: w, size: size, offset: offset, typ: typ}
940                 dupe := common[sk]
941                 if dupe == nil {
942                         common[sk] = v
943                 } else if sdom.IsAncestorEq(dupe.Block, v.Block) {
944                         v.copyOf(dupe)
945                 } else {
946                         // Because values are processed in dominator order, the old common[s] will never dominate after a miss is seen.
947                         // Installing the new value might match some future values.
948                         common[sk] = v
949                 }
950         }
951
952         // Indices of entries in f.Names that need to be deleted.
953         var toDelete []namedVal
954
955         // Rewrite selectors.
956         for i, v := range allOrdered {
957                 if debug {
958                         b := v.Block
959                         fmt.Printf("allOrdered[%d] = b%d, %s, uses=%d\n", i, b.ID, v.LongString(), v.Uses)
960                 }
961                 if v.Uses == 0 {
962                         v.reset(OpInvalid)
963                         continue
964                 }
965                 if v.Op == OpCopy {
966                         continue
967                 }
968                 locs := rewriteSelect(v, v, 0)
969                 // Install new names.
970                 if v.Type.IsMemory() {
971                         continue
972                 }
973                 // Leaf types may have debug locations
974                 if !isAlreadyExpandedAggregateType(v.Type) {
975                         for _, l := range locs {
976                                 f.NamedValues[l] = append(f.NamedValues[l], v)
977                         }
978                         f.Names = append(f.Names, locs...)
979                         continue
980                 }
981                 // Not-leaf types that had debug locations need to lose them.
982                 if ns, ok := namedSelects[v]; ok {
983                         toDelete = append(toDelete, ns...)
984                 }
985         }
986
987         deleteNamedVals(f, toDelete)
988
989         // Step 4: rewrite the calls themselves, correcting the type
990         for _, b := range f.Blocks {
991                 for _, v := range b.Values {
992                         switch v.Op {
993                         case OpStaticLECall:
994                                 v.Op = OpStaticCall
995                                 v.Type = types.TypeMem
996                         case OpClosureLECall:
997                                 v.Op = OpClosureCall
998                                 v.Type = types.TypeMem
999                         case OpInterLECall:
1000                                 v.Op = OpInterCall
1001                                 v.Type = types.TypeMem
1002                         }
1003                 }
1004         }
1005
1006         // Step 5: elide any copies introduced.
1007         for _, b := range f.Blocks {
1008                 for _, v := range b.Values {
1009                         for i, a := range v.Args {
1010                                 if a.Op != OpCopy {
1011                                         continue
1012                                 }
1013                                 aa := copySource(a)
1014                                 v.SetArg(i, aa)
1015                                 for a.Uses == 0 {
1016                                         b := a.Args[0]
1017                                         a.reset(OpInvalid)
1018                                         a = b
1019                                 }
1020                         }
1021                 }
1022         }
1023 }