github.com/riscv/riscv-go@v0.0.0-20200123204226-124ebd6fcc8e/src/cmd/compile/internal/ssa/gen/RISCVOps.go (about) 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 "cmd/internal/obj/riscv" 10 11 func init() { 12 var regNamesRISCV []string 13 var gpMask, fpMask, gpspMask, gpspsbMask regMask 14 regNamed := make(map[string]regMask) 15 16 // Build the list of register names, creating an appropriately indexed 17 // regMask for the gp and fp registers as we go. 18 // 19 // If name is specified, use it rather than the riscv reg number. 20 addreg := func(r int, name string) regMask { 21 mask := regMask(1) << uint(len(regNamesRISCV)) 22 if name == "" { 23 name = riscv.RegNames[int16(r)] 24 } 25 regNamesRISCV = append(regNamesRISCV, name) 26 regNamed[name] = mask 27 return mask 28 } 29 30 // General purpose registers. 31 for r := riscv.REG_X0; r <= riscv.REG_X31; r++ { 32 if r == riscv.REG_RA { 33 // RA is not used by regalloc, so we skip it to leave 34 // room for pseudo-register SB. 35 continue 36 } 37 38 mask := addreg(r, "") 39 40 // Add general purpose registers to gpMask. 41 switch r { 42 // ZERO, g, and TMP are not in any gp mask. 43 case riscv.REG_ZERO, riscv.REG_G, riscv.REG_TMP: 44 case riscv.REG_SP: 45 gpspMask |= mask 46 gpspsbMask |= mask 47 default: 48 gpMask |= mask 49 gpspMask |= mask 50 gpspsbMask |= mask 51 } 52 } 53 54 // Floating pointer registers. 55 for r := riscv.REG_F0; r <= riscv.REG_F31; r++ { 56 mask := addreg(r, "") 57 fpMask |= mask 58 } 59 60 // Pseudo-register: SB 61 mask := addreg(-1, "SB") 62 gpspsbMask |= mask 63 64 if len(regNamesRISCV) > 64 { 65 // regMask is only 64 bits. 66 panic("Too many RISCV registers") 67 } 68 69 regCtxt := regNamed["CTXT"] 70 callerSave := gpMask | fpMask | regNamed["g"] 71 72 var ( 73 gpstore = regInfo{inputs: []regMask{gpspsbMask, gpspMask, 0}} // SB in first input so we can load from a global, but not in second to avoid using SB as a temporary register 74 gp01 = regInfo{outputs: []regMask{gpMask}} 75 gp11 = regInfo{inputs: []regMask{gpMask}, outputs: []regMask{gpMask}} 76 gp21 = regInfo{inputs: []regMask{gpMask, gpMask}, outputs: []regMask{gpMask}} 77 gpload = regInfo{inputs: []regMask{gpspsbMask, 0}, outputs: []regMask{gpMask}} 78 gp11sb = regInfo{inputs: []regMask{gpspsbMask}, outputs: []regMask{gpMask}} 79 80 fp11 = regInfo{inputs: []regMask{fpMask}, outputs: []regMask{fpMask}} 81 fp21 = regInfo{inputs: []regMask{fpMask, fpMask}, outputs: []regMask{fpMask}} 82 gpfp = regInfo{inputs: []regMask{gpMask}, outputs: []regMask{fpMask}} 83 fpgp = regInfo{inputs: []regMask{fpMask}, outputs: []regMask{gpMask}} 84 fpstore = regInfo{inputs: []regMask{gpspsbMask, fpMask, 0}} 85 fpload = regInfo{inputs: []regMask{gpspsbMask, 0}, outputs: []regMask{fpMask}} 86 fp2gp = regInfo{inputs: []regMask{fpMask, fpMask}, outputs: []regMask{gpMask}} 87 88 call = regInfo{clobbers: callerSave} 89 callClosure = regInfo{inputs: []regMask{gpspMask, regCtxt, 0}, clobbers: callerSave} 90 callInter = regInfo{inputs: []regMask{gpMask}, clobbers: callerSave} 91 ) 92 93 RISCVops := []opData{ 94 {name: "ADD", argLength: 2, reg: gp21, asm: "ADD", commutative: true}, // arg0 + arg1 95 {name: "ADDI", argLength: 1, reg: gp11sb, asm: "ADDI", aux: "Int64"}, // arg0 + auxint 96 {name: "SUB", argLength: 2, reg: gp21, asm: "SUB"}, // arg0 - arg1 97 98 // M extension. H means high (i.e., it returns the top bits of 99 // the result). U means unsigned. W means word (i.e., 32-bit). 100 {name: "MUL", argLength: 2, reg: gp21, asm: "MUL", commutative: true, typ: "Int64"}, // arg0 * arg1 101 {name: "MULW", argLength: 2, reg: gp21, asm: "MULW", commutative: true, typ: "Int32"}, 102 {name: "MULH", argLength: 2, reg: gp21, asm: "MULH", commutative: true, typ: "Int64"}, 103 {name: "MULHU", argLength: 2, reg: gp21, asm: "MULHU", commutative: true, typ: "UInt64"}, 104 {name: "DIV", argLength: 2, reg: gp21, asm: "DIV", typ: "Int64"}, // arg0 / arg1 105 {name: "DIVU", argLength: 2, reg: gp21, asm: "DIVU", typ: "UInt64"}, 106 {name: "DIVW", argLength: 2, reg: gp21, asm: "DIVW", typ: "Int32"}, 107 {name: "DIVUW", argLength: 2, reg: gp21, asm: "DIVUW", typ: "UInt32"}, 108 {name: "REM", argLength: 2, reg: gp21, asm: "REM", typ: "Int64"}, // arg0 % arg1 109 {name: "REMU", argLength: 2, reg: gp21, asm: "REMU", typ: "UInt64"}, 110 {name: "REMW", argLength: 2, reg: gp21, asm: "REMW", typ: "Int32"}, 111 {name: "REMUW", argLength: 2, reg: gp21, asm: "REMUW", typ: "UInt32"}, 112 113 {name: "MOVaddr", argLength: 1, reg: gp11sb, asm: "MOV", aux: "SymOff", rematerializeable: true}, // arg0 + auxint + offset encoded in aux 114 // auxint+aux == add auxint and the offset of the symbol in aux (if any) to the effective address 115 116 {name: "MOVBconst", reg: gp01, asm: "MOV", typ: "UInt8", aux: "Int8", rematerializeable: true}, // 8 low bits of auxint 117 {name: "MOVHconst", reg: gp01, asm: "MOV", typ: "UInt16", aux: "Int16", rematerializeable: true}, // 16 low bits of auxint 118 {name: "MOVWconst", reg: gp01, asm: "MOV", typ: "UInt32", aux: "Int32", rematerializeable: true}, // 32 low bits of auxint 119 {name: "MOVDconst", reg: gp01, asm: "MOV", typ: "UInt64", aux: "Int64", rematerializeable: true}, // auxint 120 {name: "MOVSconst", reg: gp01, asm: "MOV", typ: "Float32", aux: "Int32", rematerializeable: true}, // auxint as float 121 122 // Loads: load <size> bits from arg0+auxint+aux and extend to 64 bits; arg1=mem 123 {name: "MOVBload", argLength: 2, reg: gpload, asm: "MOVB", aux: "SymOff", typ: "Int8", faultOnNilArg0: true}, // 8 bits, sign extend 124 {name: "MOVHload", argLength: 2, reg: gpload, asm: "MOVH", aux: "SymOff", typ: "Int16", faultOnNilArg0: true}, // 16 bits, sign extend 125 {name: "MOVWload", argLength: 2, reg: gpload, asm: "MOVW", aux: "SymOff", typ: "Int32", faultOnNilArg0: true}, // 32 bits, sign extend 126 {name: "MOVDload", argLength: 2, reg: gpload, asm: "MOV", aux: "SymOff", typ: "Int64", faultOnNilArg0: true}, // 64 bits 127 {name: "MOVBUload", argLength: 2, reg: gpload, asm: "MOVBU", aux: "SymOff", typ: "UInt8", faultOnNilArg0: true}, // 8 bits, zero extend 128 {name: "MOVHUload", argLength: 2, reg: gpload, asm: "MOVHU", aux: "SymOff", typ: "UInt16", faultOnNilArg0: true}, // 16 bits, zero extend 129 {name: "MOVWUload", argLength: 2, reg: gpload, asm: "MOVWU", aux: "SymOff", typ: "UInt32", faultOnNilArg0: true}, // 32 bits, zero extend 130 131 // Stores: store <size> lowest bits in arg1 to arg0+auxint+aux; arg2=mem 132 {name: "MOVBstore", argLength: 3, reg: gpstore, asm: "MOVB", aux: "SymOff", typ: "Mem", faultOnNilArg0: true}, // 8 bits 133 {name: "MOVHstore", argLength: 3, reg: gpstore, asm: "MOVH", aux: "SymOff", typ: "Mem", faultOnNilArg0: true}, // 16 bits 134 {name: "MOVWstore", argLength: 3, reg: gpstore, asm: "MOVW", aux: "SymOff", typ: "Mem", faultOnNilArg0: true}, // 32 bits 135 {name: "MOVDstore", argLength: 3, reg: gpstore, asm: "MOV", aux: "SymOff", typ: "Mem", faultOnNilArg0: true}, // 64 bits 136 137 // Shift ops 138 {name: "SLL", argLength: 2, reg: gp21, asm: "SLL"}, // arg0 << aux1 139 {name: "SRA", argLength: 2, reg: gp21, asm: "SRA"}, // arg0 >> aux1, signed 140 {name: "SRL", argLength: 2, reg: gp21, asm: "SRL"}, // arg0 >> aux1, unsigned 141 {name: "SLLI", argLength: 1, reg: gp11, asm: "SLLI", aux: "Int64"}, // arg0 << auxint 142 {name: "SRAI", argLength: 1, reg: gp11, asm: "SRAI", aux: "Int64"}, // arg0 >> auxint, signed 143 {name: "SRLI", argLength: 1, reg: gp11, asm: "SRLI", aux: "Int64"}, // arg0 >> auxint, unsigned 144 145 // Bitwise ops 146 {name: "XOR", argLength: 2, reg: gp21, asm: "XOR", commutative: true}, // arg0 ^ arg1 147 {name: "XORI", argLength: 1, reg: gp11, asm: "XORI", aux: "Int64"}, // arg0 ^ auxint 148 {name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true}, // arg0 | arg1 149 {name: "ORI", argLength: 1, reg: gp11, asm: "ORI", aux: "Int64"}, // arg0 | auxint 150 {name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true}, // arg0 & arg1 151 {name: "ANDI", argLength: 1, reg: gp11, asm: "ANDI", aux: "Int64"}, // arg0 & auxint 152 153 // Generate boolean values 154 {name: "SEQZ", argLength: 1, reg: gp11, asm: "SEQZ"}, // arg0 == 0, result is 0 or 1 155 {name: "SNEZ", argLength: 1, reg: gp11, asm: "SNEZ"}, // arg0 != 0, result is 0 or 1 156 {name: "SLT", argLength: 2, reg: gp21, asm: "SLT"}, // arg0 < arg1, result is 0 or 1 157 {name: "SLTI", argLength: 1, reg: gp11, asm: "SLTI", aux: "Int64"}, // arg0 < auxint, result is 0 or 1 158 {name: "SLTU", argLength: 2, reg: gp21, asm: "SLTU"}, // arg0 < arg1, unsigned, result is 0 or 1 159 {name: "SLTIU", argLength: 1, reg: gp11, asm: "SLTIU", aux: "Int64"}, // arg0 < auxint, unsigned, result is 0 or 1 160 161 // MOVconvert converts between pointers and integers. 162 // We have a special op for this so as to not confuse GC 163 // (particularly stack maps). It takes a memory arg so it 164 // gets correctly ordered with respect to GC safepoints. 165 {name: "MOVconvert", argLength: 2, reg: gp11, asm: "MOV"}, // arg0, but converted to int/ptr as appropriate; arg1=mem 166 167 // Calls 168 {name: "CALLstatic", argLength: 1, reg: call, aux: "SymOff", call: true}, // call static function aux.(*gc.Sym). arg0=mem, auxint=argsize, returns mem 169 {name: "CALLclosure", argLength: 3, reg: callClosure, aux: "Int64", call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem 170 {name: "CALLdefer", argLength: 1, reg: call, aux: "Int64", call: true}, // call deferproc. arg0=mem, auxint=argsize, returns mem 171 {name: "CALLgo", argLength: 1, reg: call, aux: "Int64", call: true}, // call newproc. arg0=mem, auxint=argsize, returns mem 172 {name: "CALLinter", argLength: 2, reg: callInter, aux: "Int64", call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem 173 174 // Generic moves and zeros 175 176 // general unaligned zeroing 177 // arg0 = address of memory to zero (in T0, changed as side effect) 178 // arg1 = address of the last element to zero 179 // arg2 = mem 180 // auxint = alignment 181 // returns mem 182 // mov ZERO, (T0) 183 // ADD $sz, T0 184 // BNE Rarg1, T0, -2(PC) 185 { 186 name: "LoweredZero", 187 aux: "Int64", 188 argLength: 3, 189 reg: regInfo{ 190 inputs: []regMask{regNamed["T0"], gpMask}, 191 clobbers: regNamed["T0"], 192 }, 193 typ: "Mem", 194 faultOnNilArg0: true, 195 }, 196 197 // general unaligned move 198 // arg0 = address of dst memory (in T0, changed as side effect) 199 // arg1 = address of src memory (in T1, changed as side effect) 200 // arg2 = address of the last element of src (can't be T2 as we clobber it before using arg2) 201 // arg3 = mem 202 // auxint = alignment 203 // clobbers T2 as a tmp register. 204 // returns mem 205 // mov (T1), T2 206 // mov T2, (T0) 207 // ADD $sz, T0 208 // ADD $sz, T1 209 // BNE Rarg2, T0, -4(PC) 210 { 211 name: "LoweredMove", 212 aux: "Int64", 213 argLength: 4, 214 reg: regInfo{ 215 inputs: []regMask{regNamed["T0"], regNamed["T1"], gpMask &^ regNamed["T2"]}, 216 clobbers: regNamed["T0"] | regNamed["T1"] | regNamed["T2"], 217 }, 218 typ: "Mem", 219 faultOnNilArg0: true, 220 faultOnNilArg1: true, 221 }, 222 223 // Lowering pass-throughs 224 {name: "LoweredNilCheck", argLength: 2, faultOnNilArg0: true, nilCheck: true, reg: regInfo{inputs: []regMask{gpspMask}}}, // arg0=ptr,arg1=mem, returns void. Faults if ptr is nil. 225 {name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{regCtxt}}}, // scheduler ensures only at beginning of entry block 226 227 // F extension. 228 {name: "FADDS", argLength: 2, reg: fp21, asm: "FADDS", commutative: true, typ: "Float32"}, // arg0 + arg1 229 {name: "FSUBS", argLength: 2, reg: fp21, asm: "FSUBS", commutative: false, typ: "Float32"}, // arg0 - arg1 230 {name: "FMULS", argLength: 2, reg: fp21, asm: "FMULS", commutative: true, typ: "Float32"}, // arg0 * arg1 231 {name: "FDIVS", argLength: 2, reg: fp21, asm: "FDIVS", commutative: false, typ: "Float32"}, // arg0 / arg1 232 {name: "FSQRTS", argLength: 1, reg: fp11, asm: "FSQRTS", typ: "Float32"}, // sqrt(arg0) 233 {name: "FNEGS", argLength: 1, reg: fp11, asm: "FNEGS", typ: "Float32"}, // -arg0 234 {name: "FMVSX", argLength: 1, reg: gpfp, asm: "FMVSX", typ: "Float32"}, // reinterpret arg0 as float 235 {name: "FCVTSW", argLength: 1, reg: gpfp, asm: "FCVTSW", typ: "Float32"}, // float32(arg0) 236 {name: "FCVTSL", argLength: 1, reg: gpfp, asm: "FCVTSL", typ: "Float32"}, // float32(arg0) 237 {name: "FCVTWS", argLength: 1, reg: fpgp, asm: "FCVTWS", typ: "Int32"}, // int32(arg0) 238 {name: "FCVTLS", argLength: 1, reg: fpgp, asm: "FCVTLS", typ: "Int64"}, // int64(arg0) 239 {name: "FMOVWload", argLength: 2, reg: fpload, asm: "MOVF", aux: "SymOff", typ: "Float32", faultOnNilArg0: true}, // load float32 from arg0+auxint+aux 240 {name: "FMOVWstore", argLength: 3, reg: fpstore, asm: "MOVF", aux: "SymOff", typ: "Mem", faultOnNilArg0: true}, // store float32 to arg0+auxint+aux 241 {name: "FEQS", argLength: 2, reg: fp2gp, asm: "FEQS", commutative: true}, // arg0 == arg1 242 {name: "FNES", argLength: 2, reg: fp2gp, asm: "FNES", commutative: true}, // arg0 != arg1 243 {name: "FLTS", argLength: 2, reg: fp2gp, asm: "FLTS"}, // arg0 < arg1 244 {name: "FLES", argLength: 2, reg: fp2gp, asm: "FLES"}, // arg0 <= arg1 245 246 // D extension. 247 {name: "FADDD", argLength: 2, reg: fp21, asm: "FADDD", commutative: true, typ: "Float64"}, // arg0 + arg1 248 {name: "FSUBD", argLength: 2, reg: fp21, asm: "FSUBD", commutative: false, typ: "Float64"}, // arg0 - arg1 249 {name: "FMULD", argLength: 2, reg: fp21, asm: "FMULD", commutative: true, typ: "Float64"}, // arg0 * arg1 250 {name: "FDIVD", argLength: 2, reg: fp21, asm: "FDIVD", commutative: false, typ: "Float64"}, // arg0 / arg1 251 {name: "FSQRTD", argLength: 1, reg: fp11, asm: "FSQRTD", typ: "Float64"}, // sqrt(arg0) 252 {name: "FNEGD", argLength: 1, reg: fp11, asm: "FNEGD", typ: "Float64"}, // -arg0 253 {name: "FMVDX", argLength: 1, reg: gpfp, asm: "FMVDX", typ: "Float64"}, // reinterpret arg0 as float 254 {name: "FCVTDW", argLength: 1, reg: gpfp, asm: "FCVTDW", typ: "Float64"}, // float64(arg0) 255 {name: "FCVTDL", argLength: 1, reg: gpfp, asm: "FCVTDL", typ: "Float64"}, // float64(arg0) 256 {name: "FCVTWD", argLength: 1, reg: fpgp, asm: "FCVTWD", typ: "Int32"}, // int32(arg0) 257 {name: "FCVTLD", argLength: 1, reg: fpgp, asm: "FCVTLD", typ: "Int64"}, // int64(arg0) 258 {name: "FCVTDS", argLength: 1, reg: fp11, asm: "FCVTDS", typ: "Float64"}, // float64(arg0) 259 {name: "FCVTSD", argLength: 1, reg: fp11, asm: "FCVTSD", typ: "Float32"}, // float32(arg0) 260 {name: "FMOVDload", argLength: 2, reg: fpload, asm: "MOVD", aux: "SymOff", typ: "Float64", faultOnNilArg0: true}, // load float64 from arg0+auxint+aux 261 {name: "FMOVDstore", argLength: 3, reg: fpstore, asm: "MOVD", aux: "SymOff", typ: "Mem", faultOnNilArg0: true}, // store float6 to arg0+auxint+aux 262 {name: "FEQD", argLength: 2, reg: fp2gp, asm: "FEQD", commutative: true}, // arg0 == arg1 263 {name: "FNED", argLength: 2, reg: fp2gp, asm: "FNED", commutative: true}, // arg0 != arg1 264 {name: "FLTD", argLength: 2, reg: fp2gp, asm: "FLTD"}, // arg0 < arg1 265 {name: "FLED", argLength: 2, reg: fp2gp, asm: "FLED"}, // arg0 <= arg1 266 } 267 268 RISCVblocks := []blockData{ 269 {name: "BNE"}, // Control != 0 (take a register) 270 } 271 272 archs = append(archs, arch{ 273 name: "RISCV", 274 pkg: "cmd/internal/obj/riscv", 275 genfile: "../../riscv/ssa.go", 276 ops: RISCVops, 277 blocks: RISCVblocks, 278 regnames: regNamesRISCV, 279 gpregmask: gpMask, 280 fpregmask: fpMask, 281 framepointerreg: -1, // not used 282 }) 283 }