github.com/corona10/go@v0.0.0-20180224231303-7a218942be57/src/cmd/compile/internal/arm64/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 arm64
     6  
     7  import (
     8  	"math"
     9  
    10  	"cmd/compile/internal/gc"
    11  	"cmd/compile/internal/ssa"
    12  	"cmd/compile/internal/types"
    13  	"cmd/internal/obj"
    14  	"cmd/internal/obj/arm64"
    15  )
    16  
    17  // loadByType returns the load instruction of the given type.
    18  func loadByType(t *types.Type) obj.As {
    19  	if t.IsFloat() {
    20  		switch t.Size() {
    21  		case 4:
    22  			return arm64.AFMOVS
    23  		case 8:
    24  			return arm64.AFMOVD
    25  		}
    26  	} else {
    27  		switch t.Size() {
    28  		case 1:
    29  			if t.IsSigned() {
    30  				return arm64.AMOVB
    31  			} else {
    32  				return arm64.AMOVBU
    33  			}
    34  		case 2:
    35  			if t.IsSigned() {
    36  				return arm64.AMOVH
    37  			} else {
    38  				return arm64.AMOVHU
    39  			}
    40  		case 4:
    41  			if t.IsSigned() {
    42  				return arm64.AMOVW
    43  			} else {
    44  				return arm64.AMOVWU
    45  			}
    46  		case 8:
    47  			return arm64.AMOVD
    48  		}
    49  	}
    50  	panic("bad load type")
    51  }
    52  
    53  // storeByType returns the store instruction of the given type.
    54  func storeByType(t *types.Type) obj.As {
    55  	if t.IsFloat() {
    56  		switch t.Size() {
    57  		case 4:
    58  			return arm64.AFMOVS
    59  		case 8:
    60  			return arm64.AFMOVD
    61  		}
    62  	} else {
    63  		switch t.Size() {
    64  		case 1:
    65  			return arm64.AMOVB
    66  		case 2:
    67  			return arm64.AMOVH
    68  		case 4:
    69  			return arm64.AMOVW
    70  		case 8:
    71  			return arm64.AMOVD
    72  		}
    73  	}
    74  	panic("bad store type")
    75  }
    76  
    77  // makeshift encodes a register shifted by a constant, used as an Offset in Prog
    78  func makeshift(reg int16, typ int64, s int64) int64 {
    79  	return int64(reg&31)<<16 | typ | (s&63)<<10
    80  }
    81  
    82  // genshift generates a Prog for r = r0 op (r1 shifted by n)
    83  func genshift(s *gc.SSAGenState, as obj.As, r0, r1, r int16, typ int64, n int64) *obj.Prog {
    84  	p := s.Prog(as)
    85  	p.From.Type = obj.TYPE_SHIFT
    86  	p.From.Offset = makeshift(r1, typ, n)
    87  	p.Reg = r0
    88  	if r != 0 {
    89  		p.To.Type = obj.TYPE_REG
    90  		p.To.Reg = r
    91  	}
    92  	return p
    93  }
    94  
    95  func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
    96  	switch v.Op {
    97  	case ssa.OpCopy, ssa.OpARM64MOVDconvert, ssa.OpARM64MOVDreg:
    98  		if v.Type.IsMemory() {
    99  			return
   100  		}
   101  		x := v.Args[0].Reg()
   102  		y := v.Reg()
   103  		if x == y {
   104  			return
   105  		}
   106  		as := arm64.AMOVD
   107  		if v.Type.IsFloat() {
   108  			switch v.Type.Size() {
   109  			case 4:
   110  				as = arm64.AFMOVS
   111  			case 8:
   112  				as = arm64.AFMOVD
   113  			default:
   114  				panic("bad float size")
   115  			}
   116  		}
   117  		p := s.Prog(as)
   118  		p.From.Type = obj.TYPE_REG
   119  		p.From.Reg = x
   120  		p.To.Type = obj.TYPE_REG
   121  		p.To.Reg = y
   122  	case ssa.OpARM64MOVDnop:
   123  		if v.Reg() != v.Args[0].Reg() {
   124  			v.Fatalf("input[0] and output not in same register %s", v.LongString())
   125  		}
   126  		// nothing to do
   127  	case ssa.OpLoadReg:
   128  		if v.Type.IsFlags() {
   129  			v.Fatalf("load flags not implemented: %v", v.LongString())
   130  			return
   131  		}
   132  		p := s.Prog(loadByType(v.Type))
   133  		gc.AddrAuto(&p.From, v.Args[0])
   134  		p.To.Type = obj.TYPE_REG
   135  		p.To.Reg = v.Reg()
   136  	case ssa.OpStoreReg:
   137  		if v.Type.IsFlags() {
   138  			v.Fatalf("store flags not implemented: %v", v.LongString())
   139  			return
   140  		}
   141  		p := s.Prog(storeByType(v.Type))
   142  		p.From.Type = obj.TYPE_REG
   143  		p.From.Reg = v.Args[0].Reg()
   144  		gc.AddrAuto(&p.To, v)
   145  	case ssa.OpARM64ADD,
   146  		ssa.OpARM64SUB,
   147  		ssa.OpARM64AND,
   148  		ssa.OpARM64OR,
   149  		ssa.OpARM64XOR,
   150  		ssa.OpARM64BIC,
   151  		ssa.OpARM64MUL,
   152  		ssa.OpARM64MULW,
   153  		ssa.OpARM64MNEG,
   154  		ssa.OpARM64MNEGW,
   155  		ssa.OpARM64MULH,
   156  		ssa.OpARM64UMULH,
   157  		ssa.OpARM64MULL,
   158  		ssa.OpARM64UMULL,
   159  		ssa.OpARM64DIV,
   160  		ssa.OpARM64UDIV,
   161  		ssa.OpARM64DIVW,
   162  		ssa.OpARM64UDIVW,
   163  		ssa.OpARM64MOD,
   164  		ssa.OpARM64UMOD,
   165  		ssa.OpARM64MODW,
   166  		ssa.OpARM64UMODW,
   167  		ssa.OpARM64SLL,
   168  		ssa.OpARM64SRL,
   169  		ssa.OpARM64SRA,
   170  		ssa.OpARM64FADDS,
   171  		ssa.OpARM64FADDD,
   172  		ssa.OpARM64FSUBS,
   173  		ssa.OpARM64FSUBD,
   174  		ssa.OpARM64FMULS,
   175  		ssa.OpARM64FMULD,
   176  		ssa.OpARM64FNMULS,
   177  		ssa.OpARM64FNMULD,
   178  		ssa.OpARM64FDIVS,
   179  		ssa.OpARM64FDIVD:
   180  		r := v.Reg()
   181  		r1 := v.Args[0].Reg()
   182  		r2 := v.Args[1].Reg()
   183  		p := s.Prog(v.Op.Asm())
   184  		p.From.Type = obj.TYPE_REG
   185  		p.From.Reg = r2
   186  		p.Reg = r1
   187  		p.To.Type = obj.TYPE_REG
   188  		p.To.Reg = r
   189  	case ssa.OpARM64FMADDS,
   190  		ssa.OpARM64FMADDD,
   191  		ssa.OpARM64FNMADDS,
   192  		ssa.OpARM64FNMADDD,
   193  		ssa.OpARM64FMSUBS,
   194  		ssa.OpARM64FMSUBD,
   195  		ssa.OpARM64FNMSUBS,
   196  		ssa.OpARM64FNMSUBD:
   197  		rt := v.Reg()
   198  		ra := v.Args[0].Reg()
   199  		rm := v.Args[1].Reg()
   200  		rn := v.Args[2].Reg()
   201  		p := s.Prog(v.Op.Asm())
   202  		p.Reg = ra
   203  		p.From.Type = obj.TYPE_REG
   204  		p.From.Reg = rm
   205  		p.SetFrom3(obj.Addr{Type: obj.TYPE_REG, Reg: rn})
   206  		p.To.Type = obj.TYPE_REG
   207  		p.To.Reg = rt
   208  	case ssa.OpARM64ADDconst,
   209  		ssa.OpARM64SUBconst,
   210  		ssa.OpARM64ANDconst,
   211  		ssa.OpARM64ORconst,
   212  		ssa.OpARM64XORconst,
   213  		ssa.OpARM64BICconst,
   214  		ssa.OpARM64SLLconst,
   215  		ssa.OpARM64SRLconst,
   216  		ssa.OpARM64SRAconst,
   217  		ssa.OpARM64RORconst,
   218  		ssa.OpARM64RORWconst:
   219  		p := s.Prog(v.Op.Asm())
   220  		p.From.Type = obj.TYPE_CONST
   221  		p.From.Offset = v.AuxInt
   222  		p.Reg = v.Args[0].Reg()
   223  		p.To.Type = obj.TYPE_REG
   224  		p.To.Reg = v.Reg()
   225  	case ssa.OpARM64ADDshiftLL,
   226  		ssa.OpARM64SUBshiftLL,
   227  		ssa.OpARM64ANDshiftLL,
   228  		ssa.OpARM64ORshiftLL,
   229  		ssa.OpARM64XORshiftLL,
   230  		ssa.OpARM64BICshiftLL:
   231  		genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_LL, v.AuxInt)
   232  	case ssa.OpARM64ADDshiftRL,
   233  		ssa.OpARM64SUBshiftRL,
   234  		ssa.OpARM64ANDshiftRL,
   235  		ssa.OpARM64ORshiftRL,
   236  		ssa.OpARM64XORshiftRL,
   237  		ssa.OpARM64BICshiftRL:
   238  		genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_LR, v.AuxInt)
   239  	case ssa.OpARM64ADDshiftRA,
   240  		ssa.OpARM64SUBshiftRA,
   241  		ssa.OpARM64ANDshiftRA,
   242  		ssa.OpARM64ORshiftRA,
   243  		ssa.OpARM64XORshiftRA,
   244  		ssa.OpARM64BICshiftRA:
   245  		genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_AR, v.AuxInt)
   246  	case ssa.OpARM64MOVDconst:
   247  		p := s.Prog(v.Op.Asm())
   248  		p.From.Type = obj.TYPE_CONST
   249  		p.From.Offset = v.AuxInt
   250  		p.To.Type = obj.TYPE_REG
   251  		p.To.Reg = v.Reg()
   252  	case ssa.OpARM64FMOVSconst,
   253  		ssa.OpARM64FMOVDconst:
   254  		p := s.Prog(v.Op.Asm())
   255  		p.From.Type = obj.TYPE_FCONST
   256  		p.From.Val = math.Float64frombits(uint64(v.AuxInt))
   257  		p.To.Type = obj.TYPE_REG
   258  		p.To.Reg = v.Reg()
   259  	case ssa.OpARM64CMP,
   260  		ssa.OpARM64CMPW,
   261  		ssa.OpARM64CMN,
   262  		ssa.OpARM64CMNW,
   263  		ssa.OpARM64FCMPS,
   264  		ssa.OpARM64FCMPD:
   265  		p := s.Prog(v.Op.Asm())
   266  		p.From.Type = obj.TYPE_REG
   267  		p.From.Reg = v.Args[1].Reg()
   268  		p.Reg = v.Args[0].Reg()
   269  	case ssa.OpARM64CMPconst,
   270  		ssa.OpARM64CMPWconst,
   271  		ssa.OpARM64CMNconst,
   272  		ssa.OpARM64CMNWconst:
   273  		p := s.Prog(v.Op.Asm())
   274  		p.From.Type = obj.TYPE_CONST
   275  		p.From.Offset = v.AuxInt
   276  		p.Reg = v.Args[0].Reg()
   277  	case ssa.OpARM64CMPshiftLL:
   278  		genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_LL, v.AuxInt)
   279  	case ssa.OpARM64CMPshiftRL:
   280  		genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_LR, v.AuxInt)
   281  	case ssa.OpARM64CMPshiftRA:
   282  		genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_AR, v.AuxInt)
   283  	case ssa.OpARM64MOVDaddr:
   284  		p := s.Prog(arm64.AMOVD)
   285  		p.From.Type = obj.TYPE_ADDR
   286  		p.From.Reg = v.Args[0].Reg()
   287  		p.To.Type = obj.TYPE_REG
   288  		p.To.Reg = v.Reg()
   289  
   290  		var wantreg string
   291  		// MOVD $sym+off(base), R
   292  		// the assembler expands it as the following:
   293  		// - base is SP: add constant offset to SP (R13)
   294  		//               when constant is large, tmp register (R11) may be used
   295  		// - base is SB: load external address from constant pool (use relocation)
   296  		switch v.Aux.(type) {
   297  		default:
   298  			v.Fatalf("aux is of unknown type %T", v.Aux)
   299  		case *obj.LSym:
   300  			wantreg = "SB"
   301  			gc.AddAux(&p.From, v)
   302  		case *gc.Node:
   303  			wantreg = "SP"
   304  			gc.AddAux(&p.From, v)
   305  		case nil:
   306  			// No sym, just MOVD $off(SP), R
   307  			wantreg = "SP"
   308  			p.From.Offset = v.AuxInt
   309  		}
   310  		if reg := v.Args[0].RegName(); reg != wantreg {
   311  			v.Fatalf("bad reg %s for symbol type %T, want %s", reg, v.Aux, wantreg)
   312  		}
   313  	case ssa.OpARM64MOVBload,
   314  		ssa.OpARM64MOVBUload,
   315  		ssa.OpARM64MOVHload,
   316  		ssa.OpARM64MOVHUload,
   317  		ssa.OpARM64MOVWload,
   318  		ssa.OpARM64MOVWUload,
   319  		ssa.OpARM64MOVDload,
   320  		ssa.OpARM64FMOVSload,
   321  		ssa.OpARM64FMOVDload:
   322  		p := s.Prog(v.Op.Asm())
   323  		p.From.Type = obj.TYPE_MEM
   324  		p.From.Reg = v.Args[0].Reg()
   325  		gc.AddAux(&p.From, v)
   326  		p.To.Type = obj.TYPE_REG
   327  		p.To.Reg = v.Reg()
   328  	case ssa.OpARM64LDAR,
   329  		ssa.OpARM64LDARW:
   330  		p := s.Prog(v.Op.Asm())
   331  		p.From.Type = obj.TYPE_MEM
   332  		p.From.Reg = v.Args[0].Reg()
   333  		gc.AddAux(&p.From, v)
   334  		p.To.Type = obj.TYPE_REG
   335  		p.To.Reg = v.Reg0()
   336  	case ssa.OpARM64MOVBstore,
   337  		ssa.OpARM64MOVHstore,
   338  		ssa.OpARM64MOVWstore,
   339  		ssa.OpARM64MOVDstore,
   340  		ssa.OpARM64FMOVSstore,
   341  		ssa.OpARM64FMOVDstore,
   342  		ssa.OpARM64STLR,
   343  		ssa.OpARM64STLRW:
   344  		p := s.Prog(v.Op.Asm())
   345  		p.From.Type = obj.TYPE_REG
   346  		p.From.Reg = v.Args[1].Reg()
   347  		p.To.Type = obj.TYPE_MEM
   348  		p.To.Reg = v.Args[0].Reg()
   349  		gc.AddAux(&p.To, v)
   350  	case ssa.OpARM64STP:
   351  		p := s.Prog(v.Op.Asm())
   352  		p.From.Type = obj.TYPE_REGREG
   353  		p.From.Reg = v.Args[1].Reg()
   354  		p.From.Offset = int64(v.Args[2].Reg())
   355  		p.To.Type = obj.TYPE_MEM
   356  		p.To.Reg = v.Args[0].Reg()
   357  		gc.AddAux(&p.To, v)
   358  	case ssa.OpARM64MOVBstorezero,
   359  		ssa.OpARM64MOVHstorezero,
   360  		ssa.OpARM64MOVWstorezero,
   361  		ssa.OpARM64MOVDstorezero:
   362  		p := s.Prog(v.Op.Asm())
   363  		p.From.Type = obj.TYPE_REG
   364  		p.From.Reg = arm64.REGZERO
   365  		p.To.Type = obj.TYPE_MEM
   366  		p.To.Reg = v.Args[0].Reg()
   367  		gc.AddAux(&p.To, v)
   368  	case ssa.OpARM64MOVQstorezero:
   369  		p := s.Prog(v.Op.Asm())
   370  		p.From.Type = obj.TYPE_REGREG
   371  		p.From.Reg = arm64.REGZERO
   372  		p.From.Offset = int64(arm64.REGZERO)
   373  		p.To.Type = obj.TYPE_MEM
   374  		p.To.Reg = v.Args[0].Reg()
   375  		gc.AddAux(&p.To, v)
   376  	case ssa.OpARM64LoweredAtomicExchange64,
   377  		ssa.OpARM64LoweredAtomicExchange32:
   378  		// LDAXR	(Rarg0), Rout
   379  		// STLXR	Rarg1, (Rarg0), Rtmp
   380  		// CBNZ		Rtmp, -2(PC)
   381  		ld := arm64.ALDAXR
   382  		st := arm64.ASTLXR
   383  		if v.Op == ssa.OpARM64LoweredAtomicExchange32 {
   384  			ld = arm64.ALDAXRW
   385  			st = arm64.ASTLXRW
   386  		}
   387  		r0 := v.Args[0].Reg()
   388  		r1 := v.Args[1].Reg()
   389  		out := v.Reg0()
   390  		p := s.Prog(ld)
   391  		p.From.Type = obj.TYPE_MEM
   392  		p.From.Reg = r0
   393  		p.To.Type = obj.TYPE_REG
   394  		p.To.Reg = out
   395  		p1 := s.Prog(st)
   396  		p1.From.Type = obj.TYPE_REG
   397  		p1.From.Reg = r1
   398  		p1.To.Type = obj.TYPE_MEM
   399  		p1.To.Reg = r0
   400  		p1.RegTo2 = arm64.REGTMP
   401  		p2 := s.Prog(arm64.ACBNZ)
   402  		p2.From.Type = obj.TYPE_REG
   403  		p2.From.Reg = arm64.REGTMP
   404  		p2.To.Type = obj.TYPE_BRANCH
   405  		gc.Patch(p2, p)
   406  	case ssa.OpARM64LoweredAtomicAdd64,
   407  		ssa.OpARM64LoweredAtomicAdd32:
   408  		// LDAXR	(Rarg0), Rout
   409  		// ADD		Rarg1, Rout
   410  		// STLXR	Rout, (Rarg0), Rtmp
   411  		// CBNZ		Rtmp, -3(PC)
   412  		ld := arm64.ALDAXR
   413  		st := arm64.ASTLXR
   414  		if v.Op == ssa.OpARM64LoweredAtomicAdd32 {
   415  			ld = arm64.ALDAXRW
   416  			st = arm64.ASTLXRW
   417  		}
   418  		r0 := v.Args[0].Reg()
   419  		r1 := v.Args[1].Reg()
   420  		out := v.Reg0()
   421  		p := s.Prog(ld)
   422  		p.From.Type = obj.TYPE_MEM
   423  		p.From.Reg = r0
   424  		p.To.Type = obj.TYPE_REG
   425  		p.To.Reg = out
   426  		p1 := s.Prog(arm64.AADD)
   427  		p1.From.Type = obj.TYPE_REG
   428  		p1.From.Reg = r1
   429  		p1.To.Type = obj.TYPE_REG
   430  		p1.To.Reg = out
   431  		p2 := s.Prog(st)
   432  		p2.From.Type = obj.TYPE_REG
   433  		p2.From.Reg = out
   434  		p2.To.Type = obj.TYPE_MEM
   435  		p2.To.Reg = r0
   436  		p2.RegTo2 = arm64.REGTMP
   437  		p3 := s.Prog(arm64.ACBNZ)
   438  		p3.From.Type = obj.TYPE_REG
   439  		p3.From.Reg = arm64.REGTMP
   440  		p3.To.Type = obj.TYPE_BRANCH
   441  		gc.Patch(p3, p)
   442  	case ssa.OpARM64LoweredAtomicCas64,
   443  		ssa.OpARM64LoweredAtomicCas32:
   444  		// LDAXR	(Rarg0), Rtmp
   445  		// CMP		Rarg1, Rtmp
   446  		// BNE		3(PC)
   447  		// STLXR	Rarg2, (Rarg0), Rtmp
   448  		// CBNZ		Rtmp, -4(PC)
   449  		// CSET		EQ, Rout
   450  		ld := arm64.ALDAXR
   451  		st := arm64.ASTLXR
   452  		cmp := arm64.ACMP
   453  		if v.Op == ssa.OpARM64LoweredAtomicCas32 {
   454  			ld = arm64.ALDAXRW
   455  			st = arm64.ASTLXRW
   456  			cmp = arm64.ACMPW
   457  		}
   458  		r0 := v.Args[0].Reg()
   459  		r1 := v.Args[1].Reg()
   460  		r2 := v.Args[2].Reg()
   461  		out := v.Reg0()
   462  		p := s.Prog(ld)
   463  		p.From.Type = obj.TYPE_MEM
   464  		p.From.Reg = r0
   465  		p.To.Type = obj.TYPE_REG
   466  		p.To.Reg = arm64.REGTMP
   467  		p1 := s.Prog(cmp)
   468  		p1.From.Type = obj.TYPE_REG
   469  		p1.From.Reg = r1
   470  		p1.Reg = arm64.REGTMP
   471  		p2 := s.Prog(arm64.ABNE)
   472  		p2.To.Type = obj.TYPE_BRANCH
   473  		p3 := s.Prog(st)
   474  		p3.From.Type = obj.TYPE_REG
   475  		p3.From.Reg = r2
   476  		p3.To.Type = obj.TYPE_MEM
   477  		p3.To.Reg = r0
   478  		p3.RegTo2 = arm64.REGTMP
   479  		p4 := s.Prog(arm64.ACBNZ)
   480  		p4.From.Type = obj.TYPE_REG
   481  		p4.From.Reg = arm64.REGTMP
   482  		p4.To.Type = obj.TYPE_BRANCH
   483  		gc.Patch(p4, p)
   484  		p5 := s.Prog(arm64.ACSET)
   485  		p5.From.Type = obj.TYPE_REG // assembler encodes conditional bits in Reg
   486  		p5.From.Reg = arm64.COND_EQ
   487  		p5.To.Type = obj.TYPE_REG
   488  		p5.To.Reg = out
   489  		gc.Patch(p2, p5)
   490  	case ssa.OpARM64LoweredAtomicAnd8,
   491  		ssa.OpARM64LoweredAtomicOr8:
   492  		// LDAXRB	(Rarg0), Rtmp
   493  		// AND/OR	Rarg1, Rtmp
   494  		// STLXRB	Rtmp, (Rarg0), Rtmp
   495  		// CBNZ		Rtmp, -3(PC)
   496  		r0 := v.Args[0].Reg()
   497  		r1 := v.Args[1].Reg()
   498  		p := s.Prog(arm64.ALDAXRB)
   499  		p.From.Type = obj.TYPE_MEM
   500  		p.From.Reg = r0
   501  		p.To.Type = obj.TYPE_REG
   502  		p.To.Reg = arm64.REGTMP
   503  		p1 := s.Prog(v.Op.Asm())
   504  		p1.From.Type = obj.TYPE_REG
   505  		p1.From.Reg = r1
   506  		p1.To.Type = obj.TYPE_REG
   507  		p1.To.Reg = arm64.REGTMP
   508  		p2 := s.Prog(arm64.ASTLXRB)
   509  		p2.From.Type = obj.TYPE_REG
   510  		p2.From.Reg = arm64.REGTMP
   511  		p2.To.Type = obj.TYPE_MEM
   512  		p2.To.Reg = r0
   513  		p2.RegTo2 = arm64.REGTMP
   514  		p3 := s.Prog(arm64.ACBNZ)
   515  		p3.From.Type = obj.TYPE_REG
   516  		p3.From.Reg = arm64.REGTMP
   517  		p3.To.Type = obj.TYPE_BRANCH
   518  		gc.Patch(p3, p)
   519  	case ssa.OpARM64MOVBreg,
   520  		ssa.OpARM64MOVBUreg,
   521  		ssa.OpARM64MOVHreg,
   522  		ssa.OpARM64MOVHUreg,
   523  		ssa.OpARM64MOVWreg,
   524  		ssa.OpARM64MOVWUreg:
   525  		a := v.Args[0]
   526  		for a.Op == ssa.OpCopy || a.Op == ssa.OpARM64MOVDreg {
   527  			a = a.Args[0]
   528  		}
   529  		if a.Op == ssa.OpLoadReg {
   530  			t := a.Type
   531  			switch {
   532  			case v.Op == ssa.OpARM64MOVBreg && t.Size() == 1 && t.IsSigned(),
   533  				v.Op == ssa.OpARM64MOVBUreg && t.Size() == 1 && !t.IsSigned(),
   534  				v.Op == ssa.OpARM64MOVHreg && t.Size() == 2 && t.IsSigned(),
   535  				v.Op == ssa.OpARM64MOVHUreg && t.Size() == 2 && !t.IsSigned(),
   536  				v.Op == ssa.OpARM64MOVWreg && t.Size() == 4 && t.IsSigned(),
   537  				v.Op == ssa.OpARM64MOVWUreg && t.Size() == 4 && !t.IsSigned():
   538  				// arg is a proper-typed load, already zero/sign-extended, don't extend again
   539  				if v.Reg() == v.Args[0].Reg() {
   540  					return
   541  				}
   542  				p := s.Prog(arm64.AMOVD)
   543  				p.From.Type = obj.TYPE_REG
   544  				p.From.Reg = v.Args[0].Reg()
   545  				p.To.Type = obj.TYPE_REG
   546  				p.To.Reg = v.Reg()
   547  				return
   548  			default:
   549  			}
   550  		}
   551  		fallthrough
   552  	case ssa.OpARM64MVN,
   553  		ssa.OpARM64NEG,
   554  		ssa.OpARM64FMOVDfpgp,
   555  		ssa.OpARM64FMOVDgpfp,
   556  		ssa.OpARM64FNEGS,
   557  		ssa.OpARM64FNEGD,
   558  		ssa.OpARM64FSQRTD,
   559  		ssa.OpARM64FCVTZSSW,
   560  		ssa.OpARM64FCVTZSDW,
   561  		ssa.OpARM64FCVTZUSW,
   562  		ssa.OpARM64FCVTZUDW,
   563  		ssa.OpARM64FCVTZSS,
   564  		ssa.OpARM64FCVTZSD,
   565  		ssa.OpARM64FCVTZUS,
   566  		ssa.OpARM64FCVTZUD,
   567  		ssa.OpARM64SCVTFWS,
   568  		ssa.OpARM64SCVTFWD,
   569  		ssa.OpARM64SCVTFS,
   570  		ssa.OpARM64SCVTFD,
   571  		ssa.OpARM64UCVTFWS,
   572  		ssa.OpARM64UCVTFWD,
   573  		ssa.OpARM64UCVTFS,
   574  		ssa.OpARM64UCVTFD,
   575  		ssa.OpARM64FCVTSD,
   576  		ssa.OpARM64FCVTDS,
   577  		ssa.OpARM64REV,
   578  		ssa.OpARM64REVW,
   579  		ssa.OpARM64REV16W,
   580  		ssa.OpARM64RBIT,
   581  		ssa.OpARM64RBITW,
   582  		ssa.OpARM64CLZ,
   583  		ssa.OpARM64CLZW,
   584  		ssa.OpARM64FRINTAD,
   585  		ssa.OpARM64FRINTMD,
   586  		ssa.OpARM64FRINTPD,
   587  		ssa.OpARM64FRINTZD:
   588  		p := s.Prog(v.Op.Asm())
   589  		p.From.Type = obj.TYPE_REG
   590  		p.From.Reg = v.Args[0].Reg()
   591  		p.To.Type = obj.TYPE_REG
   592  		p.To.Reg = v.Reg()
   593  	case ssa.OpARM64LoweredRound32F, ssa.OpARM64LoweredRound64F:
   594  		// input is already rounded
   595  	case ssa.OpARM64VCNT:
   596  		p := s.Prog(v.Op.Asm())
   597  		p.From.Type = obj.TYPE_REG
   598  		p.From.Reg = (v.Args[0].Reg()-arm64.REG_F0)&31 + arm64.REG_ARNG + ((arm64.ARNG_8B & 15) << 5)
   599  		p.To.Type = obj.TYPE_REG
   600  		p.To.Reg = (v.Reg()-arm64.REG_F0)&31 + arm64.REG_ARNG + ((arm64.ARNG_8B & 15) << 5)
   601  	case ssa.OpARM64VUADDLV:
   602  		p := s.Prog(v.Op.Asm())
   603  		p.From.Type = obj.TYPE_REG
   604  		p.From.Reg = (v.Args[0].Reg()-arm64.REG_F0)&31 + arm64.REG_ARNG + ((arm64.ARNG_8B & 15) << 5)
   605  		p.To.Type = obj.TYPE_REG
   606  		p.To.Reg = v.Reg() - arm64.REG_F0 + arm64.REG_V0
   607  	case ssa.OpARM64CSEL, ssa.OpARM64CSEL0:
   608  		r1 := int16(arm64.REGZERO)
   609  		if v.Op != ssa.OpARM64CSEL0 {
   610  			r1 = v.Args[1].Reg()
   611  		}
   612  		p := s.Prog(v.Op.Asm())
   613  		p.From.Type = obj.TYPE_REG // assembler encodes conditional bits in Reg
   614  		p.From.Reg = condBits[v.Aux.(ssa.Op)]
   615  		p.Reg = v.Args[0].Reg()
   616  		p.SetFrom3(obj.Addr{Type: obj.TYPE_REG, Reg: r1})
   617  		p.To.Type = obj.TYPE_REG
   618  		p.To.Reg = v.Reg()
   619  	case ssa.OpARM64DUFFZERO:
   620  		// runtime.duffzero expects start address in R16
   621  		p := s.Prog(obj.ADUFFZERO)
   622  		p.To.Type = obj.TYPE_MEM
   623  		p.To.Name = obj.NAME_EXTERN
   624  		p.To.Sym = gc.Duffzero
   625  		p.To.Offset = v.AuxInt
   626  	case ssa.OpARM64LoweredZero:
   627  		// STP.P	(ZR,ZR), 16(R16)
   628  		// CMP	Rarg1, R16
   629  		// BLE	-2(PC)
   630  		// arg1 is the address of the last 16-byte unit to zero
   631  		p := s.Prog(arm64.ASTP)
   632  		p.Scond = arm64.C_XPOST
   633  		p.From.Type = obj.TYPE_REGREG
   634  		p.From.Reg = arm64.REGZERO
   635  		p.From.Offset = int64(arm64.REGZERO)
   636  		p.To.Type = obj.TYPE_MEM
   637  		p.To.Reg = arm64.REG_R16
   638  		p.To.Offset = 16
   639  		p2 := s.Prog(arm64.ACMP)
   640  		p2.From.Type = obj.TYPE_REG
   641  		p2.From.Reg = v.Args[1].Reg()
   642  		p2.Reg = arm64.REG_R16
   643  		p3 := s.Prog(arm64.ABLE)
   644  		p3.To.Type = obj.TYPE_BRANCH
   645  		gc.Patch(p3, p)
   646  	case ssa.OpARM64DUFFCOPY:
   647  		p := s.Prog(obj.ADUFFCOPY)
   648  		p.To.Type = obj.TYPE_MEM
   649  		p.To.Name = obj.NAME_EXTERN
   650  		p.To.Sym = gc.Duffcopy
   651  		p.To.Offset = v.AuxInt
   652  	case ssa.OpARM64LoweredMove:
   653  		// MOVD.P	8(R16), Rtmp
   654  		// MOVD.P	Rtmp, 8(R17)
   655  		// CMP	Rarg2, R16
   656  		// BLE	-3(PC)
   657  		// arg2 is the address of the last element of src
   658  		p := s.Prog(arm64.AMOVD)
   659  		p.Scond = arm64.C_XPOST
   660  		p.From.Type = obj.TYPE_MEM
   661  		p.From.Reg = arm64.REG_R16
   662  		p.From.Offset = 8
   663  		p.To.Type = obj.TYPE_REG
   664  		p.To.Reg = arm64.REGTMP
   665  		p2 := s.Prog(arm64.AMOVD)
   666  		p2.Scond = arm64.C_XPOST
   667  		p2.From.Type = obj.TYPE_REG
   668  		p2.From.Reg = arm64.REGTMP
   669  		p2.To.Type = obj.TYPE_MEM
   670  		p2.To.Reg = arm64.REG_R17
   671  		p2.To.Offset = 8
   672  		p3 := s.Prog(arm64.ACMP)
   673  		p3.From.Type = obj.TYPE_REG
   674  		p3.From.Reg = v.Args[2].Reg()
   675  		p3.Reg = arm64.REG_R16
   676  		p4 := s.Prog(arm64.ABLE)
   677  		p4.To.Type = obj.TYPE_BRANCH
   678  		gc.Patch(p4, p)
   679  	case ssa.OpARM64CALLstatic, ssa.OpARM64CALLclosure, ssa.OpARM64CALLinter:
   680  		s.Call(v)
   681  	case ssa.OpARM64LoweredWB:
   682  		p := s.Prog(obj.ACALL)
   683  		p.To.Type = obj.TYPE_MEM
   684  		p.To.Name = obj.NAME_EXTERN
   685  		p.To.Sym = v.Aux.(*obj.LSym)
   686  	case ssa.OpARM64LoweredNilCheck:
   687  		// Issue a load which will fault if arg is nil.
   688  		p := s.Prog(arm64.AMOVB)
   689  		p.From.Type = obj.TYPE_MEM
   690  		p.From.Reg = v.Args[0].Reg()
   691  		gc.AddAux(&p.From, v)
   692  		p.To.Type = obj.TYPE_REG
   693  		p.To.Reg = arm64.REGTMP
   694  		if gc.Debug_checknil != 0 && v.Pos.Line() > 1 { // v.Line==1 in generated wrappers
   695  			gc.Warnl(v.Pos, "generated nil check")
   696  		}
   697  	case ssa.OpARM64Equal,
   698  		ssa.OpARM64NotEqual,
   699  		ssa.OpARM64LessThan,
   700  		ssa.OpARM64LessEqual,
   701  		ssa.OpARM64GreaterThan,
   702  		ssa.OpARM64GreaterEqual,
   703  		ssa.OpARM64LessThanU,
   704  		ssa.OpARM64LessEqualU,
   705  		ssa.OpARM64GreaterThanU,
   706  		ssa.OpARM64GreaterEqualU:
   707  		// generate boolean values using CSET
   708  		p := s.Prog(arm64.ACSET)
   709  		p.From.Type = obj.TYPE_REG // assembler encodes conditional bits in Reg
   710  		p.From.Reg = condBits[v.Op]
   711  		p.To.Type = obj.TYPE_REG
   712  		p.To.Reg = v.Reg()
   713  	case ssa.OpARM64LoweredGetClosurePtr:
   714  		// Closure pointer is R26 (arm64.REGCTXT).
   715  		gc.CheckLoweredGetClosurePtr(v)
   716  	case ssa.OpARM64LoweredGetCallerSP:
   717  		// caller's SP is FixedFrameSize below the address of the first arg
   718  		p := s.Prog(arm64.AMOVD)
   719  		p.From.Type = obj.TYPE_ADDR
   720  		p.From.Offset = -gc.Ctxt.FixedFrameSize()
   721  		p.From.Name = obj.NAME_PARAM
   722  		p.To.Type = obj.TYPE_REG
   723  		p.To.Reg = v.Reg()
   724  	case ssa.OpARM64FlagEQ,
   725  		ssa.OpARM64FlagLT_ULT,
   726  		ssa.OpARM64FlagLT_UGT,
   727  		ssa.OpARM64FlagGT_ULT,
   728  		ssa.OpARM64FlagGT_UGT:
   729  		v.Fatalf("Flag* ops should never make it to codegen %v", v.LongString())
   730  	case ssa.OpARM64InvertFlags:
   731  		v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
   732  	case ssa.OpClobber:
   733  		// TODO: implement for clobberdead experiment. Nop is ok for now.
   734  	default:
   735  		v.Fatalf("genValue not implemented: %s", v.LongString())
   736  	}
   737  }
   738  
   739  var condBits = map[ssa.Op]int16{
   740  	ssa.OpARM64Equal:         arm64.COND_EQ,
   741  	ssa.OpARM64NotEqual:      arm64.COND_NE,
   742  	ssa.OpARM64LessThan:      arm64.COND_LT,
   743  	ssa.OpARM64LessThanU:     arm64.COND_LO,
   744  	ssa.OpARM64LessEqual:     arm64.COND_LE,
   745  	ssa.OpARM64LessEqualU:    arm64.COND_LS,
   746  	ssa.OpARM64GreaterThan:   arm64.COND_GT,
   747  	ssa.OpARM64GreaterThanU:  arm64.COND_HI,
   748  	ssa.OpARM64GreaterEqual:  arm64.COND_GE,
   749  	ssa.OpARM64GreaterEqualU: arm64.COND_HS,
   750  }
   751  
   752  var blockJump = map[ssa.BlockKind]struct {
   753  	asm, invasm obj.As
   754  }{
   755  	ssa.BlockARM64EQ:   {arm64.ABEQ, arm64.ABNE},
   756  	ssa.BlockARM64NE:   {arm64.ABNE, arm64.ABEQ},
   757  	ssa.BlockARM64LT:   {arm64.ABLT, arm64.ABGE},
   758  	ssa.BlockARM64GE:   {arm64.ABGE, arm64.ABLT},
   759  	ssa.BlockARM64LE:   {arm64.ABLE, arm64.ABGT},
   760  	ssa.BlockARM64GT:   {arm64.ABGT, arm64.ABLE},
   761  	ssa.BlockARM64ULT:  {arm64.ABLO, arm64.ABHS},
   762  	ssa.BlockARM64UGE:  {arm64.ABHS, arm64.ABLO},
   763  	ssa.BlockARM64UGT:  {arm64.ABHI, arm64.ABLS},
   764  	ssa.BlockARM64ULE:  {arm64.ABLS, arm64.ABHI},
   765  	ssa.BlockARM64Z:    {arm64.ACBZ, arm64.ACBNZ},
   766  	ssa.BlockARM64NZ:   {arm64.ACBNZ, arm64.ACBZ},
   767  	ssa.BlockARM64ZW:   {arm64.ACBZW, arm64.ACBNZW},
   768  	ssa.BlockARM64NZW:  {arm64.ACBNZW, arm64.ACBZW},
   769  	ssa.BlockARM64TBZ:  {arm64.ATBZ, arm64.ATBNZ},
   770  	ssa.BlockARM64TBNZ: {arm64.ATBNZ, arm64.ATBZ},
   771  }
   772  
   773  func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
   774  	switch b.Kind {
   775  	case ssa.BlockPlain:
   776  		if b.Succs[0].Block() != next {
   777  			p := s.Prog(obj.AJMP)
   778  			p.To.Type = obj.TYPE_BRANCH
   779  			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
   780  		}
   781  
   782  	case ssa.BlockDefer:
   783  		// defer returns in R0:
   784  		// 0 if we should continue executing
   785  		// 1 if we should jump to deferreturn call
   786  		p := s.Prog(arm64.ACMP)
   787  		p.From.Type = obj.TYPE_CONST
   788  		p.From.Offset = 0
   789  		p.Reg = arm64.REG_R0
   790  		p = s.Prog(arm64.ABNE)
   791  		p.To.Type = obj.TYPE_BRANCH
   792  		s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
   793  		if b.Succs[0].Block() != next {
   794  			p := s.Prog(obj.AJMP)
   795  			p.To.Type = obj.TYPE_BRANCH
   796  			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
   797  		}
   798  
   799  	case ssa.BlockExit:
   800  		s.Prog(obj.AUNDEF) // tell plive.go that we never reach here
   801  
   802  	case ssa.BlockRet:
   803  		s.Prog(obj.ARET)
   804  
   805  	case ssa.BlockRetJmp:
   806  		p := s.Prog(obj.ARET)
   807  		p.To.Type = obj.TYPE_MEM
   808  		p.To.Name = obj.NAME_EXTERN
   809  		p.To.Sym = b.Aux.(*obj.LSym)
   810  
   811  	case ssa.BlockARM64EQ, ssa.BlockARM64NE,
   812  		ssa.BlockARM64LT, ssa.BlockARM64GE,
   813  		ssa.BlockARM64LE, ssa.BlockARM64GT,
   814  		ssa.BlockARM64ULT, ssa.BlockARM64UGT,
   815  		ssa.BlockARM64ULE, ssa.BlockARM64UGE,
   816  		ssa.BlockARM64Z, ssa.BlockARM64NZ,
   817  		ssa.BlockARM64ZW, ssa.BlockARM64NZW:
   818  		jmp := blockJump[b.Kind]
   819  		var p *obj.Prog
   820  		switch next {
   821  		case b.Succs[0].Block():
   822  			p = s.Prog(jmp.invasm)
   823  			p.To.Type = obj.TYPE_BRANCH
   824  			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
   825  		case b.Succs[1].Block():
   826  			p = s.Prog(jmp.asm)
   827  			p.To.Type = obj.TYPE_BRANCH
   828  			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
   829  		default:
   830  			p = s.Prog(jmp.asm)
   831  			p.To.Type = obj.TYPE_BRANCH
   832  			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
   833  			q := s.Prog(obj.AJMP)
   834  			q.To.Type = obj.TYPE_BRANCH
   835  			s.Branches = append(s.Branches, gc.Branch{P: q, B: b.Succs[1].Block()})
   836  		}
   837  		if !b.Control.Type.IsFlags() {
   838  			p.From.Type = obj.TYPE_REG
   839  			p.From.Reg = b.Control.Reg()
   840  		}
   841  	case ssa.BlockARM64TBZ, ssa.BlockARM64TBNZ:
   842  		jmp := blockJump[b.Kind]
   843  		var p *obj.Prog
   844  		switch next {
   845  		case b.Succs[0].Block():
   846  			p = s.Prog(jmp.invasm)
   847  			p.To.Type = obj.TYPE_BRANCH
   848  			p.From.Offset = b.Aux.(int64)
   849  			p.From.Type = obj.TYPE_CONST
   850  			p.Reg = b.Control.Reg()
   851  			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
   852  		case b.Succs[1].Block():
   853  			p = s.Prog(jmp.asm)
   854  			p.To.Type = obj.TYPE_BRANCH
   855  			p.From.Offset = b.Aux.(int64)
   856  			p.From.Type = obj.TYPE_CONST
   857  			p.Reg = b.Control.Reg()
   858  			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
   859  		default:
   860  			p = s.Prog(jmp.asm)
   861  			p.To.Type = obj.TYPE_BRANCH
   862  			p.From.Offset = b.Aux.(int64)
   863  			p.From.Type = obj.TYPE_CONST
   864  			p.Reg = b.Control.Reg()
   865  			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
   866  			q := s.Prog(obj.AJMP)
   867  			q.To.Type = obj.TYPE_BRANCH
   868  			s.Branches = append(s.Branches, gc.Branch{P: q, B: b.Succs[1].Block()})
   869  		}
   870  
   871  	default:
   872  		b.Fatalf("branch not implemented: %s. Control: %s", b.LongString(), b.Control.LongString())
   873  	}
   874  }