// 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 Len int // Length of encoding in bytes. Args Args // Instruction arguments, in s390x 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 := 0; j < len(i.Args); j++ { if i.Args[j] == nil { break } str := i.Args[j].String(pc) if j == 0 { buf.WriteString(" ") } else { switch i.Args[j].(type) { case VReg: 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 Reg: if _, ok := i.Args[j-1].(Disp12); ok { if str != "" { buf.WriteString("(") } } else if _, ok := i.Args[j-1].(Disp20); ok { if str != "" { 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(",") } else if _, ok := i.Args[j-1].(Disp12); ok { if str != "" { buf.WriteString("(") } } else if _, ok := i.Args[j-1].(Disp20); ok { if str != "" { buf.WriteString("(") } } else if _, ok := i.Args[j-1].(Len); ok { buf.WriteString(",") } else if _, ok := i.Args[j-1].(Index); ok { if ((i.Args[j-1].String(pc)) != "") && str != "" { str = "," + str } else if str == "" { str = ")" } } case Index, Len: if str != "" || (i.Args[j+1].String(pc)) != "" { buf.WriteString("(") } else { j = j + 1 } default: buf.WriteString(",") } } buf.WriteString(str) 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) }