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

     1  // Copyright 2015 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  	"sort"
     9  
    10  	"github.com/go-asm/go/cmd/compile/types"
    11  )
    12  
    13  // decompose converts phi ops on compound builtin types into phi
    14  // ops on simple types, then invokes rewrite rules to decompose
    15  // other ops on those types.
    16  func decomposeBuiltIn(f *Func) {
    17  	// Decompose phis
    18  	for _, b := range f.Blocks {
    19  		for _, v := range b.Values {
    20  			if v.Op != OpPhi {
    21  				continue
    22  			}
    23  			decomposeBuiltInPhi(v)
    24  		}
    25  	}
    26  
    27  	// Decompose other values
    28  	// Note: Leave dead values because we need to keep the original
    29  	// values around so the name component resolution below can still work.
    30  	applyRewrite(f, rewriteBlockdec, rewriteValuedec, leaveDeadValues)
    31  	if f.Config.RegSize == 4 {
    32  		applyRewrite(f, rewriteBlockdec64, rewriteValuedec64, leaveDeadValues)
    33  	}
    34  
    35  	// Split up named values into their components.
    36  	// accumulate old names for aggregates (that are decomposed) in toDelete for efficient bulk deletion,
    37  	// accumulate new LocalSlots in newNames for addition after the iteration.  This decomposition is for
    38  	// builtin types with leaf components, and thus there is no need to reprocess the newly create LocalSlots.
    39  	var toDelete []namedVal
    40  	var newNames []*LocalSlot
    41  	for i, name := range f.Names {
    42  		t := name.Type
    43  		switch {
    44  		case t.IsInteger() && t.Size() > f.Config.RegSize:
    45  			hiName, loName := f.SplitInt64(name)
    46  			newNames = maybeAppend2(f, newNames, hiName, loName)
    47  			for j, v := range f.NamedValues[*name] {
    48  				if v.Op != OpInt64Make {
    49  					continue
    50  				}
    51  				f.NamedValues[*hiName] = append(f.NamedValues[*hiName], v.Args[0])
    52  				f.NamedValues[*loName] = append(f.NamedValues[*loName], v.Args[1])
    53  				toDelete = append(toDelete, namedVal{i, j})
    54  			}
    55  		case t.IsComplex():
    56  			rName, iName := f.SplitComplex(name)
    57  			newNames = maybeAppend2(f, newNames, rName, iName)
    58  			for j, v := range f.NamedValues[*name] {
    59  				if v.Op != OpComplexMake {
    60  					continue
    61  				}
    62  				f.NamedValues[*rName] = append(f.NamedValues[*rName], v.Args[0])
    63  				f.NamedValues[*iName] = append(f.NamedValues[*iName], v.Args[1])
    64  				toDelete = append(toDelete, namedVal{i, j})
    65  			}
    66  		case t.IsString():
    67  			ptrName, lenName := f.SplitString(name)
    68  			newNames = maybeAppend2(f, newNames, ptrName, lenName)
    69  			for j, v := range f.NamedValues[*name] {
    70  				if v.Op != OpStringMake {
    71  					continue
    72  				}
    73  				f.NamedValues[*ptrName] = append(f.NamedValues[*ptrName], v.Args[0])
    74  				f.NamedValues[*lenName] = append(f.NamedValues[*lenName], v.Args[1])
    75  				toDelete = append(toDelete, namedVal{i, j})
    76  			}
    77  		case t.IsSlice():
    78  			ptrName, lenName, capName := f.SplitSlice(name)
    79  			newNames = maybeAppend2(f, newNames, ptrName, lenName)
    80  			newNames = maybeAppend(f, newNames, capName)
    81  			for j, v := range f.NamedValues[*name] {
    82  				if v.Op != OpSliceMake {
    83  					continue
    84  				}
    85  				f.NamedValues[*ptrName] = append(f.NamedValues[*ptrName], v.Args[0])
    86  				f.NamedValues[*lenName] = append(f.NamedValues[*lenName], v.Args[1])
    87  				f.NamedValues[*capName] = append(f.NamedValues[*capName], v.Args[2])
    88  				toDelete = append(toDelete, namedVal{i, j})
    89  			}
    90  		case t.IsInterface():
    91  			typeName, dataName := f.SplitInterface(name)
    92  			newNames = maybeAppend2(f, newNames, typeName, dataName)
    93  			for j, v := range f.NamedValues[*name] {
    94  				if v.Op != OpIMake {
    95  					continue
    96  				}
    97  				f.NamedValues[*typeName] = append(f.NamedValues[*typeName], v.Args[0])
    98  				f.NamedValues[*dataName] = append(f.NamedValues[*dataName], v.Args[1])
    99  				toDelete = append(toDelete, namedVal{i, j})
   100  			}
   101  		case t.IsFloat():
   102  			// floats are never decomposed, even ones bigger than RegSize
   103  		case t.Size() > f.Config.RegSize:
   104  			f.Fatalf("undecomposed named type %s %v", name, t)
   105  		}
   106  	}
   107  
   108  	deleteNamedVals(f, toDelete)
   109  	f.Names = append(f.Names, newNames...)
   110  }
   111  
   112  func maybeAppend(f *Func, ss []*LocalSlot, s *LocalSlot) []*LocalSlot {
   113  	if _, ok := f.NamedValues[*s]; !ok {
   114  		f.NamedValues[*s] = nil
   115  		return append(ss, s)
   116  	}
   117  	return ss
   118  }
   119  
   120  func maybeAppend2(f *Func, ss []*LocalSlot, s1, s2 *LocalSlot) []*LocalSlot {
   121  	return maybeAppend(f, maybeAppend(f, ss, s1), s2)
   122  }
   123  
   124  func decomposeBuiltInPhi(v *Value) {
   125  	switch {
   126  	case v.Type.IsInteger() && v.Type.Size() > v.Block.Func.Config.RegSize:
   127  		decomposeInt64Phi(v)
   128  	case v.Type.IsComplex():
   129  		decomposeComplexPhi(v)
   130  	case v.Type.IsString():
   131  		decomposeStringPhi(v)
   132  	case v.Type.IsSlice():
   133  		decomposeSlicePhi(v)
   134  	case v.Type.IsInterface():
   135  		decomposeInterfacePhi(v)
   136  	case v.Type.IsFloat():
   137  		// floats are never decomposed, even ones bigger than RegSize
   138  	case v.Type.Size() > v.Block.Func.Config.RegSize:
   139  		v.Fatalf("%v undecomposed type %v", v, v.Type)
   140  	}
   141  }
   142  
   143  func decomposeStringPhi(v *Value) {
   144  	types := &v.Block.Func.Config.Types
   145  	ptrType := types.BytePtr
   146  	lenType := types.Int
   147  
   148  	ptr := v.Block.NewValue0(v.Pos, OpPhi, ptrType)
   149  	len := v.Block.NewValue0(v.Pos, OpPhi, lenType)
   150  	for _, a := range v.Args {
   151  		ptr.AddArg(a.Block.NewValue1(v.Pos, OpStringPtr, ptrType, a))
   152  		len.AddArg(a.Block.NewValue1(v.Pos, OpStringLen, lenType, a))
   153  	}
   154  	v.reset(OpStringMake)
   155  	v.AddArg(ptr)
   156  	v.AddArg(len)
   157  }
   158  
   159  func decomposeSlicePhi(v *Value) {
   160  	types := &v.Block.Func.Config.Types
   161  	ptrType := v.Type.Elem().PtrTo()
   162  	lenType := types.Int
   163  
   164  	ptr := v.Block.NewValue0(v.Pos, OpPhi, ptrType)
   165  	len := v.Block.NewValue0(v.Pos, OpPhi, lenType)
   166  	cap := v.Block.NewValue0(v.Pos, OpPhi, lenType)
   167  	for _, a := range v.Args {
   168  		ptr.AddArg(a.Block.NewValue1(v.Pos, OpSlicePtr, ptrType, a))
   169  		len.AddArg(a.Block.NewValue1(v.Pos, OpSliceLen, lenType, a))
   170  		cap.AddArg(a.Block.NewValue1(v.Pos, OpSliceCap, lenType, a))
   171  	}
   172  	v.reset(OpSliceMake)
   173  	v.AddArg(ptr)
   174  	v.AddArg(len)
   175  	v.AddArg(cap)
   176  }
   177  
   178  func decomposeInt64Phi(v *Value) {
   179  	cfgtypes := &v.Block.Func.Config.Types
   180  	var partType *types.Type
   181  	if v.Type.IsSigned() {
   182  		partType = cfgtypes.Int32
   183  	} else {
   184  		partType = cfgtypes.UInt32
   185  	}
   186  
   187  	hi := v.Block.NewValue0(v.Pos, OpPhi, partType)
   188  	lo := v.Block.NewValue0(v.Pos, OpPhi, cfgtypes.UInt32)
   189  	for _, a := range v.Args {
   190  		hi.AddArg(a.Block.NewValue1(v.Pos, OpInt64Hi, partType, a))
   191  		lo.AddArg(a.Block.NewValue1(v.Pos, OpInt64Lo, cfgtypes.UInt32, a))
   192  	}
   193  	v.reset(OpInt64Make)
   194  	v.AddArg(hi)
   195  	v.AddArg(lo)
   196  }
   197  
   198  func decomposeComplexPhi(v *Value) {
   199  	cfgtypes := &v.Block.Func.Config.Types
   200  	var partType *types.Type
   201  	switch z := v.Type.Size(); z {
   202  	case 8:
   203  		partType = cfgtypes.Float32
   204  	case 16:
   205  		partType = cfgtypes.Float64
   206  	default:
   207  		v.Fatalf("decomposeComplexPhi: bad complex size %d", z)
   208  	}
   209  
   210  	real := v.Block.NewValue0(v.Pos, OpPhi, partType)
   211  	imag := v.Block.NewValue0(v.Pos, OpPhi, partType)
   212  	for _, a := range v.Args {
   213  		real.AddArg(a.Block.NewValue1(v.Pos, OpComplexReal, partType, a))
   214  		imag.AddArg(a.Block.NewValue1(v.Pos, OpComplexImag, partType, a))
   215  	}
   216  	v.reset(OpComplexMake)
   217  	v.AddArg(real)
   218  	v.AddArg(imag)
   219  }
   220  
   221  func decomposeInterfacePhi(v *Value) {
   222  	uintptrType := v.Block.Func.Config.Types.Uintptr
   223  	ptrType := v.Block.Func.Config.Types.BytePtr
   224  
   225  	itab := v.Block.NewValue0(v.Pos, OpPhi, uintptrType)
   226  	data := v.Block.NewValue0(v.Pos, OpPhi, ptrType)
   227  	for _, a := range v.Args {
   228  		itab.AddArg(a.Block.NewValue1(v.Pos, OpITab, uintptrType, a))
   229  		data.AddArg(a.Block.NewValue1(v.Pos, OpIData, ptrType, a))
   230  	}
   231  	v.reset(OpIMake)
   232  	v.AddArg(itab)
   233  	v.AddArg(data)
   234  }
   235  
   236  func decomposeUser(f *Func) {
   237  	for _, b := range f.Blocks {
   238  		for _, v := range b.Values {
   239  			if v.Op != OpPhi {
   240  				continue
   241  			}
   242  			decomposeUserPhi(v)
   243  		}
   244  	}
   245  	// Split up named values into their components.
   246  	i := 0
   247  	var newNames []*LocalSlot
   248  	for _, name := range f.Names {
   249  		t := name.Type
   250  		switch {
   251  		case t.IsStruct():
   252  			newNames = decomposeUserStructInto(f, name, newNames)
   253  		case t.IsArray():
   254  			newNames = decomposeUserArrayInto(f, name, newNames)
   255  		default:
   256  			f.Names[i] = name
   257  			i++
   258  		}
   259  	}
   260  	f.Names = f.Names[:i]
   261  	f.Names = append(f.Names, newNames...)
   262  }
   263  
   264  // decomposeUserArrayInto creates names for the element(s) of arrays referenced
   265  // by name where possible, and appends those new names to slots, which is then
   266  // returned.
   267  func decomposeUserArrayInto(f *Func, name *LocalSlot, slots []*LocalSlot) []*LocalSlot {
   268  	t := name.Type
   269  	if t.NumElem() == 0 {
   270  		// TODO(khr): Not sure what to do here.  Probably nothing.
   271  		// Names for empty arrays aren't important.
   272  		return slots
   273  	}
   274  	if t.NumElem() != 1 {
   275  		// shouldn't get here due to CanSSA
   276  		f.Fatalf("array not of size 1")
   277  	}
   278  	elemName := f.SplitArray(name)
   279  	var keep []*Value
   280  	for _, v := range f.NamedValues[*name] {
   281  		if v.Op != OpArrayMake1 {
   282  			keep = append(keep, v)
   283  			continue
   284  		}
   285  		f.NamedValues[*elemName] = append(f.NamedValues[*elemName], v.Args[0])
   286  	}
   287  	if len(keep) == 0 {
   288  		// delete the name for the array as a whole
   289  		delete(f.NamedValues, *name)
   290  	} else {
   291  		f.NamedValues[*name] = keep
   292  	}
   293  
   294  	if t.Elem().IsArray() {
   295  		return decomposeUserArrayInto(f, elemName, slots)
   296  	} else if t.Elem().IsStruct() {
   297  		return decomposeUserStructInto(f, elemName, slots)
   298  	}
   299  
   300  	return append(slots, elemName)
   301  }
   302  
   303  // decomposeUserStructInto creates names for the fields(s) of structs referenced
   304  // by name where possible, and appends those new names to slots, which is then
   305  // returned.
   306  func decomposeUserStructInto(f *Func, name *LocalSlot, slots []*LocalSlot) []*LocalSlot {
   307  	fnames := []*LocalSlot{} // slots for struct in name
   308  	t := name.Type
   309  	n := t.NumFields()
   310  
   311  	for i := 0; i < n; i++ {
   312  		fs := f.SplitStruct(name, i)
   313  		fnames = append(fnames, fs)
   314  		// arrays and structs will be decomposed further, so
   315  		// there's no need to record a name
   316  		if !fs.Type.IsArray() && !fs.Type.IsStruct() {
   317  			slots = maybeAppend(f, slots, fs)
   318  		}
   319  	}
   320  
   321  	makeOp := StructMakeOp(n)
   322  	var keep []*Value
   323  	// create named values for each struct field
   324  	for _, v := range f.NamedValues[*name] {
   325  		if v.Op != makeOp {
   326  			keep = append(keep, v)
   327  			continue
   328  		}
   329  		for i := 0; i < len(fnames); i++ {
   330  			f.NamedValues[*fnames[i]] = append(f.NamedValues[*fnames[i]], v.Args[i])
   331  		}
   332  	}
   333  	if len(keep) == 0 {
   334  		// delete the name for the struct as a whole
   335  		delete(f.NamedValues, *name)
   336  	} else {
   337  		f.NamedValues[*name] = keep
   338  	}
   339  
   340  	// now that this f.NamedValues contains values for the struct
   341  	// fields, recurse into nested structs
   342  	for i := 0; i < n; i++ {
   343  		if name.Type.FieldType(i).IsStruct() {
   344  			slots = decomposeUserStructInto(f, fnames[i], slots)
   345  			delete(f.NamedValues, *fnames[i])
   346  		} else if name.Type.FieldType(i).IsArray() {
   347  			slots = decomposeUserArrayInto(f, fnames[i], slots)
   348  			delete(f.NamedValues, *fnames[i])
   349  		}
   350  	}
   351  	return slots
   352  }
   353  func decomposeUserPhi(v *Value) {
   354  	switch {
   355  	case v.Type.IsStruct():
   356  		decomposeStructPhi(v)
   357  	case v.Type.IsArray():
   358  		decomposeArrayPhi(v)
   359  	}
   360  }
   361  
   362  // decomposeStructPhi replaces phi-of-struct with structmake(phi-for-each-field),
   363  // and then recursively decomposes the phis for each field.
   364  func decomposeStructPhi(v *Value) {
   365  	t := v.Type
   366  	n := t.NumFields()
   367  	var fields [MaxStruct]*Value
   368  	for i := 0; i < n; i++ {
   369  		fields[i] = v.Block.NewValue0(v.Pos, OpPhi, t.FieldType(i))
   370  	}
   371  	for _, a := range v.Args {
   372  		for i := 0; i < n; i++ {
   373  			fields[i].AddArg(a.Block.NewValue1I(v.Pos, OpStructSelect, t.FieldType(i), int64(i), a))
   374  		}
   375  	}
   376  	v.reset(StructMakeOp(n))
   377  	v.AddArgs(fields[:n]...)
   378  
   379  	// Recursively decompose phis for each field.
   380  	for _, f := range fields[:n] {
   381  		decomposeUserPhi(f)
   382  	}
   383  }
   384  
   385  // decomposeArrayPhi replaces phi-of-array with arraymake(phi-of-array-element),
   386  // and then recursively decomposes the element phi.
   387  func decomposeArrayPhi(v *Value) {
   388  	t := v.Type
   389  	if t.NumElem() == 0 {
   390  		v.reset(OpArrayMake0)
   391  		return
   392  	}
   393  	if t.NumElem() != 1 {
   394  		v.Fatalf("SSAable array must have no more than 1 element")
   395  	}
   396  	elem := v.Block.NewValue0(v.Pos, OpPhi, t.Elem())
   397  	for _, a := range v.Args {
   398  		elem.AddArg(a.Block.NewValue1I(v.Pos, OpArraySelect, t.Elem(), 0, a))
   399  	}
   400  	v.reset(OpArrayMake1)
   401  	v.AddArg(elem)
   402  
   403  	// Recursively decompose elem phi.
   404  	decomposeUserPhi(elem)
   405  }
   406  
   407  // MaxStruct is the maximum number of fields a struct
   408  // can have and still be SSAable.
   409  const MaxStruct = 4
   410  
   411  // StructMakeOp returns the opcode to construct a struct with the
   412  // given number of fields.
   413  func StructMakeOp(nf int) Op {
   414  	switch nf {
   415  	case 0:
   416  		return OpStructMake0
   417  	case 1:
   418  		return OpStructMake1
   419  	case 2:
   420  		return OpStructMake2
   421  	case 3:
   422  		return OpStructMake3
   423  	case 4:
   424  		return OpStructMake4
   425  	}
   426  	panic("too many fields in an SSAable struct")
   427  }
   428  
   429  type namedVal struct {
   430  	locIndex, valIndex int // f.NamedValues[f.Names[locIndex]][valIndex] = key
   431  }
   432  
   433  // deleteNamedVals removes particular values with debugger names from f's naming data structures,
   434  // removes all values with OpInvalid, and re-sorts the list of Names.
   435  func deleteNamedVals(f *Func, toDelete []namedVal) {
   436  	// Arrange to delete from larger indices to smaller, to ensure swap-with-end deletion does not invalidate pending indices.
   437  	sort.Slice(toDelete, func(i, j int) bool {
   438  		if toDelete[i].locIndex != toDelete[j].locIndex {
   439  			return toDelete[i].locIndex > toDelete[j].locIndex
   440  		}
   441  		return toDelete[i].valIndex > toDelete[j].valIndex
   442  
   443  	})
   444  
   445  	// Get rid of obsolete names
   446  	for _, d := range toDelete {
   447  		loc := f.Names[d.locIndex]
   448  		vals := f.NamedValues[*loc]
   449  		l := len(vals) - 1
   450  		if l > 0 {
   451  			vals[d.valIndex] = vals[l]
   452  		}
   453  		vals[l] = nil
   454  		f.NamedValues[*loc] = vals[:l]
   455  	}
   456  	// Delete locations with no values attached.
   457  	end := len(f.Names)
   458  	for i := len(f.Names) - 1; i >= 0; i-- {
   459  		loc := f.Names[i]
   460  		vals := f.NamedValues[*loc]
   461  		last := len(vals)
   462  		for j := len(vals) - 1; j >= 0; j-- {
   463  			if vals[j].Op == OpInvalid {
   464  				last--
   465  				vals[j] = vals[last]
   466  				vals[last] = nil
   467  			}
   468  		}
   469  		if last < len(vals) {
   470  			f.NamedValues[*loc] = vals[:last]
   471  		}
   472  		if len(vals) == 0 {
   473  			delete(f.NamedValues, *loc)
   474  			end--
   475  			f.Names[i] = f.Names[end]
   476  			f.Names[end] = nil
   477  		}
   478  	}
   479  	f.Names = f.Names[:end]
   480  }