Source file src/cmd/compile/internal/ssa/config.go

     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  	"cmd/compile/internal/abi"
     9  	"cmd/compile/internal/base"
    10  	"cmd/compile/internal/ir"
    11  	"cmd/compile/internal/types"
    12  	"cmd/internal/obj"
    13  	"cmd/internal/src"
    14  )
    15  
    16  // A Config holds readonly compilation information.
    17  // It is created once, early during compilation,
    18  // and shared across all compilations.
    19  type Config struct {
    20  	arch           string // "amd64", etc.
    21  	PtrSize        int64  // 4 or 8; copy of cmd/internal/sys.Arch.PtrSize
    22  	RegSize        int64  // 4 or 8; copy of cmd/internal/sys.Arch.RegSize
    23  	Types          Types
    24  	lowerBlock     blockRewriter  // block lowering function, first round
    25  	lowerValue     valueRewriter  // value lowering function, first round
    26  	lateLowerBlock blockRewriter  // block lowering function that needs to be run after the first round; only used on some architectures
    27  	lateLowerValue valueRewriter  // value lowering function that needs to be run after the first round; only used on some architectures
    28  	splitLoad      valueRewriter  // function for splitting merged load ops; only used on some architectures
    29  	registers      []Register     // machine registers
    30  	gpRegMask      regMask        // general purpose integer register mask
    31  	fpRegMask      regMask        // floating point register mask
    32  	fp32RegMask    regMask        // floating point register mask
    33  	fp64RegMask    regMask        // floating point register mask
    34  	simdRegMask    regMask        // simd register mask; may be same as fpRegMask
    35  	specialRegMask regMask        // special register mask
    36  	intParamRegs   []int8         // register numbers of integer param (in/out) registers
    37  	floatParamRegs []int8         // register numbers of floating param (in/out) registers
    38  	ABI1           *abi.ABIConfig // "ABIInternal" under development // TODO change comment when this becomes current
    39  	ABI0           *abi.ABIConfig
    40  	FPReg          int8      // register number of frame pointer, -1 if not used
    41  	LinkReg        int8      // register number of link register if it is a general purpose register, -1 if not used
    42  	hasGReg        bool      // has hardware g register
    43  	ctxt           *obj.Link // Generic arch information
    44  	optimize       bool      // Do optimization
    45  	SoftFloat      bool      //
    46  	Race           bool      // race detector enabled
    47  	BigEndian      bool      //
    48  	unalignedOK    bool      // Unaligned loads/stores are ok
    49  	haveBswap64    bool      // architecture implements Bswap64
    50  	haveBswap32    bool      // architecture implements Bswap32
    51  	haveBswap16    bool      // architecture implements Bswap16
    52  	haveCondSelect bool      // architecture implements CondSelect
    53  
    54  	// mulRecipes[x] = function to build v * x from v.
    55  	mulRecipes map[int64]mulRecipe
    56  }
    57  
    58  type mulRecipe struct {
    59  	cost  int
    60  	build func(*Value, *Value) *Value // build(m, v) returns v * x built at m.
    61  }
    62  
    63  type (
    64  	blockRewriter func(*Block) bool
    65  	valueRewriter func(*Value) bool
    66  )
    67  
    68  type Types struct {
    69  	Bool       *types.Type
    70  	Int8       *types.Type
    71  	Int16      *types.Type
    72  	Int32      *types.Type
    73  	Int64      *types.Type
    74  	UInt8      *types.Type
    75  	UInt16     *types.Type
    76  	UInt32     *types.Type
    77  	UInt64     *types.Type
    78  	Int        *types.Type
    79  	Float32    *types.Type
    80  	Float64    *types.Type
    81  	UInt       *types.Type
    82  	Uintptr    *types.Type
    83  	String     *types.Type
    84  	BytePtr    *types.Type // TODO: use unsafe.Pointer instead?
    85  	Int32Ptr   *types.Type
    86  	UInt32Ptr  *types.Type
    87  	IntPtr     *types.Type
    88  	UintptrPtr *types.Type
    89  	Float32Ptr *types.Type
    90  	Float64Ptr *types.Type
    91  	BytePtrPtr *types.Type
    92  	Vec128     *types.Type
    93  	Vec256     *types.Type
    94  	Vec512     *types.Type
    95  	Mask       *types.Type
    96  }
    97  
    98  // NewTypes creates and populates a Types.
    99  func NewTypes() *Types {
   100  	t := new(Types)
   101  	t.SetTypPtrs()
   102  	return t
   103  }
   104  
   105  // SetTypPtrs populates t.
   106  func (t *Types) SetTypPtrs() {
   107  	t.Bool = types.Types[types.TBOOL]
   108  	t.Int8 = types.Types[types.TINT8]
   109  	t.Int16 = types.Types[types.TINT16]
   110  	t.Int32 = types.Types[types.TINT32]
   111  	t.Int64 = types.Types[types.TINT64]
   112  	t.UInt8 = types.Types[types.TUINT8]
   113  	t.UInt16 = types.Types[types.TUINT16]
   114  	t.UInt32 = types.Types[types.TUINT32]
   115  	t.UInt64 = types.Types[types.TUINT64]
   116  	t.Int = types.Types[types.TINT]
   117  	t.Float32 = types.Types[types.TFLOAT32]
   118  	t.Float64 = types.Types[types.TFLOAT64]
   119  	t.UInt = types.Types[types.TUINT]
   120  	t.Uintptr = types.Types[types.TUINTPTR]
   121  	t.String = types.Types[types.TSTRING]
   122  	t.BytePtr = types.NewPtr(types.Types[types.TUINT8])
   123  	t.Int32Ptr = types.NewPtr(types.Types[types.TINT32])
   124  	t.UInt32Ptr = types.NewPtr(types.Types[types.TUINT32])
   125  	t.IntPtr = types.NewPtr(types.Types[types.TINT])
   126  	t.UintptrPtr = types.NewPtr(types.Types[types.TUINTPTR])
   127  	t.Float32Ptr = types.NewPtr(types.Types[types.TFLOAT32])
   128  	t.Float64Ptr = types.NewPtr(types.Types[types.TFLOAT64])
   129  	t.BytePtrPtr = types.NewPtr(types.NewPtr(types.Types[types.TUINT8]))
   130  	t.Vec128 = types.TypeVec128
   131  	t.Vec256 = types.TypeVec256
   132  	t.Vec512 = types.TypeVec512
   133  	t.Mask = types.TypeMask
   134  }
   135  
   136  type Logger interface {
   137  	// Logf logs a message from the compiler.
   138  	Logf(string, ...any)
   139  
   140  	// Log reports whether logging is not a no-op
   141  	// some logging calls account for more than a few heap allocations.
   142  	Log() bool
   143  
   144  	// Fatalf reports a compiler error and exits.
   145  	Fatalf(pos src.XPos, msg string, args ...any)
   146  
   147  	// Warnl writes compiler messages in the form expected by "errorcheck" tests
   148  	Warnl(pos src.XPos, fmt_ string, args ...any)
   149  
   150  	// Forwards the Debug flags from gc
   151  	Debug_checknil() bool
   152  }
   153  
   154  type Frontend interface {
   155  	Logger
   156  
   157  	// StringData returns a symbol pointing to the given string's contents.
   158  	StringData(string) *obj.LSym
   159  
   160  	// Given the name for a compound type, returns the name we should use
   161  	// for the parts of that compound type.
   162  	SplitSlot(parent *LocalSlot, suffix string, offset int64, t *types.Type) LocalSlot
   163  
   164  	// Syslook returns a symbol of the runtime function/variable with the
   165  	// given name.
   166  	Syslook(string) *obj.LSym
   167  
   168  	// UseWriteBarrier reports whether write barrier is enabled
   169  	UseWriteBarrier() bool
   170  
   171  	// Func returns the ir.Func of the function being compiled.
   172  	Func() *ir.Func
   173  }
   174  
   175  // NewConfig returns a new configuration object for the given architecture.
   176  func NewConfig(arch string, types Types, ctxt *obj.Link, optimize, softfloat bool) *Config {
   177  	c := &Config{arch: arch, Types: types}
   178  	switch arch {
   179  	case "amd64":
   180  		c.PtrSize = 8
   181  		c.RegSize = 8
   182  		c.lowerBlock = rewriteBlockAMD64
   183  		c.lowerValue = rewriteValueAMD64
   184  		c.lateLowerBlock = rewriteBlockAMD64latelower
   185  		c.lateLowerValue = rewriteValueAMD64latelower
   186  		c.splitLoad = rewriteValueAMD64splitload
   187  		c.registers = registersAMD64[:]
   188  		c.gpRegMask = gpRegMaskAMD64
   189  		c.fpRegMask = fpRegMaskAMD64
   190  		c.simdRegMask = simdRegMaskAMD64
   191  		c.specialRegMask = specialRegMaskAMD64
   192  		c.intParamRegs = paramIntRegAMD64
   193  		c.floatParamRegs = paramFloatRegAMD64
   194  		c.FPReg = framepointerRegAMD64
   195  		c.LinkReg = linkRegAMD64
   196  		c.hasGReg = true
   197  		c.unalignedOK = true
   198  		c.haveBswap64 = true
   199  		c.haveBswap32 = true
   200  		c.haveBswap16 = true
   201  		c.haveCondSelect = true
   202  	case "386":
   203  		c.PtrSize = 4
   204  		c.RegSize = 4
   205  		c.lowerBlock = rewriteBlock386
   206  		c.lowerValue = rewriteValue386
   207  		c.splitLoad = rewriteValue386splitload
   208  		c.registers = registers386[:]
   209  		c.gpRegMask = gpRegMask386
   210  		c.fpRegMask = fpRegMask386
   211  		c.FPReg = framepointerReg386
   212  		c.LinkReg = linkReg386
   213  		c.hasGReg = false
   214  		c.unalignedOK = true
   215  		c.haveBswap32 = true
   216  		c.haveBswap16 = true
   217  	case "arm":
   218  		c.PtrSize = 4
   219  		c.RegSize = 4
   220  		c.lowerBlock = rewriteBlockARM
   221  		c.lowerValue = rewriteValueARM
   222  		c.registers = registersARM[:]
   223  		c.gpRegMask = gpRegMaskARM
   224  		c.fpRegMask = fpRegMaskARM
   225  		c.FPReg = framepointerRegARM
   226  		c.LinkReg = linkRegARM
   227  		c.hasGReg = true
   228  	case "arm64":
   229  		c.PtrSize = 8
   230  		c.RegSize = 8
   231  		c.lowerBlock = rewriteBlockARM64
   232  		c.lowerValue = rewriteValueARM64
   233  		c.lateLowerBlock = rewriteBlockARM64latelower
   234  		c.lateLowerValue = rewriteValueARM64latelower
   235  		c.registers = registersARM64[:]
   236  		c.gpRegMask = gpRegMaskARM64
   237  		c.fpRegMask = fpRegMaskARM64
   238  		c.simdRegMask = simdRegMaskARM64
   239  		c.intParamRegs = paramIntRegARM64
   240  		c.floatParamRegs = paramFloatRegARM64
   241  		c.FPReg = framepointerRegARM64
   242  		c.LinkReg = linkRegARM64
   243  		c.hasGReg = true
   244  		c.unalignedOK = true
   245  		c.haveBswap64 = true
   246  		c.haveBswap32 = true
   247  		c.haveBswap16 = true
   248  		c.haveCondSelect = true
   249  	case "ppc64":
   250  		c.BigEndian = true
   251  		fallthrough
   252  	case "ppc64le":
   253  		c.PtrSize = 8
   254  		c.RegSize = 8
   255  		c.lowerBlock = rewriteBlockPPC64
   256  		c.lowerValue = rewriteValuePPC64
   257  		c.lateLowerBlock = rewriteBlockPPC64latelower
   258  		c.lateLowerValue = rewriteValuePPC64latelower
   259  		c.registers = registersPPC64[:]
   260  		c.gpRegMask = gpRegMaskPPC64
   261  		c.fpRegMask = fpRegMaskPPC64
   262  		c.specialRegMask = specialRegMaskPPC64
   263  		c.intParamRegs = paramIntRegPPC64
   264  		c.floatParamRegs = paramFloatRegPPC64
   265  		c.FPReg = framepointerRegPPC64
   266  		c.LinkReg = linkRegPPC64
   267  		c.hasGReg = true
   268  		c.unalignedOK = true
   269  		// Note: ppc64 has register bswap ops only when GOPPC64>=10.
   270  		// But it has bswap+load and bswap+store ops for all ppc64 variants.
   271  		// That is the sense we're using them here - they are only used
   272  		// in contexts where they can be merged with a load or store.
   273  		c.haveBswap64 = true
   274  		c.haveBswap32 = true
   275  		c.haveBswap16 = true
   276  		c.haveCondSelect = true
   277  	case "mips64":
   278  		c.BigEndian = true
   279  		fallthrough
   280  	case "mips64le":
   281  		c.PtrSize = 8
   282  		c.RegSize = 8
   283  		c.lowerBlock = rewriteBlockMIPS64
   284  		c.lowerValue = rewriteValueMIPS64
   285  		c.lateLowerBlock = rewriteBlockMIPS64latelower
   286  		c.lateLowerValue = rewriteValueMIPS64latelower
   287  		c.registers = registersMIPS64[:]
   288  		c.gpRegMask = gpRegMaskMIPS64
   289  		c.fpRegMask = fpRegMaskMIPS64
   290  		c.specialRegMask = specialRegMaskMIPS64
   291  		c.FPReg = framepointerRegMIPS64
   292  		c.LinkReg = linkRegMIPS64
   293  		c.hasGReg = true
   294  	case "loong64":
   295  		c.PtrSize = 8
   296  		c.RegSize = 8
   297  		c.lowerBlock = rewriteBlockLOONG64
   298  		c.lowerValue = rewriteValueLOONG64
   299  		c.lateLowerBlock = rewriteBlockLOONG64latelower
   300  		c.lateLowerValue = rewriteValueLOONG64latelower
   301  		c.registers = registersLOONG64[:]
   302  		c.gpRegMask = gpRegMaskLOONG64
   303  		c.fpRegMask = fpRegMaskLOONG64
   304  		c.intParamRegs = paramIntRegLOONG64
   305  		c.floatParamRegs = paramFloatRegLOONG64
   306  		c.FPReg = framepointerRegLOONG64
   307  		c.LinkReg = linkRegLOONG64
   308  		c.hasGReg = true
   309  		c.unalignedOK = true
   310  		c.haveBswap64 = true
   311  		c.haveBswap32 = true
   312  		c.haveBswap16 = true
   313  		c.haveCondSelect = true
   314  	case "s390x":
   315  		c.PtrSize = 8
   316  		c.RegSize = 8
   317  		c.lowerBlock = rewriteBlockS390X
   318  		c.lowerValue = rewriteValueS390X
   319  		c.registers = registersS390X[:]
   320  		c.gpRegMask = gpRegMaskS390X
   321  		c.fpRegMask = fpRegMaskS390X
   322  		c.intParamRegs = paramIntRegS390X
   323  		c.floatParamRegs = paramFloatRegS390X
   324  		c.FPReg = framepointerRegS390X
   325  		c.LinkReg = linkRegS390X
   326  		c.hasGReg = true
   327  		c.BigEndian = true
   328  		c.unalignedOK = true
   329  		c.haveBswap64 = true
   330  		c.haveBswap32 = true
   331  		c.haveBswap16 = true // only for loads&stores, see ppc64 comment
   332  	case "mips":
   333  		c.BigEndian = true
   334  		fallthrough
   335  	case "mipsle":
   336  		c.PtrSize = 4
   337  		c.RegSize = 4
   338  		c.lowerBlock = rewriteBlockMIPS
   339  		c.lowerValue = rewriteValueMIPS
   340  		c.registers = registersMIPS[:]
   341  		c.gpRegMask = gpRegMaskMIPS
   342  		c.fpRegMask = fpRegMaskMIPS
   343  		c.specialRegMask = specialRegMaskMIPS
   344  		c.FPReg = framepointerRegMIPS
   345  		c.LinkReg = linkRegMIPS
   346  		c.hasGReg = true
   347  	case "riscv64":
   348  		c.PtrSize = 8
   349  		c.RegSize = 8
   350  		c.lowerBlock = rewriteBlockRISCV64
   351  		c.lowerValue = rewriteValueRISCV64
   352  		c.lateLowerBlock = rewriteBlockRISCV64latelower
   353  		c.lateLowerValue = rewriteValueRISCV64latelower
   354  		c.registers = registersRISCV64[:]
   355  		c.gpRegMask = gpRegMaskRISCV64
   356  		c.fpRegMask = fpRegMaskRISCV64
   357  		c.intParamRegs = paramIntRegRISCV64
   358  		c.floatParamRegs = paramFloatRegRISCV64
   359  		c.FPReg = framepointerRegRISCV64
   360  		c.hasGReg = true
   361  	case "wasm":
   362  		c.PtrSize = 8
   363  		c.RegSize = 8
   364  		c.lowerBlock = rewriteBlockWasm
   365  		c.lowerValue = rewriteValueWasm
   366  		c.registers = registersWasm[:]
   367  		c.gpRegMask = gpRegMaskWasm
   368  		c.fpRegMask = fpRegMaskWasm
   369  		c.fp32RegMask = fp32RegMaskWasm
   370  		c.fp64RegMask = fp64RegMaskWasm
   371  		c.simdRegMask = simdRegMaskWasm
   372  		c.FPReg = framepointerRegWasm
   373  		c.LinkReg = linkRegWasm
   374  		c.hasGReg = true
   375  		c.unalignedOK = true
   376  		c.haveCondSelect = true
   377  	default:
   378  		ctxt.Diag("arch %s not implemented", arch)
   379  	}
   380  	c.ctxt = ctxt
   381  	c.optimize = optimize
   382  	c.SoftFloat = softfloat
   383  	if softfloat {
   384  		c.floatParamRegs = nil // no FP registers in softfloat mode
   385  	}
   386  
   387  	c.ABI0 = abi.NewABIConfig(0, 0, ctxt.Arch.FixedFrameSize, 0)
   388  	c.ABI1 = abi.NewABIConfig(len(c.intParamRegs), len(c.floatParamRegs), ctxt.Arch.FixedFrameSize, 1)
   389  
   390  	if ctxt.Flag_shared {
   391  		// LoweredWB is secretly a CALL and CALLs on 386 in
   392  		// shared mode get rewritten by obj6.go to go through
   393  		// the GOT, which clobbers BX.
   394  		opcodeTable[Op386LoweredWB].reg.clobbers = opcodeTable[Op386LoweredWB].reg.clobbers.addReg(3) // BX
   395  	}
   396  
   397  	c.buildRecipes(arch)
   398  
   399  	return c
   400  }
   401  
   402  func (c *Config) Ctxt() *obj.Link { return c.ctxt }
   403  
   404  func (c *Config) haveByteSwap(size int64) bool {
   405  	switch size {
   406  	case 8:
   407  		return c.haveBswap64
   408  	case 4:
   409  		return c.haveBswap32
   410  	case 2:
   411  		return c.haveBswap16
   412  	default:
   413  		base.Fatalf("bad size %d\n", size)
   414  		return false
   415  	}
   416  }
   417  
   418  func (c *Config) buildRecipes(arch string) {
   419  	// Information for strength-reducing multiplies.
   420  	type linearCombo struct {
   421  		// we can compute a*x+b*y in one instruction
   422  		a, b int64
   423  		// cost, in arbitrary units (tenths of cycles, usually)
   424  		cost int
   425  		// builds SSA value for a*x+b*y. Use the position
   426  		// information from m.
   427  		build func(m, x, y *Value) *Value
   428  	}
   429  
   430  	// List all the linear combination instructions we have.
   431  	var linearCombos []linearCombo
   432  	r := func(a, b int64, cost int, build func(m, x, y *Value) *Value) {
   433  		linearCombos = append(linearCombos, linearCombo{a: a, b: b, cost: cost, build: build})
   434  	}
   435  	var mulCost int
   436  	switch arch {
   437  	case "amd64":
   438  		// Assumes that the following costs from https://gmplib.org/~tege/x86-timing.pdf:
   439  		//    1 - addq, shlq, leaq, negq, subq
   440  		//    3 - imulq
   441  		// These costs limit the rewrites to two instructions.
   442  		// Operations which have to happen in place (and thus
   443  		// may require a reg-reg move) score slightly higher.
   444  		mulCost = 30
   445  		// add
   446  		r(1, 1, 10,
   447  			func(m, x, y *Value) *Value {
   448  				v := m.Block.NewValue2(m.Pos, OpAMD64ADDQ, m.Type, x, y)
   449  				if m.Type.Size() == 4 {
   450  					v.Op = OpAMD64ADDL
   451  				}
   452  				return v
   453  			})
   454  		// neg
   455  		r(-1, 0, 11,
   456  			func(m, x, y *Value) *Value {
   457  				v := m.Block.NewValue1(m.Pos, OpAMD64NEGQ, m.Type, x)
   458  				if m.Type.Size() == 4 {
   459  					v.Op = OpAMD64NEGL
   460  				}
   461  				return v
   462  			})
   463  		// sub
   464  		r(1, -1, 11,
   465  			func(m, x, y *Value) *Value {
   466  				v := m.Block.NewValue2(m.Pos, OpAMD64SUBQ, m.Type, x, y)
   467  				if m.Type.Size() == 4 {
   468  					v.Op = OpAMD64SUBL
   469  				}
   470  				return v
   471  			})
   472  		// lea
   473  		r(1, 2, 10,
   474  			func(m, x, y *Value) *Value {
   475  				v := m.Block.NewValue2(m.Pos, OpAMD64LEAQ2, m.Type, x, y)
   476  				if m.Type.Size() == 4 {
   477  					v.Op = OpAMD64LEAL2
   478  				}
   479  				return v
   480  			})
   481  		r(1, 4, 10,
   482  			func(m, x, y *Value) *Value {
   483  				v := m.Block.NewValue2(m.Pos, OpAMD64LEAQ4, m.Type, x, y)
   484  				if m.Type.Size() == 4 {
   485  					v.Op = OpAMD64LEAL4
   486  				}
   487  				return v
   488  			})
   489  		r(1, 8, 10,
   490  			func(m, x, y *Value) *Value {
   491  				v := m.Block.NewValue2(m.Pos, OpAMD64LEAQ8, m.Type, x, y)
   492  				if m.Type.Size() == 4 {
   493  					v.Op = OpAMD64LEAL8
   494  				}
   495  				return v
   496  			})
   497  		// regular shifts
   498  		for i := 2; i < 64; i++ {
   499  			r(1<<i, 0, 11,
   500  				func(m, x, y *Value) *Value {
   501  					v := m.Block.NewValue1I(m.Pos, OpAMD64SHLQconst, m.Type, int64(i), x)
   502  					if m.Type.Size() == 4 {
   503  						v.Op = OpAMD64SHLLconst
   504  					}
   505  					return v
   506  				})
   507  		}
   508  
   509  	case "arm64":
   510  		// Rationale (for M2 ultra):
   511  		// - multiply is 3 cycles.
   512  		// - add/neg/sub/shift are 1 cycle.
   513  		// - add/neg/sub+shiftLL are 2 cycles.
   514  		// We break ties against the multiply because using a
   515  		// multiply also needs to load the constant into a register.
   516  		// (It's 3 cycles and 2 instructions either way, but the
   517  		// linear combo one might use 1 less register.)
   518  		// The multiply constant might get lifted out of a loop though. Hmm....
   519  		// Other arm64 chips have different tradeoffs.
   520  		// Some chip's add+shift instructions are 1 cycle for shifts up to 4
   521  		// and 2 cycles for shifts bigger than 4. So weight the larger shifts
   522  		// a bit more.
   523  		// TODO: figure out a happy medium.
   524  		mulCost = 35
   525  		// add
   526  		r(1, 1, 10,
   527  			func(m, x, y *Value) *Value {
   528  				return m.Block.NewValue2(m.Pos, OpARM64ADD, m.Type, x, y)
   529  			})
   530  		// neg
   531  		r(-1, 0, 10,
   532  			func(m, x, y *Value) *Value {
   533  				return m.Block.NewValue1(m.Pos, OpARM64NEG, m.Type, x)
   534  			})
   535  		// sub
   536  		r(1, -1, 10,
   537  			func(m, x, y *Value) *Value {
   538  				return m.Block.NewValue2(m.Pos, OpARM64SUB, m.Type, x, y)
   539  			})
   540  		// regular shifts
   541  		for i := 1; i < 64; i++ {
   542  			c := 10
   543  			if i == 1 {
   544  				// Prefer x<<1 over x+x.
   545  				// Note that we eventually reverse this decision in ARM64latelower.rules,
   546  				// but this makes shift combining rules in ARM64.rules simpler.
   547  				c--
   548  			}
   549  			r(1<<i, 0, c,
   550  				func(m, x, y *Value) *Value {
   551  					return m.Block.NewValue1I(m.Pos, OpARM64SLLconst, m.Type, int64(i), x)
   552  				})
   553  		}
   554  		// ADDshiftLL
   555  		for i := 1; i < 64; i++ {
   556  			c := 20
   557  			if i > 4 {
   558  				c++
   559  			}
   560  			r(1, 1<<i, c,
   561  				func(m, x, y *Value) *Value {
   562  					return m.Block.NewValue2I(m.Pos, OpARM64ADDshiftLL, m.Type, int64(i), x, y)
   563  				})
   564  		}
   565  		// NEGshiftLL
   566  		for i := 1; i < 64; i++ {
   567  			c := 20
   568  			if i > 4 {
   569  				c++
   570  			}
   571  			r(-1<<i, 0, c,
   572  				func(m, x, y *Value) *Value {
   573  					return m.Block.NewValue1I(m.Pos, OpARM64NEGshiftLL, m.Type, int64(i), x)
   574  				})
   575  		}
   576  		// SUBshiftLL
   577  		for i := 1; i < 64; i++ {
   578  			c := 20
   579  			if i > 4 {
   580  				c++
   581  			}
   582  			r(1, -1<<i, c,
   583  				func(m, x, y *Value) *Value {
   584  					return m.Block.NewValue2I(m.Pos, OpARM64SUBshiftLL, m.Type, int64(i), x, y)
   585  				})
   586  		}
   587  	case "loong64":
   588  		// - multiply is 4 cycles.
   589  		// - add/sub/shift/alsl are 1 cycle.
   590  		// On loong64, using a multiply also needs to load the constant into a register.
   591  		// TODO: figure out a happy medium.
   592  		mulCost = 45
   593  
   594  		// add
   595  		r(1, 1, 10,
   596  			func(m, x, y *Value) *Value {
   597  				return m.Block.NewValue2(m.Pos, OpLOONG64ADDV, m.Type, x, y)
   598  			})
   599  		// neg
   600  		r(-1, 0, 10,
   601  			func(m, x, y *Value) *Value {
   602  				return m.Block.NewValue1(m.Pos, OpLOONG64NEGV, m.Type, x)
   603  			})
   604  		// sub
   605  		r(1, -1, 10,
   606  			func(m, x, y *Value) *Value {
   607  				return m.Block.NewValue2(m.Pos, OpLOONG64SUBV, m.Type, x, y)
   608  			})
   609  
   610  		// regular shifts
   611  		for i := 1; i < 64; i++ {
   612  			c := 10
   613  			if i == 1 {
   614  				// Prefer x<<1 over x+x.
   615  				// Note that we eventually reverse this decision in LOONG64latelower.rules,
   616  				// but this makes shift combining rules in LOONG64.rules simpler.
   617  				c--
   618  			}
   619  			r(1<<i, 0, c,
   620  				func(m, x, y *Value) *Value {
   621  					return m.Block.NewValue1I(m.Pos, OpLOONG64SLLVconst, m.Type, int64(i), x)
   622  				})
   623  		}
   624  
   625  		// ADDshiftLLV
   626  		for i := 1; i < 5; i++ {
   627  			c := 10
   628  			r(1, 1<<i, c,
   629  				func(m, x, y *Value) *Value {
   630  					return m.Block.NewValue2I(m.Pos, OpLOONG64ADDshiftLLV, m.Type, int64(i), x, y)
   631  				})
   632  		}
   633  	}
   634  
   635  	c.mulRecipes = map[int64]mulRecipe{}
   636  
   637  	// Single-instruction recipes.
   638  	// The only option for the input value(s) is v.
   639  	for _, combo := range linearCombos {
   640  		x := combo.a + combo.b
   641  		cost := combo.cost
   642  		old := c.mulRecipes[x]
   643  		if (old.build == nil || cost < old.cost) && cost < mulCost {
   644  			c.mulRecipes[x] = mulRecipe{cost: cost, build: func(m, v *Value) *Value {
   645  				return combo.build(m, v, v)
   646  			}}
   647  		}
   648  	}
   649  	// Two-instruction recipes.
   650  	// A: Both of the outer's inputs are from the same single-instruction recipe.
   651  	// B: First input is v and the second is from a single-instruction recipe.
   652  	// C: Second input is v and the first is from a single-instruction recipe.
   653  	// A is slightly preferred because it often needs 1 less register, so it
   654  	// goes first.
   655  
   656  	// A
   657  	for _, inner := range linearCombos {
   658  		for _, outer := range linearCombos {
   659  			x := (inner.a + inner.b) * (outer.a + outer.b)
   660  			cost := inner.cost + outer.cost
   661  			old := c.mulRecipes[x]
   662  			if (old.build == nil || cost < old.cost) && cost < mulCost {
   663  				c.mulRecipes[x] = mulRecipe{cost: cost, build: func(m, v *Value) *Value {
   664  					v = inner.build(m, v, v)
   665  					return outer.build(m, v, v)
   666  				}}
   667  			}
   668  		}
   669  	}
   670  
   671  	// B
   672  	for _, inner := range linearCombos {
   673  		for _, outer := range linearCombos {
   674  			x := outer.a + outer.b*(inner.a+inner.b)
   675  			cost := inner.cost + outer.cost
   676  			old := c.mulRecipes[x]
   677  			if (old.build == nil || cost < old.cost) && cost < mulCost {
   678  				c.mulRecipes[x] = mulRecipe{cost: cost, build: func(m, v *Value) *Value {
   679  					return outer.build(m, v, inner.build(m, v, v))
   680  				}}
   681  			}
   682  		}
   683  	}
   684  
   685  	// C
   686  	for _, inner := range linearCombos {
   687  		for _, outer := range linearCombos {
   688  			x := outer.a*(inner.a+inner.b) + outer.b
   689  			cost := inner.cost + outer.cost
   690  			old := c.mulRecipes[x]
   691  			if (old.build == nil || cost < old.cost) && cost < mulCost {
   692  				c.mulRecipes[x] = mulRecipe{cost: cost, build: func(m, v *Value) *Value {
   693  					return outer.build(m, inner.build(m, v, v), v)
   694  				}}
   695  			}
   696  		}
   697  	}
   698  
   699  	// Currently we only process 3 linear combination instructions for loong64.
   700  	if arch == "loong64" {
   701  		// Three-instruction recipes.
   702  		// D: The first and the second are all single-instruction recipes, and they are also the third's inputs.
   703  		// E: The first single-instruction is the second's input, and the second is the third's input.
   704  
   705  		// D
   706  		for _, first := range linearCombos {
   707  			for _, second := range linearCombos {
   708  				for _, third := range linearCombos {
   709  					x := third.a*(first.a+first.b) + third.b*(second.a+second.b)
   710  					cost := first.cost + second.cost + third.cost
   711  					old := c.mulRecipes[x]
   712  					if (old.build == nil || cost < old.cost) && cost < mulCost {
   713  						c.mulRecipes[x] = mulRecipe{cost: cost, build: func(m, v *Value) *Value {
   714  							v1 := first.build(m, v, v)
   715  							v2 := second.build(m, v, v)
   716  							return third.build(m, v1, v2)
   717  						}}
   718  					}
   719  				}
   720  			}
   721  		}
   722  
   723  		// E
   724  		for _, first := range linearCombos {
   725  			for _, second := range linearCombos {
   726  				for _, third := range linearCombos {
   727  					x := third.a*(second.a*(first.a+first.b)+second.b) + third.b
   728  					cost := first.cost + second.cost + third.cost
   729  					old := c.mulRecipes[x]
   730  					if (old.build == nil || cost < old.cost) && cost < mulCost {
   731  						c.mulRecipes[x] = mulRecipe{cost: cost, build: func(m, v *Value) *Value {
   732  							v1 := first.build(m, v, v)
   733  							v2 := second.build(m, v1, v)
   734  							return third.build(m, v2, v)
   735  						}}
   736  					}
   737  				}
   738  			}
   739  		}
   740  	}
   741  
   742  	// These cases should be handled specially by rewrite rules.
   743  	// (Otherwise v * 1 == (neg (neg v)))
   744  	delete(c.mulRecipes, 0)
   745  	delete(c.mulRecipes, 1)
   746  
   747  	// Currently:
   748  	// len(c.mulRecipes) == 5984 on arm64
   749  	//                       680 on amd64
   750  	//                      9738 on loong64
   751  	// This function takes ~2.5ms on arm64.
   752  	//println(len(c.mulRecipes))
   753  }
   754  

View as plain text