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