Skip to content

Commit

Permalink
ppc64/ppc64asm: speed up PPC64 instruction decoding
Browse files Browse the repository at this point in the history
It's really slow to iterate every instruction until a match is found.
This turns decoding PPC64 binaries into a seemingly quick operation
instead of a seconds long process for go toolchain sized binaries.

Use the primary opcode to map each instruction into a list of viable
masks, and group instructions with identical masks into a map to
speed up decoding.

Change-Id: Id0d0eefbb77244c379832d8a602662e551a7568a
Reviewed-on: https://go-review.googlesource.com/c/arch/+/602717
Reviewed-by: Archana Ravindar <[email protected]>
Reviewed-by: Cherry Mui <[email protected]>
LUCI-TryBot-Result: Go LUCI <[email protected]>
Reviewed-by: Dmitri Shuralyov <[email protected]>
  • Loading branch information
pmur committed Oct 17, 2024
1 parent 7874f23 commit bc8e2b9
Showing 1 changed file with 48 additions and 2 deletions.
50 changes: 48 additions & 2 deletions ppc64/ppc64asm/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"encoding/binary"
"fmt"
"log"
"sort"
"sync"
)

const debugDecode = false
Expand Down Expand Up @@ -111,6 +113,47 @@ const (
TypeLast // must be the last one
)

type InstMaskMap struct {
mask uint64
insn map[uint64]*instFormat
}

// Note, plxv/pstxv have a 5 bit opcode in the second instruction word. Only match the most significant 5 of 6 bits of the second primary opcode.
const lookupOpcodeMask = uint64(0xFC000000F8000000)

// Three level lookup for any instruction:
// 1. Primary opcode map to a list of secondary opcode maps.
// 2. A list of opcodes with distinct masks, sorted by largest to smallest mask.
// 3. A map to a specific opcodes with a given mask.
var getLookupMap = sync.OnceValue(func() map[uint64][]InstMaskMap {
lMap := make(map[uint64][]InstMaskMap)
for idx, _ := range instFormats {
i := &instFormats[idx]
pop := i.Value & lookupOpcodeMask
var me *InstMaskMap
masks := lMap[pop]
for im, m := range masks {
if m.mask == i.Mask {
me = &masks[im]
break
}
}
if me == nil {
me = &InstMaskMap{i.Mask, map[uint64]*instFormat{}}
masks = append(masks, *me)
}
me.insn[i.Value] = i
lMap[pop] = masks
}
// Reverse sort masks to ensure extended mnemonics match before more generic forms of an opcode (e.x nop over ori 0,0,0)
for _, v := range lMap {
sort.Slice(v, func(i, j int) bool {
return v[i].mask > v[j].mask
})
}
return lMap
})

func (t ArgType) String() string {
switch t {
default:
Expand Down Expand Up @@ -191,10 +234,13 @@ func Decode(src []byte, ord binary.ByteOrder) (inst Inst, err error) {
ui |= uint64(ui_extn[1])
inst.SuffixEnc = ui_extn[1]
}
for i, iform := range instFormats {
if ui&iform.Mask != iform.Value {

fmts := getLookupMap()[ui&lookupOpcodeMask]
for i, masks := range fmts {
if _, fnd := masks.insn[masks.mask&ui]; !fnd {
continue
}
iform := masks.insn[masks.mask&ui]
if ui&iform.DontCare != 0 {
if debugDecode {
log.Printf("Decode(%#x): unused bit is 1 for Op %s", ui, iform.Op)
Expand Down

0 comments on commit bc8e2b9

Please sign in to comment.