golang.org/x/arch@v0.17.0/riscv64/riscv64spec/spec.go (about) 1 // Copyright 2024 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 // riscv64spec reads the files contained in riscv-opcodes repo 6 // to collect instruction encoding details. 7 // repo url: https://github.com/riscv/riscv-opcodes 8 // usage: go run spec.go <opcodes-repo-path> 9 10 package main 11 12 import ( 13 "bufio" 14 "fmt" 15 "log" 16 "os" 17 "path/filepath" 18 "sort" 19 "strconv" 20 "strings" 21 ) 22 23 // RV64GC_zba_zbb_zbs Extensions Listing 24 // Reference: $GOROOT/src/src/cmd/internal/obj/riscv/inst.go 25 var extensions = []string{ 26 "rv_a", 27 "rv_c", 28 "rv_c_d", 29 "rv_d", 30 "rv_f", 31 "rv_i", 32 "rv_m", 33 "rv_q", 34 "rv_zba", 35 "rv_zbb", 36 "rv_zbs", 37 "rv_zfh", 38 "rv_zicsr", 39 "rv_zifencei", 40 "rv64_a", 41 "rv64_c", 42 "rv64_d", 43 "rv64_f", 44 "rv64_i", 45 "rv64_m", 46 "rv64_q", 47 "rv64_zba", 48 "rv64_zbb", 49 "rv64_zbs", 50 "rv64_zfh", 51 } 52 53 const ( 54 prologueSec = "// Code generated by riscv64spec riscv-opcodes\n// DO NOT EDIT\n\n// Copyright 2024 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage riscv64asm\n\n" 55 opSec = "const (\n\t_ Op = iota\n" 56 opstrSec = "var opstr = [...]string{\n" 57 instFormatsSec = "var instFormats = [...]instFormat{\n" 58 ) 59 60 var ( 61 ops []string 62 opstrs = make(map[string]string) 63 instFormatComments = make(map[string]string) 64 instFormats = make(map[string]string) 65 ) 66 67 func main() { 68 log.SetFlags(0) 69 log.SetPrefix("riscv64spec: ") 70 71 var repoPath string 72 if len(os.Args) < 1 { 73 log.Fatal("usage: go run spec.go <opcodes-repo-path>") 74 } 75 repoPath = os.Args[1] 76 77 fileTables, err := os.Create("tables.go") 78 if err != nil { 79 log.Fatal(err) 80 } 81 82 buf := bufio.NewWriter(fileTables) 83 _, err = buf.Write([]byte(prologueSec)) 84 if err != nil { 85 log.Fatal(err) 86 } 87 88 for _, ext := range extensions { 89 f, err := os.Open(filepath.Join(repoPath, ext)) 90 if err != nil { 91 log.Fatal(err) 92 } 93 defer f.Close() 94 95 buf := bufio.NewScanner(f) 96 for buf.Scan() { 97 line := buf.Text() 98 if len(line) == 0 { 99 continue 100 } 101 words := strings.Fields(line) 102 if len(words) == 0 || words[0][0] == '#' { 103 continue 104 } 105 106 // skip $pseudo_op except rv_zbb/rv64_zbb 107 if words[0][0] == '$' { 108 if ext != "rv_zbb" && ext != "rv64_zbb" { 109 continue 110 } 111 words = words[2:] 112 } 113 114 genInst(words) 115 } 116 } 117 118 // c.unimp wasn't in riscv-opcodes, so add it there 119 c_unimp := "c.unimp 15..0=0" 120 genInst(strings.Fields(c_unimp)) 121 122 sort.Strings(ops) 123 124 // 1. write op 125 if _, err := buf.Write([]byte(opSec)); err != nil { 126 log.Fatal(err) 127 } 128 for _, op := range ops { 129 if _, err := fmt.Fprintf(buf, "\t%s\n", op); err != nil { 130 log.Fatal(err) 131 } 132 } 133 if _, err := buf.Write([]byte(")\n\n")); err != nil { 134 log.Fatal(err) 135 } 136 137 // 2. write opstr 138 if _, err := buf.Write([]byte(opstrSec)); err != nil { 139 log.Fatal(err) 140 } 141 for _, op := range ops { 142 if _, err := fmt.Fprintf(buf, "\t%s\n", opstrs[op]); err != nil { 143 log.Fatal(err) 144 } 145 } 146 if _, err := buf.Write([]byte("}\n\n")); err != nil { 147 log.Fatal(err) 148 } 149 150 // 3. write instFormatComment and instFormat 151 if _, err := buf.Write([]byte(instFormatsSec)); err != nil { 152 log.Fatal(err) 153 } 154 for _, op := range ops { 155 if _, err := fmt.Fprintf(buf, "\t%s\n\t%s\n", instFormatComments[op], instFormats[op]); err != nil { 156 log.Fatal(err) 157 } 158 } 159 if _, err = buf.Write([]byte("}\n")); err != nil { 160 log.Fatal(err) 161 } 162 163 if err := buf.Flush(); err != nil { 164 log.Fatal(err) 165 } 166 167 if err := fileTables.Close(); err != nil { 168 log.Fatal(err) 169 } 170 } 171 172 func genInst(words []string) { 173 op := strings.ToUpper(strings.Replace(words[0], ".", "_", -1)) 174 opstr := fmt.Sprintf("%s:\t\"%s\",", op, strings.ToUpper(words[0])) 175 176 var value uint32 177 var mask uint32 178 var argTypeList []string 179 180 for i := 1; i < len(words); i++ { 181 if strings.Contains(words[i], "=") { 182 val := strings.Split(words[i], "=") 183 sec := strings.Split(val[0], "..") 184 if len(sec) < 2 { 185 sec[0] = val[0] 186 } 187 subval, submsk := genValueAndMask(val, sec) 188 value |= subval 189 mask |= submsk 190 } else if len(words[i]) > 0 { 191 argTypeList = append(argTypeList, words[i]) 192 } 193 } 194 195 instArgsStr := inferFormats(argTypeList, op) 196 instFormatComment := "// " + strings.Replace(op, "_", ".", -1) + " " + strings.Replace(instArgsStr, "arg_", "", -1) 197 instFormat := fmt.Sprintf("{mask: %#08x, value: %#08x, op: %s, args: argTypeList{%s}},", mask, value, op, instArgsStr) 198 199 // Handle the suffix of atomic instruction. 200 if isAtomic(op) { 201 suffix := []string{"", ".RL", ".AQ", ".AQRL"} 202 // Re-generate the opcode string, opcode value and mask. 203 for i, suf := range suffix { 204 aop := op + strings.Replace(suf, ".", "_", -1) 205 aopstr := fmt.Sprintf("%s:\t\"%s\",", aop, strings.ToUpper(words[0])+suf) 206 avalue := value | (uint32(i) << 25) 207 amask := mask | 0x06000000 208 ainstFormatComment := "// " + strings.Replace(aop, "_", ".", -1) + " " + strings.Replace(instArgsStr, "arg_", "", -1) 209 ainstFormat := fmt.Sprintf("{mask: %#08x, value: %#08x, op: %s, args: argTypeList{%s}},", amask, avalue, aop, instArgsStr) 210 ops = append(ops, aop) 211 opstrs[aop] = aopstr 212 instFormats[aop] = ainstFormat 213 instFormatComments[aop] = ainstFormatComment 214 } 215 } else { 216 ops = append(ops, op) 217 opstrs[op] = opstr 218 instFormats[op] = instFormat 219 instFormatComments[op] = instFormatComment 220 } 221 } 222 223 // inferFormats identifies inst format: 224 // R-Type (inst rd, rs1, rs2), 225 // I-Type (inst rd, rs1, imm / inst rd, offset(rs1)), 226 // UJ-Type (inst rd, imm), 227 // U-Type (inst rd, imm), 228 // SB-Type (inst rs1, rs2, offset) 229 // S-Type (inst rs2, offset(rs1)) 230 func inferFormats(argTypeList []string, op string) string { 231 switch { 232 case strings.Contains(op, "AMO") || strings.Contains(op, "SC_"): 233 return "arg_rd, arg_rs2, arg_rs1_amo" 234 235 case strings.Contains(op, "LR_"): 236 return "arg_rd, arg_rs1_amo" 237 238 case op == "LB" || op == "LBU" || op == "LD" || 239 op == "LH" || op == "LHU" || op == "LW" || op == "LWU": 240 return "arg_rd, arg_rs1_mem" 241 242 case op == "FLD" || op == "FLW" || op == "FLH" || op == "FLQ": 243 return "arg_fd, arg_rs1_mem" 244 245 case op == "FSD" || op == "FSW" || op == "FSH" || op == "FSQ": 246 return "arg_fs2, arg_rs1_store" 247 248 case op == "SD" || op == "SB" || op == "SW" || op == "SH": 249 return "arg_rs2, arg_rs1_store" 250 251 case op == "CSRRW" || op == "CSRRS" || op == "CSRRC": 252 return "arg_rd, arg_csr, arg_rs1" 253 254 case op == "CSRRWI" || op == "CSRRSI" || op == "CSRRCI": 255 return "arg_rd, arg_csr, arg_zimm" 256 257 case op == "JALR": 258 return "arg_rd, arg_rs1_mem" 259 260 case op == "FENCE_I": 261 return "" 262 263 case op == "FENCE": 264 return "arg_pred, arg_succ" 265 266 default: 267 var instStr []string 268 for _, arg := range argTypeList { 269 if decodeArgs(arg, op) != "" { 270 instStr = append(instStr, decodeArgs(arg, op)) 271 } 272 } 273 return strings.Join(instStr, ", ") 274 } 275 } 276 277 // decodeArgs turns the args into formats defined in arg.go 278 func decodeArgs(arg string, op string) string { 279 switch { 280 case strings.Contains("arg_rd", arg): 281 if isFloatReg(op, "rd") || strings.Contains(op, "C_FLDSP") { 282 return "arg_fd" 283 } 284 return "arg_rd" 285 286 case strings.Contains("arg_rs1", arg): 287 if isFloatReg(op, "rs") { 288 return "arg_fs1" 289 } 290 return "arg_rs1" 291 292 case strings.Contains("arg_rs2", arg): 293 if isFloatReg(op, "rs") { 294 return "arg_fs2" 295 } 296 return "arg_rs2" 297 298 case strings.Contains("arg_rs3", arg): 299 if isFloatReg(op, "rs") { 300 return "arg_fs3" 301 } 302 return "arg_rs3" 303 304 case arg == "imm12": 305 return "arg_imm12" 306 307 case arg == "imm20": 308 return "arg_imm20" 309 310 case arg == "jimm20": 311 return "arg_jimm20" 312 313 case arg == "bimm12lo": 314 return "arg_bimm12" 315 316 case arg == "imm12lo": 317 return "arg_simm12" 318 319 case arg == "shamtw": 320 return "arg_shamt5" 321 322 case arg == "shamtd": 323 return "arg_shamt6" 324 325 case arg == "rd_p": 326 if strings.Contains(op, "C_FLD") { 327 return "arg_fd_p" 328 } 329 return "arg_rd_p" 330 331 case arg == "rs1_p": 332 return "arg_rs1_p" 333 334 case arg == "rd_rs1_p": 335 return "arg_rd_rs1_p" 336 337 case arg == "rs2_p": 338 if strings.Contains(op, "C_FSD") { 339 return "arg_fs2_p" 340 } 341 return "arg_rs2_p" 342 343 case arg == "rd_n0": 344 return "arg_rd_n0" 345 346 case arg == "rs1_n0": 347 return "arg_rs1_n0" 348 349 case arg == "rd_rs1_n0": 350 return "arg_rd_rs1_n0" 351 352 case arg == "c_rs1_n0": 353 return "arg_c_rs1_n0" 354 355 case arg == "c_rs2_n0": 356 return "arg_c_rs2_n0" 357 358 case arg == "c_rs2": 359 if strings.Contains(op, "C_FSDSP") { 360 return "arg_c_fs2" 361 } 362 return "arg_c_rs2" 363 364 case arg == "rd_n2": 365 return "arg_rd_n2" 366 367 case arg == "c_imm6lo": 368 return "arg_c_imm6" 369 370 case arg == "c_nzimm6lo": 371 return "arg_c_nzimm6" 372 373 case arg == "c_nzuimm6lo": 374 return "arg_c_nzuimm6" 375 376 case arg == "c_uimm7lo": 377 return "arg_c_uimm7" 378 379 case arg == "c_uimm8lo": 380 return "arg_c_uimm8" 381 382 case arg == "c_uimm8sp_s": 383 return "arg_c_uimm8sp_s" 384 385 case arg == "c_uimm8splo": 386 return "arg_c_uimm8sp" 387 388 case arg == "c_uimm9sp_s": 389 return "arg_c_uimm9sp_s" 390 391 case arg == "c_uimm9splo": 392 return "arg_c_uimm9sp" 393 394 case arg == "c_bimm9lo": 395 return "arg_c_bimm9" 396 397 case arg == "c_nzimm10lo": 398 return "arg_c_nzimm10" 399 400 case arg == "c_nzuimm10": 401 return "arg_c_nzuimm10" 402 403 case arg == "c_imm12": 404 return "arg_c_imm12" 405 406 case arg == "c_nzimm18lo": 407 return "arg_c_nzimm18" 408 } 409 return "" 410 } 411 412 // genValueAndMask generates instruction value and relative mask. 413 func genValueAndMask(valStr []string, secStr []string) (uint32, uint32) { 414 var val int64 415 416 val, err := strconv.ParseInt(valStr[1], 0, 32) 417 if err != nil { 418 log.Fatal(err) 419 } 420 421 l, err := strconv.Atoi(secStr[0]) 422 if err != nil { 423 log.Fatal(err) 424 } 425 var r int 426 if len(secStr) == 1 { 427 r = l 428 } else { 429 r, err = strconv.Atoi(secStr[1]) 430 if err != nil { 431 log.Fatal(err) 432 } 433 } 434 435 subval := uint32(val << r) 436 submsk := ^uint32(0) << (31 - l) >> (31 - l + r) << r 437 return subval, submsk 438 } 439 440 // isAtomic reports whether the instruction is atomic. 441 func isAtomic(op string) bool { 442 return strings.HasPrefix(op, "AMO") || strings.HasPrefix(op, "LR_") || strings.HasPrefix(op, "SC_") 443 } 444 445 // isFloatReg reports whether the register of a floating point instruction is a floating point register. 446 func isFloatReg(op string, reg string) bool { 447 switch { 448 case strings.Contains(op, "FADD") || strings.Contains(op, "FSUB") || 449 strings.Contains(op, "FDIV") || strings.Contains(op, "FMUL") || 450 strings.Contains(op, "FMIN") || strings.Contains(op, "FMAX") || 451 strings.Contains(op, "FMADD") || strings.Contains(op, "FMSUB") || 452 strings.Contains(op, "FCVT_D_S") || strings.Contains(op, "FCVT_S_D") || 453 strings.Contains(op, "FCVT_D_Q") || strings.Contains(op, "FCVT_Q_D") || 454 strings.Contains(op, "FCVT_S_Q") || strings.Contains(op, "FCVT_Q_S") || 455 strings.Contains(op, "FCVT_H_S") || strings.Contains(op, "FCVT_S_H") || 456 strings.Contains(op, "FNM") || strings.Contains(op, "FNEG") || 457 strings.Contains(op, "FSQRT") || strings.Contains(op, "FSGNJ"): 458 return true 459 460 case strings.Contains(op, "FCLASS") || strings.Contains(op, "FCVT_L") || 461 strings.Contains(op, "FCVT_W") || strings.Contains(op, "FEQ") || 462 strings.Contains(op, "FLE") || strings.Contains(op, "FLT") || 463 strings.Contains(op, "FMV_X_H") || strings.Contains(op, "FMV_X_D") || 464 strings.Contains(op, "FMV_X_W"): 465 return reg != "rd" 466 467 case strings.Contains(op, "FCVT_D") || strings.Contains(op, "FCVT_S") || 468 strings.Contains(op, "FCVT_H") || strings.Contains(op, "FCVT_Q") || 469 strings.Contains(op, "FMV_H_X") || strings.Contains(op, "FMV_D_X") || 470 strings.Contains(op, "FMV_W_X"): 471 return reg != "rs" 472 473 default: 474 return false 475 } 476 }