Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[asmdb] More encoding space overview page features #175

Merged
merged 24 commits into from
Apr 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
c4ef9c5
build: yarn upgrade
xen0n Apr 17, 2024
a9a2682
refactor(asmdb): kill the redundant action field
xen0n Apr 18, 2024
a05bcd6
fix(asmdb): fix determined format not showing for non-leaf decodetree…
xen0n Apr 18, 2024
18a40fa
feat(asmdb): augment decodetree nodes with parents
xen0n Apr 18, 2024
dc880d5
feat(asmdb): lay grounds for showing selected node's details
xen0n Apr 18, 2024
e82de9a
feat(asmdb): record match key in augmented decodetree nodes
xen0n Apr 18, 2024
a77b3a1
refactor(asmdb): disentangle decodetree augmentation from antd presen…
xen0n Apr 18, 2024
7e2e93b
refactor(asmdb): eliminate redundant presentation logic
xen0n Apr 18, 2024
d72f40a
feat(asmdb): lay more grounds for node detail presentation
xen0n Apr 18, 2024
e377f47
refactor(asmdb): move well-known alias handling into decodetree augme…
xen0n Apr 18, 2024
15797f5
fix(asmdb): fix some decodetree leaves not being really decided
xen0n Apr 19, 2024
c355955
feat(asmdb): mark the matched values with bitfield separators
xen0n Apr 19, 2024
0ea849a
feat(asmdb): give the decodetree view a max height
xen0n Apr 19, 2024
566885e
feat(asmdb): show encoding space details
xen0n Apr 20, 2024
b447fb0
feat(asmdb): use tags in node titles
xen0n Apr 20, 2024
1bebe18
feat(asmdb): clarify description: 占用 vs 分配
xen0n Apr 20, 2024
f75120e
feat(asmdb): revise well-known aliases
xen0n Apr 20, 2024
23182be
refactor(asmdb): insnFormat.tsx -> insnFormatName.tsx
xen0n Apr 20, 2024
5acf6bb
feat(asmdb): show decodetree bitfields in asmdb design
xen0n Apr 20, 2024
9b105e2
feat(asmdb): mark bits that are (to be) checked
xen0n Apr 20, 2024
0685609
feat(asmdb): implement gradient for large imm operands
xen0n Apr 20, 2024
4df0b22
feat: add rendered asmdb-concept for viewing convenience
xen0n Apr 20, 2024
145d091
feat(asmdb): mark arg kind on MSB for extra clarity
xen0n Apr 20, 2024
374cd97
build: yarn upgrade
xen0n Apr 20, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added art/asmdb-concept.webp
Binary file not shown.
51 changes: 28 additions & 23 deletions scripts/asmdb/gen_decodetree.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ func (x bitfield) extractFrom(n uint32) uint32 {
return (n >> uint32(x.LSB)) & ((1 << x.Len) - 1)
}

func (x bitfield) toMask() uint32 {
return ((1 << x.Len) - 1) << x.LSB
}

func (x bitfield) toSet() map[int]struct{} {
s := map[int]struct{}{}
for i := x.LSB; i < x.LSB+x.Len; i++ {
Expand Down Expand Up @@ -58,6 +62,14 @@ func (x bitfields) totalWidth() int {
return w
}

func (x bitfields) toMask() uint32 {
ret := uint32(0)
for _, bf := range x {
ret |= bf.toMask()
}
return ret
}

func (x bitfields) toSet() map[int]struct{} {
s := map[int]struct{}{}
for _, bf := range x {
Expand Down Expand Up @@ -141,24 +153,16 @@ func bitfieldsFromSet(s map[int]struct{}) bitfields {
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"`
Match uint32 `json:"match"`

// contains the determined insn format if all descendant nodes share this
// format
Fmt string `json:"fmt,omitempty"`
// if action is finish, contains the matched insn's mnemonic
// if node is leaf (insn is already fully decided), contains the matched
// insn's mnemonic
Matched string `json:"matched,omitempty"`
// if action is continue, points to the next decodetree node
// if node is not leaf, points to the next decodetree node
Next *decodetreeNode `json:"next,omitempty"`
}

Expand Down Expand Up @@ -346,25 +350,26 @@ func buildDecodetreeSubset(
fmt: "",
}
for k, l := range furtherSubsets {
nextConsumedFixedBitfields := consumedFixedBitfields.union(commonFixedBits)

if len(l) == 1 {
// fully decided
n.Matches = append(n.Matches, &decodetreeMatch{
Match: k,
Action: decodetreeActionFinish,
Fmt: l[0].fmt,
Matched: l[0].mnemonic,
Next: nil,
})
continue
if l[0].mask == nextConsumedFixedBitfields.toMask() {
// fully decided (all fixed bits are already checked)
n.Matches = append(n.Matches, &decodetreeMatch{
Match: k,
Fmt: l[0].fmt,
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,
Fmt: "",
Matched: "",
Next: subsetNode,
Expand Down
240 changes: 56 additions & 184 deletions src/components/AsmDB/antdDecodeTreeAdapter.tsx
Original file line number Diff line number Diff line change
@@ -1,128 +1,78 @@
import type { TreeDataNode } from "antd"
import { Tag, type TreeDataNode } from "antd"
import { CheckOutlined, EyeOutlined } from '@ant-design/icons'
import _ from 'lodash'

import styles from './index.module.css'
import { type AugmentedDecodeTreeMatch, type AugmentedDecodeTreeNode } from "./augmentedDecodeTree"
import { bitfieldWidth, representBitfields } from "./bitfield"
import type { Bitfield } from "./types"

function representBitfield(bf: Bitfield): string {
return bf.len == 1 ? bf.lsb.toString(10) : `${bf.lsb + bf.len - 1}:${bf.lsb}`
}

function representBitfields(bfs: Bitfield[]): string {
if (bfs.length == 1)
return representBitfield(bfs[0])
return _.map(_.reverse(_.cloneDeep(bfs)), representBitfield).join(',')
}

function bitfieldWidth(bfs: Bitfield[]): number {
return _.sum(_.map(bfs, (x) => x.len))
}

function mergeBitfields(a: Bitfield[], b: Bitfield[]): Bitfield[] {
const tmp = _.sortBy(_.concat(_.cloneDeep(a), ..._.cloneDeep(b)), 'lsb')
if (tmp.length < 2)
return tmp

for (let i = 1; i < tmp.length; i++) {
if (tmp[i].lsb == tmp[i - 1].lsb + tmp[i - 1].len) {
// merge tmp[i] into tmp[i-1]
tmp[i - 1].len += tmp[i].len
tmp.splice(i, 1)
i--
}
}

return tmp
}

function restoreIntoBitfields(num: number, bfs: Bitfield[]): number {
let y = 0
for (const bf of bfs) {
y |= (num & ((1 << bf.len) - 1)) << bf.lsb
num >>= bf.len
function representMatchValue(val: number, bfs: Bitfield[]): string {
const sortedBFs = _.sortBy(_.clone(bfs), 'lsb')
const reprs = []
for (const bf of sortedBFs) {
reprs.push((val & ((1 << bf.len) - 1)).toString(2).padStart(bf.len, '0'))
val >>= bf.len
}
return y
}
reprs.reverse()

function representMatchValue(val: number, bfs: Bitfield[]): string {
const width = bitfieldWidth(bfs)
return `0b${val.toString(2).padStart(width, '0')}`
return `0b${reprs.join("'")}`
}

type NodeTitleProps = {
match?: AugmentedDecodeTreeMatch
node?: AugmentedDecodeTreeNode
matchNumber?: number
matchPattern?: string
lookAt: Bitfield[]
parentLookAt?: Bitfield[]
insn?: string
}

const wellKnownMatchPatterns = {
'000000xxxxxxxxxxxxxxxxxxxxxxxxxx': '运算',
'00000000000000000xxxxxxxxxxxxxxx': '双寄存器',
'00000000000100xxxxxxxxxxxxxxxxxx': '三寄存器-甲',
'00000000000101xxxxxxxxxxxxxxxxxx': '三寄存器-乙',
'00000000000110xxxxxxxxxxxxxxxxxx': '三寄存器-丙',
'00000000000111xxxxxxxxxxxxxxxxxx': '乘',
'00000000001000xxxxxxxxxxxxxxxxxx': '除',
'00000000001001xxxxxxxxxxxxxxxxxx': 'CRC',
'00000000010xxxxxxxxxxxxxxxxxxxxx': '移位',
'0000000100xxxxxxxxxxxxxxxxxxxxxx': 'FP 三寄存器',
'000001xxxxxxxxxxxxxxxxxxxxxxxxxx': '特权',
'00000110010010000xxxxxxxxxxxxxxx': 'IOCSR-TLB',
'000010xxxxxxxxxxxxxxxxxxxxxxxxxx': '四寄存器',
'000011xxxxxxxxxxxxxxxxxxxxxxxxxx': '比较选择',
'000011000001xxxxxxxxxxxxxxxxxxxx': '比较选择-FP FP32',
'000011000010xxxxxxxxxxxxxxxxxxxx': '比较选择-FP FP64',
'000011000101xxxxxxxxxxxxxxxxxxxx': '比较选择-LSX FP32',
'000011000110xxxxxxxxxxxxxxxxxxxx': '比较选择-LSX FP64',
'000011001001xxxxxxxxxxxxxxxxxxxx': '比较选择-LASX FP32',
'000011001010xxxxxxxxxxxxxxxxxxxx': '比较选择-LASX FP64',
'000101xxxxxxxxxxxxxxxxxxxxxxxxxx': '大立即数-甲',
'000110xxxxxxxxxxxxxxxxxxxxxxxxxx': '大立即数-乙',
'000111xxxxxxxxxxxxxxxxxxxxxxxxxx': '大立即数-丙',
'001000xxxxxxxxxxxxxxxxxxxxxxxxxx': '访存-LLSC',
'001001xxxxxxxxxxxxxxxxxxxxxxxxxx': '访存-大立即数',
'001010xxxxxxxxxxxxxxxxxxxxxxxxxx': '访存-整数与 FP',
'001011xxxxxxxxxxxxxxxxxxxxxxxxxx': '访存-SIMD 与 LBT',
'001100xxxxxxxxxxxxxxxxxxxxxxxxxx': '访存-SIMD 元素',
'001110xxxxxxxxxxxxxxxxxxxxxxxxxx': '访存-原子与复合',
'010010xxxxxxxxxxxxxxxxxxxxxxxxxx': '跳转-FP 与 LBT',
'011100xxxxxxxxxxxxxxxxxxxxxxxxxx': '运算-LSX',
'011101xxxxxxxxxxxxxxxxxxxxxxxxxx': '运算-LASX',
}

function NodeTitle({ match, node, matchNumber, matchPattern, lookAt, parentLookAt, insn }: NodeTitleProps): JSX.Element {
if (match)
matchNumber = match.match
function NodeTitle({ match, node }: NodeTitleProps): JSX.Element {
if (!match)
match = node.parentMatch

let preAttribs: JSX.Element[] = []
let postAttribs: JSX.Element[] = []
const matchNumber = match ? match.match : 0
const matchPattern = node ? node.key : match.key
const alias = match?.wellKnownAlias || node?.wellKnownAlias
const insn = match ? match.matched : ''
const lookAt = node ? node.look_at : match.parentNode.look_at
const parentLookAt = node ? node.parentLookAt : null

const preAttribs: JSX.Element[] = []
const postAttribs: JSX.Element[] = []
if (match?.fmt)
postAttribs.push(<span className={styles.attrib} key={`${matchPattern}-fmt`}>格式 {match.fmt}</span>)
if (wellKnownMatchPatterns.hasOwnProperty(matchPattern))
preAttribs.push(<span className={styles.attrib} key={`${matchPattern}-alias`}>{wellKnownMatchPatterns[matchPattern]}</span>)
if (node)
postAttribs.push(<span className={styles.contentAttrib} key={`${matchPattern}-fmt`}>并确定格式为 {match.fmt}</span>)
else
postAttribs.push(<Tag className={styles.insnFmtTagAttrib} key={`${matchPattern}-fmt`}>{match.fmt}</Tag>)
if (alias)
preAttribs.push(<Tag className={styles.aliasTagAttrib} key={`${matchPattern}-alias`}>{alias}</Tag>)

if (insn) {
postAttribs.push(<span className={styles.attrib} key={`${matchPattern}-subspace`}>空间占用 {match.numUsedInsnWords}</span>)
return <>
<span>{representMatchValue(matchNumber, lookAt)}{preAttribs}: {insn}</span>
<span>{representMatchValue(matchNumber, lookAt)}: {preAttribs}{insn}</span>
{postAttribs}
</>
}

const root = matchPattern == 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
if (root)
postAttribs.push(<span className={styles.attrib} key={`${matchPattern}-major`}>主操作码</span>)
if (node)
postAttribs.push(<span className={styles.attrib} key={`${matchPattern}-fanout`}>扇出 {node.matches.length}</span>)

if (node.numUsedInsnWords == node.numTotalInsnWords)
postAttribs.push(<span className={styles.attrib} key={`${matchPattern}-subspace`}>子空间共 {node.numTotalInsnWords} 已满</span>)
else
postAttribs.push(<span className={styles.attrib} key={`${matchPattern}-subspace`}>子空间共 {node.numTotalInsnWords} 已分配 {node.numUsedInsnWords} ({(node.numUsedInsnWords / node.numTotalInsnWords * 100).toFixed(2)}%)</span>)
if (node) {
const numAllocatedPrefixes = node.matches.length
const numTotalPrefixes = 1 << bitfieldWidth(node.look_at)
if (numAllocatedPrefixes == numTotalPrefixes && node.numUsedInsnWords == node.numTotalInsnWords) {
postAttribs.push(<span className={styles.attrib} key={`${matchPattern}-subspace`}>子空间已满</span>)
} else {
if (numAllocatedPrefixes == numTotalPrefixes)
postAttribs.push(<span className={styles.attrib} key={`${matchPattern}-fanout`}>子前缀空间已满</span>)
else
postAttribs.push(<span className={styles.attrib} key={`${matchPattern}-fanout`}>子前缀空间 {numAllocatedPrefixes}/{numTotalPrefixes}</span>)

if (node.numUsedInsnWords == node.numTotalInsnWords)
postAttribs.push(<span className={styles.attrib} key={`${matchPattern}-subspace`}>子编码空间已满</span>)
else
postAttribs.push(<span className={styles.attrib} key={`${matchPattern}-subspace`}>子编码空间已用 {(node.numUsedInsnWords / node.numTotalInsnWords * 100).toFixed(2)}%</span>)
}
}

if (root)
return <>
Expand All @@ -131,109 +81,31 @@ function NodeTitle({ match, node, matchNumber, matchPattern, lookAt, parentLookA
</>

return <>
<span>{representMatchValue(matchNumber, parentLookAt)}{preAttribs}: 检查 [{representBitfields(lookAt)}] 位</span>
<span>{representMatchValue(matchNumber, parentLookAt)}: {preAttribs}检查 [{representBitfields(lookAt)}] 位</span>
{postAttribs}
</>
}

function makeMatchNode(
m: AugmentedDecodeTreeMatch,
node: AugmentedDecodeTreeNode,
matchSoFar: number,
myMatchBitfields: Bitfield[],
): TreeDataNode {
const myMatch = matchSoFar | restoreIntoBitfields(m.match, node.look_at)
const key = `m${makeMatchPatternKey(myMatch, myMatchBitfields)}`

if (m.next)
return transformDecodeTreeForAntdInner(m.next, m.match, node.look_at, myMatch, myMatchBitfields)
return transformDecodeTreeForAntd(m.next)

return {
title: <NodeTitle match={m} lookAt={node.look_at} insn={m.matched} />,
key: key,
title: <NodeTitle match={m} />,
key: m.key,
icon: <CheckOutlined />,
}
}

function makeMatchPatternKey(match: number, bfs: Bitfield[]): string {
let s = match.toString(2).padStart(32, '0').split('')
s.reverse()
let y = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'.split('')
for (const bf of bfs)
for (let i = bf.lsb; i < bf.lsb + bf.len; i++)
y[i] = s[i]
y.reverse()
return y.join('')
}

function transformDecodeTreeForAntdInner(
export function transformDecodeTreeForAntd(
node: AugmentedDecodeTreeNode,
myMatch: number,
parentLookAt: Bitfield[],
matchSoFar: number,
matchBitfieldsSoFar: Bitfield[],
): TreeDataNode {
// figure out the actual match pattern so far, for showing helpful opcode aliases
const myMatchBitfields = mergeBitfields(matchBitfieldsSoFar, node.look_at)
const matchPattern = makeMatchPatternKey(matchSoFar, matchBitfieldsSoFar)

return {
title: <NodeTitle
node={node}
matchNumber={myMatch}
matchPattern={matchPattern}
lookAt={node.look_at}
parentLookAt={parentLookAt}
/>,
key: matchPattern,
title: <NodeTitle node={node} />,
key: node.key,
icon: <EyeOutlined />,
children: _.map(node.matches, (x) => makeMatchNode(x, node, matchSoFar, myMatchBitfields)),
children: _.map(node.matches, makeMatchNode),
}
}

type AugmentedDecodeTreeMatch = DecodeTreeMatch & {
next?: AugmentedDecodeTreeNode
numUsedInsnWords: number
}

export type AugmentedDecodeTreeNode = DecodeTreeNode & {
matches: AugmentedDecodeTreeMatch[]
numTotalInsnWords: number
numUsedInsnWords: number
}

export function augmentDecodeTree(node: DecodeTreeNode): AugmentedDecodeTreeNode {
let x = _.cloneDeep(node) as AugmentedDecodeTreeNode
augmentDecodeTreeInplace(x, [])
return x
}

function augmentDecodeTreeInplace(
node: AugmentedDecodeTreeNode,
matchBitfieldsSoFar: Bitfield[],
) {
const myMatchBitfields = mergeBitfields(matchBitfieldsSoFar, node.look_at)
const myChildMatchWidth = bitfieldWidth(myMatchBitfields)
if (matchBitfieldsSoFar.length == 0)
// 1 << 32 = 1...
node.numTotalInsnWords = 0x100000000
else
node.numTotalInsnWords = 1 << (32 - bitfieldWidth(matchBitfieldsSoFar))

node.numUsedInsnWords = 0

for (let m of node.matches)
if (m.next) {
augmentDecodeTreeInplace(m.next, myMatchBitfields)
node.numUsedInsnWords += m.next.numUsedInsnWords
} else {
m.numUsedInsnWords = 1 << (32 - myChildMatchWidth)
node.numUsedInsnWords += m.numUsedInsnWords
}

return node
}

export function transformDecodeTreeForAntd(node: AugmentedDecodeTreeNode): TreeDataNode {
return transformDecodeTreeForAntdInner(node, 0, [], 0, [])
}
Loading
Loading