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

     1  // Copyright 2016 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 arm
     6  
     7  import (
     8  	"fmt"
     9  	"math"
    10  	"math/bits"
    11  
    12  	"github.com/go-asm/go/buildcfg"
    13  
    14  	"github.com/go-asm/go/cmd/compile/base"
    15  	"github.com/go-asm/go/cmd/compile/ir"
    16  	"github.com/go-asm/go/cmd/compile/logopt"
    17  	"github.com/go-asm/go/cmd/compile/ssa"
    18  	"github.com/go-asm/go/cmd/compile/ssagen"
    19  	"github.com/go-asm/go/cmd/compile/types"
    20  	"github.com/go-asm/go/cmd/obj"
    21  	"github.com/go-asm/go/cmd/obj/arm"
    22  )
    23  
    24  // loadByType returns the load instruction of the given type.
    25  func loadByType(t *types.Type) obj.As {
    26  	if t.IsFloat() {
    27  		switch t.Size() {
    28  		case 4:
    29  			return arm.AMOVF
    30  		case 8:
    31  			return arm.AMOVD
    32  		}
    33  	} else {
    34  		switch t.Size() {
    35  		case 1:
    36  			if t.IsSigned() {
    37  				return arm.AMOVB
    38  			} else {
    39  				return arm.AMOVBU
    40  			}
    41  		case 2:
    42  			if t.IsSigned() {
    43  				return arm.AMOVH
    44  			} else {
    45  				return arm.AMOVHU
    46  			}
    47  		case 4:
    48  			return arm.AMOVW
    49  		}
    50  	}
    51  	panic("bad load type")
    52  }
    53  
    54  // storeByType returns the store instruction of the given type.
    55  func storeByType(t *types.Type) obj.As {
    56  	if t.IsFloat() {
    57  		switch t.Size() {
    58  		case 4:
    59  			return arm.AMOVF
    60  		case 8:
    61  			return arm.AMOVD
    62  		}
    63  	} else {
    64  		switch t.Size() {
    65  		case 1:
    66  			return arm.AMOVB
    67  		case 2:
    68  			return arm.AMOVH
    69  		case 4:
    70  			return arm.AMOVW
    71  		}
    72  	}
    73  	panic("bad store type")
    74  }
    75  
    76  // shift type is used as Offset in obj.TYPE_SHIFT operands to encode shifted register operands.
    77  type shift int64
    78  
    79  // copied from ../../../github.com/go-asm/go/obj/util.go:/TYPE_SHIFT
    80  func (v shift) String() string {
    81  	op := "<<>>->@>"[((v>>5)&3)<<1:]
    82  	if v&(1<<4) != 0 {
    83  		// register shift
    84  		return fmt.Sprintf("R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15)
    85  	} else {
    86  		// constant shift
    87  		return fmt.Sprintf("R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31)
    88  	}
    89  }
    90  
    91  // makeshift encodes a register shifted by a constant.
    92  func makeshift(v *ssa.Value, reg int16, typ int64, s int64) shift {
    93  	if s < 0 || s >= 32 {
    94  		v.Fatalf("shift out of range: %d", s)
    95  	}
    96  	return shift(int64(reg&0xf) | typ | (s&31)<<7)
    97  }
    98  
    99  // genshift generates a Prog for r = r0 op (r1 shifted by n).
   100  func genshift(s *ssagen.State, v *ssa.Value, as obj.As, r0, r1, r int16, typ int64, n int64) *obj.Prog {
   101  	p := s.Prog(as)
   102  	p.From.Type = obj.TYPE_SHIFT
   103  	p.From.Offset = int64(makeshift(v, r1, typ, n))
   104  	p.Reg = r0
   105  	if r != 0 {
   106  		p.To.Type = obj.TYPE_REG
   107  		p.To.Reg = r
   108  	}
   109  	return p
   110  }
   111  
   112  // makeregshift encodes a register shifted by a register.
   113  func makeregshift(r1 int16, typ int64, r2 int16) shift {
   114  	return shift(int64(r1&0xf) | typ | int64(r2&0xf)<<8 | 1<<4)
   115  }
   116  
   117  // genregshift generates a Prog for r = r0 op (r1 shifted by r2).
   118  func genregshift(s *ssagen.State, as obj.As, r0, r1, r2, r int16, typ int64) *obj.Prog {
   119  	p := s.Prog(as)
   120  	p.From.Type = obj.TYPE_SHIFT
   121  	p.From.Offset = int64(makeregshift(r1, typ, r2))
   122  	p.Reg = r0
   123  	if r != 0 {
   124  		p.To.Type = obj.TYPE_REG
   125  		p.To.Reg = r
   126  	}
   127  	return p
   128  }
   129  
   130  // find a (lsb, width) pair for BFC
   131  // lsb must be in [0, 31], width must be in [1, 32 - lsb]
   132  // return (0xffffffff, 0) if v is not a binary like 0...01...10...0
   133  func getBFC(v uint32) (uint32, uint32) {
   134  	var m, l uint32
   135  	// BFC is not applicable with zero
   136  	if v == 0 {
   137  		return 0xffffffff, 0
   138  	}
   139  	// find the lowest set bit, for example l=2 for 0x3ffffffc
   140  	l = uint32(bits.TrailingZeros32(v))
   141  	// m-1 represents the highest set bit index, for example m=30 for 0x3ffffffc
   142  	m = 32 - uint32(bits.LeadingZeros32(v))
   143  	// check if v is a binary like 0...01...10...0
   144  	if (1<<m)-(1<<l) == v {
   145  		// it must be m > l for non-zero v
   146  		return l, m - l
   147  	}
   148  	// invalid
   149  	return 0xffffffff, 0
   150  }
   151  
   152  func ssaGenValue(s *ssagen.State, v *ssa.Value) {
   153  	switch v.Op {
   154  	case ssa.OpCopy, ssa.OpARMMOVWreg:
   155  		if v.Type.IsMemory() {
   156  			return
   157  		}
   158  		x := v.Args[0].Reg()
   159  		y := v.Reg()
   160  		if x == y {
   161  			return
   162  		}
   163  		as := arm.AMOVW
   164  		if v.Type.IsFloat() {
   165  			switch v.Type.Size() {
   166  			case 4:
   167  				as = arm.AMOVF
   168  			case 8:
   169  				as = arm.AMOVD
   170  			default:
   171  				panic("bad float size")
   172  			}
   173  		}
   174  		p := s.Prog(as)
   175  		p.From.Type = obj.TYPE_REG
   176  		p.From.Reg = x
   177  		p.To.Type = obj.TYPE_REG
   178  		p.To.Reg = y
   179  	case ssa.OpARMMOVWnop:
   180  		// nothing to do
   181  	case ssa.OpLoadReg:
   182  		if v.Type.IsFlags() {
   183  			v.Fatalf("load flags not implemented: %v", v.LongString())
   184  			return
   185  		}
   186  		p := s.Prog(loadByType(v.Type))
   187  		ssagen.AddrAuto(&p.From, v.Args[0])
   188  		p.To.Type = obj.TYPE_REG
   189  		p.To.Reg = v.Reg()
   190  	case ssa.OpStoreReg:
   191  		if v.Type.IsFlags() {
   192  			v.Fatalf("store flags not implemented: %v", v.LongString())
   193  			return
   194  		}
   195  		p := s.Prog(storeByType(v.Type))
   196  		p.From.Type = obj.TYPE_REG
   197  		p.From.Reg = v.Args[0].Reg()
   198  		ssagen.AddrAuto(&p.To, v)
   199  	case ssa.OpARMADD,
   200  		ssa.OpARMADC,
   201  		ssa.OpARMSUB,
   202  		ssa.OpARMSBC,
   203  		ssa.OpARMRSB,
   204  		ssa.OpARMAND,
   205  		ssa.OpARMOR,
   206  		ssa.OpARMXOR,
   207  		ssa.OpARMBIC,
   208  		ssa.OpARMMUL,
   209  		ssa.OpARMADDF,
   210  		ssa.OpARMADDD,
   211  		ssa.OpARMSUBF,
   212  		ssa.OpARMSUBD,
   213  		ssa.OpARMSLL,
   214  		ssa.OpARMSRL,
   215  		ssa.OpARMSRA,
   216  		ssa.OpARMMULF,
   217  		ssa.OpARMMULD,
   218  		ssa.OpARMNMULF,
   219  		ssa.OpARMNMULD,
   220  		ssa.OpARMDIVF,
   221  		ssa.OpARMDIVD:
   222  		r := v.Reg()
   223  		r1 := v.Args[0].Reg()
   224  		r2 := v.Args[1].Reg()
   225  		p := s.Prog(v.Op.Asm())
   226  		p.From.Type = obj.TYPE_REG
   227  		p.From.Reg = r2
   228  		p.Reg = r1
   229  		p.To.Type = obj.TYPE_REG
   230  		p.To.Reg = r
   231  	case ssa.OpARMSRR:
   232  		genregshift(s, arm.AMOVW, 0, v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_RR)
   233  	case ssa.OpARMMULAF, ssa.OpARMMULAD, ssa.OpARMMULSF, ssa.OpARMMULSD, ssa.OpARMFMULAD:
   234  		r := v.Reg()
   235  		r0 := v.Args[0].Reg()
   236  		r1 := v.Args[1].Reg()
   237  		r2 := v.Args[2].Reg()
   238  		if r != r0 {
   239  			v.Fatalf("result and addend are not in the same register: %v", v.LongString())
   240  		}
   241  		p := s.Prog(v.Op.Asm())
   242  		p.From.Type = obj.TYPE_REG
   243  		p.From.Reg = r2
   244  		p.Reg = r1
   245  		p.To.Type = obj.TYPE_REG
   246  		p.To.Reg = r
   247  	case ssa.OpARMADDS,
   248  		ssa.OpARMSUBS:
   249  		r := v.Reg0()
   250  		r1 := v.Args[0].Reg()
   251  		r2 := v.Args[1].Reg()
   252  		p := s.Prog(v.Op.Asm())
   253  		p.Scond = arm.C_SBIT
   254  		p.From.Type = obj.TYPE_REG
   255  		p.From.Reg = r2
   256  		p.Reg = r1
   257  		p.To.Type = obj.TYPE_REG
   258  		p.To.Reg = r
   259  	case ssa.OpARMSRAcond:
   260  		// ARM shift instructions uses only the low-order byte of the shift amount
   261  		// generate conditional instructions to deal with large shifts
   262  		// flag is already set
   263  		// SRA.HS	$31, Rarg0, Rdst // shift 31 bits to get the sign bit
   264  		// SRA.LO	Rarg1, Rarg0, Rdst
   265  		r := v.Reg()
   266  		r1 := v.Args[0].Reg()
   267  		r2 := v.Args[1].Reg()
   268  		p := s.Prog(arm.ASRA)
   269  		p.Scond = arm.C_SCOND_HS
   270  		p.From.Type = obj.TYPE_CONST
   271  		p.From.Offset = 31
   272  		p.Reg = r1
   273  		p.To.Type = obj.TYPE_REG
   274  		p.To.Reg = r
   275  		p = s.Prog(arm.ASRA)
   276  		p.Scond = arm.C_SCOND_LO
   277  		p.From.Type = obj.TYPE_REG
   278  		p.From.Reg = r2
   279  		p.Reg = r1
   280  		p.To.Type = obj.TYPE_REG
   281  		p.To.Reg = r
   282  	case ssa.OpARMBFX, ssa.OpARMBFXU:
   283  		p := s.Prog(v.Op.Asm())
   284  		p.From.Type = obj.TYPE_CONST
   285  		p.From.Offset = v.AuxInt >> 8
   286  		p.AddRestSourceConst(v.AuxInt & 0xff)
   287  		p.Reg = v.Args[0].Reg()
   288  		p.To.Type = obj.TYPE_REG
   289  		p.To.Reg = v.Reg()
   290  	case ssa.OpARMANDconst, ssa.OpARMBICconst:
   291  		// try to optimize ANDconst and BICconst to BFC, which saves bytes and ticks
   292  		// BFC is only available on ARMv7, and its result and source are in the same register
   293  		if buildcfg.GOARM.Version == 7 && v.Reg() == v.Args[0].Reg() {
   294  			var val uint32
   295  			if v.Op == ssa.OpARMANDconst {
   296  				val = ^uint32(v.AuxInt)
   297  			} else { // BICconst
   298  				val = uint32(v.AuxInt)
   299  			}
   300  			lsb, width := getBFC(val)
   301  			// omit BFC for ARM's imm12
   302  			if 8 < width && width < 24 {
   303  				p := s.Prog(arm.ABFC)
   304  				p.From.Type = obj.TYPE_CONST
   305  				p.From.Offset = int64(width)
   306  				p.AddRestSourceConst(int64(lsb))
   307  				p.To.Type = obj.TYPE_REG
   308  				p.To.Reg = v.Reg()
   309  				break
   310  			}
   311  		}
   312  		// fall back to ordinary form
   313  		fallthrough
   314  	case ssa.OpARMADDconst,
   315  		ssa.OpARMADCconst,
   316  		ssa.OpARMSUBconst,
   317  		ssa.OpARMSBCconst,
   318  		ssa.OpARMRSBconst,
   319  		ssa.OpARMRSCconst,
   320  		ssa.OpARMORconst,
   321  		ssa.OpARMXORconst,
   322  		ssa.OpARMSLLconst,
   323  		ssa.OpARMSRLconst,
   324  		ssa.OpARMSRAconst:
   325  		p := s.Prog(v.Op.Asm())
   326  		p.From.Type = obj.TYPE_CONST
   327  		p.From.Offset = v.AuxInt
   328  		p.Reg = v.Args[0].Reg()
   329  		p.To.Type = obj.TYPE_REG
   330  		p.To.Reg = v.Reg()
   331  	case ssa.OpARMADDSconst,
   332  		ssa.OpARMSUBSconst,
   333  		ssa.OpARMRSBSconst:
   334  		p := s.Prog(v.Op.Asm())
   335  		p.Scond = arm.C_SBIT
   336  		p.From.Type = obj.TYPE_CONST
   337  		p.From.Offset = v.AuxInt
   338  		p.Reg = v.Args[0].Reg()
   339  		p.To.Type = obj.TYPE_REG
   340  		p.To.Reg = v.Reg0()
   341  	case ssa.OpARMSRRconst:
   342  		genshift(s, v, arm.AMOVW, 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_RR, v.AuxInt)
   343  	case ssa.OpARMADDshiftLL,
   344  		ssa.OpARMADCshiftLL,
   345  		ssa.OpARMSUBshiftLL,
   346  		ssa.OpARMSBCshiftLL,
   347  		ssa.OpARMRSBshiftLL,
   348  		ssa.OpARMRSCshiftLL,
   349  		ssa.OpARMANDshiftLL,
   350  		ssa.OpARMORshiftLL,
   351  		ssa.OpARMXORshiftLL,
   352  		ssa.OpARMBICshiftLL:
   353  		genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LL, v.AuxInt)
   354  	case ssa.OpARMADDSshiftLL,
   355  		ssa.OpARMSUBSshiftLL,
   356  		ssa.OpARMRSBSshiftLL:
   357  		p := genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg0(), arm.SHIFT_LL, v.AuxInt)
   358  		p.Scond = arm.C_SBIT
   359  	case ssa.OpARMADDshiftRL,
   360  		ssa.OpARMADCshiftRL,
   361  		ssa.OpARMSUBshiftRL,
   362  		ssa.OpARMSBCshiftRL,
   363  		ssa.OpARMRSBshiftRL,
   364  		ssa.OpARMRSCshiftRL,
   365  		ssa.OpARMANDshiftRL,
   366  		ssa.OpARMORshiftRL,
   367  		ssa.OpARMXORshiftRL,
   368  		ssa.OpARMBICshiftRL:
   369  		genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LR, v.AuxInt)
   370  	case ssa.OpARMADDSshiftRL,
   371  		ssa.OpARMSUBSshiftRL,
   372  		ssa.OpARMRSBSshiftRL:
   373  		p := genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg0(), arm.SHIFT_LR, v.AuxInt)
   374  		p.Scond = arm.C_SBIT
   375  	case ssa.OpARMADDshiftRA,
   376  		ssa.OpARMADCshiftRA,
   377  		ssa.OpARMSUBshiftRA,
   378  		ssa.OpARMSBCshiftRA,
   379  		ssa.OpARMRSBshiftRA,
   380  		ssa.OpARMRSCshiftRA,
   381  		ssa.OpARMANDshiftRA,
   382  		ssa.OpARMORshiftRA,
   383  		ssa.OpARMXORshiftRA,
   384  		ssa.OpARMBICshiftRA:
   385  		genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_AR, v.AuxInt)
   386  	case ssa.OpARMADDSshiftRA,
   387  		ssa.OpARMSUBSshiftRA,
   388  		ssa.OpARMRSBSshiftRA:
   389  		p := genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg0(), arm.SHIFT_AR, v.AuxInt)
   390  		p.Scond = arm.C_SBIT
   391  	case ssa.OpARMXORshiftRR:
   392  		genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_RR, v.AuxInt)
   393  	case ssa.OpARMMVNshiftLL:
   394  		genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_LL, v.AuxInt)
   395  	case ssa.OpARMMVNshiftRL:
   396  		genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_LR, v.AuxInt)
   397  	case ssa.OpARMMVNshiftRA:
   398  		genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_AR, v.AuxInt)
   399  	case ssa.OpARMMVNshiftLLreg:
   400  		genregshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LL)
   401  	case ssa.OpARMMVNshiftRLreg:
   402  		genregshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LR)
   403  	case ssa.OpARMMVNshiftRAreg:
   404  		genregshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_AR)
   405  	case ssa.OpARMADDshiftLLreg,
   406  		ssa.OpARMADCshiftLLreg,
   407  		ssa.OpARMSUBshiftLLreg,
   408  		ssa.OpARMSBCshiftLLreg,
   409  		ssa.OpARMRSBshiftLLreg,
   410  		ssa.OpARMRSCshiftLLreg,
   411  		ssa.OpARMANDshiftLLreg,
   412  		ssa.OpARMORshiftLLreg,
   413  		ssa.OpARMXORshiftLLreg,
   414  		ssa.OpARMBICshiftLLreg:
   415  		genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), v.Reg(), arm.SHIFT_LL)
   416  	case ssa.OpARMADDSshiftLLreg,
   417  		ssa.OpARMSUBSshiftLLreg,
   418  		ssa.OpARMRSBSshiftLLreg:
   419  		p := genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), v.Reg0(), arm.SHIFT_LL)
   420  		p.Scond = arm.C_SBIT
   421  	case ssa.OpARMADDshiftRLreg,
   422  		ssa.OpARMADCshiftRLreg,
   423  		ssa.OpARMSUBshiftRLreg,
   424  		ssa.OpARMSBCshiftRLreg,
   425  		ssa.OpARMRSBshiftRLreg,
   426  		ssa.OpARMRSCshiftRLreg,
   427  		ssa.OpARMANDshiftRLreg,
   428  		ssa.OpARMORshiftRLreg,
   429  		ssa.OpARMXORshiftRLreg,
   430  		ssa.OpARMBICshiftRLreg:
   431  		genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), v.Reg(), arm.SHIFT_LR)
   432  	case ssa.OpARMADDSshiftRLreg,
   433  		ssa.OpARMSUBSshiftRLreg,
   434  		ssa.OpARMRSBSshiftRLreg:
   435  		p := genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), v.Reg0(), arm.SHIFT_LR)
   436  		p.Scond = arm.C_SBIT
   437  	case ssa.OpARMADDshiftRAreg,
   438  		ssa.OpARMADCshiftRAreg,
   439  		ssa.OpARMSUBshiftRAreg,
   440  		ssa.OpARMSBCshiftRAreg,
   441  		ssa.OpARMRSBshiftRAreg,
   442  		ssa.OpARMRSCshiftRAreg,
   443  		ssa.OpARMANDshiftRAreg,
   444  		ssa.OpARMORshiftRAreg,
   445  		ssa.OpARMXORshiftRAreg,
   446  		ssa.OpARMBICshiftRAreg:
   447  		genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), v.Reg(), arm.SHIFT_AR)
   448  	case ssa.OpARMADDSshiftRAreg,
   449  		ssa.OpARMSUBSshiftRAreg,
   450  		ssa.OpARMRSBSshiftRAreg:
   451  		p := genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), v.Reg0(), arm.SHIFT_AR)
   452  		p.Scond = arm.C_SBIT
   453  	case ssa.OpARMHMUL,
   454  		ssa.OpARMHMULU:
   455  		// 32-bit high multiplication
   456  		p := s.Prog(v.Op.Asm())
   457  		p.From.Type = obj.TYPE_REG
   458  		p.From.Reg = v.Args[0].Reg()
   459  		p.Reg = v.Args[1].Reg()
   460  		p.To.Type = obj.TYPE_REGREG
   461  		p.To.Reg = v.Reg()
   462  		p.To.Offset = arm.REGTMP // throw away low 32-bit into tmp register
   463  	case ssa.OpARMMULLU:
   464  		// 32-bit multiplication, results 64-bit, high 32-bit in out0, low 32-bit in out1
   465  		p := s.Prog(v.Op.Asm())
   466  		p.From.Type = obj.TYPE_REG
   467  		p.From.Reg = v.Args[0].Reg()
   468  		p.Reg = v.Args[1].Reg()
   469  		p.To.Type = obj.TYPE_REGREG
   470  		p.To.Reg = v.Reg0()           // high 32-bit
   471  		p.To.Offset = int64(v.Reg1()) // low 32-bit
   472  	case ssa.OpARMMULA, ssa.OpARMMULS:
   473  		p := s.Prog(v.Op.Asm())
   474  		p.From.Type = obj.TYPE_REG
   475  		p.From.Reg = v.Args[0].Reg()
   476  		p.Reg = v.Args[1].Reg()
   477  		p.To.Type = obj.TYPE_REGREG2
   478  		p.To.Reg = v.Reg()                   // result
   479  		p.To.Offset = int64(v.Args[2].Reg()) // addend
   480  	case ssa.OpARMMOVWconst:
   481  		p := s.Prog(v.Op.Asm())
   482  		p.From.Type = obj.TYPE_CONST
   483  		p.From.Offset = v.AuxInt
   484  		p.To.Type = obj.TYPE_REG
   485  		p.To.Reg = v.Reg()
   486  	case ssa.OpARMMOVFconst,
   487  		ssa.OpARMMOVDconst:
   488  		p := s.Prog(v.Op.Asm())
   489  		p.From.Type = obj.TYPE_FCONST
   490  		p.From.Val = math.Float64frombits(uint64(v.AuxInt))
   491  		p.To.Type = obj.TYPE_REG
   492  		p.To.Reg = v.Reg()
   493  	case ssa.OpARMCMP,
   494  		ssa.OpARMCMN,
   495  		ssa.OpARMTST,
   496  		ssa.OpARMTEQ,
   497  		ssa.OpARMCMPF,
   498  		ssa.OpARMCMPD:
   499  		p := s.Prog(v.Op.Asm())
   500  		p.From.Type = obj.TYPE_REG
   501  		// Special layout in ARM assembly
   502  		// Comparing to x86, the operands of ARM's CMP are reversed.
   503  		p.From.Reg = v.Args[1].Reg()
   504  		p.Reg = v.Args[0].Reg()
   505  	case ssa.OpARMCMPconst,
   506  		ssa.OpARMCMNconst,
   507  		ssa.OpARMTSTconst,
   508  		ssa.OpARMTEQconst:
   509  		// Special layout in ARM assembly
   510  		p := s.Prog(v.Op.Asm())
   511  		p.From.Type = obj.TYPE_CONST
   512  		p.From.Offset = v.AuxInt
   513  		p.Reg = v.Args[0].Reg()
   514  	case ssa.OpARMCMPF0,
   515  		ssa.OpARMCMPD0:
   516  		p := s.Prog(v.Op.Asm())
   517  		p.From.Type = obj.TYPE_REG
   518  		p.From.Reg = v.Args[0].Reg()
   519  	case ssa.OpARMCMPshiftLL, ssa.OpARMCMNshiftLL, ssa.OpARMTSTshiftLL, ssa.OpARMTEQshiftLL:
   520  		genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm.SHIFT_LL, v.AuxInt)
   521  	case ssa.OpARMCMPshiftRL, ssa.OpARMCMNshiftRL, ssa.OpARMTSTshiftRL, ssa.OpARMTEQshiftRL:
   522  		genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm.SHIFT_LR, v.AuxInt)
   523  	case ssa.OpARMCMPshiftRA, ssa.OpARMCMNshiftRA, ssa.OpARMTSTshiftRA, ssa.OpARMTEQshiftRA:
   524  		genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm.SHIFT_AR, v.AuxInt)
   525  	case ssa.OpARMCMPshiftLLreg, ssa.OpARMCMNshiftLLreg, ssa.OpARMTSTshiftLLreg, ssa.OpARMTEQshiftLLreg:
   526  		genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), 0, arm.SHIFT_LL)
   527  	case ssa.OpARMCMPshiftRLreg, ssa.OpARMCMNshiftRLreg, ssa.OpARMTSTshiftRLreg, ssa.OpARMTEQshiftRLreg:
   528  		genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), 0, arm.SHIFT_LR)
   529  	case ssa.OpARMCMPshiftRAreg, ssa.OpARMCMNshiftRAreg, ssa.OpARMTSTshiftRAreg, ssa.OpARMTEQshiftRAreg:
   530  		genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), 0, arm.SHIFT_AR)
   531  	case ssa.OpARMMOVWaddr:
   532  		p := s.Prog(arm.AMOVW)
   533  		p.From.Type = obj.TYPE_ADDR
   534  		p.From.Reg = v.Args[0].Reg()
   535  		p.To.Type = obj.TYPE_REG
   536  		p.To.Reg = v.Reg()
   537  
   538  		var wantreg string
   539  		// MOVW $sym+off(base), R
   540  		// the assembler expands it as the following:
   541  		// - base is SP: add constant offset to SP (R13)
   542  		//               when constant is large, tmp register (R11) may be used
   543  		// - base is SB: load external address from constant pool (use relocation)
   544  		switch v.Aux.(type) {
   545  		default:
   546  			v.Fatalf("aux is of unknown type %T", v.Aux)
   547  		case *obj.LSym:
   548  			wantreg = "SB"
   549  			ssagen.AddAux(&p.From, v)
   550  		case *ir.Name:
   551  			wantreg = "SP"
   552  			ssagen.AddAux(&p.From, v)
   553  		case nil:
   554  			// No sym, just MOVW $off(SP), R
   555  			wantreg = "SP"
   556  			p.From.Offset = v.AuxInt
   557  		}
   558  		if reg := v.Args[0].RegName(); reg != wantreg {
   559  			v.Fatalf("bad reg %s for symbol type %T, want %s", reg, v.Aux, wantreg)
   560  		}
   561  
   562  	case ssa.OpARMMOVBload,
   563  		ssa.OpARMMOVBUload,
   564  		ssa.OpARMMOVHload,
   565  		ssa.OpARMMOVHUload,
   566  		ssa.OpARMMOVWload,
   567  		ssa.OpARMMOVFload,
   568  		ssa.OpARMMOVDload:
   569  		p := s.Prog(v.Op.Asm())
   570  		p.From.Type = obj.TYPE_MEM
   571  		p.From.Reg = v.Args[0].Reg()
   572  		ssagen.AddAux(&p.From, v)
   573  		p.To.Type = obj.TYPE_REG
   574  		p.To.Reg = v.Reg()
   575  	case ssa.OpARMMOVBstore,
   576  		ssa.OpARMMOVHstore,
   577  		ssa.OpARMMOVWstore,
   578  		ssa.OpARMMOVFstore,
   579  		ssa.OpARMMOVDstore:
   580  		p := s.Prog(v.Op.Asm())
   581  		p.From.Type = obj.TYPE_REG
   582  		p.From.Reg = v.Args[1].Reg()
   583  		p.To.Type = obj.TYPE_MEM
   584  		p.To.Reg = v.Args[0].Reg()
   585  		ssagen.AddAux(&p.To, v)
   586  	case ssa.OpARMMOVWloadidx, ssa.OpARMMOVBUloadidx, ssa.OpARMMOVBloadidx, ssa.OpARMMOVHUloadidx, ssa.OpARMMOVHloadidx:
   587  		// this is just shift 0 bits
   588  		fallthrough
   589  	case ssa.OpARMMOVWloadshiftLL:
   590  		p := genshift(s, v, v.Op.Asm(), 0, v.Args[1].Reg(), v.Reg(), arm.SHIFT_LL, v.AuxInt)
   591  		p.From.Reg = v.Args[0].Reg()
   592  	case ssa.OpARMMOVWloadshiftRL:
   593  		p := genshift(s, v, v.Op.Asm(), 0, v.Args[1].Reg(), v.Reg(), arm.SHIFT_LR, v.AuxInt)
   594  		p.From.Reg = v.Args[0].Reg()
   595  	case ssa.OpARMMOVWloadshiftRA:
   596  		p := genshift(s, v, v.Op.Asm(), 0, v.Args[1].Reg(), v.Reg(), arm.SHIFT_AR, v.AuxInt)
   597  		p.From.Reg = v.Args[0].Reg()
   598  	case ssa.OpARMMOVWstoreidx, ssa.OpARMMOVBstoreidx, ssa.OpARMMOVHstoreidx:
   599  		// this is just shift 0 bits
   600  		fallthrough
   601  	case ssa.OpARMMOVWstoreshiftLL:
   602  		p := s.Prog(v.Op.Asm())
   603  		p.From.Type = obj.TYPE_REG
   604  		p.From.Reg = v.Args[2].Reg()
   605  		p.To.Type = obj.TYPE_SHIFT
   606  		p.To.Reg = v.Args[0].Reg()
   607  		p.To.Offset = int64(makeshift(v, v.Args[1].Reg(), arm.SHIFT_LL, v.AuxInt))
   608  	case ssa.OpARMMOVWstoreshiftRL:
   609  		p := s.Prog(v.Op.Asm())
   610  		p.From.Type = obj.TYPE_REG
   611  		p.From.Reg = v.Args[2].Reg()
   612  		p.To.Type = obj.TYPE_SHIFT
   613  		p.To.Reg = v.Args[0].Reg()
   614  		p.To.Offset = int64(makeshift(v, v.Args[1].Reg(), arm.SHIFT_LR, v.AuxInt))
   615  	case ssa.OpARMMOVWstoreshiftRA:
   616  		p := s.Prog(v.Op.Asm())
   617  		p.From.Type = obj.TYPE_REG
   618  		p.From.Reg = v.Args[2].Reg()
   619  		p.To.Type = obj.TYPE_SHIFT
   620  		p.To.Reg = v.Args[0].Reg()
   621  		p.To.Offset = int64(makeshift(v, v.Args[1].Reg(), arm.SHIFT_AR, v.AuxInt))
   622  	case ssa.OpARMMOVBreg,
   623  		ssa.OpARMMOVBUreg,
   624  		ssa.OpARMMOVHreg,
   625  		ssa.OpARMMOVHUreg:
   626  		a := v.Args[0]
   627  		for a.Op == ssa.OpCopy || a.Op == ssa.OpARMMOVWreg || a.Op == ssa.OpARMMOVWnop {
   628  			a = a.Args[0]
   629  		}
   630  		if a.Op == ssa.OpLoadReg {
   631  			t := a.Type
   632  			switch {
   633  			case v.Op == ssa.OpARMMOVBreg && t.Size() == 1 && t.IsSigned(),
   634  				v.Op == ssa.OpARMMOVBUreg && t.Size() == 1 && !t.IsSigned(),
   635  				v.Op == ssa.OpARMMOVHreg && t.Size() == 2 && t.IsSigned(),
   636  				v.Op == ssa.OpARMMOVHUreg && t.Size() == 2 && !t.IsSigned():
   637  				// arg is a proper-typed load, already zero/sign-extended, don't extend again
   638  				if v.Reg() == v.Args[0].Reg() {
   639  					return
   640  				}
   641  				p := s.Prog(arm.AMOVW)
   642  				p.From.Type = obj.TYPE_REG
   643  				p.From.Reg = v.Args[0].Reg()
   644  				p.To.Type = obj.TYPE_REG
   645  				p.To.Reg = v.Reg()
   646  				return
   647  			default:
   648  			}
   649  		}
   650  		if buildcfg.GOARM.Version >= 6 {
   651  			// generate more efficient "MOVB/MOVBU/MOVH/MOVHU Reg@>0, Reg" on ARMv6 & ARMv7
   652  			genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_RR, 0)
   653  			return
   654  		}
   655  		fallthrough
   656  	case ssa.OpARMMVN,
   657  		ssa.OpARMCLZ,
   658  		ssa.OpARMREV,
   659  		ssa.OpARMREV16,
   660  		ssa.OpARMRBIT,
   661  		ssa.OpARMSQRTF,
   662  		ssa.OpARMSQRTD,
   663  		ssa.OpARMNEGF,
   664  		ssa.OpARMNEGD,
   665  		ssa.OpARMABSD,
   666  		ssa.OpARMMOVWF,
   667  		ssa.OpARMMOVWD,
   668  		ssa.OpARMMOVFW,
   669  		ssa.OpARMMOVDW,
   670  		ssa.OpARMMOVFD,
   671  		ssa.OpARMMOVDF:
   672  		p := s.Prog(v.Op.Asm())
   673  		p.From.Type = obj.TYPE_REG
   674  		p.From.Reg = v.Args[0].Reg()
   675  		p.To.Type = obj.TYPE_REG
   676  		p.To.Reg = v.Reg()
   677  	case ssa.OpARMMOVWUF,
   678  		ssa.OpARMMOVWUD,
   679  		ssa.OpARMMOVFWU,
   680  		ssa.OpARMMOVDWU:
   681  		p := s.Prog(v.Op.Asm())
   682  		p.Scond = arm.C_UBIT
   683  		p.From.Type = obj.TYPE_REG
   684  		p.From.Reg = v.Args[0].Reg()
   685  		p.To.Type = obj.TYPE_REG
   686  		p.To.Reg = v.Reg()
   687  	case ssa.OpARMCMOVWHSconst:
   688  		p := s.Prog(arm.AMOVW)
   689  		p.Scond = arm.C_SCOND_HS
   690  		p.From.Type = obj.TYPE_CONST
   691  		p.From.Offset = v.AuxInt
   692  		p.To.Type = obj.TYPE_REG
   693  		p.To.Reg = v.Reg()
   694  	case ssa.OpARMCMOVWLSconst:
   695  		p := s.Prog(arm.AMOVW)
   696  		p.Scond = arm.C_SCOND_LS
   697  		p.From.Type = obj.TYPE_CONST
   698  		p.From.Offset = v.AuxInt
   699  		p.To.Type = obj.TYPE_REG
   700  		p.To.Reg = v.Reg()
   701  	case ssa.OpARMCALLstatic, ssa.OpARMCALLclosure, ssa.OpARMCALLinter:
   702  		s.Call(v)
   703  	case ssa.OpARMCALLtail:
   704  		s.TailCall(v)
   705  	case ssa.OpARMCALLudiv:
   706  		p := s.Prog(obj.ACALL)
   707  		p.To.Type = obj.TYPE_MEM
   708  		p.To.Name = obj.NAME_EXTERN
   709  		p.To.Sym = ir.Syms.Udiv
   710  	case ssa.OpARMLoweredWB:
   711  		p := s.Prog(obj.ACALL)
   712  		p.To.Type = obj.TYPE_MEM
   713  		p.To.Name = obj.NAME_EXTERN
   714  		// AuxInt encodes how many buffer entries we need.
   715  		p.To.Sym = ir.Syms.GCWriteBarrier[v.AuxInt-1]
   716  	case ssa.OpARMLoweredPanicBoundsA, ssa.OpARMLoweredPanicBoundsB, ssa.OpARMLoweredPanicBoundsC:
   717  		p := s.Prog(obj.ACALL)
   718  		p.To.Type = obj.TYPE_MEM
   719  		p.To.Name = obj.NAME_EXTERN
   720  		p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt]
   721  		s.UseArgs(8) // space used in callee args area by assembly stubs
   722  	case ssa.OpARMLoweredPanicExtendA, ssa.OpARMLoweredPanicExtendB, ssa.OpARMLoweredPanicExtendC:
   723  		p := s.Prog(obj.ACALL)
   724  		p.To.Type = obj.TYPE_MEM
   725  		p.To.Name = obj.NAME_EXTERN
   726  		p.To.Sym = ssagen.ExtendCheckFunc[v.AuxInt]
   727  		s.UseArgs(12) // space used in callee args area by assembly stubs
   728  	case ssa.OpARMDUFFZERO:
   729  		p := s.Prog(obj.ADUFFZERO)
   730  		p.To.Type = obj.TYPE_MEM
   731  		p.To.Name = obj.NAME_EXTERN
   732  		p.To.Sym = ir.Syms.Duffzero
   733  		p.To.Offset = v.AuxInt
   734  	case ssa.OpARMDUFFCOPY:
   735  		p := s.Prog(obj.ADUFFCOPY)
   736  		p.To.Type = obj.TYPE_MEM
   737  		p.To.Name = obj.NAME_EXTERN
   738  		p.To.Sym = ir.Syms.Duffcopy
   739  		p.To.Offset = v.AuxInt
   740  	case ssa.OpARMLoweredNilCheck:
   741  		// Issue a load which will fault if arg is nil.
   742  		p := s.Prog(arm.AMOVB)
   743  		p.From.Type = obj.TYPE_MEM
   744  		p.From.Reg = v.Args[0].Reg()
   745  		ssagen.AddAux(&p.From, v)
   746  		p.To.Type = obj.TYPE_REG
   747  		p.To.Reg = arm.REGTMP
   748  		if logopt.Enabled() {
   749  			logopt.LogOpt(v.Pos, "nilcheck", "genssa", v.Block.Func.Name)
   750  		}
   751  		if base.Debug.Nil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
   752  			base.WarnfAt(v.Pos, "generated nil check")
   753  		}
   754  	case ssa.OpARMLoweredZero:
   755  		// MOVW.P	Rarg2, 4(R1)
   756  		// CMP	Rarg1, R1
   757  		// BLE	-2(PC)
   758  		// arg1 is the address of the last element to zero
   759  		// arg2 is known to be zero
   760  		// auxint is alignment
   761  		var sz int64
   762  		var mov obj.As
   763  		switch {
   764  		case v.AuxInt%4 == 0:
   765  			sz = 4
   766  			mov = arm.AMOVW
   767  		case v.AuxInt%2 == 0:
   768  			sz = 2
   769  			mov = arm.AMOVH
   770  		default:
   771  			sz = 1
   772  			mov = arm.AMOVB
   773  		}
   774  		p := s.Prog(mov)
   775  		p.Scond = arm.C_PBIT
   776  		p.From.Type = obj.TYPE_REG
   777  		p.From.Reg = v.Args[2].Reg()
   778  		p.To.Type = obj.TYPE_MEM
   779  		p.To.Reg = arm.REG_R1
   780  		p.To.Offset = sz
   781  		p2 := s.Prog(arm.ACMP)
   782  		p2.From.Type = obj.TYPE_REG
   783  		p2.From.Reg = v.Args[1].Reg()
   784  		p2.Reg = arm.REG_R1
   785  		p3 := s.Prog(arm.ABLE)
   786  		p3.To.Type = obj.TYPE_BRANCH
   787  		p3.To.SetTarget(p)
   788  	case ssa.OpARMLoweredMove:
   789  		// MOVW.P	4(R1), Rtmp
   790  		// MOVW.P	Rtmp, 4(R2)
   791  		// CMP	Rarg2, R1
   792  		// BLE	-3(PC)
   793  		// arg2 is the address of the last element of src
   794  		// auxint is alignment
   795  		var sz int64
   796  		var mov obj.As
   797  		switch {
   798  		case v.AuxInt%4 == 0:
   799  			sz = 4
   800  			mov = arm.AMOVW
   801  		case v.AuxInt%2 == 0:
   802  			sz = 2
   803  			mov = arm.AMOVH
   804  		default:
   805  			sz = 1
   806  			mov = arm.AMOVB
   807  		}
   808  		p := s.Prog(mov)
   809  		p.Scond = arm.C_PBIT
   810  		p.From.Type = obj.TYPE_MEM
   811  		p.From.Reg = arm.REG_R1
   812  		p.From.Offset = sz
   813  		p.To.Type = obj.TYPE_REG
   814  		p.To.Reg = arm.REGTMP
   815  		p2 := s.Prog(mov)
   816  		p2.Scond = arm.C_PBIT
   817  		p2.From.Type = obj.TYPE_REG
   818  		p2.From.Reg = arm.REGTMP
   819  		p2.To.Type = obj.TYPE_MEM
   820  		p2.To.Reg = arm.REG_R2
   821  		p2.To.Offset = sz
   822  		p3 := s.Prog(arm.ACMP)
   823  		p3.From.Type = obj.TYPE_REG
   824  		p3.From.Reg = v.Args[2].Reg()
   825  		p3.Reg = arm.REG_R1
   826  		p4 := s.Prog(arm.ABLE)
   827  		p4.To.Type = obj.TYPE_BRANCH
   828  		p4.To.SetTarget(p)
   829  	case ssa.OpARMEqual,
   830  		ssa.OpARMNotEqual,
   831  		ssa.OpARMLessThan,
   832  		ssa.OpARMLessEqual,
   833  		ssa.OpARMGreaterThan,
   834  		ssa.OpARMGreaterEqual,
   835  		ssa.OpARMLessThanU,
   836  		ssa.OpARMLessEqualU,
   837  		ssa.OpARMGreaterThanU,
   838  		ssa.OpARMGreaterEqualU:
   839  		// generate boolean values
   840  		// use conditional move
   841  		p := s.Prog(arm.AMOVW)
   842  		p.From.Type = obj.TYPE_CONST
   843  		p.From.Offset = 0
   844  		p.To.Type = obj.TYPE_REG
   845  		p.To.Reg = v.Reg()
   846  		p = s.Prog(arm.AMOVW)
   847  		p.Scond = condBits[v.Op]
   848  		p.From.Type = obj.TYPE_CONST
   849  		p.From.Offset = 1
   850  		p.To.Type = obj.TYPE_REG
   851  		p.To.Reg = v.Reg()
   852  	case ssa.OpARMLoweredGetClosurePtr:
   853  		// Closure pointer is R7 (arm.REGCTXT).
   854  		ssagen.CheckLoweredGetClosurePtr(v)
   855  	case ssa.OpARMLoweredGetCallerSP:
   856  		// caller's SP is FixedFrameSize below the address of the first arg
   857  		p := s.Prog(arm.AMOVW)
   858  		p.From.Type = obj.TYPE_ADDR
   859  		p.From.Offset = -base.Ctxt.Arch.FixedFrameSize
   860  		p.From.Name = obj.NAME_PARAM
   861  		p.To.Type = obj.TYPE_REG
   862  		p.To.Reg = v.Reg()
   863  	case ssa.OpARMLoweredGetCallerPC:
   864  		p := s.Prog(obj.AGETCALLERPC)
   865  		p.To.Type = obj.TYPE_REG
   866  		p.To.Reg = v.Reg()
   867  	case ssa.OpARMFlagConstant:
   868  		v.Fatalf("FlagConstant op should never make it to codegen %v", v.LongString())
   869  	case ssa.OpARMInvertFlags:
   870  		v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
   871  	case ssa.OpClobber, ssa.OpClobberReg:
   872  		// TODO: implement for clobberdead experiment. Nop is ok for now.
   873  	default:
   874  		v.Fatalf("genValue not implemented: %s", v.LongString())
   875  	}
   876  }
   877  
   878  var condBits = map[ssa.Op]uint8{
   879  	ssa.OpARMEqual:         arm.C_SCOND_EQ,
   880  	ssa.OpARMNotEqual:      arm.C_SCOND_NE,
   881  	ssa.OpARMLessThan:      arm.C_SCOND_LT,
   882  	ssa.OpARMLessThanU:     arm.C_SCOND_LO,
   883  	ssa.OpARMLessEqual:     arm.C_SCOND_LE,
   884  	ssa.OpARMLessEqualU:    arm.C_SCOND_LS,
   885  	ssa.OpARMGreaterThan:   arm.C_SCOND_GT,
   886  	ssa.OpARMGreaterThanU:  arm.C_SCOND_HI,
   887  	ssa.OpARMGreaterEqual:  arm.C_SCOND_GE,
   888  	ssa.OpARMGreaterEqualU: arm.C_SCOND_HS,
   889  }
   890  
   891  var blockJump = map[ssa.BlockKind]struct {
   892  	asm, invasm obj.As
   893  }{
   894  	ssa.BlockARMEQ:     {arm.ABEQ, arm.ABNE},
   895  	ssa.BlockARMNE:     {arm.ABNE, arm.ABEQ},
   896  	ssa.BlockARMLT:     {arm.ABLT, arm.ABGE},
   897  	ssa.BlockARMGE:     {arm.ABGE, arm.ABLT},
   898  	ssa.BlockARMLE:     {arm.ABLE, arm.ABGT},
   899  	ssa.BlockARMGT:     {arm.ABGT, arm.ABLE},
   900  	ssa.BlockARMULT:    {arm.ABLO, arm.ABHS},
   901  	ssa.BlockARMUGE:    {arm.ABHS, arm.ABLO},
   902  	ssa.BlockARMUGT:    {arm.ABHI, arm.ABLS},
   903  	ssa.BlockARMULE:    {arm.ABLS, arm.ABHI},
   904  	ssa.BlockARMLTnoov: {arm.ABMI, arm.ABPL},
   905  	ssa.BlockARMGEnoov: {arm.ABPL, arm.ABMI},
   906  }
   907  
   908  // To model a 'LEnoov' ('<=' without overflow checking) branching.
   909  var leJumps = [2][2]ssagen.IndexJump{
   910  	{{Jump: arm.ABEQ, Index: 0}, {Jump: arm.ABPL, Index: 1}}, // next == b.Succs[0]
   911  	{{Jump: arm.ABMI, Index: 0}, {Jump: arm.ABEQ, Index: 0}}, // next == b.Succs[1]
   912  }
   913  
   914  // To model a 'GTnoov' ('>' without overflow checking) branching.
   915  var gtJumps = [2][2]ssagen.IndexJump{
   916  	{{Jump: arm.ABMI, Index: 1}, {Jump: arm.ABEQ, Index: 1}}, // next == b.Succs[0]
   917  	{{Jump: arm.ABEQ, Index: 1}, {Jump: arm.ABPL, Index: 0}}, // next == b.Succs[1]
   918  }
   919  
   920  func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
   921  	switch b.Kind {
   922  	case ssa.BlockPlain:
   923  		if b.Succs[0].Block() != next {
   924  			p := s.Prog(obj.AJMP)
   925  			p.To.Type = obj.TYPE_BRANCH
   926  			s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
   927  		}
   928  
   929  	case ssa.BlockDefer:
   930  		// defer returns in R0:
   931  		// 0 if we should continue executing
   932  		// 1 if we should jump to deferreturn call
   933  		p := s.Prog(arm.ACMP)
   934  		p.From.Type = obj.TYPE_CONST
   935  		p.From.Offset = 0
   936  		p.Reg = arm.REG_R0
   937  		p = s.Prog(arm.ABNE)
   938  		p.To.Type = obj.TYPE_BRANCH
   939  		s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
   940  		if b.Succs[0].Block() != next {
   941  			p := s.Prog(obj.AJMP)
   942  			p.To.Type = obj.TYPE_BRANCH
   943  			s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
   944  		}
   945  
   946  	case ssa.BlockExit, ssa.BlockRetJmp:
   947  
   948  	case ssa.BlockRet:
   949  		s.Prog(obj.ARET)
   950  
   951  	case ssa.BlockARMEQ, ssa.BlockARMNE,
   952  		ssa.BlockARMLT, ssa.BlockARMGE,
   953  		ssa.BlockARMLE, ssa.BlockARMGT,
   954  		ssa.BlockARMULT, ssa.BlockARMUGT,
   955  		ssa.BlockARMULE, ssa.BlockARMUGE,
   956  		ssa.BlockARMLTnoov, ssa.BlockARMGEnoov:
   957  		jmp := blockJump[b.Kind]
   958  		switch next {
   959  		case b.Succs[0].Block():
   960  			s.Br(jmp.invasm, b.Succs[1].Block())
   961  		case b.Succs[1].Block():
   962  			s.Br(jmp.asm, b.Succs[0].Block())
   963  		default:
   964  			if b.Likely != ssa.BranchUnlikely {
   965  				s.Br(jmp.asm, b.Succs[0].Block())
   966  				s.Br(obj.AJMP, b.Succs[1].Block())
   967  			} else {
   968  				s.Br(jmp.invasm, b.Succs[1].Block())
   969  				s.Br(obj.AJMP, b.Succs[0].Block())
   970  			}
   971  		}
   972  
   973  	case ssa.BlockARMLEnoov:
   974  		s.CombJump(b, next, &leJumps)
   975  
   976  	case ssa.BlockARMGTnoov:
   977  		s.CombJump(b, next, &gtJumps)
   978  
   979  	default:
   980  		b.Fatalf("branch not implemented: %s", b.LongString())
   981  	}
   982  }