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  }