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