github.com/FenixAra/go@v0.0.0-20170127160404-96ea0918e670/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  
   315  		{name: "MOVDconst", reg: gp01, asm: "MOVD", typ: "UInt64", aux: "Int64", rematerializeable: true}, // auxint
   316  
   317  		{name: "CFDBRA", argLength: 1, reg: fpgp, asm: "CFDBRA"}, // convert float64 to int32
   318  		{name: "CGDBRA", argLength: 1, reg: fpgp, asm: "CGDBRA"}, // convert float64 to int64
   319  		{name: "CFEBRA", argLength: 1, reg: fpgp, asm: "CFEBRA"}, // convert float32 to int32
   320  		{name: "CGEBRA", argLength: 1, reg: fpgp, asm: "CGEBRA"}, // convert float32 to int64
   321  		{name: "CEFBRA", argLength: 1, reg: gpfp, asm: "CEFBRA"}, // convert int32 to float32
   322  		{name: "CDFBRA", argLength: 1, reg: gpfp, asm: "CDFBRA"}, // convert int32 to float64
   323  		{name: "CEGBRA", argLength: 1, reg: gpfp, asm: "CEGBRA"}, // convert int64 to float32
   324  		{name: "CDGBRA", argLength: 1, reg: gpfp, asm: "CDGBRA"}, // convert int64 to float64
   325  		{name: "LEDBR", argLength: 1, reg: fp11, asm: "LEDBR"},   // convert float64 to float32
   326  		{name: "LDEBR", argLength: 1, reg: fp11, asm: "LDEBR"},   // convert float32 to float64
   327  
   328  		{name: "MOVDaddr", argLength: 1, reg: addr, aux: "SymOff", rematerializeable: true, clobberFlags: true}, // arg0 + auxint + offset encoded in aux
   329  		{name: "MOVDaddridx", argLength: 2, reg: addridx, aux: "SymOff", clobberFlags: true},                    // arg0 + arg1 + auxint + aux
   330  
   331  		// auxint+aux == add auxint and the offset of the symbol in aux (if any) to the effective address
   332  		{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.
   333  		{name: "MOVBload", argLength: 2, reg: gpload, asm: "MOVB", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true},                  // ditto, sign extend to int64
   334  		{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.
   335  		{name: "MOVHload", argLength: 2, reg: gpload, asm: "MOVH", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true},                  // ditto, sign extend to int64
   336  		{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.
   337  		{name: "MOVWload", argLength: 2, reg: gpload, asm: "MOVW", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true},                  // ditto, sign extend to int64
   338  		{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
   339  
   340  		{name: "MOVWBR", argLength: 1, reg: gp11, asm: "MOVWBR"}, // arg0 swap bytes
   341  		{name: "MOVDBR", argLength: 1, reg: gp11, asm: "MOVDBR"}, // arg0 swap bytes
   342  
   343  		{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.
   344  		{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.
   345  		{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.
   346  
   347  		{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
   348  		{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
   349  		{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
   350  		{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
   351  		{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.
   352  		{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.
   353  		{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.
   354  
   355  		{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
   356  
   357  		// indexed loads/stores
   358  		// TODO(mundaym): add sign-extended indexed loads
   359  		{name: "MOVBZloadidx", argLength: 3, reg: gploadidx, asm: "MOVBZ", aux: "SymOff", clobberFlags: true},     // load a byte from arg0+arg1+auxint+aux. arg2=mem
   360  		{name: "MOVHZloadidx", argLength: 3, reg: gploadidx, asm: "MOVHZ", aux: "SymOff", clobberFlags: true},     // load 2 bytes from arg0+arg1+auxint+aux. arg2=mem
   361  		{name: "MOVWZloadidx", argLength: 3, reg: gploadidx, asm: "MOVWZ", aux: "SymOff", clobberFlags: true},     // load 4 bytes from arg0+arg1+auxint+aux. arg2=mem
   362  		{name: "MOVDloadidx", argLength: 3, reg: gploadidx, asm: "MOVD", aux: "SymOff", clobberFlags: true},       // load 8 bytes from arg0+arg1+auxint+aux. arg2=mem
   363  		{name: "MOVHBRloadidx", argLength: 3, reg: gploadidx, asm: "MOVHBR", aux: "SymOff", clobberFlags: true},   // load 2 bytes from arg0+arg1+auxint+aux. arg2=mem. Reverse bytes.
   364  		{name: "MOVWBRloadidx", argLength: 3, reg: gploadidx, asm: "MOVWBR", aux: "SymOff", clobberFlags: true},   // load 4 bytes from arg0+arg1+auxint+aux. arg2=mem. Reverse bytes.
   365  		{name: "MOVDBRloadidx", argLength: 3, reg: gploadidx, asm: "MOVDBR", aux: "SymOff", clobberFlags: true},   // load 8 bytes from arg0+arg1+auxint+aux. arg2=mem. Reverse bytes.
   366  		{name: "MOVBstoreidx", argLength: 4, reg: gpstoreidx, asm: "MOVB", aux: "SymOff", clobberFlags: true},     // store byte in arg2 to arg0+arg1+auxint+aux. arg3=mem
   367  		{name: "MOVHstoreidx", argLength: 4, reg: gpstoreidx, asm: "MOVH", aux: "SymOff", clobberFlags: true},     // store 2 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem
   368  		{name: "MOVWstoreidx", argLength: 4, reg: gpstoreidx, asm: "MOVW", aux: "SymOff", clobberFlags: true},     // store 4 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem
   369  		{name: "MOVDstoreidx", argLength: 4, reg: gpstoreidx, asm: "MOVD", aux: "SymOff", clobberFlags: true},     // store 8 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem
   370  		{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.
   371  		{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.
   372  		{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.
   373  
   374  		// For storeconst ops, the AuxInt field encodes both
   375  		// the value to store and an address offset of the store.
   376  		// Cast AuxInt to a ValAndOff to extract Val and Off fields.
   377  		{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
   378  		{name: "MOVHstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVH", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true}, // store low 2 bytes of ...
   379  		{name: "MOVWstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVW", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true}, // store low 4 bytes of ...
   380  		{name: "MOVDstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVD", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true}, // store 8 bytes of ...
   381  
   382  		{name: "CLEAR", argLength: 2, reg: regInfo{inputs: []regMask{ptr, 0}}, asm: "CLEAR", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true},
   383  
   384  		{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
   385  		{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
   386  		{name: "CALLdefer", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                                                 // call deferproc.  arg0=mem, auxint=argsize, returns mem
   387  		{name: "CALLgo", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                                                    // call newproc.  arg0=mem, auxint=argsize, returns mem
   388  		{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
   389  
   390  		// (InvertFlags (CMP a b)) == (CMP b a)
   391  		// InvertFlags is a pseudo-op which can't appear in assembly output.
   392  		{name: "InvertFlags", argLength: 1}, // reverse direction of arg0
   393  
   394  		// Pseudo-ops
   395  		{name: "LoweredGetG", argLength: 1, reg: gp01}, // arg0=mem
   396  		// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
   397  		// and sorts it to the very beginning of the block to prevent other
   398  		// use of R12 (the closure pointer)
   399  		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R12")}}},
   400  		// arg0=ptr,arg1=mem, returns void.  Faults if ptr is nil.
   401  		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{ptrsp}}, clobberFlags: true, nilCheck: true, faultOnNilArg0: true},
   402  
   403  		// MOVDconvert converts between pointers and integers.
   404  		// We have a special op for this so as to not confuse GC
   405  		// (particularly stack maps). It takes a memory arg so it
   406  		// gets correctly ordered with respect to GC safepoints.
   407  		// arg0=ptr/int arg1=mem, output=int/ptr
   408  		{name: "MOVDconvert", argLength: 2, reg: gp11sp, asm: "MOVD"},
   409  
   410  		// Constant flag values. For any comparison, there are 5 possible
   411  		// outcomes: the three from the signed total order (<,==,>) and the
   412  		// three from the unsigned total order. The == cases overlap.
   413  		// Note: there's a sixth "unordered" outcome for floating-point
   414  		// comparisons, but we don't use such a beast yet.
   415  		// These ops are for temporary use by rewrite rules. They
   416  		// cannot appear in the generated assembly.
   417  		{name: "FlagEQ"}, // equal
   418  		{name: "FlagLT"}, // <
   419  		{name: "FlagGT"}, // >
   420  
   421  		// Atomic loads. These are just normal loads but return <value,memory> tuples
   422  		// so they can be properly ordered with other loads.
   423  		// load from arg0+auxint+aux.  arg1=mem.
   424  		{name: "MOVWZatomicload", argLength: 2, reg: gpload, asm: "MOVWZ", aux: "SymOff", faultOnNilArg0: true},
   425  		{name: "MOVDatomicload", argLength: 2, reg: gpload, asm: "MOVD", aux: "SymOff", faultOnNilArg0: true},
   426  
   427  		// Atomic stores. These are just normal stores.
   428  		// store arg1 to arg0+auxint+aux. arg2=mem.
   429  		{name: "MOVWatomicstore", argLength: 3, reg: gpstore, asm: "MOVW", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true},
   430  		{name: "MOVDatomicstore", argLength: 3, reg: gpstore, asm: "MOVD", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true},
   431  
   432  		// Atomic adds.
   433  		// *(arg0+auxint+aux) += arg1.  arg2=mem.
   434  		// Returns a tuple of <old contents of *(arg0+auxint+aux), memory>.
   435  		{name: "LAA", argLength: 3, reg: gpstorelaa, asm: "LAA", typ: "(UInt32,Mem)", aux: "SymOff", faultOnNilArg0: true},
   436  		{name: "LAAG", argLength: 3, reg: gpstorelaa, asm: "LAAG", typ: "(UInt64,Mem)", aux: "SymOff", faultOnNilArg0: true},
   437  		{name: "AddTupleFirst32", argLength: 2}, // arg0=tuple <x,y>.  Returns <x+arg1,y>.
   438  		{name: "AddTupleFirst64", argLength: 2}, // arg0=tuple <x,y>.  Returns <x+arg1,y>.
   439  
   440  		// Compare and swap.
   441  		// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory.
   442  		// if *(arg0+auxint+aux) == arg1 {
   443  		//   *(arg0+auxint+aux) = arg2
   444  		//   return (true, memory)
   445  		// } else {
   446  		//   return (false, memory)
   447  		// }
   448  		// Note that these instructions also return the old value in arg1, but we ignore it.
   449  		// TODO: have these return flags instead of bool.  The current system generates:
   450  		//    CS ...
   451  		//    MOVD  $0, ret
   452  		//    BNE   2(PC)
   453  		//    MOVD  $1, ret
   454  		//    CMPW  ret, $0
   455  		//    BNE ...
   456  		// instead of just
   457  		//    CS ...
   458  		//    BEQ ...
   459  		// but we can't do that because memory-using ops can't generate flags yet
   460  		// (flagalloc wants to move flag-generating instructions around).
   461  		{name: "LoweredAtomicCas32", argLength: 4, reg: cas, asm: "CS", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true},
   462  		{name: "LoweredAtomicCas64", argLength: 4, reg: cas, asm: "CSG", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true},
   463  
   464  		// Lowered atomic swaps, emulated using compare-and-swap.
   465  		// store arg1 to arg0+auxint+aux, arg2=mem.
   466  		{name: "LoweredAtomicExchange32", argLength: 3, reg: exchange, asm: "CS", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true},
   467  		{name: "LoweredAtomicExchange64", argLength: 3, reg: exchange, asm: "CSG", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true},
   468  
   469  		// find leftmost one
   470  		{
   471  			name:         "FLOGR",
   472  			argLength:    1,
   473  			reg:          regInfo{inputs: gponly, outputs: []regMask{buildReg("R0")}, clobbers: buildReg("R1")},
   474  			asm:          "FLOGR",
   475  			typ:          "UInt64",
   476  			clobberFlags: true,
   477  		},
   478  
   479  		// store multiple
   480  		{
   481  			name:           "STMG2",
   482  			argLength:      4,
   483  			reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), 0}},
   484  			aux:            "SymOff",
   485  			typ:            "Mem",
   486  			asm:            "STMG",
   487  			faultOnNilArg0: true,
   488  		},
   489  		{
   490  			name:           "STMG3",
   491  			argLength:      5,
   492  			reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), buildReg("R3"), 0}},
   493  			aux:            "SymOff",
   494  			typ:            "Mem",
   495  			asm:            "STMG",
   496  			faultOnNilArg0: true,
   497  		},
   498  		{
   499  			name:      "STMG4",
   500  			argLength: 6,
   501  			reg: regInfo{inputs: []regMask{
   502  				ptrsp,
   503  				buildReg("R1"),
   504  				buildReg("R2"),
   505  				buildReg("R3"),
   506  				buildReg("R4"),
   507  				0,
   508  			}},
   509  			aux:            "SymOff",
   510  			typ:            "Mem",
   511  			asm:            "STMG",
   512  			faultOnNilArg0: true,
   513  		},
   514  		{
   515  			name:           "STM2",
   516  			argLength:      4,
   517  			reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), 0}},
   518  			aux:            "SymOff",
   519  			typ:            "Mem",
   520  			asm:            "STMY",
   521  			faultOnNilArg0: true,
   522  		},
   523  		{
   524  			name:           "STM3",
   525  			argLength:      5,
   526  			reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), buildReg("R3"), 0}},
   527  			aux:            "SymOff",
   528  			typ:            "Mem",
   529  			asm:            "STMY",
   530  			faultOnNilArg0: true,
   531  		},
   532  		{
   533  			name:      "STM4",
   534  			argLength: 6,
   535  			reg: regInfo{inputs: []regMask{
   536  				ptrsp,
   537  				buildReg("R1"),
   538  				buildReg("R2"),
   539  				buildReg("R3"),
   540  				buildReg("R4"),
   541  				0,
   542  			}},
   543  			aux:            "SymOff",
   544  			typ:            "Mem",
   545  			asm:            "STMY",
   546  			faultOnNilArg0: true,
   547  		},
   548  
   549  		// large move
   550  		// auxint = remaining bytes after loop (rem)
   551  		// arg0 = address of dst memory (in R1, changed as a side effect)
   552  		// arg1 = address of src memory (in R2, changed as a side effect)
   553  		// arg2 = pointer to last address to move in loop + 256
   554  		// arg3 = mem
   555  		// returns mem
   556  		//
   557  		// mvc: MVC  $256, 0(R2), 0(R1)
   558  		//      MOVD $256(R1), R1
   559  		//      MOVD $256(R2), R2
   560  		//      CMP  R2, Rarg2
   561  		//      BNE  mvc
   562  		//	MVC  $rem, 0(R2), 0(R1) // if rem > 0
   563  		{
   564  			name:      "LoweredMove",
   565  			aux:       "Int64",
   566  			argLength: 4,
   567  			reg: regInfo{
   568  				inputs:   []regMask{buildReg("R1"), buildReg("R2"), gpsp},
   569  				clobbers: buildReg("R1 R2"),
   570  			},
   571  			clobberFlags: true,
   572  			typ:          "Mem",
   573  		},
   574  
   575  		// large clear
   576  		// auxint = remaining bytes after loop (rem)
   577  		// arg0 = address of dst memory (in R1, changed as a side effect)
   578  		// arg1 = pointer to last address to zero in loop + 256
   579  		// arg2 = mem
   580  		// returns mem
   581  		//
   582  		// clear: CLEAR $256, 0(R1)
   583  		//        MOVD  $256(R1), R1
   584  		//        CMP   R1, Rarg2
   585  		//        BNE   clear
   586  		//	  CLEAR $rem, 0(R1) // if rem > 0
   587  		{
   588  			name:      "LoweredZero",
   589  			aux:       "Int64",
   590  			argLength: 3,
   591  			reg: regInfo{
   592  				inputs:   []regMask{buildReg("R1"), gpsp},
   593  				clobbers: buildReg("R1"),
   594  			},
   595  			clobberFlags: true,
   596  			typ:          "Mem",
   597  		},
   598  	}
   599  
   600  	var S390Xblocks = []blockData{
   601  		{name: "EQ"},
   602  		{name: "NE"},
   603  		{name: "LT"},
   604  		{name: "LE"},
   605  		{name: "GT"},
   606  		{name: "GE"},
   607  		{name: "GTF"}, // FP comparison
   608  		{name: "GEF"}, // FP comparison
   609  	}
   610  
   611  	archs = append(archs, arch{
   612  		name:            "S390X",
   613  		pkg:             "cmd/internal/obj/s390x",
   614  		genfile:         "../../s390x/ssa.go",
   615  		ops:             S390Xops,
   616  		blocks:          S390Xblocks,
   617  		regnames:        regNamesS390X,
   618  		gpregmask:       gp,
   619  		fpregmask:       fp,
   620  		framepointerreg: -1, // not used
   621  		linkreg:         int8(num["R14"]),
   622  	})
   623  }