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