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