s390x: add s390x disassembler support, GNU syntax

Change-Id: Idd91cc89510ce117e49db541fd68b0fa113b92fa
Reviewed-on: https://go-review.googlesource.com/c/arch/+/575675
Auto-Submit: Cherry Mui <cherryyz@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Bill O'Farrell <billotosyr@gmail.com>
Reviewed-by: Srinivas Pokala <Pokala.Srinivas@ibm.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
Vishwanatha HD 2024-07-13 19:52:41 +00:00 коммит произвёл Gopher Robot
Родитель b863392466
Коммит 9d90945922
13 изменённых файлов: 11255 добавлений и 0 удалений

1277
s390x/s390x.csv Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

2
s390x/s390xasm/Makefile Normal file
Просмотреть файл

@ -0,0 +1,2 @@
tables.go: ../s390xmap/map.go ../s390x.csv
go run ../s390xmap/map.go -fmt=decoder ../s390x.csv >_tables.go && gofmt _tables.go >tables.go && rm _tables.go

241
s390x/s390xasm/decode.go Normal file
Просмотреть файл

@ -0,0 +1,241 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package s390xasm
import (
"encoding/binary"
"fmt"
)
// instFormat is a decoding rule for one specific instruction form.
// An instruction ins matches the rule if ins&Mask == Value.
// DontCare bits are mainly used for finding the same instruction
// name differing with the number of argument fields.
// The Args are stored in the same order as the instruction manual.
type instFormat struct {
Op Op
Mask uint64
Value uint64
DontCare uint64
Args [8]*argField
}
// argField indicate how to decode an argument to an instruction.
// First parse the value from the BitFields, shift it left by Shift
// bits to get the actual numerical value.
type argField struct {
Type ArgType
flags uint16
BitField
}
// Parse parses the Arg out from the given binary instruction i.
func (a argField) Parse(i uint64) Arg {
switch a.Type {
default:
return nil
case TypeUnknown:
return nil
case TypeReg:
return R0 + Reg(a.BitField.Parse(i))
case TypeFPReg:
return F0 + Reg(a.BitField.Parse(i))
case TypeCReg:
return C0 + Reg(a.BitField.Parse(i))
case TypeACReg:
return A0 + Reg(a.BitField.Parse(i))
case TypeBaseReg:
return B0 + Base(a.BitField.Parse(i))
case TypeIndexReg:
return X0 + Index(a.BitField.Parse(i))
case TypeDispUnsigned:
return Disp12(a.BitField.Parse(i))
case TypeDispSigned20:
return Disp20(a.BitField.ParseSigned(i))
case TypeVecReg:
m := i >> 24 // Handling RXB field(bits 36 to 39)
if ((m>>3)&0x1 == 1) && (a.BitField.Offs == 8) {
return V0 + VReg(a.BitField.Parse(i)) + VReg(16)
} else if ((m>>2)&0x1 == 1) && (a.BitField.Offs == 12) {
return V0 + VReg(a.BitField.Parse(i)) + VReg(16)
} else if ((m>>1)&0x1 == 1) && (a.BitField.Offs == 16) {
return V0 + VReg(a.BitField.Parse(i)) + VReg(16)
} else if ((m)&0x1 == 1) && (a.BitField.Offs == 32) {
return V0 + VReg(a.BitField.Parse(i)) + VReg(16)
} else {
return V0 + VReg(a.BitField.Parse(i))
}
case TypeImmSigned8:
return Sign8(a.BitField.ParseSigned(i))
case TypeImmSigned16:
return Sign16(a.BitField.ParseSigned(i))
case TypeImmSigned32:
return Sign32(a.BitField.ParseSigned(i))
case TypeImmUnsigned:
return Imm(a.BitField.Parse(i))
case TypeRegImSigned12:
return RegIm12(a.BitField.ParseSigned(i))
case TypeRegImSigned16:
return RegIm16(a.BitField.ParseSigned(i))
case TypeRegImSigned24:
return RegIm24(a.BitField.ParseSigned(i))
case TypeRegImSigned32:
return RegIm32(a.BitField.ParseSigned(i))
case TypeMask:
return Mask(a.BitField.Parse(i))
case TypeLen:
return Len(a.BitField.Parse(i))
}
}
type ArgType int8
const (
TypeUnknown ArgType = iota
TypeReg // integer register
TypeFPReg // floating point register
TypeACReg // access register
TypeCReg // control register
TypeVecReg // vector register
TypeImmUnsigned // unsigned immediate/flag/mask, this is the catch-all type
TypeImmSigned8 // Signed 8-bit Immdediate
TypeImmSigned16 // Signed 16-bit Immdediate
TypeImmSigned32 // Signed 32-bit Immdediate
TypeBaseReg // Base Register for accessing memory
TypeIndexReg // Index Register
TypeDispUnsigned // Displacement 12-bit unsigned for memory address
TypeDispSigned20 // Displacement 20-bit signed for memory address
TypeRegImSigned12 // RegisterImmediate 12-bit signed data
TypeRegImSigned16 // RegisterImmediate 16-bit signed data
TypeRegImSigned24 // RegisterImmediate 24-bit signed data
TypeRegImSigned32 // RegisterImmediate 32-bit signed data
TypeMask // 4-bit Mask
TypeLen // Length of Memory Operand
TypeLast
)
func (t ArgType) String() string {
switch t {
default:
return fmt.Sprintf("ArgType(%d)", int(t))
case TypeUnknown:
return "Unknown"
case TypeReg:
return "Reg"
case TypeFPReg:
return "FPReg"
case TypeACReg:
return "ACReg"
case TypeCReg:
return "CReg"
case TypeDispUnsigned:
return "DispUnsigned"
case TypeDispSigned20:
return "DispSigned20"
case TypeBaseReg:
return "BaseReg"
case TypeIndexReg:
return "IndexReg"
case TypeVecReg:
return "VecReg"
case TypeImmSigned8:
return "ImmSigned8"
case TypeImmSigned16:
return "ImmSigned16"
case TypeImmSigned32:
return "ImmSigned32"
case TypeImmUnsigned:
return "ImmUnsigned"
case TypeRegImSigned12:
return "RegImSigned12"
case TypeRegImSigned16:
return "RegImSigned16"
case TypeRegImSigned24:
return "RegImSigned24"
case TypeRegImSigned32:
return "RegImSigned32"
case TypeMask:
return "Mask"
case TypeLen:
return "Len"
}
}
func (t ArgType) GoString() string {
s := t.String()
if t > 0 && t < TypeLast {
return "Type" + s
}
return s
}
var (
// Errors
errShort = fmt.Errorf("truncated instruction")
errUnknown = fmt.Errorf("unknown instruction")
)
var decoderCover []bool
// Decode decodes the leading bytes in src as a single instruction using
// byte order ord.
func Decode(src []byte) (inst Inst, err error) {
if len(src) < 2 {
return inst, errShort
}
if decoderCover == nil {
decoderCover = make([]bool, len(instFormats))
}
bit_check := binary.BigEndian.Uint16(src[:2])
bit_check = bit_check >> 14
l := int(0)
if (bit_check & 0x03) == 0 {
l = 2
} else if bit_check&0x03 == 3 {
l = 6
} else if (bit_check&0x01 == 1) || (bit_check&0x02 == 2) {
l = 4
}
inst.Len = l
ui_extn := uint64(0)
switch l {
case 2:
ui_extn = uint64(binary.BigEndian.Uint16(src[:inst.Len]))
inst.Enc = ui_extn
ui_extn = ui_extn << 48
case 4:
ui_extn = uint64(binary.BigEndian.Uint32(src[:inst.Len]))
inst.Enc = ui_extn
ui_extn = ui_extn << 32
case 6:
u1 := binary.BigEndian.Uint32(src[:(inst.Len - 2)])
u2 := binary.BigEndian.Uint16(src[(inst.Len - 2):inst.Len])
ui_extn = uint64(u1)<<16 | uint64(u2)
ui_extn = ui_extn << 16
inst.Enc = ui_extn
default:
return inst, errShort
}
for _, iform := range instFormats {
if ui_extn&iform.Mask != iform.Value {
continue
}
if (iform.DontCare & ^(ui_extn)) != iform.DontCare {
continue
}
for j, argfield := range iform.Args {
if argfield == nil {
break
}
inst.Args[j] = argfield.Parse(ui_extn)
}
inst.Op = iform.Op
break
}
if inst.Op == 0 && inst.Enc != 0 {
return inst, errUnknown
}
return inst, nil
}

Просмотреть файл

@ -0,0 +1,88 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package s390xasm
import (
"encoding/hex"
"io/ioutil"
"path"
"strings"
"testing"
)
func TestDecode(t *testing.T) {
files, err := ioutil.ReadDir("testdata")
if err != nil {
t.Fatal(err)
}
for _, f := range files {
if !strings.HasPrefix(f.Name(), "decode") {
continue
}
filename := path.Join("testdata", f.Name())
data, err := ioutil.ReadFile(filename)
if err != nil {
t.Fatal(err)
}
decode(data, t, filename)
}
}
// Provide a fake symbol to verify PCrel argument decoding.
func symlookup(pc uint64) (string, uint64) {
foopc := uint64(0x100000)
if pc >= foopc && pc < foopc+0x10 {
return "foo", foopc
}
return "", 0
}
func decode(data []byte, t *testing.T, filename string) {
all := string(data)
// Simulate PC based on number of instructions found in the test file.
pc := uint64(0)
for strings.Contains(all, "\t\t") {
all = strings.Replace(all, "\t\t", "\t", -1)
}
for _, line := range strings.Split(all, "\n") {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
f := strings.SplitN(line, "\t", 3)
i := strings.Index(f[0], "|")
if i < 0 {
t.Errorf("%s: parsing %q: missing | separator", filename, f[0])
continue
}
if i%2 != 0 {
t.Errorf("%s: parsing %q: misaligned | separator", filename, f[0])
}
size := i / 2
code, err := hex.DecodeString(f[0][:i] + f[0][i+1:])
if err != nil {
t.Errorf("%s: parsing %q: %v", filename, f[0], err)
continue
}
syntax, asm := f[1], f[2]
inst, err := Decode(code)
var out string
if err != nil {
out = "error: " + err.Error()
} else {
switch syntax {
case "gnu":
out = GNUSyntax(inst, pc)
default:
t.Errorf("unknown syntax %q", syntax)
continue
}
}
pc += uint64(size)
if out != asm || inst.Len != size {
t.Errorf("%s: Decode(%s) [%s] = %s want %s", filename, f[0], syntax, out, asm)
}
}
}

98
s390x/s390xasm/field.go Normal file
Просмотреть файл

@ -0,0 +1,98 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package s390xasm
import (
"fmt"
"strings"
)
// A BitField is a bit-field in a 64-bit double word.
// Bits are counted from 0 from the MSB to 63 as the LSB.
type BitField struct {
Offs uint8 // the offset of the left-most bit.
Bits uint8 // length in bits.
}
func (b BitField) String() string {
if b.Bits > 1 {
return fmt.Sprintf("[%d:%d]", b.Offs, int(b.Offs+b.Bits)-1)
} else if b.Bits == 1 {
return fmt.Sprintf("[%d]", b.Offs)
} else {
return fmt.Sprintf("[%d, len=0]", b.Offs)
}
}
// Parse extracts the bitfield b from i, and return it as an unsigned integer.
// Parse will panic if b is invalid.
func (b BitField) Parse(i uint64) uint64 {
if b.Bits > 64 || b.Bits == 0 || b.Offs > 63 || b.Offs+b.Bits > 64 {
panic(fmt.Sprintf("invalid bitfiled %v", b))
}
if b.Bits == 20 {
return ((((i >> (64 - b.Offs - b.Bits)) & ((1 << 8) - 1)) << 12) | ((i >> (64 - b.Offs - b.Bits + 8)) & 0xFFF))
} else {
return (i >> (64 - b.Offs - b.Bits)) & ((1 << b.Bits) - 1)
}
}
// ParseSigned extracts the bitfield b from i, and return it as a signed integer.
// ParseSigned will panic if b is invalid.
func (b BitField) ParseSigned(i uint64) int64 {
u := int64(b.Parse(i))
return u << (64 - b.Bits) >> (64 - b.Bits)
}
// BitFields is a series of BitFields representing a single number.
type BitFields []BitField
func (bs BitFields) String() string {
ss := make([]string, len(bs))
for i, bf := range bs {
ss[i] = bf.String()
}
return fmt.Sprintf("<%s>", strings.Join(ss, "|"))
}
func (bs *BitFields) Append(b BitField) {
*bs = append(*bs, b)
}
// parse extracts the bitfields from i, concatenate them and return the result
// as an unsigned integer and the total length of all the bitfields.
// parse will panic if any bitfield in b is invalid, but it doesn't check if
// the sequence of bitfields is reasonable.
func (bs BitFields) parse(i uint64) (u uint64, Bits uint8) {
for _, b := range bs {
u = (u << b.Bits) | uint64(b.Parse(i))
Bits += b.Bits
}
return u, Bits
}
// Parse extracts the bitfields from i, concatenate them and return the result
// as an unsigned integer. Parse will panic if any bitfield in b is invalid.
func (bs BitFields) Parse(i uint64) uint64 {
u, _ := bs.parse(i)
return u
}
// ParseSigned extracts the bitfields from i, concatenate them and return the result
// as a signed integer. Parse will panic if any bitfield in b is invalid.
func (bs BitFields) ParseSigned(i uint64) int64 {
u, l := bs.parse(i)
return int64(u) << (64 - l) >> (64 - l)
}
// Count the number of bits in the aggregate BitFields
func (bs BitFields) NumBits() int {
num := 0
for _, b := range bs {
num += int(b.Bits)
}
return num
}

1018
s390x/s390xasm/gnu.go Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

399
s390x/s390xasm/inst.go Normal file
Просмотреть файл

@ -0,0 +1,399 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package s390xasm
import (
"bytes"
"fmt"
"strings"
)
type Inst struct {
Op Op // Opcode mnemonic
Enc uint64 // Raw encoding bits (if Len == 8, this is the prefix word)
Len int // Length of encoding in bytes.
Args Args // Instruction arguments, in Power ISA manual order.
}
func (i Inst) String(pc uint64) string {
var buf bytes.Buffer
var rxb_check bool
m := i.Op.String()
if strings.HasPrefix(m, "v") || strings.Contains(m, "wfc") || strings.Contains(m, "wfk") {
rxb_check = true
}
mnemonic := HandleExtndMnemonic(&i)
buf.WriteString(fmt.Sprintf("%s", mnemonic))
for j, arg := range i.Args {
if arg == nil {
break
}
if j == 0 {
buf.WriteString(" ")
} else {
switch arg.(type) {
case VReg, Reg:
if _, ok := i.Args[j-1].(Disp12); ok {
buf.WriteString("")
} else if _, ok := i.Args[j-1].(Disp20); ok {
buf.WriteString("")
} else {
buf.WriteString(",")
}
case Base:
if _, ok := i.Args[j-1].(VReg); ok {
buf.WriteString(",")
} else if _, ok := i.Args[j-1].(Reg); ok {
buf.WriteString(",")
}
case Index, Len:
default:
buf.WriteString(",")
}
}
buf.WriteString(arg.String(pc))
if rxb_check && i.Args[j+2] == nil {
break
}
}
return buf.String()
}
// An Op is an instruction operation.
type Op uint16
func (o Op) String() string {
if int(o) >= len(opstr) || opstr[o] == "" {
return fmt.Sprintf("Op(%d)", int(o))
}
return opstr[o]
}
// An Arg is a single instruction argument.
// One of these types: Reg, Base, Index, Disp20, Disp12, Len, Mask, Sign8, Sign16, Sign32, RegIm12, RegIm16, RegIm24, RegIm32.
type Arg interface {
IsArg()
String(pc uint64) string
}
// An Args holds the instruction arguments.
// If an instruction has fewer than 6 arguments,
// the final elements in the array are nil.
type Args [8]Arg
// Base represents an 4-bit Base Register field
type Base uint8
const (
B0 Base = iota
B1
B2
B3
B4
B5
B6
B7
B8
B9
B10
B11
B12
B13
B14
B15
)
func (Base) IsArg() {}
func (r Base) String(pc uint64) string {
switch {
case B1 <= r && r <= B15:
s := "%"
return fmt.Sprintf("%sr%d)", s, int(r-B0))
case B0 == r:
return fmt.Sprintf("")
default:
return fmt.Sprintf("Base(%d)", int(r))
}
}
// Index represents an 4-bit Index Register field
type Index uint8
const (
X0 Index = iota
X1
X2
X3
X4
X5
X6
X7
X8
X9
X10
X11
X12
X13
X14
X15
)
func (Index) IsArg() {}
func (r Index) String(pc uint64) string {
switch {
case X1 <= r && r <= X15:
s := "%"
return fmt.Sprintf("%sr%d,", s, int(r-X0))
case X0 == r:
return fmt.Sprintf("")
default:
return fmt.Sprintf("Base(%d)", int(r))
}
}
// Disp20 represents an 20-bit Unsigned Displacement
type Disp20 uint32
func (Disp20) IsArg() {}
func (r Disp20) String(pc uint64) string {
if (r>>19)&0x01 == 1 {
return fmt.Sprintf("%d(", int32(r|0xfff<<20))
} else {
return fmt.Sprintf("%d(", int32(r))
}
}
// Disp12 represents an 12-bit Unsigned Displacement
type Disp12 uint16
func (Disp12) IsArg() {}
func (r Disp12) String(pc uint64) string {
return fmt.Sprintf("%d(", r)
}
// RegIm12 represents an 12-bit Register immediate number.
type RegIm12 uint16
func (RegIm12) IsArg() {}
func (r RegIm12) String(pc uint64) string {
if (r>>11)&0x01 == 1 {
return fmt.Sprintf("%#x", pc+(2*uint64(int16(r|0xf<<12))))
} else {
return fmt.Sprintf("%#x", pc+(2*uint64(int16(r))))
}
}
// RegIm16 represents an 16-bit Register immediate number.
type RegIm16 uint16
func (RegIm16) IsArg() {}
func (r RegIm16) String(pc uint64) string {
return fmt.Sprintf("%#x", pc+(2*uint64(int16(r))))
}
// RegIm24 represents an 24-bit Register immediate number.
type RegIm24 uint32
func (RegIm24) IsArg() {}
func (r RegIm24) String(pc uint64) string {
if (r>>23)&0x01 == 1 {
return fmt.Sprintf("%#x", pc+(2*uint64(int32(r|0xff<<24))))
} else {
return fmt.Sprintf("%#x", pc+(2*uint64(int32(r))))
}
}
// RegIm32 represents an 32-bit Register immediate number.
type RegIm32 uint32
func (RegIm32) IsArg() {}
func (r RegIm32) String(pc uint64) string {
return fmt.Sprintf("%#x", pc+(2*uint64(int32(r))))
}
// A Reg is a single register. The zero value means R0, not the absence of a register.
// It also includes special registers.
type Reg uint16
const (
R0 Reg = iota
R1
R2
R3
R4
R5
R6
R7
R8
R9
R10
R11
R12
R13
R14
R15
F0
F1
F2
F3
F4
F5
F6
F7
F8
F9
F10
F11
F12
F13
F14
F15
A0
A1
A2
A3
A4
A5
A6
A7
A8
A9
A10
A11
A12
A13
A14
A15
C0
C1
C2
C3
C4
C5
C6
C7
C8
C9
C10
C11
C12
C13
C14
C15
)
func (Reg) IsArg() {}
func (r Reg) String(pc uint64) string {
s := "%"
switch {
case R0 <= r && r <= R15:
return fmt.Sprintf("%sr%d", s, int(r-R0))
case F0 <= r && r <= F15:
return fmt.Sprintf("%sf%d", s, int(r-F0))
case A0 <= r && r <= A15:
return fmt.Sprintf("%sa%d", s, int(r-A0))
case C0 <= r && r <= C15:
return fmt.Sprintf("%sc%d", s, int(r-C0))
default:
return fmt.Sprintf("Reg(%d)", int(r))
}
}
// VReg is a vector register. The zero value means V0, not the absence of a register.
type VReg uint8
const (
V0 VReg = iota
V1
V2
V3
V4
V5
V6
V7
V8
V9
V10
V11
V12
V13
V14
V15
V16
V17
V18
V19
V20
V21
V22
V23
V24
V25
V26
V27
V28
V29
V30
V31
)
func (VReg) IsArg() {}
func (r VReg) String(pc uint64) string {
s := "%"
if V0 <= r && r <= V31 {
return fmt.Sprintf("%sv%d", s, int(r-V0))
} else {
return fmt.Sprintf("VReg(%d)", int(r))
}
}
// Imm represents an immediate number.
type Imm uint32
func (Imm) IsArg() {}
func (i Imm) String(pc uint64) string {
return fmt.Sprintf("%d", uint32(i))
}
// Sign8 represents an 8-bit signed immediate number.
type Sign8 int8
func (Sign8) IsArg() {}
func (i Sign8) String(pc uint64) string {
return fmt.Sprintf("%d", i)
}
// Sign16 represents an 16-bit signed immediate number.
type Sign16 int16
func (Sign16) IsArg() {}
func (i Sign16) String(pc uint64) string {
return fmt.Sprintf("%d", i)
}
// Sign32 represents an 32-bit signed immediate number.
type Sign32 int32
func (Sign32) IsArg() {}
func (i Sign32) String(pc uint64) string {
return fmt.Sprintf("%d", i)
}
// Mask represents an 4-bit mask value
type Mask uint8
func (Mask) IsArg() {}
func (i Mask) String(pc uint64) string {
return fmt.Sprintf("%d", i)
}
// Len represents an 8-bit type holds 4/8-bit Len argument
type Len uint8
func (Len) IsArg() {}
func (i Len) String(pc uint64) string {
return fmt.Sprintf("%d,", uint16(i)+1)
}

5046
s390x/s390xasm/tables.go Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

1245
s390x/s390xasm/testdata/decode_generated.txt поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

636
s390x/s390xmap/map.go Normal file
Просмотреть файл

@ -0,0 +1,636 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// s390xmap constructs the s390x opcode map from the instruction set CSV file.
//
// Usage:
//
// s390map [-fmt=format] s390x.csv
//
// The known output formats are:
//
// text (default) - print decoding tree in text form
// decoder - print decoding tables for the s390xasm package
// encoder - generate a self-contained file which can be used to encode
// go obj.Progs into machine code
// asm - generate a GNU asm file which can be compiled by gcc containing
// all opcodes discovered in s390x.csv using macro friendly arguments.
package main
import (
"bytes"
"encoding/csv"
"flag"
"fmt"
gofmt "go/format"
asm "golang.org/x/arch/s390x/s390xasm"
"log"
"os"
"regexp"
"strconv"
"strings"
)
var format = flag.String("fmt", "text", "output format: text, decoder, asm")
var debug = flag.Bool("debug", false, "enable debugging output")
var inputFile string
func usage() {
fmt.Fprintf(os.Stderr, "usage: s390xmap [-fmt=format] s390x.csv\n")
os.Exit(2)
}
func main() {
log.SetFlags(0)
log.SetPrefix("s390xmap: ")
flag.Usage = usage
flag.Parse()
if flag.NArg() != 1 {
usage()
}
inputFile = flag.Arg(0)
var printTyp func(*Prog)
switch *format {
default:
log.Fatalf("unknown output format %q", *format)
case "text":
printTyp = printText
case "decoder":
printTyp = printDecoder
case "asm":
printTyp = printASM
case "encoder":
printTyp = printEncoder
}
p, err := readCSV(flag.Arg(0))
if err != nil {
log.Fatal(err)
}
log.Printf("Parsed %d instruction forms.", len(p.Insts))
printTyp(p)
}
// readCSV reads the CSV file and returns the corresponding Prog.
// It may print details about problems to standard error using the log package.
func readCSV(file string) (*Prog, error) {
// Read input.
// Skip leading blank and # comment lines.
f, err := os.Open(file)
if err != nil {
return nil, err
}
csvReader := csv.NewReader(f)
csvReader.Comment = '#'
table, err := csvReader.ReadAll()
if err != nil {
return nil, fmt.Errorf("parsing %s: %v", file, err)
}
if len(table) == 0 {
return nil, fmt.Errorf("empty csv input")
}
if len(table[0]) < 3 {
return nil, fmt.Errorf("csv too narrow: need at least four columns")
}
p := &Prog{}
for _, row := range table {
add(p, row[0], row[1], row[2], row[3])
}
return p, nil
}
type Prog struct {
Insts []Inst
OpRanges map[string]string
nextOrder int // Next position value (used for Insts[x].order)
}
type Field struct {
Name string
BitField asm.BitField
Type asm.ArgType
flags uint16
}
func (f Field) String() string {
return fmt.Sprintf("%v(%s%v)", f.Type, f.Name, f.BitField)
}
type Inst struct {
Text string
Encoding string
Op string
Mask uint64
Value uint64
DontCare uint64
Len uint16
Fields []Field
}
func (i Inst) String() string {
return fmt.Sprintf("%s (%s) %08x/%08x %v (%s)", i.Op, i.Encoding, i.Value, i.Mask, i.Fields, i.Text)
}
type Arg struct {
Name string
Bits int8
Offs int8
}
func (a Arg) String() string {
return fmt.Sprintf("%s[%d:%d]", a.Name, a.Offs, a.Offs+a.Bits-1)
}
func (a Arg) Maximum() int {
return 1<<uint8(a.Bits) - 1
}
func (a Arg) BitMask() uint64 {
return uint64(a.Maximum()) << a.Shift()
}
func (a Arg) Shift() uint8 {
return uint8(64 - a.Offs - a.Bits)
}
type Args []Arg
func (as Args) String() string {
ss := make([]string, len(as))
for i := range as {
ss[i] = as[i].String()
}
return strings.Join(ss, "|")
}
func (as Args) Find(name string) int {
for i := range as {
if as[i].Name == name {
return i
}
}
return -1
}
func (as *Args) Append(a Arg) {
*as = append(*as, a)
}
func (as *Args) Delete(i int) {
*as = append((*as)[:i], (*as)[i+1:]...)
}
func (as Args) Clone() Args {
return append(Args{}, as...)
}
func (a Arg) isDontCare() bool {
return a.Name[0] == '/' && a.Name == strings.Repeat("/", len(a.Name))
}
// Split the string encoding into an Args. The encoding string loosely matches the regex
// (arg@bitpos|)+
func parseFields(encoding, text string) Args {
var err error
var args Args
fields := strings.Split(encoding, "|")
for i, f := range fields {
name, off := "", -1
if f == "" {
off = 64
if i == 0 || i != len(fields)-1 {
fmt.Fprintf(os.Stderr, "%s: wrong %d-th encoding field: %q\n", text, i, f)
panic("Invalid encoding entry.")
}
} else {
j := strings.Index(f, "@")
if j < 0 {
fmt.Fprintf(os.Stderr, "%s: wrong %d-th encoding field: %q\n", text, i, f)
panic("Invalid encoding entry.")
continue
}
off, err = strconv.Atoi(f[j+1:])
if err != nil {
fmt.Fprintf(os.Stderr, "err for: %s has: %s for %s\n", f[:j], err, f[j+1:])
}
name = f[:j]
}
if len(args) > 0 {
args[len(args)-1].Bits += int8(off)
}
if name != "" && name != "??" {
arg := Arg{Name: name, Offs: int8(off), Bits: int8(-off)}
args.Append(arg)
}
}
return args
}
// Compute the Mask (usually Opcode + secondary Opcode bitfields),
// the Value (the expected value under the mask), and
// reserved bits (i.e the // fields which should be set to 0)
func computeMaskValueReserved(args Args, text string) (mask, value, reserved uint64) {
for i := 0; i < len(args); i++ {
arg := args[i]
v, err := strconv.Atoi(arg.Name)
switch {
case err == nil && v >= 0: // is a numbered field
if v < 0 || v > arg.Maximum() {
fmt.Fprintf(os.Stderr, "%s: field %s value (%d) is out of range (%d-bit)\n", text, arg, v, arg.Bits)
}
mask |= arg.BitMask()
value |= uint64(v) << arg.Shift()
args.Delete(i)
i--
case arg.Name[0] == '/': // don't care
if arg.Name != strings.Repeat("/", len(arg.Name)) {
log.Fatalf("%s: arg %v named like a don't care bit, but it's not", text, arg)
}
reserved |= arg.BitMask()
args.Delete(i)
i--
default:
continue
}
}
// sanity checks
if mask&reserved != 0 {
log.Fatalf("%s: mask (%08x) and don't care (%08x) collide", text, mask, reserved)
}
if value&^mask != 0 {
log.Fatalf("%s: value (%08x) out of range of mask (%08x)", text, value, mask)
}
return
}
func Imm_signed_8bit_check(op string) bool {
imm_8 := []string{"ASI", "AGSI", "ALSI", "ALGSI", "CIB", "CGIB", "CIJ", "CGIJ"}
var ret bool
ret = false
for _, str := range imm_8 {
if strings.Compare(op, str) == 0 {
ret = true
break
}
}
return ret
}
func Imm_signed_16bit_check(op string) bool {
imm_16 := []string{"AHI", "AGHI", "ALHSIK", "ALGHSIK", "AHIK", "AGHIK", "LHI", "LGHI", "MVGHI", "CIT", "CGIT", "CGHI", "CGHSI", "CHHSI", "CHI", "CHSI", "CRJ", "CGRJ"}
var ret bool
ret = false
for _, str := range imm_16 {
if strings.Compare(op, str) == 0 {
ret = true
break
}
}
return ret
}
func Imm_signed_32bit_check(op string) bool {
imm_32 := []string{"AFI", "AGFI", "AIH", "CIH", "CFI", "CGFI", "CRL", "STRL", "STGRL"}
var ret bool
ret = false
for _, str := range imm_32 {
if strings.Compare(op, str) == 0 {
ret = true
break
}
}
return ret
}
func check_flags(flags string) bool {
if strings.Contains(flags, "Da") {
return true
} else if strings.Contains(flags, "Db") {
return true
} else if strings.Contains(flags, "Dt") {
return true
} else {
return false
}
}
// Parse a row from the CSV describing the instructions, and place the
// detected instructions into p. One entry may generate multiple intruction
// entries as each extended mnemonic listed in text is treated like a unique
// instruction.
// func add(p *Prog, text, mnemonics, encoding, format string) {
func add(p *Prog, text, mnemonics, encoding, flags string) {
// Parse encoding, building size and offset of each field.
// The first field in the encoding is the smallest offset.
// And note the MSB is bit 0, not bit 31.
// Example: "31@0|RS@6|RA@11|///@16|26@21|Rc@31|"
var args Args
args = parseFields(encoding, text)
mask, value, dontCare := computeMaskValueReserved(args, text)
// split mnemonics into individual instructions
// example: "b target_addr (AA=0 LK=0)|ba target_addr (AA=1 LK=0)|bl target_addr (AA=0 LK=1)|bla target_addr (AA=1 LK=1)"
inst := Inst{Text: text, Encoding: mnemonics, Value: value, Mask: mask, DontCare: dontCare}
// order inst.Args according to mnemonics order
for i, opr := range operandRe.FindAllString(mnemonics, -1) {
if i == 0 { // operation
inst.Op = opr
continue
}
field := Field{Name: opr}
typ := asm.TypeUnknown
flag := uint16(0)
switch opr {
case "R1", "R2", "R3":
s := strings.Split(mnemonics, " ")
switch opr {
case "R1":
switch s[0] {
case "CPDT", "CPXT", "CDXT", "CZXT", "CZDT":
typ = asm.TypeFPReg
flag = 0x2
case "CUXTR", "EEXTR", "EEDTR", "EFPC", "ESXTR", "ESDTR", "LGDR", "SFPC", "SFASR":
typ = asm.TypeReg
flag = 0x1
case "CPYA", "LAM", "LAMY", "STAM", "STAMY", "SAR", "TAR":
typ = asm.TypeACReg
flag = 0x3
case "LCTL", "LCTLG", "STCTL", "STCTG":
typ = asm.TypeCReg
flag = 0x4
default:
if check_flags(flags) {
if strings.Contains(text, "CONVERT TO") {
typ = asm.TypeReg
flag = 0x1
} else {
typ = asm.TypeFPReg
flag = 0x2
}
} else {
typ = asm.TypeReg
flag = 0x1
}
}
case "R2":
switch s[0] {
case "IEXTR", "IEDTR", "LDGR", "RRXTR", "RRDTR":
typ = asm.TypeReg
flag = 0x1
case "CPYA", "EAR":
typ = asm.TypeACReg
flag = 0x3
default:
if check_flags(flags) {
if strings.Contains(text, "CONVERT FROM") {
typ = asm.TypeReg
flag = 0x1
} else {
typ = asm.TypeFPReg
flag = 0x2
}
} else {
typ = asm.TypeReg
flag = 0x1
}
}
case "R3":
switch s[0] {
case "LAM", "LAMY", "STAM", "STAMY":
typ = asm.TypeACReg
flag = 0x3
case "LCTL", "LCTLG", "STCTL", "STCTG":
typ = asm.TypeCReg
flag = 0x4
default:
if check_flags(flags) {
typ = asm.TypeFPReg
flag = 0x2
} else {
typ = asm.TypeReg
flag = 0x1
}
}
}
case "I", "I1", "I2", "I3", "I4", "I5":
flag = 0x0
switch opr {
case "I", "I1":
typ = asm.TypeImmUnsigned
case "I2":
if Imm_signed_8bit_check(inst.Op) {
typ = asm.TypeImmSigned8
break
} else if Imm_signed_16bit_check(inst.Op) { // "ASI", "AGSI", "ALSI", "ALGSI"
typ = asm.TypeImmSigned16
break
} else if Imm_signed_32bit_check(inst.Op) { // "AHI", "AGHI", "AHIK", "AGHIK", "LHI", "LGHI"
typ = asm.TypeImmSigned32
break
} else {
typ = asm.TypeImmUnsigned
break
}
case "I3", "I4", "I5":
typ = asm.TypeImmUnsigned
}
case "RI2", "RI3", "RI4":
flag = 0x80
i := args.Find(opr)
count := uint8(args[i].Bits)
if count == 12 {
typ = asm.TypeRegImSigned12
break
} else if count == 16 {
typ = asm.TypeRegImSigned16
break
} else if count == 24 {
typ = asm.TypeRegImSigned24
break
} else if count == 32 {
typ = asm.TypeRegImSigned32
break
}
case "M1", "M3", "M4", "M5", "M6":
flag = 0x800
typ = asm.TypeMask
case "B1", "B2", "B3", "B4":
typ = asm.TypeBaseReg
flag = 0x20 | 0x01
case "X2":
typ = asm.TypeIndexReg
flag = 0x40 | 0x01
case "D1", "D2", "D3", "D4":
flag = 0x10
i := args.Find(opr)
if uint8(args[i].Bits) == 20 {
typ = asm.TypeDispSigned20
break
} else {
typ = asm.TypeDispUnsigned
break
}
case "L1", "L2":
typ = asm.TypeLen
flag = 0x10
case "V1", "V2", "V3", "V4", "V5", "V6":
typ = asm.TypeVecReg
flag = 0x08
}
if typ == asm.TypeUnknown {
log.Fatalf("%s %s unknown type for opr %s", text, inst, opr)
}
field.Type = typ
field.flags = flag
var f1 asm.BitField
i := args.Find(opr)
if i < 0 {
log.Fatalf("%s: couldn't find %s in %s", text, opr, args)
}
f1.Offs, f1.Bits = uint8(args[i].Offs), uint8(args[i].Bits)
field.BitField = f1
inst.Fields = append(inst.Fields, field)
}
if strings.HasPrefix(inst.Op, "V") || strings.Contains(inst.Op, "WFC") || strings.Contains(inst.Op, "WFK") { //Check Vector Instructions
Bits := asm.BitField{Offs: 36, Bits: 4}
field := Field{Name: "RXB", BitField: Bits, Type: asm.TypeImmUnsigned, flags: 0xC00}
inst.Fields = append(inst.Fields, field)
}
if *debug {
fmt.Printf("%v\n", inst)
}
p.Insts = append(p.Insts, inst)
}
// operandRe matches each operand (including opcode) in instruction mnemonics
var operandRe = regexp.MustCompile(`([[:alpha:]][[:alnum:]_]*\.?)`)
// printText implements the -fmt=text mode, which is not implemented (yet?).
func printText(p *Prog) {
log.Fatal("-fmt=text not implemented")
}
// printEncoder implements the -fmt=encoder mode. which is not implemented (yet?).
func printEncoder(p *Prog) {
log.Fatal("-fmt=encoder not implemented")
}
func printASM(p *Prog) {
fmt.Printf("#include \"hack.h\"\n")
fmt.Printf(".text\n")
for _, inst := range p.Insts {
fmt.Printf("\t%s\n", inst.Encoding)
}
}
// argFieldName constructs a name for the argField
func argFieldName(f Field) string {
ns := []string{"ap", f.Type.String()}
b := f.BitField
ns = append(ns, fmt.Sprintf("%d_%d", b.Offs, b.Offs+b.Bits-1))
return strings.Join(ns, "_")
}
// printDecoder implements the -fmt=decoder mode.
// It emits the tables.go for package armasm's decoder.
func printDecoder(p *Prog) {
var buf bytes.Buffer
fmt.Fprintf(&buf, "// Code generated by s390xmap -fmt=decoder %s DO NOT EDIT.\n", inputFile)
fmt.Fprintf(&buf, "\n")
fmt.Fprintf(&buf, "package s390xasm\n\n")
// Build list of opcodes, using the csv order (which corresponds to ISA docs order)
m := map[string]bool{}
fmt.Fprintf(&buf, "const (\n\t_ Op = iota\n")
for i := 0; i < len(p.Insts); i++ {
name := p.Insts[i].Op
switch name {
case "CUUTF", "CUTFU", "PPNO":
m[name] = false
p.Insts = append(p.Insts[:i], p.Insts[i+1:]...)
i--
default:
m[name] = true
}
if ok := m[name]; !ok {
continue
}
fmt.Fprintf(&buf, "\t%s\n", name)
}
fmt.Fprint(&buf, ")\n\n\n")
// Emit slice mapping opcode number to name string.
m = map[string]bool{}
fmt.Fprintf(&buf, "var opstr = [...]string{\n")
for _, inst := range p.Insts {
name := inst.Op
if ok := m[name]; ok {
continue
}
m[name] = true
fmt.Fprintf(&buf, "\t%s: %q,\n", inst.Op, strings.ToLower(inst.Op))
}
fmt.Fprint(&buf, "}\n\n\n")
// print out argFields
fmt.Fprintf(&buf, "var (\n")
m = map[string]bool{}
for _, inst := range p.Insts {
for _, f := range inst.Fields {
name := argFieldName(f)
if ok := m[name]; ok {
continue
}
m[name] = true
fmt.Fprintf(&buf, "\t%s = &argField{Type: %#v, flags: %#x, BitField: BitField", name, f.Type, f.flags)
b := f.BitField
fmt.Fprintf(&buf, "{%d, %d }", b.Offs, b.Bits)
fmt.Fprintf(&buf, "}\n")
}
}
fmt.Fprint(&buf, ")\n\n\n")
// Emit decoding table.
fmt.Fprintf(&buf, "var instFormats = [...]instFormat{\n")
for _, inst := range p.Insts {
m, v, dc := inst.Mask, inst.Value, inst.DontCare
fmt.Fprintf(&buf, "\t{ %s, %#x, %#x, %#x,", inst.Op, m, v, dc)
fmt.Fprintf(&buf, " // %s (%s)\n\t\t[8]*argField{", inst.Text, inst.Encoding)
for _, f := range inst.Fields {
fmt.Fprintf(&buf, "%s, ", argFieldName(f))
}
fmt.Fprintf(&buf, "}},\n")
}
fmt.Fprint(&buf, "}\n\n")
out, err := gofmt.Source(buf.Bytes())
if err != nil {
log.Fatalf("gofmt error: %v", err)
fmt.Printf("%s", buf.Bytes())
} else {
fmt.Printf("%s", out)
}
}

1059
s390x/s390xspec/spec.go Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

56
s390x/s390xutil/hack.h Normal file
Просмотреть файл

@ -0,0 +1,56 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// This file requires gcc and binutils with -march=z16 support.
// s390xutil runs a series of commands like:
// go run map.go -fmt=asm ../s390x.csv > asm.S
// /usr/bin/gcc -c asm.S -march=z16
// /usr/bin/objdump -d asm.o
// to create the file decode_generated.txt used to verify the disassembler.
//
// Note, the Go disassembler is not expected to support every extended
// mnemonic, but it should support those which frequently show up in object
// files compiled by the Go toolchain.
#define R1 8
#define R2 0
#define R3 0
#define X2 2
#define L1 4
#define L2 4
#define B1 2
#define B2 1
#define B3 6
#define B4 8
#define D1 6
#define D2 11
#define D3 182
#define D4 205
#define V1 18
#define V2 3
#define V3 5
#define V4 8
#define I 124
#define I1 12
#define I2 8
#define I3 9
#define I4 105
#define I5 18
#define RI2 0
#define RI3 294
#define RI4 -168
#define M1 7
#define M3 3
#define M4 1
#define M5 9
#define M6 11

90
s390x/s390xutil/util.go Normal file
Просмотреть файл

@ -0,0 +1,90 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build ignore
// Generate interesting test cases from s390x objdump via
// go run util.go
//
// This requires "/usr/bin/gcc" and "objdump" be in the PATH this command is run.
//
// These tools can be acquired from the IBM advance toolchain for amd64 hosts too.
package main
import (
"bufio"
"fmt"
"io"
"os"
"os/exec"
"regexp"
"strconv"
"strings"
)
// Emit a test file using the generator called name.txt. This requires
// a GCC toolchain which supports -march=z16.
func genOutput(name, tcPfx string, generator func(io.Writer)) {
// Generate object code from gcc
cmd := exec.Command(tcPfx+"gcc", "-c", "-march=z16", "-x", "assembler-with-cpp", "-o", name+".o", "-")
input, _ := cmd.StdinPipe()
cmd.Stderr = os.Stderr
go func() {
defer input.Close()
generator(input.(io.Writer))
}()
if cmd.Run() != nil {
fmt.Printf("Failed running gcc for: %s\n", name)
return
}
defer os.Remove(name + ".o")
cmd = exec.Command(tcPfx+"objdump", "-d", name+".o")
// Run objdump and parse output into test format
output, _ := cmd.StdoutPipe()
defer output.Close()
scanner := bufio.NewScanner(output)
spacere := regexp.MustCompile("[[:space:]]+")
outf, _ := os.Create(name + ".txt")
defer outf.Close()
if cmd.Start() != nil {
fmt.Printf("Failed running objdump for: %s\n", name)
return
}
for scanner.Scan() {
ln := spacere.Split(scanner.Text(), -1)
var cnt int16
if len(ln) >= 5 {
v, _ := strconv.ParseInt(ln[2], 16, 16)
if (v >> 6 & 0x3) == 0 {
cnt = 2
} else if v>>6&0x3 == 1 || v>>6&0x3 == 2 {
cnt = 4
} else {
cnt = 6
}
opc := strings.Join(ln[2:cnt+2], "")
dec := strings.Join(ln[cnt+2:], " ")
fmt.Fprintf(outf, "%12s|\tgnu\t%-18s\n", opc, dec)
}
}
cmd.Wait()
}
// Generate representative instructions for all[1] instructions in s390x.csv.
//
// [1] See hack.h for a few minor, exceptional workarounds.
func emitGenerated(out io.Writer) {
cmd := exec.Command("go", "run", "../s390xmap/map.go", "-fmt=asm", "../s390x.csv")
cmdout, _ := cmd.Output()
out.Write(cmdout)
}
// Produce generated test outputs. This should be run every so often with
// new versions of objdump to ensure we stay up to date.
func main() {
genOutput("decode_generated", "/usr/bin/", emitGenerated)
}