golang.org/x/arch@v0.17.0/s390x/s390xmap/map.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 // s390xmap constructs the s390x opcode map from the instruction set CSV file. 6 // 7 // Usage: 8 // 9 // s390map [-fmt=format] s390x.csv 10 // 11 // The known output formats are: 12 // 13 // text (default) - print decoding tree in text form 14 // decoder - print decoding tables for the s390xasm package 15 // encoder - generate a self-contained file which can be used to encode 16 // go obj.Progs into machine code 17 // asm - generate a GNU asm file which can be compiled by gcc containing 18 // all opcodes discovered in s390x.csv using macro friendly arguments. 19 package main 20 21 import ( 22 "bytes" 23 "encoding/csv" 24 "flag" 25 "fmt" 26 gofmt "go/format" 27 "log" 28 "os" 29 "regexp" 30 "strconv" 31 "strings" 32 33 asm "golang.org/x/arch/s390x/s390xasm" 34 ) 35 36 var format = flag.String("fmt", "text", "output format: text, decoder, asm") 37 var debug = flag.Bool("debug", false, "enable debugging output") 38 39 var inputFile string 40 41 func usage() { 42 fmt.Fprintf(os.Stderr, "usage: s390xmap [-fmt=format] s390x.csv\n") 43 os.Exit(2) 44 } 45 46 func main() { 47 log.SetFlags(0) 48 log.SetPrefix("s390xmap: ") 49 50 flag.Usage = usage 51 flag.Parse() 52 if flag.NArg() != 1 { 53 usage() 54 } 55 56 inputFile = flag.Arg(0) 57 58 var printTyp func(*Prog) 59 switch *format { 60 default: 61 log.Fatalf("unknown output format %q", *format) 62 case "text": 63 printTyp = printText 64 case "decoder": 65 printTyp = printDecoder 66 case "asm": 67 printTyp = printASM 68 case "encoder": 69 printTyp = printEncoder 70 } 71 72 p, err := readCSV(flag.Arg(0)) 73 if err != nil { 74 log.Fatal(err) 75 } 76 log.Printf("Parsed %d instruction forms.", len(p.Insts)) 77 printTyp(p) 78 } 79 80 // readCSV reads the CSV file and returns the corresponding Prog. 81 // It may print details about problems to standard error using the log package. 82 func readCSV(file string) (*Prog, error) { 83 // Read input. 84 // Skip leading blank and # comment lines. 85 f, err := os.Open(file) 86 if err != nil { 87 return nil, err 88 } 89 csvReader := csv.NewReader(f) 90 csvReader.Comment = '#' 91 table, err := csvReader.ReadAll() 92 if err != nil { 93 return nil, fmt.Errorf("parsing %s: %v", file, err) 94 } 95 if len(table) == 0 { 96 return nil, fmt.Errorf("empty csv input") 97 } 98 if len(table[0]) < 3 { 99 return nil, fmt.Errorf("csv too narrow: need at least four columns") 100 } 101 102 p := &Prog{} 103 for _, row := range table { 104 add(p, row[0], row[1], row[2], row[3]) 105 } 106 return p, nil 107 } 108 109 type Prog struct { 110 Insts []Inst 111 OpRanges map[string]string 112 nextOrder int // Next position value (used for Insts[x].order) 113 } 114 115 type Field struct { 116 Name string 117 BitField asm.BitField 118 Type asm.ArgType 119 flags uint16 120 } 121 122 func (f Field) String() string { 123 return fmt.Sprintf("%v(%s%v)", f.Type, f.Name, f.BitField) 124 } 125 126 type Inst struct { 127 Text string 128 Encoding string 129 Op string 130 Mask uint64 131 Value uint64 132 DontCare uint64 133 Len uint16 134 Fields []Field 135 } 136 137 func (i Inst) String() string { 138 return fmt.Sprintf("%s (%s) %08x/%08x %v (%s)", i.Op, i.Encoding, i.Value, i.Mask, i.Fields, i.Text) 139 } 140 141 type Arg struct { 142 Name string 143 Bits int8 144 Offs int8 145 } 146 147 func (a Arg) String() string { 148 return fmt.Sprintf("%s[%d:%d]", a.Name, a.Offs, a.Offs+a.Bits-1) 149 } 150 151 func (a Arg) Maximum() int { 152 return 1<<uint8(a.Bits) - 1 153 } 154 155 func (a Arg) BitMask() uint64 { 156 return uint64(a.Maximum()) << a.Shift() 157 } 158 159 func (a Arg) Shift() uint8 { 160 return uint8(64 - a.Offs - a.Bits) 161 } 162 163 type Args []Arg 164 165 func (as Args) String() string { 166 ss := make([]string, len(as)) 167 for i := range as { 168 ss[i] = as[i].String() 169 } 170 return strings.Join(ss, "|") 171 } 172 173 func (as Args) Find(name string) int { 174 for i := range as { 175 if as[i].Name == name { 176 return i 177 } 178 } 179 return -1 180 } 181 182 func (as *Args) Append(a Arg) { 183 *as = append(*as, a) 184 } 185 186 func (as *Args) Delete(i int) { 187 *as = append((*as)[:i], (*as)[i+1:]...) 188 } 189 190 func (as Args) Clone() Args { 191 return append(Args{}, as...) 192 } 193 194 func (a Arg) isDontCare() bool { 195 return a.Name[0] == '/' && a.Name == strings.Repeat("/", len(a.Name)) 196 } 197 198 // Split the string encoding into an Args. The encoding string loosely matches the regex 199 // (arg@bitpos|)+ 200 func parseFields(encoding, text string) Args { 201 var err error 202 var args Args 203 204 fields := strings.Split(encoding, "|") 205 206 for i, f := range fields { 207 name, off := "", -1 208 if f == "" { 209 off = 64 210 if i == 0 || i != len(fields)-1 { 211 fmt.Fprintf(os.Stderr, "%s: wrong %d-th encoding field: %q\n", text, i, f) 212 panic("Invalid encoding entry.") 213 } 214 } else { 215 j := strings.Index(f, "@") 216 if j < 0 { 217 fmt.Fprintf(os.Stderr, "%s: wrong %d-th encoding field: %q\n", text, i, f) 218 panic("Invalid encoding entry.") 219 } 220 off, err = strconv.Atoi(f[j+1:]) 221 if err != nil { 222 fmt.Fprintf(os.Stderr, "err for: %s has: %s for %s\n", f[:j], err, f[j+1:]) 223 } 224 name = f[:j] 225 } 226 if len(args) > 0 { 227 args[len(args)-1].Bits += int8(off) 228 } 229 if name != "" && name != "??" { 230 arg := Arg{Name: name, Offs: int8(off), Bits: int8(-off)} 231 args.Append(arg) 232 } 233 } 234 return args 235 } 236 237 // Compute the Mask (usually Opcode + secondary Opcode bitfields), 238 // the Value (the expected value under the mask), and 239 // reserved bits (i.e the // fields which should be set to 0) 240 func computeMaskValueReserved(args Args, text string) (mask, value, reserved uint64) { 241 for i := 0; i < len(args); i++ { 242 arg := args[i] 243 v, err := strconv.Atoi(arg.Name) 244 switch { 245 case err == nil && v >= 0: // is a numbered field 246 if v < 0 || v > arg.Maximum() { 247 fmt.Fprintf(os.Stderr, "%s: field %s value (%d) is out of range (%d-bit)\n", text, arg, v, arg.Bits) 248 } 249 mask |= arg.BitMask() 250 value |= uint64(v) << arg.Shift() 251 args.Delete(i) 252 i-- 253 case arg.Name[0] == '/': // don't care 254 if arg.Name != strings.Repeat("/", len(arg.Name)) { 255 log.Fatalf("%s: arg %v named like a don't care bit, but it's not", text, arg) 256 } 257 reserved |= arg.BitMask() 258 args.Delete(i) 259 i-- 260 default: 261 continue 262 } 263 } 264 // sanity checks 265 if mask&reserved != 0 { 266 log.Fatalf("%s: mask (%08x) and don't care (%08x) collide", text, mask, reserved) 267 } 268 if value&^mask != 0 { 269 log.Fatalf("%s: value (%08x) out of range of mask (%08x)", text, value, mask) 270 } 271 return 272 } 273 274 func Imm_signed_8bit_check(op string) bool { 275 imm_8 := []string{"ASI", "AGSI", "ALSI", "ALGSI", "CIB", "CGIB", "CIJ", "CGIJ", "NI", "NIY", "OI", "OIY", "XI", "XIY"} 276 var ret bool 277 ret = false 278 for _, str := range imm_8 { 279 if strings.Compare(op, str) == 0 { 280 ret = true 281 break 282 } 283 } 284 return ret 285 } 286 287 func Imm_signed_16bit_check(op string) bool { 288 imm_16 := []string{"AHI", "AGHI", "ALHSIK", "ALGHSIK", "AHIK", "AGHIK", "LHI", "LGHI", "MVGHI", "CIT", "CGIT", "CGHI", "CGHSI", "CHHSI", "CHI", "CHSI", "CRJ", "CGRJ", "NIHH", "NILL", "NIHL", "NILH", "LLIHH", "LLILL", "LLIHL", "LLILH", "OIHH", "OILL", "OIHL", "OILH", "VLEIB", "VLEIH", "VLEIF", "VLEIG"} 289 var ret bool 290 ret = false 291 for _, str := range imm_16 { 292 if strings.Compare(op, str) == 0 { 293 ret = true 294 break 295 } 296 } 297 return ret 298 } 299 300 func Imm_signed_32bit_check(op string) bool { 301 imm_32 := []string{"AFI", "AGFI", "AIH", "CIH", "CFI", "CGFI", "CRL", "STRL", "STGRL", "LGFI", "LLIHF", "LLILF", "MSFI", "MSGFI", "MGHI", "MHI", "NIHF", "NILF", "OILF", "OIHF", "XILF", "XIHF"} 302 var ret bool 303 ret = false 304 for _, str := range imm_32 { 305 if strings.Compare(op, str) == 0 { 306 ret = true 307 break 308 } 309 } 310 return ret 311 } 312 313 func check_flags(flags string) bool { 314 if strings.Contains(flags, "Da") { 315 return true 316 } else if strings.Contains(flags, "Db") { 317 return true 318 } else if strings.Contains(flags, "Dt") { 319 return true 320 } else { 321 return false 322 } 323 } 324 325 // Parse a row from the CSV describing the instructions, and place the 326 // detected instructions into p. One entry may generate multiple intruction 327 // entries as each extended mnemonic listed in text is treated like a unique 328 // instruction. 329 func add(p *Prog, text, mnemonics, encoding, flags string) { 330 // Parse encoding, building size and offset of each field. 331 // The first field in the encoding is the smallest offset. 332 // And note the MSB is bit 0, not bit 31. 333 // Example: "31@0|RS@6|RA@11|///@16|26@21|Rc@31|" 334 var args Args 335 336 args = parseFields(encoding, text) 337 mask, value, dontCare := computeMaskValueReserved(args, text) 338 339 // split mnemonics into individual instructions 340 inst := Inst{Text: text, Encoding: mnemonics, Value: value, Mask: mask, DontCare: dontCare} 341 342 // order inst.Args according to mnemonics order 343 for i, opr := range operandRe.FindAllString(mnemonics, -1) { 344 if i == 0 { // operation 345 inst.Op = opr 346 continue 347 } 348 field := Field{Name: opr} 349 typ := asm.TypeUnknown 350 flag := uint16(0) 351 switch opr { 352 case "R1", "R2", "R3": 353 s := strings.Split(mnemonics, " ") 354 switch opr { 355 case "R1": 356 switch s[0] { 357 case "CPDT", "CPXT", "CDXT", "CZXT", "CZDT": 358 typ = asm.TypeFPReg 359 flag = 0x2 360 case "CUXTR", "EEXTR", "EEDTR", "EFPC", "ESXTR", "ESDTR", "LGDR", "SFPC", "SFASR": 361 typ = asm.TypeReg 362 flag = 0x1 363 case "CPYA", "LAM", "LAMY", "STAM", "STAMY", "SAR", "TAR": 364 typ = asm.TypeACReg 365 flag = 0x3 366 case "LCTL", "LCTLG", "STCTL", "STCTG": 367 typ = asm.TypeCReg 368 flag = 0x4 369 default: 370 if check_flags(flags) { 371 if strings.Contains(text, "CONVERT TO") { 372 typ = asm.TypeReg 373 flag = 0x1 374 } else { 375 typ = asm.TypeFPReg 376 flag = 0x2 377 } 378 } else { 379 typ = asm.TypeReg 380 flag = 0x1 381 } 382 } 383 case "R2": 384 switch s[0] { 385 case "IEXTR", "IEDTR", "LDGR", "RRXTR", "RRDTR": 386 typ = asm.TypeReg 387 flag = 0x1 388 case "CPYA", "EAR": 389 typ = asm.TypeACReg 390 flag = 0x3 391 default: 392 if check_flags(flags) { 393 if strings.Contains(text, "CONVERT FROM") { 394 typ = asm.TypeReg 395 flag = 0x1 396 } else { 397 typ = asm.TypeFPReg 398 flag = 0x2 399 } 400 } else { 401 typ = asm.TypeReg 402 flag = 0x1 403 } 404 } 405 case "R3": 406 switch s[0] { 407 case "LAM", "LAMY", "STAM", "STAMY": 408 typ = asm.TypeACReg 409 flag = 0x3 410 case "LCTL", "LCTLG", "STCTL", "STCTG": 411 typ = asm.TypeCReg 412 flag = 0x4 413 default: 414 if check_flags(flags) { 415 typ = asm.TypeFPReg 416 flag = 0x2 417 } else { 418 typ = asm.TypeReg 419 flag = 0x1 420 } 421 } 422 } 423 424 case "I", "I1", "I2", "I3", "I4", "I5": 425 flag = 0x0 426 switch opr { 427 case "I", "I1": 428 typ = asm.TypeImmUnsigned 429 430 case "I2": 431 if Imm_signed_8bit_check(inst.Op) { 432 typ = asm.TypeImmSigned8 433 break 434 } else if Imm_signed_16bit_check(inst.Op) { // "ASI", "AGSI", "ALSI", "ALGSI" 435 typ = asm.TypeImmSigned16 436 break 437 } else if Imm_signed_32bit_check(inst.Op) { // "AHI", "AGHI", "AHIK", "AGHIK", "LHI", "LGHI" 438 typ = asm.TypeImmSigned32 439 break 440 } else { 441 typ = asm.TypeImmUnsigned 442 break 443 } 444 445 case "I3", "I4", "I5": 446 typ = asm.TypeImmUnsigned 447 448 } 449 450 case "RI2", "RI3", "RI4": 451 flag = 0x80 452 i := args.Find(opr) 453 count := uint8(args[i].Bits) 454 if count == 12 { 455 typ = asm.TypeRegImSigned12 456 break 457 } else if count == 16 { 458 typ = asm.TypeRegImSigned16 459 break 460 } else if count == 24 { 461 typ = asm.TypeRegImSigned24 462 break 463 } else if count == 32 { 464 typ = asm.TypeRegImSigned32 465 break 466 } 467 468 case "M1", "M3", "M4", "M5", "M6": 469 flag = 0x800 470 typ = asm.TypeMask 471 472 case "B1", "B2", "B3", "B4": 473 typ = asm.TypeBaseReg 474 flag = 0x20 | 0x01 475 476 case "X2": 477 typ = asm.TypeIndexReg 478 flag = 0x40 | 0x01 479 480 case "D1", "D2", "D3", "D4": 481 flag = 0x10 482 i := args.Find(opr) 483 if uint8(args[i].Bits) == 20 { 484 typ = asm.TypeDispSigned20 485 break 486 } else { 487 typ = asm.TypeDispUnsigned 488 break 489 } 490 491 case "L1", "L2": 492 typ = asm.TypeLen 493 flag = 0x10 494 case "V1", "V2", "V3", "V4", "V5", "V6": 495 typ = asm.TypeVecReg 496 flag = 0x08 497 } 498 499 if typ == asm.TypeUnknown { 500 log.Fatalf("%s %s unknown type for opr %s", text, inst, opr) 501 } 502 field.Type = typ 503 field.flags = flag 504 var f1 asm.BitField 505 i := args.Find(opr) 506 if i < 0 { 507 log.Fatalf("%s: couldn't find %s in %s", text, opr, args) 508 } 509 f1.Offs, f1.Bits = uint8(args[i].Offs), uint8(args[i].Bits) 510 field.BitField = f1 511 inst.Fields = append(inst.Fields, field) 512 } 513 if strings.HasPrefix(inst.Op, "V") || strings.Contains(inst.Op, "WFC") || strings.Contains(inst.Op, "WFK") { //Check Vector Instructions 514 Bits := asm.BitField{Offs: 36, Bits: 4} 515 field := Field{Name: "RXB", BitField: Bits, Type: asm.TypeImmUnsigned, flags: 0xC00} 516 inst.Fields = append(inst.Fields, field) 517 } 518 if *debug { 519 fmt.Printf("%v\n", inst) 520 } 521 p.Insts = append(p.Insts, inst) 522 } 523 524 // operandRe matches each operand (including opcode) in instruction mnemonics 525 var operandRe = regexp.MustCompile(`([[:alpha:]][[:alnum:]_]*\.?)`) 526 527 // printText implements the -fmt=text mode, which is not implemented (yet?). 528 func printText(p *Prog) { 529 log.Fatal("-fmt=text not implemented") 530 } 531 532 // printEncoder implements the -fmt=encoder mode. which is not implemented (yet?). 533 func printEncoder(p *Prog) { 534 log.Fatal("-fmt=encoder not implemented") 535 } 536 537 func printASM(p *Prog) { 538 fmt.Printf("#include \"hack.h\"\n") 539 fmt.Printf(".text\n") 540 for _, inst := range p.Insts { 541 fmt.Printf("\t%s\n", inst.Encoding) 542 } 543 } 544 545 // argFieldName constructs a name for the argField 546 func argFieldName(f Field) string { 547 ns := []string{"ap", f.Type.String()} 548 b := f.BitField 549 ns = append(ns, fmt.Sprintf("%d_%d", b.Offs, b.Offs+b.Bits-1)) 550 return strings.Join(ns, "_") 551 } 552 553 // printDecoder implements the -fmt=decoder mode. 554 // It emits the tables.go for package armasm's decoder. 555 func printDecoder(p *Prog) { 556 var buf bytes.Buffer 557 558 fmt.Fprintf(&buf, "// Code generated by s390xmap -fmt=decoder %s DO NOT EDIT.\n", inputFile) 559 fmt.Fprintf(&buf, "\n") 560 561 fmt.Fprintf(&buf, "package s390xasm\n\n") 562 563 // Build list of opcodes, using the csv order (which corresponds to ISA docs order) 564 m := map[string]bool{} 565 fmt.Fprintf(&buf, "const (\n\t_ Op = iota\n") 566 for i := 0; i < len(p.Insts); i++ { 567 name := p.Insts[i].Op 568 switch name { 569 case "CUUTF", "CUTFU", "PPNO": 570 m[name] = false 571 p.Insts = append(p.Insts[:i], p.Insts[i+1:]...) 572 i-- 573 default: 574 m[name] = true 575 } 576 if ok := m[name]; !ok { 577 continue 578 } 579 fmt.Fprintf(&buf, "\t%s\n", name) 580 } 581 fmt.Fprint(&buf, ")\n\n\n") 582 583 // Emit slice mapping opcode number to name string. 584 m = map[string]bool{} 585 fmt.Fprintf(&buf, "var opstr = [...]string{\n") 586 for _, inst := range p.Insts { 587 name := inst.Op 588 if ok := m[name]; ok { 589 continue 590 } 591 m[name] = true 592 fmt.Fprintf(&buf, "\t%s: %q,\n", inst.Op, strings.ToLower(inst.Op)) 593 } 594 fmt.Fprint(&buf, "}\n\n\n") 595 596 // print out argFields 597 fmt.Fprintf(&buf, "var (\n") 598 m = map[string]bool{} 599 for _, inst := range p.Insts { 600 for _, f := range inst.Fields { 601 name := argFieldName(f) 602 if ok := m[name]; ok { 603 continue 604 } 605 m[name] = true 606 fmt.Fprintf(&buf, "\t%s = &argField{Type: %#v, flags: %#x, BitField: BitField", name, f.Type, f.flags) 607 b := f.BitField 608 fmt.Fprintf(&buf, "{%d, %d }", b.Offs, b.Bits) 609 fmt.Fprintf(&buf, "}\n") 610 } 611 } 612 fmt.Fprint(&buf, ")\n\n\n") 613 614 // Emit decoding table. 615 fmt.Fprintf(&buf, "var instFormats = [...]instFormat{\n") 616 for _, inst := range p.Insts { 617 m, v, dc := inst.Mask, inst.Value, inst.DontCare 618 fmt.Fprintf(&buf, "\t{ %s, %#x, %#x, %#x,", inst.Op, m, v, dc) 619 fmt.Fprintf(&buf, " // %s (%s)\n\t\t[8]*argField{", inst.Text, inst.Encoding) 620 for _, f := range inst.Fields { 621 fmt.Fprintf(&buf, "%s, ", argFieldName(f)) 622 } 623 fmt.Fprintf(&buf, "}},\n") 624 } 625 fmt.Fprint(&buf, "}\n\n") 626 627 out, err := gofmt.Source(buf.Bytes()) 628 if err != nil { 629 log.Fatalf("gofmt error: %v", err) 630 fmt.Printf("%s", buf.Bytes()) 631 } else { 632 fmt.Printf("%s", out) 633 } 634 }