github.com/mattn/go@v0.0.0-20171011075504-07f7db3ea99f/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  		// Round to integer, float64 only.
   210  		//
   211  		// aux | rounding mode
   212  		// ----+-----------------------------------
   213  		//   1 | round to nearest, ties away from 0
   214  		//   4 | round to nearest, ties to even
   215  		//   5 | round toward 0
   216  		//   6 | round toward +∞
   217  		//   7 | round toward -∞
   218  		{name: "FIDBR", argLength: 1, reg: fp11, asm: "FIDBR", aux: "Int8"},
   219  
   220  		{name: "FMOVSload", argLength: 2, reg: fpload, asm: "FMOVS", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"}, // fp32 load
   221  		{name: "FMOVDload", argLength: 2, reg: fpload, asm: "FMOVD", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"}, // fp64 load
   222  		{name: "FMOVSconst", reg: fp01, asm: "FMOVS", aux: "Float32", rematerializeable: true},                               // fp32 constant
   223  		{name: "FMOVDconst", reg: fp01, asm: "FMOVD", aux: "Float64", rematerializeable: true},                               // fp64 constant
   224  		{name: "FMOVSloadidx", argLength: 3, reg: fploadidx, asm: "FMOVS", aux: "SymOff", symEffect: "Read"},                 // fp32 load indexed by i
   225  		{name: "FMOVDloadidx", argLength: 3, reg: fploadidx, asm: "FMOVD", aux: "SymOff", symEffect: "Read"},                 // fp64 load indexed by i
   226  
   227  		{name: "FMOVSstore", argLength: 3, reg: fpstore, asm: "FMOVS", aux: "SymOff", faultOnNilArg0: true, symEffect: "Write"}, // fp32 store
   228  		{name: "FMOVDstore", argLength: 3, reg: fpstore, asm: "FMOVD", aux: "SymOff", faultOnNilArg0: true, symEffect: "Write"}, // fp64 store
   229  		{name: "FMOVSstoreidx", argLength: 4, reg: fpstoreidx, asm: "FMOVS", aux: "SymOff", symEffect: "Write"},                 // fp32 indexed by i store
   230  		{name: "FMOVDstoreidx", argLength: 4, reg: fpstoreidx, asm: "FMOVD", aux: "SymOff", symEffect: "Write"},                 // fp64 indexed by i store
   231  
   232  		// binary ops
   233  		{name: "ADD", argLength: 2, reg: gp21sp, asm: "ADD", commutative: true, clobberFlags: true},                                                                  // arg0 + arg1
   234  		{name: "ADDW", argLength: 2, reg: gp21sp, asm: "ADDW", commutative: true, clobberFlags: true},                                                                // arg0 + arg1
   235  		{name: "ADDconst", argLength: 1, reg: gp11sp, asm: "ADD", aux: "Int64", typ: "UInt64", clobberFlags: true},                                                   // arg0 + auxint
   236  		{name: "ADDWconst", argLength: 1, reg: gp11sp, asm: "ADDW", aux: "Int32", clobberFlags: true},                                                                // arg0 + auxint
   237  		{name: "ADDload", argLength: 3, reg: gpopload, asm: "ADD", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"},   // arg0 + *arg1. arg2=mem
   238  		{name: "ADDWload", argLength: 3, reg: gpopload, asm: "ADDW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 + *arg1. arg2=mem
   239  
   240  		{name: "SUB", argLength: 2, reg: gp21, asm: "SUB", clobberFlags: true},                                                                                       // arg0 - arg1
   241  		{name: "SUBW", argLength: 2, reg: gp21, asm: "SUBW", clobberFlags: true},                                                                                     // arg0 - arg1
   242  		{name: "SUBconst", argLength: 1, reg: gp11, asm: "SUB", aux: "Int64", resultInArg0: true, clobberFlags: true},                                                // arg0 - auxint
   243  		{name: "SUBWconst", argLength: 1, reg: gp11, asm: "SUBW", aux: "Int32", resultInArg0: true, clobberFlags: true},                                              // arg0 - auxint
   244  		{name: "SUBload", argLength: 3, reg: gpopload, asm: "SUB", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"},   // arg0 - *arg1. arg2=mem
   245  		{name: "SUBWload", argLength: 3, reg: gpopload, asm: "SUBW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 - *arg1. arg2=mem
   246  
   247  		{name: "MULLD", argLength: 2, reg: gp21, asm: "MULLD", typ: "Int64", commutative: true, resultInArg0: true, clobberFlags: true},                                // arg0 * arg1
   248  		{name: "MULLW", argLength: 2, reg: gp21, asm: "MULLW", typ: "Int32", commutative: true, resultInArg0: true, clobberFlags: true},                                // arg0 * arg1
   249  		{name: "MULLDconst", argLength: 1, reg: gp11, asm: "MULLD", aux: "Int64", typ: "Int64", resultInArg0: true, clobberFlags: true},                                // arg0 * auxint
   250  		{name: "MULLWconst", argLength: 1, reg: gp11, asm: "MULLW", aux: "Int32", typ: "Int32", resultInArg0: true, clobberFlags: true},                                // arg0 * auxint
   251  		{name: "MULLDload", argLength: 3, reg: gpopload, asm: "MULLD", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 * *arg1. arg2=mem
   252  		{name: "MULLWload", argLength: 3, reg: gpopload, asm: "MULLW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 * *arg1. arg2=mem
   253  
   254  		{name: "MULHD", argLength: 2, reg: gp21, asm: "MULHD", typ: "Int64", commutative: true, resultInArg0: true, clobberFlags: true},   // (arg0 * arg1) >> width
   255  		{name: "MULHDU", argLength: 2, reg: gp21, asm: "MULHDU", typ: "Int64", commutative: true, resultInArg0: true, clobberFlags: true}, // (arg0 * arg1) >> width
   256  
   257  		{name: "DIVD", argLength: 2, reg: gp21, asm: "DIVD", resultInArg0: true, clobberFlags: true},   // arg0 / arg1
   258  		{name: "DIVW", argLength: 2, reg: gp21, asm: "DIVW", resultInArg0: true, clobberFlags: true},   // arg0 / arg1
   259  		{name: "DIVDU", argLength: 2, reg: gp21, asm: "DIVDU", resultInArg0: true, clobberFlags: true}, // arg0 / arg1
   260  		{name: "DIVWU", argLength: 2, reg: gp21, asm: "DIVWU", resultInArg0: true, clobberFlags: true}, // arg0 / arg1
   261  
   262  		{name: "MODD", argLength: 2, reg: gp21, asm: "MODD", resultInArg0: true, clobberFlags: true}, // arg0 % arg1
   263  		{name: "MODW", argLength: 2, reg: gp21, asm: "MODW", resultInArg0: true, clobberFlags: true}, // arg0 % arg1
   264  
   265  		{name: "MODDU", argLength: 2, reg: gp21, asm: "MODDU", resultInArg0: true, clobberFlags: true}, // arg0 % arg1
   266  		{name: "MODWU", argLength: 2, reg: gp21, asm: "MODWU", resultInArg0: true, clobberFlags: true}, // arg0 % arg1
   267  
   268  		{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true, clobberFlags: true},                                                                    // arg0 & arg1
   269  		{name: "ANDW", argLength: 2, reg: gp21, asm: "ANDW", commutative: true, clobberFlags: true},                                                                  // arg0 & arg1
   270  		{name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int64", resultInArg0: true, clobberFlags: true},                                                // arg0 & auxint
   271  		{name: "ANDWconst", argLength: 1, reg: gp11, asm: "ANDW", aux: "Int32", resultInArg0: true, clobberFlags: true},                                              // arg0 & auxint
   272  		{name: "ANDload", argLength: 3, reg: gpopload, asm: "AND", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"},   // arg0 & *arg1. arg2=mem
   273  		{name: "ANDWload", argLength: 3, reg: gpopload, asm: "ANDW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 & *arg1. arg2=mem
   274  
   275  		{name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true, clobberFlags: true},                                                                    // arg0 | arg1
   276  		{name: "ORW", argLength: 2, reg: gp21, asm: "ORW", commutative: true, clobberFlags: true},                                                                  // arg0 | arg1
   277  		{name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int64", resultInArg0: true, clobberFlags: true},                                                // arg0 | auxint
   278  		{name: "ORWconst", argLength: 1, reg: gp11, asm: "ORW", aux: "Int32", resultInArg0: true, clobberFlags: true},                                              // arg0 | auxint
   279  		{name: "ORload", argLength: 3, reg: gpopload, asm: "OR", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"},   // arg0 | *arg1. arg2=mem
   280  		{name: "ORWload", argLength: 3, reg: gpopload, asm: "ORW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 | *arg1. arg2=mem
   281  
   282  		{name: "XOR", argLength: 2, reg: gp21, asm: "XOR", commutative: true, clobberFlags: true},                                                                    // arg0 ^ arg1
   283  		{name: "XORW", argLength: 2, reg: gp21, asm: "XORW", commutative: true, clobberFlags: true},                                                                  // arg0 ^ arg1
   284  		{name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int64", resultInArg0: true, clobberFlags: true},                                                // arg0 ^ auxint
   285  		{name: "XORWconst", argLength: 1, reg: gp11, asm: "XORW", aux: "Int32", resultInArg0: true, clobberFlags: true},                                              // arg0 ^ auxint
   286  		{name: "XORload", argLength: 3, reg: gpopload, asm: "XOR", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"},   // arg0 ^ *arg1. arg2=mem
   287  		{name: "XORWload", argLength: 3, reg: gpopload, asm: "XORW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 ^ *arg1. arg2=mem
   288  
   289  		{name: "CMP", argLength: 2, reg: gp2flags, asm: "CMP", typ: "Flags"},   // arg0 compare to arg1
   290  		{name: "CMPW", argLength: 2, reg: gp2flags, asm: "CMPW", typ: "Flags"}, // arg0 compare to arg1
   291  
   292  		{name: "CMPU", argLength: 2, reg: gp2flags, asm: "CMPU", typ: "Flags"},   // arg0 compare to arg1
   293  		{name: "CMPWU", argLength: 2, reg: gp2flags, asm: "CMPWU", typ: "Flags"}, // arg0 compare to arg1
   294  
   295  		{name: "CMPconst", argLength: 1, reg: gp1flags, asm: "CMP", typ: "Flags", aux: "Int64"},     // arg0 compare to auxint
   296  		{name: "CMPWconst", argLength: 1, reg: gp1flags, asm: "CMPW", typ: "Flags", aux: "Int32"},   // arg0 compare to auxint
   297  		{name: "CMPUconst", argLength: 1, reg: gp1flags, asm: "CMPU", typ: "Flags", aux: "Int64"},   // arg0 compare to auxint
   298  		{name: "CMPWUconst", argLength: 1, reg: gp1flags, asm: "CMPWU", typ: "Flags", aux: "Int32"}, // arg0 compare to auxint
   299  
   300  		{name: "FCMPS", argLength: 2, reg: fp2flags, asm: "CEBR", typ: "Flags"}, // arg0 compare to arg1, f32
   301  		{name: "FCMP", argLength: 2, reg: fp2flags, asm: "FCMPU", typ: "Flags"}, // arg0 compare to arg1, f64
   302  
   303  		{name: "SLD", argLength: 2, reg: sh21, asm: "SLD"},                   // arg0 << arg1, shift amount is mod 64
   304  		{name: "SLW", argLength: 2, reg: sh21, asm: "SLW"},                   // arg0 << arg1, shift amount is mod 32
   305  		{name: "SLDconst", argLength: 1, reg: gp11, asm: "SLD", aux: "Int8"}, // arg0 << auxint, shift amount 0-63
   306  		{name: "SLWconst", argLength: 1, reg: gp11, asm: "SLW", aux: "Int8"}, // arg0 << auxint, shift amount 0-31
   307  
   308  		{name: "SRD", argLength: 2, reg: sh21, asm: "SRD"},                   // unsigned arg0 >> arg1, shift amount is mod 64
   309  		{name: "SRW", argLength: 2, reg: sh21, asm: "SRW"},                   // unsigned arg0 >> arg1, shift amount is mod 32
   310  		{name: "SRDconst", argLength: 1, reg: gp11, asm: "SRD", aux: "Int8"}, // unsigned arg0 >> auxint, shift amount 0-63
   311  		{name: "SRWconst", argLength: 1, reg: gp11, asm: "SRW", aux: "Int8"}, // unsigned arg0 >> auxint, shift amount 0-31
   312  
   313  		// Arithmetic shifts clobber flags.
   314  		{name: "SRAD", argLength: 2, reg: sh21, asm: "SRAD", clobberFlags: true},                   // signed arg0 >> arg1, shift amount is mod 64
   315  		{name: "SRAW", argLength: 2, reg: sh21, asm: "SRAW", clobberFlags: true},                   // signed arg0 >> arg1, shift amount is mod 32
   316  		{name: "SRADconst", argLength: 1, reg: gp11, asm: "SRAD", aux: "Int8", clobberFlags: true}, // signed arg0 >> auxint, shift amount 0-63
   317  		{name: "SRAWconst", argLength: 1, reg: gp11, asm: "SRAW", aux: "Int8", clobberFlags: true}, // signed arg0 >> auxint, shift amount 0-31
   318  
   319  		{name: "RLLGconst", argLength: 1, reg: gp11, asm: "RLLG", aux: "Int8"}, // arg0 rotate left auxint, rotate amount 0-63
   320  		{name: "RLLconst", argLength: 1, reg: gp11, asm: "RLL", aux: "Int8"},   // arg0 rotate left auxint, rotate amount 0-31
   321  
   322  		// unary ops
   323  		{name: "NEG", argLength: 1, reg: gp11, asm: "NEG", clobberFlags: true},   // -arg0
   324  		{name: "NEGW", argLength: 1, reg: gp11, asm: "NEGW", clobberFlags: true}, // -arg0
   325  
   326  		{name: "NOT", argLength: 1, reg: gp11, resultInArg0: true, clobberFlags: true},  // ^arg0
   327  		{name: "NOTW", argLength: 1, reg: gp11, resultInArg0: true, clobberFlags: true}, // ^arg0
   328  
   329  		{name: "FSQRT", argLength: 1, reg: fp11, asm: "FSQRT"}, // sqrt(arg0)
   330  
   331  		{name: "SUBEcarrymask", argLength: 1, reg: flagsgp, asm: "SUBE"},  // (int64)(-1) if carry is set, 0 if carry is clear.
   332  		{name: "SUBEWcarrymask", argLength: 1, reg: flagsgp, asm: "SUBE"}, // (int32)(-1) if carry is set, 0 if carry is clear.
   333  		// Note: 32-bits subtraction is not implemented in S390X. Temporarily use SUBE (64-bits).
   334  
   335  		{name: "MOVDEQ", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDEQ"}, // extract == condition from arg0
   336  		{name: "MOVDNE", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDNE"}, // extract != condition from arg0
   337  		{name: "MOVDLT", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDLT"}, // extract signed < condition from arg0
   338  		{name: "MOVDLE", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDLE"}, // extract signed <= condition from arg0
   339  		{name: "MOVDGT", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDGT"}, // extract signed > condition from arg0
   340  		{name: "MOVDGE", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDGE"}, // extract signed >= condition from arg0
   341  
   342  		// Different rules for floating point conditions because
   343  		// any comparison involving a NaN is always false and thus
   344  		// the patterns for inverting conditions cannot be used.
   345  		{name: "MOVDGTnoinv", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDGT"}, // extract floating > condition from arg0
   346  		{name: "MOVDGEnoinv", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDGE"}, // extract floating >= condition from arg0
   347  
   348  		{name: "MOVBreg", argLength: 1, reg: gp11sp, asm: "MOVB", typ: "Int64"},    // sign extend arg0 from int8 to int64
   349  		{name: "MOVBZreg", argLength: 1, reg: gp11sp, asm: "MOVBZ", typ: "UInt64"}, // zero extend arg0 from int8 to int64
   350  		{name: "MOVHreg", argLength: 1, reg: gp11sp, asm: "MOVH", typ: "Int64"},    // sign extend arg0 from int16 to int64
   351  		{name: "MOVHZreg", argLength: 1, reg: gp11sp, asm: "MOVHZ", typ: "UInt64"}, // zero extend arg0 from int16 to int64
   352  		{name: "MOVWreg", argLength: 1, reg: gp11sp, asm: "MOVW", typ: "Int64"},    // sign extend arg0 from int32 to int64
   353  		{name: "MOVWZreg", argLength: 1, reg: gp11sp, asm: "MOVWZ", typ: "UInt64"}, // zero extend arg0 from int32 to int64
   354  		{name: "MOVDreg", argLength: 1, reg: gp11sp, asm: "MOVD"},                  // move from arg0
   355  
   356  		{name: "MOVDnop", argLength: 1, reg: gp11, resultInArg0: true}, // nop, return arg0 in same register
   357  
   358  		{name: "MOVDconst", reg: gp01, asm: "MOVD", typ: "UInt64", aux: "Int64", rematerializeable: true}, // auxint
   359  
   360  		{name: "CFDBRA", argLength: 1, reg: fpgp, asm: "CFDBRA"}, // convert float64 to int32
   361  		{name: "CGDBRA", argLength: 1, reg: fpgp, asm: "CGDBRA"}, // convert float64 to int64
   362  		{name: "CFEBRA", argLength: 1, reg: fpgp, asm: "CFEBRA"}, // convert float32 to int32
   363  		{name: "CGEBRA", argLength: 1, reg: fpgp, asm: "CGEBRA"}, // convert float32 to int64
   364  		{name: "CEFBRA", argLength: 1, reg: gpfp, asm: "CEFBRA"}, // convert int32 to float32
   365  		{name: "CDFBRA", argLength: 1, reg: gpfp, asm: "CDFBRA"}, // convert int32 to float64
   366  		{name: "CEGBRA", argLength: 1, reg: gpfp, asm: "CEGBRA"}, // convert int64 to float32
   367  		{name: "CDGBRA", argLength: 1, reg: gpfp, asm: "CDGBRA"}, // convert int64 to float64
   368  		{name: "LEDBR", argLength: 1, reg: fp11, asm: "LEDBR"},   // convert float64 to float32
   369  		{name: "LDEBR", argLength: 1, reg: fp11, asm: "LDEBR"},   // convert float32 to float64
   370  
   371  		{name: "MOVDaddr", argLength: 1, reg: addr, aux: "SymOff", rematerializeable: true, symEffect: "Read"}, // arg0 + auxint + offset encoded in aux
   372  		{name: "MOVDaddridx", argLength: 2, reg: addridx, aux: "SymOff", symEffect: "Read"},                    // arg0 + arg1 + auxint + aux
   373  
   374  		// auxint+aux == add auxint and the offset of the symbol in aux (if any) to the effective address
   375  		{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.
   376  		{name: "MOVBload", argLength: 2, reg: gpload, asm: "MOVB", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, symEffect: "Read"},                  // ditto, sign extend to int64
   377  		{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.
   378  		{name: "MOVHload", argLength: 2, reg: gpload, asm: "MOVH", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, symEffect: "Read"},                  // ditto, sign extend to int64
   379  		{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.
   380  		{name: "MOVWload", argLength: 2, reg: gpload, asm: "MOVW", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, symEffect: "Read"},                  // ditto, sign extend to int64
   381  		{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
   382  
   383  		{name: "MOVWBR", argLength: 1, reg: gp11, asm: "MOVWBR"}, // arg0 swap bytes
   384  		{name: "MOVDBR", argLength: 1, reg: gp11, asm: "MOVDBR"}, // arg0 swap bytes
   385  
   386  		{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.
   387  		{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.
   388  		{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.
   389  
   390  		{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
   391  		{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
   392  		{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
   393  		{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
   394  		{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.
   395  		{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.
   396  		{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.
   397  
   398  		{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
   399  
   400  		// indexed loads/stores
   401  		// TODO(mundaym): add sign-extended indexed loads
   402  		{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
   403  		{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
   404  		{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
   405  		{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
   406  		{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.
   407  		{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.
   408  		{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.
   409  		{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
   410  		{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
   411  		{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
   412  		{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
   413  		{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.
   414  		{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.
   415  		{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.
   416  
   417  		// For storeconst ops, the AuxInt field encodes both
   418  		// the value to store and an address offset of the store.
   419  		// Cast AuxInt to a ValAndOff to extract Val and Off fields.
   420  		{name: "MOVBstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVB", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store low byte of ValAndOff(AuxInt).Val() to arg0+ValAndOff(AuxInt).Off()+aux.  arg1=mem
   421  		{name: "MOVHstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVH", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store low 2 bytes of ...
   422  		{name: "MOVWstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVW", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store low 4 bytes of ...
   423  		{name: "MOVDstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVD", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of ...
   424  
   425  		{name: "CLEAR", argLength: 2, reg: regInfo{inputs: []regMask{ptr, 0}}, asm: "CLEAR", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, symEffect: "Write"},
   426  
   427  		{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true, symEffect: "None"},                            // call static function aux.(*obj.LSym).  arg0=mem, auxint=argsize, returns mem
   428  		{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
   429  		{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
   430  
   431  		// (InvertFlags (CMP a b)) == (CMP b a)
   432  		// InvertFlags is a pseudo-op which can't appear in assembly output.
   433  		{name: "InvertFlags", argLength: 1}, // reverse direction of arg0
   434  
   435  		// Pseudo-ops
   436  		{name: "LoweredGetG", argLength: 1, reg: gp01}, // arg0=mem
   437  		// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
   438  		// and sorts it to the very beginning of the block to prevent other
   439  		// use of R12 (the closure pointer)
   440  		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R12")}}},
   441  		// arg0=ptr,arg1=mem, returns void.  Faults if ptr is nil.
   442  		// LoweredGetCallerSP returns the SP of the caller of the current function.
   443  		{name: "LoweredGetCallerSP", reg: gp01, rematerializeable: true},
   444  		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{ptrsp}}, clobberFlags: true, nilCheck: true, faultOnNilArg0: true},
   445  		// Round ops to block fused-multiply-add extraction.
   446  		{name: "LoweredRound32F", argLength: 1, reg: fp11, resultInArg0: true},
   447  		{name: "LoweredRound64F", argLength: 1, reg: fp11, resultInArg0: true},
   448  
   449  		// MOVDconvert converts between pointers and integers.
   450  		// We have a special op for this so as to not confuse GC
   451  		// (particularly stack maps). It takes a memory arg so it
   452  		// gets correctly ordered with respect to GC safepoints.
   453  		// arg0=ptr/int arg1=mem, output=int/ptr
   454  		{name: "MOVDconvert", argLength: 2, reg: gp11sp, asm: "MOVD"},
   455  
   456  		// Constant flag values. For any comparison, there are 5 possible
   457  		// outcomes: the three from the signed total order (<,==,>) and the
   458  		// three from the unsigned total order. The == cases overlap.
   459  		// Note: there's a sixth "unordered" outcome for floating-point
   460  		// comparisons, but we don't use such a beast yet.
   461  		// These ops are for temporary use by rewrite rules. They
   462  		// cannot appear in the generated assembly.
   463  		{name: "FlagEQ"}, // equal
   464  		{name: "FlagLT"}, // <
   465  		{name: "FlagGT"}, // >
   466  
   467  		// Atomic loads. These are just normal loads but return <value,memory> tuples
   468  		// so they can be properly ordered with other loads.
   469  		// load from arg0+auxint+aux.  arg1=mem.
   470  		{name: "MOVWZatomicload", argLength: 2, reg: gpload, asm: "MOVWZ", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"},
   471  		{name: "MOVDatomicload", argLength: 2, reg: gpload, asm: "MOVD", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"},
   472  
   473  		// Atomic stores. These are just normal stores.
   474  		// store arg1 to arg0+auxint+aux. arg2=mem.
   475  		{name: "MOVWatomicstore", argLength: 3, reg: gpstore, asm: "MOVW", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "Write"},
   476  		{name: "MOVDatomicstore", argLength: 3, reg: gpstore, asm: "MOVD", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "Write"},
   477  
   478  		// Atomic adds.
   479  		// *(arg0+auxint+aux) += arg1.  arg2=mem.
   480  		// Returns a tuple of <old contents of *(arg0+auxint+aux), memory>.
   481  		{name: "LAA", argLength: 3, reg: gpstorelaa, asm: "LAA", typ: "(UInt32,Mem)", aux: "SymOff", faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"},
   482  		{name: "LAAG", argLength: 3, reg: gpstorelaa, asm: "LAAG", typ: "(UInt64,Mem)", aux: "SymOff", faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"},
   483  		{name: "AddTupleFirst32", argLength: 2}, // arg1=tuple <x,y>.  Returns <x+arg0,y>.
   484  		{name: "AddTupleFirst64", argLength: 2}, // arg1=tuple <x,y>.  Returns <x+arg0,y>.
   485  
   486  		// Compare and swap.
   487  		// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory.
   488  		// if *(arg0+auxint+aux) == arg1 {
   489  		//   *(arg0+auxint+aux) = arg2
   490  		//   return (true, memory)
   491  		// } else {
   492  		//   return (false, memory)
   493  		// }
   494  		// Note that these instructions also return the old value in arg1, but we ignore it.
   495  		// TODO: have these return flags instead of bool.  The current system generates:
   496  		//    CS ...
   497  		//    MOVD  $0, ret
   498  		//    BNE   2(PC)
   499  		//    MOVD  $1, ret
   500  		//    CMPW  ret, $0
   501  		//    BNE ...
   502  		// instead of just
   503  		//    CS ...
   504  		//    BEQ ...
   505  		// but we can't do that because memory-using ops can't generate flags yet
   506  		// (flagalloc wants to move flag-generating instructions around).
   507  		{name: "LoweredAtomicCas32", argLength: 4, reg: cas, asm: "CS", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"},
   508  		{name: "LoweredAtomicCas64", argLength: 4, reg: cas, asm: "CSG", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"},
   509  
   510  		// Lowered atomic swaps, emulated using compare-and-swap.
   511  		// store arg1 to arg0+auxint+aux, arg2=mem.
   512  		{name: "LoweredAtomicExchange32", argLength: 3, reg: exchange, asm: "CS", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"},
   513  		{name: "LoweredAtomicExchange64", argLength: 3, reg: exchange, asm: "CSG", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"},
   514  
   515  		// find leftmost one
   516  		{
   517  			name:         "FLOGR",
   518  			argLength:    1,
   519  			reg:          regInfo{inputs: gponly, outputs: []regMask{buildReg("R0")}, clobbers: buildReg("R1")},
   520  			asm:          "FLOGR",
   521  			typ:          "UInt64",
   522  			clobberFlags: true,
   523  		},
   524  
   525  		// store multiple
   526  		{
   527  			name:           "STMG2",
   528  			argLength:      4,
   529  			reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), 0}},
   530  			aux:            "SymOff",
   531  			typ:            "Mem",
   532  			asm:            "STMG",
   533  			faultOnNilArg0: true,
   534  			symEffect:      "Write",
   535  		},
   536  		{
   537  			name:           "STMG3",
   538  			argLength:      5,
   539  			reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), buildReg("R3"), 0}},
   540  			aux:            "SymOff",
   541  			typ:            "Mem",
   542  			asm:            "STMG",
   543  			faultOnNilArg0: true,
   544  			symEffect:      "Write",
   545  		},
   546  		{
   547  			name:      "STMG4",
   548  			argLength: 6,
   549  			reg: regInfo{inputs: []regMask{
   550  				ptrsp,
   551  				buildReg("R1"),
   552  				buildReg("R2"),
   553  				buildReg("R3"),
   554  				buildReg("R4"),
   555  				0,
   556  			}},
   557  			aux:            "SymOff",
   558  			typ:            "Mem",
   559  			asm:            "STMG",
   560  			faultOnNilArg0: true,
   561  			symEffect:      "Write",
   562  		},
   563  		{
   564  			name:           "STM2",
   565  			argLength:      4,
   566  			reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), 0}},
   567  			aux:            "SymOff",
   568  			typ:            "Mem",
   569  			asm:            "STMY",
   570  			faultOnNilArg0: true,
   571  			symEffect:      "Write",
   572  		},
   573  		{
   574  			name:           "STM3",
   575  			argLength:      5,
   576  			reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), buildReg("R3"), 0}},
   577  			aux:            "SymOff",
   578  			typ:            "Mem",
   579  			asm:            "STMY",
   580  			faultOnNilArg0: true,
   581  			symEffect:      "Write",
   582  		},
   583  		{
   584  			name:      "STM4",
   585  			argLength: 6,
   586  			reg: regInfo{inputs: []regMask{
   587  				ptrsp,
   588  				buildReg("R1"),
   589  				buildReg("R2"),
   590  				buildReg("R3"),
   591  				buildReg("R4"),
   592  				0,
   593  			}},
   594  			aux:            "SymOff",
   595  			typ:            "Mem",
   596  			asm:            "STMY",
   597  			faultOnNilArg0: true,
   598  			symEffect:      "Write",
   599  		},
   600  
   601  		// large move
   602  		// auxint = remaining bytes after loop (rem)
   603  		// arg0 = address of dst memory (in R1, changed as a side effect)
   604  		// arg1 = address of src memory (in R2, changed as a side effect)
   605  		// arg2 = pointer to last address to move in loop + 256
   606  		// arg3 = mem
   607  		// returns mem
   608  		//
   609  		// mvc: MVC  $256, 0(R2), 0(R1)
   610  		//      MOVD $256(R1), R1
   611  		//      MOVD $256(R2), R2
   612  		//      CMP  R2, Rarg2
   613  		//      BNE  mvc
   614  		//	MVC  $rem, 0(R2), 0(R1) // if rem > 0
   615  		{
   616  			name:      "LoweredMove",
   617  			aux:       "Int64",
   618  			argLength: 4,
   619  			reg: regInfo{
   620  				inputs:   []regMask{buildReg("R1"), buildReg("R2"), gpsp},
   621  				clobbers: buildReg("R1 R2"),
   622  			},
   623  			clobberFlags:   true,
   624  			typ:            "Mem",
   625  			faultOnNilArg0: true,
   626  			faultOnNilArg1: true,
   627  		},
   628  
   629  		// large clear
   630  		// auxint = remaining bytes after loop (rem)
   631  		// arg0 = address of dst memory (in R1, changed as a side effect)
   632  		// arg1 = pointer to last address to zero in loop + 256
   633  		// arg2 = mem
   634  		// returns mem
   635  		//
   636  		// clear: CLEAR $256, 0(R1)
   637  		//        MOVD  $256(R1), R1
   638  		//        CMP   R1, Rarg2
   639  		//        BNE   clear
   640  		//	  CLEAR $rem, 0(R1) // if rem > 0
   641  		{
   642  			name:      "LoweredZero",
   643  			aux:       "Int64",
   644  			argLength: 3,
   645  			reg: regInfo{
   646  				inputs:   []regMask{buildReg("R1"), gpsp},
   647  				clobbers: buildReg("R1"),
   648  			},
   649  			clobberFlags:   true,
   650  			typ:            "Mem",
   651  			faultOnNilArg0: true,
   652  		},
   653  	}
   654  
   655  	var S390Xblocks = []blockData{
   656  		{name: "EQ"},
   657  		{name: "NE"},
   658  		{name: "LT"},
   659  		{name: "LE"},
   660  		{name: "GT"},
   661  		{name: "GE"},
   662  		{name: "GTF"}, // FP comparison
   663  		{name: "GEF"}, // FP comparison
   664  	}
   665  
   666  	archs = append(archs, arch{
   667  		name:            "S390X",
   668  		pkg:             "cmd/internal/obj/s390x",
   669  		genfile:         "../../s390x/ssa.go",
   670  		ops:             S390Xops,
   671  		blocks:          S390Xblocks,
   672  		regnames:        regNamesS390X,
   673  		gpregmask:       gp,
   674  		fpregmask:       fp,
   675  		framepointerreg: -1, // not used
   676  		linkreg:         int8(num["R14"]),
   677  	})
   678  }