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