github.com/rakyll/go@v0.0.0-20170216000551-64c02460d703/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  		gpxchg    = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{gp}}
   148  		gpcas     = regInfo{inputs: []regMask{gpspsbg, gpg, gpg}, outputs: []regMask{gp}}
   149  		fp01      = regInfo{inputs: nil, outputs: []regMask{fp}}
   150  		fp11      = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}}
   151  		fpgp      = regInfo{inputs: []regMask{fp}, outputs: []regMask{gp}}
   152  		gpfp      = regInfo{inputs: []regMask{gp}, outputs: []regMask{fp}}
   153  		fp21      = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{fp}}
   154  		fp2flags  = regInfo{inputs: []regMask{fp, fp}}
   155  		fpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp}}
   156  		fpstore   = regInfo{inputs: []regMask{gpspsbg, fp}}
   157  		readflags = regInfo{inputs: nil, outputs: []regMask{gp}}
   158  	)
   159  	ops := []opData{
   160  		// binary ops
   161  		{name: "ADD", argLength: 2, reg: gp21, asm: "ADD", commutative: true},     // arg0 + arg1
   162  		{name: "ADDconst", argLength: 1, reg: gp11sp, asm: "ADD", aux: "Int64"},   // arg0 + auxInt
   163  		{name: "SUB", argLength: 2, reg: gp21, asm: "SUB"},                        // arg0 - arg1
   164  		{name: "SUBconst", argLength: 1, reg: gp11, asm: "SUB", aux: "Int64"},     // arg0 - auxInt
   165  		{name: "MUL", argLength: 2, reg: gp21, asm: "MUL", commutative: true},     // arg0 * arg1
   166  		{name: "MULW", argLength: 2, reg: gp21, asm: "MULW", commutative: true},   // arg0 * arg1, 32-bit
   167  		{name: "MULH", argLength: 2, reg: gp21, asm: "SMULH", commutative: true},  // (arg0 * arg1) >> 64, signed
   168  		{name: "UMULH", argLength: 2, reg: gp21, asm: "UMULH", commutative: true}, // (arg0 * arg1) >> 64, unsigned
   169  		{name: "MULL", argLength: 2, reg: gp21, asm: "SMULL", commutative: true},  // arg0 * arg1, signed, 32-bit mult results in 64-bit
   170  		{name: "UMULL", argLength: 2, reg: gp21, asm: "UMULL", commutative: true}, // arg0 * arg1, unsigned, 32-bit mult results in 64-bit
   171  		{name: "DIV", argLength: 2, reg: gp21, asm: "SDIV"},                       // arg0 / arg1, signed
   172  		{name: "UDIV", argLength: 2, reg: gp21, asm: "UDIV"},                      // arg0 / arg1, unsighed
   173  		{name: "DIVW", argLength: 2, reg: gp21, asm: "SDIVW"},                     // arg0 / arg1, signed, 32 bit
   174  		{name: "UDIVW", argLength: 2, reg: gp21, asm: "UDIVW"},                    // arg0 / arg1, unsighed, 32 bit
   175  		{name: "MOD", argLength: 2, reg: gp21, asm: "REM"},                        // arg0 % arg1, signed
   176  		{name: "UMOD", argLength: 2, reg: gp21, asm: "UREM"},                      // arg0 % arg1, unsigned
   177  		{name: "MODW", argLength: 2, reg: gp21, asm: "REMW"},                      // arg0 % arg1, signed, 32 bit
   178  		{name: "UMODW", argLength: 2, reg: gp21, asm: "UREMW"},                    // arg0 % arg1, unsigned, 32 bit
   179  
   180  		{name: "FADDS", argLength: 2, reg: fp21, asm: "FADDS", commutative: true}, // arg0 + arg1
   181  		{name: "FADDD", argLength: 2, reg: fp21, asm: "FADDD", commutative: true}, // arg0 + arg1
   182  		{name: "FSUBS", argLength: 2, reg: fp21, asm: "FSUBS"},                    // arg0 - arg1
   183  		{name: "FSUBD", argLength: 2, reg: fp21, asm: "FSUBD"},                    // arg0 - arg1
   184  		{name: "FMULS", argLength: 2, reg: fp21, asm: "FMULS", commutative: true}, // arg0 * arg1
   185  		{name: "FMULD", argLength: 2, reg: fp21, asm: "FMULD", commutative: true}, // arg0 * arg1
   186  		{name: "FDIVS", argLength: 2, reg: fp21, asm: "FDIVS"},                    // arg0 / arg1
   187  		{name: "FDIVD", argLength: 2, reg: fp21, asm: "FDIVD"},                    // arg0 / arg1
   188  
   189  		{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true}, // arg0 & arg1
   190  		{name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int64"}, // arg0 & auxInt
   191  		{name: "OR", argLength: 2, reg: gp21, asm: "ORR", commutative: true},  // arg0 | arg1
   192  		{name: "ORconst", argLength: 1, reg: gp11, asm: "ORR", aux: "Int64"},  // arg0 | auxInt
   193  		{name: "XOR", argLength: 2, reg: gp21, asm: "EOR", commutative: true}, // arg0 ^ arg1
   194  		{name: "XORconst", argLength: 1, reg: gp11, asm: "EOR", aux: "Int64"}, // arg0 ^ auxInt
   195  		{name: "BIC", argLength: 2, reg: gp21, asm: "BIC"},                    // arg0 &^ arg1
   196  		{name: "BICconst", argLength: 1, reg: gp11, asm: "BIC", aux: "Int64"}, // arg0 &^ auxInt
   197  
   198  		// unary ops
   199  		{name: "MVN", argLength: 1, reg: gp11, asm: "MVN"},       // ^arg0
   200  		{name: "NEG", argLength: 1, reg: gp11, asm: "NEG"},       // -arg0
   201  		{name: "FNEGS", argLength: 1, reg: fp11, asm: "FNEGS"},   // -arg0, float32
   202  		{name: "FNEGD", argLength: 1, reg: fp11, asm: "FNEGD"},   // -arg0, float64
   203  		{name: "FSQRTD", argLength: 1, reg: fp11, asm: "FSQRTD"}, // sqrt(arg0), float64
   204  		{name: "REV", argLength: 1, reg: gp11, asm: "REV"},       // byte reverse, 64-bit
   205  		{name: "REVW", argLength: 1, reg: gp11, asm: "REVW"},     // byte reverse, 32-bit
   206  		{name: "REV16W", argLength: 1, reg: gp11, asm: "REV16W"}, // byte reverse in each 16-bit halfword, 32-bit
   207  		{name: "RBIT", argLength: 1, reg: gp11, asm: "RBIT"},     // bit reverse, 64-bit
   208  		{name: "RBITW", argLength: 1, reg: gp11, asm: "RBITW"},   // bit reverse, 32-bit
   209  		{name: "CLZ", argLength: 1, reg: gp11, asm: "CLZ"},       // count leading zero, 64-bit
   210  		{name: "CLZW", argLength: 1, reg: gp11, asm: "CLZW"},     // count leading zero, 32-bit
   211  
   212  		// shifts
   213  		{name: "SLL", argLength: 2, reg: gp21, asm: "LSL"},                      // arg0 << arg1, shift amount is mod 64
   214  		{name: "SLLconst", argLength: 1, reg: gp11, asm: "LSL", aux: "Int64"},   // arg0 << auxInt
   215  		{name: "SRL", argLength: 2, reg: gp21, asm: "LSR"},                      // arg0 >> arg1, unsigned, shift amount is mod 64
   216  		{name: "SRLconst", argLength: 1, reg: gp11, asm: "LSR", aux: "Int64"},   // arg0 >> auxInt, unsigned
   217  		{name: "SRA", argLength: 2, reg: gp21, asm: "ASR"},                      // arg0 >> arg1, signed, shift amount is mod 64
   218  		{name: "SRAconst", argLength: 1, reg: gp11, asm: "ASR", aux: "Int64"},   // arg0 >> auxInt, signed
   219  		{name: "RORconst", argLength: 1, reg: gp11, asm: "ROR", aux: "Int64"},   // arg0 right rotate by auxInt bits
   220  		{name: "RORWconst", argLength: 1, reg: gp11, asm: "RORW", aux: "Int64"}, // uint32(arg0) right rotate by auxInt bits
   221  
   222  		// comparisons
   223  		{name: "CMP", argLength: 2, reg: gp2flags, asm: "CMP", typ: "Flags"},                      // arg0 compare to arg1
   224  		{name: "CMPconst", argLength: 1, reg: gp1flags, asm: "CMP", aux: "Int64", typ: "Flags"},   // arg0 compare to auxInt
   225  		{name: "CMPW", argLength: 2, reg: gp2flags, asm: "CMPW", typ: "Flags"},                    // arg0 compare to arg1, 32 bit
   226  		{name: "CMPWconst", argLength: 1, reg: gp1flags, asm: "CMPW", aux: "Int32", typ: "Flags"}, // arg0 compare to auxInt, 32 bit
   227  		{name: "CMN", argLength: 2, reg: gp2flags, asm: "CMN", typ: "Flags"},                      // arg0 compare to -arg1
   228  		{name: "CMNconst", argLength: 1, reg: gp1flags, asm: "CMN", aux: "Int64", typ: "Flags"},   // arg0 compare to -auxInt
   229  		{name: "CMNW", argLength: 2, reg: gp2flags, asm: "CMNW", typ: "Flags"},                    // arg0 compare to -arg1, 32 bit
   230  		{name: "CMNWconst", argLength: 1, reg: gp1flags, asm: "CMNW", aux: "Int32", typ: "Flags"}, // arg0 compare to -auxInt, 32 bit
   231  		{name: "FCMPS", argLength: 2, reg: fp2flags, asm: "FCMPS", typ: "Flags"},                  // arg0 compare to arg1, float32
   232  		{name: "FCMPD", argLength: 2, reg: fp2flags, asm: "FCMPD", typ: "Flags"},                  // arg0 compare to arg1, float64
   233  
   234  		// shifted ops
   235  		{name: "ADDshiftLL", argLength: 2, reg: gp21, asm: "ADD", aux: "Int64"},                   // arg0 + arg1<<auxInt
   236  		{name: "ADDshiftRL", argLength: 2, reg: gp21, asm: "ADD", aux: "Int64"},                   // arg0 + arg1>>auxInt, unsigned shift
   237  		{name: "ADDshiftRA", argLength: 2, reg: gp21, asm: "ADD", aux: "Int64"},                   // arg0 + arg1>>auxInt, signed shift
   238  		{name: "SUBshiftLL", argLength: 2, reg: gp21, asm: "SUB", aux: "Int64"},                   // arg0 - arg1<<auxInt
   239  		{name: "SUBshiftRL", argLength: 2, reg: gp21, asm: "SUB", aux: "Int64"},                   // arg0 - arg1>>auxInt, unsigned shift
   240  		{name: "SUBshiftRA", argLength: 2, reg: gp21, asm: "SUB", aux: "Int64"},                   // arg0 - arg1>>auxInt, signed shift
   241  		{name: "ANDshiftLL", argLength: 2, reg: gp21, asm: "AND", aux: "Int64"},                   // arg0 & (arg1<<auxInt)
   242  		{name: "ANDshiftRL", argLength: 2, reg: gp21, asm: "AND", aux: "Int64"},                   // arg0 & (arg1>>auxInt), unsigned shift
   243  		{name: "ANDshiftRA", argLength: 2, reg: gp21, asm: "AND", aux: "Int64"},                   // arg0 & (arg1>>auxInt), signed shift
   244  		{name: "ORshiftLL", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64"},                    // arg0 | arg1<<auxInt
   245  		{name: "ORshiftRL", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64"},                    // arg0 | arg1>>auxInt, unsigned shift
   246  		{name: "ORshiftRA", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64"},                    // arg0 | arg1>>auxInt, signed shift
   247  		{name: "XORshiftLL", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64"},                   // arg0 ^ arg1<<auxInt
   248  		{name: "XORshiftRL", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64"},                   // arg0 ^ arg1>>auxInt, unsigned shift
   249  		{name: "XORshiftRA", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64"},                   // arg0 ^ arg1>>auxInt, signed shift
   250  		{name: "BICshiftLL", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64"},                   // arg0 &^ (arg1<<auxInt)
   251  		{name: "BICshiftRL", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64"},                   // arg0 &^ (arg1>>auxInt), unsigned shift
   252  		{name: "BICshiftRA", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64"},                   // arg0 &^ (arg1>>auxInt), signed shift
   253  		{name: "CMPshiftLL", argLength: 2, reg: gp2flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to arg1<<auxInt
   254  		{name: "CMPshiftRL", argLength: 2, reg: gp2flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to arg1>>auxInt, unsigned shift
   255  		{name: "CMPshiftRA", argLength: 2, reg: gp2flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to arg1>>auxInt, signed shift
   256  
   257  		// moves
   258  		{name: "MOVDconst", argLength: 0, reg: gp01, aux: "Int64", asm: "MOVD", typ: "UInt64", rematerializeable: true},      // 32 low bits of auxint
   259  		{name: "FMOVSconst", argLength: 0, reg: fp01, aux: "Float64", asm: "FMOVS", typ: "Float32", rematerializeable: true}, // auxint as 64-bit float, convert to 32-bit float
   260  		{name: "FMOVDconst", argLength: 0, reg: fp01, aux: "Float64", asm: "FMOVD", typ: "Float64", rematerializeable: true}, // auxint as 64-bit float
   261  
   262  		{name: "MOVDaddr", argLength: 1, reg: regInfo{inputs: []regMask{buildReg("SP") | buildReg("SB")}, outputs: []regMask{gp}}, aux: "SymOff", asm: "MOVD", rematerializeable: true}, // arg0 + auxInt + aux.(*gc.Sym), arg0=SP/SB
   263  
   264  		{name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true},      // load from arg0 + auxInt + aux.  arg1=mem.
   265  		{name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true},   // load from arg0 + auxInt + aux.  arg1=mem.
   266  		{name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true},     // load from arg0 + auxInt + aux.  arg1=mem.
   267  		{name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true},  // load from arg0 + auxInt + aux.  arg1=mem.
   268  		{name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "Int32", faultOnNilArg0: true},     // load from arg0 + auxInt + aux.  arg1=mem.
   269  		{name: "MOVWUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVWU", typ: "UInt32", faultOnNilArg0: true},  // load from arg0 + auxInt + aux.  arg1=mem.
   270  		{name: "MOVDload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVD", typ: "UInt64", faultOnNilArg0: true},    // load from arg0 + auxInt + aux.  arg1=mem.
   271  		{name: "FMOVSload", argLength: 2, reg: fpload, aux: "SymOff", asm: "FMOVS", typ: "Float32", faultOnNilArg0: true}, // load from arg0 + auxInt + aux.  arg1=mem.
   272  		{name: "FMOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "FMOVD", typ: "Float64", faultOnNilArg0: true}, // load from arg0 + auxInt + aux.  arg1=mem.
   273  
   274  		{name: "MOVBstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVB", typ: "Mem", faultOnNilArg0: true},   // store 1 byte of arg1 to arg0 + auxInt + aux.  arg2=mem.
   275  		{name: "MOVHstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVH", typ: "Mem", faultOnNilArg0: true},   // store 2 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   276  		{name: "MOVWstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVW", typ: "Mem", faultOnNilArg0: true},   // store 4 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   277  		{name: "MOVDstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVD", typ: "Mem", faultOnNilArg0: true},   // store 8 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   278  		{name: "FMOVSstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "FMOVS", typ: "Mem", faultOnNilArg0: true}, // store 4 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   279  		{name: "FMOVDstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "FMOVD", typ: "Mem", faultOnNilArg0: true}, // store 8 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   280  
   281  		{name: "MOVBstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVB", typ: "Mem", faultOnNilArg0: true}, // store 1 byte of zero to arg0 + auxInt + aux.  arg1=mem.
   282  		{name: "MOVHstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVH", typ: "Mem", faultOnNilArg0: true}, // store 2 bytes of zero to arg0 + auxInt + aux.  arg1=mem.
   283  		{name: "MOVWstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVW", typ: "Mem", faultOnNilArg0: true}, // store 4 bytes of zero to arg0 + auxInt + aux.  arg1=mem.
   284  		{name: "MOVDstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVD", typ: "Mem", faultOnNilArg0: true}, // store 8 bytes of zero to arg0 + auxInt + aux.  ar12=mem.
   285  
   286  		// conversions
   287  		{name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVB"},   // move from arg0, sign-extended from byte
   288  		{name: "MOVBUreg", argLength: 1, reg: gp11, asm: "MOVBU"}, // move from arg0, unsign-extended from byte
   289  		{name: "MOVHreg", argLength: 1, reg: gp11, asm: "MOVH"},   // move from arg0, sign-extended from half
   290  		{name: "MOVHUreg", argLength: 1, reg: gp11, asm: "MOVHU"}, // move from arg0, unsign-extended from half
   291  		{name: "MOVWreg", argLength: 1, reg: gp11, asm: "MOVW"},   // move from arg0, sign-extended from word
   292  		{name: "MOVWUreg", argLength: 1, reg: gp11, asm: "MOVWU"}, // move from arg0, unsign-extended from word
   293  		{name: "MOVDreg", argLength: 1, reg: gp11, asm: "MOVD"},   // move from arg0
   294  
   295  		{name: "MOVDnop", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}, resultInArg0: true}, // nop, return arg0 in same register
   296  
   297  		{name: "SCVTFWS", argLength: 1, reg: gpfp, asm: "SCVTFWS"},   // int32 -> float32
   298  		{name: "SCVTFWD", argLength: 1, reg: gpfp, asm: "SCVTFWD"},   // int32 -> float64
   299  		{name: "UCVTFWS", argLength: 1, reg: gpfp, asm: "UCVTFWS"},   // uint32 -> float32
   300  		{name: "UCVTFWD", argLength: 1, reg: gpfp, asm: "UCVTFWD"},   // uint32 -> float64
   301  		{name: "SCVTFS", argLength: 1, reg: gpfp, asm: "SCVTFS"},     // int64 -> float32
   302  		{name: "SCVTFD", argLength: 1, reg: gpfp, asm: "SCVTFD"},     // int64 -> float64
   303  		{name: "UCVTFS", argLength: 1, reg: gpfp, asm: "UCVTFS"},     // uint64 -> float32
   304  		{name: "UCVTFD", argLength: 1, reg: gpfp, asm: "UCVTFD"},     // uint64 -> float64
   305  		{name: "FCVTZSSW", argLength: 1, reg: fpgp, asm: "FCVTZSSW"}, // float32 -> int32
   306  		{name: "FCVTZSDW", argLength: 1, reg: fpgp, asm: "FCVTZSDW"}, // float64 -> int32
   307  		{name: "FCVTZUSW", argLength: 1, reg: fpgp, asm: "FCVTZUSW"}, // float32 -> uint32
   308  		{name: "FCVTZUDW", argLength: 1, reg: fpgp, asm: "FCVTZUDW"}, // float64 -> uint32
   309  		{name: "FCVTZSS", argLength: 1, reg: fpgp, asm: "FCVTZSS"},   // float32 -> int64
   310  		{name: "FCVTZSD", argLength: 1, reg: fpgp, asm: "FCVTZSD"},   // float64 -> int64
   311  		{name: "FCVTZUS", argLength: 1, reg: fpgp, asm: "FCVTZUS"},   // float32 -> uint64
   312  		{name: "FCVTZUD", argLength: 1, reg: fpgp, asm: "FCVTZUD"},   // float64 -> uint64
   313  		{name: "FCVTSD", argLength: 1, reg: fp11, asm: "FCVTSD"},     // float32 -> float64
   314  		{name: "FCVTDS", argLength: 1, reg: fp11, asm: "FCVTDS"},     // float64 -> float32
   315  
   316  		// conditional instructions
   317  		{name: "CSELULT", argLength: 3, reg: gp2flags1, asm: "CSEL"},  // returns arg0 if flags indicates unsigned LT, arg1 otherwise, arg2=flags
   318  		{name: "CSELULT0", argLength: 2, reg: gp1flags1, asm: "CSEL"}, // returns arg0 if flags indicates unsigned LT, 0 otherwise, arg1=flags
   319  
   320  		// function calls
   321  		{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true},                                              // call static function aux.(*gc.Sym).  arg0=mem, auxint=argsize, returns mem
   322  		{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
   323  		{name: "CALLdefer", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                                                // call deferproc.  arg0=mem, auxint=argsize, returns mem
   324  		{name: "CALLgo", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                                                   // call newproc.  arg0=mem, auxint=argsize, returns mem
   325  		{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
   326  
   327  		// pseudo-ops
   328  		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil.  arg1=mem.
   329  
   330  		{name: "Equal", argLength: 1, reg: readflags},         // bool, true flags encode x==y false otherwise.
   331  		{name: "NotEqual", argLength: 1, reg: readflags},      // bool, true flags encode x!=y false otherwise.
   332  		{name: "LessThan", argLength: 1, reg: readflags},      // bool, true flags encode signed x<y false otherwise.
   333  		{name: "LessEqual", argLength: 1, reg: readflags},     // bool, true flags encode signed x<=y false otherwise.
   334  		{name: "GreaterThan", argLength: 1, reg: readflags},   // bool, true flags encode signed x>y false otherwise.
   335  		{name: "GreaterEqual", argLength: 1, reg: readflags},  // bool, true flags encode signed x>=y false otherwise.
   336  		{name: "LessThanU", argLength: 1, reg: readflags},     // bool, true flags encode unsigned x<y false otherwise.
   337  		{name: "LessEqualU", argLength: 1, reg: readflags},    // bool, true flags encode unsigned x<=y false otherwise.
   338  		{name: "GreaterThanU", argLength: 1, reg: readflags},  // bool, true flags encode unsigned x>y false otherwise.
   339  		{name: "GreaterEqualU", argLength: 1, reg: readflags}, // bool, true flags encode unsigned x>=y false otherwise.
   340  
   341  		// duffzero
   342  		// arg0 = address of memory to zero
   343  		// arg1 = mem
   344  		// auxint = offset into duffzero code to start executing
   345  		// returns mem
   346  		// R16 aka arm64.REGRT1 changed as side effect
   347  		{
   348  			name:      "DUFFZERO",
   349  			aux:       "Int64",
   350  			argLength: 2,
   351  			reg: regInfo{
   352  				inputs:   []regMask{gp},
   353  				clobbers: buildReg("R16 R30"),
   354  			},
   355  			faultOnNilArg0: true,
   356  		},
   357  
   358  		// large zeroing
   359  		// arg0 = address of memory to zero (in R16 aka arm64.REGRT1, changed as side effect)
   360  		// arg1 = address of the last element to zero
   361  		// arg2 = mem
   362  		// returns mem
   363  		//	MOVD.P	ZR, 8(R16)
   364  		//	CMP	Rarg1, R16
   365  		//	BLE	-2(PC)
   366  		// Note: the-end-of-the-memory may be not a valid pointer. it's a problem if it is spilled.
   367  		// the-end-of-the-memory - 8 is with the area to zero, ok to spill.
   368  		{
   369  			name:      "LoweredZero",
   370  			argLength: 3,
   371  			reg: regInfo{
   372  				inputs:   []regMask{buildReg("R16"), gp},
   373  				clobbers: buildReg("R16"),
   374  			},
   375  			clobberFlags:   true,
   376  			faultOnNilArg0: true,
   377  		},
   378  
   379  		// duffcopy
   380  		// arg0 = address of dst memory (in R17 aka arm64.REGRT2, changed as side effect)
   381  		// arg1 = address of src memory (in R16 aka arm64.REGRT1, changed as side effect)
   382  		// arg2 = mem
   383  		// auxint = offset into duffcopy code to start executing
   384  		// returns mem
   385  		// R16, R17 changed as side effect
   386  		{
   387  			name:      "DUFFCOPY",
   388  			aux:       "Int64",
   389  			argLength: 3,
   390  			reg: regInfo{
   391  				inputs:   []regMask{buildReg("R17"), buildReg("R16")},
   392  				clobbers: buildReg("R16 R17 R30"),
   393  			},
   394  			faultOnNilArg0: true,
   395  			faultOnNilArg1: true,
   396  		},
   397  
   398  		// large move
   399  		// arg0 = address of dst memory (in R17 aka arm64.REGRT2, changed as side effect)
   400  		// arg1 = address of src memory (in R16 aka arm64.REGRT1, changed as side effect)
   401  		// arg2 = address of the last element of src
   402  		// arg3 = mem
   403  		// returns mem
   404  		//	MOVD.P	8(R16), Rtmp
   405  		//	MOVD.P	Rtmp, 8(R17)
   406  		//	CMP	Rarg2, R16
   407  		//	BLE	-3(PC)
   408  		// Note: the-end-of-src may be not a valid pointer. it's a problem if it is spilled.
   409  		// the-end-of-src - 8 is within the area to copy, ok to spill.
   410  		{
   411  			name:      "LoweredMove",
   412  			argLength: 4,
   413  			reg: regInfo{
   414  				inputs:   []regMask{buildReg("R17"), buildReg("R16"), gp},
   415  				clobbers: buildReg("R16 R17"),
   416  			},
   417  			clobberFlags:   true,
   418  			faultOnNilArg0: true,
   419  			faultOnNilArg1: true,
   420  		},
   421  
   422  		// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
   423  		// and sorts it to the very beginning of the block to prevent other
   424  		// use of R26 (arm64.REGCTXT, the closure pointer)
   425  		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R26")}}},
   426  
   427  		// MOVDconvert converts between pointers and integers.
   428  		// We have a special op for this so as to not confuse GC
   429  		// (particularly stack maps).  It takes a memory arg so it
   430  		// gets correctly ordered with respect to GC safepoints.
   431  		// arg0=ptr/int arg1=mem, output=int/ptr
   432  		{name: "MOVDconvert", argLength: 2, reg: gp11, asm: "MOVD"},
   433  
   434  		// Constant flag values. For any comparison, there are 5 possible
   435  		// outcomes: the three from the signed total order (<,==,>) and the
   436  		// three from the unsigned total order. The == cases overlap.
   437  		// Note: there's a sixth "unordered" outcome for floating-point
   438  		// comparisons, but we don't use such a beast yet.
   439  		// These ops are for temporary use by rewrite rules. They
   440  		// cannot appear in the generated assembly.
   441  		{name: "FlagEQ"},     // equal
   442  		{name: "FlagLT_ULT"}, // signed < and unsigned <
   443  		{name: "FlagLT_UGT"}, // signed < and unsigned >
   444  		{name: "FlagGT_UGT"}, // signed > and unsigned <
   445  		{name: "FlagGT_ULT"}, // signed > and unsigned >
   446  
   447  		// (InvertFlags (CMP a b)) == (CMP b a)
   448  		// InvertFlags is a pseudo-op which can't appear in assembly output.
   449  		{name: "InvertFlags", argLength: 1}, // reverse direction of arg0
   450  
   451  		// atomic loads.
   452  		// load from arg0. arg1=mem. auxint must be zero.
   453  		// returns <value,memory> so they can be properly ordered with other loads.
   454  		{name: "LDAR", argLength: 2, reg: gpload, asm: "LDAR", faultOnNilArg0: true},
   455  		{name: "LDARW", argLength: 2, reg: gpload, asm: "LDARW", faultOnNilArg0: true},
   456  
   457  		// atomic stores.
   458  		// store arg1 to arg0. arg2=mem. returns memory. auxint must be zero.
   459  		{name: "STLR", argLength: 3, reg: gpstore, asm: "STLR", faultOnNilArg0: true},
   460  		{name: "STLRW", argLength: 3, reg: gpstore, asm: "STLRW", faultOnNilArg0: true},
   461  
   462  		// atomic exchange.
   463  		// store arg1 to arg0. arg2=mem. returns <old content of *arg0, memory>. auxint must be zero.
   464  		// LDAXR	(Rarg0), Rout
   465  		// STLXR	Rarg1, (Rarg0), Rtmp
   466  		// CBNZ		Rtmp, -2(PC)
   467  		{name: "LoweredAtomicExchange64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true},
   468  		{name: "LoweredAtomicExchange32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true},
   469  
   470  		// atomic add.
   471  		// *arg0 += arg1. arg2=mem. returns <new content of *arg0, memory>. auxint must be zero.
   472  		// LDAXR	(Rarg0), Rout
   473  		// ADD		Rarg1, Rout
   474  		// STLXR	Rout, (Rarg0), Rtmp
   475  		// CBNZ		Rtmp, -3(PC)
   476  		{name: "LoweredAtomicAdd64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true},
   477  		{name: "LoweredAtomicAdd32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true},
   478  
   479  		// atomic compare and swap.
   480  		// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory. auxint must be zero.
   481  		// if *arg0 == arg1 {
   482  		//   *arg0 = arg2
   483  		//   return (true, memory)
   484  		// } else {
   485  		//   return (false, memory)
   486  		// }
   487  		// LDAXR	(Rarg0), Rtmp
   488  		// CMP		Rarg1, Rtmp
   489  		// BNE		3(PC)
   490  		// STLXR	Rarg2, (Rarg0), Rtmp
   491  		// CBNZ		Rtmp, -4(PC)
   492  		// CSET		EQ, Rout
   493  		{name: "LoweredAtomicCas64", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true},
   494  		{name: "LoweredAtomicCas32", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true},
   495  
   496  		// atomic and/or.
   497  		// *arg0 &= (|=) arg1. arg2=mem. returns memory. auxint must be zero.
   498  		// LDAXRB	(Rarg0), Rtmp
   499  		// AND/OR	Rarg1, Rtmp
   500  		// STLXRB	Rtmp, (Rarg0), Rtmp
   501  		// CBNZ		Rtmp, -3(PC)
   502  		{name: "LoweredAtomicAnd8", argLength: 3, reg: gpstore, asm: "AND", faultOnNilArg0: true},
   503  		{name: "LoweredAtomicOr8", argLength: 3, reg: gpstore, asm: "ORR", faultOnNilArg0: true},
   504  	}
   505  
   506  	blocks := []blockData{
   507  		{name: "EQ"},
   508  		{name: "NE"},
   509  		{name: "LT"},
   510  		{name: "LE"},
   511  		{name: "GT"},
   512  		{name: "GE"},
   513  		{name: "ULT"},
   514  		{name: "ULE"},
   515  		{name: "UGT"},
   516  		{name: "UGE"},
   517  		{name: "Z"},   // Control == 0 (take a register instead of flags)
   518  		{name: "NZ"},  // Control != 0
   519  		{name: "ZW"},  // Control == 0, 32-bit
   520  		{name: "NZW"}, // Control != 0, 32-bit
   521  	}
   522  
   523  	archs = append(archs, arch{
   524  		name:            "ARM64",
   525  		pkg:             "cmd/internal/obj/arm64",
   526  		genfile:         "../../arm64/ssa.go",
   527  		ops:             ops,
   528  		blocks:          blocks,
   529  		regnames:        regNamesARM64,
   530  		gpregmask:       gp,
   531  		fpregmask:       fp,
   532  		framepointerreg: -1, // not used
   533  		linkreg:         int8(num["R30"]),
   534  	})
   535  }