github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/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  package main
     6  
     7  import "strings"
     8  
     9  // Notes:
    10  //  - Integer types live in the low portion of registers. Upper portions are junk.
    11  //  - Boolean types use the low-order byte of a register. 0=false, 1=true.
    12  //    Upper bytes are junk.
    13  //  - When doing sub-register operations, we try to write the whole
    14  //    destination register to avoid a partial-register write.
    15  //  - Unused portions of AuxInt (or the Val portion of ValAndOff) are
    16  //    filled by sign-extending the used portion. Users of AuxInt which interpret
    17  //    AuxInt as unsigned (e.g. shifts) must be careful.
    18  //  - The SB 'register' is implemented using instruction-relative addressing. This
    19  //    places some limitations on when and how memory operands that are addressed
    20  //    relative to SB can be used:
    21  //
    22  //     1. Pseudo-instructions do not always map to a single machine instruction when
    23  //        using the SB 'register' to address data. This is because many machine
    24  //        instructions do not have relative long (RL suffix) equivalents. For example,
    25  //        ADDload, which is assembled as AG.
    26  //
    27  //     2. Loads and stores using relative addressing require the data be aligned
    28  //        according to its size (8-bytes for double words, 4-bytes for words
    29  //        and so on).
    30  //
    31  //    We can always work around these by inserting LARL instructions (load address
    32  //    relative long) in the assembler, but typically this results in worse code
    33  //    generation because the address can't be re-used. Inserting instructions in the
    34  //    assembler also means clobbering the temp register and it is a long-term goal
    35  //    to prevent the compiler doing this so that it can be allocated as a normal
    36  //    register.
    37  //
    38  // For more information about the z/Architecture, the instruction set and the
    39  // addressing modes it supports take a look at the z/Architecture Principles of
    40  // Operation: http://publibfp.boulder.ibm.com/epubs/pdf/dz9zr010.pdf
    41  //
    42  // Suffixes encode the bit width of pseudo-instructions.
    43  // D (double word)  = 64 bit (frequently omitted)
    44  // W (word)         = 32 bit
    45  // H (half word)    = 16 bit
    46  // B (byte)         = 8 bit
    47  // S (single prec.) = 32 bit (double precision is omitted)
    48  
    49  // copied from ../../s390x/reg.go
    50  var regNamesS390X = []string{
    51  	"R0",
    52  	"R1",
    53  	"R2",
    54  	"R3",
    55  	"R4",
    56  	"R5",
    57  	"R6",
    58  	"R7",
    59  	"R8",
    60  	"R9",
    61  	"R10",
    62  	"R11",
    63  	"R12",
    64  	"g", // R13
    65  	"R14",
    66  	"SP", // R15
    67  	"F0",
    68  	"F1",
    69  	"F2",
    70  	"F3",
    71  	"F4",
    72  	"F5",
    73  	"F6",
    74  	"F7",
    75  	"F8",
    76  	"F9",
    77  	"F10",
    78  	"F11",
    79  	"F12",
    80  	"F13",
    81  	"F14",
    82  	"F15",
    83  
    84  	// If you add registers, update asyncPreempt in runtime.
    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  		tmp = buildReg("R11") // R11 is used as a temporary in a small number of instructions.
   117  
   118  		// R10 is reserved by the assembler.
   119  		gp   = buildReg("R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14")
   120  		gpg  = gp | buildReg("g")
   121  		gpsp = gp | sp
   122  
   123  		// R0 is considered to contain the value 0 in address calculations.
   124  		ptr     = gp &^ r0
   125  		ptrsp   = ptr | sp
   126  		ptrspsb = ptrsp | sb
   127  
   128  		fp         = buildReg("F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15")
   129  		callerSave = gp | fp | buildReg("g") // runtime.setg (and anything calling it) may clobber g
   130  		r1         = buildReg("R1")
   131  		r2         = buildReg("R2")
   132  		r3         = buildReg("R3")
   133  		r9         = buildReg("R9")
   134  	)
   135  	// Common slices of register masks
   136  	var (
   137  		gponly = []regMask{gp}
   138  		fponly = []regMask{fp}
   139  	)
   140  
   141  	// Common regInfo
   142  	var (
   143  		gp01    = regInfo{inputs: []regMask{}, outputs: gponly}
   144  		gp11    = regInfo{inputs: []regMask{gp}, outputs: gponly}
   145  		gp11sp  = regInfo{inputs: []regMask{gpsp}, outputs: gponly}
   146  		gp21    = regInfo{inputs: []regMask{gp, gp}, outputs: gponly}
   147  		gp21sp  = regInfo{inputs: []regMask{gpsp, gp}, outputs: gponly}
   148  		gp21tmp = regInfo{inputs: []regMask{gp &^ tmp, gp &^ tmp}, outputs: []regMask{gp &^ tmp}, clobbers: tmp}
   149  
   150  		// R0 evaluates to 0 when used as the number of bits to shift
   151  		// so we need to exclude it from that operand.
   152  		sh21 = regInfo{inputs: []regMask{gp, ptr}, outputs: gponly}
   153  
   154  		addr    = regInfo{inputs: []regMask{sp | sb}, outputs: gponly}
   155  		addridx = regInfo{inputs: []regMask{sp | sb, ptrsp}, outputs: gponly}
   156  
   157  		gp2flags       = regInfo{inputs: []regMask{gpsp, gpsp}}
   158  		gp1flags       = regInfo{inputs: []regMask{gpsp}}
   159  		gp2flags1      = regInfo{inputs: []regMask{gp, gp}, outputs: gponly}
   160  		gp11flags      = regInfo{inputs: []regMask{gp}, outputs: gponly}
   161  		gp21flags      = regInfo{inputs: []regMask{gp, gp}, outputs: gponly}
   162  		gp2flags1flags = regInfo{inputs: []regMask{gp, gp}, outputs: gponly}
   163  
   164  		gpload       = regInfo{inputs: []regMask{ptrspsb, 0}, outputs: gponly}
   165  		gploadidx    = regInfo{inputs: []regMask{ptrspsb, ptrsp, 0}, outputs: gponly}
   166  		gpopload     = regInfo{inputs: []regMask{gp, ptrsp, 0}, outputs: gponly}
   167  		gpstore      = regInfo{inputs: []regMask{ptrspsb, gpsp, 0}}
   168  		gpstoreconst = regInfo{inputs: []regMask{ptrspsb, 0}}
   169  		gpstoreidx   = regInfo{inputs: []regMask{ptrsp, ptrsp, gpsp, 0}}
   170  		gpstorebr    = regInfo{inputs: []regMask{ptrsp, gpsp, 0}}
   171  		gpstorelaa   = regInfo{inputs: []regMask{ptrspsb, gpsp, 0}, outputs: gponly}
   172  		gpstorelab   = regInfo{inputs: []regMask{r1, gpsp, 0}, clobbers: r1}
   173  
   174  		gpmvc = regInfo{inputs: []regMask{ptrsp, ptrsp, 0}}
   175  
   176  		fp01        = regInfo{inputs: []regMask{}, outputs: fponly}
   177  		fp21        = regInfo{inputs: []regMask{fp, fp}, outputs: fponly}
   178  		fp31        = regInfo{inputs: []regMask{fp, fp, fp}, outputs: fponly}
   179  		fp21clobber = regInfo{inputs: []regMask{fp, fp}, outputs: fponly}
   180  		fpgp        = regInfo{inputs: fponly, outputs: gponly}
   181  		gpfp        = regInfo{inputs: gponly, outputs: fponly}
   182  		fp11        = regInfo{inputs: fponly, outputs: fponly}
   183  		fp1flags    = regInfo{inputs: []regMask{fp}}
   184  		fp11clobber = regInfo{inputs: fponly, outputs: fponly}
   185  		fp2flags    = regInfo{inputs: []regMask{fp, fp}}
   186  
   187  		fpload    = regInfo{inputs: []regMask{ptrspsb, 0}, outputs: fponly}
   188  		fploadidx = regInfo{inputs: []regMask{ptrsp, ptrsp, 0}, outputs: fponly}
   189  
   190  		fpstore    = regInfo{inputs: []regMask{ptrspsb, fp, 0}}
   191  		fpstoreidx = regInfo{inputs: []regMask{ptrsp, ptrsp, fp, 0}}
   192  
   193  		sync = regInfo{inputs: []regMask{0}}
   194  
   195  		// LoweredAtomicCas may overwrite arg1, so force it to R0 for now.
   196  		cas = regInfo{inputs: []regMask{ptrsp, r0, gpsp, 0}, outputs: []regMask{gp, 0}, clobbers: r0}
   197  
   198  		// LoweredAtomicExchange overwrites the output before executing
   199  		// CS{,G}, so the output register must not be the same as the
   200  		// input register. For now we just force the output register to
   201  		// R0.
   202  		exchange = regInfo{inputs: []regMask{ptrsp, gpsp &^ r0, 0}, outputs: []regMask{r0, 0}}
   203  	)
   204  
   205  	var S390Xops = []opData{
   206  		// fp ops
   207  		{name: "FADDS", argLength: 2, reg: fp21clobber, typ: "(Float32,Flags)", asm: "FADDS", commutative: true, resultInArg0: true}, // fp32 arg0 + arg1
   208  		{name: "FADD", argLength: 2, reg: fp21clobber, typ: "(Float64,Flags)", asm: "FADD", commutative: true, resultInArg0: true},   // fp64 arg0 + arg1
   209  		{name: "FSUBS", argLength: 2, reg: fp21clobber, typ: "(Float32,Flags)", asm: "FSUBS", resultInArg0: true},                    // fp32 arg0 - arg1
   210  		{name: "FSUB", argLength: 2, reg: fp21clobber, typ: "(Float64,Flags)", asm: "FSUB", resultInArg0: true},                      // fp64 arg0 - arg1
   211  		{name: "FMULS", argLength: 2, reg: fp21, asm: "FMULS", commutative: true, resultInArg0: true},                                // fp32 arg0 * arg1
   212  		{name: "FMUL", argLength: 2, reg: fp21, asm: "FMUL", commutative: true, resultInArg0: true},                                  // fp64 arg0 * arg1
   213  		{name: "FDIVS", argLength: 2, reg: fp21, asm: "FDIVS", resultInArg0: true},                                                   // fp32 arg0 / arg1
   214  		{name: "FDIV", argLength: 2, reg: fp21, asm: "FDIV", resultInArg0: true},                                                     // fp64 arg0 / arg1
   215  		{name: "FNEGS", argLength: 1, reg: fp11clobber, asm: "FNEGS", clobberFlags: true},                                            // fp32 -arg0
   216  		{name: "FNEG", argLength: 1, reg: fp11clobber, asm: "FNEG", clobberFlags: true},                                              // fp64 -arg0
   217  		{name: "FMADDS", argLength: 3, reg: fp31, asm: "FMADDS", resultInArg0: true},                                                 // fp32 arg1 * arg2 + arg0
   218  		{name: "FMADD", argLength: 3, reg: fp31, asm: "FMADD", resultInArg0: true},                                                   // fp64 arg1 * arg2 + arg0
   219  		{name: "FMSUBS", argLength: 3, reg: fp31, asm: "FMSUBS", resultInArg0: true},                                                 // fp32 arg1 * arg2 - arg0
   220  		{name: "FMSUB", argLength: 3, reg: fp31, asm: "FMSUB", resultInArg0: true},                                                   // fp64 arg1 * arg2 - arg0
   221  		{name: "LPDFR", argLength: 1, reg: fp11, asm: "LPDFR"},                                                                       // fp64/fp32 set sign bit
   222  		{name: "LNDFR", argLength: 1, reg: fp11, asm: "LNDFR"},                                                                       // fp64/fp32 clear sign bit
   223  		{name: "CPSDR", argLength: 2, reg: fp21, asm: "CPSDR"},                                                                       // fp64/fp32 copy arg1 sign bit to arg0
   224  
   225  		// Round to integer, float64 only.
   226  		//
   227  		// aux | rounding mode
   228  		// ----+-----------------------------------
   229  		//   1 | round to nearest, ties away from 0
   230  		//   4 | round to nearest, ties to even
   231  		//   5 | round toward 0
   232  		//   6 | round toward +∞
   233  		//   7 | round toward -∞
   234  		{name: "FIDBR", argLength: 1, reg: fp11, asm: "FIDBR", aux: "Int8"},
   235  
   236  		{name: "FMOVSload", argLength: 2, reg: fpload, asm: "FMOVS", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"}, // fp32 load
   237  		{name: "FMOVDload", argLength: 2, reg: fpload, asm: "FMOVD", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"}, // fp64 load
   238  		{name: "FMOVSconst", reg: fp01, asm: "FMOVS", aux: "Float32", rematerializeable: true},                               // fp32 constant
   239  		{name: "FMOVDconst", reg: fp01, asm: "FMOVD", aux: "Float64", rematerializeable: true},                               // fp64 constant
   240  		{name: "FMOVSloadidx", argLength: 3, reg: fploadidx, asm: "FMOVS", aux: "SymOff", symEffect: "Read"},                 // fp32 load indexed by i
   241  		{name: "FMOVDloadidx", argLength: 3, reg: fploadidx, asm: "FMOVD", aux: "SymOff", symEffect: "Read"},                 // fp64 load indexed by i
   242  
   243  		{name: "FMOVSstore", argLength: 3, reg: fpstore, asm: "FMOVS", aux: "SymOff", faultOnNilArg0: true, symEffect: "Write"}, // fp32 store
   244  		{name: "FMOVDstore", argLength: 3, reg: fpstore, asm: "FMOVD", aux: "SymOff", faultOnNilArg0: true, symEffect: "Write"}, // fp64 store
   245  		{name: "FMOVSstoreidx", argLength: 4, reg: fpstoreidx, asm: "FMOVS", aux: "SymOff", symEffect: "Write"},                 // fp32 indexed by i store
   246  		{name: "FMOVDstoreidx", argLength: 4, reg: fpstoreidx, asm: "FMOVD", aux: "SymOff", symEffect: "Write"},                 // fp64 indexed by i store
   247  
   248  		// binary ops
   249  		{name: "ADD", argLength: 2, reg: gp21sp, asm: "ADD", commutative: true, clobberFlags: true},                                                                  // arg0 + arg1
   250  		{name: "ADDW", argLength: 2, reg: gp21sp, asm: "ADDW", commutative: true, clobberFlags: true},                                                                // arg0 + arg1
   251  		{name: "ADDconst", argLength: 1, reg: gp11sp, asm: "ADD", aux: "Int32", typ: "UInt64", clobberFlags: true},                                                   // arg0 + auxint
   252  		{name: "ADDWconst", argLength: 1, reg: gp11sp, asm: "ADDW", aux: "Int32", clobberFlags: true},                                                                // arg0 + auxint
   253  		{name: "ADDload", argLength: 3, reg: gpopload, asm: "ADD", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"},   // arg0 + *arg1. arg2=mem
   254  		{name: "ADDWload", argLength: 3, reg: gpopload, asm: "ADDW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 + *arg1. arg2=mem
   255  
   256  		{name: "SUB", argLength: 2, reg: gp21, asm: "SUB", clobberFlags: true},                                                                                       // arg0 - arg1
   257  		{name: "SUBW", argLength: 2, reg: gp21, asm: "SUBW", clobberFlags: true},                                                                                     // arg0 - arg1
   258  		{name: "SUBconst", argLength: 1, reg: gp11, asm: "SUB", aux: "Int32", resultInArg0: true, clobberFlags: true},                                                // arg0 - auxint
   259  		{name: "SUBWconst", argLength: 1, reg: gp11, asm: "SUBW", aux: "Int32", resultInArg0: true, clobberFlags: true},                                              // arg0 - auxint
   260  		{name: "SUBload", argLength: 3, reg: gpopload, asm: "SUB", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"},   // arg0 - *arg1. arg2=mem
   261  		{name: "SUBWload", argLength: 3, reg: gpopload, asm: "SUBW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 - *arg1. arg2=mem
   262  
   263  		{name: "MULLD", argLength: 2, reg: gp21, asm: "MULLD", typ: "Int64", commutative: true, resultInArg0: true, clobberFlags: true},                                // arg0 * arg1
   264  		{name: "MULLW", argLength: 2, reg: gp21, asm: "MULLW", typ: "Int32", commutative: true, resultInArg0: true, clobberFlags: true},                                // arg0 * arg1
   265  		{name: "MULLDconst", argLength: 1, reg: gp11, asm: "MULLD", aux: "Int32", typ: "Int64", resultInArg0: true, clobberFlags: true},                                // arg0 * auxint
   266  		{name: "MULLWconst", argLength: 1, reg: gp11, asm: "MULLW", aux: "Int32", typ: "Int32", resultInArg0: true, clobberFlags: true},                                // arg0 * auxint
   267  		{name: "MULLDload", argLength: 3, reg: gpopload, asm: "MULLD", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 * *arg1. arg2=mem
   268  		{name: "MULLWload", argLength: 3, reg: gpopload, asm: "MULLW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 * *arg1. arg2=mem
   269  
   270  		{name: "MULHD", argLength: 2, reg: gp21tmp, asm: "MULHD", typ: "Int64", commutative: true, resultInArg0: true, clobberFlags: true},   // (arg0 * arg1) >> width
   271  		{name: "MULHDU", argLength: 2, reg: gp21tmp, asm: "MULHDU", typ: "Int64", commutative: true, resultInArg0: true, clobberFlags: true}, // (arg0 * arg1) >> width
   272  
   273  		{name: "DIVD", argLength: 2, reg: gp21tmp, asm: "DIVD", resultInArg0: true, clobberFlags: true},   // arg0 / arg1
   274  		{name: "DIVW", argLength: 2, reg: gp21tmp, asm: "DIVW", resultInArg0: true, clobberFlags: true},   // arg0 / arg1
   275  		{name: "DIVDU", argLength: 2, reg: gp21tmp, asm: "DIVDU", resultInArg0: true, clobberFlags: true}, // arg0 / arg1
   276  		{name: "DIVWU", argLength: 2, reg: gp21tmp, asm: "DIVWU", resultInArg0: true, clobberFlags: true}, // arg0 / arg1
   277  
   278  		{name: "MODD", argLength: 2, reg: gp21tmp, asm: "MODD", resultInArg0: true, clobberFlags: true}, // arg0 % arg1
   279  		{name: "MODW", argLength: 2, reg: gp21tmp, asm: "MODW", resultInArg0: true, clobberFlags: true}, // arg0 % arg1
   280  
   281  		{name: "MODDU", argLength: 2, reg: gp21tmp, asm: "MODDU", resultInArg0: true, clobberFlags: true}, // arg0 % arg1
   282  		{name: "MODWU", argLength: 2, reg: gp21tmp, asm: "MODWU", resultInArg0: true, clobberFlags: true}, // arg0 % arg1
   283  
   284  		{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true, clobberFlags: true},                                                                    // arg0 & arg1
   285  		{name: "ANDW", argLength: 2, reg: gp21, asm: "ANDW", commutative: true, clobberFlags: true},                                                                  // arg0 & arg1
   286  		{name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int64", resultInArg0: true, clobberFlags: true},                                                // arg0 & auxint
   287  		{name: "ANDWconst", argLength: 1, reg: gp11, asm: "ANDW", aux: "Int32", resultInArg0: true, clobberFlags: true},                                              // arg0 & auxint
   288  		{name: "ANDload", argLength: 3, reg: gpopload, asm: "AND", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"},   // arg0 & *arg1. arg2=mem
   289  		{name: "ANDWload", argLength: 3, reg: gpopload, asm: "ANDW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 & *arg1. arg2=mem
   290  
   291  		{name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true, clobberFlags: true},                                                                    // arg0 | arg1
   292  		{name: "ORW", argLength: 2, reg: gp21, asm: "ORW", commutative: true, clobberFlags: true},                                                                  // arg0 | arg1
   293  		{name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int64", resultInArg0: true, clobberFlags: true},                                                // arg0 | auxint
   294  		{name: "ORWconst", argLength: 1, reg: gp11, asm: "ORW", aux: "Int32", resultInArg0: true, clobberFlags: true},                                              // arg0 | auxint
   295  		{name: "ORload", argLength: 3, reg: gpopload, asm: "OR", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"},   // arg0 | *arg1. arg2=mem
   296  		{name: "ORWload", argLength: 3, reg: gpopload, asm: "ORW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 | *arg1. arg2=mem
   297  
   298  		{name: "XOR", argLength: 2, reg: gp21, asm: "XOR", commutative: true, clobberFlags: true},                                                                    // arg0 ^ arg1
   299  		{name: "XORW", argLength: 2, reg: gp21, asm: "XORW", commutative: true, clobberFlags: true},                                                                  // arg0 ^ arg1
   300  		{name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int64", resultInArg0: true, clobberFlags: true},                                                // arg0 ^ auxint
   301  		{name: "XORWconst", argLength: 1, reg: gp11, asm: "XORW", aux: "Int32", resultInArg0: true, clobberFlags: true},                                              // arg0 ^ auxint
   302  		{name: "XORload", argLength: 3, reg: gpopload, asm: "XOR", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"},   // arg0 ^ *arg1. arg2=mem
   303  		{name: "XORWload", argLength: 3, reg: gpopload, asm: "XORW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 ^ *arg1. arg2=mem
   304  
   305  		// Arithmetic ops with carry/borrow chain.
   306  		//
   307  		// A carry is represented by a condition code of 2 or 3 (GT or OV).
   308  		// A borrow is represented by a condition code of 0 or 1 (EQ or LT).
   309  		{name: "ADDC", argLength: 2, reg: gp21flags, asm: "ADDC", typ: "(UInt64,Flags)", commutative: true},                          // (arg0 + arg1, carry out)
   310  		{name: "ADDCconst", argLength: 1, reg: gp11flags, asm: "ADDC", typ: "(UInt64,Flags)", aux: "Int16"},                          // (arg0 + auxint, carry out)
   311  		{name: "ADDE", argLength: 3, reg: gp2flags1flags, asm: "ADDE", typ: "(UInt64,Flags)", commutative: true, resultInArg0: true}, // (arg0 + arg1 + arg2 (carry in), carry out)
   312  		{name: "SUBC", argLength: 2, reg: gp21flags, asm: "SUBC", typ: "(UInt64,Flags)"},                                             // (arg0 - arg1, borrow out)
   313  		{name: "SUBE", argLength: 3, reg: gp2flags1flags, asm: "SUBE", typ: "(UInt64,Flags)", resultInArg0: true},                    // (arg0 - arg1 - arg2 (borrow in), borrow out)
   314  
   315  		// Comparisons.
   316  		{name: "CMP", argLength: 2, reg: gp2flags, asm: "CMP", typ: "Flags"},   // arg0 compare to arg1
   317  		{name: "CMPW", argLength: 2, reg: gp2flags, asm: "CMPW", typ: "Flags"}, // arg0 compare to arg1
   318  
   319  		{name: "CMPU", argLength: 2, reg: gp2flags, asm: "CMPU", typ: "Flags"},   // arg0 compare to arg1
   320  		{name: "CMPWU", argLength: 2, reg: gp2flags, asm: "CMPWU", typ: "Flags"}, // arg0 compare to arg1
   321  
   322  		{name: "CMPconst", argLength: 1, reg: gp1flags, asm: "CMP", typ: "Flags", aux: "Int32"},     // arg0 compare to auxint
   323  		{name: "CMPWconst", argLength: 1, reg: gp1flags, asm: "CMPW", typ: "Flags", aux: "Int32"},   // arg0 compare to auxint
   324  		{name: "CMPUconst", argLength: 1, reg: gp1flags, asm: "CMPU", typ: "Flags", aux: "Int32"},   // arg0 compare to auxint
   325  		{name: "CMPWUconst", argLength: 1, reg: gp1flags, asm: "CMPWU", typ: "Flags", aux: "Int32"}, // arg0 compare to auxint
   326  
   327  		{name: "FCMPS", argLength: 2, reg: fp2flags, asm: "CEBR", typ: "Flags"},  // arg0 compare to arg1, f32
   328  		{name: "FCMP", argLength: 2, reg: fp2flags, asm: "FCMPU", typ: "Flags"},  // arg0 compare to arg1, f64
   329  		{name: "LTDBR", argLength: 1, reg: fp1flags, asm: "LTDBR", typ: "Flags"}, // arg0 compare to 0, f64
   330  		{name: "LTEBR", argLength: 1, reg: fp1flags, asm: "LTEBR", typ: "Flags"}, // arg0 compare to 0, f32
   331  
   332  		{name: "SLD", argLength: 2, reg: sh21, asm: "SLD"},                    // arg0 << arg1, shift amount is mod 64
   333  		{name: "SLW", argLength: 2, reg: sh21, asm: "SLW"},                    // arg0 << arg1, shift amount is mod 64
   334  		{name: "SLDconst", argLength: 1, reg: gp11, asm: "SLD", aux: "UInt8"}, // arg0 << auxint, shift amount 0-63
   335  		{name: "SLWconst", argLength: 1, reg: gp11, asm: "SLW", aux: "UInt8"}, // arg0 << auxint, shift amount 0-31
   336  
   337  		{name: "SRD", argLength: 2, reg: sh21, asm: "SRD"},                    // unsigned arg0 >> arg1, shift amount is mod 64
   338  		{name: "SRW", argLength: 2, reg: sh21, asm: "SRW"},                    // unsigned uint32(arg0) >> arg1, shift amount is mod 64
   339  		{name: "SRDconst", argLength: 1, reg: gp11, asm: "SRD", aux: "UInt8"}, // unsigned arg0 >> auxint, shift amount 0-63
   340  		{name: "SRWconst", argLength: 1, reg: gp11, asm: "SRW", aux: "UInt8"}, // unsigned uint32(arg0) >> auxint, shift amount 0-31
   341  
   342  		// Arithmetic shifts clobber flags.
   343  		{name: "SRAD", argLength: 2, reg: sh21, asm: "SRAD", clobberFlags: true},                    // signed arg0 >> arg1, shift amount is mod 64
   344  		{name: "SRAW", argLength: 2, reg: sh21, asm: "SRAW", clobberFlags: true},                    // signed int32(arg0) >> arg1, shift amount is mod 64
   345  		{name: "SRADconst", argLength: 1, reg: gp11, asm: "SRAD", aux: "UInt8", clobberFlags: true}, // signed arg0 >> auxint, shift amount 0-63
   346  		{name: "SRAWconst", argLength: 1, reg: gp11, asm: "SRAW", aux: "UInt8", clobberFlags: true}, // signed int32(arg0) >> auxint, shift amount 0-31
   347  
   348  		// Rotate instructions.
   349  		// Note: no RLLGconst - use RISBGZ instead.
   350  		{name: "RLLG", argLength: 2, reg: sh21, asm: "RLLG"},                  // arg0 rotate left arg1, rotate amount 0-63
   351  		{name: "RLL", argLength: 2, reg: sh21, asm: "RLL"},                    // arg0 rotate left arg1, rotate amount 0-31
   352  		{name: "RLLconst", argLength: 1, reg: gp11, asm: "RLL", aux: "UInt8"}, // arg0 rotate left auxint, rotate amount 0-31
   353  
   354  		// Rotate then (and|or|xor|insert) selected bits instructions.
   355  		//
   356  		// Aux is an s390x.RotateParams struct containing Start, End and rotation
   357  		// Amount fields.
   358  		//
   359  		// arg1 is rotated left by the rotation amount then the bits from the start
   360  		// bit to the end bit (inclusive) are combined with arg0 using the logical
   361  		// operation specified. Bit indices are specified from left to right - the
   362  		// MSB is 0 and the LSB is 63.
   363  		//
   364  		// Examples:
   365  		//               |          aux         |
   366  		// | instruction | start | end | amount |          arg0         |          arg1         |         result        |
   367  		// +-------------+-------+-----+--------+-----------------------+-----------------------+-----------------------+
   368  		// | RXSBG (XOR) |     0 |   1 |      0 | 0xffff_ffff_ffff_ffff | 0xffff_ffff_ffff_ffff | 0x3fff_ffff_ffff_ffff |
   369  		// | RXSBG (XOR) |    62 |  63 |      0 | 0xffff_ffff_ffff_ffff | 0xffff_ffff_ffff_ffff | 0xffff_ffff_ffff_fffc |
   370  		// | RXSBG (XOR) |     0 |  47 |     16 | 0xffff_ffff_ffff_ffff | 0x0000_0000_0000_ffff | 0xffff_ffff_0000_ffff |
   371  		// +-------------+-------+-----+--------+-----------------------+-----------------------+-----------------------+
   372  		//
   373  		{name: "RXSBG", argLength: 2, reg: gp21, asm: "RXSBG", resultInArg0: true, aux: "S390XRotateParams", clobberFlags: true}, // rotate then xor selected bits
   374  		{name: "RISBGZ", argLength: 1, reg: gp11, asm: "RISBGZ", aux: "S390XRotateParams", clobberFlags: true},                   // rotate then insert selected bits [into zero]
   375  
   376  		// unary ops
   377  		{name: "NEG", argLength: 1, reg: gp11, asm: "NEG", clobberFlags: true},   // -arg0
   378  		{name: "NEGW", argLength: 1, reg: gp11, asm: "NEGW", clobberFlags: true}, // -arg0
   379  
   380  		{name: "NOT", argLength: 1, reg: gp11, resultInArg0: true, clobberFlags: true},  // ^arg0
   381  		{name: "NOTW", argLength: 1, reg: gp11, resultInArg0: true, clobberFlags: true}, // ^arg0
   382  
   383  		{name: "FSQRT", argLength: 1, reg: fp11, asm: "FSQRT"},   // sqrt(arg0)
   384  		{name: "FSQRTS", argLength: 1, reg: fp11, asm: "FSQRTS"}, // sqrt(arg0), float32
   385  
   386  		// Conditional register-register moves.
   387  		// The aux for these values is an s390x.CCMask value representing the condition code mask.
   388  		{name: "LOCGR", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "LOCGR", aux: "S390XCCMask"}, // load arg1 into arg0 if the condition code in arg2 matches a masked bit in aux.
   389  
   390  		{name: "MOVBreg", argLength: 1, reg: gp11sp, asm: "MOVB", typ: "Int64"},    // sign extend arg0 from int8 to int64
   391  		{name: "MOVBZreg", argLength: 1, reg: gp11sp, asm: "MOVBZ", typ: "UInt64"}, // zero extend arg0 from int8 to int64
   392  		{name: "MOVHreg", argLength: 1, reg: gp11sp, asm: "MOVH", typ: "Int64"},    // sign extend arg0 from int16 to int64
   393  		{name: "MOVHZreg", argLength: 1, reg: gp11sp, asm: "MOVHZ", typ: "UInt64"}, // zero extend arg0 from int16 to int64
   394  		{name: "MOVWreg", argLength: 1, reg: gp11sp, asm: "MOVW", typ: "Int64"},    // sign extend arg0 from int32 to int64
   395  		{name: "MOVWZreg", argLength: 1, reg: gp11sp, asm: "MOVWZ", typ: "UInt64"}, // zero extend arg0 from int32 to int64
   396  
   397  		{name: "MOVDconst", reg: gp01, asm: "MOVD", typ: "UInt64", aux: "Int64", rematerializeable: true}, // auxint
   398  
   399  		{name: "LDGR", argLength: 1, reg: gpfp, asm: "LDGR"}, // move int64 to float64 (no conversion)
   400  		{name: "LGDR", argLength: 1, reg: fpgp, asm: "LGDR"}, // move float64 to int64 (no conversion)
   401  
   402  		{name: "CFDBRA", argLength: 1, reg: fpgp, asm: "CFDBRA", clobberFlags: true}, // convert float64 to int32
   403  		{name: "CGDBRA", argLength: 1, reg: fpgp, asm: "CGDBRA", clobberFlags: true}, // convert float64 to int64
   404  		{name: "CFEBRA", argLength: 1, reg: fpgp, asm: "CFEBRA", clobberFlags: true}, // convert float32 to int32
   405  		{name: "CGEBRA", argLength: 1, reg: fpgp, asm: "CGEBRA", clobberFlags: true}, // convert float32 to int64
   406  		{name: "CEFBRA", argLength: 1, reg: gpfp, asm: "CEFBRA", clobberFlags: true}, // convert int32 to float32
   407  		{name: "CDFBRA", argLength: 1, reg: gpfp, asm: "CDFBRA", clobberFlags: true}, // convert int32 to float64
   408  		{name: "CEGBRA", argLength: 1, reg: gpfp, asm: "CEGBRA", clobberFlags: true}, // convert int64 to float32
   409  		{name: "CDGBRA", argLength: 1, reg: gpfp, asm: "CDGBRA", clobberFlags: true}, // convert int64 to float64
   410  		{name: "CLFEBR", argLength: 1, reg: fpgp, asm: "CLFEBR", clobberFlags: true}, // convert float32 to uint32
   411  		{name: "CLFDBR", argLength: 1, reg: fpgp, asm: "CLFDBR", clobberFlags: true}, // convert float64 to uint32
   412  		{name: "CLGEBR", argLength: 1, reg: fpgp, asm: "CLGEBR", clobberFlags: true}, // convert float32 to uint64
   413  		{name: "CLGDBR", argLength: 1, reg: fpgp, asm: "CLGDBR", clobberFlags: true}, // convert float64 to uint64
   414  		{name: "CELFBR", argLength: 1, reg: gpfp, asm: "CELFBR", clobberFlags: true}, // convert uint32 to float32
   415  		{name: "CDLFBR", argLength: 1, reg: gpfp, asm: "CDLFBR", clobberFlags: true}, // convert uint32 to float64
   416  		{name: "CELGBR", argLength: 1, reg: gpfp, asm: "CELGBR", clobberFlags: true}, // convert uint64 to float32
   417  		{name: "CDLGBR", argLength: 1, reg: gpfp, asm: "CDLGBR", clobberFlags: true}, // convert uint64 to float64
   418  
   419  		{name: "LEDBR", argLength: 1, reg: fp11, asm: "LEDBR"}, // convert float64 to float32
   420  		{name: "LDEBR", argLength: 1, reg: fp11, asm: "LDEBR"}, // convert float32 to float64
   421  
   422  		{name: "MOVDaddr", argLength: 1, reg: addr, aux: "SymOff", rematerializeable: true, symEffect: "Addr"}, // arg0 + auxint + offset encoded in aux
   423  		{name: "MOVDaddridx", argLength: 2, reg: addridx, aux: "SymOff", symEffect: "Addr"},                    // arg0 + arg1 + auxint + aux
   424  
   425  		// auxint+aux == add auxint and the offset of the symbol in aux (if any) to the effective address
   426  		{name: "MOVBZload", argLength: 2, reg: gpload, asm: "MOVBZ", aux: "SymOff", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"},  // load byte from arg0+auxint+aux. arg1=mem.  Zero extend.
   427  		{name: "MOVBload", argLength: 2, reg: gpload, asm: "MOVB", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"},                  // ditto, sign extend to int64
   428  		{name: "MOVHZload", argLength: 2, reg: gpload, asm: "MOVHZ", aux: "SymOff", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // load 2 bytes from arg0+auxint+aux. arg1=mem.  Zero extend.
   429  		{name: "MOVHload", argLength: 2, reg: gpload, asm: "MOVH", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"},                  // ditto, sign extend to int64
   430  		{name: "MOVWZload", argLength: 2, reg: gpload, asm: "MOVWZ", aux: "SymOff", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // load 4 bytes from arg0+auxint+aux. arg1=mem.  Zero extend.
   431  		{name: "MOVWload", argLength: 2, reg: gpload, asm: "MOVW", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"},                  // ditto, sign extend to int64
   432  		{name: "MOVDload", argLength: 2, reg: gpload, asm: "MOVD", aux: "SymOff", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"},   // load 8 bytes from arg0+auxint+aux. arg1=mem
   433  
   434  		{name: "MOVWBR", argLength: 1, reg: gp11, asm: "MOVWBR"}, // arg0 swap bytes
   435  		{name: "MOVDBR", argLength: 1, reg: gp11, asm: "MOVDBR"}, // arg0 swap bytes
   436  
   437  		{name: "MOVHBRload", argLength: 2, reg: gpload, asm: "MOVHBR", aux: "SymOff", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // load 2 bytes from arg0+auxint+aux. arg1=mem. Reverse bytes.
   438  		{name: "MOVWBRload", argLength: 2, reg: gpload, asm: "MOVWBR", aux: "SymOff", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // load 4 bytes from arg0+auxint+aux. arg1=mem. Reverse bytes.
   439  		{name: "MOVDBRload", argLength: 2, reg: gpload, asm: "MOVDBR", aux: "SymOff", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"}, // load 8 bytes from arg0+auxint+aux. arg1=mem. Reverse bytes.
   440  
   441  		{name: "MOVBstore", argLength: 3, reg: gpstore, asm: "MOVB", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"},       // store byte in arg1 to arg0+auxint+aux. arg2=mem
   442  		{name: "MOVHstore", argLength: 3, reg: gpstore, asm: "MOVH", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"},       // store 2 bytes in arg1 to arg0+auxint+aux. arg2=mem
   443  		{name: "MOVWstore", argLength: 3, reg: gpstore, asm: "MOVW", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"},       // store 4 bytes in arg1 to arg0+auxint+aux. arg2=mem
   444  		{name: "MOVDstore", argLength: 3, reg: gpstore, asm: "MOVD", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"},       // store 8 bytes in arg1 to arg0+auxint+aux. arg2=mem
   445  		{name: "MOVHBRstore", argLength: 3, reg: gpstorebr, asm: "MOVHBR", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 2 bytes in arg1 to arg0+auxint+aux. arg2=mem. Reverse bytes.
   446  		{name: "MOVWBRstore", argLength: 3, reg: gpstorebr, asm: "MOVWBR", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 4 bytes in arg1 to arg0+auxint+aux. arg2=mem. Reverse bytes.
   447  		{name: "MOVDBRstore", argLength: 3, reg: gpstorebr, asm: "MOVDBR", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes in arg1 to arg0+auxint+aux. arg2=mem. Reverse bytes.
   448  
   449  		{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
   450  
   451  		// indexed loads/stores
   452  		{name: "MOVBZloadidx", argLength: 3, reg: gploadidx, commutative: true, asm: "MOVBZ", aux: "SymOff", typ: "UInt8", symEffect: "Read"},   // load a byte from arg0+arg1+auxint+aux. arg2=mem. Zero extend.
   453  		{name: "MOVBloadidx", argLength: 3, reg: gploadidx, commutative: true, asm: "MOVB", aux: "SymOff", typ: "Int8", symEffect: "Read"},      // load a byte from arg0+arg1+auxint+aux. arg2=mem. Sign extend.
   454  		{name: "MOVHZloadidx", argLength: 3, reg: gploadidx, commutative: true, asm: "MOVHZ", aux: "SymOff", typ: "UInt16", symEffect: "Read"},  // load 2 bytes from arg0+arg1+auxint+aux. arg2=mem. Zero extend.
   455  		{name: "MOVHloadidx", argLength: 3, reg: gploadidx, commutative: true, asm: "MOVH", aux: "SymOff", typ: "Int16", symEffect: "Read"},     // load 2 bytes from arg0+arg1+auxint+aux. arg2=mem. Sign extend.
   456  		{name: "MOVWZloadidx", argLength: 3, reg: gploadidx, commutative: true, asm: "MOVWZ", aux: "SymOff", typ: "UInt32", symEffect: "Read"},  // load 4 bytes from arg0+arg1+auxint+aux. arg2=mem. Zero extend.
   457  		{name: "MOVWloadidx", argLength: 3, reg: gploadidx, commutative: true, asm: "MOVW", aux: "SymOff", typ: "Int32", symEffect: "Read"},     // load 4 bytes from arg0+arg1+auxint+aux. arg2=mem. Sign extend.
   458  		{name: "MOVDloadidx", argLength: 3, reg: gploadidx, commutative: true, asm: "MOVD", aux: "SymOff", typ: "UInt64", symEffect: "Read"},    // load 8 bytes from arg0+arg1+auxint+aux. arg2=mem
   459  		{name: "MOVHBRloadidx", argLength: 3, reg: gploadidx, commutative: true, asm: "MOVHBR", aux: "SymOff", typ: "Int16", symEffect: "Read"}, // load 2 bytes from arg0+arg1+auxint+aux. arg2=mem. Reverse bytes.
   460  		{name: "MOVWBRloadidx", argLength: 3, reg: gploadidx, commutative: true, asm: "MOVWBR", aux: "SymOff", typ: "Int32", symEffect: "Read"}, // load 4 bytes from arg0+arg1+auxint+aux. arg2=mem. Reverse bytes.
   461  		{name: "MOVDBRloadidx", argLength: 3, reg: gploadidx, commutative: true, asm: "MOVDBR", aux: "SymOff", typ: "Int64", symEffect: "Read"}, // load 8 bytes from arg0+arg1+auxint+aux. arg2=mem. Reverse bytes.
   462  		{name: "MOVBstoreidx", argLength: 4, reg: gpstoreidx, commutative: true, asm: "MOVB", aux: "SymOff", symEffect: "Write"},                // store byte in arg2 to arg0+arg1+auxint+aux. arg3=mem
   463  		{name: "MOVHstoreidx", argLength: 4, reg: gpstoreidx, commutative: true, asm: "MOVH", aux: "SymOff", symEffect: "Write"},                // store 2 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem
   464  		{name: "MOVWstoreidx", argLength: 4, reg: gpstoreidx, commutative: true, asm: "MOVW", aux: "SymOff", symEffect: "Write"},                // store 4 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem
   465  		{name: "MOVDstoreidx", argLength: 4, reg: gpstoreidx, commutative: true, asm: "MOVD", aux: "SymOff", symEffect: "Write"},                // store 8 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem
   466  		{name: "MOVHBRstoreidx", argLength: 4, reg: gpstoreidx, commutative: true, asm: "MOVHBR", aux: "SymOff", symEffect: "Write"},            // store 2 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem. Reverse bytes.
   467  		{name: "MOVWBRstoreidx", argLength: 4, reg: gpstoreidx, commutative: true, asm: "MOVWBR", aux: "SymOff", symEffect: "Write"},            // store 4 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem. Reverse bytes.
   468  		{name: "MOVDBRstoreidx", argLength: 4, reg: gpstoreidx, commutative: true, asm: "MOVDBR", aux: "SymOff", symEffect: "Write"},            // store 8 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem. Reverse bytes.
   469  
   470  		// For storeconst ops, the AuxInt field encodes both
   471  		// the value to store and an address offset of the store.
   472  		// Cast AuxInt to a ValAndOff to extract Val and Off fields.
   473  		{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
   474  		{name: "MOVHstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVH", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store low 2 bytes of ...
   475  		{name: "MOVWstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVW", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store low 4 bytes of ...
   476  		{name: "MOVDstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVD", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of ...
   477  
   478  		{name: "CLEAR", argLength: 2, reg: regInfo{inputs: []regMask{ptr, 0}}, asm: "CLEAR", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, symEffect: "Write"},
   479  
   480  		{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                                                // call static function aux.(*obj.LSym).  arg0=mem, auxint=argsize, returns mem
   481  		{name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true},                                  // tail call static function aux.(*obj.LSym).  arg0=mem, auxint=argsize, returns mem
   482  		{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{ptrsp, buildReg("R12"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
   483  		{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{ptr}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                         // call fn by pointer.  arg0=codeptr, arg1=mem, auxint=argsize, returns mem
   484  
   485  		// (InvertFlags (CMP a b)) == (CMP b a)
   486  		// InvertFlags is a pseudo-op which can't appear in assembly output.
   487  		{name: "InvertFlags", argLength: 1}, // reverse direction of arg0
   488  
   489  		// Pseudo-ops
   490  		{name: "LoweredGetG", argLength: 1, reg: gp01}, // arg0=mem
   491  		// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
   492  		// and sorts it to the very beginning of the block to prevent other
   493  		// use of R12 (the closure pointer)
   494  		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R12")}}, zeroWidth: true},
   495  		// arg0=ptr,arg1=mem, returns void.  Faults if ptr is nil.
   496  		// LoweredGetCallerSP returns the SP of the caller of the current function. arg0=mem.
   497  		{name: "LoweredGetCallerSP", argLength: 1, reg: gp01, rematerializeable: true},
   498  		// LoweredGetCallerPC evaluates to the PC to which its "caller" will return.
   499  		// I.e., if f calls g "calls" getcallerpc,
   500  		// the result should be the PC within f that g will return to.
   501  		// See runtime/stubs.go for a more detailed discussion.
   502  		{name: "LoweredGetCallerPC", reg: gp01, rematerializeable: true},
   503  		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{ptrsp}}, clobberFlags: true, nilCheck: true, faultOnNilArg0: true},
   504  		// Round ops to block fused-multiply-add extraction.
   505  		{name: "LoweredRound32F", argLength: 1, reg: fp11, resultInArg0: true, zeroWidth: true},
   506  		{name: "LoweredRound64F", argLength: 1, reg: fp11, resultInArg0: true, zeroWidth: true},
   507  
   508  		// LoweredWB invokes runtime.gcWriteBarrier. arg0=mem, aux=# of buffer entries needed
   509  		// It saves all GP registers if necessary,
   510  		// but clobbers R14 (LR) because it's a call,
   511  		// and also clobbers R1 as the PLT stub does.
   512  		// Returns a pointer to a write barrier buffer in R9.
   513  		{name: "LoweredWB", argLength: 1, reg: regInfo{clobbers: (callerSave &^ gpg) | buildReg("R14") | r1, outputs: []regMask{r9}}, clobberFlags: true, aux: "Int64"},
   514  
   515  		// There are three of these functions so that they can have three different register inputs.
   516  		// When we check 0 <= c <= cap (A), then 0 <= b <= c (B), then 0 <= a <= b (C), we want the
   517  		// default registers to match so we don't need to copy registers around unnecessarily.
   518  		{name: "LoweredPanicBoundsA", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r2, r3}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in generic.go).
   519  		{name: "LoweredPanicBoundsB", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r1, r2}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in generic.go).
   520  		{name: "LoweredPanicBoundsC", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r0, r1}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in generic.go).
   521  
   522  		// Constant condition code values. The condition code can be 0, 1, 2 or 3.
   523  		{name: "FlagEQ"}, // CC=0 (equal)
   524  		{name: "FlagLT"}, // CC=1 (less than)
   525  		{name: "FlagGT"}, // CC=2 (greater than)
   526  		{name: "FlagOV"}, // CC=3 (overflow)
   527  
   528  		// Fast-BCR-serialization to ensure store-load ordering.
   529  		{name: "SYNC", argLength: 1, reg: sync, asm: "SYNC", typ: "Mem"},
   530  
   531  		// Atomic loads. These are just normal loads but return <value,memory> tuples
   532  		// so they can be properly ordered with other loads.
   533  		// load from arg0+auxint+aux.  arg1=mem.
   534  		{name: "MOVBZatomicload", argLength: 2, reg: gpload, asm: "MOVBZ", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"},
   535  		{name: "MOVWZatomicload", argLength: 2, reg: gpload, asm: "MOVWZ", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"},
   536  		{name: "MOVDatomicload", argLength: 2, reg: gpload, asm: "MOVD", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"},
   537  
   538  		// Atomic stores. These are just normal stores.
   539  		// store arg1 to arg0+auxint+aux. arg2=mem.
   540  		{name: "MOVBatomicstore", argLength: 3, reg: gpstore, asm: "MOVB", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "Write"},
   541  		{name: "MOVWatomicstore", argLength: 3, reg: gpstore, asm: "MOVW", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "Write"},
   542  		{name: "MOVDatomicstore", argLength: 3, reg: gpstore, asm: "MOVD", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "Write"},
   543  
   544  		// Atomic adds.
   545  		// *(arg0+auxint+aux) += arg1.  arg2=mem.
   546  		// Returns a tuple of <old contents of *(arg0+auxint+aux), memory>.
   547  		{name: "LAA", argLength: 3, reg: gpstorelaa, asm: "LAA", typ: "(UInt32,Mem)", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"},
   548  		{name: "LAAG", argLength: 3, reg: gpstorelaa, asm: "LAAG", typ: "(UInt64,Mem)", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"},
   549  		{name: "AddTupleFirst32", argLength: 2}, // arg1=tuple <x,y>.  Returns <x+arg0,y>.
   550  		{name: "AddTupleFirst64", argLength: 2}, // arg1=tuple <x,y>.  Returns <x+arg0,y>.
   551  
   552  		// Atomic bitwise operations.
   553  		// Note: 'floor' operations round the pointer down to the nearest word boundary
   554  		// which reflects how they are used in the runtime.
   555  		{name: "LAN", argLength: 3, reg: gpstore, asm: "LAN", typ: "Mem", clobberFlags: true, hasSideEffects: true},         // *arg0 &= arg1. arg2 = mem.
   556  		{name: "LANfloor", argLength: 3, reg: gpstorelab, asm: "LAN", typ: "Mem", clobberFlags: true, hasSideEffects: true}, // *(floor(arg0, 4)) &= arg1. arg2 = mem.
   557  		{name: "LAO", argLength: 3, reg: gpstore, asm: "LAO", typ: "Mem", clobberFlags: true, hasSideEffects: true},         // *arg0 |= arg1. arg2 = mem.
   558  		{name: "LAOfloor", argLength: 3, reg: gpstorelab, asm: "LAO", typ: "Mem", clobberFlags: true, hasSideEffects: true}, // *(floor(arg0, 4)) |= arg1. arg2 = mem.
   559  
   560  		// Compare and swap.
   561  		// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory.
   562  		// if *(arg0+auxint+aux) == arg1 {
   563  		//   *(arg0+auxint+aux) = arg2
   564  		//   return (true, memory)
   565  		// } else {
   566  		//   return (false, memory)
   567  		// }
   568  		// Note that these instructions also return the old value in arg1, but we ignore it.
   569  		// TODO: have these return flags instead of bool.  The current system generates:
   570  		//    CS ...
   571  		//    MOVD  $0, ret
   572  		//    BNE   2(PC)
   573  		//    MOVD  $1, ret
   574  		//    CMPW  ret, $0
   575  		//    BNE ...
   576  		// instead of just
   577  		//    CS ...
   578  		//    BEQ ...
   579  		// but we can't do that because memory-using ops can't generate flags yet
   580  		// (flagalloc wants to move flag-generating instructions around).
   581  		{name: "LoweredAtomicCas32", argLength: 4, reg: cas, asm: "CS", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"},
   582  		{name: "LoweredAtomicCas64", argLength: 4, reg: cas, asm: "CSG", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"},
   583  
   584  		// Lowered atomic swaps, emulated using compare-and-swap.
   585  		// store arg1 to arg0+auxint+aux, arg2=mem.
   586  		{name: "LoweredAtomicExchange32", argLength: 3, reg: exchange, asm: "CS", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"},
   587  		{name: "LoweredAtomicExchange64", argLength: 3, reg: exchange, asm: "CSG", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"},
   588  
   589  		// find leftmost one
   590  		{
   591  			name:         "FLOGR",
   592  			argLength:    1,
   593  			reg:          regInfo{inputs: gponly, outputs: []regMask{buildReg("R0")}, clobbers: buildReg("R1")},
   594  			asm:          "FLOGR",
   595  			typ:          "UInt64",
   596  			clobberFlags: true,
   597  		},
   598  
   599  		// population count
   600  		//
   601  		// Counts the number of ones in each byte of arg0
   602  		// and places the result into the corresponding byte
   603  		// of the result.
   604  		{
   605  			name:         "POPCNT",
   606  			argLength:    1,
   607  			reg:          gp11,
   608  			asm:          "POPCNT",
   609  			typ:          "UInt64",
   610  			clobberFlags: true,
   611  		},
   612  
   613  		// unsigned multiplication (64x64 → 128)
   614  		//
   615  		// Multiply the two 64-bit input operands together and place the 128-bit result into
   616  		// an even-odd register pair. The second register in the target pair also contains
   617  		// one of the input operands. Since we don't currently have a way to specify an
   618  		// even-odd register pair we hardcode this register pair as R2:R3.
   619  		{
   620  			name:      "MLGR",
   621  			argLength: 2,
   622  			reg:       regInfo{inputs: []regMask{gp, r3}, outputs: []regMask{r2, r3}},
   623  			asm:       "MLGR",
   624  		},
   625  
   626  		// pseudo operations to sum the output of the POPCNT instruction
   627  		{name: "SumBytes2", argLength: 1, typ: "UInt8"}, // sum the rightmost 2 bytes in arg0 ignoring overflow
   628  		{name: "SumBytes4", argLength: 1, typ: "UInt8"}, // sum the rightmost 4 bytes in arg0 ignoring overflow
   629  		{name: "SumBytes8", argLength: 1, typ: "UInt8"}, // sum all the bytes in arg0 ignoring overflow
   630  
   631  		// store multiple
   632  		{
   633  			name:           "STMG2",
   634  			argLength:      4,
   635  			reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), 0}},
   636  			aux:            "SymOff",
   637  			typ:            "Mem",
   638  			asm:            "STMG",
   639  			faultOnNilArg0: true,
   640  			symEffect:      "Write",
   641  			clobberFlags:   true, // TODO(mundaym): currently uses AGFI to handle large offsets
   642  		},
   643  		{
   644  			name:           "STMG3",
   645  			argLength:      5,
   646  			reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), buildReg("R3"), 0}},
   647  			aux:            "SymOff",
   648  			typ:            "Mem",
   649  			asm:            "STMG",
   650  			faultOnNilArg0: true,
   651  			symEffect:      "Write",
   652  			clobberFlags:   true, // TODO(mundaym): currently uses AGFI to handle large offsets
   653  		},
   654  		{
   655  			name:      "STMG4",
   656  			argLength: 6,
   657  			reg: regInfo{inputs: []regMask{
   658  				ptrsp,
   659  				buildReg("R1"),
   660  				buildReg("R2"),
   661  				buildReg("R3"),
   662  				buildReg("R4"),
   663  				0,
   664  			}},
   665  			aux:            "SymOff",
   666  			typ:            "Mem",
   667  			asm:            "STMG",
   668  			faultOnNilArg0: true,
   669  			symEffect:      "Write",
   670  			clobberFlags:   true, // TODO(mundaym): currently uses AGFI to handle large offsets
   671  		},
   672  		{
   673  			name:           "STM2",
   674  			argLength:      4,
   675  			reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), 0}},
   676  			aux:            "SymOff",
   677  			typ:            "Mem",
   678  			asm:            "STMY",
   679  			faultOnNilArg0: true,
   680  			symEffect:      "Write",
   681  			clobberFlags:   true, // TODO(mundaym): currently uses AGFI to handle large offsets
   682  		},
   683  		{
   684  			name:           "STM3",
   685  			argLength:      5,
   686  			reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), buildReg("R3"), 0}},
   687  			aux:            "SymOff",
   688  			typ:            "Mem",
   689  			asm:            "STMY",
   690  			faultOnNilArg0: true,
   691  			symEffect:      "Write",
   692  			clobberFlags:   true, // TODO(mundaym): currently uses AGFI to handle large offsets
   693  		},
   694  		{
   695  			name:      "STM4",
   696  			argLength: 6,
   697  			reg: regInfo{inputs: []regMask{
   698  				ptrsp,
   699  				buildReg("R1"),
   700  				buildReg("R2"),
   701  				buildReg("R3"),
   702  				buildReg("R4"),
   703  				0,
   704  			}},
   705  			aux:            "SymOff",
   706  			typ:            "Mem",
   707  			asm:            "STMY",
   708  			faultOnNilArg0: true,
   709  			symEffect:      "Write",
   710  			clobberFlags:   true, // TODO(mundaym): currently uses AGFI to handle large offsets
   711  		},
   712  
   713  		// large move
   714  		// auxint = remaining bytes after loop (rem)
   715  		// arg0 = address of dst memory (in R1, changed as a side effect)
   716  		// arg1 = address of src memory (in R2, changed as a side effect)
   717  		// arg2 = pointer to last address to move in loop + 256
   718  		// arg3 = mem
   719  		// returns mem
   720  		//
   721  		// mvc: MVC  $256, 0(R2), 0(R1)
   722  		//      MOVD $256(R1), R1
   723  		//      MOVD $256(R2), R2
   724  		//      CMP  R2, Rarg2
   725  		//      BNE  mvc
   726  		//	MVC  $rem, 0(R2), 0(R1) // if rem > 0
   727  		{
   728  			name:      "LoweredMove",
   729  			aux:       "Int64",
   730  			argLength: 4,
   731  			reg: regInfo{
   732  				inputs:   []regMask{buildReg("R1"), buildReg("R2"), gpsp},
   733  				clobbers: buildReg("R1 R2"),
   734  			},
   735  			clobberFlags:   true,
   736  			typ:            "Mem",
   737  			faultOnNilArg0: true,
   738  			faultOnNilArg1: true,
   739  		},
   740  
   741  		// large clear
   742  		// auxint = remaining bytes after loop (rem)
   743  		// arg0 = address of dst memory (in R1, changed as a side effect)
   744  		// arg1 = pointer to last address to zero in loop + 256
   745  		// arg2 = mem
   746  		// returns mem
   747  		//
   748  		// clear: CLEAR $256, 0(R1)
   749  		//        MOVD  $256(R1), R1
   750  		//        CMP   R1, Rarg2
   751  		//        BNE   clear
   752  		//	  CLEAR $rem, 0(R1) // if rem > 0
   753  		{
   754  			name:      "LoweredZero",
   755  			aux:       "Int64",
   756  			argLength: 3,
   757  			reg: regInfo{
   758  				inputs:   []regMask{buildReg("R1"), gpsp},
   759  				clobbers: buildReg("R1"),
   760  			},
   761  			clobberFlags:   true,
   762  			typ:            "Mem",
   763  			faultOnNilArg0: true,
   764  		},
   765  	}
   766  
   767  	// All blocks on s390x have their condition code mask (s390x.CCMask) as the Aux value.
   768  	// The condition code mask is a 4-bit mask where each bit corresponds to a condition
   769  	// code value. If the value of the condition code matches a bit set in the condition
   770  	// code mask then the first successor is executed. Otherwise the second successor is
   771  	// executed.
   772  	//
   773  	// | condition code value |  mask bit  |
   774  	// +----------------------+------------+
   775  	// | 0 (equal)            | 0b1000 (8) |
   776  	// | 1 (less than)        | 0b0100 (4) |
   777  	// | 2 (greater than)     | 0b0010 (2) |
   778  	// | 3 (unordered)        | 0b0001 (1) |
   779  	//
   780  	// Note: that compare-and-branch instructions must not have bit 3 (0b0001) set.
   781  	var S390Xblocks = []blockData{
   782  		// branch on condition
   783  		{name: "BRC", controls: 1, aux: "S390XCCMask"}, // condition code value (flags) is Controls[0]
   784  
   785  		// compare-and-branch (register-register)
   786  		//  - integrates comparison of Controls[0] with Controls[1]
   787  		//  - both control values must be in general purpose registers
   788  		{name: "CRJ", controls: 2, aux: "S390XCCMask"},   // signed 32-bit integer comparison
   789  		{name: "CGRJ", controls: 2, aux: "S390XCCMask"},  // signed 64-bit integer comparison
   790  		{name: "CLRJ", controls: 2, aux: "S390XCCMask"},  // unsigned 32-bit integer comparison
   791  		{name: "CLGRJ", controls: 2, aux: "S390XCCMask"}, // unsigned 64-bit integer comparison
   792  
   793  		// compare-and-branch (register-immediate)
   794  		//  - integrates comparison of Controls[0] with AuxInt
   795  		//  - control value must be in a general purpose register
   796  		//  - the AuxInt value is sign-extended for signed comparisons
   797  		//    and zero-extended for unsigned comparisons
   798  		{name: "CIJ", controls: 1, aux: "S390XCCMaskInt8"},    // signed 32-bit integer comparison
   799  		{name: "CGIJ", controls: 1, aux: "S390XCCMaskInt8"},   // signed 64-bit integer comparison
   800  		{name: "CLIJ", controls: 1, aux: "S390XCCMaskUint8"},  // unsigned 32-bit integer comparison
   801  		{name: "CLGIJ", controls: 1, aux: "S390XCCMaskUint8"}, // unsigned 64-bit integer comparison
   802  	}
   803  
   804  	archs = append(archs, arch{
   805  		name:            "S390X",
   806  		pkg:             "github.com/go-asm/go/cmd/obj/s390x",
   807  		genfile:         "../../s390x/ssa.go",
   808  		ops:             S390Xops,
   809  		blocks:          S390Xblocks,
   810  		regnames:        regNamesS390X,
   811  		gpregmask:       gp,
   812  		fpregmask:       fp,
   813  		framepointerreg: -1, // not used
   814  		linkreg:         int8(num["R14"]),
   815  		imports: []string{
   816  			"github.com/go-asm/go/cmd/obj/s390x",
   817  		},
   818  	})
   819  }