]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/ssa/gen/S390XOps.go
net/http: mention socks5 support in proxy
[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                 {name: "FSQRTS", argLength: 1, reg: fp11, asm: "FSQRTS"}, // sqrt(arg0), float32
386
387                 // Conditional register-register moves.
388                 // The aux for these values is an s390x.CCMask value representing the condition code mask.
389                 {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.
390
391                 {name: "MOVBreg", argLength: 1, reg: gp11sp, asm: "MOVB", typ: "Int64"},    // sign extend arg0 from int8 to int64
392                 {name: "MOVBZreg", argLength: 1, reg: gp11sp, asm: "MOVBZ", typ: "UInt64"}, // zero extend arg0 from int8 to int64
393                 {name: "MOVHreg", argLength: 1, reg: gp11sp, asm: "MOVH", typ: "Int64"},    // sign extend arg0 from int16 to int64
394                 {name: "MOVHZreg", argLength: 1, reg: gp11sp, asm: "MOVHZ", typ: "UInt64"}, // zero extend arg0 from int16 to int64
395                 {name: "MOVWreg", argLength: 1, reg: gp11sp, asm: "MOVW", typ: "Int64"},    // sign extend arg0 from int32 to int64
396                 {name: "MOVWZreg", argLength: 1, reg: gp11sp, asm: "MOVWZ", typ: "UInt64"}, // zero extend arg0 from int32 to int64
397
398                 {name: "MOVDconst", reg: gp01, asm: "MOVD", typ: "UInt64", aux: "Int64", rematerializeable: true}, // auxint
399
400                 {name: "LDGR", argLength: 1, reg: gpfp, asm: "LDGR"}, // move int64 to float64 (no conversion)
401                 {name: "LGDR", argLength: 1, reg: fpgp, asm: "LGDR"}, // move float64 to int64 (no conversion)
402
403                 {name: "CFDBRA", argLength: 1, reg: fpgp, asm: "CFDBRA", clobberFlags: true}, // convert float64 to int32
404                 {name: "CGDBRA", argLength: 1, reg: fpgp, asm: "CGDBRA", clobberFlags: true}, // convert float64 to int64
405                 {name: "CFEBRA", argLength: 1, reg: fpgp, asm: "CFEBRA", clobberFlags: true}, // convert float32 to int32
406                 {name: "CGEBRA", argLength: 1, reg: fpgp, asm: "CGEBRA", clobberFlags: true}, // convert float32 to int64
407                 {name: "CEFBRA", argLength: 1, reg: gpfp, asm: "CEFBRA", clobberFlags: true}, // convert int32 to float32
408                 {name: "CDFBRA", argLength: 1, reg: gpfp, asm: "CDFBRA", clobberFlags: true}, // convert int32 to float64
409                 {name: "CEGBRA", argLength: 1, reg: gpfp, asm: "CEGBRA", clobberFlags: true}, // convert int64 to float32
410                 {name: "CDGBRA", argLength: 1, reg: gpfp, asm: "CDGBRA", clobberFlags: true}, // convert int64 to float64
411                 {name: "CLFEBR", argLength: 1, reg: fpgp, asm: "CLFEBR", clobberFlags: true}, // convert float32 to uint32
412                 {name: "CLFDBR", argLength: 1, reg: fpgp, asm: "CLFDBR", clobberFlags: true}, // convert float64 to uint32
413                 {name: "CLGEBR", argLength: 1, reg: fpgp, asm: "CLGEBR", clobberFlags: true}, // convert float32 to uint64
414                 {name: "CLGDBR", argLength: 1, reg: fpgp, asm: "CLGDBR", clobberFlags: true}, // convert float64 to uint64
415                 {name: "CELFBR", argLength: 1, reg: gpfp, asm: "CELFBR", clobberFlags: true}, // convert uint32 to float32
416                 {name: "CDLFBR", argLength: 1, reg: gpfp, asm: "CDLFBR", clobberFlags: true}, // convert uint32 to float64
417                 {name: "CELGBR", argLength: 1, reg: gpfp, asm: "CELGBR", clobberFlags: true}, // convert uint64 to float32
418                 {name: "CDLGBR", argLength: 1, reg: gpfp, asm: "CDLGBR", clobberFlags: true}, // convert uint64 to float64
419
420                 {name: "LEDBR", argLength: 1, reg: fp11, asm: "LEDBR"}, // convert float64 to float32
421                 {name: "LDEBR", argLength: 1, reg: fp11, asm: "LDEBR"}, // convert float32 to float64
422
423                 {name: "MOVDaddr", argLength: 1, reg: addr, aux: "SymOff", rematerializeable: true, symEffect: "Read"}, // arg0 + auxint + offset encoded in aux
424                 {name: "MOVDaddridx", argLength: 2, reg: addridx, aux: "SymOff", symEffect: "Read"},                    // arg0 + arg1 + auxint + aux
425
426                 // auxint+aux == add auxint and the offset of the symbol in aux (if any) to the effective address
427                 {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.
428                 {name: "MOVBload", argLength: 2, reg: gpload, asm: "MOVB", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"},                  // ditto, sign extend to int64
429                 {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.
430                 {name: "MOVHload", argLength: 2, reg: gpload, asm: "MOVH", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"},                  // ditto, sign extend to int64
431                 {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.
432                 {name: "MOVWload", argLength: 2, reg: gpload, asm: "MOVW", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"},                  // ditto, sign extend to int64
433                 {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
434
435                 {name: "MOVWBR", argLength: 1, reg: gp11, asm: "MOVWBR"}, // arg0 swap bytes
436                 {name: "MOVDBR", argLength: 1, reg: gp11, asm: "MOVDBR"}, // arg0 swap bytes
437
438                 {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.
439                 {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.
440                 {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.
441
442                 {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
443                 {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
444                 {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
445                 {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
446                 {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.
447                 {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.
448                 {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.
449
450                 {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
451
452                 // indexed loads/stores
453                 {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.
454                 {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.
455                 {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.
456                 {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.
457                 {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.
458                 {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.
459                 {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
460                 {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.
461                 {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.
462                 {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.
463                 {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
464                 {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
465                 {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
466                 {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
467                 {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.
468                 {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.
469                 {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.
470
471                 // For storeconst ops, the AuxInt field encodes both
472                 // the value to store and an address offset of the store.
473                 // Cast AuxInt to a ValAndOff to extract Val and Off fields.
474                 {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
475                 {name: "MOVHstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVH", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store low 2 bytes of ...
476                 {name: "MOVWstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVW", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store low 4 bytes of ...
477                 {name: "MOVDstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVD", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of ...
478
479                 {name: "CLEAR", argLength: 2, reg: regInfo{inputs: []regMask{ptr, 0}}, asm: "CLEAR", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, symEffect: "Write"},
480
481                 {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
482                 {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{ptrsp, buildReg("R12"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
483                 {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{ptr}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                         // call fn by pointer.  arg0=codeptr, arg1=mem, auxint=argsize, returns mem
484
485                 // (InvertFlags (CMP a b)) == (CMP b a)
486                 // InvertFlags is a pseudo-op which can't appear in assembly output.
487                 {name: "InvertFlags", argLength: 1}, // reverse direction of arg0
488
489                 // Pseudo-ops
490                 {name: "LoweredGetG", argLength: 1, reg: gp01}, // arg0=mem
491                 // Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
492                 // and sorts it to the very beginning of the block to prevent other
493                 // use of R12 (the closure pointer)
494                 {name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R12")}}, zeroWidth: true},
495                 // arg0=ptr,arg1=mem, returns void.  Faults if ptr is nil.
496                 // LoweredGetCallerSP returns the SP of the caller of the current function.
497                 {name: "LoweredGetCallerSP", reg: gp01, rematerializeable: true},
498                 // LoweredGetCallerPC evaluates to the PC to which its "caller" will return.
499                 // I.e., if f calls g "calls" getcallerpc,
500                 // the result should be the PC within f that g will return to.
501                 // See runtime/stubs.go for a more detailed discussion.
502                 {name: "LoweredGetCallerPC", reg: gp01, rematerializeable: true},
503                 {name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{ptrsp}}, clobberFlags: true, nilCheck: true, faultOnNilArg0: true},
504                 // Round ops to block fused-multiply-add extraction.
505                 {name: "LoweredRound32F", argLength: 1, reg: fp11, resultInArg0: true, zeroWidth: true},
506                 {name: "LoweredRound64F", argLength: 1, reg: fp11, resultInArg0: true, zeroWidth: true},
507
508                 // LoweredWB invokes runtime.gcWriteBarrier. arg0=destptr, arg1=srcptr, arg2=mem, aux=runtime.gcWriteBarrier
509                 // It saves all GP registers if necessary,
510                 // but clobbers R14 (LR) because it's a call.
511                 {name: "LoweredWB", argLength: 3, reg: regInfo{inputs: []regMask{buildReg("R2"), buildReg("R3")}, clobbers: (callerSave &^ gpg) | buildReg("R14")}, clobberFlags: true, aux: "Sym", symEffect: "None"},
512
513                 // There are three of these functions so that they can have three different register inputs.
514                 // When we check 0 <= c <= cap (A), then 0 <= b <= c (B), then 0 <= a <= b (C), we want the
515                 // default registers to match so we don't need to copy registers around unnecessarily.
516                 {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).
517                 {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).
518                 {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).
519
520                 // Constant condition code values. The condition code can be 0, 1, 2 or 3.
521                 {name: "FlagEQ"}, // CC=0 (equal)
522                 {name: "FlagLT"}, // CC=1 (less than)
523                 {name: "FlagGT"}, // CC=2 (greater than)
524                 {name: "FlagOV"}, // CC=3 (overflow)
525
526                 // Fast-BCR-serialization to ensure store-load ordering.
527                 {name: "SYNC", argLength: 1, reg: sync, asm: "SYNC", typ: "Mem"},
528
529                 // Atomic loads. These are just normal loads but return <value,memory> tuples
530                 // so they can be properly ordered with other loads.
531                 // load from arg0+auxint+aux.  arg1=mem.
532                 {name: "MOVBZatomicload", argLength: 2, reg: gpload, asm: "MOVBZ", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"},
533                 {name: "MOVWZatomicload", argLength: 2, reg: gpload, asm: "MOVWZ", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"},
534                 {name: "MOVDatomicload", argLength: 2, reg: gpload, asm: "MOVD", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"},
535
536                 // Atomic stores. These are just normal stores.
537                 // store arg1 to arg0+auxint+aux. arg2=mem.
538                 {name: "MOVBatomicstore", argLength: 3, reg: gpstore, asm: "MOVB", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "Write"},
539                 {name: "MOVWatomicstore", argLength: 3, reg: gpstore, asm: "MOVW", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "Write"},
540                 {name: "MOVDatomicstore", argLength: 3, reg: gpstore, asm: "MOVD", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "Write"},
541
542                 // Atomic adds.
543                 // *(arg0+auxint+aux) += arg1.  arg2=mem.
544                 // Returns a tuple of <old contents of *(arg0+auxint+aux), memory>.
545                 {name: "LAA", argLength: 3, reg: gpstorelaa, asm: "LAA", typ: "(UInt32,Mem)", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"},
546                 {name: "LAAG", argLength: 3, reg: gpstorelaa, asm: "LAAG", typ: "(UInt64,Mem)", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"},
547                 {name: "AddTupleFirst32", argLength: 2}, // arg1=tuple <x,y>.  Returns <x+arg0,y>.
548                 {name: "AddTupleFirst64", argLength: 2}, // arg1=tuple <x,y>.  Returns <x+arg0,y>.
549
550                 // Atomic bitwise operations.
551                 // Note: 'floor' operations round the pointer down to the nearest word boundary
552                 // which reflects how they are used in the runtime.
553                 {name: "LAN", argLength: 3, reg: gpstore, asm: "LAN", typ: "Mem", clobberFlags: true, hasSideEffects: true},         // *arg0 &= arg1. arg2 = mem.
554                 {name: "LANfloor", argLength: 3, reg: gpstorelab, asm: "LAN", typ: "Mem", clobberFlags: true, hasSideEffects: true}, // *(floor(arg0, 4)) &= arg1. arg2 = mem.
555                 {name: "LAO", argLength: 3, reg: gpstore, asm: "LAO", typ: "Mem", clobberFlags: true, hasSideEffects: true},         // *arg0 |= arg1. arg2 = mem.
556                 {name: "LAOfloor", argLength: 3, reg: gpstorelab, asm: "LAO", typ: "Mem", clobberFlags: true, hasSideEffects: true}, // *(floor(arg0, 4)) |= arg1. arg2 = mem.
557
558                 // Compare and swap.
559                 // arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory.
560                 // if *(arg0+auxint+aux) == arg1 {
561                 //   *(arg0+auxint+aux) = arg2
562                 //   return (true, memory)
563                 // } else {
564                 //   return (false, memory)
565                 // }
566                 // Note that these instructions also return the old value in arg1, but we ignore it.
567                 // TODO: have these return flags instead of bool.  The current system generates:
568                 //    CS ...
569                 //    MOVD  $0, ret
570                 //    BNE   2(PC)
571                 //    MOVD  $1, ret
572                 //    CMPW  ret, $0
573                 //    BNE ...
574                 // instead of just
575                 //    CS ...
576                 //    BEQ ...
577                 // but we can't do that because memory-using ops can't generate flags yet
578                 // (flagalloc wants to move flag-generating instructions around).
579                 {name: "LoweredAtomicCas32", argLength: 4, reg: cas, asm: "CS", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"},
580                 {name: "LoweredAtomicCas64", argLength: 4, reg: cas, asm: "CSG", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"},
581
582                 // Lowered atomic swaps, emulated using compare-and-swap.
583                 // store arg1 to arg0+auxint+aux, arg2=mem.
584                 {name: "LoweredAtomicExchange32", argLength: 3, reg: exchange, asm: "CS", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"},
585                 {name: "LoweredAtomicExchange64", argLength: 3, reg: exchange, asm: "CSG", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"},
586
587                 // find leftmost one
588                 {
589                         name:         "FLOGR",
590                         argLength:    1,
591                         reg:          regInfo{inputs: gponly, outputs: []regMask{buildReg("R0")}, clobbers: buildReg("R1")},
592                         asm:          "FLOGR",
593                         typ:          "UInt64",
594                         clobberFlags: true,
595                 },
596
597                 // population count
598                 //
599                 // Counts the number of ones in each byte of arg0
600                 // and places the result into the corresponding byte
601                 // of the result.
602                 {
603                         name:         "POPCNT",
604                         argLength:    1,
605                         reg:          gp11,
606                         asm:          "POPCNT",
607                         typ:          "UInt64",
608                         clobberFlags: true,
609                 },
610
611                 // unsigned multiplication (64x64 â†’ 128)
612                 //
613                 // Multiply the two 64-bit input operands together and place the 128-bit result into
614                 // an even-odd register pair. The second register in the target pair also contains
615                 // one of the input operands. Since we don't currently have a way to specify an
616                 // even-odd register pair we hardcode this register pair as R2:R3.
617                 {
618                         name:      "MLGR",
619                         argLength: 2,
620                         reg:       regInfo{inputs: []regMask{gp, r3}, outputs: []regMask{r2, r3}},
621                         asm:       "MLGR",
622                 },
623
624                 // pseudo operations to sum the output of the POPCNT instruction
625                 {name: "SumBytes2", argLength: 1, typ: "UInt8"}, // sum the rightmost 2 bytes in arg0 ignoring overflow
626                 {name: "SumBytes4", argLength: 1, typ: "UInt8"}, // sum the rightmost 4 bytes in arg0 ignoring overflow
627                 {name: "SumBytes8", argLength: 1, typ: "UInt8"}, // sum all the bytes in arg0 ignoring overflow
628
629                 // store multiple
630                 {
631                         name:           "STMG2",
632                         argLength:      4,
633                         reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), 0}},
634                         aux:            "SymOff",
635                         typ:            "Mem",
636                         asm:            "STMG",
637                         faultOnNilArg0: true,
638                         symEffect:      "Write",
639                         clobberFlags:   true, // TODO(mundaym): currently uses AGFI to handle large offsets
640                 },
641                 {
642                         name:           "STMG3",
643                         argLength:      5,
644                         reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), buildReg("R3"), 0}},
645                         aux:            "SymOff",
646                         typ:            "Mem",
647                         asm:            "STMG",
648                         faultOnNilArg0: true,
649                         symEffect:      "Write",
650                         clobberFlags:   true, // TODO(mundaym): currently uses AGFI to handle large offsets
651                 },
652                 {
653                         name:      "STMG4",
654                         argLength: 6,
655                         reg: regInfo{inputs: []regMask{
656                                 ptrsp,
657                                 buildReg("R1"),
658                                 buildReg("R2"),
659                                 buildReg("R3"),
660                                 buildReg("R4"),
661                                 0,
662                         }},
663                         aux:            "SymOff",
664                         typ:            "Mem",
665                         asm:            "STMG",
666                         faultOnNilArg0: true,
667                         symEffect:      "Write",
668                         clobberFlags:   true, // TODO(mundaym): currently uses AGFI to handle large offsets
669                 },
670                 {
671                         name:           "STM2",
672                         argLength:      4,
673                         reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), 0}},
674                         aux:            "SymOff",
675                         typ:            "Mem",
676                         asm:            "STMY",
677                         faultOnNilArg0: true,
678                         symEffect:      "Write",
679                         clobberFlags:   true, // TODO(mundaym): currently uses AGFI to handle large offsets
680                 },
681                 {
682                         name:           "STM3",
683                         argLength:      5,
684                         reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), buildReg("R3"), 0}},
685                         aux:            "SymOff",
686                         typ:            "Mem",
687                         asm:            "STMY",
688                         faultOnNilArg0: true,
689                         symEffect:      "Write",
690                         clobberFlags:   true, // TODO(mundaym): currently uses AGFI to handle large offsets
691                 },
692                 {
693                         name:      "STM4",
694                         argLength: 6,
695                         reg: regInfo{inputs: []regMask{
696                                 ptrsp,
697                                 buildReg("R1"),
698                                 buildReg("R2"),
699                                 buildReg("R3"),
700                                 buildReg("R4"),
701                                 0,
702                         }},
703                         aux:            "SymOff",
704                         typ:            "Mem",
705                         asm:            "STMY",
706                         faultOnNilArg0: true,
707                         symEffect:      "Write",
708                         clobberFlags:   true, // TODO(mundaym): currently uses AGFI to handle large offsets
709                 },
710
711                 // large move
712                 // auxint = remaining bytes after loop (rem)
713                 // arg0 = address of dst memory (in R1, changed as a side effect)
714                 // arg1 = address of src memory (in R2, changed as a side effect)
715                 // arg2 = pointer to last address to move in loop + 256
716                 // arg3 = mem
717                 // returns mem
718                 //
719                 // mvc: MVC  $256, 0(R2), 0(R1)
720                 //      MOVD $256(R1), R1
721                 //      MOVD $256(R2), R2
722                 //      CMP  R2, Rarg2
723                 //      BNE  mvc
724                 //      MVC  $rem, 0(R2), 0(R1) // if rem > 0
725                 {
726                         name:      "LoweredMove",
727                         aux:       "Int64",
728                         argLength: 4,
729                         reg: regInfo{
730                                 inputs:   []regMask{buildReg("R1"), buildReg("R2"), gpsp},
731                                 clobbers: buildReg("R1 R2"),
732                         },
733                         clobberFlags:   true,
734                         typ:            "Mem",
735                         faultOnNilArg0: true,
736                         faultOnNilArg1: true,
737                 },
738
739                 // large clear
740                 // auxint = remaining bytes after loop (rem)
741                 // arg0 = address of dst memory (in R1, changed as a side effect)
742                 // arg1 = pointer to last address to zero in loop + 256
743                 // arg2 = mem
744                 // returns mem
745                 //
746                 // clear: CLEAR $256, 0(R1)
747                 //        MOVD  $256(R1), R1
748                 //        CMP   R1, Rarg2
749                 //        BNE   clear
750                 //        CLEAR $rem, 0(R1) // if rem > 0
751                 {
752                         name:      "LoweredZero",
753                         aux:       "Int64",
754                         argLength: 3,
755                         reg: regInfo{
756                                 inputs:   []regMask{buildReg("R1"), gpsp},
757                                 clobbers: buildReg("R1"),
758                         },
759                         clobberFlags:   true,
760                         typ:            "Mem",
761                         faultOnNilArg0: true,
762                 },
763         }
764
765         // All blocks on s390x have their condition code mask (s390x.CCMask) as the Aux value.
766         // The condition code mask is a 4-bit mask where each bit corresponds to a condition
767         // code value. If the value of the condition code matches a bit set in the condition
768         // code mask then the first successor is executed. Otherwise the second successor is
769         // executed.
770         //
771         // | condition code value |  mask bit  |
772         // +----------------------+------------+
773         // | 0 (equal)            | 0b1000 (8) |
774         // | 1 (less than)        | 0b0100 (4) |
775         // | 2 (greater than)     | 0b0010 (2) |
776         // | 3 (unordered)        | 0b0001 (1) |
777         //
778         // Note: that compare-and-branch instructions must not have bit 3 (0b0001) set.
779         var S390Xblocks = []blockData{
780                 // branch on condition
781                 {name: "BRC", controls: 1, aux: "S390XCCMask"}, // condition code value (flags) is Controls[0]
782
783                 // compare-and-branch (register-register)
784                 //  - integrates comparison of Controls[0] with Controls[1]
785                 //  - both control values must be in general purpose registers
786                 {name: "CRJ", controls: 2, aux: "S390XCCMask"},   // signed 32-bit integer comparison
787                 {name: "CGRJ", controls: 2, aux: "S390XCCMask"},  // signed 64-bit integer comparison
788                 {name: "CLRJ", controls: 2, aux: "S390XCCMask"},  // unsigned 32-bit integer comparison
789                 {name: "CLGRJ", controls: 2, aux: "S390XCCMask"}, // unsigned 64-bit integer comparison
790
791                 // compare-and-branch (register-immediate)
792                 //  - integrates comparison of Controls[0] with AuxInt
793                 //  - control value must be in a general purpose register
794                 //  - the AuxInt value is sign-extended for signed comparisons
795                 //    and zero-extended for unsigned comparisons
796                 {name: "CIJ", controls: 1, aux: "S390XCCMaskInt8"},    // signed 32-bit integer comparison
797                 {name: "CGIJ", controls: 1, aux: "S390XCCMaskInt8"},   // signed 64-bit integer comparison
798                 {name: "CLIJ", controls: 1, aux: "S390XCCMaskUint8"},  // unsigned 32-bit integer comparison
799                 {name: "CLGIJ", controls: 1, aux: "S390XCCMaskUint8"}, // unsigned 64-bit integer comparison
800         }
801
802         archs = append(archs, arch{
803                 name:            "S390X",
804                 pkg:             "cmd/internal/obj/s390x",
805                 genfile:         "../../s390x/ssa.go",
806                 ops:             S390Xops,
807                 blocks:          S390Xblocks,
808                 regnames:        regNamesS390X,
809                 gpregmask:       gp,
810                 fpregmask:       fp,
811                 framepointerreg: -1, // not used
812                 linkreg:         int8(num["R14"]),
813                 imports: []string{
814                         "cmd/internal/obj/s390x",
815                 },
816         })
817 }