Skip to content

Commit

Permalink
Merge pull request #37 from xen0n/asmdb
Browse files Browse the repository at this point in the history
asmdb updates
  • Loading branch information
xen0n authored Apr 13, 2024
2 parents 75eac44 + 0a5902a commit 7bce2a9
Show file tree
Hide file tree
Showing 11 changed files with 1,091 additions and 3,645 deletions.
19 changes: 8 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids",
"typecheck": "tsc",
"build-helpers": "cd scripts/asmdb && go build ./gen-asmdb-data.go"
"build-helpers": "cd scripts/asmdb && go build -o ./gen-asmdb-data ./..."
},
"dependencies": {
"@docusaurus/core": "^3.1.0",
"@docusaurus/plugin-client-redirects": "^3.1.0",
"@docusaurus/preset-classic": "^3.1.0",
"@docusaurus/theme-mermaid": "^3.1.0",
"@docusaurus/core": "^3.2.1",
"@docusaurus/plugin-client-redirects": "^3.2.1",
"@docusaurus/preset-classic": "^3.2.1",
"@docusaurus/theme-mermaid": "^3.2.1",
"@easyops-cn/docusaurus-search-local": "^0.38.1",
"@jsdevtools/rehype-url-inspector": "^2.0.2",
"@mdx-js/react": "^3.0.0",
Expand All @@ -29,19 +29,16 @@
"@types/react": "^18",
"antd": "^5",
"clsx": "^1.2.1",
"glob": "^8.0.3",
"glob-promise": "^5.0.0",
"glob": "^10.3.12",
"lodash": "^4.17.21",
"mobx": "^6.7.0",
"mobx-react-lite": "^3.4.0",
"prism-react-renderer": "^2.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"yaml": "^2.1.3"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "^3.1.0",
"@docusaurus/tsconfig": "^3.1.0",
"@docusaurus/module-type-aliases": "^3.2.1",
"@docusaurus/tsconfig": "^3.2.1",
"typescript": "^5.2.2",
"update-browserslist-db": "^1.0.13"
},
Expand Down
22 changes: 15 additions & 7 deletions scripts/asmdb/gen-asmdb-data.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,14 @@ func main() {
}
})

result, err := json.Marshal(asmdbData{Insns: insns})
// also build a decode tree for frontend disassembly
var db decodetreeBuilder
for _, insn := range descs {
db.addInsn(insn.Mnemonic, insn.Word, insn.Format.MatchBitmask())
}
decodetreeRoot := db.build()

result, err := json.Marshal(asmdbData{Insns: insns, DecodeTree: decodetreeRoot})
if err != nil {
panic(err)
}
Expand All @@ -74,16 +81,17 @@ func main() {
}

type asmdbData struct {
Insns []asmdbInsn `json:"insns"`
Insns []asmdbInsn `json:"insns"`
DecodeTree *decodetreeNode `json:"decodetree"`
}

type asmdbInsn struct {
Word uint32 `json:"word"`
Mask uint32 `json:"mask"`
Mnemonic string `json:"mnemonic"`
ManualMnemonic string `json:"manual_mnemonic,omitempty"`
Format insnFormat `json:"format"`
ManualFormat insnFormat `json:"manual_format,omitempty"`
Format *insnFormat `json:"format"`
ManualFormat *insnFormat `json:"manual_format,omitempty"`
SinceRev string `json:"since_rev,omitempty"`
Subsets subsetFlags `json:"subsets"`
}
Expand Down Expand Up @@ -119,12 +127,12 @@ type argSlot struct {
Width uint `json:"width"`
}

func convertInsnFormat(x *common.InsnFormat) insnFormat {
func convertInsnFormat(x *common.InsnFormat) *insnFormat {
if x == nil {
return insnFormat{}
return nil
}

return insnFormat{
return &insnFormat{
Repr: x.CanonicalRepr(),
Args: lo.Map(x.Args, func(a *common.Arg, _ int) insnArgs {
shiftAmt := 0
Expand Down
260 changes: 260 additions & 0 deletions scripts/asmdb/gen_decodetree.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
package main

import (
"sort"

"github.com/samber/lo"
)

type bitfield struct {
LSB int `json:"lsb"`
Len int `json:"len"`
}

func (x bitfield) isBitCovered(bitIndex int) bool {
return bitIndex >= x.LSB && bitIndex < (x.LSB+x.Len)
}

func (x bitfield) extractFrom(n uint32) uint32 {
return (n >> uint32(x.LSB)) & ((1 << x.Len) - 1)
}

func (x bitfield) toSet() map[int]struct{} {
s := map[int]struct{}{}
for i := x.LSB; i < x.LSB+x.Len; i++ {
s[i] = struct{}{}
}
return s
}

type bitfields []bitfield

func (x bitfields) toSet() map[int]struct{} {
s := map[int]struct{}{}
for _, bf := range x {
for i := range bf.toSet() {
s[i] = struct{}{}
}
}
return s
}

func (x bitfields) union(y bitfields) bitfields {
zSet := x.toSet()
for i := range y.toSet() {
zSet[i] = struct{}{}
}
return bitfieldsFromSet(zSet)
}

func (x bitfields) isBitCovered(bitIndex int) bool {
for _, bf := range x {
if bf.isBitCovered(bitIndex) {
return true
}
}
return false
}

func (x bitfields) extractFrom(n uint32) uint32 {
if len(x) == 0 {
return 0
}
if len(x) == 1 {
return x[0].extractFrom(n)
}

sort.Slice(x, func(i int, j int) bool {
return x[i].LSB < x[j].LSB
})
y := uint32(0)
shift := 0
for _, bf := range x {
y |= bf.extractFrom(n) << shift
shift += bf.Len
}
return y
}

func bitfieldsFromSet(s map[int]struct{}) bitfields {
min := 32
max := 0
for b := range s {
if b < min {
min = b
}
if b > max {
max = b
}
}

var r bitfields
inSpan, spanLSB := true, min
for b := min; b < max; b++ {
if _, ok := s[b]; ok {
if !inSpan {
inSpan, spanLSB = true, b
}
continue
}

if inSpan {
// we've just stepped outside of a span
// b is spanMSB+1
inSpan = false
r = append(r, bitfield{LSB: spanLSB, Len: b - spanLSB})
}
}
if inSpan {
r = append(r, bitfield{LSB: spanLSB, Len: max + 1 - spanLSB})
}

return r
}

type decodetreeAction int

const (
decodetreeActionUnknown decodetreeAction = 0
decodetreeActionFinish decodetreeAction = 1
decodetreeActionContinue decodetreeAction = 2
)

type decodetreeMatch struct {
Match uint32 `json:"match"`
Action decodetreeAction `json:"action"`

// if action is finish, contains the matched insn's mnemonic
Matched string `json:"matched,omitempty"`
// if action is continue, points to the next decodetree node
Next *decodetreeNode `json:"next,omitempty"`
}

type decodetreeNode struct {
LookAt bitfields `json:"look_at"`
Matches []*decodetreeMatch `json:"matches"`
}

type insnForDecodeTree struct {
mnemonic string
match uint32
mask uint32
}

func (x *insnForDecodeTree) isBitFixed(bit int) bool {
return x.mask&(1<<bit) != 0
}

type decodetreeBuilder struct {
insns []*insnForDecodeTree
}

func (x *decodetreeBuilder) addInsn(mnemonic string, match uint32, mask uint32) {
x.insns = append(x.insns, &insnForDecodeTree{
mnemonic: mnemonic,
match: match,
mask: mask,
})
}

func (x *decodetreeBuilder) build() *decodetreeNode {
return buildDecodetreeSubset(x.insns, nil)
}

func getCommonFixedBitfieldsForSubset(
subset []*insnForDecodeTree,
excludeBitfields bitfields,
) bitfields {
var commonFixedBitsSet map[int]struct{}
for _, insn := range subset {
fb := make(map[int]struct{})
for i := 0; i < 32; i++ {
if !insn.isBitFixed(i) || excludeBitfields.isBitCovered(i) {
continue
}
fb[i] = struct{}{}
}

if len(fb) == 0 {
// this insn has no fixed bit that's left unexamined, thus leaving
// the subset without any guaranteed fixed bit
return nil
}

if commonFixedBitsSet == nil {
commonFixedBitsSet = fb
continue
}

// kill any common bits not found in fb
var bitsToDelete []int
for b := range commonFixedBitsSet {
if _, ok := fb[b]; !ok {
// avoid deleting keys while iterating
bitsToDelete = append(bitsToDelete, b)
}
}

for _, b := range bitsToDelete {
delete(commonFixedBitsSet, b)
}

if len(commonFixedBitsSet) == 0 {
// we already don't have any common fixed bits left
return nil
}
}

return bitfieldsFromSet(commonFixedBitsSet)
}

func categorizeInsnsByFixedBitfields(
insns []*insnForDecodeTree,
fb bitfields,
) map[uint32][]*insnForDecodeTree {
return lo.GroupBy(insns, func(x *insnForDecodeTree) uint32 {
return fb.extractFrom(x.match)
})
}

func buildDecodetreeSubset(
subset []*insnForDecodeTree,
consumedFixedBitfields bitfields,
) *decodetreeNode {
commonFixedBits := getCommonFixedBitfieldsForSubset(subset, consumedFixedBitfields)
furtherSubsets := categorizeInsnsByFixedBitfields(subset, commonFixedBits)

n := &decodetreeNode{
LookAt: commonFixedBits,
Matches: nil,
}
for k, l := range furtherSubsets {
if len(l) == 1 {
// fully decided
n.Matches = append(n.Matches, &decodetreeMatch{
Match: k,
Action: decodetreeActionFinish,
Matched: l[0].mnemonic,
Next: nil,
})
continue
}

// recurse
nextConsumedFixedBitfields := consumedFixedBitfields.union(commonFixedBits)
subsetNode := buildDecodetreeSubset(l, nextConsumedFixedBitfields)

n.Matches = append(n.Matches, &decodetreeMatch{
Match: k,
Action: decodetreeActionContinue,
Matched: "",
Next: subsetNode,
})
}

sort.Slice(n.Matches, func(i int, j int) bool {
return n.Matches[i].Match < n.Matches[j].Match
})

return n
}
7 changes: 6 additions & 1 deletion src/components/AsmDB/bits.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,14 @@ export default function AsmDBBits(props: BitsOptions): JSX.Element {
insnFormatDesc = <InsnFormatName className={styles.showFormatPrefix} overrideStr={mfn} />
}
} else {
let mfnDesc = <></>
if (props.insn.manual_format && props.insn.manual_format.repr != '') {
mfnDesc = <InsnFormatName fmt={props.insn.manual_format} className={styles.showManualFormatPrefix} />
}

insnFormatDesc = <>
<InsnFormatName fmt={props.insn.format} className={styles.showFormatPrefix} />
{props.insn.manual_format.repr != '' ? <InsnFormatName fmt={props.insn.manual_format} className={styles.showManualFormatPrefix} />: ''}
{mfnDesc}
</>
}

Expand Down
Loading

0 comments on commit 7bce2a9

Please sign in to comment.