github.com/goproxy0/go@v0.0.0-20171111080102-49cc0c489d2c/src/cmd/compile/internal/ssa/gen/ARM64Ops.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  // +build ignore
     6  
     7  package main
     8  
     9  import "strings"
    10  
    11  // Notes:
    12  //  - Integer types live in the low portion of registers. Upper portions are junk.
    13  //  - Boolean types use the low-order byte of a register. 0=false, 1=true.
    14  //    Upper bytes are junk.
    15  //  - *const instructions may use a constant larger than the instruction can encode.
    16  //    In this case the assembler expands to multiple instructions and uses tmp
    17  //    register (R27).
    18  
    19  // Suffixes encode the bit width of various instructions.
    20  // D (double word) = 64 bit
    21  // W (word)        = 32 bit
    22  // H (half word)   = 16 bit
    23  // HU              = 16 bit unsigned
    24  // B (byte)        = 8 bit
    25  // BU              = 8 bit unsigned
    26  // S (single)      = 32 bit float
    27  // D (double)      = 64 bit float
    28  
    29  // Note: registers not used in regalloc are not included in this list,
    30  // so that regmask stays within int64
    31  // Be careful when hand coding regmasks.
    32  var regNamesARM64 = []string{
    33  	"R0",
    34  	"R1",
    35  	"R2",
    36  	"R3",
    37  	"R4",
    38  	"R5",
    39  	"R6",
    40  	"R7",
    41  	"R8",
    42  	"R9",
    43  	"R10",
    44  	"R11",
    45  	"R12",
    46  	"R13",
    47  	"R14",
    48  	"R15",
    49  	"R16",
    50  	"R17",
    51  	"R18", // platform register, not used
    52  	"R19",
    53  	"R20",
    54  	"R21",
    55  	"R22",
    56  	"R23",
    57  	"R24",
    58  	"R25",
    59  	"R26",
    60  	// R27 = REGTMP not used in regalloc
    61  	"g",   // aka R28
    62  	"R29", // frame pointer, not used
    63  	"R30", // aka REGLINK
    64  	"SP",  // aka R31
    65  
    66  	"F0",
    67  	"F1",
    68  	"F2",
    69  	"F3",
    70  	"F4",
    71  	"F5",
    72  	"F6",
    73  	"F7",
    74  	"F8",
    75  	"F9",
    76  	"F10",
    77  	"F11",
    78  	"F12",
    79  	"F13",
    80  	"F14",
    81  	"F15",
    82  	"F16",
    83  	"F17",
    84  	"F18",
    85  	"F19",
    86  	"F20",
    87  	"F21",
    88  	"F22",
    89  	"F23",
    90  	"F24",
    91  	"F25",
    92  	"F26",
    93  	"F27",
    94  	"F28",
    95  	"F29",
    96  	"F30",
    97  	"F31",
    98  
    99  	// pseudo-registers
   100  	"SB",
   101  }
   102  
   103  func init() {
   104  	// Make map from reg names to reg integers.
   105  	if len(regNamesARM64) > 64 {
   106  		panic("too many registers")
   107  	}
   108  	num := map[string]int{}
   109  	for i, name := range regNamesARM64 {
   110  		num[name] = i
   111  	}
   112  	buildReg := func(s string) regMask {
   113  		m := regMask(0)
   114  		for _, r := range strings.Split(s, " ") {
   115  			if n, ok := num[r]; ok {
   116  				m |= regMask(1) << uint(n)
   117  				continue
   118  			}
   119  			panic("register " + r + " not found")
   120  		}
   121  		return m
   122  	}
   123  
   124  	// Common individual register masks
   125  	var (
   126  		gp         = buildReg("R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30")
   127  		gpg        = gp | buildReg("g")
   128  		gpsp       = gp | buildReg("SP")
   129  		gpspg      = gpg | buildReg("SP")
   130  		gpspsbg    = gpspg | buildReg("SB")
   131  		fp         = buildReg("F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31")
   132  		callerSave = gp | fp | buildReg("g") // runtime.setg (and anything calling it) may clobber g
   133  	)
   134  	// Common regInfo
   135  	var (
   136  		gp01      = regInfo{inputs: nil, outputs: []regMask{gp}}
   137  		gp11      = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}}
   138  		gp11sp    = regInfo{inputs: []regMask{gpspg}, outputs: []regMask{gp}}
   139  		gp1flags  = regInfo{inputs: []regMask{gpg}}
   140  		gp1flags1 = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}}
   141  		gp21      = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}}
   142  		gp2flags  = regInfo{inputs: []regMask{gpg, gpg}}
   143  		gp2flags1 = regInfo{inputs: []regMask{gp, gp}, outputs: []regMask{gp}}
   144  		gpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}
   145  		gpstore   = regInfo{inputs: []regMask{gpspsbg, gpg}}
   146  		gpstore0  = regInfo{inputs: []regMask{gpspsbg}}
   147  		gpstore2  = regInfo{inputs: []regMask{gpspsbg, gpg, gpg}}
   148  		gpxchg    = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{gp}}
   149  		gpcas     = regInfo{inputs: []regMask{gpspsbg, gpg, gpg}, outputs: []regMask{gp}}
   150  		fp01      = regInfo{inputs: nil, outputs: []regMask{fp}}
   151  		fp11      = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}}
   152  		fpgp      = regInfo{inputs: []regMask{fp}, outputs: []regMask{gp}}
   153  		gpfp      = regInfo{inputs: []regMask{gp}, outputs: []regMask{fp}}
   154  		fp21      = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{fp}}
   155  		fp2flags  = regInfo{inputs: []regMask{fp, fp}}
   156  		fpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp}}
   157  		fpstore   = regInfo{inputs: []regMask{gpspsbg, fp}}
   158  		readflags = regInfo{inputs: nil, outputs: []regMask{gp}}
   159  	)
   160  	ops := []opData{
   161  		// binary ops
   162  		{name: "ADD", argLength: 2, reg: gp21, asm: "ADD", commutative: true},     // arg0 + arg1
   163  		{name: "ADDconst", argLength: 1, reg: gp11sp, asm: "ADD", aux: "Int64"},   // arg0 + auxInt
   164  		{name: "SUB", argLength: 2, reg: gp21, asm: "SUB"},                        // arg0 - arg1
   165  		{name: "SUBconst", argLength: 1, reg: gp11, asm: "SUB", aux: "Int64"},     // arg0 - auxInt
   166  		{name: "MUL", argLength: 2, reg: gp21, asm: "MUL", commutative: true},     // arg0 * arg1
   167  		{name: "MULW", argLength: 2, reg: gp21, asm: "MULW", commutative: true},   // arg0 * arg1, 32-bit
   168  		{name: "MULH", argLength: 2, reg: gp21, asm: "SMULH", commutative: true},  // (arg0 * arg1) >> 64, signed
   169  		{name: "UMULH", argLength: 2, reg: gp21, asm: "UMULH", commutative: true}, // (arg0 * arg1) >> 64, unsigned
   170  		{name: "MULL", argLength: 2, reg: gp21, asm: "SMULL", commutative: true},  // arg0 * arg1, signed, 32-bit mult results in 64-bit
   171  		{name: "UMULL", argLength: 2, reg: gp21, asm: "UMULL", commutative: true}, // arg0 * arg1, unsigned, 32-bit mult results in 64-bit
   172  		{name: "DIV", argLength: 2, reg: gp21, asm: "SDIV"},                       // arg0 / arg1, signed
   173  		{name: "UDIV", argLength: 2, reg: gp21, asm: "UDIV"},                      // arg0 / arg1, unsighed
   174  		{name: "DIVW", argLength: 2, reg: gp21, asm: "SDIVW"},                     // arg0 / arg1, signed, 32 bit
   175  		{name: "UDIVW", argLength: 2, reg: gp21, asm: "UDIVW"},                    // arg0 / arg1, unsighed, 32 bit
   176  		{name: "MOD", argLength: 2, reg: gp21, asm: "REM"},                        // arg0 % arg1, signed
   177  		{name: "UMOD", argLength: 2, reg: gp21, asm: "UREM"},                      // arg0 % arg1, unsigned
   178  		{name: "MODW", argLength: 2, reg: gp21, asm: "REMW"},                      // arg0 % arg1, signed, 32 bit
   179  		{name: "UMODW", argLength: 2, reg: gp21, asm: "UREMW"},                    // arg0 % arg1, unsigned, 32 bit
   180  
   181  		{name: "FADDS", argLength: 2, reg: fp21, asm: "FADDS", commutative: true}, // arg0 + arg1
   182  		{name: "FADDD", argLength: 2, reg: fp21, asm: "FADDD", commutative: true}, // arg0 + arg1
   183  		{name: "FSUBS", argLength: 2, reg: fp21, asm: "FSUBS"},                    // arg0 - arg1
   184  		{name: "FSUBD", argLength: 2, reg: fp21, asm: "FSUBD"},                    // arg0 - arg1
   185  		{name: "FMULS", argLength: 2, reg: fp21, asm: "FMULS", commutative: true}, // arg0 * arg1
   186  		{name: "FMULD", argLength: 2, reg: fp21, asm: "FMULD", commutative: true}, // arg0 * arg1
   187  		{name: "FDIVS", argLength: 2, reg: fp21, asm: "FDIVS"},                    // arg0 / arg1
   188  		{name: "FDIVD", argLength: 2, reg: fp21, asm: "FDIVD"},                    // arg0 / arg1
   189  
   190  		{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true}, // arg0 & arg1
   191  		{name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int64"}, // arg0 & auxInt
   192  		{name: "OR", argLength: 2, reg: gp21, asm: "ORR", commutative: true},  // arg0 | arg1
   193  		{name: "ORconst", argLength: 1, reg: gp11, asm: "ORR", aux: "Int64"},  // arg0 | auxInt
   194  		{name: "XOR", argLength: 2, reg: gp21, asm: "EOR", commutative: true}, // arg0 ^ arg1
   195  		{name: "XORconst", argLength: 1, reg: gp11, asm: "EOR", aux: "Int64"}, // arg0 ^ auxInt
   196  		{name: "BIC", argLength: 2, reg: gp21, asm: "BIC"},                    // arg0 &^ arg1
   197  		{name: "BICconst", argLength: 1, reg: gp11, asm: "BIC", aux: "Int64"}, // arg0 &^ auxInt
   198  
   199  		// unary ops
   200  		{name: "MVN", argLength: 1, reg: gp11, asm: "MVN"},       // ^arg0
   201  		{name: "NEG", argLength: 1, reg: gp11, asm: "NEG"},       // -arg0
   202  		{name: "FNEGS", argLength: 1, reg: fp11, asm: "FNEGS"},   // -arg0, float32
   203  		{name: "FNEGD", argLength: 1, reg: fp11, asm: "FNEGD"},   // -arg0, float64
   204  		{name: "FSQRTD", argLength: 1, reg: fp11, asm: "FSQRTD"}, // sqrt(arg0), float64
   205  		{name: "REV", argLength: 1, reg: gp11, asm: "REV"},       // byte reverse, 64-bit
   206  		{name: "REVW", argLength: 1, reg: gp11, asm: "REVW"},     // byte reverse, 32-bit
   207  		{name: "REV16W", argLength: 1, reg: gp11, asm: "REV16W"}, // byte reverse in each 16-bit halfword, 32-bit
   208  		{name: "RBIT", argLength: 1, reg: gp11, asm: "RBIT"},     // bit reverse, 64-bit
   209  		{name: "RBITW", argLength: 1, reg: gp11, asm: "RBITW"},   // bit reverse, 32-bit
   210  		{name: "CLZ", argLength: 1, reg: gp11, asm: "CLZ"},       // count leading zero, 64-bit
   211  		{name: "CLZW", argLength: 1, reg: gp11, asm: "CLZW"},     // count leading zero, 32-bit
   212  
   213  		// shifts
   214  		{name: "SLL", argLength: 2, reg: gp21, asm: "LSL"},                      // arg0 << arg1, shift amount is mod 64
   215  		{name: "SLLconst", argLength: 1, reg: gp11, asm: "LSL", aux: "Int64"},   // arg0 << auxInt
   216  		{name: "SRL", argLength: 2, reg: gp21, asm: "LSR"},                      // arg0 >> arg1, unsigned, shift amount is mod 64
   217  		{name: "SRLconst", argLength: 1, reg: gp11, asm: "LSR", aux: "Int64"},   // arg0 >> auxInt, unsigned
   218  		{name: "SRA", argLength: 2, reg: gp21, asm: "ASR"},                      // arg0 >> arg1, signed, shift amount is mod 64
   219  		{name: "SRAconst", argLength: 1, reg: gp11, asm: "ASR", aux: "Int64"},   // arg0 >> auxInt, signed
   220  		{name: "RORconst", argLength: 1, reg: gp11, asm: "ROR", aux: "Int64"},   // arg0 right rotate by auxInt bits
   221  		{name: "RORWconst", argLength: 1, reg: gp11, asm: "RORW", aux: "Int64"}, // uint32(arg0) right rotate by auxInt bits
   222  
   223  		// comparisons
   224  		{name: "CMP", argLength: 2, reg: gp2flags, asm: "CMP", typ: "Flags"},                      // arg0 compare to arg1
   225  		{name: "CMPconst", argLength: 1, reg: gp1flags, asm: "CMP", aux: "Int64", typ: "Flags"},   // arg0 compare to auxInt
   226  		{name: "CMPW", argLength: 2, reg: gp2flags, asm: "CMPW", typ: "Flags"},                    // arg0 compare to arg1, 32 bit
   227  		{name: "CMPWconst", argLength: 1, reg: gp1flags, asm: "CMPW", aux: "Int32", typ: "Flags"}, // arg0 compare to auxInt, 32 bit
   228  		{name: "CMN", argLength: 2, reg: gp2flags, asm: "CMN", typ: "Flags"},                      // arg0 compare to -arg1
   229  		{name: "CMNconst", argLength: 1, reg: gp1flags, asm: "CMN", aux: "Int64", typ: "Flags"},   // arg0 compare to -auxInt
   230  		{name: "CMNW", argLength: 2, reg: gp2flags, asm: "CMNW", typ: "Flags"},                    // arg0 compare to -arg1, 32 bit
   231  		{name: "CMNWconst", argLength: 1, reg: gp1flags, asm: "CMNW", aux: "Int32", typ: "Flags"}, // arg0 compare to -auxInt, 32 bit
   232  		{name: "FCMPS", argLength: 2, reg: fp2flags, asm: "FCMPS", typ: "Flags"},                  // arg0 compare to arg1, float32
   233  		{name: "FCMPD", argLength: 2, reg: fp2flags, asm: "FCMPD", typ: "Flags"},                  // arg0 compare to arg1, float64
   234  
   235  		// shifted ops
   236  		{name: "ADDshiftLL", argLength: 2, reg: gp21, asm: "ADD", aux: "Int64"},                   // arg0 + arg1<<auxInt
   237  		{name: "ADDshiftRL", argLength: 2, reg: gp21, asm: "ADD", aux: "Int64"},                   // arg0 + arg1>>auxInt, unsigned shift
   238  		{name: "ADDshiftRA", argLength: 2, reg: gp21, asm: "ADD", aux: "Int64"},                   // arg0 + arg1>>auxInt, signed shift
   239  		{name: "SUBshiftLL", argLength: 2, reg: gp21, asm: "SUB", aux: "Int64"},                   // arg0 - arg1<<auxInt
   240  		{name: "SUBshiftRL", argLength: 2, reg: gp21, asm: "SUB", aux: "Int64"},                   // arg0 - arg1>>auxInt, unsigned shift
   241  		{name: "SUBshiftRA", argLength: 2, reg: gp21, asm: "SUB", aux: "Int64"},                   // arg0 - arg1>>auxInt, signed shift
   242  		{name: "ANDshiftLL", argLength: 2, reg: gp21, asm: "AND", aux: "Int64"},                   // arg0 & (arg1<<auxInt)
   243  		{name: "ANDshiftRL", argLength: 2, reg: gp21, asm: "AND", aux: "Int64"},                   // arg0 & (arg1>>auxInt), unsigned shift
   244  		{name: "ANDshiftRA", argLength: 2, reg: gp21, asm: "AND", aux: "Int64"},                   // arg0 & (arg1>>auxInt), signed shift
   245  		{name: "ORshiftLL", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64"},                    // arg0 | arg1<<auxInt
   246  		{name: "ORshiftRL", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64"},                    // arg0 | arg1>>auxInt, unsigned shift
   247  		{name: "ORshiftRA", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64"},                    // arg0 | arg1>>auxInt, signed shift
   248  		{name: "XORshiftLL", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64"},                   // arg0 ^ arg1<<auxInt
   249  		{name: "XORshiftRL", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64"},                   // arg0 ^ arg1>>auxInt, unsigned shift
   250  		{name: "XORshiftRA", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64"},                   // arg0 ^ arg1>>auxInt, signed shift
   251  		{name: "BICshiftLL", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64"},                   // arg0 &^ (arg1<<auxInt)
   252  		{name: "BICshiftRL", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64"},                   // arg0 &^ (arg1>>auxInt), unsigned shift
   253  		{name: "BICshiftRA", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64"},                   // arg0 &^ (arg1>>auxInt), signed shift
   254  		{name: "CMPshiftLL", argLength: 2, reg: gp2flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to arg1<<auxInt
   255  		{name: "CMPshiftRL", argLength: 2, reg: gp2flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to arg1>>auxInt, unsigned shift
   256  		{name: "CMPshiftRA", argLength: 2, reg: gp2flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to arg1>>auxInt, signed shift
   257  
   258  		// moves
   259  		{name: "MOVDconst", argLength: 0, reg: gp01, aux: "Int64", asm: "MOVD", typ: "UInt64", rematerializeable: true},      // 32 low bits of auxint
   260  		{name: "FMOVSconst", argLength: 0, reg: fp01, aux: "Float64", asm: "FMOVS", typ: "Float32", rematerializeable: true}, // auxint as 64-bit float, convert to 32-bit float
   261  		{name: "FMOVDconst", argLength: 0, reg: fp01, aux: "Float64", asm: "FMOVD", typ: "Float64", rematerializeable: true}, // auxint as 64-bit float
   262  
   263  		{name: "MOVDaddr", argLength: 1, reg: regInfo{inputs: []regMask{buildReg("SP") | buildReg("SB")}, outputs: []regMask{gp}}, aux: "SymOff", asm: "MOVD", rematerializeable: true, symEffect: "Addr"}, // arg0 + auxInt + aux.(*gc.Sym), arg0=SP/SB
   264  
   265  		{name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true, symEffect: "Read"},      // load from arg0 + auxInt + aux.  arg1=mem.
   266  		{name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"},   // load from arg0 + auxInt + aux.  arg1=mem.
   267  		{name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true, symEffect: "Read"},     // load from arg0 + auxInt + aux.  arg1=mem.
   268  		{name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
   269  		{name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "Int32", faultOnNilArg0: true, symEffect: "Read"},     // load from arg0 + auxInt + aux.  arg1=mem.
   270  		{name: "MOVWUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVWU", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
   271  		{name: "MOVDload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVD", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"},    // load from arg0 + auxInt + aux.  arg1=mem.
   272  		{name: "FMOVSload", argLength: 2, reg: fpload, aux: "SymOff", asm: "FMOVS", typ: "Float32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux.  arg1=mem.
   273  		{name: "FMOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "FMOVD", typ: "Float64", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux.  arg1=mem.
   274  
   275  		{name: "MOVBstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVB", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"},   // store 1 byte of arg1 to arg0 + auxInt + aux.  arg2=mem.
   276  		{name: "MOVHstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVH", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"},   // store 2 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   277  		{name: "MOVWstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVW", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"},   // store 4 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   278  		{name: "MOVDstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVD", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"},   // store 8 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   279  		{name: "STP", argLength: 4, reg: gpstore2, aux: "SymOff", asm: "STP", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"},         // store 16 bytes of arg1 and arg2 to arg0 + auxInt + aux.  arg3=mem.
   280  		{name: "FMOVSstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "FMOVS", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 4 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   281  		{name: "FMOVDstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "FMOVD", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   282  
   283  		{name: "MOVBstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVB", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 1 byte of zero to arg0 + auxInt + aux.  arg1=mem.
   284  		{name: "MOVHstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVH", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 2 bytes of zero to arg0 + auxInt + aux.  arg1=mem.
   285  		{name: "MOVWstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVW", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 4 bytes of zero to arg0 + auxInt + aux.  arg1=mem.
   286  		{name: "MOVDstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVD", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of zero to arg0 + auxInt + aux.  arg1=mem.
   287  		{name: "MOVQstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "STP", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"},  // store 16 bytes of zero to arg0 + auxInt + aux.  arg1=mem.
   288  
   289  		// conversions
   290  		{name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVB"},   // move from arg0, sign-extended from byte
   291  		{name: "MOVBUreg", argLength: 1, reg: gp11, asm: "MOVBU"}, // move from arg0, unsign-extended from byte
   292  		{name: "MOVHreg", argLength: 1, reg: gp11, asm: "MOVH"},   // move from arg0, sign-extended from half
   293  		{name: "MOVHUreg", argLength: 1, reg: gp11, asm: "MOVHU"}, // move from arg0, unsign-extended from half
   294  		{name: "MOVWreg", argLength: 1, reg: gp11, asm: "MOVW"},   // move from arg0, sign-extended from word
   295  		{name: "MOVWUreg", argLength: 1, reg: gp11, asm: "MOVWU"}, // move from arg0, unsign-extended from word
   296  		{name: "MOVDreg", argLength: 1, reg: gp11, asm: "MOVD"},   // move from arg0
   297  
   298  		{name: "MOVDnop", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}, resultInArg0: true}, // nop, return arg0 in same register
   299  
   300  		{name: "SCVTFWS", argLength: 1, reg: gpfp, asm: "SCVTFWS"},   // int32 -> float32
   301  		{name: "SCVTFWD", argLength: 1, reg: gpfp, asm: "SCVTFWD"},   // int32 -> float64
   302  		{name: "UCVTFWS", argLength: 1, reg: gpfp, asm: "UCVTFWS"},   // uint32 -> float32
   303  		{name: "UCVTFWD", argLength: 1, reg: gpfp, asm: "UCVTFWD"},   // uint32 -> float64
   304  		{name: "SCVTFS", argLength: 1, reg: gpfp, asm: "SCVTFS"},     // int64 -> float32
   305  		{name: "SCVTFD", argLength: 1, reg: gpfp, asm: "SCVTFD"},     // int64 -> float64
   306  		{name: "UCVTFS", argLength: 1, reg: gpfp, asm: "UCVTFS"},     // uint64 -> float32
   307  		{name: "UCVTFD", argLength: 1, reg: gpfp, asm: "UCVTFD"},     // uint64 -> float64
   308  		{name: "FCVTZSSW", argLength: 1, reg: fpgp, asm: "FCVTZSSW"}, // float32 -> int32
   309  		{name: "FCVTZSDW", argLength: 1, reg: fpgp, asm: "FCVTZSDW"}, // float64 -> int32
   310  		{name: "FCVTZUSW", argLength: 1, reg: fpgp, asm: "FCVTZUSW"}, // float32 -> uint32
   311  		{name: "FCVTZUDW", argLength: 1, reg: fpgp, asm: "FCVTZUDW"}, // float64 -> uint32
   312  		{name: "FCVTZSS", argLength: 1, reg: fpgp, asm: "FCVTZSS"},   // float32 -> int64
   313  		{name: "FCVTZSD", argLength: 1, reg: fpgp, asm: "FCVTZSD"},   // float64 -> int64
   314  		{name: "FCVTZUS", argLength: 1, reg: fpgp, asm: "FCVTZUS"},   // float32 -> uint64
   315  		{name: "FCVTZUD", argLength: 1, reg: fpgp, asm: "FCVTZUD"},   // float64 -> uint64
   316  		{name: "FCVTSD", argLength: 1, reg: fp11, asm: "FCVTSD"},     // float32 -> float64
   317  		{name: "FCVTDS", argLength: 1, reg: fp11, asm: "FCVTDS"},     // float64 -> float32
   318  
   319  		// conditional instructions
   320  		{name: "CSELULT", argLength: 3, reg: gp2flags1, asm: "CSEL"},  // returns arg0 if flags indicates unsigned LT, arg1 otherwise, arg2=flags
   321  		{name: "CSELULT0", argLength: 2, reg: gp1flags1, asm: "CSEL"}, // returns arg0 if flags indicates unsigned LT, 0 otherwise, arg1=flags
   322  
   323  		// function calls
   324  		{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true, symEffect: "None"},                           // call static function aux.(*obj.LSym).  arg0=mem, auxint=argsize, returns mem
   325  		{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R26"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
   326  		{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                         // call fn by pointer.  arg0=codeptr, arg1=mem, auxint=argsize, returns mem
   327  
   328  		// pseudo-ops
   329  		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil.  arg1=mem.
   330  
   331  		{name: "Equal", argLength: 1, reg: readflags},         // bool, true flags encode x==y false otherwise.
   332  		{name: "NotEqual", argLength: 1, reg: readflags},      // bool, true flags encode x!=y false otherwise.
   333  		{name: "LessThan", argLength: 1, reg: readflags},      // bool, true flags encode signed x<y false otherwise.
   334  		{name: "LessEqual", argLength: 1, reg: readflags},     // bool, true flags encode signed x<=y false otherwise.
   335  		{name: "GreaterThan", argLength: 1, reg: readflags},   // bool, true flags encode signed x>y false otherwise.
   336  		{name: "GreaterEqual", argLength: 1, reg: readflags},  // bool, true flags encode signed x>=y false otherwise.
   337  		{name: "LessThanU", argLength: 1, reg: readflags},     // bool, true flags encode unsigned x<y false otherwise.
   338  		{name: "LessEqualU", argLength: 1, reg: readflags},    // bool, true flags encode unsigned x<=y false otherwise.
   339  		{name: "GreaterThanU", argLength: 1, reg: readflags},  // bool, true flags encode unsigned x>y false otherwise.
   340  		{name: "GreaterEqualU", argLength: 1, reg: readflags}, // bool, true flags encode unsigned x>=y false otherwise.
   341  
   342  		// duffzero
   343  		// arg0 = address of memory to zero
   344  		// arg1 = mem
   345  		// auxint = offset into duffzero code to start executing
   346  		// returns mem
   347  		// R16 aka arm64.REGRT1 changed as side effect
   348  		{
   349  			name:      "DUFFZERO",
   350  			aux:       "Int64",
   351  			argLength: 2,
   352  			reg: regInfo{
   353  				inputs:   []regMask{buildReg("R16")},
   354  				clobbers: buildReg("R16 R30"),
   355  			},
   356  			faultOnNilArg0: true,
   357  		},
   358  
   359  		// large zeroing
   360  		// arg0 = address of memory to zero (in R16 aka arm64.REGRT1, changed as side effect)
   361  		// arg1 = address of the last 16-byte unit to zero
   362  		// arg2 = mem
   363  		// returns mem
   364  		//	STP.P	(ZR,ZR), 16(R16)
   365  		//	CMP	Rarg1, R16
   366  		//	BLE	-2(PC)
   367  		// Note: the-end-of-the-memory may be not a valid pointer. it's a problem if it is spilled.
   368  		// the-end-of-the-memory - 16 is with the area to zero, ok to spill.
   369  		{
   370  			name:      "LoweredZero",
   371  			argLength: 3,
   372  			reg: regInfo{
   373  				inputs:   []regMask{buildReg("R16"), gp},
   374  				clobbers: buildReg("R16"),
   375  			},
   376  			clobberFlags:   true,
   377  			faultOnNilArg0: true,
   378  		},
   379  
   380  		// duffcopy
   381  		// arg0 = address of dst memory (in R17 aka arm64.REGRT2, changed as side effect)
   382  		// arg1 = address of src memory (in R16 aka arm64.REGRT1, changed as side effect)
   383  		// arg2 = mem
   384  		// auxint = offset into duffcopy code to start executing
   385  		// returns mem
   386  		// R16, R17 changed as side effect
   387  		{
   388  			name:      "DUFFCOPY",
   389  			aux:       "Int64",
   390  			argLength: 3,
   391  			reg: regInfo{
   392  				inputs:   []regMask{buildReg("R17"), buildReg("R16")},
   393  				clobbers: buildReg("R16 R17 R30"),
   394  			},
   395  			faultOnNilArg0: true,
   396  			faultOnNilArg1: true,
   397  		},
   398  
   399  		// large move
   400  		// arg0 = address of dst memory (in R17 aka arm64.REGRT2, changed as side effect)
   401  		// arg1 = address of src memory (in R16 aka arm64.REGRT1, changed as side effect)
   402  		// arg2 = address of the last element of src
   403  		// arg3 = mem
   404  		// returns mem
   405  		//	MOVD.P	8(R16), Rtmp
   406  		//	MOVD.P	Rtmp, 8(R17)
   407  		//	CMP	Rarg2, R16
   408  		//	BLE	-3(PC)
   409  		// Note: the-end-of-src may be not a valid pointer. it's a problem if it is spilled.
   410  		// the-end-of-src - 8 is within the area to copy, ok to spill.
   411  		{
   412  			name:      "LoweredMove",
   413  			argLength: 4,
   414  			reg: regInfo{
   415  				inputs:   []regMask{buildReg("R17"), buildReg("R16"), gp},
   416  				clobbers: buildReg("R16 R17"),
   417  			},
   418  			clobberFlags:   true,
   419  			faultOnNilArg0: true,
   420  			faultOnNilArg1: true,
   421  		},
   422  
   423  		// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
   424  		// and sorts it to the very beginning of the block to prevent other
   425  		// use of R26 (arm64.REGCTXT, the closure pointer)
   426  		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R26")}}},
   427  
   428  		// LoweredGetCallerSP returns the SP of the caller of the current function.
   429  		{name: "LoweredGetCallerSP", reg: gp01, rematerializeable: true},
   430  
   431  		// MOVDconvert converts between pointers and integers.
   432  		// We have a special op for this so as to not confuse GC
   433  		// (particularly stack maps).  It takes a memory arg so it
   434  		// gets correctly ordered with respect to GC safepoints.
   435  		// arg0=ptr/int arg1=mem, output=int/ptr
   436  		{name: "MOVDconvert", argLength: 2, reg: gp11, asm: "MOVD"},
   437  
   438  		// Constant flag values. For any comparison, there are 5 possible
   439  		// outcomes: the three from the signed total order (<,==,>) and the
   440  		// three from the unsigned total order. The == cases overlap.
   441  		// Note: there's a sixth "unordered" outcome for floating-point
   442  		// comparisons, but we don't use such a beast yet.
   443  		// These ops are for temporary use by rewrite rules. They
   444  		// cannot appear in the generated assembly.
   445  		{name: "FlagEQ"},     // equal
   446  		{name: "FlagLT_ULT"}, // signed < and unsigned <
   447  		{name: "FlagLT_UGT"}, // signed < and unsigned >
   448  		{name: "FlagGT_UGT"}, // signed > and unsigned <
   449  		{name: "FlagGT_ULT"}, // signed > and unsigned >
   450  
   451  		// (InvertFlags (CMP a b)) == (CMP b a)
   452  		// InvertFlags is a pseudo-op which can't appear in assembly output.
   453  		{name: "InvertFlags", argLength: 1}, // reverse direction of arg0
   454  
   455  		// atomic loads.
   456  		// load from arg0. arg1=mem. auxint must be zero.
   457  		// returns <value,memory> so they can be properly ordered with other loads.
   458  		{name: "LDAR", argLength: 2, reg: gpload, asm: "LDAR", faultOnNilArg0: true},
   459  		{name: "LDARW", argLength: 2, reg: gpload, asm: "LDARW", faultOnNilArg0: true},
   460  
   461  		// atomic stores.
   462  		// store arg1 to arg0. arg2=mem. returns memory. auxint must be zero.
   463  		{name: "STLR", argLength: 3, reg: gpstore, asm: "STLR", faultOnNilArg0: true, hasSideEffects: true},
   464  		{name: "STLRW", argLength: 3, reg: gpstore, asm: "STLRW", faultOnNilArg0: true, hasSideEffects: true},
   465  
   466  		// atomic exchange.
   467  		// store arg1 to arg0. arg2=mem. returns <old content of *arg0, memory>. auxint must be zero.
   468  		// LDAXR	(Rarg0), Rout
   469  		// STLXR	Rarg1, (Rarg0), Rtmp
   470  		// CBNZ		Rtmp, -2(PC)
   471  		{name: "LoweredAtomicExchange64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   472  		{name: "LoweredAtomicExchange32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   473  
   474  		// atomic add.
   475  		// *arg0 += arg1. arg2=mem. returns <new content of *arg0, memory>. auxint must be zero.
   476  		// LDAXR	(Rarg0), Rout
   477  		// ADD		Rarg1, Rout
   478  		// STLXR	Rout, (Rarg0), Rtmp
   479  		// CBNZ		Rtmp, -3(PC)
   480  		{name: "LoweredAtomicAdd64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   481  		{name: "LoweredAtomicAdd32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   482  
   483  		// atomic compare and swap.
   484  		// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory. auxint must be zero.
   485  		// if *arg0 == arg1 {
   486  		//   *arg0 = arg2
   487  		//   return (true, memory)
   488  		// } else {
   489  		//   return (false, memory)
   490  		// }
   491  		// LDAXR	(Rarg0), Rtmp
   492  		// CMP		Rarg1, Rtmp
   493  		// BNE		3(PC)
   494  		// STLXR	Rarg2, (Rarg0), Rtmp
   495  		// CBNZ		Rtmp, -4(PC)
   496  		// CSET		EQ, Rout
   497  		{name: "LoweredAtomicCas64", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true},
   498  		{name: "LoweredAtomicCas32", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true},
   499  
   500  		// atomic and/or.
   501  		// *arg0 &= (|=) arg1. arg2=mem. returns memory. auxint must be zero.
   502  		// LDAXRB	(Rarg0), Rtmp
   503  		// AND/OR	Rarg1, Rtmp
   504  		// STLXRB	Rtmp, (Rarg0), Rtmp
   505  		// CBNZ		Rtmp, -3(PC)
   506  		{name: "LoweredAtomicAnd8", argLength: 3, reg: gpstore, asm: "AND", faultOnNilArg0: true, hasSideEffects: true},
   507  		{name: "LoweredAtomicOr8", argLength: 3, reg: gpstore, asm: "ORR", faultOnNilArg0: true, hasSideEffects: true},
   508  	}
   509  
   510  	blocks := []blockData{
   511  		{name: "EQ"},
   512  		{name: "NE"},
   513  		{name: "LT"},
   514  		{name: "LE"},
   515  		{name: "GT"},
   516  		{name: "GE"},
   517  		{name: "ULT"},
   518  		{name: "ULE"},
   519  		{name: "UGT"},
   520  		{name: "UGE"},
   521  		{name: "Z"},    // Control == 0 (take a register instead of flags)
   522  		{name: "NZ"},   // Control != 0
   523  		{name: "ZW"},   // Control == 0, 32-bit
   524  		{name: "NZW"},  // Control != 0, 32-bit
   525  		{name: "TBZ"},  // Control & (1 << Aux.(int64)) == 0
   526  		{name: "TBNZ"}, // Control & (1 << Aux.(int64)) != 0
   527  	}
   528  
   529  	archs = append(archs, arch{
   530  		name:            "ARM64",
   531  		pkg:             "cmd/internal/obj/arm64",
   532  		genfile:         "../../arm64/ssa.go",
   533  		ops:             ops,
   534  		blocks:          blocks,
   535  		regnames:        regNamesARM64,
   536  		gpregmask:       gp,
   537  		fpregmask:       fp,
   538  		framepointerreg: -1, // not used
   539  		linkreg:         int8(num["R30"]),
   540  	})
   541  }