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