github.com/bir3/gocompiler@v0.3.205/src/cmd/compile/internal/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 R23 R24 R25 R26 R27 R28 R29 R31") // R1 is LR, R2 is thread pointer, R3 is stack pointer, R21-unused, R22 is g, R30 is REGTMP 127 gps = buildReg("R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31") | buildReg("g") 128 gpg = gp | buildReg("g") 129 gpsp = gp | buildReg("SP") 130 gpspg = gpg | buildReg("SP") 131 gpspsbg = gpspg | buildReg("SB") 132 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") 133 callerSave = gp | fp | buildReg("g") // runtime.setg (and anything calling it) may clobber g 134 r1 = buildReg("R19") 135 r2 = buildReg("R18") 136 r3 = buildReg("R17") 137 r4 = buildReg("R4") 138 ) 139 // Common regInfo 140 var ( 141 gp01 = regInfo{inputs: nil, outputs: []regMask{gp}} 142 gp11 = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}} 143 gp11sp = regInfo{inputs: []regMask{gpspg}, outputs: []regMask{gp}} 144 gp21 = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}} 145 gp22 = regInfo{inputs: []regMask{gps, gps}, outputs: []regMask{gp, gp}} 146 gpload = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}} 147 gpstore = regInfo{inputs: []regMask{gpspsbg, gpg}} 148 gpstore0 = regInfo{inputs: []regMask{gpspsbg}} 149 gpxchg = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{gp}} 150 gpcas = regInfo{inputs: []regMask{gpspsbg, gpg, gpg}, outputs: []regMask{gp}} 151 fp01 = regInfo{inputs: nil, outputs: []regMask{fp}} 152 fp11 = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}} 153 fp21 = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{fp}} 154 fp2flags = regInfo{inputs: []regMask{fp, fp}} 155 fpload = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp}} 156 fpstore = regInfo{inputs: []regMask{gpspsbg, fp}} 157 readflags = regInfo{inputs: nil, outputs: []regMask{gp}} 158 ) 159 ops := []opData{ 160 // binary ops 161 {name: "ADDV", argLength: 2, reg: gp21, asm: "ADDVU", commutative: true}, // arg0 + arg1 162 {name: "ADDVconst", argLength: 1, reg: gp11sp, asm: "ADDVU", aux: "Int64"}, // arg0 + auxInt. auxInt is 32-bit, also in other *const ops. 163 {name: "SUBV", argLength: 2, reg: gp21, asm: "SUBVU"}, // arg0 - arg1 164 {name: "SUBVconst", argLength: 1, reg: gp11, asm: "SUBVU", aux: "Int64"}, // arg0 - auxInt 165 166 {name: "MULV", argLength: 2, reg: gp22, resultNotInArgs: true, commutative: true, typ: "(Int64,Int64)"}, // arg0 * arg1, signed 167 {name: "MULVU", argLength: 2, reg: gp22, resultNotInArgs: true, commutative: true, typ: "(UInt64,UInt64)"}, // arg0 * arg1, unsigned 168 {name: "DIVV", argLength: 2, reg: gp22, resultNotInArgs: true, typ: "(Int64,Int64)"}, // arg0 / arg1, signed 169 {name: "DIVVU", argLength: 2, reg: gp22, resultNotInArgs: true, typ: "(UInt64,UInt64)"}, // arg0 / arg1, unsigned 170 171 {name: "ADDF", argLength: 2, reg: fp21, asm: "ADDF", commutative: true}, // arg0 + arg1 172 {name: "ADDD", argLength: 2, reg: fp21, asm: "ADDD", commutative: true}, // arg0 + arg1 173 {name: "SUBF", argLength: 2, reg: fp21, asm: "SUBF"}, // arg0 - arg1 174 {name: "SUBD", argLength: 2, reg: fp21, asm: "SUBD"}, // arg0 - arg1 175 {name: "MULF", argLength: 2, reg: fp21, asm: "MULF", commutative: true}, // arg0 * arg1 176 {name: "MULD", argLength: 2, reg: fp21, asm: "MULD", commutative: true}, // arg0 * arg1 177 {name: "DIVF", argLength: 2, reg: fp21, asm: "DIVF"}, // arg0 / arg1 178 {name: "DIVD", argLength: 2, reg: fp21, asm: "DIVD"}, // arg0 / arg1 179 180 {name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true}, // arg0 & arg1 181 {name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int64"}, // arg0 & auxInt 182 {name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true}, // arg0 | arg1 183 {name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int64"}, // arg0 | auxInt 184 {name: "XOR", argLength: 2, reg: gp21, asm: "XOR", commutative: true, typ: "UInt64"}, // arg0 ^ arg1 185 {name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int64", typ: "UInt64"}, // arg0 ^ auxInt 186 {name: "NOR", argLength: 2, reg: gp21, asm: "NOR", commutative: true}, // ^(arg0 | arg1) 187 {name: "NORconst", argLength: 1, reg: gp11, asm: "NOR", aux: "Int64"}, // ^(arg0 | auxInt) 188 189 {name: "NEGV", argLength: 1, reg: gp11}, // -arg0 190 {name: "NEGF", argLength: 1, reg: fp11, asm: "NEGF"}, // -arg0, float32 191 {name: "NEGD", argLength: 1, reg: fp11, asm: "NEGD"}, // -arg0, float64 192 {name: "SQRTD", argLength: 1, reg: fp11, asm: "SQRTD"}, // sqrt(arg0), float64 193 {name: "SQRTF", argLength: 1, reg: fp11, asm: "SQRTF"}, // sqrt(arg0), float32 194 195 {name: "MASKEQZ", argLength: 2, reg: gp21, asm: "MASKEQZ"}, // returns 0 if arg1 == 0, otherwise returns arg0 196 {name: "MASKNEZ", argLength: 2, reg: gp21, asm: "MASKNEZ"}, // returns 0 if arg1 != 0, otherwise returns arg0 197 198 // shifts 199 {name: "SLLV", argLength: 2, reg: gp21, asm: "SLLV"}, // arg0 << arg1, shift amount is mod 64 200 {name: "SLLVconst", argLength: 1, reg: gp11, asm: "SLLV", aux: "Int64"}, // arg0 << auxInt 201 {name: "SRLV", argLength: 2, reg: gp21, asm: "SRLV"}, // arg0 >> arg1, unsigned, shift amount is mod 64 202 {name: "SRLVconst", argLength: 1, reg: gp11, asm: "SRLV", aux: "Int64"}, // arg0 >> auxInt, unsigned 203 {name: "SRAV", argLength: 2, reg: gp21, asm: "SRAV"}, // arg0 >> arg1, signed, shift amount is mod 64 204 {name: "SRAVconst", argLength: 1, reg: gp11, asm: "SRAV", aux: "Int64"}, // arg0 >> auxInt, signed 205 {name: "ROTR", argLength: 2, reg: gp21, asm: "ROTR"}, // arg0 right rotate by (arg1 mod 32) bits 206 {name: "ROTRV", argLength: 2, reg: gp21, asm: "ROTRV"}, // arg0 right rotate by (arg1 mod 64) bits 207 {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. 208 {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. 209 210 // comparisons 211 {name: "SGT", argLength: 2, reg: gp21, asm: "SGT", typ: "Bool"}, // 1 if arg0 > arg1 (signed), 0 otherwise 212 {name: "SGTconst", argLength: 1, reg: gp11, asm: "SGT", aux: "Int64", typ: "Bool"}, // 1 if auxInt > arg0 (signed), 0 otherwise 213 {name: "SGTU", argLength: 2, reg: gp21, asm: "SGTU", typ: "Bool"}, // 1 if arg0 > arg1 (unsigned), 0 otherwise 214 {name: "SGTUconst", argLength: 1, reg: gp11, asm: "SGTU", aux: "Int64", typ: "Bool"}, // 1 if auxInt > arg0 (unsigned), 0 otherwise 215 216 {name: "CMPEQF", argLength: 2, reg: fp2flags, asm: "CMPEQF", typ: "Flags"}, // flags=true if arg0 = arg1, float32 217 {name: "CMPEQD", argLength: 2, reg: fp2flags, asm: "CMPEQD", typ: "Flags"}, // flags=true if arg0 = arg1, float64 218 {name: "CMPGEF", argLength: 2, reg: fp2flags, asm: "CMPGEF", typ: "Flags"}, // flags=true if arg0 >= arg1, float32 219 {name: "CMPGED", argLength: 2, reg: fp2flags, asm: "CMPGED", typ: "Flags"}, // flags=true if arg0 >= arg1, float64 220 {name: "CMPGTF", argLength: 2, reg: fp2flags, asm: "CMPGTF", typ: "Flags"}, // flags=true if arg0 > arg1, float32 221 {name: "CMPGTD", argLength: 2, reg: fp2flags, asm: "CMPGTD", typ: "Flags"}, // flags=true if arg0 > arg1, float64 222 223 // moves 224 {name: "MOVVconst", argLength: 0, reg: gp01, aux: "Int64", asm: "MOVV", typ: "UInt64", rematerializeable: true}, // auxint 225 {name: "MOVFconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVF", typ: "Float32", rematerializeable: true}, // auxint as 64-bit float, convert to 32-bit float 226 {name: "MOVDconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVD", typ: "Float64", rematerializeable: true}, // auxint as 64-bit float 227 228 {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 229 230 {name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 231 {name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 232 {name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 233 {name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 234 {name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "Int32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 235 {name: "MOVWUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVWU", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 236 {name: "MOVVload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVV", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 237 {name: "MOVFload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVF", typ: "Float32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 238 {name: "MOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVD", typ: "Float64", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. 239 240 {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. 241 {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. 242 {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. 243 {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. 244 {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. 245 {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. 246 247 {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. 248 {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. 249 {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. 250 {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. 251 252 // conversions 253 {name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVB"}, // move from arg0, sign-extended from byte 254 {name: "MOVBUreg", argLength: 1, reg: gp11, asm: "MOVBU"}, // move from arg0, unsign-extended from byte 255 {name: "MOVHreg", argLength: 1, reg: gp11, asm: "MOVH"}, // move from arg0, sign-extended from half 256 {name: "MOVHUreg", argLength: 1, reg: gp11, asm: "MOVHU"}, // move from arg0, unsign-extended from half 257 {name: "MOVWreg", argLength: 1, reg: gp11, asm: "MOVW"}, // move from arg0, sign-extended from word 258 {name: "MOVWUreg", argLength: 1, reg: gp11, asm: "MOVWU"}, // move from arg0, unsign-extended from word 259 {name: "MOVVreg", argLength: 1, reg: gp11, asm: "MOVV"}, // move from arg0 260 261 {name: "MOVVnop", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}, resultInArg0: true}, // nop, return arg0 in same register 262 263 {name: "MOVWF", argLength: 1, reg: fp11, asm: "MOVWF"}, // int32 -> float32 264 {name: "MOVWD", argLength: 1, reg: fp11, asm: "MOVWD"}, // int32 -> float64 265 {name: "MOVVF", argLength: 1, reg: fp11, asm: "MOVVF"}, // int64 -> float32 266 {name: "MOVVD", argLength: 1, reg: fp11, asm: "MOVVD"}, // int64 -> float64 267 {name: "TRUNCFW", argLength: 1, reg: fp11, asm: "TRUNCFW"}, // float32 -> int32 268 {name: "TRUNCDW", argLength: 1, reg: fp11, asm: "TRUNCDW"}, // float64 -> int32 269 {name: "TRUNCFV", argLength: 1, reg: fp11, asm: "TRUNCFV"}, // float32 -> int64 270 {name: "TRUNCDV", argLength: 1, reg: fp11, asm: "TRUNCDV"}, // float64 -> int64 271 {name: "MOVFD", argLength: 1, reg: fp11, asm: "MOVFD"}, // float32 -> float64 272 {name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"}, // float64 -> float32 273 274 // function calls 275 {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 276 {name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem 277 {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R29"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem 278 {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem 279 280 // duffzero 281 // arg0 = address of memory to zero 282 // arg1 = mem 283 // auxint = offset into duffzero code to start executing 284 // returns mem 285 // R19 aka loong64.REGRT1 changed as side effect 286 { 287 name: "DUFFZERO", 288 aux: "Int64", 289 argLength: 2, 290 reg: regInfo{ 291 inputs: []regMask{gp}, 292 clobbers: buildReg("R19 R1"), 293 }, 294 faultOnNilArg0: true, 295 }, 296 297 // duffcopy 298 // arg0 = address of dst memory (in R20, changed as side effect) REGRT2 299 // arg1 = address of src memory (in R19, changed as side effect) REGRT1 300 // arg2 = mem 301 // auxint = offset into duffcopy code to start executing 302 // returns mem 303 { 304 name: "DUFFCOPY", 305 aux: "Int64", 306 argLength: 3, 307 reg: regInfo{ 308 inputs: []regMask{buildReg("R20"), buildReg("R19")}, 309 clobbers: buildReg("R19 R20 R1"), 310 }, 311 faultOnNilArg0: true, 312 faultOnNilArg1: true, 313 }, 314 315 // large or unaligned zeroing 316 // arg0 = address of memory to zero (in R19, changed as side effect) 317 // arg1 = address of the last element to zero 318 // arg2 = mem 319 // auxint = alignment 320 // returns mem 321 // SUBV $8, R19 322 // MOVV R0, 8(R19) 323 // ADDV $8, R19 324 // BNE Rarg1, R19, -2(PC) 325 { 326 name: "LoweredZero", 327 aux: "Int64", 328 argLength: 3, 329 reg: regInfo{ 330 inputs: []regMask{buildReg("R19"), gp}, 331 clobbers: buildReg("R19"), 332 }, 333 clobberFlags: true, 334 faultOnNilArg0: true, 335 }, 336 337 // large or unaligned move 338 // arg0 = address of dst memory (in R4, changed as side effect) 339 // arg1 = address of src memory (in R19, changed as side effect) 340 // arg2 = address of the last element of src 341 // arg3 = mem 342 // auxint = alignment 343 // returns mem 344 // SUBV $8, R19 345 // MOVV 8(R19), Rtmp 346 // MOVV Rtmp, (R4) 347 // ADDV $8, R19 348 // ADDV $8, R4 349 // BNE Rarg2, R19, -4(PC) 350 { 351 name: "LoweredMove", 352 aux: "Int64", 353 argLength: 4, 354 reg: regInfo{ 355 inputs: []regMask{buildReg("R4"), buildReg("R19"), gp}, 356 clobbers: buildReg("R19 R4"), 357 }, 358 clobberFlags: true, 359 faultOnNilArg0: true, 360 faultOnNilArg1: true, 361 }, 362 363 // atomic loads. 364 // load from arg0. arg1=mem. 365 // returns <value,memory> so they can be properly ordered with other loads. 366 {name: "LoweredAtomicLoad8", argLength: 2, reg: gpload, faultOnNilArg0: true}, 367 {name: "LoweredAtomicLoad32", argLength: 2, reg: gpload, faultOnNilArg0: true}, 368 {name: "LoweredAtomicLoad64", argLength: 2, reg: gpload, faultOnNilArg0: true}, 369 370 // atomic stores. 371 // store arg1 to arg0. arg2=mem. returns memory. 372 {name: "LoweredAtomicStore8", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true}, 373 {name: "LoweredAtomicStore32", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true}, 374 {name: "LoweredAtomicStore64", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true}, 375 // store zero to arg0. arg1=mem. returns memory. 376 {name: "LoweredAtomicStorezero32", argLength: 2, reg: gpstore0, faultOnNilArg0: true, hasSideEffects: true}, 377 {name: "LoweredAtomicStorezero64", argLength: 2, reg: gpstore0, faultOnNilArg0: true, hasSideEffects: true}, 378 379 // atomic exchange. 380 // store arg1 to arg0. arg2=mem. returns <old content of *arg0, memory>. 381 // DBAR 382 // LL (Rarg0), Rout 383 // MOVV Rarg1, Rtmp 384 // SC Rtmp, (Rarg0) 385 // BEQ Rtmp, -3(PC) 386 // DBAR 387 {name: "LoweredAtomicExchange32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, 388 {name: "LoweredAtomicExchange64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, 389 390 // atomic add. 391 // *arg0 += arg1. arg2=mem. returns <new content of *arg0, memory>. 392 // DBAR 393 // LL (Rarg0), Rout 394 // ADDV Rarg1, Rout, Rtmp 395 // SC Rtmp, (Rarg0) 396 // BEQ Rtmp, -3(PC) 397 // DBAR 398 // ADDV Rarg1, Rout 399 {name: "LoweredAtomicAdd32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, 400 {name: "LoweredAtomicAdd64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, 401 // *arg0 += auxint. arg1=mem. returns <new content of *arg0, memory>. auxint is 32-bit. 402 {name: "LoweredAtomicAddconst32", argLength: 2, reg: regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}, aux: "Int32", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, 403 {name: "LoweredAtomicAddconst64", argLength: 2, reg: regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}, aux: "Int64", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, 404 405 // atomic compare and swap. 406 // arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory. 407 // if *arg0 == arg1 { 408 // *arg0 = arg2 409 // return (true, memory) 410 // } else { 411 // return (false, memory) 412 // } 413 // DBAR 414 // MOVV $0, Rout 415 // LL (Rarg0), Rtmp 416 // BNE Rtmp, Rarg1, 4(PC) 417 // MOVV Rarg2, Rout 418 // SC Rout, (Rarg0) 419 // BEQ Rout, -4(PC) 420 // DBAR 421 {name: "LoweredAtomicCas32", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, 422 {name: "LoweredAtomicCas64", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, 423 424 // pseudo-ops 425 {name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil. arg1=mem. 426 427 {name: "FPFlagTrue", argLength: 1, reg: readflags}, // bool, true if FP flag is true 428 {name: "FPFlagFalse", argLength: 1, reg: readflags}, // bool, true if FP flag is false 429 430 // Scheduler ensures LoweredGetClosurePtr occurs only in entry block, 431 // and sorts it to the very beginning of the block to prevent other 432 // use of R22 (loong64.REGCTXT, the closure pointer) 433 {name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R29")}}, zeroWidth: true}, 434 435 // LoweredGetCallerSP returns the SP of the caller of the current function. 436 {name: "LoweredGetCallerSP", reg: gp01, rematerializeable: true}, 437 438 // LoweredGetCallerPC evaluates to the PC to which its "caller" will return. 439 // I.e., if f calls g "calls" getcallerpc, 440 // the result should be the PC within f that g will return to. 441 // See runtime/stubs.go for a more detailed discussion. 442 {name: "LoweredGetCallerPC", reg: gp01, rematerializeable: true}, 443 444 // LoweredWB invokes runtime.gcWriteBarrier. arg0=destptr, arg1=srcptr, arg2=mem, aux=runtime.gcWriteBarrier 445 // It saves all GP registers if necessary, 446 // but clobbers R1 (LR) because it's a call 447 // and R30 (REGTMP). 448 {name: "LoweredWB", argLength: 3, reg: regInfo{inputs: []regMask{buildReg("R27"), buildReg("R28")}, clobbers: (callerSave &^ gpg) | buildReg("R1")}, clobberFlags: true, aux: "Sym", symEffect: "None"}, 449 450 // There are three of these functions so that they can have three different register inputs. 451 // When we check 0 <= c <= cap (A), then 0 <= b <= c (B), then 0 <= a <= b (C), we want the 452 // default registers to match so we don't need to copy registers around unnecessarily. 453 {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). 454 {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). 455 {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). 456 } 457 458 blocks := []blockData{ 459 {name: "EQ", controls: 1}, 460 {name: "NE", controls: 1}, 461 {name: "LTZ", controls: 1}, // < 0 462 {name: "LEZ", controls: 1}, // <= 0 463 {name: "GTZ", controls: 1}, // > 0 464 {name: "GEZ", controls: 1}, // >= 0 465 {name: "FPT", controls: 1}, // FP flag is true 466 {name: "FPF", controls: 1}, // FP flag is false 467 } 468 469 archs = append(archs, arch{ 470 name: "LOONG64", 471 pkg: "cmd/internal/obj/loong64", 472 genfile: "../../loong64/ssa.go", 473 ops: ops, 474 blocks: blocks, 475 regnames: regNamesLOONG64, 476 // TODO: support register ABI on loong64 477 ParamIntRegNames: "R4 R5 R6 R7 R8 R9 R10 R11", 478 ParamFloatRegNames: "F0 F1 F2 F3 F4 F5 F6 F7", 479 gpregmask: gp, 480 fpregmask: fp, 481 framepointerreg: -1, // not used 482 linkreg: int8(num["R1"]), 483 }) 484 }