diff --git a/art/asmdb-concept.webp b/art/asmdb-concept.webp new file mode 100644 index 00000000..9eca4b85 Binary files /dev/null and b/art/asmdb-concept.webp differ diff --git a/scripts/asmdb/gen_decodetree.go b/scripts/asmdb/gen_decodetree.go index 154194b4..ff081c4f 100644 --- a/scripts/asmdb/gen_decodetree.go +++ b/scripts/asmdb/gen_decodetree.go @@ -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++ { @@ -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 { @@ -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"` } @@ -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, diff --git a/src/components/AsmDB/antdDecodeTreeAdapter.tsx b/src/components/AsmDB/antdDecodeTreeAdapter.tsx index 965ba5c0..c0a8acae 100644 --- a/src/components/AsmDB/antdDecodeTreeAdapter.tsx +++ b/src/components/AsmDB/antdDecodeTreeAdapter.tsx @@ -1,114 +1,54 @@ -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(格式 {match.fmt}) - if (wellKnownMatchPatterns.hasOwnProperty(matchPattern)) - preAttribs.push({wellKnownMatchPatterns[matchPattern]}) + if (node) + postAttribs.push(并确定格式为 {match.fmt}) + else + postAttribs.push({match.fmt}) + if (alias) + preAttribs.push({alias}) if (insn) { - postAttribs.push(空间占用 {match.numUsedInsnWords}) return <> - {representMatchValue(matchNumber, lookAt)}{preAttribs}: {insn} + {representMatchValue(matchNumber, lookAt)}: {preAttribs}{insn} {postAttribs} } @@ -116,13 +56,23 @@ function NodeTitle({ match, node, matchNumber, matchPattern, lookAt, parentLookA const root = matchPattern == 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' if (root) postAttribs.push(主操作码) - if (node) - postAttribs.push(扇出 {node.matches.length}) - - if (node.numUsedInsnWords == node.numTotalInsnWords) - postAttribs.push(子空间共 {node.numTotalInsnWords} 已满) - else - postAttribs.push(子空间共 {node.numTotalInsnWords} 已分配 {node.numUsedInsnWords} ({(node.numUsedInsnWords / node.numTotalInsnWords * 100).toFixed(2)}%)) + if (node) { + const numAllocatedPrefixes = node.matches.length + const numTotalPrefixes = 1 << bitfieldWidth(node.look_at) + if (numAllocatedPrefixes == numTotalPrefixes && node.numUsedInsnWords == node.numTotalInsnWords) { + postAttribs.push(子空间已满) + } else { + if (numAllocatedPrefixes == numTotalPrefixes) + postAttribs.push(子前缀空间已满) + else + postAttribs.push(子前缀空间 {numAllocatedPrefixes}/{numTotalPrefixes}) + + if (node.numUsedInsnWords == node.numTotalInsnWords) + postAttribs.push(子编码空间已满) + else + postAttribs.push(子编码空间已用 {(node.numUsedInsnWords / node.numTotalInsnWords * 100).toFixed(2)}%) + } + } if (root) return <> @@ -131,109 +81,31 @@ function NodeTitle({ match, node, matchNumber, matchPattern, lookAt, parentLookA return <> - {representMatchValue(matchNumber, parentLookAt)}{preAttribs}: 检查 [{representBitfields(lookAt)}] 位 + {representMatchValue(matchNumber, parentLookAt)}: {preAttribs}检查 [{representBitfields(lookAt)}] 位 {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: , - key: key, + title: , + key: m.key, icon: , } } -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: , - key: matchPattern, + title: , + key: node.key, icon: , - 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, []) -} diff --git a/src/components/AsmDB/augmentedDecodeTree.tsx b/src/components/AsmDB/augmentedDecodeTree.tsx new file mode 100644 index 00000000..90e6a385 --- /dev/null +++ b/src/components/AsmDB/augmentedDecodeTree.tsx @@ -0,0 +1,164 @@ +import _ from 'lodash' + +import { bitfieldWidth, bitfieldsToMask, mergeBitfields, restoreIntoBitfields } from './bitfield' +import type { Bitfield, DecodeTreeMatch, DecodeTreeNode } from './types' + +const wellKnownMatchPatterns = { + '000000xxxxxxxxxxxxxxxxxxxxxxxxxx': '运算', + '00000000000000000xxxxxxxxxxxxxxx': '双寄存器', + '00000000000100xxxxxxxxxxxxxxxxxx': '三寄存器・甲', + '00000000000101xxxxxxxxxxxxxxxxxx': '三寄存器・乙', + '00000000000110xxxxxxxxxxxxxxxxxx': '三寄存器・丙', + '00000000000111xxxxxxxxxxxxxxxxxx': '三寄存器・乘', + '00000000001000xxxxxxxxxxxxxxxxxx': '三寄存器・除', + '00000000001001xxxxxxxxxxxxxxxxxx': '三寄存器・CRC', + '0000000100xxxxxxxxxxxxxxxxxxxxxx': 'FP', + '000001xxxxxxxxxxxxxxxxxxxxxxxxxx': '特权', + '00000110010010000xxxxxxxxxxxxxxx': 'IOCSR TLB', + '000010xxxxxxxxxxxxxxxxxxxxxxxxxx': '四寄存器', + '000011xxxxxxxxxxxxxxxxxxxxxxxxxx': '比较选择', + '000101xxxxxxxxxxxxxxxxxxxxxxxxxx': '大立即数・甲', + '000110xxxxxxxxxxxxxxxxxxxxxxxxxx': '大立即数・乙', + '000111xxxxxxxxxxxxxxxxxxxxxxxxxx': '大立即数・丙', + '001000xxxxxxxxxxxxxxxxxxxxxxxxxx': '访存・LLSC', + '001001xxxxxxxxxxxxxxxxxxxxxxxxxx': '访存・大立即数', + '001010xxxxxxxxxxxxxxxxxxxxxxxxxx': '访存・基本', + '001011xxxxxxxxxxxxxxxxxxxxxxxxxx': '访存・扩展・甲', + '001100xxxxxxxxxxxxxxxxxxxxxxxxxx': '访存・扩展・乙', + '001110xxxxxxxxxxxxxxxxxxxxxxxxxx': '访存・复杂', + '010010xxxxxxxxxxxxxxxxxxxxxxxxxx': '分支・扩展', + '011100xxxxxxxxxxxxxxxxxxxxxxxxxx': 'LSX', + '011101xxxxxxxxxxxxxxxxxxxxxxxxxx': 'LASX', +} + +function getWellKnownAlias(pat: string): string { + if (wellKnownMatchPatterns.hasOwnProperty(pat)) + return wellKnownMatchPatterns[pat] + return '' +} + +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('') +} + +export type AugmentedDecodeTreeMatch = DecodeTreeMatch & { + key: string + wellKnownAlias: string + rawMatch: number + mask: number + parentNode: AugmentedDecodeTreeNode + next?: AugmentedDecodeTreeNode + numUsedInsnWords: number + possibleFmts: string[] +} + +export type AugmentedDecodeTreeNode = DecodeTreeNode & { + key: string + wellKnownAlias: string + rawMatch: number + mask: number + parentMatch?: AugmentedDecodeTreeMatch + parentLookAt?: Bitfield[] + matches: AugmentedDecodeTreeMatch[] + numTotalInsnWords: number + numUsedInsnWords: number + possibleFmts: string[] +} + +export function augmentDecodeTree(node: DecodeTreeNode): AugmentedDecodeTreeNode { + let x = _.cloneDeep(node) as AugmentedDecodeTreeNode + augmentDecodeTreeInplace(x, null, 0, []) + return x +} + +function augmentDecodeTreeInplace( + node: AugmentedDecodeTreeNode, + nodeMatch: AugmentedDecodeTreeMatch, + matchSoFar: number, + 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 + + node.key = makeMatchPatternKey(matchSoFar, matchBitfieldsSoFar) + node.wellKnownAlias = getWellKnownAlias(node.key) + node.rawMatch = matchSoFar + node.mask = bitfieldsToMask(matchBitfieldsSoFar) + node.parentMatch = nodeMatch + node.parentLookAt = nodeMatch ? nodeMatch.parentNode.look_at : [] + for (const m of node.matches) { + const myMatch = matchSoFar | restoreIntoBitfields(m.match, node.look_at) + m.key = makeMatchPatternKey(myMatch, myMatchBitfields) + m.wellKnownAlias = getWellKnownAlias(m.key) + m.rawMatch = myMatch + m.mask = bitfieldsToMask(myMatchBitfields) + m.parentNode = node + + if (m.next) { + augmentDecodeTreeInplace(m.next, m, myMatch, myMatchBitfields) + node.numUsedInsnWords += m.next.numUsedInsnWords + m.possibleFmts = m.next.possibleFmts + } else { + m.numUsedInsnWords = 1 << (32 - myChildMatchWidth) + m.possibleFmts = [m.fmt] + node.numUsedInsnWords += m.numUsedInsnWords + } + } + + node.possibleFmts = _.uniq(_.flatten(_.map(node.matches, 'possibleFmts'))) + node.possibleFmts.sort() + + return node +} + +export type FlattenedAugmentedNode = { + key: string + node?: AugmentedDecodeTreeNode + match?: AugmentedDecodeTreeMatch +} + +export type AugmentedNodeMap = { [key: string]: FlattenedAugmentedNode } + +function makeFlattenedAugmentedNode( + node?: AugmentedDecodeTreeNode, + match?: AugmentedDecodeTreeMatch, +): FlattenedAugmentedNode { + const key = node ? node.key : match.key + return { + key: key, + node: node, + match: match, + } +} + +function flattenAugmentedDecodeTreeInto(node: AugmentedDecodeTreeNode, result: FlattenedAugmentedNode[]): void { + result.push(makeFlattenedAugmentedNode(node, null)) + result.push(...(_.map(node.matches, (x) => makeFlattenedAugmentedNode(null, x)))) + for (const m of node.matches) + if (m.next) + flattenAugmentedDecodeTreeInto(m.next, result) +} + +function flattenAugmentedDecodeTree(node: AugmentedDecodeTreeNode): FlattenedAugmentedNode[] { + const result = [] + flattenAugmentedDecodeTreeInto(node, result) + return result +} + +export function mapifyAugmentedDecodeTree(node: AugmentedDecodeTreeNode): AugmentedNodeMap { + return _.keyBy(flattenAugmentedDecodeTree(node), 'key') +} diff --git a/src/components/AsmDB/bitfield.ts b/src/components/AsmDB/bitfield.ts new file mode 100644 index 00000000..5672e61d --- /dev/null +++ b/src/components/AsmDB/bitfield.ts @@ -0,0 +1,50 @@ +import _ from 'lodash' + +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}` +} + +export function representBitfields(bfs: Bitfield[]): string { + if (bfs.length == 1) + return representBitfield(bfs[0]) + return _.map(_.reverse(_.cloneDeep(bfs)), representBitfield).join(',') +} + +export function bitfieldWidth(bfs: Bitfield[]): number { + return _.sum(_.map(bfs, (x) => x.len)) +} + +export 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 +} + +export 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 + } + return y +} + +export function bitfieldsToMask(bfs: Bitfield[]): number { + let mask = 0 + for (const bf of bfs) + mask |= (bf.len == 32 ? 0xffffffff : ((1 << bf.len) - 1)) << bf.lsb + return mask +} diff --git a/src/components/AsmDB/bits.module.css b/src/components/AsmDB/bits.module.css index dc1b1200..be060c9d 100644 --- a/src/components/AsmDB/bits.module.css +++ b/src/components/AsmDB/bits.module.css @@ -1,5 +1,6 @@ -.widget { +:root { --bit-palette-opcode: var(--ifm-color-emphasis-200); + --bit-palette-undecided: var(--ifm-color-emphasis-100); --bit-palette-p1: #feb30b; --bit-palette-p2: #66ccff; --bit-palette-p3: #90d408; @@ -37,6 +38,7 @@ font-family: var(--ifm-font-family-monospace); --bit-bg-p0: var(--bit-palette-opcode); + --bit-bg-undecided: var(--bit-palette-undecided); --bit-bg-p1: var(--bit-palette-p1); --bit-bg-p2: var(--bit-palette-p2); --bit-bg-p3: var(--bit-palette-p3); @@ -45,6 +47,12 @@ --bit-bg-p6: var(--bit-palette-p6); --bit-fg-p0: var(--ifm-color-content); + --bit-fg-p1: var(--ifm-color-black); + --bit-fg-p2: var(--ifm-color-black); + --bit-fg-p3: var(--ifm-color-black); + --bit-fg-p4: var(--ifm-color-black); + --bit-fg-p5: var(--ifm-color-black); + --bit-fg-p6: var(--ifm-color-black); } .bit { @@ -63,6 +71,10 @@ margin-left: 0; } +.bitEmph { + text-emphasis-style: triangle; +} + .hex { float: right; margin-bottom: 0; diff --git a/src/components/AsmDB/bits.tsx b/src/components/AsmDB/bits.tsx index 3c78f571..cc2996d2 100644 --- a/src/components/AsmDB/bits.tsx +++ b/src/components/AsmDB/bits.tsx @@ -1,56 +1,138 @@ +import clsx from 'clsx' +import { EyeOutlined } from '@ant-design/icons' +import _ from 'lodash' + import styles from './bits.module.css' -import InsnFormatName from './insnFormat' +import { isImmArg } from './insnFormat' +import InsnFormatName from './insnFormatName' import { getManualInsnFormatName } from './manualFormatNames' -import { BitPalette, styleFromBitPalette } from './palette' +import { BitPalette, type AlphaStep, styleFromBitPalette } from './palette' +import { ArgKind, type Insn, type InsnFormat } from './types' type BitsOptions = { insn: Insn useManualSyntax?: boolean } -type BitOptions = { - placeholder: boolean - value: 0|1 +type BitProps = { + isFixed: boolean + isUndecided: boolean + placeholder?: string | JSX.Element + value: 0 | 1 palette: BitPalette + alpha?: AlphaStep + isEmphasized: boolean } -function littleEndianBitsFromU32(x: number): (0|1)[] { - const y = new Array<0|1>(32) +function littleEndianBitsFromU32(x: number): (0 | 1)[] { + const y = new Array<0 | 1>(32) for (let i = 0; i < 32; i++) { y[i] = (x & (1 << i)) ? 1 : 0 } return y } -function Bit(props: BitOptions): JSX.Element { - return ( - {props.placeholder ? '' : props.value} - ) +const Bit: React.FC> = (props) => { + const classNames = [props.className, styles.bit] + if (props.isEmphasized) + classNames.push(styles.bitEmph) + return { + props.isFixed ? props.value : props.placeholder + } +} + +function placeholderForBit( + hasFmt: boolean, + isFixed: boolean, + isBeingChecked: boolean, +): string | JSX.Element { + if (isFixed) + return null + if (hasFmt) + return '' + return isBeingChecked ? : 'x' +} + +function getOperandPrefixForDisplay(k: ArgKind): string { + switch (k) { + case ArgKind.IntReg: + default: + return '' + case ArgKind.FPReg: + return 'F' + case ArgKind.FCCReg: + return 'C' + case ArgKind.ScratchReg: + return 'T' + case ArgKind.VReg: + return 'V' + case ArgKind.XReg: + return 'X' + case ArgKind.SignedImm: + return '±' + case ArgKind.UnsignedImm: + return 'U' + } } -function cookBits(x: Insn): BitOptions[] { - const bits = littleEndianBitsFromU32(x.word) - const maskBits = littleEndianBitsFromU32(x.mask) - const result = bits.map((b, i) => { +function cookBits( + word: number, + mask: number, + maskToCheck: number, + maskToEmph: number, + fmt: InsnFormat, +): BitProps[] { + const hasFmt = !!fmt?.repr + const bits = littleEndianBitsFromU32(word) + const maskBits = littleEndianBitsFromU32(mask) + const maskToCheckBits = littleEndianBitsFromU32(maskToCheck) + const maskToEmphBits = littleEndianBitsFromU32(maskToEmph) + const result: BitProps[] = bits.map((b, i) => { + const isFixed = maskBits[i] != 0 + const isUndecided = !isFixed && !hasFmt + const isBeingChecked = maskToCheckBits[i] != 0 + const isEmphasized = maskToEmphBits[i] != 0 + const placeholder = placeholderForBit(hasFmt, isFixed, isBeingChecked) return { - placeholder: maskBits[i] == 0, + isFixed, + isUndecided, + placeholder, value: b, // this will get refined later if we have insn fmt palette: maskBits[i] != 0 ? BitPalette.Opcode : BitPalette.P1, + isEmphasized, } }) - if (x.format.repr == '') { + if (!hasFmt) { // fmt isn't given, cannot proceed any further return result.reverse() } // assign color to bits according to insn format - x.format.args.forEach((arg, argIdx) => { + fmt.args.forEach((arg, argIdx) => { + const totalArgWidth = _.sum(_.map(arg.slots, 'width')) + const shouldApplyImmGradient = isImmArg(arg) && totalArgWidth >= 8 + const argMSB = totalArgWidth - 1 + let argBitIdx = argMSB for (const slot of arg.slots) { - for (let i = 0; i < slot.width; i++) { + for (let i = slot.width - 1; i >= 0; i--) { const bitIdx = slot.offset + i result[bitIdx].palette = (argIdx + 1) as BitPalette + + if (argBitIdx == argMSB) + result[bitIdx].placeholder = getOperandPrefixForDisplay(arg.kind) + + if (shouldApplyImmGradient) { + result[bitIdx].alpha = { + step: argBitIdx, + totalSteps: totalArgWidth - 1, + } + } + argBitIdx-- } } }) @@ -58,9 +140,28 @@ function cookBits(x: Insn): BitOptions[] { return result.reverse() } -export default function AsmDBBits(props: BitsOptions): JSX.Element { - const cookedBits = cookBits(props.insn) +export type BitsReprProps = { + word: number + mask: number + maskToCheck?: number + maskToEmph?: number + fmt?: InsnFormat +} +export const BitsRepr: React.FC> = (props) => { + const cookedBits = cookBits( + props.word, + props.mask, + props.maskToCheck || 0, + props.maskToEmph || 0, + props.fmt, + ) + return
+ {cookedBits.map((b, i) => ())} +
+} + +export function InsnBitsRepr(props: BitsOptions): JSX.Element { let insnFormatDesc: JSX.Element if (props.useManualSyntax) { const mfn = getManualInsnFormatName(props.insn) @@ -83,9 +184,7 @@ export default function AsmDBBits(props: BitsOptions): JSX.Element { return (
-
- {cookedBits.map((b, i) => ())} -
+ {insnFormatDesc}
= 0x{props.insn.word.toString(16).padStart(8, '0')} diff --git a/src/components/AsmDB/encodingSpaceOverviewPage.tsx b/src/components/AsmDB/encodingSpaceOverviewPage.tsx index 462905e9..07f926d1 100644 --- a/src/components/AsmDB/encodingSpaceOverviewPage.tsx +++ b/src/components/AsmDB/encodingSpaceOverviewPage.tsx @@ -1,8 +1,14 @@ -import { Col, Grid, Row, Statistic, Tree } from 'antd' +import { Col, Grid, Row, Statistic, Tag, Tree, TreeDataNode } from 'antd' import _ from 'lodash' +import { useState } from 'react' import styles from './index.module.css' -import { augmentDecodeTree, transformDecodeTreeForAntd, type AugmentedDecodeTreeNode } from './antdDecodeTreeAdapter' +import { transformDecodeTreeForAntd } from './antdDecodeTreeAdapter' +import { augmentDecodeTree, mapifyAugmentedDecodeTree, type AugmentedNodeMap } from './augmentedDecodeTree' +import { bitfieldWidth, bitfieldsToMask } from './bitfield' +import { BitsRepr } from './bits' +import { parseInsnFormat } from './insnFormat' +import type { AsmDBData, DecodeTreeNode } from './types' const { useBreakpoint } = Grid @@ -14,25 +20,120 @@ function decodeTreeDepth(node: DecodeTreeNode): number { } type DecodeTreeViewProps = { - node: AugmentedDecodeTreeNode + treeData: TreeDataNode + onMatchKeyChange?: (newVal: string) => void } const DecodeTreeView: React.FC> = (props) => { - const antdNode = transformDecodeTreeForAntd(props.node) + let onSelect = null + if (props.onMatchKeyChange) + onSelect = (sk: string[]) => props.onMatchKeyChange(sk[0]) return } +type DecodeTreeNodeDetailProps = { + data: AugmentedNodeMap + selectedKey: string +} + +const DecodeTreeNodeDetail: React.FC> = (props) => { + if (!props.data.hasOwnProperty(props.selectedKey)) + return <> +

编码空间明细

+

在译码决策树中选择一个节点,以查看其详细信息。

+ + + const vertMargin = { marginTop: 16 } + + const selectedNode = props.data[props.selectedKey] + if (!selectedNode.node) { + // insn + const m = selectedNode.match + const fmt = parseInsnFormat(m.fmt) + const subspaceUsageRatio = m.numUsedInsnWords / m.parentNode.numTotalInsnWords * 100 + const universeUsageRatio = m.numUsedInsnWords / 0x100000000 * 1000 + const universeUsageRatioText = universeUsageRatio < 0.001 ? '< 0.001' : universeUsageRatio.toFixed(3).toString() + + return <> +

{m.matched}

+ + + + + + + + + + + } + + // (sub)space + const n = selectedNode.node + const root = props.selectedKey == 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' + const title = root ? '根编码空间' : '子编码空间' + const allocationRatio = n.numUsedInsnWords / n.numTotalInsnWords * 100 + const subspaceUsageRatio = root ? 1 : n.numTotalInsnWords / n.parentMatch.parentNode.numTotalInsnWords * 100 + const universeUsageRatio = root ? 1 : n.numTotalInsnWords / 0x100000000 * 1000 + const universeUsageRatioText = universeUsageRatio < 0.001 ? '< 0.001' : universeUsageRatio.toFixed(3).toString() + const numAllocatedPrefixes = n.matches.length + const numTotalPrefixes = 1 << bitfieldWidth(n.look_at) + + let spaceUsageRatios = <> + if (!root) + spaceUsageRatios = + + + + + let insnFmtDisplay + if (n.possibleFmts.length == 1) + insnFmtDisplay = n.possibleFmts[0] + else if (n.possibleFmts.length < 50) + insnFmtDisplay = <> + {_.map(n.possibleFmts, (x) => {x})} + + else + insnFmtDisplay = `${n.possibleFmts.length} 种` + + return <> +

{title}

+ + + + + + {spaceUsageRatios} + + + + + + {/* https://github.com/ant-design/ant-design/issues/43830 */} + insnFmtDisplay} style={vertMargin} /> + +} + export default function EncodingSpaceOverviewPage({ data }: { data: AsmDBData }): JSX.Element { const numInsns = data.insns.length const augmentedDecodeTree = augmentDecodeTree(data.decodetree) + const augmentedDataMap = mapifyAugmentedDecodeTree(augmentedDecodeTree) + const antdNode = transformDecodeTreeForAntd(augmentedDecodeTree) const depth = decodeTreeDepth(data.decodetree) const numAllocatedOpcodes = data.decodetree.matches.length const numFirstPartyOpcodes = _.filter(data.decodetree.matches, (x) => x.match <= 0b011111).length @@ -43,6 +144,7 @@ export default function EncodingSpaceOverviewPage({ data }: { data: AsmDBData }) const firstPartyAllocationRatio = augmentedDecodeTree.numUsedInsnWords / 0x80000000 * 100 const screens = useBreakpoint() + const [selectedMatchKey, setSelectedMatchKey] = useState('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx') return <> @@ -64,11 +166,15 @@ export default function EncodingSpaceOverviewPage({ data }: { data: AsmDBData }) - - + + - - {/* TODO */} + + diff --git a/src/components/AsmDB/index.module.css b/src/components/AsmDB/index.module.css index 87fb4fc9..a9bb1e42 100644 --- a/src/components/AsmDB/index.module.css +++ b/src/components/AsmDB/index.module.css @@ -2,10 +2,36 @@ border: 1px solid var(--ifm-color-secondary-contrast-background); } -.attrib::before { +.attrib::before, +.contentAttrib::before { content: ' '; } -.attrib { +.attrib, +.insnFmtTagAttrib { color: var(--ifm-color-gray-700); } + +.aliasTagAttrib { + color: var(--ifm-color-info); + background-color: var(--ifm-color-info-contrast-background); +} + +.insnFmtTag, +.insnFmtTagAttrib { + font-family: var(--ifm-font-family-monospace); +} + +.aliasTagAttrib, +.insnFmtTagAttrib { + font-size: 14px; /* should follow default style of tree node titles */ + line-height: 20px; +} + +.insnFmtTagAttrib { + margin-left: 0.5em; +} + +.statsVal { + font-size: 18px; +} diff --git a/src/components/AsmDB/index.tsx b/src/components/AsmDB/index.tsx index 8470f484..a3d8be8a 100644 --- a/src/components/AsmDB/index.tsx +++ b/src/components/AsmDB/index.tsx @@ -7,6 +7,7 @@ import ThemeAwareAntdContainer from '@site/src/components/ThemeAwareAntdContaine import EncodingSpaceOverviewPage from './encodingSpaceOverviewPage' import InsnExplainerPage from './insnExplainerPage' import InsnListPage from './insnListPage' +import type { AsmDBData } from './types' export default function AsmDBPage({ data }: { data: AsmDBData }): JSX.Element { const panes = [ diff --git a/src/components/AsmDB/insn.ts b/src/components/AsmDB/insn.ts index 1d5cb2b6..d71318fe 100644 --- a/src/components/AsmDB/insn.ts +++ b/src/components/AsmDB/insn.ts @@ -1,3 +1,5 @@ +import type { Insn } from './types' + export function getInsnMnemonic(insn: Insn, useManualSyntax: boolean): string { if (useManualSyntax) return insn.manual_mnemonic ? insn.manual_mnemonic : insn.mnemonic diff --git a/src/components/AsmDB/insnExplainerPage.tsx b/src/components/AsmDB/insnExplainerPage.tsx index 3ac75c56..8033ed4b 100644 --- a/src/components/AsmDB/insnExplainerPage.tsx +++ b/src/components/AsmDB/insnExplainerPage.tsx @@ -1,3 +1,5 @@ +import type { AsmDBData } from "./types" + export default function InsnExplainerPage({ data }: { data: AsmDBData }): JSX.Element { return

TODO

} diff --git a/src/components/AsmDB/insnFormat.ts b/src/components/AsmDB/insnFormat.ts new file mode 100644 index 00000000..4b30d451 --- /dev/null +++ b/src/components/AsmDB/insnFormat.ts @@ -0,0 +1,176 @@ +import _ from "lodash" + +import { ArgKind, type ArgSlot, type InsnArg, type InsnFormat } from "./types" + +export function isImmArg(a: InsnArg): boolean { + return a.kind == ArgKind.SignedImm || a.kind == ArgKind.UnsignedImm +} + +export function parseInsnFormat(s: string): InsnFormat { + if (s == 'EMPTY') + return { repr: s, args: [] } + + const origInput = s + const args: InsnArg[] = [] + while (s) { + const { remaining, arg } = parseInsnArg(s) + if (!arg || s == remaining) + // malformed input + return null + + args.push(arg) + s = remaining + } + + return { + repr: origInput, + args: args, + } +} + +function parseInsnArg(s: string): { remaining: string, arg: InsnArg } { + const fail = { remaining: s, arg: null } + if (s.length == 0) + // malformed input + return fail + + const prefix = s[0] + if (!prefixKindMap.hasOwnProperty(prefix)) + // malformed input + return fail + + const kind = prefixKindMap[prefix] + switch (kind) { + case ArgKind.IntReg: { + const lsb = lsbMap[prefix.toLowerCase()] + return { + remaining: s.slice(1), + arg: { + kind, + repr: s[0], + slots: [{ repr: `${prefix.toLowerCase()}5`, offset: lsb, width: 5 }], + }, + } + } + + case ArgKind.SignedImm: + case ArgKind.UnsignedImm: { + const { remaining, slots } = parseArgSlots(s.slice(1)) + return { + remaining, + arg: { + kind, + repr: s.slice(0, s.length - remaining.length), + slots, + }, + } + } + + default: { + const idx = s[1] + const lsb = lsbMap[idx] + const width = regWidthMap[prefix] + return { + remaining: s.slice(2), + arg: { + kind, + repr: s.slice(0, 2), + slots: [{ repr: `${idx}${width}`, offset: lsb, width: width }], + }, + } + } + } +} + +const prefixKindMap = { + 'D': ArgKind.IntReg, + 'J': ArgKind.IntReg, + 'K': ArgKind.IntReg, + 'A': ArgKind.IntReg, + 'C': ArgKind.FCCReg, + 'F': ArgKind.FPReg, + 'T': ArgKind.ScratchReg, + 'V': ArgKind.VReg, + 'X': ArgKind.XReg, + 'S': ArgKind.SignedImm, + 'U': ArgKind.UnsignedImm, +} + +const regWidthMap = { + 'C': 3, + 'F': 5, + 'T': 2, + 'V': 5, + 'X': 5, +} + +const lsbMap = { + 'd': 0, + 'j': 5, + 'k': 10, + 'a': 15, + 'm': 16, + 'n': 18, +} + +function parseArgSlots(s: string): { remaining: string, slots: ArgSlot[] } { + const fail = { remaining: s, slots: null } + if (s.length == 0) + // malformed input + return fail + + const slots: ArgSlot[] = [] + while (s) { + const {remaining, finish, slot} = parseArgSlot(s) + if (finish) + break + if (!slot) + return fail + slots.push(slot) + s = remaining + } + + return { + remaining: s, + slots, + } +} + +function parseArgSlot(s: string): { remaining: string, finish: boolean, slot?: ArgSlot } { + const fail = { remaining: s, finish: false, slot: null } + if (s.length == 0) + // malformed input + return fail + + const lsbChar = s[0] + if (!lsbMap.hasOwnProperty(lsbChar)) { + if (prefixKindMap.hasOwnProperty(lsbChar)) + // we've finished + return { remaining: s, finish: true, slot: null } + + if (lsbChar == 'p') + // TODO + return fail + + // malformed input + return fail + } + const lsb = lsbMap[lsbChar] + + const widthMatch = /^\d+/.exec(s.slice(1)) + if (!widthMatch) + // malformed input + return fail + const width = parseInt(widthMatch[0], 10) + const totalConsumed = 1 + widthMatch[0].length + + return { + remaining: s.slice(totalConsumed), + finish: false, + slot: { + repr: s.slice(0, totalConsumed), + offset: lsb, + width, + } + } +} diff --git a/src/components/AsmDB/insnFormat.tsx b/src/components/AsmDB/insnFormatName.tsx similarity index 93% rename from src/components/AsmDB/insnFormat.tsx rename to src/components/AsmDB/insnFormatName.tsx index f2a9084e..1eddfd4d 100644 --- a/src/components/AsmDB/insnFormat.tsx +++ b/src/components/AsmDB/insnFormatName.tsx @@ -2,6 +2,7 @@ import clsx from 'clsx' import styles from './bits.module.css' import { styleFromBitPalette } from './palette' +import type { InsnFormat } from './types' type InsnFormatNameOptions = { fmt?: InsnFormat diff --git a/src/components/AsmDB/insnListPage.tsx b/src/components/AsmDB/insnListPage.tsx index 4e7643d8..1fefdbf5 100644 --- a/src/components/AsmDB/insnListPage.tsx +++ b/src/components/AsmDB/insnListPage.tsx @@ -2,8 +2,9 @@ import { Checkbox, Switch } from 'antd' import { useState } from 'react' import BoolFlag from '@site/src/components/BoolFlag' -import AsmDBBits from './bits' +import { InsnBitsRepr } from './bits' import { getInsnMnemonic } from './insn' +import type { AsmDBData, Insn, SubsetFlags } from './types' function Subsets({ ss }: { ss: SubsetFlags }): JSX.Element { if (ss.provisional) @@ -29,7 +30,7 @@ function AsmDBInsn({ insn, useManualSyntax }: { insn: Insn, useManualSyntax: boo return (

{getInsnMnemonic(insn, useManualSyntax)}

- +
) diff --git a/src/components/AsmDB/manualFormatNames.ts b/src/components/AsmDB/manualFormatNames.ts index 01362e14..c6e1294c 100644 --- a/src/components/AsmDB/manualFormatNames.ts +++ b/src/components/AsmDB/manualFormatNames.ts @@ -1,3 +1,5 @@ +import type { Insn } from "./types" + export function getManualInsnFormatName(insn: Insn): string { const a = getManualInsnFormatNameFromRepr(insn.format.repr) if (a != '') diff --git a/src/components/AsmDB/palette.tsx b/src/components/AsmDB/palette.tsx index 8268dade..89c19af2 100644 --- a/src/components/AsmDB/palette.tsx +++ b/src/components/AsmDB/palette.tsx @@ -14,9 +14,29 @@ function colorVarFromBitPalette(kind: 'bg' | 'fg', x: BitPalette): string { return `var(--bit-${kind}-p${x})` } -export function styleFromBitPalette(p: BitPalette): CSSProperties { - return { - color: colorVarFromBitPalette('fg', p), - backgroundColor: colorVarFromBitPalette('bg', p), +export type AlphaStep = { + step: number + totalSteps: number +} + +export function styleFromBitPalette( + p: BitPalette, + alpha?: AlphaStep, + undecided?: boolean, +): CSSProperties { + if (undecided) + return { + color: 'var(--bit-fg-undecided)', + backgroundColor: 'var(--bit-bg-undecided)', + } + + const color = colorVarFromBitPalette('fg', p) + let backgroundColor = colorVarFromBitPalette('bg', p) + if (alpha) { + const renderAlpha = (alpha.step / alpha.totalSteps) * 0.75 + 0.25 + const alphaPercent = (renderAlpha * 100).toFixed(0) + backgroundColor = `color-mix(in srgb, ${backgroundColor} ${alphaPercent}%, transparent)` } + + return { color, backgroundColor } } diff --git a/src/components/AsmDB/types.ts b/src/components/AsmDB/types.ts index 9e628790..a40192c2 100644 --- a/src/components/AsmDB/types.ts +++ b/src/components/AsmDB/types.ts @@ -1,9 +1,9 @@ -type AsmDBData = { +export type AsmDBData = { insns: Insn[] decodetree: DecodeTreeNode } -type Insn = { +export type Insn = { word: number mask: number mnemonic: string @@ -14,7 +14,7 @@ type Insn = { subsets: SubsetFlags } -type SubsetFlags = { +export type SubsetFlags = { la32?: boolean primary?: boolean la64?: boolean @@ -25,12 +25,12 @@ type SubsetFlags = { provisional?: boolean } -type InsnFormat = { +export type InsnFormat = { repr: string args: InsnArg[] } -type InsnArg = { +export type InsnArg = { kind: ArgKind repr: string slots: ArgSlot[] @@ -38,35 +38,36 @@ type InsnArg = { add_amt?: number } -enum ArgKind { +export enum ArgKind { Unknown = 0, + IntReg = 1, + FPReg = 2, + FCCReg = 3, + ScratchReg = 4, + VReg = 5, + XReg = 6, + SignedImm = 7, + UnsignedImm = 8, } -type ArgSlot = { +export type ArgSlot = { repr: string offset: number width: number } -type DecodeTreeNode = { +export type DecodeTreeNode = { look_at: Bitfield[] matches: DecodeTreeMatch[] } -type Bitfield = { +export type Bitfield = { lsb: number len: number } -enum DecodeTreeAction { - Unknown = 0, - Finish = 1, - Continue = 2, -} - -type DecodeTreeMatch = { +export type DecodeTreeMatch = { match: number - action: DecodeTreeAction fmt?: string matched?: string next?: DecodeTreeNode diff --git a/yarn.lock b/yarn.lock index c5b661a5..b0c5616d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -171,9 +171,9 @@ "@ctrl/tinycolor" "^3.6.1" "@ant-design/cssinjs@^1.18.5": - version "1.19.1" - resolved "https://registry.yarnpkg.com/@ant-design/cssinjs/-/cssinjs-1.19.1.tgz#d3bb4f58ee884c9c757688e611e7a6de9867ea75" - integrity sha512-hgQ3wiys3X0sqDKWkqCJ6EYdF79i9JCvtavmIGwuuPUKmoJXV8Ff0sY+yQQSxk2dRmMyam/bYKo/Bwor45hnZw== + version "1.20.0" + resolved "https://registry.yarnpkg.com/@ant-design/cssinjs/-/cssinjs-1.20.0.tgz#878bc6c5b08f73db76da54c347a7ebb3fa4858bb" + integrity sha512-uG3iWzJxgNkADdZmc6W0Ci3iQAUOvLMcM8SnnmWq3r6JeocACft4ChnY/YWvI2Y+rG/68QBla/O+udke1yH3vg== dependencies: "@babel/runtime" "^7.11.1" "@emotion/hash" "^0.8.0" @@ -2314,9 +2314,9 @@ integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== "@tybys/wasm-util@^0.8.1": - version "0.8.1" - resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.8.1.tgz#10360976b7f679497ea8526791006417ff304abb" - integrity sha512-GSsTwyBl4pIzsxAY5wroZdyQKyhXk0d8PCRZtrSZ2WEB1cBdrp2EgGBwHOGCZtIIPun/DL3+AykCv+J6fyRH4Q== + version "0.8.2" + resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.8.2.tgz#33aa636e019d60b3805df797c54d53e10155a291" + integrity sha512-kzI0zdUtC/ymNQCVQiqHCKobSSyppUHet+pHaiOdKUV48B/uKCEDznwf4N9tOoDKdh7Uuhm8V60xPU8VYbBbrQ== dependencies: tslib "^2.4.0" @@ -2390,9 +2390,9 @@ "@types/estree" "*" "@types/eslint@*": - version "8.56.9" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.9.tgz#403e9ced04a34e63f1c383c5b8ee1a94442c8cc4" - integrity sha512-W4W3KcqzjJ0sHg2vAq9vfml6OhsJ53TcUjUqfzzZf/EChUtwspszj/S0pzMxnfRcO55/iGq47dscXw71Fxc4Zg== + version "8.56.10" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.10.tgz#eb2370a73bf04a901eeba8f22595c7ee0f7eb58d" + integrity sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ== dependencies: "@types/estree" "*" "@types/json-schema" "*" @@ -2579,9 +2579,9 @@ integrity sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q== "@types/qs@*": - version "6.9.14" - resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.14.tgz#169e142bfe493895287bee382af6039795e9b75b" - integrity sha512-5khscbd3SwWMhFqylJBLQ0zIu7c1K6Vz0uBIt915BI3zV0q1nfjRQD3RqSBcPaO6PHEF4ov/t9y89fSiyThlPA== + version "6.9.15" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.15.tgz#adde8a060ec9c305a82de1babc1056e73bd64dce" + integrity sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg== "@types/range-parser@*": version "1.2.7" @@ -2615,9 +2615,9 @@ "@types/react" "*" "@types/react@*", "@types/react@^18": - version "18.2.78" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.78.tgz#94aec453d0ccca909998a2b4b2fd78af15a7d2fe" - integrity sha512-qOwdPnnitQY4xKlKayt42q5W5UQrSHjgoXNVEtxeqdITJ99k4VXJOP3vt8Rkm9HmgJpH50UNU+rlqfkfWOqp0A== + version "18.2.79" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.79.tgz#c40efb4f255711f554d47b449f796d1c7756d865" + integrity sha512-RwGAGXPl9kSXwdNTafkOEuFrTBD5SA2B3iEB96xi8+xu5ddUa/cpvyVCSNn+asgLCTHkb5ZxN8gbuibYJi4s1w== dependencies: "@types/prop-types" "*" csstype "^3.0.2" @@ -2911,9 +2911,9 @@ ajv@^8.0.0, ajv@^8.9.0: uri-js "^4.2.2" algoliasearch-helper@^3.13.3: - version "3.17.0" - resolved "https://registry.yarnpkg.com/algoliasearch-helper/-/algoliasearch-helper-3.17.0.tgz#b8f2f98c9a49d9affb51205f8df116164050a842" - integrity sha512-R5422OiQjvjlK3VdpNQ/Qk7KsTIGeM5ACm8civGifOVWdRRV/3SgXuKmeNxe94Dz6fwj/IgpVmXbHutU4mHubg== + version "3.18.0" + resolved "https://registry.yarnpkg.com/algoliasearch-helper/-/algoliasearch-helper-3.18.0.tgz#e3651090cc6c41f244a6db2573bdd116ca4b7085" + integrity sha512-ZXvA8r6VG46V343jnIE7Tei8Xr0/9N8YhD27joC0BKxeogQyvNu7O37i510wA7FnrDjoa/tFhK90WUaBlkaqnw== dependencies: "@algolia/events" "^4.0.1" @@ -2980,9 +2980,9 @@ ansi-styles@^6.1.0: integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== antd@^5: - version "5.16.1" - resolved "https://registry.yarnpkg.com/antd/-/antd-5.16.1.tgz#a258e73cac8c6e63c4ec44588e5474f0403cd33a" - integrity sha512-XAlLRrgYV+nj9FHnkXEPS6HNcKcluEa8v44e7Cixjlp8aOXRhUI6IfZaKpc2MPGjQ+06rp62/dsxOUNJW9kfLA== + version "5.16.2" + resolved "https://registry.yarnpkg.com/antd/-/antd-5.16.2.tgz#30ef0294699bc12b4fce5ca30baa5779c2bd2659" + integrity sha512-46BC1IaCRzkdcTAs1TuIZ1D56JrIRP5yWGrlfV+kGjro1c2dVaQP7yGXA2/m29uF41TqptxOYl36opY5ydN++A== dependencies: "@ant-design/colors" "^7.0.2" "@ant-design/cssinjs" "^1.18.5" @@ -2993,12 +2993,12 @@ antd@^5: "@rc-component/color-picker" "~1.5.3" "@rc-component/mutate-observer" "^1.1.0" "@rc-component/tour" "~1.14.2" - "@rc-component/trigger" "^2.0.0" + "@rc-component/trigger" "^2.1.1" classnames "^2.5.1" copy-to-clipboard "^3.3.3" dayjs "^1.11.10" qrcode.react "^3.1.0" - rc-cascader "~3.24.0" + rc-cascader "~3.24.1" rc-checkbox "~3.2.0" rc-collapse "~3.7.3" rc-dialog "~9.4.0" @@ -3013,12 +3013,12 @@ antd@^5: rc-motion "^2.9.0" rc-notification "~5.4.0" rc-pagination "~4.0.4" - rc-picker "~4.3.0" + rc-picker "~4.3.2" rc-progress "~4.0.0" rc-rate "~2.12.0" rc-resize-observer "^1.4.0" rc-segmented "~2.3.0" - rc-select "~14.13.0" + rc-select "~14.13.1" rc-slider "~10.5.0" rc-steps "~6.0.1" rc-switch "~4.1.0" @@ -3333,9 +3333,9 @@ caniuse-api@^3.0.0: lodash.uniq "^4.5.0" caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001587, caniuse-lite@^1.0.30001599: - version "1.0.30001609" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001609.tgz#fc34fad75c0c6d6d6303bdbceec2da8f203dabd6" - integrity sha512-JFPQs34lHKx1B5t1EpQpWH4c+29zIyn/haGsbpfq3suuV9v56enjFt23zqijxGTMwy1p/4H2tjnQMY+p1WoAyA== + version "1.0.30001611" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001611.tgz#4dbe78935b65851c2d2df1868af39f709a93a96e" + integrity sha512-19NuN1/3PjA3QI8Eki55N8my4LzfkMCRLgCVfrl/slbSAchQfV0+GwjPrK3rq37As4UCLlM/DHajbKkAqbv92Q== ccount@^2.0.0: version "2.0.1" @@ -3684,21 +3684,21 @@ copy-webpack-plugin@^11.0.0: serialize-javascript "^6.0.0" core-js-compat@^3.31.0, core-js-compat@^3.36.1: - version "3.36.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.36.1.tgz#1818695d72c99c25d621dca94e6883e190cea3c8" - integrity sha512-Dk997v9ZCt3X/npqzyGdTlq6t7lDBhZwGvV94PKzDArjp7BTRm7WlDAXYd/OWdeFHO8OChQYRJNJvUCqCbrtKA== + version "3.37.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.37.0.tgz#d9570e544163779bb4dff1031c7972f44918dc73" + integrity sha512-vYq4L+T8aS5UuFg4UwDhc7YNRWVeVZwltad9C/jV3R2LgVOpS9BDr7l/WL6BN0dbV3k1XejPTHqqEzJgsa0frA== dependencies: browserslist "^4.23.0" core-js-pure@^3.30.2: - version "3.36.1" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.36.1.tgz#1461c89e76116528b54eba20a0aff30164087a94" - integrity sha512-NXCvHvSVYSrewP0L5OhltzXeWFJLo2AL2TYnj6iLV3Bw8mM62wAQMNgUCRI6EBu6hVVpbCxmOPlxh1Ikw2PfUA== + version "3.37.0" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.37.0.tgz#ce99fb4a7cec023fdbbe5b5bd1f06bbcba83316e" + integrity sha512-d3BrpyFr5eD4KcbRvQ3FTUx/KWmaDesr7+a3+1+P46IUnNoEt+oiLijPINZMEon7w9oGkIINWxrBAU9DEciwFQ== core-js@^3.31.1: - version "3.36.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.36.1.tgz#c97a7160ebd00b2de19e62f4bbd3406ab720e578" - integrity sha512-BTvUrwxVBezj5SZ3f10ImnX2oRByMxql3EimVqMysepbC9EeMUOpLwdy6Eoili2x6E4kf+ZUB5k/+Jv55alPfA== + version "3.37.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.37.0.tgz#d8dde58e91d156b2547c19d8a4efd5c7f6c426bb" + integrity sha512-fu5vHevQ8ZG4og+LXug8ulUtVxjOcEYvifJr7L5Bfq9GOztVqsKd9/59hUk2ZSbCrS3BqUr3EpaYGIYzq7g3Ug== core-util-is@~1.0.0: version "1.0.3" @@ -3912,12 +3912,9 @@ cytoscape-cose-bilkent@^4.1.0: cose-base "^1.0.0" cytoscape@^3.28.1: - version "3.28.1" - resolved "https://registry.yarnpkg.com/cytoscape/-/cytoscape-3.28.1.tgz#f32c3e009bdf32d47845a16a4cd2be2bbc01baf7" - integrity sha512-xyItz4O/4zp9/239wCcH8ZcFuuZooEeF8KHRmzjDfGdXsj3OG9MFSMA0pJE0uX3uCN/ygof6hHf4L7lst+JaDg== - dependencies: - heap "^0.2.6" - lodash "^4.17.21" + version "3.29.0" + resolved "https://registry.yarnpkg.com/cytoscape/-/cytoscape-3.29.0.tgz#fb3b25b78a4a5ab2ca267e54f5f09725fdc006d8" + integrity sha512-ADqhlrCKhhQF8s/s3hTpvVAIyWwsfgFI/hD2vhAXc2ndncJFVZaq3/uBkDIhf4RrNwPw93vUarW36x6rFbUk0Q== "d3-array@1 - 2": version "2.12.1" @@ -4467,14 +4464,14 @@ ee-first@1.1.1: integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== electron-to-chromium@^1.4.668: - version "1.4.736" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.736.tgz#ecb4348f4d5c70fb1e31c347e5bad6b751066416" - integrity sha512-Rer6wc3ynLelKNM4lOCg7/zPQj8tPOCB2hzD32PX9wd3hgRRi9MxEbmkFCokzcEhRVMiOVLjnL9ig9cefJ+6+Q== + version "1.4.745" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.745.tgz#9c202ce9cbf18a5b5e0ca47145fd127cc4dd2290" + integrity sha512-tRbzkaRI5gbUn5DEvF0dV4TQbMZ5CLkWeTAXmpC9IrYT+GE+x76i9p+o3RJ5l9XmdQlI1pPhVtE9uNcJJ0G0EA== elkjs@^0.9.0: - version "0.9.2" - resolved "https://registry.yarnpkg.com/elkjs/-/elkjs-0.9.2.tgz#3d4ef6f17fde06a5d7eaa3063bb875e25e59e972" - integrity sha512-2Y/RaA1pdgSHpY0YG4TYuYCD2wh97CRvu22eLG3Kz0pgQ/6KbIFTxsTnDc4MH/6hFlg2L/9qXrDMG0nMjP63iw== + version "0.9.3" + resolved "https://registry.yarnpkg.com/elkjs/-/elkjs-0.9.3.tgz#16711f8ceb09f1b12b99e971b138a8384a529161" + integrity sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ== emoji-regex@^8.0.0: version "8.0.0" @@ -5327,11 +5324,6 @@ he@^1.2.0: resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -heap@^0.2.6: - version "0.2.7" - resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.7.tgz#1e6adf711d3f27ce35a81fe3b7bd576c2260a8fc" - integrity sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg== - history@^4.9.0: version "4.10.1" resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" @@ -5647,9 +5639,9 @@ ipaddr.js@1.9.1: integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== ipaddr.js@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.1.0.tgz#2119bc447ff8c257753b196fc5f1ce08a4cdf39f" - integrity sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ== + version "2.2.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz#d33fa7bac284f4de7af949638c9d68157c6b92e8" + integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== is-alphabetical@^2.0.0: version "2.0.1" @@ -6997,9 +6989,9 @@ micromark-util-subtokenize@^1.0.0: uvu "^0.5.0" micromark-util-subtokenize@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.0.tgz#9f412442d77e0c5789ffdf42377fa8a2bcbdf581" - integrity sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg== + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.1.tgz#76129c49ac65da6e479c09d0ec4b5f29ec6eace5" + integrity sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q== dependencies: devlop "^1.0.0" micromark-util-chunked "^2.0.0" @@ -7125,9 +7117,9 @@ mimic-response@^4.0.0: integrity sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg== mini-css-extract-plugin@^2.7.6: - version "2.8.1" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.8.1.tgz#75245f3f30ce3a56dbdd478084df6fe475f02dc7" - integrity sha512-/1HDlyFRxWIZPI1ZpgqlZ8jMw/1Dp/dl3P0L1jtZ+zVcHqwPhGwaJwKL00WVgfnBy6PWCde9W65or7IIETImuA== + version "2.9.0" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz#c73a1327ccf466f69026ac22a8e8fd707b78a235" + integrity sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA== dependencies: schema-utils "^4.0.0" tapable "^2.2.1" @@ -8027,7 +8019,7 @@ raw-body@2.5.2: iconv-lite "0.4.24" unpipe "1.0.0" -rc-cascader@~3.24.0: +rc-cascader@~3.24.1: version "3.24.1" resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.24.1.tgz#603bac4427f8865b6ec056705bae8485c65d7798" integrity sha512-RgKuYgEGPx+6wCgguYFHjMsDZdCyydZd58YJRCfYQ8FObqLnZW0x/vUcEyPjhWIj1EhjV958IcR+NFPDbbj9kg== @@ -8194,7 +8186,7 @@ rc-pagination@~4.0.4: classnames "^2.3.2" rc-util "^5.38.0" -rc-picker@~4.3.0: +rc-picker@~4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/rc-picker/-/rc-picker-4.3.2.tgz#b62e02122b3b6043a5a347674e5d003b8e51873e" integrity sha512-2NtobLxG2YqllXn4YczbupgIH6PSqzjCfFCnGlgPIY9k0HZti8WmBPjS1OD9JKQl+Tdg0pMVUeTEc07y4X9ZRQ== @@ -8244,7 +8236,7 @@ rc-segmented@~2.3.0: rc-motion "^2.4.4" rc-util "^5.17.0" -rc-select@~14.13.0: +rc-select@~14.13.0, rc-select@~14.13.1: version "14.13.1" resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.13.1.tgz#2d3ca539032bf8184e511597bb93436e5b9cf02c" integrity sha512-A1VHqjIOemxLnUGRxLGVqXBs8jGcJemI5NXxOJwU5PQc1wigAu1T4PRLgMkTPDOz8gPhlY9dwsPzMgakMc2QjQ== @@ -8369,9 +8361,9 @@ rc-util@^5.0.1, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18.1, rc-util@^5.2. react-is "^18.2.0" rc-virtual-list@^3.11.1, rc-virtual-list@^3.5.1, rc-virtual-list@^3.5.2: - version "3.11.4" - resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.11.4.tgz#d0a8937843160b7b00d5586854290bf56d396af7" - integrity sha512-NbBi0fvyIu26gP69nQBiWgUMTPX3mr4FcuBQiVqagU0BnuX8WQkiivnMs105JROeuUIFczLrlgUhLQwTWV1XDA== + version "3.11.5" + resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.11.5.tgz#d4ba3bbd8e7ceae846f575a7d982d061ace1e76e" + integrity sha512-iZRW99m5jAxtwKNPLwUrPryurcnKpXBdTyhuBp6ythf7kg/otKO5cCiIvL55GQwU0QGSlouQS0tnkciRMJUwRQ== dependencies: "@babel/runtime" "^7.20.0" classnames "^2.2.6"