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:
Russ Cox 2016-10-12 12:35:10 -04:00
Родитель bebde3aee3
Коммит 21375ab36a
16 изменённых файлов: 9641 добавлений и 0 удалений

1200
ppc64/pp64.csv Normal file

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

174
ppc64/ppc64asm/decode.go Normal file
Просмотреть файл

@ -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)
}
}
}

6
ppc64/ppc64asm/doc.go Normal file
Просмотреть файл

@ -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

535
ppc64/ppc64asm/ext_test.go Normal file
Просмотреть файл

@ -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
}

84
ppc64/ppc64asm/field.go Normal file
Просмотреть файл

@ -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)
}
}
}

125
ppc64/ppc64asm/gnu.go Normal file
Просмотреть файл

@ -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
}

310
ppc64/ppc64asm/inst.go Normal file
Просмотреть файл

@ -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, &sect)
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, &sect) // 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, &sect) // .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, &sect)
buf.WriteString("\x00.text\x00.shstrtab\x00")
f.Write(buf.Bytes())
return nil
}

164
ppc64/ppc64asm/plan9.go Normal file
Просмотреть файл

@ -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",
}

5418
ppc64/ppc64asm/tables.go Normal file

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

25
ppc64/ppc64asm/testdata/decode.txt поставляемый Normal file
Просмотреть файл

@ -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

592
ppc64/ppc64map/map.go Normal file
Просмотреть файл

@ -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)
}
}

503
ppc64/ppc64spec/spec.go Normal file
Просмотреть файл

@ -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
}