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