github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/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  	// If you add registers, update asyncPreempt in runtime.
    87  
    88  	//pseudo-registers
    89  	"SB",
    90  }
    91  
    92  func init() {
    93  	// Make map from reg names to reg integers.
    94  	if len(regNamesS390X) > 64 {
    95  		panic("too many registers")
    96  	}
    97  	num := map[string]int{}
    98  	for i, name := range regNamesS390X {
    99  		num[name] = i
   100  	}
   101  	buildReg := func(s string) regMask {
   102  		m := regMask(0)
   103  		for _, r := range strings.Split(s, " ") {
   104  			if n, ok := num[r]; ok {
   105  				m |= regMask(1) << uint(n)
   106  				continue
   107  			}
   108  			panic("register " + r + " not found")
   109  		}
   110  		return m
   111  	}
   112  
   113  	// Common individual register masks
   114  	var (
   115  		sp  = buildReg("SP")
   116  		sb  = buildReg("SB")
   117  		r0  = buildReg("R0")
   118  		tmp = buildReg("R11") // R11 is used as a temporary in a small number of instructions.
   119  
   120  		// R10 is reserved by the assembler.
   121  		gp   = buildReg("R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14")
   122  		gpg  = gp | buildReg("g")
   123  		gpsp = gp | sp
   124  
   125  		// R0 is considered to contain the value 0 in address calculations.
   126  		ptr     = gp &^ r0
   127  		ptrsp   = ptr | sp
   128  		ptrspsb = ptrsp | sb
   129  
   130  		fp         = buildReg("F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15")
   131  		callerSave = gp | fp | buildReg("g") // runtime.setg (and anything calling it) may clobber g
   132  		r1         = buildReg("R1")
   133  		r2         = buildReg("R2")
   134  		r3         = buildReg("R3")
   135  	)
   136  	// Common slices of register masks
   137  	var (
   138  		gponly = []regMask{gp}
   139  		fponly = []regMask{fp}
   140  	)
   141  
   142  	// Common regInfo
   143  	var (
   144  		gp01    = regInfo{inputs: []regMask{}, outputs: gponly}
   145  		gp11    = regInfo{inputs: []regMask{gp}, outputs: gponly}
   146  		gp11sp  = regInfo{inputs: []regMask{gpsp}, outputs: gponly}
   147  		gp21    = regInfo{inputs: []regMask{gp, gp}, outputs: gponly}
   148  		gp21sp  = regInfo{inputs: []regMask{gpsp, gp}, outputs: gponly}
   149  		gp21tmp = regInfo{inputs: []regMask{gp &^ tmp, gp &^ tmp}, outputs: []regMask{gp &^ tmp}, clobbers: tmp}
   150  
   151  		// R0 evaluates to 0 when used as the number of bits to shift
   152  		// so we need to exclude it from that operand.
   153  		sh21 = regInfo{inputs: []regMask{gp, ptr}, outputs: gponly}
   154  
   155  		addr    = regInfo{inputs: []regMask{sp | sb}, outputs: gponly}
   156  		addridx = regInfo{inputs: []regMask{sp | sb, ptrsp}, outputs: gponly}
   157  
   158  		gp2flags       = regInfo{inputs: []regMask{gpsp, gpsp}}
   159  		gp1flags       = regInfo{inputs: []regMask{gpsp}}
   160  		gp2flags1      = regInfo{inputs: []regMask{gp, gp}, outputs: gponly}
   161  		gp11flags      = regInfo{inputs: []regMask{gp}, outputs: gponly}
   162  		gp21flags      = regInfo{inputs: []regMask{gp, gp}, outputs: gponly}
   163  		gp2flags1flags = regInfo{inputs: []regMask{gp, gp}, outputs: gponly}
   164  
   165  		gpload       = regInfo{inputs: []regMask{ptrspsb, 0}, outputs: gponly}
   166  		gploadidx    = regInfo{inputs: []regMask{ptrspsb, ptrsp, 0}, outputs: gponly}
   167  		gpopload     = regInfo{inputs: []regMask{gp, ptrsp, 0}, outputs: gponly}
   168  		gpstore      = regInfo{inputs: []regMask{ptrspsb, gpsp, 0}}
   169  		gpstoreconst = regInfo{inputs: []regMask{ptrspsb, 0}}
   170  		gpstoreidx   = regInfo{inputs: []regMask{ptrsp, ptrsp, gpsp, 0}}
   171  		gpstorebr    = regInfo{inputs: []regMask{ptrsp, gpsp, 0}}
   172  		gpstorelaa   = regInfo{inputs: []regMask{ptrspsb, gpsp, 0}, outputs: gponly}
   173  		gpstorelab   = regInfo{inputs: []regMask{r1, gpsp, 0}, clobbers: r1}
   174  
   175  		gpmvc = regInfo{inputs: []regMask{ptrsp, ptrsp, 0}}
   176  
   177  		fp01        = regInfo{inputs: []regMask{}, outputs: fponly}
   178  		fp21        = regInfo{inputs: []regMask{fp, fp}, outputs: fponly}
   179  		fp31        = regInfo{inputs: []regMask{fp, fp, fp}, outputs: fponly}
   180  		fp21clobber = regInfo{inputs: []regMask{fp, fp}, outputs: fponly}
   181  		fpgp        = regInfo{inputs: fponly, outputs: gponly}
   182  		gpfp        = regInfo{inputs: gponly, outputs: fponly}
   183  		fp11        = regInfo{inputs: fponly, outputs: fponly}
   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, asm: "FADDS", commutative: true, resultInArg0: true, clobberFlags: true}, // fp32 arg0 + arg1
   208  		{name: "FADD", argLength: 2, reg: fp21clobber, asm: "FADD", commutative: true, resultInArg0: true, clobberFlags: true},   // fp64 arg0 + arg1
   209  		{name: "FSUBS", argLength: 2, reg: fp21clobber, asm: "FSUBS", resultInArg0: true, clobberFlags: true},                    // fp32 arg0 - arg1
   210  		{name: "FSUB", argLength: 2, reg: fp21clobber, asm: "FSUB", resultInArg0: true, clobberFlags: 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  
   330  		{name: "SLD", argLength: 2, reg: sh21, asm: "SLD"},                   // arg0 << arg1, shift amount is mod 64
   331  		{name: "SLW", argLength: 2, reg: sh21, asm: "SLW"},                   // arg0 << arg1, shift amount is mod 32
   332  		{name: "SLDconst", argLength: 1, reg: gp11, asm: "SLD", aux: "Int8"}, // arg0 << auxint, shift amount 0-63
   333  		{name: "SLWconst", argLength: 1, reg: gp11, asm: "SLW", aux: "Int8"}, // arg0 << auxint, shift amount 0-31
   334  
   335  		{name: "SRD", argLength: 2, reg: sh21, asm: "SRD"},                   // unsigned arg0 >> arg1, shift amount is mod 64
   336  		{name: "SRW", argLength: 2, reg: sh21, asm: "SRW"},                   // unsigned uint32(arg0) >> arg1, shift amount is mod 32
   337  		{name: "SRDconst", argLength: 1, reg: gp11, asm: "SRD", aux: "Int8"}, // unsigned arg0 >> auxint, shift amount 0-63
   338  		{name: "SRWconst", argLength: 1, reg: gp11, asm: "SRW", aux: "Int8"}, // unsigned uint32(arg0) >> auxint, shift amount 0-31
   339  
   340  		// Arithmetic shifts clobber flags.
   341  		{name: "SRAD", argLength: 2, reg: sh21, asm: "SRAD", clobberFlags: true},                   // signed arg0 >> arg1, shift amount is mod 64
   342  		{name: "SRAW", argLength: 2, reg: sh21, asm: "SRAW", clobberFlags: true},                   // signed int32(arg0) >> arg1, shift amount is mod 32
   343  		{name: "SRADconst", argLength: 1, reg: gp11, asm: "SRAD", aux: "Int8", clobberFlags: true}, // signed arg0 >> auxint, shift amount 0-63
   344  		{name: "SRAWconst", argLength: 1, reg: gp11, asm: "SRAW", aux: "Int8", clobberFlags: true}, // signed int32(arg0) >> auxint, shift amount 0-31
   345  
   346  		{name: "RLLG", argLength: 2, reg: sh21, asm: "RLLG"},                   // arg0 rotate left arg1, rotate amount 0-63
   347  		{name: "RLL", argLength: 2, reg: sh21, asm: "RLL"},                     // arg0 rotate left arg1, rotate amount 0-31
   348  		{name: "RLLGconst", argLength: 1, reg: gp11, asm: "RLLG", aux: "Int8"}, // arg0 rotate left auxint, rotate amount 0-63
   349  		{name: "RLLconst", argLength: 1, reg: gp11, asm: "RLL", aux: "Int8"},   // arg0 rotate left auxint, rotate amount 0-31
   350  
   351  		// Rotate then (and|or|xor|insert) selected bits instructions.
   352  		//
   353  		// Aux is an s390x.RotateParams struct containing Start, End and rotation
   354  		// Amount fields.
   355  		//
   356  		// arg1 is rotated left by the rotation amount then the bits from the start
   357  		// bit to the end bit (inclusive) are combined with arg0 using the logical
   358  		// operation specified. Bit indices are specified from left to right - the
   359  		// MSB is 0 and the LSB is 63.
   360  		//
   361  		// Examples:
   362  		//               |          aux         |
   363  		// | instruction | start | end | amount |          arg0         |          arg1         |         result        |
   364  		// +-------------+-------+-----+--------+-----------------------+-----------------------+-----------------------+
   365  		// | RXSBG (XOR) |     0 |   1 |      0 | 0xffff_ffff_ffff_ffff | 0xffff_ffff_ffff_ffff | 0x3fff_ffff_ffff_ffff |
   366  		// | RXSBG (XOR) |    62 |  63 |      0 | 0xffff_ffff_ffff_ffff | 0xffff_ffff_ffff_ffff | 0xffff_ffff_ffff_fffc |
   367  		// | RXSBG (XOR) |     0 |  47 |     16 | 0xffff_ffff_ffff_ffff | 0x0000_0000_0000_ffff | 0xffff_ffff_0000_ffff |
   368  		// +-------------+-------+-----+--------+-----------------------+-----------------------+-----------------------+
   369  		//
   370  		{name: "RXSBG", argLength: 2, reg: gp21, asm: "RXSBG", resultInArg0: true, aux: "ArchSpecific", clobberFlags: true}, // rotate then xor selected bits
   371  
   372  		// unary ops
   373  		{name: "NEG", argLength: 1, reg: gp11, asm: "NEG", clobberFlags: true},   // -arg0
   374  		{name: "NEGW", argLength: 1, reg: gp11, asm: "NEGW", clobberFlags: true}, // -arg0
   375  
   376  		{name: "NOT", argLength: 1, reg: gp11, resultInArg0: true, clobberFlags: true},  // ^arg0
   377  		{name: "NOTW", argLength: 1, reg: gp11, resultInArg0: true, clobberFlags: true}, // ^arg0
   378  
   379  		{name: "FSQRT", argLength: 1, reg: fp11, asm: "FSQRT"}, // sqrt(arg0)
   380  
   381  		// Conditional register-register moves.
   382  		// The aux for these values is an s390x.CCMask value representing the condition code mask.
   383  		{name: "LOCGR", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "LOCGR", aux: "ArchSpecific"}, // load arg1 into arg0 if the condition code in arg2 matches a masked bit in aux.
   384  
   385  		{name: "MOVBreg", argLength: 1, reg: gp11sp, asm: "MOVB", typ: "Int64"},    // sign extend arg0 from int8 to int64
   386  		{name: "MOVBZreg", argLength: 1, reg: gp11sp, asm: "MOVBZ", typ: "UInt64"}, // zero extend arg0 from int8 to int64
   387  		{name: "MOVHreg", argLength: 1, reg: gp11sp, asm: "MOVH", typ: "Int64"},    // sign extend arg0 from int16 to int64
   388  		{name: "MOVHZreg", argLength: 1, reg: gp11sp, asm: "MOVHZ", typ: "UInt64"}, // zero extend arg0 from int16 to int64
   389  		{name: "MOVWreg", argLength: 1, reg: gp11sp, asm: "MOVW", typ: "Int64"},    // sign extend arg0 from int32 to int64
   390  		{name: "MOVWZreg", argLength: 1, reg: gp11sp, asm: "MOVWZ", typ: "UInt64"}, // zero extend arg0 from int32 to int64
   391  
   392  		{name: "MOVDconst", reg: gp01, asm: "MOVD", typ: "UInt64", aux: "Int64", rematerializeable: true}, // auxint
   393  
   394  		{name: "LDGR", argLength: 1, reg: gpfp, asm: "LDGR"},     // move int64 to float64 (no conversion)
   395  		{name: "LGDR", argLength: 1, reg: fpgp, asm: "LGDR"},     // move float64 to int64 (no conversion)
   396  		{name: "CFDBRA", argLength: 1, reg: fpgp, asm: "CFDBRA"}, // convert float64 to int32
   397  		{name: "CGDBRA", argLength: 1, reg: fpgp, asm: "CGDBRA"}, // convert float64 to int64
   398  		{name: "CFEBRA", argLength: 1, reg: fpgp, asm: "CFEBRA"}, // convert float32 to int32
   399  		{name: "CGEBRA", argLength: 1, reg: fpgp, asm: "CGEBRA"}, // convert float32 to int64
   400  		{name: "CEFBRA", argLength: 1, reg: gpfp, asm: "CEFBRA"}, // convert int32 to float32
   401  		{name: "CDFBRA", argLength: 1, reg: gpfp, asm: "CDFBRA"}, // convert int32 to float64
   402  		{name: "CEGBRA", argLength: 1, reg: gpfp, asm: "CEGBRA"}, // convert int64 to float32
   403  		{name: "CDGBRA", argLength: 1, reg: gpfp, asm: "CDGBRA"}, // convert int64 to float64
   404  		{name: "LEDBR", argLength: 1, reg: fp11, asm: "LEDBR"},   // convert float64 to float32
   405  		{name: "LDEBR", argLength: 1, reg: fp11, asm: "LDEBR"},   // convert float32 to float64
   406  
   407  		{name: "MOVDaddr", argLength: 1, reg: addr, aux: "SymOff", rematerializeable: true, symEffect: "Read"}, // arg0 + auxint + offset encoded in aux
   408  		{name: "MOVDaddridx", argLength: 2, reg: addridx, aux: "SymOff", symEffect: "Read"},                    // arg0 + arg1 + auxint + aux
   409  
   410  		// auxint+aux == add auxint and the offset of the symbol in aux (if any) to the effective address
   411  		{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.
   412  		{name: "MOVBload", argLength: 2, reg: gpload, asm: "MOVB", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"},                  // ditto, sign extend to int64
   413  		{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.
   414  		{name: "MOVHload", argLength: 2, reg: gpload, asm: "MOVH", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"},                  // ditto, sign extend to int64
   415  		{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.
   416  		{name: "MOVWload", argLength: 2, reg: gpload, asm: "MOVW", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"},                  // ditto, sign extend to int64
   417  		{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
   418  
   419  		{name: "MOVWBR", argLength: 1, reg: gp11, asm: "MOVWBR"}, // arg0 swap bytes
   420  		{name: "MOVDBR", argLength: 1, reg: gp11, asm: "MOVDBR"}, // arg0 swap bytes
   421  
   422  		{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.
   423  		{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.
   424  		{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.
   425  
   426  		{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
   427  		{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
   428  		{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
   429  		{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
   430  		{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.
   431  		{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.
   432  		{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.
   433  
   434  		{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
   435  
   436  		// indexed loads/stores
   437  		{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.
   438  		{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.
   439  		{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.
   440  		{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.
   441  		{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.
   442  		{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.
   443  		{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
   444  		{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.
   445  		{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.
   446  		{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.
   447  		{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
   448  		{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
   449  		{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
   450  		{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
   451  		{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.
   452  		{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.
   453  		{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.
   454  
   455  		// For storeconst ops, the AuxInt field encodes both
   456  		// the value to store and an address offset of the store.
   457  		// Cast AuxInt to a ValAndOff to extract Val and Off fields.
   458  		{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
   459  		{name: "MOVHstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVH", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store low 2 bytes of ...
   460  		{name: "MOVWstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVW", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store low 4 bytes of ...
   461  		{name: "MOVDstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVD", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of ...
   462  
   463  		{name: "CLEAR", argLength: 2, reg: regInfo{inputs: []regMask{ptr, 0}}, asm: "CLEAR", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, symEffect: "Write"},
   464  
   465  		{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
   466  		{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
   467  		{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
   468  
   469  		// (InvertFlags (CMP a b)) == (CMP b a)
   470  		// InvertFlags is a pseudo-op which can't appear in assembly output.
   471  		{name: "InvertFlags", argLength: 1}, // reverse direction of arg0
   472  
   473  		// Pseudo-ops
   474  		{name: "LoweredGetG", argLength: 1, reg: gp01}, // arg0=mem
   475  		// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
   476  		// and sorts it to the very beginning of the block to prevent other
   477  		// use of R12 (the closure pointer)
   478  		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R12")}}, zeroWidth: true},
   479  		// arg0=ptr,arg1=mem, returns void.  Faults if ptr is nil.
   480  		// LoweredGetCallerSP returns the SP of the caller of the current function.
   481  		{name: "LoweredGetCallerSP", reg: gp01, rematerializeable: true},
   482  		// LoweredGetCallerPC evaluates to the PC to which its "caller" will return.
   483  		// I.e., if f calls g "calls" getcallerpc,
   484  		// the result should be the PC within f that g will return to.
   485  		// See runtime/stubs.go for a more detailed discussion.
   486  		{name: "LoweredGetCallerPC", reg: gp01, rematerializeable: true},
   487  		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{ptrsp}}, clobberFlags: true, nilCheck: true, faultOnNilArg0: true},
   488  		// Round ops to block fused-multiply-add extraction.
   489  		{name: "LoweredRound32F", argLength: 1, reg: fp11, resultInArg0: true, zeroWidth: true},
   490  		{name: "LoweredRound64F", argLength: 1, reg: fp11, resultInArg0: true, zeroWidth: true},
   491  
   492  		// LoweredWB invokes runtime.gcWriteBarrier. arg0=destptr, arg1=srcptr, arg2=mem, aux=runtime.gcWriteBarrier
   493  		// It saves all GP registers if necessary,
   494  		// but clobbers R14 (LR) because it's a call.
   495  		{name: "LoweredWB", argLength: 3, reg: regInfo{inputs: []regMask{buildReg("R2"), buildReg("R3")}, clobbers: (callerSave &^ gpg) | buildReg("R14")}, clobberFlags: true, aux: "Sym", symEffect: "None"},
   496  
   497  		// There are three of these functions so that they can have three different register inputs.
   498  		// When we check 0 <= c <= cap (A), then 0 <= b <= c (B), then 0 <= a <= b (C), we want the
   499  		// default registers to match so we don't need to copy registers around unnecessarily.
   500  		{name: "LoweredPanicBoundsA", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r2, r3}}, typ: "Mem"}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in generic.go).
   501  		{name: "LoweredPanicBoundsB", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r1, r2}}, typ: "Mem"}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in generic.go).
   502  		{name: "LoweredPanicBoundsC", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r0, r1}}, typ: "Mem"}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in generic.go).
   503  
   504  		// Constant condition code values. The condition code can be 0, 1, 2 or 3.
   505  		{name: "FlagEQ"}, // CC=0 (equal)
   506  		{name: "FlagLT"}, // CC=1 (less than)
   507  		{name: "FlagGT"}, // CC=2 (greater than)
   508  		{name: "FlagOV"}, // CC=3 (overflow)
   509  
   510  		// Fast-BCR-serialization to ensure store-load ordering.
   511  		{name: "SYNC", argLength: 1, reg: sync, asm: "SYNC", typ: "Mem"},
   512  
   513  		// Atomic loads. These are just normal loads but return <value,memory> tuples
   514  		// so they can be properly ordered with other loads.
   515  		// load from arg0+auxint+aux.  arg1=mem.
   516  		{name: "MOVBZatomicload", argLength: 2, reg: gpload, asm: "MOVBZ", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"},
   517  		{name: "MOVWZatomicload", argLength: 2, reg: gpload, asm: "MOVWZ", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"},
   518  		{name: "MOVDatomicload", argLength: 2, reg: gpload, asm: "MOVD", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"},
   519  
   520  		// Atomic stores. These are just normal stores.
   521  		// store arg1 to arg0+auxint+aux. arg2=mem.
   522  		{name: "MOVBatomicstore", argLength: 3, reg: gpstore, asm: "MOVB", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "Write"},
   523  		{name: "MOVWatomicstore", argLength: 3, reg: gpstore, asm: "MOVW", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "Write"},
   524  		{name: "MOVDatomicstore", argLength: 3, reg: gpstore, asm: "MOVD", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "Write"},
   525  
   526  		// Atomic adds.
   527  		// *(arg0+auxint+aux) += arg1.  arg2=mem.
   528  		// Returns a tuple of <old contents of *(arg0+auxint+aux), memory>.
   529  		{name: "LAA", argLength: 3, reg: gpstorelaa, asm: "LAA", typ: "(UInt32,Mem)", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"},
   530  		{name: "LAAG", argLength: 3, reg: gpstorelaa, asm: "LAAG", typ: "(UInt64,Mem)", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"},
   531  		{name: "AddTupleFirst32", argLength: 2}, // arg1=tuple <x,y>.  Returns <x+arg0,y>.
   532  		{name: "AddTupleFirst64", argLength: 2}, // arg1=tuple <x,y>.  Returns <x+arg0,y>.
   533  
   534  		// Atomic bitwise operations.
   535  		// Note: 'floor' operations round the pointer down to the nearest word boundary
   536  		// which reflects how they are used in the runtime.
   537  		{name: "LAOfloor", argLength: 3, reg: gpstorelab, asm: "LAO", typ: "Mem", clobberFlags: true, hasSideEffects: true}, // *(floor(arg0, 4)) |= arg1. arg2 = mem.
   538  		{name: "LANfloor", argLength: 3, reg: gpstorelab, asm: "LAN", typ: "Mem", clobberFlags: true, hasSideEffects: true}, // *(floor(arg0, 4)) &= arg1. arg2 = mem.
   539  
   540  		// Compare and swap.
   541  		// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory.
   542  		// if *(arg0+auxint+aux) == arg1 {
   543  		//   *(arg0+auxint+aux) = arg2
   544  		//   return (true, memory)
   545  		// } else {
   546  		//   return (false, memory)
   547  		// }
   548  		// Note that these instructions also return the old value in arg1, but we ignore it.
   549  		// TODO: have these return flags instead of bool.  The current system generates:
   550  		//    CS ...
   551  		//    MOVD  $0, ret
   552  		//    BNE   2(PC)
   553  		//    MOVD  $1, ret
   554  		//    CMPW  ret, $0
   555  		//    BNE ...
   556  		// instead of just
   557  		//    CS ...
   558  		//    BEQ ...
   559  		// but we can't do that because memory-using ops can't generate flags yet
   560  		// (flagalloc wants to move flag-generating instructions around).
   561  		{name: "LoweredAtomicCas32", argLength: 4, reg: cas, asm: "CS", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"},
   562  		{name: "LoweredAtomicCas64", argLength: 4, reg: cas, asm: "CSG", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"},
   563  
   564  		// Lowered atomic swaps, emulated using compare-and-swap.
   565  		// store arg1 to arg0+auxint+aux, arg2=mem.
   566  		{name: "LoweredAtomicExchange32", argLength: 3, reg: exchange, asm: "CS", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"},
   567  		{name: "LoweredAtomicExchange64", argLength: 3, reg: exchange, asm: "CSG", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"},
   568  
   569  		// find leftmost one
   570  		{
   571  			name:         "FLOGR",
   572  			argLength:    1,
   573  			reg:          regInfo{inputs: gponly, outputs: []regMask{buildReg("R0")}, clobbers: buildReg("R1")},
   574  			asm:          "FLOGR",
   575  			typ:          "UInt64",
   576  			clobberFlags: true,
   577  		},
   578  
   579  		// population count
   580  		//
   581  		// Counts the number of ones in each byte of arg0
   582  		// and places the result into the corresponding byte
   583  		// of the result.
   584  		{
   585  			name:         "POPCNT",
   586  			argLength:    1,
   587  			reg:          gp11,
   588  			asm:          "POPCNT",
   589  			typ:          "UInt64",
   590  			clobberFlags: true,
   591  		},
   592  
   593  		// unsigned multiplication (64x64 → 128)
   594  		//
   595  		// Multiply the two 64-bit input operands together and place the 128-bit result into
   596  		// an even-odd register pair. The second register in the target pair also contains
   597  		// one of the input operands. Since we don't currently have a way to specify an
   598  		// even-odd register pair we hardcode this register pair as R2:R3.
   599  		{
   600  			name:      "MLGR",
   601  			argLength: 2,
   602  			reg:       regInfo{inputs: []regMask{gp, r3}, outputs: []regMask{r2, r3}},
   603  			asm:       "MLGR",
   604  		},
   605  
   606  		// pseudo operations to sum the output of the POPCNT instruction
   607  		{name: "SumBytes2", argLength: 1, typ: "UInt8"}, // sum the rightmost 2 bytes in arg0 ignoring overflow
   608  		{name: "SumBytes4", argLength: 1, typ: "UInt8"}, // sum the rightmost 4 bytes in arg0 ignoring overflow
   609  		{name: "SumBytes8", argLength: 1, typ: "UInt8"}, // sum all the bytes in arg0 ignoring overflow
   610  
   611  		// store multiple
   612  		{
   613  			name:           "STMG2",
   614  			argLength:      4,
   615  			reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), 0}},
   616  			aux:            "SymOff",
   617  			typ:            "Mem",
   618  			asm:            "STMG",
   619  			faultOnNilArg0: true,
   620  			symEffect:      "Write",
   621  		},
   622  		{
   623  			name:           "STMG3",
   624  			argLength:      5,
   625  			reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), buildReg("R3"), 0}},
   626  			aux:            "SymOff",
   627  			typ:            "Mem",
   628  			asm:            "STMG",
   629  			faultOnNilArg0: true,
   630  			symEffect:      "Write",
   631  		},
   632  		{
   633  			name:      "STMG4",
   634  			argLength: 6,
   635  			reg: regInfo{inputs: []regMask{
   636  				ptrsp,
   637  				buildReg("R1"),
   638  				buildReg("R2"),
   639  				buildReg("R3"),
   640  				buildReg("R4"),
   641  				0,
   642  			}},
   643  			aux:            "SymOff",
   644  			typ:            "Mem",
   645  			asm:            "STMG",
   646  			faultOnNilArg0: true,
   647  			symEffect:      "Write",
   648  		},
   649  		{
   650  			name:           "STM2",
   651  			argLength:      4,
   652  			reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), 0}},
   653  			aux:            "SymOff",
   654  			typ:            "Mem",
   655  			asm:            "STMY",
   656  			faultOnNilArg0: true,
   657  			symEffect:      "Write",
   658  		},
   659  		{
   660  			name:           "STM3",
   661  			argLength:      5,
   662  			reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), buildReg("R3"), 0}},
   663  			aux:            "SymOff",
   664  			typ:            "Mem",
   665  			asm:            "STMY",
   666  			faultOnNilArg0: true,
   667  			symEffect:      "Write",
   668  		},
   669  		{
   670  			name:      "STM4",
   671  			argLength: 6,
   672  			reg: regInfo{inputs: []regMask{
   673  				ptrsp,
   674  				buildReg("R1"),
   675  				buildReg("R2"),
   676  				buildReg("R3"),
   677  				buildReg("R4"),
   678  				0,
   679  			}},
   680  			aux:            "SymOff",
   681  			typ:            "Mem",
   682  			asm:            "STMY",
   683  			faultOnNilArg0: true,
   684  			symEffect:      "Write",
   685  		},
   686  
   687  		// large move
   688  		// auxint = remaining bytes after loop (rem)
   689  		// arg0 = address of dst memory (in R1, changed as a side effect)
   690  		// arg1 = address of src memory (in R2, changed as a side effect)
   691  		// arg2 = pointer to last address to move in loop + 256
   692  		// arg3 = mem
   693  		// returns mem
   694  		//
   695  		// mvc: MVC  $256, 0(R2), 0(R1)
   696  		//      MOVD $256(R1), R1
   697  		//      MOVD $256(R2), R2
   698  		//      CMP  R2, Rarg2
   699  		//      BNE  mvc
   700  		//	MVC  $rem, 0(R2), 0(R1) // if rem > 0
   701  		{
   702  			name:      "LoweredMove",
   703  			aux:       "Int64",
   704  			argLength: 4,
   705  			reg: regInfo{
   706  				inputs:   []regMask{buildReg("R1"), buildReg("R2"), gpsp},
   707  				clobbers: buildReg("R1 R2"),
   708  			},
   709  			clobberFlags:   true,
   710  			typ:            "Mem",
   711  			faultOnNilArg0: true,
   712  			faultOnNilArg1: true,
   713  		},
   714  
   715  		// large clear
   716  		// auxint = remaining bytes after loop (rem)
   717  		// arg0 = address of dst memory (in R1, changed as a side effect)
   718  		// arg1 = pointer to last address to zero in loop + 256
   719  		// arg2 = mem
   720  		// returns mem
   721  		//
   722  		// clear: CLEAR $256, 0(R1)
   723  		//        MOVD  $256(R1), R1
   724  		//        CMP   R1, Rarg2
   725  		//        BNE   clear
   726  		//	  CLEAR $rem, 0(R1) // if rem > 0
   727  		{
   728  			name:      "LoweredZero",
   729  			aux:       "Int64",
   730  			argLength: 3,
   731  			reg: regInfo{
   732  				inputs:   []regMask{buildReg("R1"), gpsp},
   733  				clobbers: buildReg("R1"),
   734  			},
   735  			clobberFlags:   true,
   736  			typ:            "Mem",
   737  			faultOnNilArg0: true,
   738  		},
   739  	}
   740  
   741  	// All blocks on s390x have their condition code mask (s390x.CCMask) as the Aux value.
   742  	// The condition code mask is a 4-bit mask where each bit corresponds to a condition
   743  	// code value. If the value of the condition code matches a bit set in the condition
   744  	// code mask then the first successor is executed. Otherwise the second successor is
   745  	// executed.
   746  	//
   747  	// | condition code value |  mask bit  |
   748  	// +----------------------+------------+
   749  	// | 0 (equal)            | 0b1000 (8) |
   750  	// | 1 (less than)        | 0b0100 (4) |
   751  	// | 2 (greater than)     | 0b0010 (2) |
   752  	// | 3 (unordered)        | 0b0001 (1) |
   753  	//
   754  	// Note: that compare-and-branch instructions must not have bit 3 (0b0001) set.
   755  	var S390Xblocks = []blockData{
   756  		// branch on condition
   757  		{name: "BRC", controls: 1}, // condition code value (flags) is Controls[0]
   758  
   759  		// compare-and-branch (register-register)
   760  		//  - integrates comparison of Controls[0] with Controls[1]
   761  		//  - both control values must be in general purpose registers
   762  		{name: "CRJ", controls: 2},   // signed 32-bit integer comparison
   763  		{name: "CGRJ", controls: 2},  // signed 64-bit integer comparison
   764  		{name: "CLRJ", controls: 2},  // unsigned 32-bit integer comparison
   765  		{name: "CLGRJ", controls: 2}, // unsigned 64-bit integer comparison
   766  
   767  		// compare-and-branch (register-immediate)
   768  		//  - integrates comparison of Controls[0] with AuxInt
   769  		//  - control value must be in a general purpose register
   770  		//  - the AuxInt value is sign-extended for signed comparisons
   771  		//    and zero-extended for unsigned comparisons
   772  		{name: "CIJ", controls: 1, auxint: "Int8"},    // signed 32-bit integer comparison
   773  		{name: "CGIJ", controls: 1, auxint: "Int8"},   // signed 64-bit integer comparison
   774  		{name: "CLIJ", controls: 1, auxint: "UInt8"},  // unsigned 32-bit integer comparison
   775  		{name: "CLGIJ", controls: 1, auxint: "UInt8"}, // unsigned 64-bit integer comparison
   776  	}
   777  
   778  	archs = append(archs, arch{
   779  		name:            "S390X",
   780  		pkg:             "github.com/gagliardetto/golang-go/cmd/internal/obj/s390x",
   781  		genfile:         "../../s390x/ssa.go",
   782  		ops:             S390Xops,
   783  		blocks:          S390Xblocks,
   784  		regnames:        regNamesS390X,
   785  		gpregmask:       gp,
   786  		fpregmask:       fp,
   787  		framepointerreg: -1, // not used
   788  		linkreg:         int8(num["R14"]),
   789  		imports: []string{
   790  			"github.com/gagliardetto/golang-go/cmd/internal/obj/s390x",
   791  		},
   792  	})
   793  }