github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/ssa/expand_calls.go (about)

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