github.com/rakyll/go@v0.0.0-20170216000551-64c02460d703/src/cmd/compile/internal/ssa/gen/S390XOps.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  //  - When doing sub-register operations, we try to write the whole
    16  //    destination register to avoid a partial-register write.
    17  //  - Unused portions of AuxInt (or the Val portion of ValAndOff) are
    18  //    filled by sign-extending the used portion.  Users of AuxInt which interpret
    19  //    AuxInt as unsigned (e.g. shifts) must be careful.
    20  
    21  // Suffixes encode the bit width of various instructions.
    22  // D (double word) = 64 bit (frequently omitted)
    23  // W (word)        = 32 bit
    24  // H (half word)   = 16 bit
    25  // B (byte)        = 8 bit
    26  
    27  // copied from ../../s390x/reg.go
    28  var regNamesS390X = []string{
    29  	"R0",
    30  	"R1",
    31  	"R2",
    32  	"R3",
    33  	"R4",
    34  	"R5",
    35  	"R6",
    36  	"R7",
    37  	"R8",
    38  	"R9",
    39  	"R10",
    40  	"R11",
    41  	"R12",
    42  	"g", // R13
    43  	"R14",
    44  	"SP", // R15
    45  	"F0",
    46  	"F1",
    47  	"F2",
    48  	"F3",
    49  	"F4",
    50  	"F5",
    51  	"F6",
    52  	"F7",
    53  	"F8",
    54  	"F9",
    55  	"F10",
    56  	"F11",
    57  	"F12",
    58  	"F13",
    59  	"F14",
    60  	"F15",
    61  
    62  	//pseudo-registers
    63  	"SB",
    64  }
    65  
    66  func init() {
    67  	// Make map from reg names to reg integers.
    68  	if len(regNamesS390X) > 64 {
    69  		panic("too many registers")
    70  	}
    71  	num := map[string]int{}
    72  	for i, name := range regNamesS390X {
    73  		num[name] = i
    74  	}
    75  	buildReg := func(s string) regMask {
    76  		m := regMask(0)
    77  		for _, r := range strings.Split(s, " ") {
    78  			if n, ok := num[r]; ok {
    79  				m |= regMask(1) << uint(n)
    80  				continue
    81  			}
    82  			panic("register " + r + " not found")
    83  		}
    84  		return m
    85  	}
    86  
    87  	// Common individual register masks
    88  	var (
    89  		sp = buildReg("SP")
    90  		sb = buildReg("SB")
    91  		r0 = buildReg("R0")
    92  
    93  		// R10 and R11 are reserved by the assembler.
    94  		gp   = buildReg("R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14")
    95  		gpsp = gp | sp
    96  
    97  		// R0 is considered to contain the value 0 in address calculations.
    98  		ptr     = gp &^ r0
    99  		ptrsp   = ptr | sp
   100  		ptrspsb = ptrsp | sb
   101  
   102  		fp         = buildReg("F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15")
   103  		callerSave = gp | fp
   104  	)
   105  	// Common slices of register masks
   106  	var (
   107  		gponly = []regMask{gp}
   108  		fponly = []regMask{fp}
   109  	)
   110  
   111  	// Common regInfo
   112  	var (
   113  		gp01   = regInfo{inputs: []regMask{}, outputs: gponly}
   114  		gp11   = regInfo{inputs: []regMask{gp}, outputs: gponly}
   115  		gp11sp = regInfo{inputs: []regMask{gpsp}, outputs: gponly}
   116  		gp21   = regInfo{inputs: []regMask{gp, gp}, outputs: gponly}
   117  		gp21sp = regInfo{inputs: []regMask{gpsp, gp}, outputs: gponly}
   118  
   119  		// R0 evaluates to 0 when used as the number of bits to shift
   120  		// so we need to exclude it from that operand.
   121  		sh21 = regInfo{inputs: []regMask{gp, ptr}, outputs: gponly}
   122  
   123  		addr    = regInfo{inputs: []regMask{sp | sb}, outputs: gponly}
   124  		addridx = regInfo{inputs: []regMask{sp | sb, ptrsp}, outputs: gponly}
   125  
   126  		gp2flags  = regInfo{inputs: []regMask{gpsp, gpsp}}
   127  		gp1flags  = regInfo{inputs: []regMask{gpsp}}
   128  		flagsgp   = regInfo{outputs: gponly}
   129  		gp2flags1 = regInfo{inputs: []regMask{gp, gp}, outputs: gponly}
   130  
   131  		gpload       = regInfo{inputs: []regMask{ptrspsb, 0}, outputs: gponly}
   132  		gploadidx    = regInfo{inputs: []regMask{ptrspsb, ptrsp, 0}, outputs: gponly}
   133  		gpopload     = regInfo{inputs: []regMask{gp, ptrsp, 0}, outputs: gponly}
   134  		gpstore      = regInfo{inputs: []regMask{ptrspsb, gpsp, 0}}
   135  		gpstoreconst = regInfo{inputs: []regMask{ptrspsb, 0}}
   136  		gpstoreidx   = regInfo{inputs: []regMask{ptrsp, ptrsp, gpsp, 0}}
   137  		gpstorebr    = regInfo{inputs: []regMask{ptrsp, gpsp, 0}}
   138  		gpstorelaa   = regInfo{inputs: []regMask{ptrspsb, gpsp, 0}, outputs: gponly}
   139  
   140  		gpmvc = regInfo{inputs: []regMask{ptrsp, ptrsp, 0}}
   141  
   142  		fp01        = regInfo{inputs: []regMask{}, outputs: fponly}
   143  		fp21        = regInfo{inputs: []regMask{fp, fp}, outputs: fponly}
   144  		fp21clobber = regInfo{inputs: []regMask{fp, fp}, outputs: fponly}
   145  		fpgp        = regInfo{inputs: fponly, outputs: gponly}
   146  		gpfp        = regInfo{inputs: gponly, outputs: fponly}
   147  		fp11        = regInfo{inputs: fponly, outputs: fponly}
   148  		fp11clobber = regInfo{inputs: fponly, outputs: fponly}
   149  		fp2flags    = regInfo{inputs: []regMask{fp, fp}}
   150  
   151  		fpload    = regInfo{inputs: []regMask{ptrspsb, 0}, outputs: fponly}
   152  		fploadidx = regInfo{inputs: []regMask{ptrsp, ptrsp, 0}, outputs: fponly}
   153  
   154  		fpstore    = regInfo{inputs: []regMask{ptrspsb, fp, 0}}
   155  		fpstoreidx = regInfo{inputs: []regMask{ptrsp, ptrsp, fp, 0}}
   156  
   157  		// LoweredAtomicCas may overwrite arg1, so force it to R0 for now.
   158  		cas = regInfo{inputs: []regMask{ptrsp, r0, gpsp, 0}, outputs: []regMask{gp, 0}, clobbers: r0}
   159  
   160  		// LoweredAtomicExchange overwrites the output before executing
   161  		// CS{,G}, so the output register must not be the same as the
   162  		// input register. For now we just force the output register to
   163  		// R0.
   164  		exchange = regInfo{inputs: []regMask{ptrsp, gpsp &^ r0, 0}, outputs: []regMask{r0, 0}}
   165  	)
   166  
   167  	var S390Xops = []opData{
   168  		// fp ops
   169  		{name: "FADDS", argLength: 2, reg: fp21clobber, asm: "FADDS", commutative: true, resultInArg0: true, clobberFlags: true}, // fp32 add
   170  		{name: "FADD", argLength: 2, reg: fp21clobber, asm: "FADD", commutative: true, resultInArg0: true, clobberFlags: true},   // fp64 add
   171  		{name: "FSUBS", argLength: 2, reg: fp21clobber, asm: "FSUBS", resultInArg0: true, clobberFlags: true},                    // fp32 sub
   172  		{name: "FSUB", argLength: 2, reg: fp21clobber, asm: "FSUB", resultInArg0: true, clobberFlags: true},                      // fp64 sub
   173  		{name: "FMULS", argLength: 2, reg: fp21, asm: "FMULS", commutative: true, resultInArg0: true},                            // fp32 mul
   174  		{name: "FMUL", argLength: 2, reg: fp21, asm: "FMUL", commutative: true, resultInArg0: true},                              // fp64 mul
   175  		{name: "FDIVS", argLength: 2, reg: fp21, asm: "FDIVS", resultInArg0: true},                                               // fp32 div
   176  		{name: "FDIV", argLength: 2, reg: fp21, asm: "FDIV", resultInArg0: true},                                                 // fp64 div
   177  		{name: "FNEGS", argLength: 1, reg: fp11clobber, asm: "FNEGS", clobberFlags: true},                                        // fp32 neg
   178  		{name: "FNEG", argLength: 1, reg: fp11clobber, asm: "FNEG", clobberFlags: true},                                          // fp64 neg
   179  
   180  		{name: "FMOVSload", argLength: 2, reg: fpload, asm: "FMOVS", aux: "SymOff", faultOnNilArg0: true}, // fp32 load
   181  		{name: "FMOVDload", argLength: 2, reg: fpload, asm: "FMOVD", aux: "SymOff", faultOnNilArg0: true}, // fp64 load
   182  		{name: "FMOVSconst", reg: fp01, asm: "FMOVS", aux: "Float32", rematerializeable: true},            // fp32 constant
   183  		{name: "FMOVDconst", reg: fp01, asm: "FMOVD", aux: "Float64", rematerializeable: true},            // fp64 constant
   184  		{name: "FMOVSloadidx", argLength: 3, reg: fploadidx, asm: "FMOVS", aux: "SymOff"},                 // fp32 load indexed by i
   185  		{name: "FMOVDloadidx", argLength: 3, reg: fploadidx, asm: "FMOVD", aux: "SymOff"},                 // fp64 load indexed by i
   186  
   187  		{name: "FMOVSstore", argLength: 3, reg: fpstore, asm: "FMOVS", aux: "SymOff", faultOnNilArg0: true}, // fp32 store
   188  		{name: "FMOVDstore", argLength: 3, reg: fpstore, asm: "FMOVD", aux: "SymOff", faultOnNilArg0: true}, // fp64 store
   189  		{name: "FMOVSstoreidx", argLength: 4, reg: fpstoreidx, asm: "FMOVS", aux: "SymOff"},                 // fp32 indexed by i store
   190  		{name: "FMOVDstoreidx", argLength: 4, reg: fpstoreidx, asm: "FMOVD", aux: "SymOff"},                 // fp64 indexed by i store
   191  
   192  		// binary ops
   193  		{name: "ADD", argLength: 2, reg: gp21sp, asm: "ADD", commutative: true, clobberFlags: true},                                               // arg0 + arg1
   194  		{name: "ADDW", argLength: 2, reg: gp21sp, asm: "ADDW", commutative: true, clobberFlags: true},                                             // arg0 + arg1
   195  		{name: "ADDconst", argLength: 1, reg: gp11sp, asm: "ADD", aux: "Int64", typ: "UInt64", clobberFlags: true},                                // arg0 + auxint
   196  		{name: "ADDWconst", argLength: 1, reg: gp11sp, asm: "ADDW", aux: "Int32", clobberFlags: true},                                             // arg0 + auxint
   197  		{name: "ADDload", argLength: 3, reg: gpopload, asm: "ADD", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true},   // arg0 + *arg1. arg2=mem
   198  		{name: "ADDWload", argLength: 3, reg: gpopload, asm: "ADDW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true}, // arg0 + *arg1. arg2=mem
   199  
   200  		{name: "SUB", argLength: 2, reg: gp21, asm: "SUB", clobberFlags: true},                                                                    // arg0 - arg1
   201  		{name: "SUBW", argLength: 2, reg: gp21, asm: "SUBW", clobberFlags: true},                                                                  // arg0 - arg1
   202  		{name: "SUBconst", argLength: 1, reg: gp11, asm: "SUB", aux: "Int64", resultInArg0: true, clobberFlags: true},                             // arg0 - auxint
   203  		{name: "SUBWconst", argLength: 1, reg: gp11, asm: "SUBW", aux: "Int32", resultInArg0: true, clobberFlags: true},                           // arg0 - auxint
   204  		{name: "SUBload", argLength: 3, reg: gpopload, asm: "SUB", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true},   // arg0 - *arg1. arg2=mem
   205  		{name: "SUBWload", argLength: 3, reg: gpopload, asm: "SUBW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true}, // arg0 - *arg1. arg2=mem
   206  
   207  		{name: "MULLD", argLength: 2, reg: gp21, asm: "MULLD", typ: "Int64", commutative: true, resultInArg0: true, clobberFlags: true},             // arg0 * arg1
   208  		{name: "MULLW", argLength: 2, reg: gp21, asm: "MULLW", typ: "Int32", commutative: true, resultInArg0: true, clobberFlags: true},             // arg0 * arg1
   209  		{name: "MULLDconst", argLength: 1, reg: gp11, asm: "MULLD", aux: "Int64", typ: "Int64", resultInArg0: true, clobberFlags: true},             // arg0 * auxint
   210  		{name: "MULLWconst", argLength: 1, reg: gp11, asm: "MULLW", aux: "Int32", typ: "Int32", resultInArg0: true, clobberFlags: true},             // arg0 * auxint
   211  		{name: "MULLDload", argLength: 3, reg: gpopload, asm: "MULLD", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true}, // arg0 * *arg1. arg2=mem
   212  		{name: "MULLWload", argLength: 3, reg: gpopload, asm: "MULLW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true}, // arg0 * *arg1. arg2=mem
   213  
   214  		{name: "MULHD", argLength: 2, reg: gp21, asm: "MULHD", typ: "Int64", resultInArg0: true, clobberFlags: true},   // (arg0 * arg1) >> width
   215  		{name: "MULHDU", argLength: 2, reg: gp21, asm: "MULHDU", typ: "Int64", resultInArg0: true, clobberFlags: true}, // (arg0 * arg1) >> width
   216  
   217  		{name: "DIVD", argLength: 2, reg: gp21, asm: "DIVD", resultInArg0: true, clobberFlags: true},   // arg0 / arg1
   218  		{name: "DIVW", argLength: 2, reg: gp21, asm: "DIVW", resultInArg0: true, clobberFlags: true},   // arg0 / arg1
   219  		{name: "DIVDU", argLength: 2, reg: gp21, asm: "DIVDU", resultInArg0: true, clobberFlags: true}, // arg0 / arg1
   220  		{name: "DIVWU", argLength: 2, reg: gp21, asm: "DIVWU", resultInArg0: true, clobberFlags: true}, // arg0 / arg1
   221  
   222  		{name: "MODD", argLength: 2, reg: gp21, asm: "MODD", resultInArg0: true, clobberFlags: true}, // arg0 % arg1
   223  		{name: "MODW", argLength: 2, reg: gp21, asm: "MODW", resultInArg0: true, clobberFlags: true}, // arg0 % arg1
   224  
   225  		{name: "MODDU", argLength: 2, reg: gp21, asm: "MODDU", resultInArg0: true, clobberFlags: true}, // arg0 % arg1
   226  		{name: "MODWU", argLength: 2, reg: gp21, asm: "MODWU", resultInArg0: true, clobberFlags: true}, // arg0 % arg1
   227  
   228  		{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true, clobberFlags: true},                                                 // arg0 & arg1
   229  		{name: "ANDW", argLength: 2, reg: gp21, asm: "ANDW", commutative: true, clobberFlags: true},                                               // arg0 & arg1
   230  		{name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int64", resultInArg0: true, clobberFlags: true},                             // arg0 & auxint
   231  		{name: "ANDWconst", argLength: 1, reg: gp11, asm: "ANDW", aux: "Int32", resultInArg0: true, clobberFlags: true},                           // arg0 & auxint
   232  		{name: "ANDload", argLength: 3, reg: gpopload, asm: "AND", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true},   // arg0 & *arg1. arg2=mem
   233  		{name: "ANDWload", argLength: 3, reg: gpopload, asm: "ANDW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true}, // arg0 & *arg1. arg2=mem
   234  
   235  		{name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true, clobberFlags: true},                                                 // arg0 | arg1
   236  		{name: "ORW", argLength: 2, reg: gp21, asm: "ORW", commutative: true, clobberFlags: true},                                               // arg0 | arg1
   237  		{name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int64", resultInArg0: true, clobberFlags: true},                             // arg0 | auxint
   238  		{name: "ORWconst", argLength: 1, reg: gp11, asm: "ORW", aux: "Int32", resultInArg0: true, clobberFlags: true},                           // arg0 | auxint
   239  		{name: "ORload", argLength: 3, reg: gpopload, asm: "OR", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true},   // arg0 | *arg1. arg2=mem
   240  		{name: "ORWload", argLength: 3, reg: gpopload, asm: "ORW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true}, // arg0 | *arg1. arg2=mem
   241  
   242  		{name: "XOR", argLength: 2, reg: gp21, asm: "XOR", commutative: true, clobberFlags: true},                                                 // arg0 ^ arg1
   243  		{name: "XORW", argLength: 2, reg: gp21, asm: "XORW", commutative: true, clobberFlags: true},                                               // arg0 ^ arg1
   244  		{name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int64", resultInArg0: true, clobberFlags: true},                             // arg0 ^ auxint
   245  		{name: "XORWconst", argLength: 1, reg: gp11, asm: "XORW", aux: "Int32", resultInArg0: true, clobberFlags: true},                           // arg0 ^ auxint
   246  		{name: "XORload", argLength: 3, reg: gpopload, asm: "XOR", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true},   // arg0 ^ *arg1. arg2=mem
   247  		{name: "XORWload", argLength: 3, reg: gpopload, asm: "XORW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true}, // arg0 ^ *arg1. arg2=mem
   248  
   249  		{name: "CMP", argLength: 2, reg: gp2flags, asm: "CMP", typ: "Flags"},   // arg0 compare to arg1
   250  		{name: "CMPW", argLength: 2, reg: gp2flags, asm: "CMPW", typ: "Flags"}, // arg0 compare to arg1
   251  
   252  		{name: "CMPU", argLength: 2, reg: gp2flags, asm: "CMPU", typ: "Flags"},   // arg0 compare to arg1
   253  		{name: "CMPWU", argLength: 2, reg: gp2flags, asm: "CMPWU", typ: "Flags"}, // arg0 compare to arg1
   254  
   255  		{name: "CMPconst", argLength: 1, reg: gp1flags, asm: "CMP", typ: "Flags", aux: "Int64"},     // arg0 compare to auxint
   256  		{name: "CMPWconst", argLength: 1, reg: gp1flags, asm: "CMPW", typ: "Flags", aux: "Int32"},   // arg0 compare to auxint
   257  		{name: "CMPUconst", argLength: 1, reg: gp1flags, asm: "CMPU", typ: "Flags", aux: "Int64"},   // arg0 compare to auxint
   258  		{name: "CMPWUconst", argLength: 1, reg: gp1flags, asm: "CMPWU", typ: "Flags", aux: "Int32"}, // arg0 compare to auxint
   259  
   260  		{name: "FCMPS", argLength: 2, reg: fp2flags, asm: "CEBR", typ: "Flags"}, // arg0 compare to arg1, f32
   261  		{name: "FCMP", argLength: 2, reg: fp2flags, asm: "FCMPU", typ: "Flags"}, // arg0 compare to arg1, f64
   262  
   263  		{name: "SLD", argLength: 2, reg: sh21, asm: "SLD"},                    // arg0 << arg1, shift amount is mod 64
   264  		{name: "SLW", argLength: 2, reg: sh21, asm: "SLW"},                    // arg0 << arg1, shift amount is mod 32
   265  		{name: "SLDconst", argLength: 1, reg: gp11, asm: "SLD", aux: "Int64"}, // arg0 << auxint, shift amount 0-63
   266  		{name: "SLWconst", argLength: 1, reg: gp11, asm: "SLW", aux: "Int32"}, // arg0 << auxint, shift amount 0-31
   267  
   268  		{name: "SRD", argLength: 2, reg: sh21, asm: "SRD"},                    // unsigned arg0 >> arg1, shift amount is mod 64
   269  		{name: "SRW", argLength: 2, reg: sh21, asm: "SRW"},                    // unsigned arg0 >> arg1, shift amount is mod 32
   270  		{name: "SRDconst", argLength: 1, reg: gp11, asm: "SRD", aux: "Int64"}, // unsigned arg0 >> auxint, shift amount 0-63
   271  		{name: "SRWconst", argLength: 1, reg: gp11, asm: "SRW", aux: "Int32"}, // unsigned arg0 >> auxint, shift amount 0-31
   272  
   273  		// Arithmetic shifts clobber flags.
   274  		{name: "SRAD", argLength: 2, reg: sh21, asm: "SRAD", clobberFlags: true},                    // signed arg0 >> arg1, shift amount is mod 64
   275  		{name: "SRAW", argLength: 2, reg: sh21, asm: "SRAW", clobberFlags: true},                    // signed arg0 >> arg1, shift amount is mod 32
   276  		{name: "SRADconst", argLength: 1, reg: gp11, asm: "SRAD", aux: "Int64", clobberFlags: true}, // signed arg0 >> auxint, shift amount 0-63
   277  		{name: "SRAWconst", argLength: 1, reg: gp11, asm: "SRAW", aux: "Int32", clobberFlags: true}, // signed arg0 >> auxint, shift amount 0-31
   278  
   279  		{name: "RLLGconst", argLength: 1, reg: gp11, asm: "RLLG", aux: "Int64"}, // arg0 rotate left auxint, rotate amount 0-63
   280  		{name: "RLLconst", argLength: 1, reg: gp11, asm: "RLL", aux: "Int32"},   // arg0 rotate left auxint, rotate amount 0-31
   281  
   282  		// unary ops
   283  		{name: "NEG", argLength: 1, reg: gp11, asm: "NEG", clobberFlags: true},   // -arg0
   284  		{name: "NEGW", argLength: 1, reg: gp11, asm: "NEGW", clobberFlags: true}, // -arg0
   285  
   286  		{name: "NOT", argLength: 1, reg: gp11, resultInArg0: true, clobberFlags: true},  // ^arg0
   287  		{name: "NOTW", argLength: 1, reg: gp11, resultInArg0: true, clobberFlags: true}, // ^arg0
   288  
   289  		{name: "FSQRT", argLength: 1, reg: fp11, asm: "FSQRT"}, // sqrt(arg0)
   290  
   291  		{name: "SUBEcarrymask", argLength: 1, reg: flagsgp, asm: "SUBE"},  // (int64)(-1) if carry is set, 0 if carry is clear.
   292  		{name: "SUBEWcarrymask", argLength: 1, reg: flagsgp, asm: "SUBE"}, // (int32)(-1) if carry is set, 0 if carry is clear.
   293  		// Note: 32-bits subtraction is not implemented in S390X. Temporarily use SUBE (64-bits).
   294  
   295  		{name: "MOVDEQ", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDEQ"}, // extract == condition from arg0
   296  		{name: "MOVDNE", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDNE"}, // extract != condition from arg0
   297  		{name: "MOVDLT", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDLT"}, // extract signed < condition from arg0
   298  		{name: "MOVDLE", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDLE"}, // extract signed <= condition from arg0
   299  		{name: "MOVDGT", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDGT"}, // extract signed > condition from arg0
   300  		{name: "MOVDGE", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDGE"}, // extract signed >= condition from arg0
   301  
   302  		// Different rules for floating point conditions because
   303  		// any comparison involving a NaN is always false and thus
   304  		// the patterns for inverting conditions cannot be used.
   305  		{name: "MOVDGTnoinv", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDGT"}, // extract floating > condition from arg0
   306  		{name: "MOVDGEnoinv", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDGE"}, // extract floating >= condition from arg0
   307  
   308  		{name: "MOVBreg", argLength: 1, reg: gp11sp, asm: "MOVB", typ: "Int64"},    // sign extend arg0 from int8 to int64
   309  		{name: "MOVBZreg", argLength: 1, reg: gp11sp, asm: "MOVBZ", typ: "UInt64"}, // zero extend arg0 from int8 to int64
   310  		{name: "MOVHreg", argLength: 1, reg: gp11sp, asm: "MOVH", typ: "Int64"},    // sign extend arg0 from int16 to int64
   311  		{name: "MOVHZreg", argLength: 1, reg: gp11sp, asm: "MOVHZ", typ: "UInt64"}, // zero extend arg0 from int16 to int64
   312  		{name: "MOVWreg", argLength: 1, reg: gp11sp, asm: "MOVW", typ: "Int64"},    // sign extend arg0 from int32 to int64
   313  		{name: "MOVWZreg", argLength: 1, reg: gp11sp, asm: "MOVWZ", typ: "UInt64"}, // zero extend arg0 from int32 to int64
   314  		{name: "MOVDreg", argLength: 1, reg: gp11sp, asm: "MOVD"},                  // move from arg0
   315  
   316  		{name: "MOVDnop", argLength: 1, reg: gp11, resultInArg0: true}, // nop, return arg0 in same register
   317  
   318  		{name: "MOVDconst", reg: gp01, asm: "MOVD", typ: "UInt64", aux: "Int64", rematerializeable: true}, // auxint
   319  
   320  		{name: "CFDBRA", argLength: 1, reg: fpgp, asm: "CFDBRA"}, // convert float64 to int32
   321  		{name: "CGDBRA", argLength: 1, reg: fpgp, asm: "CGDBRA"}, // convert float64 to int64
   322  		{name: "CFEBRA", argLength: 1, reg: fpgp, asm: "CFEBRA"}, // convert float32 to int32
   323  		{name: "CGEBRA", argLength: 1, reg: fpgp, asm: "CGEBRA"}, // convert float32 to int64
   324  		{name: "CEFBRA", argLength: 1, reg: gpfp, asm: "CEFBRA"}, // convert int32 to float32
   325  		{name: "CDFBRA", argLength: 1, reg: gpfp, asm: "CDFBRA"}, // convert int32 to float64
   326  		{name: "CEGBRA", argLength: 1, reg: gpfp, asm: "CEGBRA"}, // convert int64 to float32
   327  		{name: "CDGBRA", argLength: 1, reg: gpfp, asm: "CDGBRA"}, // convert int64 to float64
   328  		{name: "LEDBR", argLength: 1, reg: fp11, asm: "LEDBR"},   // convert float64 to float32
   329  		{name: "LDEBR", argLength: 1, reg: fp11, asm: "LDEBR"},   // convert float32 to float64
   330  
   331  		{name: "MOVDaddr", argLength: 1, reg: addr, aux: "SymOff", rematerializeable: true, clobberFlags: true}, // arg0 + auxint + offset encoded in aux
   332  		{name: "MOVDaddridx", argLength: 2, reg: addridx, aux: "SymOff", clobberFlags: true},                    // arg0 + arg1 + auxint + aux
   333  
   334  		// auxint+aux == add auxint and the offset of the symbol in aux (if any) to the effective address
   335  		{name: "MOVBZload", argLength: 2, reg: gpload, asm: "MOVBZ", aux: "SymOff", typ: "UInt8", clobberFlags: true, faultOnNilArg0: true},  // load byte from arg0+auxint+aux. arg1=mem.  Zero extend.
   336  		{name: "MOVBload", argLength: 2, reg: gpload, asm: "MOVB", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true},                  // ditto, sign extend to int64
   337  		{name: "MOVHZload", argLength: 2, reg: gpload, asm: "MOVHZ", aux: "SymOff", typ: "UInt16", clobberFlags: true, faultOnNilArg0: true}, // load 2 bytes from arg0+auxint+aux. arg1=mem.  Zero extend.
   338  		{name: "MOVHload", argLength: 2, reg: gpload, asm: "MOVH", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true},                  // ditto, sign extend to int64
   339  		{name: "MOVWZload", argLength: 2, reg: gpload, asm: "MOVWZ", aux: "SymOff", typ: "UInt32", clobberFlags: true, faultOnNilArg0: true}, // load 4 bytes from arg0+auxint+aux. arg1=mem.  Zero extend.
   340  		{name: "MOVWload", argLength: 2, reg: gpload, asm: "MOVW", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true},                  // ditto, sign extend to int64
   341  		{name: "MOVDload", argLength: 2, reg: gpload, asm: "MOVD", aux: "SymOff", typ: "UInt64", clobberFlags: true, faultOnNilArg0: true},   // load 8 bytes from arg0+auxint+aux. arg1=mem
   342  
   343  		{name: "MOVWBR", argLength: 1, reg: gp11, asm: "MOVWBR"}, // arg0 swap bytes
   344  		{name: "MOVDBR", argLength: 1, reg: gp11, asm: "MOVDBR"}, // arg0 swap bytes
   345  
   346  		{name: "MOVHBRload", argLength: 2, reg: gpload, asm: "MOVHBR", aux: "SymOff", typ: "UInt16", clobberFlags: true, faultOnNilArg0: true}, // load 2 bytes from arg0+auxint+aux. arg1=mem. Reverse bytes.
   347  		{name: "MOVWBRload", argLength: 2, reg: gpload, asm: "MOVWBR", aux: "SymOff", typ: "UInt32", clobberFlags: true, faultOnNilArg0: true}, // load 4 bytes from arg0+auxint+aux. arg1=mem. Reverse bytes.
   348  		{name: "MOVDBRload", argLength: 2, reg: gpload, asm: "MOVDBR", aux: "SymOff", typ: "UInt64", clobberFlags: true, faultOnNilArg0: true}, // load 8 bytes from arg0+auxint+aux. arg1=mem. Reverse bytes.
   349  
   350  		{name: "MOVBstore", argLength: 3, reg: gpstore, asm: "MOVB", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true},       // store byte in arg1 to arg0+auxint+aux. arg2=mem
   351  		{name: "MOVHstore", argLength: 3, reg: gpstore, asm: "MOVH", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true},       // store 2 bytes in arg1 to arg0+auxint+aux. arg2=mem
   352  		{name: "MOVWstore", argLength: 3, reg: gpstore, asm: "MOVW", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true},       // store 4 bytes in arg1 to arg0+auxint+aux. arg2=mem
   353  		{name: "MOVDstore", argLength: 3, reg: gpstore, asm: "MOVD", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true},       // store 8 bytes in arg1 to arg0+auxint+aux. arg2=mem
   354  		{name: "MOVHBRstore", argLength: 3, reg: gpstorebr, asm: "MOVHBR", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true}, // store 2 bytes in arg1 to arg0+auxint+aux. arg2=mem. Reverse bytes.
   355  		{name: "MOVWBRstore", argLength: 3, reg: gpstorebr, asm: "MOVWBR", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true}, // store 4 bytes in arg1 to arg0+auxint+aux. arg2=mem. Reverse bytes.
   356  		{name: "MOVDBRstore", argLength: 3, reg: gpstorebr, asm: "MOVDBR", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true}, // store 8 bytes in arg1 to arg0+auxint+aux. arg2=mem. Reverse bytes.
   357  
   358  		{name: "MVC", argLength: 3, reg: gpmvc, asm: "MVC", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, faultOnNilArg1: true}, // arg0=destptr, arg1=srcptr, arg2=mem, auxint=size,off
   359  
   360  		// indexed loads/stores
   361  		// TODO(mundaym): add sign-extended indexed loads
   362  		{name: "MOVBZloadidx", argLength: 3, reg: gploadidx, asm: "MOVBZ", aux: "SymOff", clobberFlags: true},     // load a byte from arg0+arg1+auxint+aux. arg2=mem
   363  		{name: "MOVHZloadidx", argLength: 3, reg: gploadidx, asm: "MOVHZ", aux: "SymOff", clobberFlags: true},     // load 2 bytes from arg0+arg1+auxint+aux. arg2=mem
   364  		{name: "MOVWZloadidx", argLength: 3, reg: gploadidx, asm: "MOVWZ", aux: "SymOff", clobberFlags: true},     // load 4 bytes from arg0+arg1+auxint+aux. arg2=mem
   365  		{name: "MOVDloadidx", argLength: 3, reg: gploadidx, asm: "MOVD", aux: "SymOff", clobberFlags: true},       // load 8 bytes from arg0+arg1+auxint+aux. arg2=mem
   366  		{name: "MOVHBRloadidx", argLength: 3, reg: gploadidx, asm: "MOVHBR", aux: "SymOff", clobberFlags: true},   // load 2 bytes from arg0+arg1+auxint+aux. arg2=mem. Reverse bytes.
   367  		{name: "MOVWBRloadidx", argLength: 3, reg: gploadidx, asm: "MOVWBR", aux: "SymOff", clobberFlags: true},   // load 4 bytes from arg0+arg1+auxint+aux. arg2=mem. Reverse bytes.
   368  		{name: "MOVDBRloadidx", argLength: 3, reg: gploadidx, asm: "MOVDBR", aux: "SymOff", clobberFlags: true},   // load 8 bytes from arg0+arg1+auxint+aux. arg2=mem. Reverse bytes.
   369  		{name: "MOVBstoreidx", argLength: 4, reg: gpstoreidx, asm: "MOVB", aux: "SymOff", clobberFlags: true},     // store byte in arg2 to arg0+arg1+auxint+aux. arg3=mem
   370  		{name: "MOVHstoreidx", argLength: 4, reg: gpstoreidx, asm: "MOVH", aux: "SymOff", clobberFlags: true},     // store 2 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem
   371  		{name: "MOVWstoreidx", argLength: 4, reg: gpstoreidx, asm: "MOVW", aux: "SymOff", clobberFlags: true},     // store 4 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem
   372  		{name: "MOVDstoreidx", argLength: 4, reg: gpstoreidx, asm: "MOVD", aux: "SymOff", clobberFlags: true},     // store 8 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem
   373  		{name: "MOVHBRstoreidx", argLength: 4, reg: gpstoreidx, asm: "MOVHBR", aux: "SymOff", clobberFlags: true}, // store 2 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem. Reverse bytes.
   374  		{name: "MOVWBRstoreidx", argLength: 4, reg: gpstoreidx, asm: "MOVWBR", aux: "SymOff", clobberFlags: true}, // store 4 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem. Reverse bytes.
   375  		{name: "MOVDBRstoreidx", argLength: 4, reg: gpstoreidx, asm: "MOVDBR", aux: "SymOff", clobberFlags: true}, // store 8 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem. Reverse bytes.
   376  
   377  		// For storeconst ops, the AuxInt field encodes both
   378  		// the value to store and an address offset of the store.
   379  		// Cast AuxInt to a ValAndOff to extract Val and Off fields.
   380  		{name: "MOVBstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVB", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true}, // store low byte of ValAndOff(AuxInt).Val() to arg0+ValAndOff(AuxInt).Off()+aux.  arg1=mem
   381  		{name: "MOVHstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVH", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true}, // store low 2 bytes of ...
   382  		{name: "MOVWstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVW", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true}, // store low 4 bytes of ...
   383  		{name: "MOVDstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVD", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true}, // store 8 bytes of ...
   384  
   385  		{name: "CLEAR", argLength: 2, reg: regInfo{inputs: []regMask{ptr, 0}}, asm: "CLEAR", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true},
   386  
   387  		{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
   388  		{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{ptrsp, buildReg("R12"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
   389  		{name: "CALLdefer", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                                                 // call deferproc.  arg0=mem, auxint=argsize, returns mem
   390  		{name: "CALLgo", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                                                    // call newproc.  arg0=mem, auxint=argsize, returns mem
   391  		{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{ptr}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                         // call fn by pointer.  arg0=codeptr, arg1=mem, auxint=argsize, returns mem
   392  
   393  		// (InvertFlags (CMP a b)) == (CMP b a)
   394  		// InvertFlags is a pseudo-op which can't appear in assembly output.
   395  		{name: "InvertFlags", argLength: 1}, // reverse direction of arg0
   396  
   397  		// Pseudo-ops
   398  		{name: "LoweredGetG", argLength: 1, reg: gp01}, // arg0=mem
   399  		// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
   400  		// and sorts it to the very beginning of the block to prevent other
   401  		// use of R12 (the closure pointer)
   402  		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R12")}}},
   403  		// arg0=ptr,arg1=mem, returns void.  Faults if ptr is nil.
   404  		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{ptrsp}}, clobberFlags: true, nilCheck: true, faultOnNilArg0: true},
   405  
   406  		// MOVDconvert converts between pointers and integers.
   407  		// We have a special op for this so as to not confuse GC
   408  		// (particularly stack maps). It takes a memory arg so it
   409  		// gets correctly ordered with respect to GC safepoints.
   410  		// arg0=ptr/int arg1=mem, output=int/ptr
   411  		{name: "MOVDconvert", argLength: 2, reg: gp11sp, asm: "MOVD"},
   412  
   413  		// Constant flag values. For any comparison, there are 5 possible
   414  		// outcomes: the three from the signed total order (<,==,>) and the
   415  		// three from the unsigned total order. The == cases overlap.
   416  		// Note: there's a sixth "unordered" outcome for floating-point
   417  		// comparisons, but we don't use such a beast yet.
   418  		// These ops are for temporary use by rewrite rules. They
   419  		// cannot appear in the generated assembly.
   420  		{name: "FlagEQ"}, // equal
   421  		{name: "FlagLT"}, // <
   422  		{name: "FlagGT"}, // >
   423  
   424  		// Atomic loads. These are just normal loads but return <value,memory> tuples
   425  		// so they can be properly ordered with other loads.
   426  		// load from arg0+auxint+aux.  arg1=mem.
   427  		{name: "MOVWZatomicload", argLength: 2, reg: gpload, asm: "MOVWZ", aux: "SymOff", faultOnNilArg0: true},
   428  		{name: "MOVDatomicload", argLength: 2, reg: gpload, asm: "MOVD", aux: "SymOff", faultOnNilArg0: true},
   429  
   430  		// Atomic stores. These are just normal stores.
   431  		// store arg1 to arg0+auxint+aux. arg2=mem.
   432  		{name: "MOVWatomicstore", argLength: 3, reg: gpstore, asm: "MOVW", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true},
   433  		{name: "MOVDatomicstore", argLength: 3, reg: gpstore, asm: "MOVD", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true},
   434  
   435  		// Atomic adds.
   436  		// *(arg0+auxint+aux) += arg1.  arg2=mem.
   437  		// Returns a tuple of <old contents of *(arg0+auxint+aux), memory>.
   438  		{name: "LAA", argLength: 3, reg: gpstorelaa, asm: "LAA", typ: "(UInt32,Mem)", aux: "SymOff", faultOnNilArg0: true},
   439  		{name: "LAAG", argLength: 3, reg: gpstorelaa, asm: "LAAG", typ: "(UInt64,Mem)", aux: "SymOff", faultOnNilArg0: true},
   440  		{name: "AddTupleFirst32", argLength: 2}, // arg0=tuple <x,y>.  Returns <x+arg1,y>.
   441  		{name: "AddTupleFirst64", argLength: 2}, // arg0=tuple <x,y>.  Returns <x+arg1,y>.
   442  
   443  		// Compare and swap.
   444  		// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory.
   445  		// if *(arg0+auxint+aux) == arg1 {
   446  		//   *(arg0+auxint+aux) = arg2
   447  		//   return (true, memory)
   448  		// } else {
   449  		//   return (false, memory)
   450  		// }
   451  		// Note that these instructions also return the old value in arg1, but we ignore it.
   452  		// TODO: have these return flags instead of bool.  The current system generates:
   453  		//    CS ...
   454  		//    MOVD  $0, ret
   455  		//    BNE   2(PC)
   456  		//    MOVD  $1, ret
   457  		//    CMPW  ret, $0
   458  		//    BNE ...
   459  		// instead of just
   460  		//    CS ...
   461  		//    BEQ ...
   462  		// but we can't do that because memory-using ops can't generate flags yet
   463  		// (flagalloc wants to move flag-generating instructions around).
   464  		{name: "LoweredAtomicCas32", argLength: 4, reg: cas, asm: "CS", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true},
   465  		{name: "LoweredAtomicCas64", argLength: 4, reg: cas, asm: "CSG", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true},
   466  
   467  		// Lowered atomic swaps, emulated using compare-and-swap.
   468  		// store arg1 to arg0+auxint+aux, arg2=mem.
   469  		{name: "LoweredAtomicExchange32", argLength: 3, reg: exchange, asm: "CS", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true},
   470  		{name: "LoweredAtomicExchange64", argLength: 3, reg: exchange, asm: "CSG", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true},
   471  
   472  		// find leftmost one
   473  		{
   474  			name:         "FLOGR",
   475  			argLength:    1,
   476  			reg:          regInfo{inputs: gponly, outputs: []regMask{buildReg("R0")}, clobbers: buildReg("R1")},
   477  			asm:          "FLOGR",
   478  			typ:          "UInt64",
   479  			clobberFlags: true,
   480  		},
   481  
   482  		// store multiple
   483  		{
   484  			name:           "STMG2",
   485  			argLength:      4,
   486  			reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), 0}},
   487  			aux:            "SymOff",
   488  			typ:            "Mem",
   489  			asm:            "STMG",
   490  			faultOnNilArg0: true,
   491  		},
   492  		{
   493  			name:           "STMG3",
   494  			argLength:      5,
   495  			reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), buildReg("R3"), 0}},
   496  			aux:            "SymOff",
   497  			typ:            "Mem",
   498  			asm:            "STMG",
   499  			faultOnNilArg0: true,
   500  		},
   501  		{
   502  			name:      "STMG4",
   503  			argLength: 6,
   504  			reg: regInfo{inputs: []regMask{
   505  				ptrsp,
   506  				buildReg("R1"),
   507  				buildReg("R2"),
   508  				buildReg("R3"),
   509  				buildReg("R4"),
   510  				0,
   511  			}},
   512  			aux:            "SymOff",
   513  			typ:            "Mem",
   514  			asm:            "STMG",
   515  			faultOnNilArg0: true,
   516  		},
   517  		{
   518  			name:           "STM2",
   519  			argLength:      4,
   520  			reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), 0}},
   521  			aux:            "SymOff",
   522  			typ:            "Mem",
   523  			asm:            "STMY",
   524  			faultOnNilArg0: true,
   525  		},
   526  		{
   527  			name:           "STM3",
   528  			argLength:      5,
   529  			reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), buildReg("R3"), 0}},
   530  			aux:            "SymOff",
   531  			typ:            "Mem",
   532  			asm:            "STMY",
   533  			faultOnNilArg0: true,
   534  		},
   535  		{
   536  			name:      "STM4",
   537  			argLength: 6,
   538  			reg: regInfo{inputs: []regMask{
   539  				ptrsp,
   540  				buildReg("R1"),
   541  				buildReg("R2"),
   542  				buildReg("R3"),
   543  				buildReg("R4"),
   544  				0,
   545  			}},
   546  			aux:            "SymOff",
   547  			typ:            "Mem",
   548  			asm:            "STMY",
   549  			faultOnNilArg0: true,
   550  		},
   551  
   552  		// large move
   553  		// auxint = remaining bytes after loop (rem)
   554  		// arg0 = address of dst memory (in R1, changed as a side effect)
   555  		// arg1 = address of src memory (in R2, changed as a side effect)
   556  		// arg2 = pointer to last address to move in loop + 256
   557  		// arg3 = mem
   558  		// returns mem
   559  		//
   560  		// mvc: MVC  $256, 0(R2), 0(R1)
   561  		//      MOVD $256(R1), R1
   562  		//      MOVD $256(R2), R2
   563  		//      CMP  R2, Rarg2
   564  		//      BNE  mvc
   565  		//	MVC  $rem, 0(R2), 0(R1) // if rem > 0
   566  		{
   567  			name:      "LoweredMove",
   568  			aux:       "Int64",
   569  			argLength: 4,
   570  			reg: regInfo{
   571  				inputs:   []regMask{buildReg("R1"), buildReg("R2"), gpsp},
   572  				clobbers: buildReg("R1 R2"),
   573  			},
   574  			clobberFlags:   true,
   575  			typ:            "Mem",
   576  			faultOnNilArg0: true,
   577  			faultOnNilArg1: true,
   578  		},
   579  
   580  		// large clear
   581  		// auxint = remaining bytes after loop (rem)
   582  		// arg0 = address of dst memory (in R1, changed as a side effect)
   583  		// arg1 = pointer to last address to zero in loop + 256
   584  		// arg2 = mem
   585  		// returns mem
   586  		//
   587  		// clear: CLEAR $256, 0(R1)
   588  		//        MOVD  $256(R1), R1
   589  		//        CMP   R1, Rarg2
   590  		//        BNE   clear
   591  		//	  CLEAR $rem, 0(R1) // if rem > 0
   592  		{
   593  			name:      "LoweredZero",
   594  			aux:       "Int64",
   595  			argLength: 3,
   596  			reg: regInfo{
   597  				inputs:   []regMask{buildReg("R1"), gpsp},
   598  				clobbers: buildReg("R1"),
   599  			},
   600  			clobberFlags:   true,
   601  			typ:            "Mem",
   602  			faultOnNilArg0: true,
   603  		},
   604  	}
   605  
   606  	var S390Xblocks = []blockData{
   607  		{name: "EQ"},
   608  		{name: "NE"},
   609  		{name: "LT"},
   610  		{name: "LE"},
   611  		{name: "GT"},
   612  		{name: "GE"},
   613  		{name: "GTF"}, // FP comparison
   614  		{name: "GEF"}, // FP comparison
   615  	}
   616  
   617  	archs = append(archs, arch{
   618  		name:            "S390X",
   619  		pkg:             "cmd/internal/obj/s390x",
   620  		genfile:         "../../s390x/ssa.go",
   621  		ops:             S390Xops,
   622  		blocks:          S390Xblocks,
   623  		regnames:        regNamesS390X,
   624  		gpregmask:       gp,
   625  		fpregmask:       fp,
   626  		framepointerreg: -1, // not used
   627  		linkreg:         int8(num["R14"]),
   628  	})
   629  }