ppc64: add, from github.com/minux/power64
This is a direct copy of github.com/minux/power64, which in turn was forked from rsc.io/power64, which was using Mercurial and is gone. The code in those places was reviewed via code review and is by Minux and me under the usual Go CLA. I've done a global search and replace of power64 to ppc64 and checked that everything still builds. Any further fixes will be in followup CLs. (The arch subrepo did not exist when this code was written.) Change-Id: I80ea16ca689c9fc51a7501c3492099f19aa30873 Reviewed-on: https://go-review.googlesource.com/30932 Run-TryBot: Russ Cox <rsc@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
Родитель
bebde3aee3
Коммит
21375ab36a
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,174 @@
|
|||
// Copyright 2014 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 ppc64asm
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
const debugDecode = false
|
||||
|
||||
// instFormat is a decoding rule for one specific instruction form.
|
||||
// a uint32 instruction ins matches the rule if ins&Mask == Value
|
||||
// DontCare bits should be zero, but the machine might not reject
|
||||
// ones in those bits, they are mainly reserved for future expansion
|
||||
// of the instruction set.
|
||||
// The Args are stored in the same order as the instruction manual.
|
||||
type instFormat struct {
|
||||
Op Op
|
||||
Mask uint32
|
||||
Value uint32
|
||||
DontCare uint32
|
||||
Args [5]*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
|
||||
Shift uint8
|
||||
BitFields
|
||||
}
|
||||
|
||||
// Parse parses the Arg out from the given binary instruction i.
|
||||
func (a argField) Parse(i uint32) Arg {
|
||||
switch a.Type {
|
||||
default:
|
||||
return nil
|
||||
case TypeUnknown:
|
||||
return nil
|
||||
case TypeReg:
|
||||
return R0 + Reg(a.BitFields.Parse(i))
|
||||
case TypeCondRegBit:
|
||||
return Cond0LT + CondReg(a.BitFields.Parse(i))
|
||||
case TypeCondRegField:
|
||||
return CR0 + CondReg(a.BitFields.Parse(i))
|
||||
case TypeFPReg:
|
||||
return F0 + Reg(a.BitFields.Parse(i))
|
||||
case TypeVecReg:
|
||||
return V0 + Reg(a.BitFields.Parse(i))
|
||||
case TypeSpReg:
|
||||
return SpReg(a.BitFields.Parse(i))
|
||||
case TypeImmSigned:
|
||||
return Imm(a.BitFields.ParseSigned(i) << a.Shift)
|
||||
case TypeImmUnsigned:
|
||||
return Imm(a.BitFields.Parse(i) << a.Shift)
|
||||
case TypePCRel:
|
||||
return PCRel(a.BitFields.ParseSigned(i) << a.Shift)
|
||||
case TypeLabel:
|
||||
return Label(a.BitFields.ParseSigned(i) << a.Shift)
|
||||
case TypeOffset:
|
||||
return Offset(a.BitFields.ParseSigned(i) << a.Shift)
|
||||
}
|
||||
}
|
||||
|
||||
type ArgType int8
|
||||
|
||||
const (
|
||||
TypeUnknown ArgType = iota
|
||||
TypePCRel // PC-relative address
|
||||
TypeLabel // absolute address
|
||||
TypeReg // integer register
|
||||
TypeCondRegBit // conditional register bit (0-31)
|
||||
TypeCondRegField // conditional register field (0-7)
|
||||
TypeFPReg // floating point register
|
||||
TypeVecReg // vector register
|
||||
TypeSpReg // special register (depends on Op)
|
||||
TypeImmSigned // signed immediate
|
||||
TypeImmUnsigned // unsigned immediate/flag/mask, this is the catch-all type
|
||||
TypeOffset // signed offset in load/store
|
||||
TypeLast // must be the last one
|
||||
)
|
||||
|
||||
func (t ArgType) String() string {
|
||||
switch t {
|
||||
default:
|
||||
return fmt.Sprintf("ArgType(%d)", int(t))
|
||||
case TypeUnknown:
|
||||
return "Unknown"
|
||||
case TypeReg:
|
||||
return "Reg"
|
||||
case TypeCondRegBit:
|
||||
return "CondRegBit"
|
||||
case TypeCondRegField:
|
||||
return "CondRegField"
|
||||
case TypeFPReg:
|
||||
return "FPReg"
|
||||
case TypeVecReg:
|
||||
return "VecReg"
|
||||
case TypeSpReg:
|
||||
return "SpReg"
|
||||
case TypeImmSigned:
|
||||
return "ImmSigned"
|
||||
case TypeImmUnsigned:
|
||||
return "ImmUnsigned"
|
||||
case TypePCRel:
|
||||
return "PCRel"
|
||||
case TypeLabel:
|
||||
return "Label"
|
||||
case TypeOffset:
|
||||
return "Offset"
|
||||
}
|
||||
}
|
||||
|
||||
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, ord binary.ByteOrder) (inst Inst, err error) {
|
||||
if len(src) < 4 {
|
||||
return inst, errShort
|
||||
}
|
||||
if decoderCover == nil {
|
||||
decoderCover = make([]bool, len(instFormats))
|
||||
}
|
||||
inst.Len = 4 // only 4-byte instructions are supported
|
||||
ui := ord.Uint32(src[:inst.Len])
|
||||
inst.Enc = ui
|
||||
for i, iform := range instFormats {
|
||||
if ui&iform.Mask != iform.Value {
|
||||
continue
|
||||
}
|
||||
if ui&iform.DontCare != 0 {
|
||||
if debugDecode {
|
||||
log.Printf("Decode(%#x): unused bit is 1 for Op %s", ui, iform.Op)
|
||||
}
|
||||
// to match GNU objdump (libopcodes), we ignore don't care bits
|
||||
}
|
||||
for i, argfield := range iform.Args {
|
||||
if argfield == nil {
|
||||
break
|
||||
}
|
||||
inst.Args[i] = argfield.Parse(ui)
|
||||
}
|
||||
inst.Op = iform.Op
|
||||
if debugDecode {
|
||||
log.Printf("%#x: search entry %d", ui, i)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
if inst.Op == 0 {
|
||||
return inst, errUnknown
|
||||
}
|
||||
return inst, nil
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
// Copyright 2014 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 ppc64asm
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDecode(t *testing.T) {
|
||||
data, err := ioutil.ReadFile("testdata/decode.txt")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
all := string(data)
|
||||
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("parsing %q: missing | separator", f[0])
|
||||
continue
|
||||
}
|
||||
if i%2 != 0 {
|
||||
t.Errorf("parsing %q: misaligned | separator", f[0])
|
||||
}
|
||||
size := i / 2
|
||||
code, err := hex.DecodeString(f[0][:i] + f[0][i+1:])
|
||||
if err != nil {
|
||||
t.Errorf("parsing %q: %v", f[0], err)
|
||||
continue
|
||||
}
|
||||
syntax, asm := f[1], f[2]
|
||||
inst, err := Decode(code, binary.BigEndian)
|
||||
var out string
|
||||
if err != nil {
|
||||
out = "error: " + err.Error()
|
||||
} else {
|
||||
switch syntax {
|
||||
case "gnu":
|
||||
out = GNUSyntax(inst)
|
||||
//case "plan9":
|
||||
// out = plan9Syntax(inst, 0, nil, nil)
|
||||
default:
|
||||
t.Errorf("unknown syntax %q", syntax)
|
||||
continue
|
||||
}
|
||||
}
|
||||
if out != asm || inst.Len != size {
|
||||
t.Errorf("Decode(%s) [%s] = %s want %s", f[0], syntax, out, asm)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
// Copyright 2014 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 ppc64asm implements decoding of 64-bit PowerPC machine code.
|
||||
package ppc64asm
|
|
@ -0,0 +1,535 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
// Support for testing against external disassembler program.
|
||||
// Copied and simplified from rsc.io/arm/armasm/ext_test.go.
|
||||
|
||||
package ppc64asm
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
printTests = flag.Bool("printtests", false, "print test cases that exercise new code paths")
|
||||
dumpTest = flag.Bool("dump", false, "dump all encodings")
|
||||
mismatch = flag.Bool("mismatch", false, "log allowed mismatches")
|
||||
longTest = flag.Bool("long", false, "long test")
|
||||
keep = flag.Bool("keep", false, "keep object files around")
|
||||
debug = false
|
||||
)
|
||||
|
||||
// A ExtInst represents a single decoded instruction parsed
|
||||
// from an external disassembler's output.
|
||||
type ExtInst struct {
|
||||
addr uint32
|
||||
enc [4]byte
|
||||
nenc int
|
||||
text string
|
||||
}
|
||||
|
||||
func (r ExtInst) String() string {
|
||||
return fmt.Sprintf("%#x: % x: %s", r.addr, r.enc, r.text)
|
||||
}
|
||||
|
||||
// An ExtDis is a connection between an external disassembler and a test.
|
||||
type ExtDis struct {
|
||||
Dec chan ExtInst
|
||||
File *os.File
|
||||
Size int
|
||||
KeepFile bool
|
||||
Cmd *exec.Cmd
|
||||
}
|
||||
|
||||
// Run runs the given command - the external disassembler - and returns
|
||||
// a buffered reader of its standard output.
|
||||
func (ext *ExtDis) Run(cmd ...string) (*bufio.Reader, error) {
|
||||
if *keep {
|
||||
log.Printf("%s\n", strings.Join(cmd, " "))
|
||||
}
|
||||
ext.Cmd = exec.Command(cmd[0], cmd[1:]...)
|
||||
out, err := ext.Cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("stdoutpipe: %v", err)
|
||||
}
|
||||
if err := ext.Cmd.Start(); err != nil {
|
||||
return nil, fmt.Errorf("exec: %v", err)
|
||||
}
|
||||
|
||||
b := bufio.NewReaderSize(out, 1<<20)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Wait waits for the command started with Run to exit.
|
||||
func (ext *ExtDis) Wait() error {
|
||||
return ext.Cmd.Wait()
|
||||
}
|
||||
|
||||
// testExtDis tests a set of byte sequences against an external disassembler.
|
||||
// The disassembler is expected to produce the given syntax and be run
|
||||
// in the given architecture mode (16, 32, or 64-bit).
|
||||
// The extdis function must start the external disassembler
|
||||
// and then parse its output, sending the parsed instructions on ext.Dec.
|
||||
// The generate function calls its argument f once for each byte sequence
|
||||
// to be tested. The generate function itself will be called twice, and it must
|
||||
// make the same sequence of calls to f each time.
|
||||
// When a disassembly does not match the internal decoding,
|
||||
// allowedMismatch determines whether this mismatch should be
|
||||
// allowed, or else considered an error.
|
||||
func testExtDis(
|
||||
t *testing.T,
|
||||
syntax string,
|
||||
extdis func(ext *ExtDis) error,
|
||||
generate func(f func([]byte)),
|
||||
allowedMismatch func(text string, size int, inst *Inst, dec ExtInst) bool,
|
||||
) {
|
||||
start := time.Now()
|
||||
ext := &ExtDis{
|
||||
Dec: make(chan ExtInst),
|
||||
}
|
||||
errc := make(chan error)
|
||||
|
||||
// First pass: write instructions to input file for external disassembler.
|
||||
file, f, size, err := writeInst(generate)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ext.Size = size
|
||||
ext.File = f
|
||||
defer func() {
|
||||
f.Close()
|
||||
if !*keep {
|
||||
os.Remove(file)
|
||||
}
|
||||
}()
|
||||
|
||||
// Second pass: compare disassembly against our decodings.
|
||||
var (
|
||||
totalTests = 0
|
||||
totalSkips = 0
|
||||
totalErrors = 0
|
||||
|
||||
errors = make([]string, 0, 100) // sampled errors, at most cap
|
||||
)
|
||||
go func() {
|
||||
errc <- extdis(ext)
|
||||
}()
|
||||
generate(func(enc []byte) {
|
||||
dec, ok := <-ext.Dec
|
||||
if !ok {
|
||||
t.Errorf("decoding stream ended early")
|
||||
return
|
||||
}
|
||||
inst, text := disasm(syntax, pad(enc))
|
||||
totalTests++
|
||||
if *dumpTest {
|
||||
fmt.Printf("%x -> %s [%d]\n", enc[:len(enc)], dec.text, dec.nenc)
|
||||
}
|
||||
if text != dec.text || inst.Len != dec.nenc {
|
||||
suffix := ""
|
||||
if allowedMismatch(text, size, &inst, dec) {
|
||||
totalSkips++
|
||||
if !*mismatch {
|
||||
return
|
||||
}
|
||||
suffix += " (allowed mismatch)"
|
||||
}
|
||||
totalErrors++
|
||||
if len(errors) >= cap(errors) {
|
||||
j := rand.Intn(totalErrors)
|
||||
if j >= cap(errors) {
|
||||
return
|
||||
}
|
||||
errors = append(errors[:j], errors[j+1:]...)
|
||||
}
|
||||
errors = append(errors, fmt.Sprintf("decode(%x) = %q, %d, want %q, %d%s", enc, text, inst.Len, dec.text, dec.nenc, suffix))
|
||||
}
|
||||
})
|
||||
|
||||
if *mismatch {
|
||||
totalErrors -= totalSkips
|
||||
}
|
||||
|
||||
for _, b := range errors {
|
||||
t.Log(b)
|
||||
}
|
||||
|
||||
if totalErrors > 0 {
|
||||
t.Fail()
|
||||
}
|
||||
t.Logf("%d test cases, %d expected mismatches, %d failures; %.0f cases/second", totalTests, totalSkips, totalErrors, float64(totalTests)/time.Since(start).Seconds())
|
||||
|
||||
if err := <-errc; err != nil {
|
||||
t.Fatal("external disassembler: %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const start = 0x8000 // start address of text
|
||||
|
||||
// writeInst writes the generated byte sequences to a new file
|
||||
// starting at offset start. That file is intended to be the input to
|
||||
// the external disassembler.
|
||||
func writeInst(generate func(func([]byte))) (file string, f *os.File, size int, err error) {
|
||||
f, err = ioutil.TempFile("", "ppc64asm")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
file = f.Name()
|
||||
|
||||
f.Seek(start, 0)
|
||||
w := bufio.NewWriter(f)
|
||||
defer w.Flush()
|
||||
size = 0
|
||||
generate(func(x []byte) {
|
||||
if len(x) > 4 {
|
||||
x = x[:4]
|
||||
}
|
||||
if debug {
|
||||
fmt.Printf("%#x: %x%x\n", start+size, x, zeros[len(x):])
|
||||
}
|
||||
w.Write(x)
|
||||
w.Write(zeros[len(x):])
|
||||
size += len(zeros)
|
||||
})
|
||||
return file, f, size, nil
|
||||
}
|
||||
|
||||
var zeros = []byte{0, 0, 0, 0}
|
||||
|
||||
// pad pads the code sequence with pops.
|
||||
func pad(enc []byte) []byte {
|
||||
if len(enc) < 4 {
|
||||
enc = append(enc[:len(enc):len(enc)], zeros[:4-len(enc)]...)
|
||||
}
|
||||
return enc
|
||||
}
|
||||
|
||||
// disasm returns the decoded instruction and text
|
||||
// for the given source bytes, using the given syntax and mode.
|
||||
func disasm(syntax string, src []byte) (inst Inst, text string) {
|
||||
// If printTests is set, we record the coverage value
|
||||
// before and after, and we write out the inputs for which
|
||||
// coverage went up, in the format expected in testdata/decode.text.
|
||||
// This produces a fairly small set of test cases that exercise nearly
|
||||
// all the code.
|
||||
var cover float64
|
||||
if *printTests {
|
||||
cover -= coverage()
|
||||
}
|
||||
|
||||
inst, err := Decode(src, binary.BigEndian)
|
||||
if err != nil {
|
||||
text = "error: " + err.Error()
|
||||
} else {
|
||||
text = inst.String()
|
||||
switch syntax {
|
||||
//case "arm":
|
||||
// text = ARMSyntax(inst)
|
||||
case "gnu":
|
||||
text = GNUSyntax(inst)
|
||||
//case "plan9":
|
||||
// text = plan9Syntax(inst, 0, nil)
|
||||
default:
|
||||
text = "error: unknown syntax " + syntax
|
||||
}
|
||||
}
|
||||
|
||||
if *printTests {
|
||||
cover += coverage()
|
||||
if cover > 0 {
|
||||
max := len(src)
|
||||
if max > 4 && inst.Len <= 4 {
|
||||
max = 4
|
||||
}
|
||||
fmt.Printf("%x|%x\t%s\t%s\n", src[:inst.Len], src[inst.Len:max], syntax, text)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// coverage returns a floating point number denoting the
|
||||
// test coverage until now. The number increases when new code paths are exercised,
|
||||
// both in the Go program and in the decoder byte code.
|
||||
func coverage() float64 {
|
||||
var f float64
|
||||
f += testing.Coverage()
|
||||
f += decodeCoverage()
|
||||
return f
|
||||
}
|
||||
|
||||
func decodeCoverage() float64 {
|
||||
n := 0
|
||||
for _, t := range decoderCover {
|
||||
if t {
|
||||
n++
|
||||
}
|
||||
}
|
||||
return float64(1+n) / float64(1+len(decoderCover))
|
||||
}
|
||||
|
||||
// Helpers for writing disassembler output parsers.
|
||||
|
||||
// hasPrefix reports whether any of the space-separated words in the text s
|
||||
// begins with any of the given prefixes.
|
||||
func hasPrefix(s string, prefixes ...string) bool {
|
||||
for _, prefix := range prefixes {
|
||||
for s := s; s != ""; {
|
||||
if strings.HasPrefix(s, prefix) {
|
||||
return true
|
||||
}
|
||||
i := strings.Index(s, " ")
|
||||
if i < 0 {
|
||||
break
|
||||
}
|
||||
s = s[i+1:]
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// contains reports whether the text s contains any of the given substrings.
|
||||
func contains(s string, substrings ...string) bool {
|
||||
for _, sub := range substrings {
|
||||
if strings.Contains(s, sub) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isHex reports whether b is a hexadecimal character (0-9A-Fa-f).
|
||||
func isHex(b byte) bool { return b == '0' || unhex[b] > 0 }
|
||||
|
||||
// parseHex parses the hexadecimal byte dump in hex,
|
||||
// appending the parsed bytes to raw and returning the updated slice.
|
||||
// The returned bool signals whether any invalid hex was found.
|
||||
// Spaces and tabs between bytes are okay but any other non-hex is not.
|
||||
func parseHex(hex []byte, raw []byte) ([]byte, bool) {
|
||||
hex = trimSpace(hex)
|
||||
for j := 0; j < len(hex); {
|
||||
for hex[j] == ' ' || hex[j] == '\t' {
|
||||
j++
|
||||
}
|
||||
if j >= len(hex) {
|
||||
break
|
||||
}
|
||||
if j+2 > len(hex) || !isHex(hex[j]) || !isHex(hex[j+1]) {
|
||||
return nil, false
|
||||
}
|
||||
raw = append(raw, unhex[hex[j]]<<4|unhex[hex[j+1]])
|
||||
j += 2
|
||||
}
|
||||
return raw, true
|
||||
}
|
||||
|
||||
var unhex = [256]byte{
|
||||
'0': 0,
|
||||
'1': 1,
|
||||
'2': 2,
|
||||
'3': 3,
|
||||
'4': 4,
|
||||
'5': 5,
|
||||
'6': 6,
|
||||
'7': 7,
|
||||
'8': 8,
|
||||
'9': 9,
|
||||
'A': 10,
|
||||
'B': 11,
|
||||
'C': 12,
|
||||
'D': 13,
|
||||
'E': 14,
|
||||
'F': 15,
|
||||
'a': 10,
|
||||
'b': 11,
|
||||
'c': 12,
|
||||
'd': 13,
|
||||
'e': 14,
|
||||
'f': 15,
|
||||
}
|
||||
|
||||
// index is like bytes.Index(s, []byte(t)) but avoids the allocation.
|
||||
func index(s []byte, t string) int {
|
||||
i := 0
|
||||
for {
|
||||
j := bytes.IndexByte(s[i:], t[0])
|
||||
if j < 0 {
|
||||
return -1
|
||||
}
|
||||
i = i + j
|
||||
if i+len(t) > len(s) {
|
||||
return -1
|
||||
}
|
||||
for k := 1; k < len(t); k++ {
|
||||
if s[i+k] != t[k] {
|
||||
goto nomatch
|
||||
}
|
||||
}
|
||||
return i
|
||||
nomatch:
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// fixSpace rewrites runs of spaces, tabs, and newline characters into single spaces in s.
|
||||
// If s must be rewritten, it is rewritten in place.
|
||||
func fixSpace(s []byte) []byte {
|
||||
s = trimSpace(s)
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] == '\t' || s[i] == '\n' || i > 0 && s[i] == ' ' && s[i-1] == ' ' {
|
||||
goto Fix
|
||||
}
|
||||
}
|
||||
return s
|
||||
|
||||
Fix:
|
||||
b := s
|
||||
w := 0
|
||||
for i := 0; i < len(s); i++ {
|
||||
c := s[i]
|
||||
if c == '\t' || c == '\n' {
|
||||
c = ' '
|
||||
}
|
||||
if c == ' ' && w > 0 && b[w-1] == ' ' {
|
||||
continue
|
||||
}
|
||||
b[w] = c
|
||||
w++
|
||||
}
|
||||
if w > 0 && b[w-1] == ' ' {
|
||||
w--
|
||||
}
|
||||
return b[:w]
|
||||
}
|
||||
|
||||
// trimSpace trims leading and trailing space from s, returning a subslice of s.
|
||||
func trimSpace(s []byte) []byte {
|
||||
j := len(s)
|
||||
for j > 0 && (s[j-1] == ' ' || s[j-1] == '\t' || s[j-1] == '\n') {
|
||||
j--
|
||||
}
|
||||
i := 0
|
||||
for i < j && (s[i] == ' ' || s[i] == '\t') {
|
||||
i++
|
||||
}
|
||||
return s[i:j]
|
||||
}
|
||||
|
||||
// pcrel matches instructions using relative addressing mode.
|
||||
var (
|
||||
pcrel = regexp.MustCompile(`^((?:.* )?(?:b|bc)[^ac ]* (?:(?:[0-9]{1,2},)|(?:[0-7]\*)|\+|lt|gt|eq|so|cr[0-7]|,)*)0x([0-9a-f]+)$`)
|
||||
)
|
||||
|
||||
// Generators.
|
||||
//
|
||||
// The test cases are described as functions that invoke a callback repeatedly,
|
||||
// with a new input sequence each time. These helpers make writing those
|
||||
// a little easier.
|
||||
|
||||
// randomCases generates random instructions.
|
||||
func randomCases(t *testing.T) func(func([]byte)) {
|
||||
return func(try func([]byte)) {
|
||||
// All the strides are relatively prime to 2 and therefore to 2²⁸,
|
||||
// so we will not repeat any instructions until we have tried all 2²⁸.
|
||||
// Using a stride other than 1 is meant to visit the instructions in a
|
||||
// pseudorandom order, which gives better variety in the set of
|
||||
// test cases chosen by -printtests.
|
||||
stride := uint32(10007)
|
||||
n := 1 << 28 / 7
|
||||
if testing.Short() {
|
||||
stride = 100003
|
||||
n = 1 << 28 / 1001
|
||||
} else if *longTest {
|
||||
stride = 2000033
|
||||
n = 1 << 29
|
||||
}
|
||||
x := uint32(0)
|
||||
for i := 0; i < n; i++ {
|
||||
enc := (x%15)<<28 | x&(1<<28-1)
|
||||
try([]byte{byte(enc), byte(enc >> 8), byte(enc >> 16), byte(enc >> 24)})
|
||||
x += stride
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// hexCases generates the cases written in hexadecimal in the encoded string.
|
||||
// Spaces in 'encoded' separate entire test cases, not individual bytes.
|
||||
func hexCases(t *testing.T, encoded string) func(func([]byte)) {
|
||||
return func(try func([]byte)) {
|
||||
for _, x := range strings.Fields(encoded) {
|
||||
src, err := hex.DecodeString(x)
|
||||
if err != nil {
|
||||
t.Errorf("parsing %q: %v", x, err)
|
||||
}
|
||||
try(src)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// testdataCases generates the test cases recorded in testdata/decode.txt.
|
||||
// It only uses the inputs; it ignores the answers recorded in that file.
|
||||
func testdataCases(t *testing.T) func(func([]byte)) {
|
||||
var codes [][]byte
|
||||
data, err := ioutil.ReadFile("testdata/decode.txt")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, line := range strings.Split(string(data), "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
f := strings.Fields(line)[0]
|
||||
i := strings.Index(f, "|")
|
||||
if i < 0 {
|
||||
t.Errorf("parsing %q: missing | separator", f)
|
||||
continue
|
||||
}
|
||||
if i%2 != 0 {
|
||||
t.Errorf("parsing %q: misaligned | separator", f)
|
||||
}
|
||||
code, err := hex.DecodeString(f[:i] + f[i+1:])
|
||||
if err != nil {
|
||||
t.Errorf("parsing %q: %v", f, err)
|
||||
continue
|
||||
}
|
||||
codes = append(codes, code)
|
||||
}
|
||||
|
||||
return func(try func([]byte)) {
|
||||
for _, code := range codes {
|
||||
try(code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func caller(skip int) string {
|
||||
pc, _, _, _ := runtime.Caller(skip)
|
||||
f := runtime.FuncForPC(pc)
|
||||
name := "?"
|
||||
if f != nil {
|
||||
name = f.Name()
|
||||
if i := strings.LastIndex(name, "."); i >= 0 {
|
||||
name = name[i+1:]
|
||||
}
|
||||
}
|
||||
return name
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
// Copyright 2014 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 ppc64asm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A BitField is a bit-field in a 32-bit word.
|
||||
// Bits are counted from 0 from the MSB to 31 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 uint32) uint32 {
|
||||
if b.Bits > 32 || b.Bits == 0 || b.Offs > 31 || b.Offs+b.Bits > 32 {
|
||||
panic(fmt.Sprintf("invalid bitfiled %v", b))
|
||||
}
|
||||
return (i >> (32 - 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 uint32) int32 {
|
||||
u := int32(b.Parse(i))
|
||||
return u << (32 - b.Bits) >> (32 - 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 uint32) (u uint32, Bits uint8) {
|
||||
for _, b := range bs {
|
||||
u = (u << b.Bits) | 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 uint32) uint32 {
|
||||
u, _ := bs.parse(i)
|
||||
return u
|
||||
}
|
||||
|
||||
// Parse 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 uint32) int32 {
|
||||
u, l := bs.parse(i)
|
||||
return int32(u) << (32 - l) >> (32 - l)
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
// Copyright 2014 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 ppc64asm
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func panicOrNot(f func()) (panicked bool) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
panicked = true
|
||||
}
|
||||
}()
|
||||
f()
|
||||
return false
|
||||
}
|
||||
|
||||
func TestBitField(t *testing.T) {
|
||||
var tests = []struct {
|
||||
b BitField
|
||||
i uint32 // input
|
||||
u uint32 // unsigned output
|
||||
s int32 // signed output
|
||||
fail bool // if the check should panic
|
||||
}{
|
||||
{BitField{0, 0}, 0, 0, 0, true},
|
||||
{BitField{31, 2}, 0, 0, 0, true},
|
||||
{BitField{31, 1}, 1, 1, -1, false},
|
||||
{BitField{29, 2}, 0 << 1, 0, 0, false},
|
||||
{BitField{29, 2}, 1 << 1, 1, 1, false},
|
||||
{BitField{29, 2}, 2 << 1, 2, -2, false},
|
||||
{BitField{29, 2}, 3 << 1, 3, -1, false},
|
||||
{BitField{0, 32}, 1<<32 - 1, 1<<32 - 1, -1, false},
|
||||
{BitField{16, 3}, 1 << 15, 4, -4, false},
|
||||
}
|
||||
for i, tst := range tests {
|
||||
var (
|
||||
ou uint32
|
||||
os int32
|
||||
)
|
||||
failed := panicOrNot(func() {
|
||||
ou = tst.b.Parse(tst.i)
|
||||
os = tst.b.ParseSigned(tst.i)
|
||||
})
|
||||
if failed != tst.fail {
|
||||
t.Errorf("case %d: %v: fail test failed, got %v, expected %v", i, tst.b, failed, tst.fail)
|
||||
continue
|
||||
}
|
||||
if ou != tst.u {
|
||||
t.Errorf("case %d: %v.Parse(%d) returned %d, expected %d", i, tst.b, tst.i, ou, tst.u)
|
||||
continue
|
||||
}
|
||||
if os != tst.s {
|
||||
t.Errorf("case %d: %v.ParseSigned(%d) returned %d, expected %d", i, tst.b, tst.i, os, tst.s)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
// Copyright 2014 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 ppc64asm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GNUSyntax returns the GNU assembler syntax for the instruction, as defined by GNU binutils.
|
||||
// This form typically matches the syntax defined in the Power ISA Reference Manual.
|
||||
func GNUSyntax(inst Inst) string {
|
||||
var buf bytes.Buffer
|
||||
if inst.Op == 0 {
|
||||
return "error: unkown instruction"
|
||||
}
|
||||
buf.WriteString(inst.Op.String())
|
||||
sep := " "
|
||||
for i, arg := range inst.Args[:] {
|
||||
if arg == nil {
|
||||
break
|
||||
}
|
||||
text := gnuArg(&inst, i, arg)
|
||||
if text == "" {
|
||||
continue
|
||||
}
|
||||
buf.WriteString(sep)
|
||||
sep = ","
|
||||
buf.WriteString(text)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// gnuArg formats arg (which is the argIndex's arg in inst) according to GNU rules.
|
||||
// NOTE: because GNUSyntax is the only caller of this func, and it receives a copy
|
||||
// of inst, it's ok to modify inst.Args here.
|
||||
func gnuArg(inst *Inst, argIndex int, arg Arg) string {
|
||||
// special cases for load/store instructions
|
||||
if _, ok := arg.(Offset); ok {
|
||||
if argIndex+1 == len(inst.Args) || inst.Args[argIndex+1] == nil {
|
||||
panic(fmt.Errorf("wrong table: offset not followed by register"))
|
||||
}
|
||||
}
|
||||
switch arg := arg.(type) {
|
||||
case Reg:
|
||||
if isLoadStoreOp(inst.Op) && argIndex == 1 && arg == R0 {
|
||||
return "0"
|
||||
}
|
||||
return arg.String()
|
||||
case CondReg:
|
||||
if arg == CR0 && strings.HasPrefix(inst.Op.String(), "cmp") {
|
||||
return "" // don't show cr0 for cmp instructions
|
||||
} else if arg >= CR0 {
|
||||
return fmt.Sprintf("cr%d", int(arg-CR0))
|
||||
}
|
||||
bit := [4]string{"lt", "gt", "eq", "so"}[(arg-Cond0LT)%4]
|
||||
if arg <= Cond0SO {
|
||||
return bit
|
||||
}
|
||||
return fmt.Sprintf("4*cr%d+%s", int(arg-Cond0LT)/4, bit)
|
||||
case Imm:
|
||||
return fmt.Sprintf("%d", arg)
|
||||
case SpReg:
|
||||
return fmt.Sprintf("%d", int(arg))
|
||||
case PCRel:
|
||||
return fmt.Sprintf(".%+#x", int(arg))
|
||||
case Label:
|
||||
return fmt.Sprintf("%#x", int(arg))
|
||||
case Offset:
|
||||
reg := inst.Args[argIndex+1].(Reg)
|
||||
removeArg(inst, argIndex+1)
|
||||
if reg == R0 {
|
||||
return fmt.Sprintf("%d(0)", int(arg))
|
||||
}
|
||||
return fmt.Sprintf("%d(r%d)", int(arg), reg-R0)
|
||||
}
|
||||
return fmt.Sprintf("???(%v)", arg)
|
||||
}
|
||||
|
||||
// removeArg removes the arg in inst.Args[index].
|
||||
func removeArg(inst *Inst, index int) {
|
||||
for i := index; i < len(inst.Args); i++ {
|
||||
if i+1 < len(inst.Args) {
|
||||
inst.Args[i] = inst.Args[i+1]
|
||||
} else {
|
||||
inst.Args[i] = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// isLoadStoreOp returns true if op is a load or store instruction
|
||||
func isLoadStoreOp(op Op) bool {
|
||||
switch op {
|
||||
case LBZ, LBZU, LBZX, LBZUX:
|
||||
return true
|
||||
case LHZ, LHZU, LHZX, LHZUX:
|
||||
return true
|
||||
case LHA, LHAU, LHAX, LHAUX:
|
||||
return true
|
||||
case LWZ, LWZU, LWZX, LWZUX:
|
||||
return true
|
||||
case LWA, LWAX, LWAUX:
|
||||
return true
|
||||
case LD, LDU, LDX, LDUX:
|
||||
return true
|
||||
case LQ:
|
||||
return true
|
||||
case STB, STBU, STBX, STBUX:
|
||||
return true
|
||||
case STH, STHU, STHX, STHUX:
|
||||
return true
|
||||
case STW, STWU, STWX, STWUX:
|
||||
return true
|
||||
case STD, STDU, STDX, STDUX:
|
||||
return true
|
||||
case STQ:
|
||||
return true
|
||||
case LHBRX, LWBRX, STHBRX, STWBRX:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,310 @@
|
|||
// Copyright 2014 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 ppc64asm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Inst struct {
|
||||
Op Op // Opcode mnemonic
|
||||
Enc uint32 // Raw encoding bits
|
||||
Len int // Length of encoding in bytes.
|
||||
Args Args // Instruction arguments, in Power ISA manual order.
|
||||
}
|
||||
|
||||
func (i Inst) String() string {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(i.Op.String())
|
||||
for j, arg := range i.Args {
|
||||
if arg == nil {
|
||||
break
|
||||
}
|
||||
if j == 0 {
|
||||
buf.WriteString(" ")
|
||||
} else {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
buf.WriteString(arg.String())
|
||||
}
|
||||
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, CondReg, SpReg, Imm, PCRel, Label, or Offset.
|
||||
type Arg interface {
|
||||
IsArg()
|
||||
String() string
|
||||
}
|
||||
|
||||
// An Args holds the instruction arguments.
|
||||
// If an instruction has fewer than 4 arguments,
|
||||
// the final elements in the array are nil.
|
||||
type Args [5]Arg
|
||||
|
||||
// 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 (
|
||||
_ Reg = iota
|
||||
R0
|
||||
R1
|
||||
R2
|
||||
R3
|
||||
R4
|
||||
R5
|
||||
R6
|
||||
R7
|
||||
R8
|
||||
R9
|
||||
R10
|
||||
R11
|
||||
R12
|
||||
R13
|
||||
R14
|
||||
R15
|
||||
R16
|
||||
R17
|
||||
R18
|
||||
R19
|
||||
R20
|
||||
R21
|
||||
R22
|
||||
R23
|
||||
R24
|
||||
R25
|
||||
R26
|
||||
R27
|
||||
R28
|
||||
R29
|
||||
R30
|
||||
R31
|
||||
F0
|
||||
F1
|
||||
F2
|
||||
F3
|
||||
F4
|
||||
F5
|
||||
F6
|
||||
F7
|
||||
F8
|
||||
F9
|
||||
F10
|
||||
F11
|
||||
F12
|
||||
F13
|
||||
F14
|
||||
F15
|
||||
F16
|
||||
F17
|
||||
F18
|
||||
F19
|
||||
F20
|
||||
F21
|
||||
F22
|
||||
F23
|
||||
F24
|
||||
F25
|
||||
F26
|
||||
F27
|
||||
F28
|
||||
F29
|
||||
F30
|
||||
F31
|
||||
V0 // VSX extension, F0 is V0[0:63].
|
||||
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
|
||||
V32
|
||||
V33
|
||||
V34
|
||||
V35
|
||||
V36
|
||||
V37
|
||||
V38
|
||||
V39
|
||||
V40
|
||||
V41
|
||||
V42
|
||||
V43
|
||||
V44
|
||||
V45
|
||||
V46
|
||||
V47
|
||||
V48
|
||||
V49
|
||||
V50
|
||||
V51
|
||||
V52
|
||||
V53
|
||||
V54
|
||||
V55
|
||||
V56
|
||||
V57
|
||||
V58
|
||||
V59
|
||||
V60
|
||||
V61
|
||||
V62
|
||||
V63
|
||||
)
|
||||
|
||||
func (Reg) IsArg() {}
|
||||
func (r Reg) String() string {
|
||||
switch {
|
||||
case R0 <= r && r <= R31:
|
||||
return fmt.Sprintf("r%d", int(r-R0))
|
||||
case F0 <= r && r <= F31:
|
||||
return fmt.Sprintf("f%d", int(r-F0))
|
||||
case V0 <= r && r <= V63:
|
||||
return fmt.Sprintf("v%d", int(r-V0))
|
||||
default:
|
||||
return fmt.Sprint("Reg(%d)", int(r))
|
||||
}
|
||||
}
|
||||
|
||||
// CondReg is a bit or field in the conditon register.
|
||||
type CondReg int8
|
||||
|
||||
const (
|
||||
_ CondReg = iota
|
||||
// Condition Regster bits
|
||||
Cond0LT
|
||||
Cond0GT
|
||||
Cond0EQ
|
||||
Cond0SO
|
||||
Cond1LT
|
||||
Cond1GT
|
||||
Cond1EQ
|
||||
Cond1SO
|
||||
Cond2LT
|
||||
Cond2GT
|
||||
Cond2EQ
|
||||
Cond2SO
|
||||
Cond3LT
|
||||
Cond3GT
|
||||
Cond3EQ
|
||||
Cond3SO
|
||||
Cond4LT
|
||||
Cond4GT
|
||||
Cond4EQ
|
||||
Cond4SO
|
||||
Cond5LT
|
||||
Cond5GT
|
||||
Cond5EQ
|
||||
Cond5SO
|
||||
Cond6LT
|
||||
Cond6GT
|
||||
Cond6EQ
|
||||
Cond6SO
|
||||
Cond7LT
|
||||
Cond7GT
|
||||
Cond7EQ
|
||||
Cond7SO
|
||||
// Condition Register Fields
|
||||
CR0
|
||||
CR1
|
||||
CR2
|
||||
CR3
|
||||
CR4
|
||||
CR5
|
||||
CR6
|
||||
CR7
|
||||
)
|
||||
|
||||
func (CondReg) IsArg() {}
|
||||
func (c CondReg) String() string {
|
||||
switch {
|
||||
default:
|
||||
return fmt.Sprintf("CondReg(%d)", int(c))
|
||||
case c >= CR0:
|
||||
return fmt.Sprintf("CR%d", int(c-CR0))
|
||||
case c >= Cond0LT && c < CR0:
|
||||
return fmt.Sprintf("Cond%d%s", int((c-Cond0LT)/4), [4]string{"LT", "GT", "EQ", "SO"}[(c-Cond0LT)%4])
|
||||
}
|
||||
}
|
||||
|
||||
// SpReg is a special register, its meaning depends on Op.
|
||||
type SpReg uint16
|
||||
|
||||
const (
|
||||
SpRegZero SpReg = 0
|
||||
)
|
||||
|
||||
func (SpReg) IsArg() {}
|
||||
func (s SpReg) String() string {
|
||||
return fmt.Sprintf("SpReg(%d)", int(s))
|
||||
}
|
||||
|
||||
// PCRel is a PC-relative offset, used only in branch instructions.
|
||||
type PCRel int32
|
||||
|
||||
func (PCRel) IsArg() {}
|
||||
func (r PCRel) String() string {
|
||||
return fmt.Sprintf("PC%+#x", int32(r))
|
||||
}
|
||||
|
||||
// A Label is a code (text) address, used only in absolute branch instructions.
|
||||
type Label uint32
|
||||
|
||||
func (Label) IsArg() {}
|
||||
func (l Label) String() string {
|
||||
return fmt.Sprintf("%#x", uint32(l))
|
||||
}
|
||||
|
||||
// Imm represents an immediate number.
|
||||
type Imm int32
|
||||
|
||||
func (Imm) IsArg() {}
|
||||
func (i Imm) String() string {
|
||||
return fmt.Sprintf("%d", int32(i))
|
||||
}
|
||||
|
||||
// Offset represents a memory offset immediate.
|
||||
type Offset int32
|
||||
|
||||
func (Offset) IsArg() {}
|
||||
func (o Offset) String() string {
|
||||
return fmt.Sprintf("%+d", int32(o))
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
// Copyright 2014 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 ppc64asm
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestObjdumpPowerTestdata(t *testing.T) { testObjdump(t, testdataCases(t)) }
|
||||
func TestObjdumpPowerManual(t *testing.T) { testObjdump(t, hexCases(t, objdumpManualTests)) }
|
||||
func TestObjdumpPowerRandom(t *testing.T) { testObjdump(t, randomCases(t)) }
|
||||
|
||||
// objdumpManualTests holds test cases that will be run by TestObjdumpARMManual.
|
||||
// If you are debugging a few cases that turned up in a longer run, it can be useful
|
||||
// to list them here and then use -run=Manual, particularly with tracing enabled.
|
||||
// Note that these are byte sequences, so they must be reversed from the usual
|
||||
// word presentation.
|
||||
var objdumpManualTests = `
|
||||
6d746162
|
||||
4c040000
|
||||
88000017
|
||||
`
|
||||
|
||||
// allowedMismatchObjdump reports whether the mismatch between text and dec
|
||||
// should be allowed by the test.
|
||||
func allowedMismatchObjdump(text string, size int, inst *Inst, dec ExtInst) bool {
|
||||
if hasPrefix(dec.text, deleted...) {
|
||||
return true
|
||||
}
|
||||
|
||||
// we support more instructions than binutils
|
||||
if strings.Contains(dec.text, ".long") {
|
||||
return true
|
||||
}
|
||||
|
||||
if hasPrefix(text, "error:") {
|
||||
if hasPrefix(dec.text, unsupported...) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
switch inst.Op {
|
||||
case BC, BCA, BL, BLA, BCL, BCLA, TDI, TWI, TW, TD:
|
||||
return true // TODO(minux): we lack the support for extended opcodes here
|
||||
case RLWNM, RLWNM_, RLDICL, RLDICL_, RLWINM, RLWINM_, RLDCL, RLDCL_:
|
||||
return true // TODO(minux): we lack the support for extended opcodes here
|
||||
case DCBTST, DCBT:
|
||||
return true // objdump uses the embedded argument order, we use the server argument order
|
||||
case MTFSF, MTFSF_: // objdump doesn't show the last two arguments
|
||||
return true
|
||||
case VSPLTB, VSPLTH, VSPLTW: // objdump generates unreasonable result "vspltw v6,v19,4" for 10c49a8c, the last 4 should be 0.
|
||||
return true
|
||||
}
|
||||
if hasPrefix(text, "evm", "evl", "efs") { // objdump will disassemble them wrong (e.g. evmhoumia as vsldoi)
|
||||
return true
|
||||
}
|
||||
|
||||
if len(dec.enc) >= 4 {
|
||||
_ = binary.BigEndian.Uint32(dec.enc[:4])
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Instructions known to libopcodes (or xed) but not to us.
|
||||
// TODO(minux): those single precision instructions are missing from ppc64.csv
|
||||
// those data cache instructions are deprecated, but must be treated as no-ops, see 4.3.2.1 pg. 774.
|
||||
var unsupported = strings.Fields(`
|
||||
fmsubs
|
||||
fmsubs.
|
||||
fnmadds
|
||||
fnmadds.
|
||||
fnmsubs
|
||||
fnmsubs.
|
||||
fmuls
|
||||
fmuls.
|
||||
fdivs
|
||||
fdivs.
|
||||
fadds
|
||||
fadds.
|
||||
fsubs
|
||||
fsubs.
|
||||
dst
|
||||
dstst
|
||||
dssall
|
||||
`)
|
||||
|
||||
// Instructions explicitly dropped in Power ISA that were in POWER architecture.
|
||||
// See A.30 Deleted Instructions and A.31 Discontiued Opcodes
|
||||
var deleted = strings.Fields(`
|
||||
abs
|
||||
clcs
|
||||
clf
|
||||
cli
|
||||
dclst
|
||||
div
|
||||
divs
|
||||
doz
|
||||
dozi
|
||||
lscbx
|
||||
maskg
|
||||
maskir
|
||||
mfsri
|
||||
mul
|
||||
nabs
|
||||
rac
|
||||
rfi
|
||||
rfsvc
|
||||
rlmi
|
||||
rrib
|
||||
sle
|
||||
sleq
|
||||
sliq
|
||||
slliq
|
||||
sllq
|
||||
slq
|
||||
sraiq
|
||||
sraq
|
||||
sre
|
||||
srea
|
||||
sreq
|
||||
sriq
|
||||
srliq
|
||||
srlq
|
||||
srq
|
||||
maskg`)
|
|
@ -0,0 +1,251 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
// Copied and simplified from rsc.io/arm/armasm/objdumpext_test.go.
|
||||
|
||||
package ppc64asm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/elf"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const objdumpPath = "/usr/local/bin/powerpc64-unknown-linux-gnu-objdump"
|
||||
|
||||
func testObjdump(t *testing.T, generate func(func([]byte))) {
|
||||
if _, err := os.Stat(objdumpPath); err != nil {
|
||||
if !testing.Short() {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Skip(err)
|
||||
}
|
||||
|
||||
testExtDis(t, "gnu", objdump, generate, allowedMismatchObjdump)
|
||||
}
|
||||
|
||||
func objdump(ext *ExtDis) error {
|
||||
// File already written with instructions; add ELF header.
|
||||
if err := writeELF64(ext.File, ext.Size); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b, err := ext.Run(objdumpPath, "-d", "-z", ext.File.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
nmatch int
|
||||
reading bool
|
||||
next uint32 = start
|
||||
addr uint32
|
||||
encbuf [4]byte
|
||||
enc []byte
|
||||
text string
|
||||
)
|
||||
flush := func() {
|
||||
if addr == next {
|
||||
if m := pcrel.FindStringSubmatch(text); m != nil {
|
||||
targ, _ := strconv.ParseUint(m[2], 16, 64)
|
||||
text = fmt.Sprintf("%s.%+#x", m[1], int32(uint32(targ)-addr))
|
||||
}
|
||||
if strings.HasPrefix(text, "stmia") {
|
||||
text = "stm" + text[5:]
|
||||
}
|
||||
if strings.HasPrefix(text, "stmfd") {
|
||||
text = "stmdb" + text[5:]
|
||||
}
|
||||
if strings.HasPrefix(text, "ldmfd") {
|
||||
text = "ldm" + text[5:]
|
||||
}
|
||||
text = strings.Replace(text, "#0.0", "#0", -1)
|
||||
if text == "undefined" && len(enc) == 4 {
|
||||
text = "error: unknown instruction"
|
||||
enc = nil
|
||||
}
|
||||
if len(enc) == 4 {
|
||||
// prints as word but we want to record bytes
|
||||
enc[0], enc[3] = enc[3], enc[0]
|
||||
enc[1], enc[2] = enc[2], enc[1]
|
||||
}
|
||||
ext.Dec <- ExtInst{addr, encbuf, len(enc), text}
|
||||
encbuf = [4]byte{}
|
||||
enc = nil
|
||||
next += 4
|
||||
}
|
||||
}
|
||||
var textangle = []byte("<.text>:")
|
||||
for {
|
||||
line, err := b.ReadSlice('\n')
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return fmt.Errorf("reading objdump output: %v", err)
|
||||
}
|
||||
if bytes.Contains(line, textangle) {
|
||||
reading = true
|
||||
continue
|
||||
}
|
||||
if !reading {
|
||||
continue
|
||||
}
|
||||
if debug {
|
||||
os.Stdout.Write(line)
|
||||
}
|
||||
if enc1 := parseContinuation(line, encbuf[:len(enc)]); enc1 != nil {
|
||||
enc = enc1
|
||||
continue
|
||||
}
|
||||
flush()
|
||||
nmatch++
|
||||
addr, enc, text = parseLine(line, encbuf[:0])
|
||||
if addr > next {
|
||||
return fmt.Errorf("address out of sync expected <= %#x at %q in:\n%s", next, line, line)
|
||||
}
|
||||
}
|
||||
flush()
|
||||
if next != start+uint32(ext.Size) {
|
||||
return fmt.Errorf("not enough results found [%d %d]", next, start+ext.Size)
|
||||
}
|
||||
if err := ext.Wait(); err != nil {
|
||||
return fmt.Errorf("exec: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
undefined = []byte("<UNDEFINED>")
|
||||
unpredictable = []byte("<UNPREDICTABLE>")
|
||||
illegalShifter = []byte("<illegal shifter operand>")
|
||||
)
|
||||
|
||||
func parseLine(line []byte, encstart []byte) (addr uint32, enc []byte, text string) {
|
||||
oline := line
|
||||
i := index(line, ":\t")
|
||||
if i < 0 {
|
||||
log.Fatalf("cannot parse disassembly: %q", oline)
|
||||
}
|
||||
x, err := strconv.ParseUint(string(trimSpace(line[:i])), 16, 32)
|
||||
if err != nil {
|
||||
log.Fatalf("cannot parse disassembly: %q", oline)
|
||||
}
|
||||
addr = uint32(x)
|
||||
line = line[i+2:]
|
||||
i = bytes.IndexByte(line, '\t')
|
||||
if i < 0 {
|
||||
log.Fatalf("cannot parse disassembly: %q", oline)
|
||||
}
|
||||
enc, ok := parseHex(line[:i], encstart)
|
||||
if !ok {
|
||||
log.Fatalf("cannot parse disassembly: %q", oline)
|
||||
}
|
||||
line = trimSpace(line[i:])
|
||||
if bytes.Contains(line, undefined) {
|
||||
text = "undefined"
|
||||
return
|
||||
}
|
||||
if bytes.Contains(line, illegalShifter) {
|
||||
text = "undefined"
|
||||
return
|
||||
}
|
||||
if false && bytes.Contains(line, unpredictable) {
|
||||
text = "unpredictable"
|
||||
return
|
||||
}
|
||||
if i := bytes.IndexByte(line, ';'); i >= 0 {
|
||||
line = trimSpace(line[:i])
|
||||
}
|
||||
text = string(fixSpace(line))
|
||||
return
|
||||
}
|
||||
|
||||
func parseContinuation(line []byte, enc []byte) []byte {
|
||||
i := index(line, ":\t")
|
||||
if i < 0 {
|
||||
return nil
|
||||
}
|
||||
line = line[i+1:]
|
||||
enc, _ = parseHex(line, enc)
|
||||
return enc
|
||||
}
|
||||
|
||||
// writeELF64 writes an ELF64 header to the file,
|
||||
// describing a text segment that starts at start
|
||||
// and extends for size bytes.
|
||||
func writeELF64(f *os.File, size int) error {
|
||||
f.Seek(0, 0)
|
||||
var hdr elf.Header64
|
||||
var prog elf.Prog64
|
||||
var sect elf.Section64
|
||||
var buf bytes.Buffer
|
||||
binary.Write(&buf, binary.BigEndian, &hdr)
|
||||
off1 := buf.Len()
|
||||
binary.Write(&buf, binary.BigEndian, &prog)
|
||||
off2 := buf.Len()
|
||||
binary.Write(&buf, binary.BigEndian, §)
|
||||
off3 := buf.Len()
|
||||
buf.Reset()
|
||||
data := byte(elf.ELFDATA2MSB)
|
||||
hdr = elf.Header64{
|
||||
Ident: [16]byte{0x7F, 'E', 'L', 'F', 2, data, 1},
|
||||
Type: 2,
|
||||
Machine: uint16(elf.EM_PPC64),
|
||||
Version: 1,
|
||||
Entry: start,
|
||||
Phoff: uint64(off1),
|
||||
Shoff: uint64(off2),
|
||||
Flags: 0x05000002,
|
||||
Ehsize: uint16(off1),
|
||||
Phentsize: uint16(off2 - off1),
|
||||
Phnum: 1,
|
||||
Shentsize: uint16(off3 - off2),
|
||||
Shnum: 3,
|
||||
Shstrndx: 2,
|
||||
}
|
||||
binary.Write(&buf, binary.BigEndian, &hdr)
|
||||
prog = elf.Prog64{
|
||||
Type: 1,
|
||||
Off: start,
|
||||
Vaddr: start,
|
||||
Paddr: start,
|
||||
Filesz: uint64(size),
|
||||
Memsz: uint64(size),
|
||||
Flags: 5,
|
||||
Align: start,
|
||||
}
|
||||
binary.Write(&buf, binary.BigEndian, &prog)
|
||||
binary.Write(&buf, binary.BigEndian, §) // NULL section
|
||||
sect = elf.Section64{
|
||||
Name: 1,
|
||||
Type: uint32(elf.SHT_PROGBITS),
|
||||
Addr: start,
|
||||
Off: start,
|
||||
Size: uint64(size),
|
||||
Flags: uint64(elf.SHF_ALLOC | elf.SHF_EXECINSTR),
|
||||
Addralign: 4,
|
||||
}
|
||||
binary.Write(&buf, binary.BigEndian, §) // .text
|
||||
sect = elf.Section64{
|
||||
Name: uint32(len("\x00.text\x00")),
|
||||
Type: uint32(elf.SHT_STRTAB),
|
||||
Addr: 0,
|
||||
Off: uint64(off2 + (off3-off2)*3),
|
||||
Size: uint64(len("\x00.text\x00.shstrtab\x00")),
|
||||
Addralign: 1,
|
||||
}
|
||||
binary.Write(&buf, binary.BigEndian, §)
|
||||
buf.WriteString("\x00.text\x00.shstrtab\x00")
|
||||
f.Write(buf.Bytes())
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
// Copyright 2015 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 ppc64asm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// plan9Syntax returns the Go assembler syntax for the instruction.
|
||||
// The syntax was originally defined by Plan 9.
|
||||
// The pc is the program counter of the first instruction, used for expanding
|
||||
// PC-relative addresses into absolute ones.
|
||||
// The symname function queries the symbol table for the program
|
||||
// being disassembled. It returns the name and base address of the symbol
|
||||
// containing the target, if any; otherwise it returns "", 0.
|
||||
func plan9Syntax(inst Inst, pc uint64, symname func(uint64) (string, uint64)) string {
|
||||
if symname == nil {
|
||||
symname = func(uint64) (string, uint64) { return "", 0 }
|
||||
}
|
||||
if inst.Op == 0 {
|
||||
return "?"
|
||||
}
|
||||
var args []string
|
||||
for i, a := range inst.Args[:] {
|
||||
if a == nil {
|
||||
break
|
||||
}
|
||||
if s := plan9Arg(&inst, i, pc, a, symname); s != "" {
|
||||
args = append(args, s)
|
||||
}
|
||||
}
|
||||
var op string
|
||||
op = plan9OpMap[inst.Op]
|
||||
if op == "" {
|
||||
op = strings.ToUpper(inst.Op.String())
|
||||
}
|
||||
// laid out the instruction
|
||||
switch inst.Op {
|
||||
default: // dst, sA, sB, ...
|
||||
if len(args) == 0 {
|
||||
return op
|
||||
} else if len(args) == 1 {
|
||||
return fmt.Sprintf("%s %s", op, args[0])
|
||||
}
|
||||
args = append(args, args[0])
|
||||
return op + " " + strings.Join(args[1:], ", ")
|
||||
// store instructions always have the memory operand at the end, no need to reorder
|
||||
case STB, STBU, STBX, STBUX,
|
||||
STH, STHU, STHX, STHUX,
|
||||
STW, STWU, STWX, STWUX,
|
||||
STD, STDU, STDX, STDUX,
|
||||
STQ,
|
||||
STHBRX, STWBRX:
|
||||
return op + " " + strings.Join(args, ", ")
|
||||
// branch instructions needs additional handling
|
||||
case BCLR:
|
||||
if int(inst.Args[0].(Imm))&20 == 20 { // unconditional
|
||||
return "RET"
|
||||
}
|
||||
return op + " " + strings.Join(args, ", ")
|
||||
case BC:
|
||||
if int(inst.Args[0].(Imm))&0x1c == 12 { // jump on cond bit set
|
||||
return fmt.Sprintf("B%s %s", args[1], args[2])
|
||||
} else if int(inst.Args[0].(Imm))&0x1c == 4 && revCondMap[args[1]] != "" { // jump on cond bit not set
|
||||
return fmt.Sprintf("B%s %s", revCondMap[args[1]], args[2])
|
||||
}
|
||||
case BCCTR:
|
||||
if int(inst.Args[0].(Imm))&20 == 20 { // unconditional
|
||||
return "BR (CTR)"
|
||||
}
|
||||
return op + " " + strings.Join(args, ", ")
|
||||
case BCCTRL:
|
||||
if int(inst.Args[0].(Imm))&20 == 20 { // unconditional
|
||||
return "BL (CTR)"
|
||||
}
|
||||
return op + " " + strings.Join(args, ", ")
|
||||
case BCA, BCL, BCLA, BCLRL, BCTAR, BCTARL:
|
||||
return op + " " + strings.Join(args, ", ")
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// plan9Arg formats arg (which is the argIndex's arg in inst) according to Plan 9 rules.
|
||||
// NOTE: because Plan9Syntax is the only caller of this func, and it receives a copy
|
||||
// of inst, it's ok to modify inst.Args here.
|
||||
func plan9Arg(inst *Inst, argIndex int, pc uint64, arg Arg, symname func(uint64) (string, uint64)) string {
|
||||
// special cases for load/store instructions
|
||||
if _, ok := arg.(Offset); ok {
|
||||
if argIndex+1 == len(inst.Args) || inst.Args[argIndex+1] == nil {
|
||||
panic(fmt.Errorf("wrong table: offset not followed by register"))
|
||||
}
|
||||
}
|
||||
switch arg := arg.(type) {
|
||||
case Reg:
|
||||
if isLoadStoreOp(inst.Op) && argIndex == 1 && arg == R0 {
|
||||
return "0"
|
||||
}
|
||||
if arg == R30 {
|
||||
return "g"
|
||||
}
|
||||
return strings.ToUpper(arg.String())
|
||||
case CondReg:
|
||||
if arg == CR0 && strings.HasPrefix(inst.Op.String(), "cmp") {
|
||||
return "" // don't show cr0 for cmp instructions
|
||||
} else if arg >= CR0 {
|
||||
return fmt.Sprintf("CR%d", int(arg-CR0))
|
||||
}
|
||||
bit := [4]string{"LT", "GT", "EQ", "SO"}[(arg-Cond0LT)%4]
|
||||
if arg <= Cond0SO {
|
||||
return bit
|
||||
}
|
||||
return fmt.Sprintf("4*CR%d+%s", int(arg-Cond0LT)/4, bit)
|
||||
case Imm:
|
||||
return fmt.Sprintf("$%d", arg)
|
||||
case SpReg:
|
||||
switch arg {
|
||||
case 8:
|
||||
return "LR"
|
||||
case 9:
|
||||
return "CTR"
|
||||
}
|
||||
return fmt.Sprintf("SPR(%d)", int(arg))
|
||||
case PCRel:
|
||||
addr := pc + uint64(int64(arg))
|
||||
if s, base := symname(addr); s != "" && base == addr {
|
||||
return fmt.Sprintf("%s(SB)", s)
|
||||
}
|
||||
return fmt.Sprintf("%#x", addr)
|
||||
case Label:
|
||||
return fmt.Sprintf("%#x", int(arg))
|
||||
case Offset:
|
||||
reg := inst.Args[argIndex+1].(Reg)
|
||||
removeArg(inst, argIndex+1)
|
||||
if reg == R0 {
|
||||
return fmt.Sprintf("%d(0)", int(arg))
|
||||
}
|
||||
return fmt.Sprintf("%d(R%d)", int(arg), reg-R0)
|
||||
}
|
||||
return fmt.Sprintf("???(%v)", arg)
|
||||
}
|
||||
|
||||
// revCondMap maps a conditional register bit to its inverse, if possible.
|
||||
var revCondMap = map[string]string{
|
||||
"LT": "GE", "GT": "LE", "EQ": "NE",
|
||||
}
|
||||
|
||||
// plan9OpMap maps an Op to its Plan 9 mnemonics, if different than its GNU mnemonics.
|
||||
var plan9OpMap = map[Op]string{
|
||||
LBZ: "MOVBZ", STB: "MOVB",
|
||||
LBZU: "MOVBZU", STBU: "MOVBU", // TODO(minux): indexed forms are not handled
|
||||
LHZ: "MOVHZ", LHA: "MOVH", STH: "MOVH",
|
||||
LHZU: "MOVHZU", STHU: "MOVHU",
|
||||
LWZ: "MOVWZ", LWA: "MOVW", STW: "MOVW",
|
||||
LWZU: "MOVWZU", STWU: "MOVWU",
|
||||
LD: "MOVD", STD: "MOVD",
|
||||
LDU: "MOVDU", STDU: "MOVDU",
|
||||
MTSPR: "MOV", MFSPR: "MOV", // the width is ambiguous for SPRs
|
||||
B: "BR",
|
||||
CMPLD: "CMPU", CMPLW: "CMPWU",
|
||||
CMPD: "CMP", CMPW: "CMPW",
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,25 @@
|
|||
6d746162| gnu xoris r20,r11,24930
|
||||
4c040000| gnu mcrf cr0,cr1
|
||||
88000017| gnu lbz r0,23(0)
|
||||
4abaa88a| gnu ba 0xfebaa888
|
||||
7d8fc2a6| gnu mfspr r12,783
|
||||
00000000| gnu error: unknown instruction
|
||||
a1841e80| gnu lhz r12,7808(r4)
|
||||
42093d10| gnu bc 16,4*cr2+gt,.+0x3d10
|
||||
e38d5b90| gnu lq r28,23440(r13)
|
||||
84127a20| gnu lwzu r0,31264(r18)
|
||||
c61bb730| gnu lfsu f16,-18640(r27)
|
||||
0825f440| gnu tdi 1,r5,-3008
|
||||
a9a912c1| gnu lha r13,4801(r9)
|
||||
ebb24fd1| gnu ldu r29,20432(r18)
|
||||
b1ce0612| gnu sth r14,1554(r14)
|
||||
f3d74322| gnu xvcvdpuxws v30,v40
|
||||
945c62a2| gnu stwu r2,25250(r28)
|
||||
9c8156e3| gnu stbu r4,22243(r1)
|
||||
f91b9c7a| gnu stq r8,-25480(r27)
|
||||
2c1c81b4| gnu cmpwi r28,-32332
|
||||
f87b904d| gnu stdu r3,-28596(r27)
|
||||
eab3c832| gnu lwa r21,-14288(r19)
|
||||
4320336b| gnu bcla 25,lt,0x3368
|
||||
7e40092e| gnu stwx r18,0,r1
|
||||
7c103c2c| gnu lwbrx r0,r16,r7
|
|
@ -0,0 +1,592 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
// Power64map constructs the Power64 opcode map from the instruction set CSV file.
|
||||
//
|
||||
// Usage:
|
||||
// ppc64map [-fmt=format] ppc64.csv
|
||||
//
|
||||
// The known output formats are:
|
||||
//
|
||||
// text (default) - print decoding tree in text form
|
||||
// decoder - print decoding tables for the ppc64asm package
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/csv"
|
||||
"flag"
|
||||
"fmt"
|
||||
gofmt "go/format"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
asm "golang.org/x/arch/ppc64/ppc64asm"
|
||||
)
|
||||
|
||||
var format = flag.String("fmt", "text", "output format: text, decoder")
|
||||
var debug = flag.Bool("debug", false, "enable debugging output")
|
||||
|
||||
var inputFile string
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintf(os.Stderr, "usage: ppc64map [-fmt=format] ppc64.csv\n")
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.SetFlags(0)
|
||||
log.SetPrefix("ppc64map: ")
|
||||
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
if flag.NArg() != 1 {
|
||||
usage()
|
||||
}
|
||||
|
||||
inputFile = flag.Arg(0)
|
||||
|
||||
var print func(*Prog)
|
||||
switch *format {
|
||||
default:
|
||||
log.Fatalf("unknown output format %q", *format)
|
||||
case "text":
|
||||
print = printText
|
||||
case "decoder":
|
||||
print = printDecoder
|
||||
}
|
||||
|
||||
p, err := readCSV(flag.Arg(0))
|
||||
log.Printf("Parsed %d instruction forms.", len(p.Insts))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
print(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
|
||||
}
|
||||
b := bufio.NewReader(f)
|
||||
for {
|
||||
c, err := b.ReadByte()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if c == '\n' {
|
||||
continue
|
||||
}
|
||||
if c == '#' {
|
||||
b.ReadBytes('\n')
|
||||
continue
|
||||
}
|
||||
b.UnreadByte()
|
||||
break
|
||||
}
|
||||
table, err := csv.NewReader(b).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]) < 4 {
|
||||
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
|
||||
}
|
||||
|
||||
type Field struct {
|
||||
Name string
|
||||
BitFields asm.BitFields
|
||||
Type asm.ArgType
|
||||
Shift uint8
|
||||
}
|
||||
|
||||
func (f Field) String() string {
|
||||
return fmt.Sprintf("%v(%s%v)", f.Type, f.Name, f.BitFields)
|
||||
}
|
||||
|
||||
type Inst struct {
|
||||
Text string
|
||||
Encoding string
|
||||
Op string
|
||||
Mask uint32
|
||||
Value uint32
|
||||
DontCare uint32
|
||||
Fields []Field
|
||||
}
|
||||
|
||||
func (i Inst) String() string {
|
||||
return fmt.Sprintf("%s (%s) %08x/%08x[%08x] %v (%s)", i.Op, i.Encoding, i.Value, i.Mask, i.DontCare, 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() uint32 {
|
||||
return uint32(a.Maximum()) << a.Shift()
|
||||
}
|
||||
|
||||
func (a Arg) Shift() uint8 {
|
||||
return uint8(32 - 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))
|
||||
}
|
||||
|
||||
// add adds the entry from the CSV described by text, mnemonics, encoding, and tags
|
||||
// to the program p.
|
||||
func add(p *Prog, text, mnemonics, encoding, tags string) {
|
||||
if strings.HasPrefix(mnemonics, "e_") || strings.HasPrefix(mnemonics, "se_") {
|
||||
// TODO(minux): VLE instructions are ignored.
|
||||
return
|
||||
}
|
||||
|
||||
// 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
|
||||
fields := strings.Split(encoding, "|")
|
||||
for i, f := range fields {
|
||||
name, off := "", -1
|
||||
if f == "" {
|
||||
off = 32
|
||||
if i == 0 || i != len(fields)-1 {
|
||||
fmt.Fprintf(os.Stderr, "%s: wrong %d-th encoding field: %q\n", text, i, f)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
j := strings.Index(f, "@")
|
||||
if j < 0 {
|
||||
fmt.Fprintf(os.Stderr, "%s: wrong %d-th encoding field: %q\n", text, i, f)
|
||||
continue
|
||||
}
|
||||
off, _ = strconv.Atoi(f[j+1:])
|
||||
name = f[:j]
|
||||
}
|
||||
if len(args) > 0 {
|
||||
args[len(args)-1].Bits += int8(off)
|
||||
}
|
||||
if name != "" {
|
||||
arg := Arg{Name: name, Offs: int8(off), Bits: int8(-off)}
|
||||
args.Append(arg)
|
||||
}
|
||||
}
|
||||
|
||||
var mask, value, dontCare uint32
|
||||
for i := 0; i < len(args); i++ {
|
||||
arg := args[i]
|
||||
v, err := strconv.Atoi(arg.Name)
|
||||
switch {
|
||||
case err == nil: // 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 |= uint32(v) << arg.Shift()
|
||||
args.Delete(i)
|
||||
i--
|
||||
case arg.Name[0] == '/': // is 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)
|
||||
}
|
||||
dontCare |= arg.BitMask()
|
||||
args.Delete(i)
|
||||
i--
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// rename duplicated fields (e.g. 30@0|RS@6|RA@11|sh@16|mb@21|0@27|sh@30|Rc@31|)
|
||||
// but only support two duplicated fields
|
||||
for i := 1; i < len(args); i++ {
|
||||
if args[:i].Find(args[i].Name) >= 0 {
|
||||
args[i].Name += "2"
|
||||
}
|
||||
if args[:i].Find(args[i].Name) >= 0 {
|
||||
log.Fatalf("%s: more than one duplicated fields: %s", text, args)
|
||||
}
|
||||
}
|
||||
|
||||
// sanity checks
|
||||
if mask&dontCare != 0 {
|
||||
log.Fatalf("%s: mask (%08x) and don't care (%08x) collide", text, mask, dontCare)
|
||||
}
|
||||
if value&^mask != 0 {
|
||||
log.Fatalf("%s: value (%08x) out of range of mask (%08x)", text, value, mask)
|
||||
}
|
||||
var argMask uint32
|
||||
for _, arg := range args {
|
||||
if arg.Bits <= 0 || arg.Bits > 32 || arg.Offs > 31 || arg.Offs <= 0 {
|
||||
log.Fatalf("%s: arg %v has wrong bit field spec", text, arg)
|
||||
}
|
||||
if mask&arg.BitMask() != 0 {
|
||||
log.Fatalf("%s: mask (%08x) intersect with arg %v", text, mask, arg)
|
||||
}
|
||||
if argMask&arg.BitMask() != 0 {
|
||||
log.Fatalf("%s: arg %v overlap with other args %v", text, arg, args)
|
||||
}
|
||||
argMask |= arg.BitMask()
|
||||
}
|
||||
if 1<<32-1 != mask|dontCare|argMask {
|
||||
log.Fatalf("%s: args %v fail to cover all 32 bits", text, args)
|
||||
}
|
||||
|
||||
// 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)"
|
||||
insts := strings.Split(categoryRe.ReplaceAllString(mnemonics, ""), "|")
|
||||
for _, inst := range insts {
|
||||
value, mask := value, mask
|
||||
args := args.Clone()
|
||||
if inst == "" {
|
||||
continue
|
||||
}
|
||||
// amend mask and value
|
||||
parts := instRe.FindStringSubmatch(inst)
|
||||
if parts == nil {
|
||||
log.Fatalf("%v couldn't match %s", instRe, inst)
|
||||
}
|
||||
conds := condRe.FindAllStringSubmatch(parts[2], -1)
|
||||
isPCRel := true
|
||||
for _, cond := range conds {
|
||||
i := args.Find(cond[1])
|
||||
v, _ := strconv.ParseInt(cond[2], 16, 32) // the regular expression has checked the number format
|
||||
if i < 0 {
|
||||
log.Fatalf("%s: %s don't contain arg %s used in %s", text, args, cond[1], inst)
|
||||
}
|
||||
if cond[1] == "AA" && v == 1 {
|
||||
isPCRel = false
|
||||
}
|
||||
mask |= args[i].BitMask()
|
||||
value |= uint32(v) << args[i].Shift()
|
||||
args.Delete(i)
|
||||
}
|
||||
inst := Inst{Text: text, Encoding: parts[1], Value: value, Mask: mask, DontCare: dontCare}
|
||||
|
||||
// order inst.Args according to mnemonics order
|
||||
for i, opr := range operandRe.FindAllString(parts[1], -1) {
|
||||
if i == 0 { // operation
|
||||
inst.Op = opr
|
||||
continue
|
||||
}
|
||||
field := Field{Name: opr}
|
||||
typ := asm.TypeUnknown
|
||||
var shift uint8
|
||||
opr2 := ""
|
||||
switch opr {
|
||||
case "target_addr":
|
||||
shift = 2
|
||||
if isPCRel {
|
||||
typ = asm.TypePCRel
|
||||
} else {
|
||||
typ = asm.TypeLabel
|
||||
}
|
||||
if args.Find("LI") >= 0 {
|
||||
opr = "LI"
|
||||
} else {
|
||||
opr = "BD"
|
||||
}
|
||||
case "UI", "BO", "BH", "TH", "LEV", "NB", "L", "TO", "FXM", "U", "W", "FLM", "UIM", "SHB", "SHW", "ST", "SIX", "PS", "DCM", "DGM", "RMC", "R", "SP", "S", "DM", "CT", "EH", "E", "MO", "WC", "A", "IH", "OC", "DUI", "DUIS":
|
||||
typ = asm.TypeImmUnsigned
|
||||
if i := args.Find(opr); i < 0 {
|
||||
opr = "D"
|
||||
}
|
||||
case "SH":
|
||||
typ = asm.TypeImmUnsigned
|
||||
if args.Find("sh2") >= 0 { // sh2 || sh
|
||||
opr = "sh2"
|
||||
opr2 = "sh"
|
||||
}
|
||||
case "MB", "ME":
|
||||
typ = asm.TypeImmUnsigned
|
||||
if n := strings.ToLower(opr); args.Find(n) >= 0 {
|
||||
opr = n // xx[5] || xx[0:4]
|
||||
}
|
||||
case "SI", "SIM", "TE":
|
||||
typ = asm.TypeImmSigned
|
||||
if i := args.Find(opr); i < 0 {
|
||||
opr = "D"
|
||||
}
|
||||
case "DS":
|
||||
typ = asm.TypeOffset
|
||||
shift = 2
|
||||
case "DQ":
|
||||
typ = asm.TypeOffset
|
||||
shift = 4
|
||||
case "D":
|
||||
if i := args.Find(opr); i >= 0 {
|
||||
typ = asm.TypeOffset
|
||||
break
|
||||
}
|
||||
if i := args.Find("UI"); i >= 0 {
|
||||
typ = asm.TypeImmUnsigned
|
||||
opr = "UI"
|
||||
break
|
||||
}
|
||||
if i := args.Find("SI"); i >= 0 {
|
||||
typ = asm.TypeImmSigned
|
||||
opr = "SI"
|
||||
break
|
||||
}
|
||||
case "RA", "RB", "RS", "RSp", "RT", "RTp":
|
||||
typ = asm.TypeReg
|
||||
case "BT", "BA", "BB", "BC", "BI":
|
||||
typ = asm.TypeCondRegBit
|
||||
case "BF", "BFA":
|
||||
typ = asm.TypeCondRegField
|
||||
case "FRA", "FRB", "FRBp", "FRC", "FRS", "FRSp", "FRT", "FRTp":
|
||||
typ = asm.TypeFPReg
|
||||
case "XA", "XB", "XC", "XS", "XT": // 5-bit, split field
|
||||
typ = asm.TypeVecReg
|
||||
opr2 = opr[1:]
|
||||
opr = opr[1:] + "X"
|
||||
case "VRA", "VRB", "VRC", "VRS", "VRT":
|
||||
typ = asm.TypeVecReg
|
||||
case "SPR", "DCRN", "BHRBE", "TBR", "SR", "TMR", "PMRN": // Note: if you add to this list and the register field needs special handling, add it to switch statement below
|
||||
typ = asm.TypeSpReg
|
||||
switch opr {
|
||||
case "BHRBE":
|
||||
opr = "bhrbe" // no special handling
|
||||
case "DCRN":
|
||||
opr = "DCR"
|
||||
}
|
||||
if n := strings.ToLower(opr); n != opr && args.Find(n) >= 0 {
|
||||
opr = n // spr[5:9] || spr[0:4]
|
||||
}
|
||||
}
|
||||
if typ == asm.TypeUnknown {
|
||||
log.Fatalf("%s %s unknown type for opr %s", text, inst, opr)
|
||||
}
|
||||
field.Type = typ
|
||||
field.Shift = shift
|
||||
var f1, f2 asm.BitField
|
||||
switch {
|
||||
case opr2 != "":
|
||||
ext := args.Find(opr)
|
||||
if ext < 0 {
|
||||
log.Fatalf("%s: couldn't find extended field %s in %s", text, opr, args)
|
||||
}
|
||||
f1.Offs, f1.Bits = uint8(args[ext].Offs), uint8(args[ext].Bits)
|
||||
base := args.Find(opr2)
|
||||
if base < 0 {
|
||||
log.Fatalf("%s: couldn't find base field %s in %s", text, opr2, args)
|
||||
}
|
||||
f2.Offs, f2.Bits = uint8(args[base].Offs), uint8(args[base].Bits)
|
||||
case opr == "mb", opr == "me": // xx[5] || xx[0:4]
|
||||
i := args.Find(opr)
|
||||
if i < 0 {
|
||||
log.Fatalf("%s: couldn't find special 'm[be]' field for %s in %s", text, opr, args)
|
||||
}
|
||||
f1.Offs, f1.Bits = uint8(args[i].Offs+args[i].Bits)-1, 1
|
||||
f2.Offs, f2.Bits = uint8(args[i].Offs), uint8(args[i].Bits)-1
|
||||
case opr == "spr", opr == "tbr", opr == "tmr", opr == "dcr": // spr[5:9] || spr[0:4]
|
||||
i := args.Find(opr)
|
||||
if i < 0 {
|
||||
log.Fatalf("%s: couldn't find special 'spr' field for %s in %s", text, opr, args)
|
||||
}
|
||||
if args[i].Bits != 10 {
|
||||
log.Fatalf("%s: special 'spr' field is not 10-bit: %s", text, args)
|
||||
}
|
||||
f1.Offs, f1.Bits = uint8(args[i].Offs)+5, 5
|
||||
f2.Offs, f2.Bits = uint8(args[i].Offs), 5
|
||||
default:
|
||||
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.BitFields.Append(f1)
|
||||
if f2.Bits > 0 {
|
||||
field.BitFields.Append(f2)
|
||||
}
|
||||
inst.Fields = append(inst.Fields, field)
|
||||
}
|
||||
if *debug {
|
||||
fmt.Printf("%v\n", inst)
|
||||
}
|
||||
|
||||
p.Insts = append(p.Insts, inst)
|
||||
}
|
||||
}
|
||||
|
||||
// condRegexp is a regular expression that matches condition in mnemonics (e.g. "AA=1")
|
||||
const condRegexp = `\s*([[:alpha:]]+)=([0-9a-f]+)\s*`
|
||||
|
||||
// condRe matches condition in mnemonics (e.g. "AA=1")
|
||||
var condRe = regexp.MustCompile(condRegexp)
|
||||
|
||||
// instRe matches instruction with potentially multiple conditions in mnemonics
|
||||
var instRe = regexp.MustCompile(`^(.*?)\s?(\((` + condRegexp + `)+\))?$`)
|
||||
|
||||
// categoryRe matches intruction category notices in mnemonics
|
||||
var categoryRe = regexp.MustCompile(`(\s*\[Category:[^]]*\]\s*)|(\s*\[Co-requisite[^]]*\]\s*)|(\s*\(\s*0[Xx][[0-9A-Fa-f_]{9}\s*\)\s*)`)
|
||||
|
||||
// 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")
|
||||
}
|
||||
|
||||
// opName translate an opcode to a valid Go identifier all-cap op name.
|
||||
func opName(op string) string {
|
||||
return strings.ToUpper(strings.Replace(op, ".", "_", 1))
|
||||
}
|
||||
|
||||
// argFieldName constructs a name for the argField
|
||||
func argFieldName(f Field) string {
|
||||
ns := []string{"ap", f.Type.String()}
|
||||
for _, b := range f.BitFields {
|
||||
ns = append(ns, fmt.Sprintf("%d_%d", b.Offs, b.Offs+b.Bits-1))
|
||||
}
|
||||
if f.Shift > 0 {
|
||||
ns = append(ns, fmt.Sprintf("shift%d", f.Shift))
|
||||
}
|
||||
return strings.Join(ns, "_")
|
||||
}
|
||||
|
||||
var funcBodyTmpl = template.Must(template.New("funcBody").Parse(``))
|
||||
|
||||
// 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, "package ppc64asm\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 _, inst := range p.Insts {
|
||||
name := opName(inst.Op)
|
||||
if ok := m[name]; ok {
|
||||
continue
|
||||
}
|
||||
m[name] = true
|
||||
fmt.Fprintf(&buf, "\t%s\n", name)
|
||||
}
|
||||
fmt.Fprintln(&buf, ")\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 := opName(inst.Op)
|
||||
if ok := m[name]; ok {
|
||||
continue
|
||||
}
|
||||
m[name] = true
|
||||
fmt.Fprintf(&buf, "\t%s: %q,\n", opName(inst.Op), inst.Op)
|
||||
}
|
||||
fmt.Fprintln(&buf, "}\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, Shift: %d, BitFields: BitFields{", name, f.Type, f.Shift)
|
||||
for _, b := range f.BitFields {
|
||||
fmt.Fprintf(&buf, "{%d, %d},", b.Offs, b.Bits)
|
||||
}
|
||||
fmt.Fprintf(&buf, "}}\n")
|
||||
}
|
||||
}
|
||||
fmt.Fprintln(&buf, ")\n\n")
|
||||
|
||||
// Emit decoding table.
|
||||
fmt.Fprintf(&buf, "var instFormats = [...]instFormat{\n")
|
||||
for _, inst := range p.Insts {
|
||||
fmt.Fprintf(&buf, "\t{ %s, %#x, %#x, %#x,", opName(inst.Op), inst.Mask, inst.Value, inst.DontCare)
|
||||
fmt.Fprintf(&buf, " // %s (%s)\n\t\t[5]*argField{", inst.Text, inst.Encoding)
|
||||
for _, f := range inst.Fields {
|
||||
fmt.Fprintf(&buf, "%s, ", argFieldName(f))
|
||||
}
|
||||
fmt.Fprintf(&buf, "}},\n")
|
||||
}
|
||||
fmt.Fprintln(&buf, "}\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)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,503 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
// Power64spec reads the ``Power ISA V2.07'' Manual
|
||||
// to collect instruction encoding details and writes those details to standard output
|
||||
// in CSV format.
|
||||
//
|
||||
// Usage:
|
||||
// ppc64spec PowerISA_V2.07_PUBLIC.pdf >ppc64.csv
|
||||
//
|
||||
// Each CSV line contains four fields:
|
||||
//
|
||||
// instruction
|
||||
// The instruction heading, such as "AAD imm8".
|
||||
// mnemonic
|
||||
// The instruction mnemonics, separated by | symbols.
|
||||
// encoding
|
||||
// The instruction encoding, a sequence of name@startbit| describing each bit field in turn.
|
||||
// tags
|
||||
// For now, empty.
|
||||
//
|
||||
// For more on the exact meaning of these fields, see the Power manual.
|
||||
//
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"rsc.io/pdf"
|
||||
)
|
||||
|
||||
type Inst struct {
|
||||
Name string
|
||||
Text string
|
||||
Enc string
|
||||
}
|
||||
|
||||
const debugPage = 0
|
||||
|
||||
var stdout *bufio.Writer
|
||||
|
||||
func main() {
|
||||
log.SetFlags(0)
|
||||
log.SetPrefix("armspec: ")
|
||||
|
||||
if len(os.Args) != 2 {
|
||||
fmt.Fprintf(os.Stderr, "usage: armspec file.pdf\n")
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
f, err := pdf.Open(os.Args[1])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Find instruction set reference in outline, to build instruction list.
|
||||
instList := instHeadings(f.Outline())
|
||||
if len(instList) < 200 {
|
||||
log.Fatalf("only found %d instructions in table of contents", len(instList))
|
||||
}
|
||||
|
||||
var all = []Inst{
|
||||
// Split across multiple columns and pages!
|
||||
{"Count Leading Zeros Word X-form", "cntlzw RA, RS (Rc=0)\ncntlzw. RA, RS (Rc=1)", "31@0|RS@6|RA@11|///@16|26@21|Rc@31|"},
|
||||
}
|
||||
|
||||
for j, headline := range instList {
|
||||
for _, inst := range all {
|
||||
if headline == inst.Name {
|
||||
instList[j] = ""
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scan document looking for instructions.
|
||||
// Must find exactly the ones in the outline.
|
||||
n := f.NumPage()
|
||||
for pageNum := 1; pageNum <= n; pageNum++ {
|
||||
if debugPage > 0 && pageNum != debugPage {
|
||||
continue
|
||||
}
|
||||
p := f.Page(pageNum)
|
||||
table := parsePage(pageNum, p)
|
||||
if len(table) == 0 {
|
||||
continue
|
||||
}
|
||||
InstLoop:
|
||||
for _, inst := range table {
|
||||
for j, headline := range instList {
|
||||
if inst.Name == headline {
|
||||
instList[j] = ""
|
||||
continue InstLoop
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "page %d: unexpected instruction %q\n", pageNum, inst.Name)
|
||||
}
|
||||
all = append(all, table...)
|
||||
}
|
||||
|
||||
if debugPage == 0 {
|
||||
for _, headline := range instList {
|
||||
if headline != "" {
|
||||
switch headline {
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "missing instruction %q\n", headline)
|
||||
case "CHKA": // ThumbEE
|
||||
case "CPS": // system instruction
|
||||
case "CPY": // synonym for MOV
|
||||
case "ENTERX": // ThumbEE
|
||||
case "F* (former VFP instruction mnemonics)": // synonyms
|
||||
case "HB, HBL, HBLP, HBP": // ThumbEE
|
||||
case "LEAVEX": // ThumbEE
|
||||
case "MOV (shifted register)": // pseudo instruction for ASR, LSL, LSR, ROR, and RRX
|
||||
case "NEG": // synonym for RSB
|
||||
case "RFE": // system instruction
|
||||
case "SMC (previously SMI)": // system instruction
|
||||
case "SRS": // system instruction
|
||||
case "SUBS PC, LR and related instructions": // system instruction
|
||||
case "VAND (immediate)": // pseudo instruction
|
||||
case "VCLE (register)": // pseudo instruction
|
||||
case "VCLT (register)": // pseudo instruction
|
||||
case "VORN (immediate)": // pseudo instruction
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stdout = bufio.NewWriter(os.Stdout)
|
||||
for _, inst := range all {
|
||||
fmt.Fprintf(stdout, "%q,%q,%q,%q\n", inst.Name, strings.Replace(inst.Text, "\n", "|", -1), inst.Enc, "")
|
||||
}
|
||||
stdout.Flush()
|
||||
|
||||
}
|
||||
|
||||
func instHeadings(outline pdf.Outline) []string {
|
||||
return appendInstHeadings(outline, nil)
|
||||
}
|
||||
|
||||
var instRE = regexp.MustCompile(` ([A-Z0-9]+-form|Byte|Word|Doubleword|Halfword)($| \[)`)
|
||||
var sectionRE = regexp.MustCompile(`^[0-9A-Z]+\.[0-9]`)
|
||||
|
||||
func appendInstHeadings(outline pdf.Outline, list []string) []string {
|
||||
if strings.Contains(outline.Title, "Variable Length Encoding (VLE) Encoding") {
|
||||
for _, child := range outline.Child {
|
||||
vle = appendInstHeadings(child, vle)
|
||||
}
|
||||
return list
|
||||
}
|
||||
if instRE.MatchString(outline.Title) && !sectionRE.MatchString(outline.Title) {
|
||||
list = append(list, outline.Title)
|
||||
}
|
||||
if outline.Title == "Transaction Abort Word Conditional" {
|
||||
list = append(list, outline.Title+" X-form")
|
||||
}
|
||||
for _, child := range outline.Child {
|
||||
list = appendInstHeadings(child, list)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
const inch = 72.0
|
||||
|
||||
func parsePage(num int, p pdf.Page) []Inst {
|
||||
content := p.Content()
|
||||
|
||||
var text []pdf.Text
|
||||
for _, t := range content.Text {
|
||||
text = append(text, t)
|
||||
}
|
||||
|
||||
text = findWords(text)
|
||||
|
||||
if debugPage > 0 {
|
||||
for _, t := range text {
|
||||
fmt.Println(t)
|
||||
}
|
||||
for _, r := range content.Rect {
|
||||
fmt.Println(r)
|
||||
}
|
||||
}
|
||||
|
||||
// Look for instruction encodings.
|
||||
// Some begin with a Helvetica-BoldOblique size 11 headline like "AND X-Form",
|
||||
// is followed by Helvetica 9 mnemonic, and then a bit box with
|
||||
// Helvetica 9 fields and Helvetica 7 bit offsets.
|
||||
// Others use Arial,BoldItalic 11 for the headline,
|
||||
// Arial 8 for the mnemonic, and Arial 4.2 for the bit offsets.
|
||||
|
||||
var insts []Inst
|
||||
for {
|
||||
// Heading
|
||||
for len(text) > 0 && !match(text[0], "Helvetica-BoldOblique", 11, "") && !match(text[0], "Arial,BoldItalic", 11, "") && !match(text[0], "Arial,BoldItalic", 10, "") {
|
||||
text = text[1:]
|
||||
}
|
||||
if len(text) == 0 {
|
||||
break
|
||||
}
|
||||
heading := text[0].S
|
||||
text = text[1:]
|
||||
for len(text) > 0 && (match(text[0], "Helvetica-BoldOblique", 11, "") || match(text[0], "Arial,BoldItalic", 11, "") || match(text[0], "Arial,BoldItalic", 10, "")) {
|
||||
heading += " " + text[0].S
|
||||
text = text[1:]
|
||||
}
|
||||
heading = strings.Replace(heading, "]", "] ", -1)
|
||||
heading = strings.Replace(heading, " ", " ", -1)
|
||||
heading = strings.Replace(heading, "rEVX-form", "r EVX-form", -1)
|
||||
heading = strings.Replace(heading, "eX-form", "e X-form", -1)
|
||||
heading = strings.Replace(heading, "mSD4-form", "m SD4-form", -1)
|
||||
heading = strings.Replace(heading, "eSCI8-form", "e SCI8-form", -1)
|
||||
heading = strings.TrimSpace(heading)
|
||||
if isVLE(heading) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Mnemonic
|
||||
if len(text) == 0 || (!match(text[0], "Helvetica", 9, "") && !match(text[0], "Helvetica-BoldOblique", 9, "") && !match(text[0], "Arial", 9, "") && !match(text[0], "Arial", 10, "")) {
|
||||
continue
|
||||
}
|
||||
mnemonic := ""
|
||||
y := text[0].Y
|
||||
x0 := text[0].X
|
||||
for len(text) > 0 && (match(text[0], "Helvetica", 9, "") || match(text[0], "Helvetica-BoldOblique", 9, "") || match(text[0], "Arial", 9, "") || match(text[0], "Courier", 8, "") || match(text[0], "LucidaConsole", 7.17, "") || text[0].Y == y) {
|
||||
if text[0].Y != y {
|
||||
if math.Abs(text[0].X-x0) > 4 {
|
||||
break
|
||||
}
|
||||
mnemonic += "\n"
|
||||
y = text[0].Y
|
||||
} else if mnemonic != "" {
|
||||
mnemonic += " "
|
||||
}
|
||||
mnemonic += text[0].S
|
||||
text = text[1:]
|
||||
}
|
||||
|
||||
// Encoding
|
||||
bits, i := readBitBox(heading, content, text, num)
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
insts = append(insts, Inst{heading, mnemonic, bits})
|
||||
}
|
||||
return insts
|
||||
}
|
||||
|
||||
var vle = []string{
|
||||
"System Call C-form,ESC-form",
|
||||
}
|
||||
|
||||
func isVLE(s string) bool {
|
||||
for _, v := range vle {
|
||||
if s == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func readBitBox(headline string, content pdf.Content, text []pdf.Text, pageNum int) (string, int) {
|
||||
// fields
|
||||
i := 0
|
||||
if len(text) == 0 || (!match(text[i], "Helvetica", 9, "") && !match(text[i], "Helvetica", 7.26, "") && !match(text[i], "Arial", 9, "") && !match(text[i], "Arial", 7.98, "") && !match(text[i], "Arial", 7.2, "")) {
|
||||
fmt.Fprintf(os.Stderr, "page %d: no bit fields for %q\n", pageNum, headline)
|
||||
if len(text) > 0 {
|
||||
fmt.Fprintf(os.Stderr, "\tlast text: %v\n", text[0])
|
||||
}
|
||||
return "", 0
|
||||
}
|
||||
sz := text[i].FontSize
|
||||
y2 := text[i].Y
|
||||
x2 := 0.0
|
||||
for i < len(text) && text[i].Y == y2 {
|
||||
if x2 < text[i].X+text[i].W {
|
||||
x2 = text[i].X + text[i].W
|
||||
}
|
||||
i++
|
||||
}
|
||||
y2 += sz / 2
|
||||
|
||||
// bit numbers
|
||||
if i >= len(text) || text[i].S != "0" {
|
||||
if headline == "Transaction Abort Doubleword Conditional X-form" {
|
||||
// Split across the next page.
|
||||
return "31@0|TO@6|RA@11|RB@16|814@21|1@31|", i
|
||||
}
|
||||
if headline == "Add Scaled Immediate SCI8-form" {
|
||||
// Very strange fonts.
|
||||
return "06@0|RT@6|RA@11|8@16|Rc@20|F@21|SCL@22|UI8@24|", i
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "page %d: no bit numbers for %s\n", pageNum, headline)
|
||||
if i < len(text) {
|
||||
fmt.Fprintf(os.Stderr, "\tlast text: %v\n", text[i])
|
||||
}
|
||||
return "", 0
|
||||
}
|
||||
sz = text[i].FontSize
|
||||
y1 := text[i].Y
|
||||
x1 := text[i].X
|
||||
for i < len(text) && text[i].Y == y1 {
|
||||
if x2 < text[i].X+text[i].W {
|
||||
x2 = text[i].X + text[i].W
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
if debugPage > 0 {
|
||||
fmt.Println("encoding box", x1, y1, x2, y2, i, text[0], text[i])
|
||||
}
|
||||
|
||||
// Find lines (thin rectangles) separating bit fields.
|
||||
var bottom, top pdf.Rect
|
||||
const (
|
||||
yMargin = 0.25 * 72
|
||||
xMargin = 1 * 72
|
||||
)
|
||||
for _, r := range content.Rect {
|
||||
// Only consider lines in the same column.
|
||||
if (x1 < 306) != (r.Max.X < 306) {
|
||||
continue
|
||||
}
|
||||
if r.Max.Y-r.Min.Y < 2 && x1-xMargin < r.Min.X && r.Min.X < x1 && x2 < r.Max.X && r.Max.X < x2+xMargin {
|
||||
if y1-yMargin < r.Min.Y && r.Min.Y < y1 {
|
||||
bottom = r
|
||||
}
|
||||
if y2 < r.Min.Y && r.Min.Y < y2+8 {
|
||||
top = r
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if bottom.Min.X == 0 {
|
||||
// maybe bit numbers are outside box; see doze, nap, sleep, rvwinkle.
|
||||
for _, r := range content.Rect {
|
||||
// Only consider lines in the same column.
|
||||
if (x1 < 306) != (r.Max.X < 306) {
|
||||
continue
|
||||
}
|
||||
if r.Max.Y-r.Min.Y < 2 && x1-xMargin < r.Min.X && r.Min.X < x1 && x2 < r.Max.X && r.Max.X < x2+xMargin {
|
||||
if y1+sz/2 < r.Min.Y && r.Min.Y < y2 {
|
||||
bottom = r
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if debugPage > 0 {
|
||||
fmt.Println("top", top, "bottom", bottom)
|
||||
}
|
||||
|
||||
const ε = 0.1 * 72
|
||||
var bars []pdf.Rect
|
||||
for _, r := range content.Rect {
|
||||
if r.Max.X-r.Min.X < 2 && math.Abs(r.Min.Y-bottom.Min.Y) < ε && math.Abs(r.Max.Y-top.Min.Y) < ε && (bottom.Min.X < 306) == (r.Max.X < 306) {
|
||||
bars = append(bars, r)
|
||||
}
|
||||
}
|
||||
sort.Sort(RectHorizontal(bars))
|
||||
|
||||
out := ""
|
||||
for i := 0; i < len(bars)-1; i++ {
|
||||
var sub []pdf.Text
|
||||
x1, x2 := bars[i].Min.X, bars[i+1].Min.X
|
||||
for _, t := range content.Text {
|
||||
tx := t.X + t.W/2
|
||||
ty := t.Y + t.FontSize/4
|
||||
if x1 < tx && tx < x2 && y1 < ty && ty < y2 {
|
||||
sub = append(sub, t)
|
||||
}
|
||||
}
|
||||
var str []string
|
||||
for _, t := range findWords(sub) {
|
||||
str = append(str, t.S)
|
||||
}
|
||||
s := strings.Join(str, "@")
|
||||
out += s + "|"
|
||||
}
|
||||
|
||||
if out == "" {
|
||||
fmt.Fprintf(os.Stderr, "page %d: no bit encodings for %s\n", pageNum, headline)
|
||||
}
|
||||
return out, i
|
||||
}
|
||||
|
||||
type RectHorizontal []pdf.Rect
|
||||
|
||||
func (x RectHorizontal) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||
func (x RectHorizontal) Less(i, j int) bool { return x[i].Min.X < x[j].Min.X }
|
||||
func (x RectHorizontal) Len() int { return len(x) }
|
||||
|
||||
func checkNoEncodings(num int, text []pdf.Text) {
|
||||
for _, t := range text {
|
||||
if match(t, "Helvetica-Bold", 9, "Encoding") {
|
||||
fmt.Fprintf(os.Stderr, "page %d: unexpected encoding: %s\n", num, t.S)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func match(t pdf.Text, font string, size float64, substr string) bool {
|
||||
return t.Font == font && (size == 0 || math.Abs(t.FontSize-size) < 0.1) && strings.Contains(t.S, substr)
|
||||
}
|
||||
|
||||
func findWords(chars []pdf.Text) (words []pdf.Text) {
|
||||
// Sort by Y coordinate and normalize.
|
||||
const nudge = 1.5
|
||||
sort.Sort(pdf.TextVertical(chars))
|
||||
old := -100000.0
|
||||
for i, c := range chars {
|
||||
if c.Y != old && math.Abs(old-c.Y) < nudge {
|
||||
chars[i].Y = old
|
||||
} else {
|
||||
old = c.Y
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by Y coordinate, breaking ties with X.
|
||||
// This will bring letters in a single word together.
|
||||
sort.Sort(pdf.TextVertical(chars))
|
||||
|
||||
// Loop over chars.
|
||||
for i := 0; i < len(chars); {
|
||||
// Find all chars on line.
|
||||
j := i + 1
|
||||
for j < len(chars) && chars[j].Y == chars[i].Y {
|
||||
j++
|
||||
}
|
||||
var end float64
|
||||
// Split line into words (really, phrases).
|
||||
for k := i; k < j; {
|
||||
ck := &chars[k]
|
||||
s := ck.S
|
||||
end = ck.X + ck.W
|
||||
charSpace := ck.FontSize / 6
|
||||
wordSpace := ck.FontSize * 2 / 3
|
||||
l := k + 1
|
||||
for l < j {
|
||||
// Grow word.
|
||||
cl := &chars[l]
|
||||
if sameFont(cl.Font, ck.Font) && math.Abs(cl.FontSize-ck.FontSize) < 0.1 && cl.X <= end+charSpace {
|
||||
s += cl.S
|
||||
end = cl.X + cl.W
|
||||
l++
|
||||
continue
|
||||
}
|
||||
// Add space to phrase before next word.
|
||||
if sameFont(cl.Font, ck.Font) && math.Abs(cl.FontSize-ck.FontSize) < 0.1 && cl.X <= end+wordSpace {
|
||||
s += " " + cl.S
|
||||
end = cl.X + cl.W
|
||||
l++
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
f := ck.Font
|
||||
f = strings.TrimSuffix(f, ",Italic")
|
||||
f = strings.TrimSuffix(f, "-Italic")
|
||||
words = append(words, pdf.Text{f, ck.FontSize, ck.X, ck.Y, end - ck.X, s})
|
||||
k = l
|
||||
}
|
||||
i = j
|
||||
}
|
||||
|
||||
// Split into two columns.
|
||||
var col1, col2 []pdf.Text
|
||||
for _, w := range words {
|
||||
if w.X > 306 {
|
||||
col2 = append(col2, w)
|
||||
} else {
|
||||
col1 = append(col1, w)
|
||||
}
|
||||
}
|
||||
return append(col1, col2...)
|
||||
}
|
||||
|
||||
func sameFont(f1, f2 string) bool {
|
||||
f1 = strings.TrimSuffix(f1, ",Italic")
|
||||
f1 = strings.TrimSuffix(f1, "-Italic")
|
||||
f2 = strings.TrimSuffix(f1, ",Italic")
|
||||
f2 = strings.TrimSuffix(f1, "-Italic")
|
||||
return strings.TrimSuffix(f1, ",Italic") == strings.TrimSuffix(f2, ",Italic") || f1 == "Symbol" || f2 == "Symbol" || f1 == "TimesNewRoman" || f2 == "TimesNewRoman"
|
||||
}
|
||||
|
||||
var jsFix = strings.NewReplacer(
|
||||
// `\u003c`, `<`,
|
||||
// `\u003e`, `>`,
|
||||
// `\u0026`, `&`,
|
||||
// `\u0009`, `\t`,
|
||||
)
|
||||
|
||||
func printTable(name string, table []Inst) {
|
||||
_ = strconv.Atoi
|
||||
}
|
Загрузка…
Ссылка в новой задаче