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