github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/ssa/_gen/LOONG64Ops.go (about) 1 // Copyright 2022 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 package main 6 7 import "strings" 8 9 // Notes: 10 // - Integer types live in the low portion of registers. Upper portions are junk. 11 // - Boolean types use the low-order byte of a register. 0=false, 1=true. 12 // Upper bytes are junk. 13 // - *const instructions may use a constant larger than the instruction can encode. 14 // In this case the assembler expands to multiple instructions and uses tmp 15 // register (R23). 16 17 // Suffixes encode the bit width of various instructions. 18 // V (vlong) = 64 bit 19 // WU (word) = 32 bit unsigned 20 // W (word) = 32 bit 21 // H (half word) = 16 bit 22 // HU = 16 bit unsigned 23 // B (byte) = 8 bit 24 // BU = 8 bit unsigned 25 // F (float) = 32 bit float 26 // D (double) = 64 bit float 27 28 // Note: registers not used in regalloc are not included in this list, 29 // so that regmask stays within int64 30 // Be careful when hand coding regmasks. 31 var regNamesLOONG64 = []string{ 32 "R0", // constant 0 33 "R1", 34 "SP", // aka R3 35 "R4", 36 "R5", 37 "R6", 38 "R7", 39 "R8", 40 "R9", 41 "R10", 42 "R11", 43 "R12", 44 "R13", 45 "R14", 46 "R15", 47 "R16", 48 "R17", 49 "R18", 50 "R19", 51 "R20", 52 "R21", 53 "g", // aka R22 54 "R23", 55 "R24", 56 "R25", 57 "R26", 58 "R27", 59 "R28", 60 "R29", 61 // R30 is REGTMP not used in regalloc 62 "R31", 63 64 "F0", 65 "F1", 66 "F2", 67 "F3", 68 "F4", 69 "F5", 70 "F6", 71 "F7", 72 "F8", 73 "F9", 74 "F10", 75 "F11", 76 "F12", 77 "F13", 78 "F14", 79 "F15", 80 "F16", 81 "F17", 82 "F18", 83 "F19", 84 "F20", 85 "F21", 86 "F22", 87 "F23", 88 "F24", 89 "F25", 90 "F26", 91 "F27", 92 "F28", 93 "F29", 94 "F30", 95 "F31", 96 97 // If you add registers, update asyncPreempt in runtime. 98 99 // pseudo-registers 100 "SB", 101 } 102 103 func init() { 104 // Make map from reg names to reg integers. 105 if len(regNamesLOONG64) > 64 { 106 panic("too many registers") 107 } 108 num := map[string]int{} 109 for i, name := range regNamesLOONG64 { 110 num[name] = i 111 } 112 buildReg := func(s string) regMask { 113 m := regMask(0) 114 for _, r := range strings.Split(s, " ") { 115 if n, ok := num[r]; ok { 116 m |= regMask(1) << uint(n) 117 continue 118 } 119 panic("register " + r + " not found") 120 } 121 return m 122 } 123 124 // Common individual register masks 125 var ( 126 gp = buildReg("R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31") // R1 is LR, R2 is thread pointer, R3 is stack pointer, R22 is g, R30 is REGTMP 127 gpg = gp | buildReg("g") 128 gpsp = gp | buildReg("SP") 129 gpspg = gpg | buildReg("SP") 130 gpspsbg = gpspg | buildReg("SB") 131 fp = buildReg("F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31") 132 callerSave = gp | fp | buildReg("g") // runtime.setg (and anything calling it) may clobber g 133 r1 = buildReg("R20") 134 r2 = buildReg("R21") 135 r3 = buildReg("R23") 136 r4 = buildReg("R24") 137 ) 138 // Common regInfo 139 var ( 140 gp01 = regInfo{inputs: nil, outputs: []regMask{gp}} 141 gp11 = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}} 142 gp11sp = regInfo{inputs: []regMask{gpspg}, outputs: []regMask{gp}} 143 gp21 = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}} 144 gpload = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}} 145 gpstore = regInfo{inputs: []regMask{gpspsbg, gpg}} 146 gpstore0 = regInfo{inputs: []regMask{gpspsbg}} 147 gpxchg = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{gp}} 148 gpcas = regInfo{inputs: []regMask{gpspsbg, gpg, gpg}, outputs: []regMask{gp}} 149 fp01 = regInfo{inputs: nil, outputs: []regMask{fp}} 150 fp11 = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}} 151 fp21 = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{fp}} 152 fp2flags = regInfo{inputs: []regMask{fp, fp}} 153 fpload = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp}} 154 fpstore = regInfo{inputs: []regMask{gpspsbg, fp}} 155 readflags = regInfo{inputs: nil, outputs: []regMask{gp}} 156 ) 157 ops := []opData{ 158 // binary ops 159 {name: "ADDV", argLength: 2, reg: gp21, asm: "ADDVU", commutative: true}, // arg0 + arg1 160 {name: "ADDVconst", argLength: 1, reg: gp11sp, asm: "ADDVU", aux: "Int64"}, // arg0 + auxInt. auxInt is 32-bit, also in other *const ops. 161 {name: "SUBV", argLength: 2, reg: gp21, asm: "SUBVU"}, // arg0 - arg1 162 {name: "SUBVconst", argLength: 1, reg: gp11, asm: "SUBVU", aux: "Int64"}, // arg0 - auxInt 163 164 {name: "MULV", argLength: 2, reg: gp21, asm: "MULV", commutative: true, typ: "Int64"}, // arg0 * arg1 165 {name: "MULHV", argLength: 2, reg: gp21, asm: "MULHV", commutative: true, typ: "Int64"}, // (arg0 * arg1) >> 64, signed 166 {name: "MULHVU", argLength: 2, reg: gp21, asm: "MULHVU", commutative: true, typ: "UInt64"}, // (arg0 * arg1) >> 64, unsigned 167 {name: "DIVV", argLength: 2, reg: gp21, asm: "DIVV", typ: "Int64"}, // arg0 / arg1, signed 168 {name: "DIVVU", argLength: 2, reg: gp21, asm: "DIVVU", typ: "UInt64"}, // arg0 / arg1, unsigned 169 {name: "REMV", argLength: 2, reg: gp21, asm: "REMV", typ: "Int64"}, // arg0 / arg1, signed 170 {name: "REMVU", argLength: 2, reg: gp21, asm: "REMVU", typ: "UInt64"}, // arg0 / arg1, unsigned 171 172 {name: "ADDF", argLength: 2, reg: fp21, asm: "ADDF", commutative: true}, // arg0 + arg1 173 {name: "ADDD", argLength: 2, reg: fp21, asm: "ADDD", commutative: true}, // arg0 + arg1 174 {name: "SUBF", argLength: 2, reg: fp21, asm: "SUBF"}, // arg0 - arg1 175 {name: "SUBD", argLength: 2, reg: fp21, asm: "SUBD"}, // arg0 - arg1 176 {name: "MULF", argLength: 2, reg: fp21, asm: "MULF", commutative: true}, // arg0 * arg1 177 {name: "MULD", argLength: 2, reg: fp21, asm: "MULD", commutative: true}, // arg0 * arg1 178 {name: "DIVF", argLength: 2, reg: fp21, asm: "DIVF"}, // arg0 / arg1 179 {name: "DIVD", argLength: 2, reg: fp21, asm: "DIVD"}, // arg0 / arg1 180 181 {name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true}, // arg0 & arg1 182 {name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int64"}, // arg0 & auxInt 183 {name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true}, // arg0 | arg1 184 {name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int64"}, // arg0 | auxInt 185 {name: "XOR", argLength: 2, reg: gp21, asm: "XOR", commutative: true, typ: "UInt64"}, // arg0 ^ arg1 186 {name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int64", typ: "UInt64"}, // arg0 ^ auxInt 187 {name: "NOR", argLength: 2, reg: gp21, asm: "NOR", commutative: true}, // ^(arg0 | arg1) 188 {name: "NORconst", argLength: 1, reg: gp11, asm: "NOR", aux: "Int64"}, // ^(arg0 | auxInt) 189 190 {name: "NEGV", argLength: 1, reg: gp11}, // -arg0 191 {name: "NEGF", argLength: 1, reg: fp11, asm: "NEGF"}, // -arg0, float32 192 {name: "NEGD", argLength: 1, reg: fp11, asm: "NEGD"}, // -arg0, float64 193 {name: "SQRTD", argLength: 1, reg: fp11, asm: "SQRTD"}, // sqrt(arg0), float64 194 {name: "SQRTF", argLength: 1, reg: fp11, asm: "SQRTF"}, // sqrt(arg0), float32 195 196 {name: "MASKEQZ", argLength: 2, reg: gp21, asm: "MASKEQZ"}, // returns 0 if arg1 == 0, otherwise returns arg0 197 {name: "MASKNEZ", argLength: 2, reg: gp21, asm: "MASKNEZ"}, // returns 0 if arg1 != 0, otherwise returns arg0 198 199 // shifts 200 {name: "SLLV", argLength: 2, reg: gp21, asm: "SLLV"}, // arg0 << arg1, shift amount is mod 64 201 {name: "SLLVconst", argLength: 1, reg: gp11, asm: "SLLV", aux: "Int64"}, // arg0 << auxInt 202 {name: "SRLV", argLength: 2, reg: gp21, asm: "SRLV"}, // arg0 >> arg1, unsigned, shift amount is mod 64 203 {name: "SRLVconst", argLength: 1, reg: gp11, asm: "SRLV", aux: "Int64"}, // arg0 >> auxInt, unsigned 204 {name: "SRAV", argLength: 2, reg: gp21, asm: "SRAV"}, // arg0 >> arg1, signed, shift amount is mod 64 205 {name: "SRAVconst", argLength: 1, reg: gp11, asm: "SRAV", aux: "Int64"}, // arg0 >> auxInt, signed 206 {name: "ROTR", argLength: 2, reg: gp21, asm: "ROTR"}, // arg0 right rotate by (arg1 mod 32) bits 207 {name: "ROTRV", argLength: 2, reg: gp21, asm: "ROTRV"}, // arg0 right rotate by (arg1 mod 64) bits 208 {name: "ROTRconst", argLength: 1, reg: gp11, asm: "ROTR", aux: "Int64"}, // uint32(arg0) right rotate by auxInt bits, auxInt should be in the range 0 to 31. 209 {name: "ROTRVconst", argLength: 1, reg: gp11, asm: "ROTRV", aux: "Int64"}, // arg0 right rotate by auxInt bits, auxInt should be in the range 0 to 63. 210 211 // comparisons 212 {name: "SGT", argLength: 2, reg: gp21, asm: "SGT", typ: "Bool"}, // 1 if arg0 > arg1 (signed), 0 otherwise 213 {name: "SGTconst", argLength: 1, reg: gp11, asm: "SGT", aux: "Int64", typ: "Bool"}, // 1 if auxInt > arg0 (signed), 0 otherwise 214 {name: "SGTU", argLength: 2, reg: gp21, asm: "SGTU", typ: "Bool"}, // 1 if arg0 > arg1 (unsigned), 0 otherwise 215 {name: "SGTUconst", argLength: 1, reg: gp11, asm: "SGTU", aux: "Int64", typ: "Bool"}, // 1 if auxInt > arg0 (unsigned), 0 otherwise 216 217 {name: "CMPEQF", argLength: 2, reg: fp2flags, asm: "CMPEQF", typ: "Flags"}, // flags=true if arg0 = arg1, float32 218 {name: "CMPEQD", argLength: 2, reg: fp2flags, asm: "CMPEQD", typ: "Flags"}, // flags=true if arg0 = arg1, float64 219 {name: "CMPGEF", argLength: 2, reg: fp2flags, asm: "CMPGEF", typ: "Flags"}, // flags=true if arg0 >= arg1, float32 220 {name: "CMPGED", argLength: 2, reg: fp2flags, asm: "CMPGED", typ: "Flags"}, // flags=true if arg0 >= arg1, float64 221 {name: "CMPGTF", argLength: 2, reg: fp2flags, asm: "CMPGTF", typ: "Flags"}, // flags=true if arg0 > arg1, float32 222 {name: "CMPGTD", argLength: 2, reg: fp2flags, asm: "CMPGTD", typ: "Flags"}, // flags=true if arg0 > arg1, float64 223 224 // moves 225 {name: "MOVVconst", argLength: 0, reg: gp01, aux: "Int64", asm: "MOVV", typ: "UInt64", rematerializeable: true}, // auxint 226 {name: "MOVFconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVF", typ: "Float32", rematerializeable: true}, // auxint as 64-bit float, convert to 32-bit float 227 {name: "MOVDconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVD", typ: "Float64", rematerializeable: true}, // auxint as 64-bit float 228 229 {name: "MOVVaddr", argLength: 1, reg: regInfo{inputs: []regMask{buildReg("SP") | buildReg("SB")}, outputs: []regMask{gp}}, aux: "SymOff", asm: "MOVV", rematerializeable: true, symEffect: "Addr"}, // arg0 + auxInt + aux.(*gc.Sym), arg0=SP/SB 230 231 {name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 232 {name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 233 {name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 234 {name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 235 {name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "Int32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 236 {name: "MOVWUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVWU", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 237 {name: "MOVVload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVV", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 238 {name: "MOVFload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVF", typ: "Float32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 239 {name: "MOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVD", typ: "Float64", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 240 241 {name: "MOVBstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVB", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 1 byte of arg1 to arg0 + auxInt + aux. arg2=mem. 242 {name: "MOVHstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVH", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 2 bytes of arg1 to arg0 + auxInt + aux. arg2=mem. 243 {name: "MOVWstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVW", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 4 bytes of arg1 to arg0 + auxInt + aux. arg2=mem. 244 {name: "MOVVstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVV", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of arg1 to arg0 + auxInt + aux. arg2=mem. 245 {name: "MOVFstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "MOVF", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 4 bytes of arg1 to arg0 + auxInt + aux. arg2=mem. 246 {name: "MOVDstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "MOVD", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of arg1 to arg0 + auxInt + aux. arg2=mem. 247 248 {name: "MOVBstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVB", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 1 byte of zero to arg0 + auxInt + aux. arg1=mem. 249 {name: "MOVHstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVH", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 2 bytes of zero to arg0 + auxInt + aux. arg1=mem. 250 {name: "MOVWstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVW", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 4 bytes of zero to arg0 + auxInt + aux. arg1=mem. 251 {name: "MOVVstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVV", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of zero to arg0 + auxInt + aux. ar12=mem. 252 253 // conversions 254 {name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVB"}, // move from arg0, sign-extended from byte 255 {name: "MOVBUreg", argLength: 1, reg: gp11, asm: "MOVBU"}, // move from arg0, unsign-extended from byte 256 {name: "MOVHreg", argLength: 1, reg: gp11, asm: "MOVH"}, // move from arg0, sign-extended from half 257 {name: "MOVHUreg", argLength: 1, reg: gp11, asm: "MOVHU"}, // move from arg0, unsign-extended from half 258 {name: "MOVWreg", argLength: 1, reg: gp11, asm: "MOVW"}, // move from arg0, sign-extended from word 259 {name: "MOVWUreg", argLength: 1, reg: gp11, asm: "MOVWU"}, // move from arg0, unsign-extended from word 260 {name: "MOVVreg", argLength: 1, reg: gp11, asm: "MOVV"}, // move from arg0 261 262 {name: "MOVVnop", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}, resultInArg0: true}, // nop, return arg0 in same register 263 264 {name: "MOVWF", argLength: 1, reg: fp11, asm: "MOVWF"}, // int32 -> float32 265 {name: "MOVWD", argLength: 1, reg: fp11, asm: "MOVWD"}, // int32 -> float64 266 {name: "MOVVF", argLength: 1, reg: fp11, asm: "MOVVF"}, // int64 -> float32 267 {name: "MOVVD", argLength: 1, reg: fp11, asm: "MOVVD"}, // int64 -> float64 268 {name: "TRUNCFW", argLength: 1, reg: fp11, asm: "TRUNCFW"}, // float32 -> int32 269 {name: "TRUNCDW", argLength: 1, reg: fp11, asm: "TRUNCDW"}, // float64 -> int32 270 {name: "TRUNCFV", argLength: 1, reg: fp11, asm: "TRUNCFV"}, // float32 -> int64 271 {name: "TRUNCDV", argLength: 1, reg: fp11, asm: "TRUNCDV"}, // float64 -> int64 272 {name: "MOVFD", argLength: 1, reg: fp11, asm: "MOVFD"}, // float32 -> float64 273 {name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"}, // float64 -> float32 274 275 // function calls 276 {name: "CALLstatic", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). last arg=mem, auxint=argsize, returns mem 277 {name: "CALLtail", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true}, // tail call static function aux.(*obj.LSym). last arg=mem, auxint=argsize, returns mem 278 {name: "CALLclosure", argLength: -1, reg: regInfo{inputs: []regMask{gpsp, buildReg("R29"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, last arg=mem, auxint=argsize, returns mem 279 {name: "CALLinter", argLength: -1, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, last arg=mem, auxint=argsize, returns mem 280 281 // duffzero 282 // arg0 = address of memory to zero 283 // arg1 = mem 284 // auxint = offset into duffzero code to start executing 285 // returns mem 286 // R20 aka loong64.REGRT1 changed as side effect 287 { 288 name: "DUFFZERO", 289 aux: "Int64", 290 argLength: 2, 291 reg: regInfo{ 292 inputs: []regMask{buildReg("R20")}, 293 clobbers: buildReg("R20 R1"), 294 }, 295 typ: "Mem", 296 faultOnNilArg0: true, 297 }, 298 299 // duffcopy 300 // arg0 = address of dst memory (in R21, changed as side effect) 301 // arg1 = address of src memory (in R20, changed as side effect) 302 // arg2 = mem 303 // auxint = offset into duffcopy code to start executing 304 // returns mem 305 { 306 name: "DUFFCOPY", 307 aux: "Int64", 308 argLength: 3, 309 reg: regInfo{ 310 inputs: []regMask{buildReg("R21"), buildReg("R20")}, 311 clobbers: buildReg("R20 R21 R1"), 312 }, 313 typ: "Mem", 314 faultOnNilArg0: true, 315 faultOnNilArg1: true, 316 }, 317 318 // large or unaligned zeroing 319 // arg0 = address of memory to zero (in R20, changed as side effect) 320 // arg1 = address of the last element to zero 321 // arg2 = mem 322 // auxint = alignment 323 // returns mem 324 // MOVx R0, (R20) 325 // ADDV $sz, R20 326 // BGEU Rarg1, R20, -2(PC) 327 { 328 name: "LoweredZero", 329 aux: "Int64", 330 argLength: 3, 331 reg: regInfo{ 332 inputs: []regMask{buildReg("R20"), gp}, 333 clobbers: buildReg("R20"), 334 }, 335 typ: "Mem", 336 faultOnNilArg0: true, 337 }, 338 339 // large or unaligned move 340 // arg0 = address of dst memory (in R21, changed as side effect) 341 // arg1 = address of src memory (in R20, changed as side effect) 342 // arg2 = address of the last element of src 343 // arg3 = mem 344 // auxint = alignment 345 // returns mem 346 // MOVx (R20), Rtmp 347 // MOVx Rtmp, (R21) 348 // ADDV $sz, R20 349 // ADDV $sz, R21 350 // BGEU Rarg2, R20, -4(PC) 351 { 352 name: "LoweredMove", 353 aux: "Int64", 354 argLength: 4, 355 reg: regInfo{ 356 inputs: []regMask{buildReg("R21"), buildReg("R20"), gp}, 357 clobbers: buildReg("R20 R21"), 358 }, 359 typ: "Mem", 360 faultOnNilArg0: true, 361 faultOnNilArg1: true, 362 }, 363 364 // atomic loads. 365 // load from arg0. arg1=mem. 366 // returns <value,memory> so they can be properly ordered with other loads. 367 {name: "LoweredAtomicLoad8", argLength: 2, reg: gpload, faultOnNilArg0: true}, 368 {name: "LoweredAtomicLoad32", argLength: 2, reg: gpload, faultOnNilArg0: true}, 369 {name: "LoweredAtomicLoad64", argLength: 2, reg: gpload, faultOnNilArg0: true}, 370 371 // atomic stores. 372 // store arg1 to arg0. arg2=mem. returns memory. 373 {name: "LoweredAtomicStore8", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true}, 374 {name: "LoweredAtomicStore32", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true}, 375 {name: "LoweredAtomicStore64", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true}, 376 // store zero to arg0. arg1=mem. returns memory. 377 {name: "LoweredAtomicStorezero32", argLength: 2, reg: gpstore0, faultOnNilArg0: true, hasSideEffects: true}, 378 {name: "LoweredAtomicStorezero64", argLength: 2, reg: gpstore0, faultOnNilArg0: true, hasSideEffects: true}, 379 380 // atomic exchange. 381 // store arg1 to arg0. arg2=mem. returns <old content of *arg0, memory>. 382 // DBAR 383 // LL (Rarg0), Rout 384 // MOVV Rarg1, Rtmp 385 // SC Rtmp, (Rarg0) 386 // BEQ Rtmp, -3(PC) 387 // DBAR 388 {name: "LoweredAtomicExchange32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, 389 {name: "LoweredAtomicExchange64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, 390 391 // atomic add. 392 // *arg0 += arg1. arg2=mem. returns <new content of *arg0, memory>. 393 // DBAR 394 // LL (Rarg0), Rout 395 // ADDV Rarg1, Rout, Rtmp 396 // SC Rtmp, (Rarg0) 397 // BEQ Rtmp, -3(PC) 398 // DBAR 399 // ADDV Rarg1, Rout 400 {name: "LoweredAtomicAdd32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, 401 {name: "LoweredAtomicAdd64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, 402 // *arg0 += auxint. arg1=mem. returns <new content of *arg0, memory>. auxint is 32-bit. 403 {name: "LoweredAtomicAddconst32", argLength: 2, reg: regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}, aux: "Int32", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, 404 {name: "LoweredAtomicAddconst64", argLength: 2, reg: regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}, aux: "Int64", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, 405 406 // atomic compare and swap. 407 // arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory. 408 // if *arg0 == arg1 { 409 // *arg0 = arg2 410 // return (true, memory) 411 // } else { 412 // return (false, memory) 413 // } 414 // DBAR 415 // MOVV $0, Rout 416 // LL (Rarg0), Rtmp 417 // BNE Rtmp, Rarg1, 4(PC) 418 // MOVV Rarg2, Rout 419 // SC Rout, (Rarg0) 420 // BEQ Rout, -4(PC) 421 // DBAR 422 {name: "LoweredAtomicCas32", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, 423 {name: "LoweredAtomicCas64", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, 424 425 // pseudo-ops 426 {name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil. arg1=mem. 427 428 {name: "FPFlagTrue", argLength: 1, reg: readflags}, // bool, true if FP flag is true 429 {name: "FPFlagFalse", argLength: 1, reg: readflags}, // bool, true if FP flag is false 430 431 // Scheduler ensures LoweredGetClosurePtr occurs only in entry block, 432 // and sorts it to the very beginning of the block to prevent other 433 // use of R22 (loong64.REGCTXT, the closure pointer) 434 {name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R29")}}, zeroWidth: true}, 435 436 // LoweredGetCallerSP returns the SP of the caller of the current function. arg0=mem. 437 {name: "LoweredGetCallerSP", argLength: 1, reg: gp01, rematerializeable: true}, 438 439 // LoweredGetCallerPC evaluates to the PC to which its "caller" will return. 440 // I.e., if f calls g "calls" getcallerpc, 441 // the result should be the PC within f that g will return to. 442 // See runtime/stubs.go for a more detailed discussion. 443 {name: "LoweredGetCallerPC", reg: gp01, rematerializeable: true}, 444 445 // LoweredWB invokes runtime.gcWriteBarrier. arg0=mem, auxint=# of buffer entries needed 446 // It saves all GP registers if necessary, 447 // but clobbers R1 (LR) because it's a call 448 // and R30 (REGTMP). 449 // Returns a pointer to a write barrier buffer in R29. 450 {name: "LoweredWB", argLength: 1, reg: regInfo{clobbers: (callerSave &^ gpg) | buildReg("R1"), outputs: []regMask{buildReg("R29")}}, clobberFlags: true, aux: "Int64"}, 451 452 // There are three of these functions so that they can have three different register inputs. 453 // When we check 0 <= c <= cap (A), then 0 <= b <= c (B), then 0 <= a <= b (C), we want the 454 // default registers to match so we don't need to copy registers around unnecessarily. 455 {name: "LoweredPanicBoundsA", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r3, r4}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in genericOps.go). 456 {name: "LoweredPanicBoundsB", 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 genericOps.go). 457 {name: "LoweredPanicBoundsC", 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 genericOps.go). 458 } 459 460 blocks := []blockData{ 461 {name: "EQ", controls: 1}, 462 {name: "NE", controls: 1}, 463 {name: "LTZ", controls: 1}, // < 0 464 {name: "LEZ", controls: 1}, // <= 0 465 {name: "GTZ", controls: 1}, // > 0 466 {name: "GEZ", controls: 1}, // >= 0 467 {name: "FPT", controls: 1}, // FP flag is true 468 {name: "FPF", controls: 1}, // FP flag is false 469 } 470 471 archs = append(archs, arch{ 472 name: "LOONG64", 473 pkg: "github.com/go-asm/go/cmd/obj/loong64", 474 genfile: "../../loong64/ssa.go", 475 ops: ops, 476 blocks: blocks, 477 regnames: regNamesLOONG64, 478 // TODO: support register ABI on loong64 479 ParamIntRegNames: "R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19", 480 ParamFloatRegNames: "F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15", 481 gpregmask: gp, 482 fpregmask: fp, 483 framepointerreg: -1, // not used 484 linkreg: int8(num["R1"]), 485 }) 486 }