mirror of
https://github.com/encounter/ppc750cl.git
synced 2025-10-04 17:29:41 +00:00
Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
4643ee0fd3 | |||
817ce36ee3 | |||
108aa7673a | |||
c75d090d82 | |||
2204612dfb | |||
|
c540ce97ca | ||
5cc2a69939 | |||
|
cfd00bcbc5 |
235
Cargo.lock
generated
235
Cargo.lock
generated
@ -4,9 +4,9 @@ version = 3
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.15"
|
||||
version = "0.6.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
|
||||
checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
@ -19,64 +19,65 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.8"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
|
||||
checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.5"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
|
||||
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.1"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
|
||||
checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.4"
|
||||
version = "3.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
|
||||
checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys 0.52.0",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.87"
|
||||
version = "1.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10f00e1f6e58a40e807377c75c6a7f97bf9044fab57816f2414e6f5f4499d7b8"
|
||||
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.3.0"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.17"
|
||||
version = "4.5.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac"
|
||||
checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.17"
|
||||
version = "4.5.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73"
|
||||
checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@ -86,61 +87,68 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.2"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
|
||||
checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.2"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
|
||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "2.1.0"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8"
|
||||
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.3.11"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
|
||||
checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
|
||||
dependencies = [
|
||||
"powerfmt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
version = "0.15.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.9"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.5.0"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
|
||||
checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -151,9 +159,9 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.11"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
@ -163,15 +171,15 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.158"
|
||||
version = "0.2.174"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.22"
|
||||
version = "0.4.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
@ -190,9 +198,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.16.0"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
|
||||
checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
@ -208,19 +216,26 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.11.2"
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
||||
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_codegen"
|
||||
version = "0.11.2"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a"
|
||||
checksum = "efbdcb6f01d193b17f0b9c3360fa7e0e620991b193ff08702f78b3ce365d7e61"
|
||||
dependencies = [
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
@ -228,19 +243,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.11.2"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
|
||||
checksum = "2cbb1126afed61dd6368748dae63b1ee7dc480191c6262a3b4ff1e29d86a6c5b"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"phf_shared",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.11.2"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
|
||||
checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
@ -252,33 +267,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
|
||||
[[package]]
|
||||
name = "ppc750cl"
|
||||
version = "0.3.3"
|
||||
name = "powerpc"
|
||||
version = "0.4.1"
|
||||
|
||||
[[package]]
|
||||
name = "ppc750cl-asm"
|
||||
version = "0.3.3"
|
||||
name = "powerpc-asm"
|
||||
version = "0.4.1"
|
||||
dependencies = [
|
||||
"phf",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppc750cl-fuzz"
|
||||
version = "0.3.3"
|
||||
name = "powerpc-fuzz"
|
||||
version = "0.4.1"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"num_cpus",
|
||||
"ppc750cl",
|
||||
"powerpc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppc750cl-genisa"
|
||||
version = "0.3.3"
|
||||
name = "powerpc-genisa"
|
||||
version = "0.4.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
"log",
|
||||
"num-traits",
|
||||
"phf",
|
||||
"phf_codegen",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
@ -291,9 +307,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.22"
|
||||
version = "0.2.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba"
|
||||
checksum = "061c1221631e079b26479d25bbf2275bfe5917ae8419cd7e34f13bfc2aa7539a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
@ -301,57 +317,42 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.86"
|
||||
version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.37"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.18"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.210"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
|
||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.210"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
|
||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -385,9 +386,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.11"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
|
||||
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
@ -397,9 +398,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.77"
|
||||
version = "2.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
|
||||
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -407,10 +408,30 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.36"
|
||||
name = "thiserror"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
|
||||
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
@ -425,15 +446,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.2"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
||||
checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.18"
|
||||
version = "0.2.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
|
||||
checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
@ -441,9 +462,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "unsafe-libyaml"
|
||||
@ -468,9 +489,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
13
Cargo.toml
13
Cargo.toml
@ -2,18 +2,11 @@
|
||||
members = ["asm", "disasm", "fuzz", "genisa"]
|
||||
resolver = "2"
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
|
||||
[profile.release-lto]
|
||||
inherits = "release"
|
||||
lto = true
|
||||
|
||||
[workspace.package]
|
||||
version = "0.3.3"
|
||||
version = "0.4.1"
|
||||
edition = "2021"
|
||||
authors = ["Luke Street <luke@street.dev>"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
keywords = ["powerpc", "wii", "gamecube"]
|
||||
repository = "https://github.com/encounter/ppc750cl"
|
||||
keywords = ["powerpc", "disassembler", "assembler", "ppc"]
|
||||
repository = "https://github.com/encounter/powerpc-rs.git"
|
||||
rust-version = "1.74"
|
||||
|
101
README.md
101
README.md
@ -1,47 +1,96 @@
|
||||
# ppc750cl [![Build Status]][actions] [![Latest Version]][crates.io] [![Api Rustdoc]][rustdoc] ![Rust Version]
|
||||
# powerpc [![Build Status]][actions] [![Latest Version]][crates.io] [![Api Rustdoc]][rustdoc] ![Rust Version]
|
||||
|
||||
[Build Status]: https://github.com/encounter/ppc750cl/actions/workflows/test.yml/badge.svg
|
||||
[actions]: https://github.com/encounter/ppc750cl/actions
|
||||
[Latest Version]: https://img.shields.io/crates/v/ppc750cl.svg
|
||||
[crates.io]: https://crates.io/crates/ppc750cl
|
||||
[Build Status]: https://github.com/encounter/powerpc-rs/actions/workflows/test.yml/badge.svg
|
||||
[actions]: https://github.com/encounter/powerpc-rs/actions
|
||||
[Latest Version]: https://img.shields.io/crates/v/powerpc.svg
|
||||
[crates.io]: https://crates.io/crates/powerpc
|
||||
[Api Rustdoc]: https://img.shields.io/badge/api-rustdoc-blue.svg
|
||||
[rustdoc]: https://docs.rs/ppc750cl
|
||||
[rustdoc]: https://docs.rs/powerpc
|
||||
[Rust Version]: https://img.shields.io/badge/rust-1.74+-blue.svg?maxAge=3600
|
||||
|
||||
Rust tools for working with the PowerPC 750CL / 750CXe family of processors.
|
||||
(Previously `ppc750cl`)
|
||||
|
||||
### Building
|
||||
Rust disassembler and assembler for the PowerPC ISA.
|
||||
|
||||
```shell
|
||||
cargo run --package ppc750cl-genisa
|
||||
cargo build --release
|
||||
### Supported Extensions
|
||||
|
||||
- PowerPC 64-bit
|
||||
- Paired Singles
|
||||
- PowerPC 750CXe "Gekko" (Nintendo GameCube)
|
||||
- PowerPC 750CL "Broadway" (Nintendo Wii)
|
||||
- AltiVec
|
||||
- VMX128
|
||||
- PowerPC "Xenon" (Xbox 360)
|
||||
|
||||
If you need support for other extensions, please open an issue.
|
||||
|
||||
## Usage
|
||||
|
||||
In Cargo.toml:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
powerpc = "0.4" # disassembler
|
||||
powerpc-asm = "0.4" # assembler
|
||||
```
|
||||
|
||||
### Instruction Set
|
||||
Disassembling and printing instructions:
|
||||
|
||||
For those unfamiliar with PowerPC, here are some basics.
|
||||
- PowerPC 7xx is a family of RISC CPUs produced from 1997 to 2012.
|
||||
- They operate with 32-bit words and every instruction is 32-bits wide.
|
||||
- This project focuses (only) on compatibility with the PowerPC 750CL and 750CXe.
|
||||
- PowerPC 750CL ("Broadway") is used in the Nintendo Wii.
|
||||
- PowerPC 750CXe ("Gekko") is used in the Nintendo GameCube.
|
||||
- They add a "paired-singles" SIMD unit and a bunch of other instructions.
|
||||
```rust
|
||||
use powerpc::{Argument, Extensions, Ins, Opcode, Simm, GPR};
|
||||
|
||||
### isa.yaml
|
||||
let ins = Ins::new(0x38A00000, Extensions::none());
|
||||
assert_eq!(ins.op, Opcode::Addi);
|
||||
|
||||
The file [isa.yaml](./isa.yaml) contains a full definition of the PowerPC 750CL / 750CXe instruction set.
|
||||
// Basic form
|
||||
let parsed = ins.basic();
|
||||
assert_eq!(parsed.args[0], Argument::GPR(GPR(5)));
|
||||
assert_eq!(parsed.args[1], Argument::GPR(GPR(0)));
|
||||
assert_eq!(parsed.args[2], Argument::Simm(Simm(0)));
|
||||
assert_eq!(parsed.to_string(), "addi r5, r0, 0x0");
|
||||
|
||||
It powers the disassembler and assembler.
|
||||
// Simplified form
|
||||
let parsed = ins.simplified();
|
||||
assert_eq!(parsed.to_string(), "li r5, 0x0");
|
||||
```
|
||||
|
||||
Similarly to LLVM TableGen, the program `ppc750cl-genisa` generates a Rust file implementing an instruction decoder.
|
||||
Assembling instructions:
|
||||
|
||||
### Safety & Correctness
|
||||
```rust
|
||||
use powerpc_asm::{assemble, Argument, Arguments};
|
||||
|
||||
- The disassembler has been fuzzed over all ~4.29 billion possible instructions (via `ppc750cl-fuzz`).
|
||||
let args: Arguments = [
|
||||
Argument::Unsigned(5),
|
||||
Argument::Unsigned(0),
|
||||
Argument::Signed(0),
|
||||
Argument::None,
|
||||
Argument::None,
|
||||
];
|
||||
let code = assemble("addi", &args).expect("Invalid arguments");
|
||||
assert_eq!(code, 0x38A00000); // addi r5, r0, 0x0
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
```
|
||||
cargo run --package powerpc-genisa
|
||||
cargo test
|
||||
```
|
||||
|
||||
## isa.yaml
|
||||
|
||||
The file [isa.yaml](./isa.yaml) contains a definition of the PowerPC instruction set.
|
||||
|
||||
Similarly to LLVM TableGen, the program `powerpc-genisa` generates Rust files implementing core functionality
|
||||
for the disassembler and assembler.
|
||||
|
||||
## Safety & Correctness
|
||||
|
||||
- The disassembler has been fuzzed over all ~4.29 billion possible instructions (via `powerpc-fuzz`).
|
||||
- It is safe to run the disassembler over untrusted byte arrays.
|
||||
- However, no guarantees on correctness are made (yet). Expect bugs.
|
||||
|
||||
### Performance
|
||||
## Performance
|
||||
|
||||
With a single thread on Ryzen 9 3900X:
|
||||
|
||||
|
@ -1,14 +1,14 @@
|
||||
[package]
|
||||
name = "ppc750cl-asm"
|
||||
name = "powerpc-asm"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
description = "Assembler for PowerPC 750CL"
|
||||
description = "PowerPC assembler"
|
||||
readme = "../README.md"
|
||||
keywords.workspace = true
|
||||
repository.workspace = true
|
||||
documentation = "https://docs.rs/ppc750cl-asm"
|
||||
documentation = "https://docs.rs/powerpc-asm"
|
||||
rust-version.workspace = true
|
||||
|
||||
[features]
|
||||
@ -16,4 +16,5 @@ default = ["std"]
|
||||
std = []
|
||||
|
||||
[dependencies]
|
||||
phf = "0.11"
|
||||
phf = "0.12"
|
||||
thiserror = "2.0"
|
||||
|
15552
asm/src/generated.rs
15552
asm/src/generated.rs
File diff suppressed because it is too large
Load Diff
@ -1,23 +1,22 @@
|
||||
use crate::Arguments;
|
||||
use core::fmt::Formatter;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ArgumentError {
|
||||
#[error(
|
||||
"argument index {index} is out of range: {value} (expected between {start} and {end})"
|
||||
)]
|
||||
ArgOutOfRangeUnsigned { index: usize, value: u32, start: u32, end: u32 },
|
||||
#[error(
|
||||
"argument index {index} is out of range: {value} (expected between {start} and {end})"
|
||||
)]
|
||||
ArgOutOfRangeSigned { index: usize, value: i32, start: i32, end: i32 },
|
||||
#[error("unexpected number of arguments: {value} (expected at most {expected})")]
|
||||
ArgCount { value: usize, expected: usize },
|
||||
#[error("unknown instruction mnemonic")]
|
||||
UnknownMnemonic,
|
||||
}
|
||||
|
||||
impl core::fmt::Display for ArgumentError {
|
||||
fn fmt(&self, _f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl std::error::Error for ArgumentError {}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum Argument {
|
||||
#[default]
|
||||
|
@ -1,24 +1,24 @@
|
||||
use ppc750cl_asm::*;
|
||||
use Argument::{None, Signed as S, Unsigned as U};
|
||||
use powerpc_asm::*;
|
||||
use Argument::{None as N, Signed as S, Unsigned as U};
|
||||
|
||||
macro_rules! assert_asm {
|
||||
($mnemonic:literal, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5: expr, $code:literal) => {{
|
||||
assert_eq!(assemble($mnemonic, &[$arg1, $arg2, $arg3, $arg4, $arg5]).unwrap(), $code)
|
||||
}};
|
||||
($mnemonic:literal, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $code:literal) => {{
|
||||
assert_eq!(assemble($mnemonic, &[$arg1, $arg2, $arg3, $arg4, None]).unwrap(), $code)
|
||||
assert_eq!(assemble($mnemonic, &[$arg1, $arg2, $arg3, $arg4, N]).unwrap(), $code)
|
||||
}};
|
||||
($mnemonic:literal, $arg1:expr, $arg2:expr, $arg3:expr, $code:literal) => {{
|
||||
assert_eq!(assemble($mnemonic, &[$arg1, $arg2, $arg3, None, None]).unwrap(), $code)
|
||||
assert_eq!(assemble($mnemonic, &[$arg1, $arg2, $arg3, N, N]).unwrap(), $code)
|
||||
}};
|
||||
($mnemonic:literal, $arg1:expr, $arg2:expr, $code:literal) => {{
|
||||
assert_eq!(assemble($mnemonic, &[$arg1, $arg2, None, None, None]).unwrap(), $code)
|
||||
assert_eq!(assemble($mnemonic, &[$arg1, $arg2, N, N, N]).unwrap(), $code)
|
||||
}};
|
||||
($mnemonic:literal, $arg1:expr, $code:literal) => {{
|
||||
assert_eq!(assemble($mnemonic, &[$arg1, None, None, None, None]).unwrap(), $code)
|
||||
assert_eq!(assemble($mnemonic, &[$arg1, N, N, N, N]).unwrap(), $code)
|
||||
}};
|
||||
($mnemonic:literal, $code:literal) => {{
|
||||
assert_eq!(assemble($mnemonic, &[None, None, None, None, None]).unwrap(), $code)
|
||||
assert_eq!(assemble($mnemonic, &[N, N, N, N, N]).unwrap(), $code)
|
||||
}};
|
||||
}
|
||||
|
||||
@ -180,3 +180,38 @@ fn test_ins_xor() {
|
||||
assert_asm!("xor", U(5), U(0), U(5), 0x7C052A78); // xor r5, r0, r5
|
||||
assert_asm!("xor.", U(7), U(9), U(10), 0x7D275279); // xor. r7, r9, r10
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sync() {
|
||||
assert_asm!("sync", 0x7C0004AC); // sync
|
||||
assert_asm!("sync", U(0), 0x7C0004AC); // sync 0
|
||||
assert_asm!("lwsync", 0x7C2004AC); // lwsync
|
||||
assert_asm!("sync", U(1), 0x7C2004AC); // sync 1
|
||||
assert_asm!("ptesync", 0x7C4004AC); // ptesync
|
||||
assert_asm!("sync", U(2), 0x7C4004AC); // sync 2
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rldcl() {
|
||||
assert_asm!("rldcl", U(3), U(0), U(6), U(27), 0x780336D0); // rldcl r3, r0, r6, 27
|
||||
assert_asm!("rotld", U(3), U(0), U(6), 0x78033010); // rotld r3, r0, r6
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rldic() {
|
||||
assert_asm!("rldic", U(5), U(6), U(3), U(36), 0x78C51928); // rldic r5, r6, 3, 36
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rldicl() {
|
||||
assert_asm!("rldicl", U(5), U(6), U(0), U(32), 0x78C50020); // rldicl r5, r6, 0, 32
|
||||
assert_asm!("rldicl", U(11), U(29), U(0), U(62), 0x7BAB07A0); // rldicl r11, r29, 0, 62
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dss() {
|
||||
assert_asm!("dss", U(1), U(0), 0x7C20066C); // dss 1, 0
|
||||
assert_asm!("dss", U(1), 0x7C20066C); // dss 1
|
||||
assert_asm!("dss", U(0), U(1), 0x7E00066C); // dss 0, 1
|
||||
assert_asm!("dssall", 0x7E00066C); // dssall
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
[package]
|
||||
name = "ppc750cl"
|
||||
name = "powerpc"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
description = "Disassembler for PowerPC 750CL"
|
||||
description = "PowerPC disassembler"
|
||||
readme = "../README.md"
|
||||
keywords.workspace = true
|
||||
repository.workspace = true
|
||||
documentation = "https://docs.rs/ppc750cl"
|
||||
documentation = "https://docs.rs/powerpc"
|
||||
rust-version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
|
@ -1,13 +1,13 @@
|
||||
use core::{
|
||||
fmt,
|
||||
fmt::{Display, Formatter, LowerHex},
|
||||
};
|
||||
|
||||
use crate::generated::{
|
||||
parse_basic, parse_defs, parse_simplified, parse_uses, Arguments, Opcode, EMPTY_ARGS,
|
||||
parse_basic, parse_defs, parse_simplified, parse_uses, Arguments, Extension, Opcode, EMPTY_ARGS,
|
||||
};
|
||||
use core::{
|
||||
fmt::{self, Display, Formatter, LowerHex},
|
||||
hash::{Hash, Hasher},
|
||||
ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Not},
|
||||
};
|
||||
|
||||
/// A PowerPC 750CL instruction.
|
||||
/// A PowerPC instruction.
|
||||
#[derive(Default, Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Ins {
|
||||
pub code: u32,
|
||||
@ -16,8 +16,9 @@ pub struct Ins {
|
||||
|
||||
impl Ins {
|
||||
/// Create a new instruction from its raw code.
|
||||
pub fn new(code: u32) -> Self {
|
||||
Self { code, op: Opcode::_detect(code) }
|
||||
#[inline]
|
||||
pub fn new(code: u32, extensions: Extensions) -> Self {
|
||||
Self { code, op: Opcode::detect(code, extensions) }
|
||||
}
|
||||
|
||||
/// Parse the instruction into a simplified mnemonic, if any match.
|
||||
@ -129,6 +130,22 @@ impl Ins {
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Ins {
|
||||
#[inline]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.code.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Opcode {
|
||||
/// Opcode enum discriminants are not stable.
|
||||
/// Instead, hash the mnemonic string.
|
||||
#[inline]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.mnemonic().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! field_arg_no_display {
|
||||
($name:ident, $typ:ident) => {
|
||||
#[derive(Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
|
||||
@ -293,6 +310,8 @@ impl From<u8> for OpaqueU {
|
||||
OpaqueU(x as u16)
|
||||
}
|
||||
}
|
||||
// Vector register.
|
||||
field_arg!(VR, u8, "v{}");
|
||||
|
||||
#[derive(Debug, Default, Copy, Clone, Eq, Hash, PartialEq)]
|
||||
pub enum Argument {
|
||||
@ -310,6 +329,7 @@ pub enum Argument {
|
||||
Offset(Offset),
|
||||
BranchDest(BranchDest),
|
||||
OpaqueU(OpaqueU),
|
||||
VR(VR),
|
||||
}
|
||||
|
||||
impl Display for Argument {
|
||||
@ -328,11 +348,12 @@ impl Display for Argument {
|
||||
Argument::Offset(x) => x.fmt(f),
|
||||
Argument::BranchDest(x) => x.fmt(f),
|
||||
Argument::OpaqueU(x) => x.fmt(f),
|
||||
Argument::VR(x) => x.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A parsed PowerPC 750CL instruction.
|
||||
/// A parsed PowerPC instruction.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct ParsedIns {
|
||||
pub mnemonic: &'static str,
|
||||
@ -340,6 +361,7 @@ pub struct ParsedIns {
|
||||
}
|
||||
|
||||
impl Default for ParsedIns {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
@ -347,6 +369,7 @@ impl Default for ParsedIns {
|
||||
|
||||
impl ParsedIns {
|
||||
/// An empty parsed instruction.
|
||||
#[inline]
|
||||
pub const fn new() -> Self {
|
||||
Self { mnemonic: "<illegal>", args: EMPTY_ARGS }
|
||||
}
|
||||
@ -369,7 +392,7 @@ impl Display for ParsedIns {
|
||||
} else if !writing_offset {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{}", argument)?;
|
||||
write!(f, "{argument}")?;
|
||||
if let Argument::Offset(_) = argument {
|
||||
write!(f, "(")?;
|
||||
writing_offset = true;
|
||||
@ -408,21 +431,30 @@ impl LowerHex for SignedHexLiteral<i32> {
|
||||
|
||||
pub struct InsIter<'a> {
|
||||
address: u32,
|
||||
extensions: Extensions,
|
||||
data: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a> InsIter<'a> {
|
||||
pub fn new(data: &'a [u8], address: u32) -> Self {
|
||||
Self { address, data }
|
||||
#[inline]
|
||||
pub fn new(data: &'a [u8], address: u32, extensions: Extensions) -> Self {
|
||||
Self { address, extensions, data }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn address(&self) -> u32 {
|
||||
self.address
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn data(&self) -> &'a [u8] {
|
||||
self.data
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn extensions(&self) -> Extensions {
|
||||
self.extensions
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for InsIter<'_> {
|
||||
@ -435,10 +467,194 @@ impl Iterator for InsIter<'_> {
|
||||
|
||||
// SAFETY: The slice is guaranteed to be at least 4 bytes long.
|
||||
let chunk = unsafe { *(self.data.as_ptr() as *const [u8; 4]) };
|
||||
let ins = Ins::new(u32::from_be_bytes(chunk));
|
||||
let ins = Ins::new(u32::from_be_bytes(chunk), self.extensions);
|
||||
let addr = self.address;
|
||||
self.address += 4;
|
||||
self.data = &self.data[4..];
|
||||
Some((addr, ins))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[repr(transparent)]
|
||||
pub struct Extensions(u32);
|
||||
|
||||
impl Extensions {
|
||||
/// Creates an empty set of extensions.
|
||||
#[inline]
|
||||
pub const fn none() -> Self {
|
||||
Self(0)
|
||||
}
|
||||
|
||||
/// The set of extensions used by the PowerPC 750CXe (Gekko) / 750CL (Broadway) CPUs
|
||||
/// used in the GameCube and Wii respectively.
|
||||
#[inline]
|
||||
pub const fn gekko_broadway() -> Self {
|
||||
Self::from_bitmask(Extension::PairedSingles.bitmask())
|
||||
}
|
||||
|
||||
/// The set of extensions used by the PowerPC Xenon CPU used in the Xbox 360.
|
||||
#[inline]
|
||||
pub const fn xenon() -> Self {
|
||||
Self::from_bitmask(
|
||||
Extension::Ppc64.bitmask() | Extension::AltiVec.bitmask() | Extension::Vmx128.bitmask(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Checks if the given extension (and all required extensions) are enabled.
|
||||
#[inline]
|
||||
pub const fn contains(&self, ext: Extension) -> bool {
|
||||
let bitmask = ext.bitmask();
|
||||
(self.0 & bitmask) == bitmask
|
||||
}
|
||||
|
||||
/// Checks if the given set of extensions are enabled.
|
||||
#[inline]
|
||||
pub const fn contains_all(&self, other: Extensions) -> bool {
|
||||
(self.0 & other.0) == other.0
|
||||
}
|
||||
|
||||
/// Enables the given extension. Implicitly enables all required extensions.
|
||||
#[inline]
|
||||
pub const fn insert(&mut self, ext: Extension) {
|
||||
self.0 |= ext.bitmask();
|
||||
}
|
||||
|
||||
/// Disables the given extension.
|
||||
#[inline]
|
||||
pub const fn remove(&mut self, ext: Extension) {
|
||||
// Instead of using bitmask, which includes required extensions,
|
||||
// we only clear the bit for the specific extension.
|
||||
self.0 &= !(1 << (ext as u32));
|
||||
}
|
||||
|
||||
/// Enables or disables the given extension.
|
||||
#[inline]
|
||||
pub const fn set(&mut self, ext: Extension, value: bool) {
|
||||
if value {
|
||||
self.insert(ext);
|
||||
} else {
|
||||
self.remove(ext);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether the extensions set is empty.
|
||||
#[inline]
|
||||
pub const fn is_empty(&self) -> bool {
|
||||
self.0 == 0
|
||||
}
|
||||
|
||||
/// Returns the raw bitmask of the extensions.
|
||||
#[inline]
|
||||
pub const fn bitmask(&self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Creates a set of extensions from a raw bitmask.
|
||||
#[inline]
|
||||
pub const fn from_bitmask(bitmask: u32) -> Self {
|
||||
Self(bitmask)
|
||||
}
|
||||
|
||||
/// Creates a set of extensions from a single extension (and its required extensions).
|
||||
#[inline]
|
||||
pub const fn from_extension(ext: Extension) -> Self {
|
||||
Self(ext.bitmask())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Extensions {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Self::none()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Extension> for Extensions {
|
||||
#[inline]
|
||||
fn from(ext: Extension) -> Self {
|
||||
Self::from_extension(ext)
|
||||
}
|
||||
}
|
||||
|
||||
impl BitOr for Extensions {
|
||||
type Output = Extensions;
|
||||
|
||||
#[inline]
|
||||
fn bitor(self, rhs: Self) -> Self::Output {
|
||||
Self(self.0 | rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl BitOr<Extension> for Extensions {
|
||||
type Output = Extensions;
|
||||
|
||||
#[inline]
|
||||
fn bitor(self, rhs: Extension) -> Self::Output {
|
||||
Self(self.0 | rhs.bitmask())
|
||||
}
|
||||
}
|
||||
|
||||
impl BitOrAssign<Extension> for Extensions {
|
||||
#[inline]
|
||||
fn bitor_assign(&mut self, rhs: Extension) {
|
||||
self.0 |= rhs.bitmask();
|
||||
}
|
||||
}
|
||||
|
||||
impl BitAnd for Extensions {
|
||||
type Output = Extensions;
|
||||
|
||||
#[inline]
|
||||
fn bitand(self, rhs: Self) -> Self::Output {
|
||||
Self(self.0 & rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl BitAnd<Extension> for Extensions {
|
||||
type Output = Extensions;
|
||||
|
||||
#[inline]
|
||||
fn bitand(self, rhs: Extension) -> Self::Output {
|
||||
Self(self.0 & rhs.bitmask())
|
||||
}
|
||||
}
|
||||
|
||||
impl BitAndAssign<Extension> for Extensions {
|
||||
#[inline]
|
||||
fn bitand_assign(&mut self, rhs: Extension) {
|
||||
self.0 &= rhs.bitmask();
|
||||
}
|
||||
}
|
||||
|
||||
impl Not for Extensions {
|
||||
type Output = Extensions;
|
||||
|
||||
#[inline]
|
||||
fn not(self) -> Self::Output {
|
||||
Self(!self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl BitOr for Extension {
|
||||
type Output = Extensions;
|
||||
|
||||
#[inline]
|
||||
fn bitor(self, rhs: Self) -> Self::Output {
|
||||
Extensions(self.bitmask() | rhs.bitmask())
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Extension {
|
||||
#[inline]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.bitmask().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Extension {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.name())
|
||||
}
|
||||
}
|
||||
|
12419
disasm/src/generated.rs
12419
disasm/src/generated.rs
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,7 @@ mod disasm;
|
||||
mod generated;
|
||||
|
||||
pub use disasm::{
|
||||
Argument, BranchDest, CRBit, CRField, Ins, InsIter, Offset, OpaqueU, ParsedIns, Simm, Uimm,
|
||||
FPR, GPR, GQR, SPR, SR,
|
||||
Argument, BranchDest, CRBit, CRField, Extensions, Ins, InsIter, Offset, OpaqueU, ParsedIns,
|
||||
Simm, Uimm, FPR, GPR, GQR, SPR, SR,
|
||||
};
|
||||
pub use generated::{Arguments, Opcode};
|
||||
pub use generated::{Arguments, Extension, Opcode};
|
||||
|
821
disasm/tests/test_altivec.rs
Normal file
821
disasm/tests/test_altivec.rs
Normal file
@ -0,0 +1,821 @@
|
||||
use powerpc::{Extension, Extensions, Ins};
|
||||
|
||||
const EXTENSIONS: Extensions = Extensions::from_extension(Extension::AltiVec);
|
||||
|
||||
macro_rules! assert_asm {
|
||||
($ins:ident, $disasm:literal) => {{
|
||||
assert_eq!(format!("{}", $ins.simplified()), $disasm)
|
||||
}};
|
||||
($code:literal, $disasm:literal) => {{
|
||||
let ins = Ins::new($code, EXTENSIONS);
|
||||
assert_eq!(format!("{}", ins.simplified()), $disasm)
|
||||
}};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_dss() {
|
||||
assert_asm!(0x7C60066C, "dss 3");
|
||||
assert_asm!(0x7E00066C, "dssall");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_dst() {
|
||||
assert_asm!(0x7C6742AC, "dst r7, r8, 3");
|
||||
assert_asm!(0x7E232AAC, "dstt r3, r5, 1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_dstst() {
|
||||
assert_asm!(0x7C44FAEC, "dstst r4, r31, 2");
|
||||
assert_asm!(0x7E63DAEC, "dststt r3, r27, 3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_lvebx() {
|
||||
assert_asm!(0x7C64380E, "lvebx v3, r4, r7");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_lvehx() {
|
||||
assert_asm!(0x7CA8F84E, "lvehx v5, r8, r31");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_lvewx() {
|
||||
assert_asm!(0x7D09508E, "lvewx v8, r9, r10");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_lvsl() {
|
||||
assert_asm!(0x7CA6480C, "lvsl v5, r6, r9");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_lvsr() {
|
||||
assert_asm!(0x7C60284C, "lvsr v3, r0, r5");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_lvx() {
|
||||
assert_asm!(0x7CF2E8CE, "lvx v7, r18, r29");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_lvxl() {
|
||||
assert_asm!(0x7D17FACE, "lvxl v8, r23, r31");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_mfvscr() {
|
||||
assert_asm!(0x13E00604, "mfvscr v31");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_mtvscr() {
|
||||
assert_asm!(0x1000CE44, "mtvscr v25");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_stvebx() {
|
||||
assert_asm!(0x7CE3210E, "stvebx v7, r3, r4");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_stvehx() {
|
||||
assert_asm!(0x7F25514E, "stvehx v25, r5, r10");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_stvewx() {
|
||||
assert_asm!(0x7E08498E, "stvewx v16, r8, r9");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_stvx() {
|
||||
assert_asm!(0x7FE039CE, "stvx v31, r0, r7");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_stvxl() {
|
||||
assert_asm!(0x7E8EF3CE, "stvxl v20, r14, r30");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vaddcuw() {
|
||||
assert_asm!(0x10A63980, "vaddcuw v5, v6, v7");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vaddfp() {
|
||||
assert_asm!(0x13BEF80A, "vaddfp v29, v30, v31");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vaddsbs() {
|
||||
assert_asm!(0x10035300, "vaddsbs v0, v3, v10");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vaddsws() {
|
||||
assert_asm!(0x11043B80, "vaddsws v8, v4, v7");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vaddubm() {
|
||||
assert_asm!(0x100CE000, "vaddubm v0, v12, v28");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vaddubs() {
|
||||
assert_asm!(0x10AACA00, "vaddubs v5, v10, v25");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vadduhm() {
|
||||
assert_asm!(0x1112E040, "vadduhm v8, v18, v28");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vadduhs() {
|
||||
assert_asm!(0x1071EA40, "vadduhs v3, v17, v29");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vadduwm() {
|
||||
assert_asm!(0x10D6F080, "vadduwm v6, v22, v30");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vadduws() {
|
||||
assert_asm!(0x1109B280, "vadduws v8, v9, v22");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vand() {
|
||||
assert_asm!(0x1156BC04, "vand v10, v22, v23");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vandc() {
|
||||
assert_asm!(0x10F4F444, "vandc v7, v20, v30");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vavgsb() {
|
||||
assert_asm!(0x10BC1D02, "vavgsb v5, v28, v3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vavgsh() {
|
||||
assert_asm!(0x106DBD42, "vavgsh v3, v13, v23");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vavgsw() {
|
||||
assert_asm!(0x112CAD82, "vavgsw v9, v12, v21");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vavgub() {
|
||||
assert_asm!(0x100FF402, "vavgub v0, v15, v30");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vavguh() {
|
||||
assert_asm!(0x108EC442, "vavguh v4, v14, v24");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vavguw() {
|
||||
assert_asm!(0x10674482, "vavguw v3, v7, v8");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vcfsx() {
|
||||
assert_asm!(0x101D534A, "vcfsx v0, v10, 0x1d");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vcfux() {
|
||||
assert_asm!(0x10B03B0A, "vcfux v5, v7, 0x10");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vcmpbfp() {
|
||||
assert_asm!(0x106963C6, "vcmpbfp v3, v9, v12");
|
||||
assert_asm!(0x10E84FC6, "vcmpbfp. v7, v8, v9");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vcmpeqfp() {
|
||||
assert_asm!(0x108640C6, "vcmpeqfp v4, v6, v8");
|
||||
assert_asm!(0x10A304C6, "vcmpeqfp. v5, v3, v0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vcmpequb() {
|
||||
assert_asm!(0x1011D806, "vcmpequb v0, v17, v27");
|
||||
assert_asm!(0x10649C06, "vcmpequb. v3, v4, v19");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vcmpequh() {
|
||||
assert_asm!(0x10A86046, "vcmpequh v5, v8, v12");
|
||||
assert_asm!(0x10E60446, "vcmpequh. v7, v6, v0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vcmpequw() {
|
||||
assert_asm!(0x10664086, "vcmpequw v3, v6, v8");
|
||||
assert_asm!(0x10A85486, "vcmpequw. v5, v8, v10");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vcmpgefp() {
|
||||
assert_asm!(0x100329C6, "vcmpgefp v0, v3, v5");
|
||||
assert_asm!(0x108545C6, "vcmpgefp. v4, v5, v8");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vcmpgtfp() {
|
||||
assert_asm!(0x10A0CAC6, "vcmpgtfp v5, v0, v25");
|
||||
assert_asm!(0x10E3A6C6, "vcmpgtfp. v7, v3, v20");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vcmpgtsb() {
|
||||
assert_asm!(0x10602306, "vcmpgtsb v3, v0, v4");
|
||||
assert_asm!(0x10E88706, "vcmpgtsb. v7, v8, v16");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vcmpgtsh() {
|
||||
assert_asm!(0x10A69B46, "vcmpgtsh v5, v6, v19");
|
||||
assert_asm!(0x1192C746, "vcmpgtsh. v12, v18, v24");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vcmpgtsw() {
|
||||
assert_asm!(0x1140F386, "vcmpgtsw v10, v0, v30");
|
||||
assert_asm!(0x1297DF86, "vcmpgtsw. v20, v23, v27");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vcmpgtub() {
|
||||
assert_asm!(0x10A88206, "vcmpgtub v5, v8, v16");
|
||||
assert_asm!(0x13CAA606, "vcmpgtub. v30, v10, v20");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vcmpgtuh() {
|
||||
assert_asm!(0x101BFA46, "vcmpgtuh v0, v27, v31");
|
||||
assert_asm!(0x10853646, "vcmpgtuh. v4, v5, v6");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vcmpgtuw() {
|
||||
assert_asm!(0x1070D286, "vcmpgtuw v3, v16, v26");
|
||||
assert_asm!(0x10FAFE86, "vcmpgtuw. v7, v26, v31");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vctsxs() {
|
||||
assert_asm!(0x10743BCA, "vctsxs v3, v7, 0x14");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vctuxs() {
|
||||
assert_asm!(0x10AB638A, "vctuxs v5, v12, 0xb");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vexptefp() {
|
||||
assert_asm!(0x10E0518A, "vexptefp v7, v10");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vlogefp() {
|
||||
assert_asm!(0x100031CA, "vlogefp v0, v6");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmaddfp() {
|
||||
assert_asm!(0x1003396E, "vmaddfp v0, v3, v5, v7");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmaxfp() {
|
||||
assert_asm!(0x10C84C0A, "vmaxfp v6, v8, v9");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmaxsb() {
|
||||
assert_asm!(0x100AB102, "vmaxsb v0, v10, v22");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmaxsh() {
|
||||
assert_asm!(0x1298E142, "vmaxsh v20, v24, v28");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmaxsw() {
|
||||
assert_asm!(0x13DF6182, "vmaxsw v30, v31, v12");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmaxub() {
|
||||
assert_asm!(0x1198F002, "vmaxub v12, v24, v30");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmaxuh() {
|
||||
assert_asm!(0x1236D842, "vmaxuh v17, v22, v27");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmaxuw() {
|
||||
assert_asm!(0x114CC082, "vmaxuw v10, v12, v24");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmhaddshs() {
|
||||
assert_asm!(0x10A63A20, "vmhaddshs v5, v6, v7, v8");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmhraddshs() {
|
||||
assert_asm!(0x112A63A1, "vmhraddshs v9, v10, v12, v14");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vminfp() {
|
||||
assert_asm!(0x106AAC4A, "vminfp v3, v10, v21");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vminsb() {
|
||||
assert_asm!(0x10643B02, "vminsb v3, v4, v7");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vminsh() {
|
||||
assert_asm!(0x10E9B342, "vminsh v7, v9, v22");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vminsw() {
|
||||
assert_asm!(0x118F9382, "vminsw v12, v15, v18");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vminub() {
|
||||
assert_asm!(0x108ED202, "vminub v4, v14, v26");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vminuh() {
|
||||
assert_asm!(0x11F19A42, "vminuh v15, v17, v19");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vminuw() {
|
||||
assert_asm!(0x1254F282, "vminuw v18, v20, v30");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmladduhm() {
|
||||
assert_asm!(0x10608762, "vmladduhm v3, v0, v16, v29");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmrghb() {
|
||||
assert_asm!(0x10F4A80C, "vmrghb v7, v20, v21");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmrghh() {
|
||||
assert_asm!(0x110AC84C, "vmrghh v8, v10, v25");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmrghw() {
|
||||
assert_asm!(0x1198E08C, "vmrghw v12, v24, v28");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmrglb() {
|
||||
assert_asm!(0x1299F10C, "vmrglb v20, v25, v30");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmrglh() {
|
||||
assert_asm!(0x131CF94C, "vmrglh v24, v28, v31");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmrglw() {
|
||||
assert_asm!(0x13DF018C, "vmrglw v30, v31, v0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmsummbm() {
|
||||
assert_asm!(0x10044325, "vmsummbm v0, v4, v8, v12");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmsumshm() {
|
||||
assert_asm!(0x1114DFE8, "vmsumshm v8, v20, v27, v31");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmsumshs() {
|
||||
assert_asm!(0x1150ADE9, "vmsumshs v10, v16, v21, v23");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmsumubm() {
|
||||
assert_asm!(0x1198D7A4, "vmsumubm v12, v24, v26, v30");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmsumuhm() {
|
||||
assert_asm!(0x13C503E6, "vmsumuhm v30, v5, v0, v15");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmsumuhs() {
|
||||
assert_asm!(0x10032167, "vmsumuhs v0, v3, v4, v5");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmulesb() {
|
||||
assert_asm!(0x110EC308, "vmulesb v8, v14, v24");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmulesh() {
|
||||
assert_asm!(0x10602B48, "vmulesh v3, v0, v5");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmuleub() {
|
||||
assert_asm!(0x10076208, "vmuleub v0, v7, v12");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmuleuh() {
|
||||
assert_asm!(0x1200FA48, "vmuleuh v16, v0, v31");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmulosb() {
|
||||
assert_asm!(0x11E01908, "vmulosb v15, v0, v3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmulosh() {
|
||||
assert_asm!(0x10685148, "vmulosh v3, v8, v10");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmuloub() {
|
||||
assert_asm!(0x10854008, "vmuloub v4, v5, v8");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vmulouh() {
|
||||
assert_asm!(0x10A70048, "vmulouh v5, v7, v0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vnmsubfp() {
|
||||
assert_asm!(0x1060F42F, "vnmsubfp v3, v0, v16, v30");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vnor() {
|
||||
assert_asm!(0x10605504, "vnor v3, v0, v10");
|
||||
assert_asm!(0x10884504, "vnot v4, v8");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vor() {
|
||||
assert_asm!(0x100D7C84, "vor v0, v13, v15");
|
||||
assert_asm!(0x1077BC84, "vmr v3, v23");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vperm() {
|
||||
assert_asm!(0x10a5302b, "vperm v5, v5, v6, v0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vpkpx() {
|
||||
assert_asm!(0x10AFE30E, "vpkpx v5, v15, v28");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vpkshss() {
|
||||
assert_asm!(0x1006498E, "vpkshss v0, v6, v9");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vpkshus() {
|
||||
assert_asm!(0x1220990E, "vpkshus v17, v0, v19");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vpkswss() {
|
||||
assert_asm!(0x1253A1CE, "vpkswss v18, v19, v20");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vpkswus() {
|
||||
assert_asm!(0x128AF14E, "vpkswus v20, v10, v30");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vpkuhum() {
|
||||
assert_asm!(0x10BBA00E, "vpkuhum v5, v27, v20");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vpkuhus() {
|
||||
assert_asm!(0x11AE788E, "vpkuhus v13, v14, v15");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vpkuwum() {
|
||||
assert_asm!(0x114B604E, "vpkuwum v10, v11, v12");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vpkuwus() {
|
||||
assert_asm!(0x1176F8CE, "vpkuwus v11, v22, v31");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vrefp() {
|
||||
assert_asm!(0x1180C10A, "vrefp v12, v24");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vrfim() {
|
||||
assert_asm!(0x1240F2CA, "vrfim v18, v30");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vrfin() {
|
||||
assert_asm!(0x1140620A, "vrfin v10, v12");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vrfip() {
|
||||
assert_asm!(0x10E08A8A, "vrfip v7, v17");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vrfiz() {
|
||||
assert_asm!(0x1000A24A, "vrfiz v0, v20");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vrlb() {
|
||||
assert_asm!(0x10EF8804, "vrlb v7, v15, v17");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vrlh() {
|
||||
assert_asm!(0x12129844, "vrlh v16, v18, v19");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vrlw() {
|
||||
assert_asm!(0x11540084, "vrlw v10, v20, v0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vrsqrtefp() {
|
||||
assert_asm!(0x1060794A, "vrsqrtefp v3, v15");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vsel() {
|
||||
assert_asm!(0x100329AA, "vsel v0, v3, v5, v6");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vsl() {
|
||||
assert_asm!(0x108CC1C4, "vsl v4, v12, v24");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vslb() {
|
||||
assert_asm!(0x114E9104, "vslb v10, v14, v18");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vsldoi() {
|
||||
assert_asm!(0x10601B6C, "vsldoi v3, v0, v3, 13");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vslh() {
|
||||
assert_asm!(0x10AFC144, "vslh v5, v15, v24");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vslo() {
|
||||
assert_asm!(0x10F1DC0C, "vslo v7, v17, v27");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vslw() {
|
||||
assert_asm!(0x11128184, "vslw v8, v18, v16");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vspltb() {
|
||||
assert_asm!(0x115C620C, "vspltb v10, v12, 0x1c");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vsplth() {
|
||||
assert_asm!(0x11947A4C, "vsplth v12, v15, 0x14");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vspltisb() {
|
||||
assert_asm!(0x1076030C, "vspltisb v3, -0xa");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vspltish() {
|
||||
assert_asm!(0x11CE034C, "vspltish v14, 0xe");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vspltisw() {
|
||||
assert_asm!(0x124C038C, "vspltisw v18, 0xc");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vspltw() {
|
||||
assert_asm!(0x1018528C, "vspltw v0, v10, 0x18");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vsr() {
|
||||
assert_asm!(0x116C6AC4, "vsr v11, v12, v13");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vsrab() {
|
||||
assert_asm!(0x11D09304, "vsrab v14, v16, v18");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vsrah() {
|
||||
assert_asm!(0x10E8C344, "vsrah v7, v8, v24");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vsraw() {
|
||||
assert_asm!(0x112CAB84, "vsraw v9, v12, v21");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vsrb() {
|
||||
assert_asm!(0x1112D204, "vsrb v8, v18, v26");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vsrh() {
|
||||
assert_asm!(0x114C8244, "vsrh v10, v12, v16");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vsro() {
|
||||
assert_asm!(0x118F9C4C, "vsro v12, v15, v19");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vsrw() {
|
||||
assert_asm!(0x100EA284, "vsrw v0, v14, v20");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vsubcuw() {
|
||||
assert_asm!(0x10AF8580, "vsubcuw v5, v15, v16");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vsubfp() {
|
||||
assert_asm!(0x1080584A, "vsubfp v4, v0, v11");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vsubsbs() {
|
||||
assert_asm!(0x10D2BF00, "vsubsbs v6, v18, v23");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vsubshs() {
|
||||
assert_asm!(0x10F16740, "vsubshs v7, v17, v12");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vsubsws() {
|
||||
assert_asm!(0x118D5780, "vsubsws v12, v13, v10");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vsububm() {
|
||||
assert_asm!(0x11402C00, "vsububm v10, v0, v5");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vsububs() {
|
||||
assert_asm!(0x10033600, "vsububs v0, v3, v6");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vsubuhm() {
|
||||
assert_asm!(0x10EB6C40, "vsubuhm v7, v11, v13");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vsubuhs() {
|
||||
assert_asm!(0x110A6640, "vsubuhs v8, v10, v12");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vsubuwm() {
|
||||
assert_asm!(0x112BDC80, "vsubuwm v9, v11, v27");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vsubuws() {
|
||||
assert_asm!(0x1149AE80, "vsubuws v10, v9, v21");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vsumsws() {
|
||||
assert_asm!(0x116C6F88, "vsumsws v11, v12, v13");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vsum2sws() {
|
||||
assert_asm!(0x11909688, "vsum2sws v12, v16, v18");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vsum4sbs() {
|
||||
assert_asm!(0x11B19708, "vsum4sbs v13, v17, v18");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vsum4shs() {
|
||||
assert_asm!(0x1296C648, "vsum4shs v20, v22, v24");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vsum4ubs() {
|
||||
assert_asm!(0x12F8CE08, "vsum4ubs v23, v24, v25");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vupkhpx() {
|
||||
assert_asm!(0x10A0434E, "vupkhpx v5, v8");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vupkhsb() {
|
||||
assert_asm!(0x10001A0E, "vupkhsb v0, v3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vupkhsh() {
|
||||
assert_asm!(0x11806A4E, "vupkhsh v12, v13");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vupklpx() {
|
||||
assert_asm!(0x108083CE, "vupklpx v4, v16");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vupklsb() {
|
||||
assert_asm!(0x11407A8E, "vupklsb v10, v15");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vupklsh() {
|
||||
assert_asm!(0x1180C2CE, "vupklsh v12, v24");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_vxor() {
|
||||
assert_asm!(0x10654CC4, "vxor v3, v5, v9");
|
||||
}
|
@ -1,11 +1,13 @@
|
||||
use ppc750cl::{Argument, Ins, InsIter, Opcode, FPR, GPR};
|
||||
use powerpc::{Argument, Extensions, Ins, InsIter, Opcode, GPR};
|
||||
|
||||
const EXTENSIONS: Extensions = Extensions::none();
|
||||
|
||||
macro_rules! assert_asm {
|
||||
($ins:ident, $disasm:literal) => {{
|
||||
assert_eq!(format!("{}", $ins.simplified()), $disasm)
|
||||
}};
|
||||
($code:literal, $disasm:literal) => {{
|
||||
let ins = Ins::new($code);
|
||||
let ins = Ins::new($code, EXTENSIONS);
|
||||
assert_eq!(format!("{}", ins.simplified()), $disasm)
|
||||
}};
|
||||
}
|
||||
@ -15,7 +17,7 @@ macro_rules! assert_basic {
|
||||
assert_eq!(format!("{}", $ins.basic_form()), $disasm)
|
||||
}};
|
||||
($code:literal, $disasm:literal) => {{
|
||||
let ins = Ins::new($code);
|
||||
let ins = Ins::new($code, EXTENSIONS);
|
||||
assert_eq!(format!("{}", ins.basic()), $disasm)
|
||||
}};
|
||||
}
|
||||
@ -30,7 +32,7 @@ fn test_ins_add() {
|
||||
|
||||
#[test]
|
||||
fn test_ins_addc() {
|
||||
let ins = Ins::new(0x7c002014);
|
||||
let ins = Ins::new(0x7c002014, EXTENSIONS);
|
||||
assert_eq!(ins.op, Opcode::Addc);
|
||||
// assert_eq!(ins.fields(), vec![rD(GPR(0)), rA(GPR(0)), rB(GPR(4))]);
|
||||
assert_asm!(ins, "addc r0, r0, r4");
|
||||
@ -42,7 +44,7 @@ fn test_ins_addc() {
|
||||
|
||||
#[test]
|
||||
fn test_ins_addi() {
|
||||
let ins = Ins::new(0x38010140);
|
||||
let ins = Ins::new(0x38010140, EXTENSIONS);
|
||||
assert_eq!(ins.op, Opcode::Addi);
|
||||
// assert_eq!(
|
||||
// ins.fields(),
|
||||
@ -205,6 +207,7 @@ fn test_ins_cmpi() {
|
||||
#[test]
|
||||
fn test_ins_cmpl() {
|
||||
assert_asm!(0x7C9A2040, "cmplw cr1, r26, r4");
|
||||
assert_asm!(0x7f295840, "cmpld cr6, r9, r11");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -285,11 +288,6 @@ fn test_ins_dcbz() {
|
||||
assert_asm!(0x7C001FEC, "dcbz r0, r3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_dcbz_l() {
|
||||
assert_asm!(0x10061FEC, "dcbz_l r6, r3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_divw() {
|
||||
assert_asm!(0x7C8073D6, "divw r4, r0, r14");
|
||||
@ -715,183 +713,6 @@ fn test_ins_oris() {
|
||||
assert_asm!(0x67A06800, "oris r0, r29, 0x6800");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_psq_l() {
|
||||
assert_asm!(0xE02500AC, "psq_l f1, 0xac(r5), 0, qr0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_psq_lu() {
|
||||
assert_asm!(0xE5435010, "psq_lu f10, 0x10(r3), 0, qr5");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_psq_lx() {
|
||||
let ins = Ins::new(0x1000000C);
|
||||
assert_eq!(ins.op, Opcode::PsqLx);
|
||||
// assert_eq!(
|
||||
// ins.fields(),
|
||||
// vec![
|
||||
// frD(FPR(0)),
|
||||
// rA(GPR(0)),
|
||||
// rB(GPR(0)),
|
||||
// ps_WX(OpaqueU(0)),
|
||||
// ps_IX(GQR(0)),
|
||||
// ]
|
||||
// );
|
||||
assert_eq!(
|
||||
ins.defs(),
|
||||
[Argument::FPR(FPR(0)), Argument::None, Argument::None, Argument::None, Argument::None]
|
||||
);
|
||||
assert_eq!(
|
||||
ins.uses(),
|
||||
[Argument::None, Argument::GPR(GPR(0)), Argument::None, Argument::None, Argument::None]
|
||||
);
|
||||
|
||||
assert_asm!(0x1000000C, "psq_lx f0, r0, r0, 0, qr0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_psq_st() {
|
||||
assert_asm!(0xF1230210, "psq_st f9, 0x210(r3), 0, qr0");
|
||||
assert_asm!(0xF1238008, "psq_st f9, 0x8(r3), 1, qr0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_psq_stu() {
|
||||
assert_asm!(0xF40A0020, "psq_stu f0, 0x20(r10), 0, qr0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_psq_stx() {
|
||||
assert_asm!(0x13E1000E, "psq_stx f31, r1, r0, 0, qr0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_abs() {
|
||||
assert_asm!(0x10A03210, "ps_abs f5, f6");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_add() {
|
||||
assert_asm!(0x1006382A, "ps_add f0, f6, f7");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_cmpo0() {
|
||||
assert_asm!(0x10070840, "ps_cmpo0 cr0, f7, f1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_cmpu0() {
|
||||
assert_asm!(0x10003000, "ps_cmpu0 cr0, f0, f6");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_cmpu1() {
|
||||
assert_asm!(0x10003080, "ps_cmpu1 cr0, f0, f6");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_madd() {
|
||||
assert_asm!(0x112141FA, "ps_madd f9, f1, f7, f8");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_madds0() {
|
||||
assert_asm!(0x10AC299C, "ps_madds0 f5, f12, f6, f5");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_madds1() {
|
||||
assert_asm!(0x110640DE, "ps_madds1 f8, f6, f3, f8");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_merge00() {
|
||||
assert_asm!(0x10400420, "ps_merge00 f2, f0, f0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_merge01() {
|
||||
assert_asm!(0x10400C60, "ps_merge01 f2, f0, f1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_merge10() {
|
||||
assert_asm!(0x104004A0, "ps_merge10 f2, f0, f0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_merge11() {
|
||||
assert_asm!(0x10AA14E0, "ps_merge11 f5, f10, f2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_mr() {
|
||||
assert_asm!(0x10200090, "ps_mr f1, f0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_msub() {
|
||||
assert_asm!(0x10A53778, "ps_msub f5, f5, f29, f6");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_mul() {
|
||||
assert_asm!(0x10000032, "ps_mul f0, f0, f0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_muls0() {
|
||||
assert_asm!(0x100002D8, "ps_muls0 f0, f0, f11");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_muls1() {
|
||||
assert_asm!(0x10A2005A, "ps_muls1 f5, f2, f1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_nabs() {
|
||||
assert_asm!(0x10803210, "ps_abs f4, f6");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_neg() {
|
||||
assert_asm!(0x10E03850, "ps_neg f7, f7");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_nmadd() {
|
||||
assert_asm!(0x10CB30FE, "ps_nmadd f6, f11, f3, f6");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_nmsub() {
|
||||
assert_asm!(0x107E083C, "ps_nmsub f3, f30, f0, f1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_sel() {
|
||||
assert_asm!(0x106428EE, "ps_sel f3, f4, f3, f5");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_sub() {
|
||||
assert_asm!(0x10A92828, "ps_sub f5, f9, f5");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_sum0() {
|
||||
assert_asm!(0x10230854, "ps_sum0 f1, f3, f1, f1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_sum1() {
|
||||
assert_asm!(0x10A12956, "ps_sum1 f5, f1, f5, f5");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_rfi() {
|
||||
assert_asm!(0x4C000064, "rfi");
|
||||
@ -1093,7 +914,12 @@ fn test_ins_subfze() {
|
||||
|
||||
#[test]
|
||||
fn test_ins_sync() {
|
||||
assert_asm!(0x7C0004AC, "sync");
|
||||
assert_basic!(0x7c0004ac, "sync 0");
|
||||
assert_basic!(0x7c2004ac, "sync 1");
|
||||
assert_basic!(0x7c4004ac, "sync 2");
|
||||
assert_asm!(0x7c0004ac, "sync");
|
||||
assert_asm!(0x7c2004ac, "lwsync");
|
||||
assert_asm!(0x7c4004Ac, "ptesync");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1140,8 +966,9 @@ fn test_ins_xoris() {
|
||||
|
||||
#[test]
|
||||
fn test_ins_iter() {
|
||||
let mut iter = InsIter::new(&[0x7C, 0x43, 0x22, 0x14, 0x7E, 0x1A, 0x02, 0xA6, 0xFF], 0);
|
||||
assert_eq!(iter.next(), Some((0, Ins::new(0x7C432214))));
|
||||
assert_eq!(iter.next(), Some((4, Ins::new(0x7E1A02A6))));
|
||||
let mut iter =
|
||||
InsIter::new(&[0x7C, 0x43, 0x22, 0x14, 0x7E, 0x1A, 0x02, 0xA6, 0xFF], 0, EXTENSIONS);
|
||||
assert_eq!(iter.next(), Some((0, Ins::new(0x7C432214, EXTENSIONS))));
|
||||
assert_eq!(iter.next(), Some((4, Ins::new(0x7E1A02A6, EXTENSIONS))));
|
||||
assert_eq!(iter.next(), None);
|
||||
}
|
||||
|
263
disasm/tests/test_ppc64.rs
Normal file
263
disasm/tests/test_ppc64.rs
Normal file
@ -0,0 +1,263 @@
|
||||
use powerpc::{Extension, Extensions, Ins};
|
||||
|
||||
const EXTENSIONS: Extensions = Extensions::from_extension(Extension::Ppc64);
|
||||
|
||||
macro_rules! assert_asm {
|
||||
($ins:ident, $disasm:literal) => {{
|
||||
assert_eq!(format!("{}", $ins.simplified()), $disasm)
|
||||
}};
|
||||
($code:literal, $disasm:literal) => {{
|
||||
let ins = Ins::new($code, EXTENSIONS);
|
||||
assert_eq!(format!("{}", ins.simplified()), $disasm)
|
||||
}};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_cntlzd() {
|
||||
assert_asm!(0x7CA30074, "cntlzd r3, r5");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_dcbzl() {
|
||||
assert_asm!(0x7c2327ec, "dcbzl r3, r4");
|
||||
assert_asm!(0x7c20ffec, "dcbzl r0, r31");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_divd() {
|
||||
assert_asm!(0x7CA63BD2, "divd r5, r6, r7");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_divdu() {
|
||||
assert_asm!(0x7C839392, "divdu r4, r3, r18");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_extsw() {
|
||||
assert_asm!(0x7CC307B4, "extsw r3, r6");
|
||||
assert_asm!(0x7CC307B5, "extsw. r3, r6");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_fcfid() {
|
||||
assert_asm!(0xFC602E9C, "fcfid f3, f5");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_fctid() {
|
||||
assert_asm!(0xFC60065C, "fctid f3, f0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_fctidz() {
|
||||
assert_asm!(0xFC60065E, "fctidz f3, f0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_fsqrt() {
|
||||
assert_asm!(0xfc60f82c, "fsqrt f3, f31");
|
||||
assert_asm!(0xffe0102d, "fsqrt. f31, f2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_fsqrts() {
|
||||
assert_asm!(0xec40182c, "fsqrts f2, f3");
|
||||
assert_asm!(0xec60f82d, "fsqrts. f3, f31");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ld() {
|
||||
assert_asm!(0xebe10058, "ld r31, 0x58(r1)");
|
||||
assert_asm!(0xe9790010, "ld r11, 0x10(r25)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ldarx() {
|
||||
assert_asm!(0x7C6538A8, "ldarx r3, r5, r7");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ldu() {
|
||||
assert_asm!(0xe97cfff9, "ldu r11, -0x8(r28)");
|
||||
assert_asm!(0xe8deffe9, "ldu r6, -0x18(r30)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ldux() {
|
||||
assert_asm!(0x7C60286A, "ldux r3, r0, r5");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ldx() {
|
||||
assert_asm!(0x7C60282A, "ldx r3, r0, r5");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_lwa() {
|
||||
assert_asm!(0xe97fffea, "lwa r11, -0x18(r31)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_lwaux() {
|
||||
assert_asm!(0x7C8532EA, "lwaux r4, r5, r6");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_lwax() {
|
||||
assert_asm!(0x7CA63AAA, "lwax r5, r6, r7");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_mfocrf() {
|
||||
assert_asm!(0x7d702026, "mfocrf r11, 2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_mtmsrd() {
|
||||
assert_asm!(0x7C000164, "mtmsrd r0, 0");
|
||||
assert_asm!(0x7D210164, "mtmsrd r9, 1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_mtsrd() {
|
||||
assert_asm!(0x7E0000A4, "mtsrd 0, r16");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_mtsrdin() {
|
||||
assert_asm!(0x7C8040E4, "mtsrdin r4, r8");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_mulhd() {
|
||||
assert_asm!(0x7C7CF892, "mulhd r3, r28, r31");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_mulhdu() {
|
||||
assert_asm!(0x7CBCF812, "mulhdu r5, r28, r31");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_mulld() {
|
||||
assert_asm!(0x7C6419D2, "mulld r3, r4, r3");
|
||||
assert_asm!(0x7d6b49d2, "mulld r11, r11, r9");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_rfid() {
|
||||
assert_asm!(0x4c000024, "rfid");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_rldcl() {
|
||||
assert_asm!(0x780336D0, "rldcl r3, r0, r6, 27");
|
||||
assert_asm!(0x78033010, "rotld r3, r0, r6");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_rldcr() {
|
||||
assert_asm!(0x78A345D2, "rldcr r3, r5, r8, 23");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_rldic() {
|
||||
assert_asm!(0x78C51928, "rldic r5, r6, 3, 36");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_rldicl() {
|
||||
assert_asm!(0x78c50020, "rldicl r5, r6, 0, 32");
|
||||
assert_asm!(0x7bab07a0, "rldicl r11, r29, 0, 62");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_rldicr() {
|
||||
assert_asm!(0x7883ffe6, "rldicr r3, r4, 63, 63");
|
||||
assert_asm!(0x798c37e4, "rldicr r12, r12, 6, 63");
|
||||
assert_asm!(0x798c07c6, "rldicr r12, r12, 32, 31");
|
||||
assert_asm!(0x798ccfe6, "rldicr r12, r12, 57, 63");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_rldimi() {
|
||||
assert_asm!(0x78a3a04e, "rldimi r3, r5, 52, 1");
|
||||
assert_asm!(0x794b000e, "rldimi r11, r10, 32, 0");
|
||||
assert_asm!(0x780331CC, "rldimi r3, r0, 6, 7");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_slbia() {
|
||||
assert_asm!(0x7c0003e4, "slbia");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_slbie() {
|
||||
assert_asm!(0x7C002B64, "slbie r5");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_sld() {
|
||||
assert_asm!(0x7d6a5036, "sld r10, r11, r10");
|
||||
assert_asm!(0x7D034836, "sld r3, r8, r9");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_srad() {
|
||||
assert_asm!(0x7d0b5e34, "srad r11, r8, r11");
|
||||
assert_asm!(0x7C033634, "srad r3, r0, r6");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_sradi() {
|
||||
assert_asm!(0x7cc4a674, "sradi r4, r6, 20");
|
||||
assert_asm!(0x7d6b0676, "sradi r11, r11, 32");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_srd() {
|
||||
assert_asm!(0x7d0a4c36, "srd r10, r8, r9");
|
||||
assert_asm!(0x7d675436, "srd r7, r11, r10");
|
||||
assert_asm!(0x7C001C36, "srd r0, r0, r3");
|
||||
assert_asm!(0x7C600436, "srd r0, r3, r0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_std() {
|
||||
assert_asm!(0xfbe1fff0, "std r31, -0x10(r1)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_stdcx() {
|
||||
assert_asm!(0x7CA749AD, "stdcx. r5, r7, r9");
|
||||
assert_asm!(0x7fc0e9ad, "stdcx. r30, r0, r29");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_stdu() {
|
||||
assert_asm!(0xf9690009, "stdu r11, 0x8(r9)");
|
||||
assert_asm!(0xf97ffff9, "stdu r11, -0x8(r31)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_stdux() {
|
||||
assert_asm!(0x7C03316A, "stdux r0, r3, r6");
|
||||
assert_asm!(0x7d5cc96a, "stdux r10, r28, r25");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_stdx() {
|
||||
assert_asm!(0x7CA7F92A, "stdx r5, r7, r31");
|
||||
assert_asm!(0x7cc3212a, "stdx r6, r3, r4");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_td() {
|
||||
assert_asm!(0x7DC30088, "td 14, r3, r0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_tdi() {
|
||||
assert_asm!(0x09830058, "tdi 12, r3, 0x58");
|
||||
}
|
195
disasm/tests/test_ps.rs
Normal file
195
disasm/tests/test_ps.rs
Normal file
@ -0,0 +1,195 @@
|
||||
use powerpc::{Argument, Extension, Extensions, Ins, Opcode, FPR, GPR};
|
||||
|
||||
const EXTENSIONS: Extensions = Extensions::from_extension(Extension::PairedSingles);
|
||||
|
||||
macro_rules! assert_asm {
|
||||
($ins:ident, $disasm:literal) => {{
|
||||
assert_eq!(format!("{}", $ins.simplified()), $disasm)
|
||||
}};
|
||||
($code:literal, $disasm:literal) => {{
|
||||
let ins = Ins::new($code, EXTENSIONS);
|
||||
assert_eq!(format!("{}", ins.simplified()), $disasm)
|
||||
}};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_dcbz_l() {
|
||||
assert_asm!(0x10061FEC, "dcbz_l r6, r3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_psq_l() {
|
||||
assert_asm!(0xE02500AC, "psq_l f1, 0xac(r5), 0, qr0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_psq_lu() {
|
||||
assert_asm!(0xE5435010, "psq_lu f10, 0x10(r3), 0, qr5");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_psq_lx() {
|
||||
let ins = Ins::new(0x1000000C, EXTENSIONS);
|
||||
assert_eq!(ins.op, Opcode::PsqLx);
|
||||
// assert_eq!(
|
||||
// ins.fields(),
|
||||
// vec![
|
||||
// frD(FPR(0)),
|
||||
// rA(GPR(0)),
|
||||
// rB(GPR(0)),
|
||||
// ps_WX(OpaqueU(0)),
|
||||
// ps_IX(GQR(0)),
|
||||
// ]
|
||||
// );
|
||||
assert_eq!(
|
||||
ins.defs(),
|
||||
[Argument::FPR(FPR(0)), Argument::None, Argument::None, Argument::None, Argument::None]
|
||||
);
|
||||
assert_eq!(
|
||||
ins.uses(),
|
||||
[Argument::None, Argument::GPR(GPR(0)), Argument::None, Argument::None, Argument::None]
|
||||
);
|
||||
|
||||
assert_asm!(0x1000000C, "psq_lx f0, r0, r0, 0, qr0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_psq_st() {
|
||||
assert_asm!(0xF1230210, "psq_st f9, 0x210(r3), 0, qr0");
|
||||
assert_asm!(0xF1238008, "psq_st f9, 0x8(r3), 1, qr0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_psq_stu() {
|
||||
assert_asm!(0xF40A0020, "psq_stu f0, 0x20(r10), 0, qr0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_psq_stx() {
|
||||
assert_asm!(0x13E1000E, "psq_stx f31, r1, r0, 0, qr0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_abs() {
|
||||
assert_asm!(0x10A03210, "ps_abs f5, f6");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_add() {
|
||||
assert_asm!(0x1006382A, "ps_add f0, f6, f7");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_cmpo0() {
|
||||
assert_asm!(0x10070840, "ps_cmpo0 cr0, f7, f1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_cmpu0() {
|
||||
assert_asm!(0x10003000, "ps_cmpu0 cr0, f0, f6");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_cmpu1() {
|
||||
assert_asm!(0x10003080, "ps_cmpu1 cr0, f0, f6");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_madd() {
|
||||
assert_asm!(0x112141FA, "ps_madd f9, f1, f7, f8");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_madds0() {
|
||||
assert_asm!(0x10AC299C, "ps_madds0 f5, f12, f6, f5");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_madds1() {
|
||||
assert_asm!(0x110640DE, "ps_madds1 f8, f6, f3, f8");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_merge00() {
|
||||
assert_asm!(0x10400420, "ps_merge00 f2, f0, f0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_merge01() {
|
||||
assert_asm!(0x10400C60, "ps_merge01 f2, f0, f1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_merge10() {
|
||||
assert_asm!(0x104004A0, "ps_merge10 f2, f0, f0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_merge11() {
|
||||
assert_asm!(0x10AA14E0, "ps_merge11 f5, f10, f2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_mr() {
|
||||
assert_asm!(0x10200090, "ps_mr f1, f0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_msub() {
|
||||
assert_asm!(0x10A53778, "ps_msub f5, f5, f29, f6");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_mul() {
|
||||
assert_asm!(0x10000032, "ps_mul f0, f0, f0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_muls0() {
|
||||
assert_asm!(0x100002D8, "ps_muls0 f0, f0, f11");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_muls1() {
|
||||
assert_asm!(0x10A2005A, "ps_muls1 f5, f2, f1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_nabs() {
|
||||
assert_asm!(0x10803210, "ps_abs f4, f6");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_neg() {
|
||||
assert_asm!(0x10E03850, "ps_neg f7, f7");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_nmadd() {
|
||||
assert_asm!(0x10CB30FE, "ps_nmadd f6, f11, f3, f6");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_nmsub() {
|
||||
assert_asm!(0x107E083C, "ps_nmsub f3, f30, f0, f1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_sel() {
|
||||
assert_asm!(0x106428EE, "ps_sel f3, f4, f3, f5");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_sub() {
|
||||
assert_asm!(0x10A92828, "ps_sub f5, f9, f5");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_sum0() {
|
||||
assert_asm!(0x10230854, "ps_sum0 f1, f3, f1, f1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ins_ps_sum1() {
|
||||
assert_asm!(0x10A12956, "ps_sum1 f5, f1, f5, f5");
|
||||
}
|
427
disasm/tests/test_vmx.rs
Normal file
427
disasm/tests/test_vmx.rs
Normal file
@ -0,0 +1,427 @@
|
||||
use powerpc::{Extension, Extensions, Ins};
|
||||
|
||||
const EXTENSIONS: Extensions = Extensions::from_extension(Extension::Vmx128);
|
||||
|
||||
macro_rules! assert_asm {
|
||||
($ins:ident, $disasm:literal) => {{
|
||||
assert_eq!(format!("{}", $ins.simplified()), $disasm)
|
||||
}};
|
||||
($code:literal, $disasm:literal) => {{
|
||||
let ins = Ins::new($code, EXTENSIONS);
|
||||
assert_eq!(format!("{}", ins.simplified()), $disasm)
|
||||
}};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_enables_altivec() {
|
||||
let extensions = Extensions::from_extension(Extension::Vmx128);
|
||||
assert!(extensions.contains(Extension::AltiVec));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extensions_remove_vmx() {
|
||||
let mut extensions = Extensions::from_extension(Extension::Vmx128);
|
||||
extensions.remove(Extension::Vmx128);
|
||||
assert!(!extensions.contains(Extension::Vmx128));
|
||||
// Ensure AltiVec is still enabled
|
||||
assert!(extensions.contains(Extension::AltiVec));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extensions_remove_altivec() {
|
||||
let mut extensions = Extensions::from_extension(Extension::Vmx128);
|
||||
extensions.remove(Extension::AltiVec);
|
||||
assert!(!extensions.contains(Extension::AltiVec));
|
||||
// Ensure Vmx128 is disabled (AltiVec required)
|
||||
assert!(!extensions.contains(Extension::Vmx128));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_lvewx128() {
|
||||
assert_asm!(0x1243388F, "lvewx128 v114, r3, r7");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_lvlx128() {
|
||||
assert_asm!(0x1085440F, "lvlx128 v100, r5, r8");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_lvrx128() {
|
||||
assert_asm!(0x108EE44B, "lvrx128 v68, r14, r28");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_lvlxl128() {
|
||||
assert_asm!(0x1105FE0B, "lvlxl128 v72, r5, r31");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_lvrxl128() {
|
||||
assert_asm!(0x12A01E4B, "lvrxl128 v85, r0, r3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_lvsl128() {
|
||||
assert_asm!(0x138AF00B, "lvsl128 v92, r10, r30");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_lvsr128() {
|
||||
assert_asm!(0x1016C04F, "lvsr128 v96, r22, r24");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_lvx128() {
|
||||
assert_asm!(0x120938CF, "lvx128 v112, r9, r7");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_lvxl128() {
|
||||
assert_asm!(0x12C322CF, "lvxl128 v118, r3, r4");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_stvewx128() {
|
||||
assert_asm!(0x131BF98F, "stvewx128 v120, r27, r31");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_stvlx128() {
|
||||
assert_asm!(0x12602D0B, "stvlx128 v83, r0, r5");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_stvlxl128() {
|
||||
assert_asm!(0x12C3A70B, "stvlxl128 v86, r3, r20");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_stvrx128() {
|
||||
assert_asm!(0x10B8F54B, "stvrx128 v69, r24, r30");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_stvrxl128() {
|
||||
assert_asm!(0x10C7074B, "stvrxl128 v70, r7, r0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_stvx128() {
|
||||
assert_asm!(0x130341C3, "stvx128 v24, r3, r8");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_stvxl128() {
|
||||
assert_asm!(0x13E553C3, "stvxl128 v31, r5, r10");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vaddfp128() {
|
||||
assert_asm!(0x151E301B, "vaddfp128 v72, v30, v102");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vand128() {
|
||||
assert_asm!(0x16900E12, "vand128 v20, v80, v65");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vandc128() {
|
||||
assert_asm!(0x15EBFE52, "vandc128 v15, v75, v95");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vctsxs128() {
|
||||
assert_asm!(0x1A42D23B, "vctsxs128 v82, v122, 0x2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vctuxs128() {
|
||||
assert_asm!(0x1BEACA78, "vctuxs128 v95, v25, 0xa");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vcmpbfp128() {
|
||||
assert_asm!(0x1BA5598E, "vcmpbfp128 v125, v5, v75");
|
||||
assert_asm!(0x198D79C2, "vcmpbfp128. v12, v13, v79");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vcmpeqfp128() {
|
||||
assert_asm!(0x1800D80B, "vcmpeqfp128 v64, v0, v123");
|
||||
assert_asm!(0x1ACD1C43, "vcmpeqfp128. v22, v77, v99");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vcmpequw128() {
|
||||
assert_asm!(0x18D0D60A, "vcmpequw128 v70, v80, v90");
|
||||
assert_asm!(0x18800A40, "vcmpequw128. v4, v0, v1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vcmpgefp128() {
|
||||
assert_asm!(0x1A8A1483, "vcmpgefp128 v20, v74, v98");
|
||||
assert_asm!(0x18EB7CEF, "vcmpgefp128. v103, v107, v111");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vcmpgtfp128() {
|
||||
assert_asm!(0x1BD48102, "vcmpgtfp128 v30, v20, v80");
|
||||
assert_asm!(0x1B586D68, "vcmpgtfp128. v90, v120, v13");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vcfsx128() {
|
||||
assert_asm!(0x18749ABC, "vcfsx128 v99, v19, -0xc");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vcfux128() {
|
||||
assert_asm!(0x1A6D1AF8, "vcfux128 v83, v3, 0xd");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vexptefp128() {
|
||||
assert_asm!(0x198056B0, "vexptefp128 v12, v10");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vlogefp128() {
|
||||
assert_asm!(0x1900FEFB, "vlogefp128 v72, v127");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vmaddcfp128() {
|
||||
assert_asm!(0x163B1912, "vmaddcfp128 v17, v27, v67");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vmaddfp128() {
|
||||
assert_asm!(0x16B3ECFB, "vmaddfp128 v85, v115, v125");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vmaxfp128() {
|
||||
assert_asm!(0x1B274683, "vmaxfp128 v25, v71, v104");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vminfp128() {
|
||||
assert_asm!(0x1BE012C0, "vminfp128 v31, v0, v2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vmrghw128() {
|
||||
assert_asm!(0x18CA730B, "vmrghw128 v70, v10, v110");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vmrglw128() {
|
||||
assert_asm!(0x1BD2D743, "vmrglw128 v30, v82, v122");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vmsum3fp128() {
|
||||
assert_asm!(0x14FBF993, "vmsum3fp128 v7, v27, v127");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vmsum4fp128() {
|
||||
assert_asm!(0x14A869D0, "vmsum4fp128 v5, v8, v13");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vmulfp128() {
|
||||
assert_asm!(0x1498DCBF, "vmulfp128 v100, v120, v123");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vnmsubfp128() {
|
||||
assert_asm!(0x17DBCD53, "vnmsubfp128 v30, v91, v121");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vnor128() {
|
||||
assert_asm!(0x176A6290, "vnor128 v27, v10, v12");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vor128() {
|
||||
assert_asm!(0x17EC3ADC, "vor128 v127, v12, v7");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vperm128() {
|
||||
assert_asm!(0x1661158F, "vperm128 v115, v65, v98, v6");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vpermwi128() {
|
||||
assert_asm!(0x19C342DE, "vpermwi128 v110, v72, 99");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vpkd3d128() {
|
||||
assert_asm!(0x1935DEDC, "vpkd3d128 v105, v27, 5, 1, 3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vpkshss128() {
|
||||
assert_asm!(0x16C0F62B, "vpkshss128 v86, v96, v126");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vpkshus128() {
|
||||
assert_asm!(0x153D6E48, "vpkshus128 v73, v93, v13");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vpkswss128() {
|
||||
assert_asm!(0x16FE7280, "vpkswss128 v23, v30, v14");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vpkswus128() {
|
||||
assert_asm!(0x161836C3, "vpkswus128 v16, v88, v102");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vpkuhum128() {
|
||||
assert_asm!(0x14E3BF02, "vpkuhum128 v7, v67, v87");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vpkuhus128() {
|
||||
assert_asm!(0x1600A348, "vpkuhus128 v80, v0, v20");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vpkuwum128() {
|
||||
assert_asm!(0x16EACB83, "vpkuwum128 v23, v10, v121");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vpkuwus128() {
|
||||
assert_asm!(0x17E72FC3, "vpkuwus128 v31, v71, v101");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vrefp128() {
|
||||
assert_asm!(0x1800F638, "vrefp128 v64, v30");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vrfim128() {
|
||||
assert_asm!(0x18802B30, "vrfim128 v4, v5");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vrfin128() {
|
||||
assert_asm!(0x1A200B73, "vrfin128 v17, v97");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vrfip128() {
|
||||
assert_asm!(0x1B605BB2, "vrfip128 v27, v75");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vrfiz128() {
|
||||
assert_asm!(0x1A8053F0, "vrfiz128 v20, v10");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vrlimi128() {
|
||||
assert_asm!(0x18796798, "vrlimi128 v67, v12, 0x19, 2")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vrlw128() {
|
||||
assert_asm!(0x1B002050, "vrlw128 v24, v0, v4");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vrsqrtefp128() {
|
||||
assert_asm!(0x19800673, "vrsqrtefp128 v12, v96");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vsel128() {
|
||||
assert_asm!(0x146CDF5A, "vsel128 v67, v76, v91");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vsldoi128() {
|
||||
assert_asm!(0x130BFF30, "vsldoi128 v24, v107, v31, 12");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vslo128() {
|
||||
assert_asm!(0x14E08B90, "vslo128 v7, v0, v17");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vslw128() {
|
||||
assert_asm!(0x1A1AC0D2, "vslw128 v16, v26, v88");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vspltisw128() {
|
||||
assert_asm!(0x1B68A772, "vspltisw128 v27, v84, 0x8");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vspltw128() {
|
||||
assert_asm!(0x1996EF32, "vspltw128 v12, v93, 0x16");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vsraw128() {
|
||||
assert_asm!(0x19B71950, "vsraw128 v13, v23, v3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vsro128() {
|
||||
assert_asm!(0x17C3E3D3, "vsro128 v30, v3, v124");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vsrw128() {
|
||||
assert_asm!(0x1B9271D3, "vsrw128 v28, v18, v110");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vsubfp128() {
|
||||
assert_asm!(0x17692C50, "vsubfp128 v27, v73, v5");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vupkd3d128() {
|
||||
assert_asm!(0x19FECFF0, "vupkd3d128 v15, v25, 0x1e");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vupkhsb128() {
|
||||
assert_asm!(0x1B60FB83, "vupkhsb128 v27, v127");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vupkhsh128() {
|
||||
assert_asm!(0x186017a0, "vupkhsh128 v3, v2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vupklsb128() {
|
||||
assert_asm!(0x1A00A3C3, "vupklsb128 v16, v116");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vupklsh128() {
|
||||
assert_asm!(0x186017e0, "vupklsh128 v3, v2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmx_vxor128() {
|
||||
assert_asm!(0x17E3EF32, "vxor128 v31, v99, v93");
|
||||
}
|
@ -1,15 +1,15 @@
|
||||
[package]
|
||||
name = "ppc750cl-fuzz"
|
||||
name = "powerpc-fuzz"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors = ["Richard Patel <me@terorie.dev>"]
|
||||
license = "GPL-3.0-or-later"
|
||||
description = "Complete fuzzer for ppc750cl"
|
||||
description = "Complete fuzzer for powerpc"
|
||||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
clap = "4"
|
||||
num_cpus = "1.16"
|
||||
ppc750cl = { path = "../disasm" }
|
||||
num_cpus = "1.17"
|
||||
powerpc = { path = "../disasm" }
|
||||
|
@ -1,3 +1,4 @@
|
||||
use powerpc::Extensions;
|
||||
use std::io::Write;
|
||||
use std::ops::Range;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
@ -5,9 +6,9 @@ use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
fn main() {
|
||||
let matches = clap::Command::new("ppc750cl-fuzz")
|
||||
.version("0.2.0")
|
||||
.about("Complete \"fuzzer\" for ppc750cl disassembler")
|
||||
let matches = clap::Command::new("powerpc-fuzz")
|
||||
.version(env!("CARGO_PKG_VERSION"))
|
||||
.about("Complete \"fuzzer\" for powerpc disassembler")
|
||||
.arg(
|
||||
clap::Arg::new("threads")
|
||||
.short('t')
|
||||
@ -65,7 +66,7 @@ impl MultiFuzzer {
|
||||
last = now;
|
||||
let progress = 100f32 * ((now as f32) / (0x1_0000_0000u64 as f32));
|
||||
let avg = now as f32 / elapsed.as_secs_f32() / this.threads.len() as f32;
|
||||
println!("{}/s\t{:05.2}%\tn=0x{:08x} (avg {}/s)", per_second, progress, now, avg);
|
||||
println!("{per_second}/s\t{progress:05.2}%\tn=0x{now:08x} (avg {avg}/s)");
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -101,10 +102,11 @@ impl Fuzzer {
|
||||
let counter = Arc::clone(&self.counter);
|
||||
let range = self.range.clone();
|
||||
std::thread::spawn(move || {
|
||||
let mut parsed = ppc750cl::ParsedIns::default();
|
||||
let mut parsed = powerpc::ParsedIns::default();
|
||||
for x in range.clone() {
|
||||
ppc750cl::Ins::new(x).parse_simplified(&mut parsed);
|
||||
writeln!(&mut devnull, "{}", parsed).unwrap();
|
||||
powerpc::Ins::new(x, Extensions::from_bitmask(u32::MAX))
|
||||
.parse_simplified(&mut parsed);
|
||||
writeln!(&mut devnull, "{parsed}").unwrap();
|
||||
if x % (1 << 19) == 0 {
|
||||
counter.store(x, Ordering::Relaxed);
|
||||
}
|
||||
|
@ -1,18 +1,20 @@
|
||||
[package]
|
||||
name = "ppc750cl-genisa"
|
||||
name = "powerpc-genisa"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
description = "Rust code generator for ppc750cl"
|
||||
description = "Rust code generator for powerpc"
|
||||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
indexmap = { version = "2.10", features = ["serde"] }
|
||||
log = "0.4"
|
||||
num-traits = "0.2"
|
||||
phf_codegen = "0.12"
|
||||
prettyplease = "0.2"
|
||||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
@ -20,5 +22,3 @@ serde = { version = "1.0", features = ["derive"] }
|
||||
serde_yaml = "0.9"
|
||||
simple_logger = "5.0"
|
||||
syn = { version = "2", default-features = false, features = ["full", "parsing"] }
|
||||
phf = "0.11"
|
||||
phf_codegen = "0.11"
|
||||
|
@ -8,21 +8,36 @@ use proc_macro2::{Literal, TokenStream};
|
||||
use quote::{format_ident, quote};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
enum OpcodeOrMnemonic {
|
||||
Opcode(Opcode),
|
||||
Mnemonic(Mnemonic),
|
||||
}
|
||||
|
||||
impl OpcodeOrMnemonic {
|
||||
fn name(&self) -> &str {
|
||||
match self {
|
||||
OpcodeOrMnemonic::Opcode(opcode) => &opcode.name,
|
||||
OpcodeOrMnemonic::Mnemonic(mnemonic) => &mnemonic.name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gen_asm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
||||
let mut functions = TokenStream::new();
|
||||
|
||||
let mut func_map = phf_codegen::Map::new();
|
||||
let mut mnemonic_map = BTreeMap::<String, Vec<OpcodeOrMnemonic>>::new();
|
||||
for opcode in &isa.opcodes {
|
||||
let name = format_ident!("gen_{}", opcode.ident());
|
||||
let inner = gen_opcode(opcode, isa)?;
|
||||
functions.extend(quote! {
|
||||
fn #name(args: &Arguments, modifiers: u32) -> Result<u32, ArgumentError> { #inner }
|
||||
});
|
||||
mnemonic_map
|
||||
.entry(opcode.name.clone())
|
||||
.or_default()
|
||||
.push(OpcodeOrMnemonic::Opcode(opcode.clone()));
|
||||
}
|
||||
|
||||
let mut mnemonic_map = BTreeMap::<String, Vec<Mnemonic>>::new();
|
||||
for mnemonic in &isa.mnemonics {
|
||||
mnemonic_map.entry(mnemonic.name.clone()).or_default().push(mnemonic.clone());
|
||||
mnemonic_map
|
||||
.entry(mnemonic.name.clone())
|
||||
.or_default()
|
||||
.push(OpcodeOrMnemonic::Mnemonic(mnemonic.clone()));
|
||||
}
|
||||
for (name, mnemonics) in &mnemonic_map {
|
||||
let fn_name = format!("gen_{}", to_ident(name));
|
||||
@ -32,12 +47,19 @@ pub fn gen_asm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
||||
inner = TokenStream::new();
|
||||
let mut max_args = 0;
|
||||
for mnemonic in mnemonics {
|
||||
let gen = gen_mnemonic(mnemonic, isa, false)?;
|
||||
let arg_n = Literal::usize_unsuffixed(mnemonic.args.len());
|
||||
let (gen, args_len) = match mnemonic {
|
||||
OpcodeOrMnemonic::Opcode(opcode) => {
|
||||
(gen_opcode(opcode, isa, false)?, opcode.args.len())
|
||||
}
|
||||
OpcodeOrMnemonic::Mnemonic(mnemonic) => {
|
||||
(gen_mnemonic(mnemonic, isa, false)?, mnemonic.args.len())
|
||||
}
|
||||
};
|
||||
let arg_n = Literal::usize_unsuffixed(args_len);
|
||||
inner.extend(quote! {
|
||||
#arg_n => { #gen }
|
||||
});
|
||||
max_args = max_args.max(mnemonic.args.len());
|
||||
max_args = max_args.max(args_len);
|
||||
}
|
||||
let max_args = Literal::usize_unsuffixed(max_args);
|
||||
inner.extend(quote! {
|
||||
@ -45,31 +67,25 @@ pub fn gen_asm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
||||
});
|
||||
inner = quote! { match arg_count(args) { #inner } };
|
||||
} else {
|
||||
inner = gen_mnemonic(mnemonics.first().unwrap(), isa, true)?;
|
||||
inner = match mnemonics.first().unwrap() {
|
||||
OpcodeOrMnemonic::Opcode(opcode) => gen_opcode(opcode, isa, true)?,
|
||||
OpcodeOrMnemonic::Mnemonic(mnemonic) => gen_mnemonic(mnemonic, isa, true)?,
|
||||
};
|
||||
}
|
||||
functions.extend(quote! {
|
||||
fn #fn_ident(args: &Arguments, modifiers: u32) -> Result<u32, ArgumentError> { #inner }
|
||||
});
|
||||
}
|
||||
|
||||
for (opcode, modifiers) in isa.opcodes.iter().flat_map(|o| {
|
||||
modifiers_iter(&o.modifiers, isa).filter(|m| modifiers_valid(m)).map(move |m| (o, m))
|
||||
}) {
|
||||
let suffix = modifiers.iter().map(|m| m.suffix).collect::<String>();
|
||||
let mut pattern = 0;
|
||||
for modifier in &modifiers {
|
||||
pattern |= modifier.mask();
|
||||
}
|
||||
func_map.entry(
|
||||
format!("{}{}", opcode.name, suffix),
|
||||
&format!("(gen_{}, {:#x})", opcode.ident(), pattern),
|
||||
);
|
||||
}
|
||||
|
||||
for (mnemonic, modifiers) in mnemonic_map.iter().flat_map(|(_, mnemonics)| {
|
||||
let mnemonic = mnemonics.first().unwrap();
|
||||
let modifiers = match mnemonic {
|
||||
OpcodeOrMnemonic::Opcode(opcode) => &opcode.modifiers,
|
||||
OpcodeOrMnemonic::Mnemonic(mnemonic) => {
|
||||
let opcode = isa.find_opcode(&mnemonic.opcode).unwrap();
|
||||
let modifiers = mnemonic.modifiers.as_deref().unwrap_or(&opcode.modifiers);
|
||||
mnemonic.modifiers.as_deref().unwrap_or(&opcode.modifiers)
|
||||
}
|
||||
};
|
||||
modifiers_iter(modifiers, isa).filter(|m| modifiers_valid(m)).map(move |m| (mnemonic, m))
|
||||
}) {
|
||||
let suffix = modifiers.iter().map(|m| m.suffix).collect::<String>();
|
||||
@ -77,10 +93,8 @@ pub fn gen_asm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
||||
for modifier in &modifiers {
|
||||
pattern |= modifier.mask();
|
||||
}
|
||||
func_map.entry(
|
||||
format!("{}{}", mnemonic.name, suffix),
|
||||
&format!("(gen_{}, {:#x})", to_ident(&mnemonic.name), pattern),
|
||||
);
|
||||
let name = format!("{}{}", mnemonic.name(), suffix);
|
||||
func_map.entry(name, format!("(gen_{}, {:#x})", to_ident(mnemonic.name()), pattern));
|
||||
}
|
||||
|
||||
let func_map = syn::parse_str::<TokenStream>(&func_map.build().to_string())?;
|
||||
@ -88,7 +102,7 @@ pub fn gen_asm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
||||
Ok(quote! {
|
||||
#![allow(unused)]
|
||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||
#[comment = " Code generated by ppc750-genisa. DO NOT EDIT."]
|
||||
#[comment = " Code generated by powerpc-genisa. DO NOT EDIT."]
|
||||
use crate::types::*;
|
||||
pub type Arguments = [Argument; #max_args];
|
||||
#functions
|
||||
@ -105,7 +119,7 @@ pub fn gen_asm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
||||
}
|
||||
|
||||
fn gen_parse_field(field: &Field, i: usize) -> Result<(TokenStream, bool)> {
|
||||
let Some(bits) = field.bits else { bail!("Field {} has no bits", field.name) };
|
||||
let Some(bits) = &field.bits else { bail!("Field {} has no bits", field.name) };
|
||||
let i = Literal::usize_unsuffixed(i);
|
||||
Ok(if field.signed {
|
||||
let max_value = 1 << (bits.len() - 1 + field.shift_left);
|
||||
@ -119,89 +133,84 @@ fn gen_parse_field(field: &Field, i: usize) -> Result<(TokenStream, bool)> {
|
||||
})
|
||||
}
|
||||
|
||||
fn gen_field(
|
||||
field: &Field,
|
||||
mut accessor: TokenStream,
|
||||
finalize: fn(TokenStream) -> TokenStream,
|
||||
signed: bool,
|
||||
) -> Result<TokenStream> {
|
||||
let Some(bits) = field.bits else { bail!("Field {} has no bits", field.name) };
|
||||
let mut shift_right = bits.shift();
|
||||
let mut shift_left = field.shift_left;
|
||||
if shift_right == shift_left {
|
||||
// Optimization: these cancel each other out
|
||||
// Adjust subsequent operations to operate on the full value
|
||||
shift_right = 0;
|
||||
shift_left = 0;
|
||||
}
|
||||
fn gen_field(field: &Field, mut accessor: TokenStream, signed: bool) -> Result<TokenStream> {
|
||||
let Some(bits) = &field.bits else { bail!("Field {} has no bits", field.name) };
|
||||
|
||||
// Optimization: offset all shifts to avoid unnecessary operations
|
||||
let shift_offset = field.shift_left.min(bits.shift());
|
||||
|
||||
// Handle the operations (in reverse order from disassembly)
|
||||
let mut operations = TokenStream::new();
|
||||
let mut inner;
|
||||
|
||||
if signed {
|
||||
accessor = quote! { #accessor as u32 };
|
||||
}
|
||||
|
||||
// Swap 5-bit halves (SPR, TBR)
|
||||
if field.split {
|
||||
operations.extend(quote! {
|
||||
value = ((value & 0b11111_00000) >> 5) | ((value & 0b00000_11111) << 5);
|
||||
});
|
||||
inner = quote! { value };
|
||||
} else {
|
||||
inner = accessor.clone();
|
||||
}
|
||||
let mut bit_position = 0;
|
||||
for range in bits.0.iter().rev() {
|
||||
let mut shift_right = range.shift() - shift_offset;
|
||||
let mut shift_left = field.shift_left + bit_position - shift_offset;
|
||||
|
||||
// Handle left shift
|
||||
if shift_left > 0 {
|
||||
// Optimize shifts
|
||||
let common_shift = shift_right.min(shift_left);
|
||||
shift_right -= common_shift;
|
||||
shift_left -= common_shift;
|
||||
|
||||
// Shift left
|
||||
let mut inner = if shift_left > 0 {
|
||||
let shift_left = Literal::u8_unsuffixed(shift_left);
|
||||
inner = quote! { (#inner >> #shift_left) };
|
||||
}
|
||||
quote! { (arg >> #shift_left) }
|
||||
} else {
|
||||
quote! { arg }
|
||||
};
|
||||
|
||||
// Mask
|
||||
let mask = HexLiteral(bits.mask() >> shift_right);
|
||||
let mask = HexLiteral(range.mask() >> shift_right);
|
||||
inner = quote! { #inner & #mask };
|
||||
|
||||
// Shift right
|
||||
if shift_right > 0 {
|
||||
let shift = Literal::u8_unsuffixed(shift_right);
|
||||
inner = quote! { (#inner) << #shift };
|
||||
let shift_right = Literal::u8_unsuffixed(shift_right);
|
||||
inner = quote! { (#inner) << #shift_right };
|
||||
}
|
||||
|
||||
bit_position += range.len();
|
||||
operations.extend(quote! {
|
||||
code |= #inner;
|
||||
});
|
||||
}
|
||||
|
||||
if operations.is_empty() {
|
||||
Ok(finalize(inner))
|
||||
} else {
|
||||
inner = finalize(inner);
|
||||
Ok(quote! {{
|
||||
let mut value = #accessor;
|
||||
let arg = #accessor;
|
||||
#operations
|
||||
#inner
|
||||
}})
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_opcode(opcode: &Opcode, isa: &Isa) -> Result<TokenStream> {
|
||||
fn gen_opcode(opcode: &Opcode, isa: &Isa, check_arg_count: bool) -> Result<TokenStream> {
|
||||
let mut args = TokenStream::new();
|
||||
for (i, arg) in opcode.args.iter().enumerate() {
|
||||
let field = isa.find_field(arg).unwrap();
|
||||
let comment = format!(" {}", field.name);
|
||||
let (accessor, signed) = gen_parse_field(field, i)?;
|
||||
let value = gen_field(field, accessor, |s| s, signed)?;
|
||||
let operations = gen_field(field, accessor, signed)?;
|
||||
args.extend(quote! {
|
||||
#[comment = #comment]
|
||||
code |= #value;
|
||||
#operations
|
||||
});
|
||||
}
|
||||
|
||||
let arg_count = Literal::usize_unsuffixed(opcode.args.len());
|
||||
let mut result = TokenStream::new();
|
||||
if check_arg_count {
|
||||
result.extend(quote! { check_arg_count(args, #arg_count)?; });
|
||||
}
|
||||
|
||||
let pattern = HexLiteral(opcode.pattern);
|
||||
Ok(quote! {
|
||||
check_arg_count(args, #arg_count)?;
|
||||
result.extend(quote! {
|
||||
let mut code = #pattern | modifiers;
|
||||
#args
|
||||
Ok(code)
|
||||
})
|
||||
});
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn gen_mnemonic(mnemonic: &Mnemonic, isa: &Isa, check_arg_count: bool) -> Result<TokenStream> {
|
||||
@ -210,11 +219,11 @@ fn gen_mnemonic(mnemonic: &Mnemonic, isa: &Isa, check_arg_count: bool) -> Result
|
||||
};
|
||||
let mut args = TokenStream::new();
|
||||
for (i, arg) in mnemonic.args.iter().enumerate() {
|
||||
let comment = format!(" {}", arg);
|
||||
let arg = gen_argument(&mnemonic.args, i, isa, mnemonic.replace_assemble.get(arg))?;
|
||||
let comment = format!(" {arg}");
|
||||
let operations = gen_argument(&mnemonic.args, i, isa, mnemonic.replace_assemble.get(arg))?;
|
||||
args.extend(quote! {
|
||||
#[comment = #comment]
|
||||
code |= #arg;
|
||||
#operations
|
||||
});
|
||||
}
|
||||
|
||||
@ -238,10 +247,10 @@ fn gen_mnemonic(mnemonic: &Mnemonic, isa: &Isa, check_arg_count: bool) -> Result
|
||||
format!("Mnemonic {}: unknown field {}", mnemonic.name, in_field.name)
|
||||
})?;
|
||||
let (accessor, signed) = gen_parse_field(in_field, arg_n)?;
|
||||
let arg = gen_field(condition.field, accessor, |s| s, signed)?;
|
||||
let operations = gen_field(condition.field, accessor, signed)?;
|
||||
args.extend(quote! {
|
||||
#[comment = #comment]
|
||||
code |= #arg;
|
||||
#operations
|
||||
});
|
||||
}
|
||||
ConditionValue::Complex(c) => {
|
||||
@ -256,10 +265,10 @@ fn gen_mnemonic(mnemonic: &Mnemonic, isa: &Isa, check_arg_count: bool) -> Result
|
||||
any_signed |= signed;
|
||||
Ok(s)
|
||||
})?;
|
||||
let arg = gen_field(condition.field, quote! { (#arg) }, |s| s, any_signed)?;
|
||||
let operations = gen_field(condition.field, quote! { (#arg) }, any_signed)?;
|
||||
args.extend(quote! {
|
||||
#[comment = #comment]
|
||||
code |= #arg;
|
||||
#operations
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -299,9 +308,9 @@ fn gen_argument(
|
||||
any_signed |= signed;
|
||||
Ok(parse)
|
||||
})?;
|
||||
gen_field(field, quote! { (#stream) }, |s| s, any_signed)
|
||||
gen_field(field, quote! { (#stream) }, any_signed)
|
||||
} else {
|
||||
let (accessor, signed) = gen_parse_field(field, arg_n)?;
|
||||
gen_field(field, accessor, |s| s, signed)
|
||||
gen_field(field, accessor, signed)
|
||||
}
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ pub fn parse_conditions<'a>(condition: &'a str, isa: &'a Isa) -> Result<Vec<Cond
|
||||
} else if let Some((field, value)) = tok.split_once(" >= ") {
|
||||
(field, value, ConditionOp::Gte)
|
||||
} else {
|
||||
log::error!("Invalid condition: {}", tok);
|
||||
log::error!("Invalid condition: {tok}");
|
||||
continue;
|
||||
};
|
||||
let mut field_mask = u32::MAX;
|
||||
@ -74,7 +74,7 @@ pub fn parse_conditions<'a>(condition: &'a str, isa: &'a Isa) -> Result<Vec<Cond
|
||||
};
|
||||
let field = isa
|
||||
.find_field(field)
|
||||
.with_context(|| format!("Condition references unknown field {}", field))?;
|
||||
.with_context(|| format!("Condition references unknown field {field}"))?;
|
||||
let value = if let Ok(value) = parse_unsigned(value) {
|
||||
ConditionValue::ConstantUnsigned(value)
|
||||
} else if let Ok(value) = parse_signed(value) {
|
||||
|
@ -1,27 +1,31 @@
|
||||
use crate::condition::{parse_conditions, replace_fields};
|
||||
use crate::ident;
|
||||
use crate::isa::{modifiers_iter, modifiers_valid, HexLiteral, Isa, Opcode};
|
||||
use crate::{
|
||||
condition::{parse_conditions, replace_fields},
|
||||
ident,
|
||||
isa::{modifiers_iter, modifiers_valid, HexLiteral, Isa, Opcode},
|
||||
};
|
||||
use anyhow::{bail, ensure, Result};
|
||||
use proc_macro2::{Literal, TokenStream};
|
||||
use proc_macro2::{Ident, Literal, TokenStream};
|
||||
use quote::{format_ident, quote};
|
||||
use std::collections::HashMap;
|
||||
use std::num::NonZeroU8;
|
||||
use std::{
|
||||
collections::{btree_map, BTreeMap, HashMap},
|
||||
hash::{DefaultHasher, Hash, Hasher},
|
||||
};
|
||||
|
||||
pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
||||
// The entry table allows us to quickly find the range of possible opcodes
|
||||
// for a given 6-bit prefix. 2*64 bytes should fit in a cache line (or two).
|
||||
struct OpcodeEntry {
|
||||
start: u8,
|
||||
count: u8,
|
||||
start: u16,
|
||||
count: u16,
|
||||
}
|
||||
let mut sorted_ops = Vec::<Opcode>::new();
|
||||
let mut entries = Vec::<OpcodeEntry>::new();
|
||||
let mut sorted_ops = Vec::<Opcode>::with_capacity(isa.opcodes.len());
|
||||
let mut entries = Vec::<OpcodeEntry>::with_capacity(64);
|
||||
for i in 0..64 {
|
||||
let mut entry = OpcodeEntry { start: 0, count: 0 };
|
||||
for opcode in &isa.opcodes {
|
||||
if (opcode.pattern >> 26) as u8 == i {
|
||||
if (opcode.pattern >> 26) as u16 == i {
|
||||
if entry.count == 0 {
|
||||
entry.start = sorted_ops.len() as u8;
|
||||
entry.start = sorted_ops.len() as u16;
|
||||
}
|
||||
// Sanity check for duplicate opcodes
|
||||
if sorted_ops.iter().any(|op| op.name == opcode.name) {
|
||||
@ -36,19 +40,18 @@ pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
||||
} else if let Some(op) = (entry.count == 1).then(|| &sorted_ops[entry.start as usize]) {
|
||||
log::info!("{:#X}: {}", i, op.name);
|
||||
} else {
|
||||
log::info!("{:#X}: <invalid>", i);
|
||||
log::info!("{i:#X}: <invalid>");
|
||||
}
|
||||
entries.push(entry);
|
||||
}
|
||||
ensure!(sorted_ops.len() == isa.opcodes.len());
|
||||
ensure!(sorted_ops.len() <= 255);
|
||||
let opcode_max = Literal::u8_unsuffixed((sorted_ops.len() - 1) as u8);
|
||||
let opcode_max = Literal::u16_unsuffixed((sorted_ops.len() - 1) as u16);
|
||||
|
||||
// Generate the opcode entries table
|
||||
let mut opcode_entries = TokenStream::new();
|
||||
for entry in &entries {
|
||||
let start = Literal::u8_unsuffixed(entry.start);
|
||||
let end = Literal::u8_unsuffixed(entry.start + entry.count);
|
||||
let start = Literal::u16_unsuffixed(entry.start);
|
||||
let end = Literal::u16_unsuffixed(entry.start + entry.count);
|
||||
opcode_entries.extend(quote! { (#start, #end), });
|
||||
}
|
||||
|
||||
@ -59,9 +62,21 @@ pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
||||
for (idx, opcode) in sorted_ops.iter().enumerate() {
|
||||
let bitmask = HexLiteral(opcode.mask(isa));
|
||||
let pattern = HexLiteral(opcode.pattern);
|
||||
let enum_idx = Literal::u8_unsuffixed(idx as u8);
|
||||
let enum_idx = Literal::u16_unsuffixed(idx as u16);
|
||||
let name = &opcode.name;
|
||||
opcode_patterns.extend(quote! { (#bitmask, #pattern), });
|
||||
let comment = format!(" {name}");
|
||||
let extension =
|
||||
isa.extensions.iter().find(|(_, e)| e.opcodes.iter().any(|o| o.name == opcode.name));
|
||||
let initializer = if let Some((id, _)) = extension {
|
||||
let ident = format_ident!("{id}");
|
||||
quote! { OpcodePattern::extension(#bitmask, #pattern, Extension::#ident) }
|
||||
} else {
|
||||
quote! { OpcodePattern::base(#bitmask, #pattern) }
|
||||
};
|
||||
opcode_patterns.extend(quote! {
|
||||
#[comment = #comment]
|
||||
#initializer,
|
||||
});
|
||||
opcode_names.extend(quote! { #name, });
|
||||
let doc = opcode.doc();
|
||||
let variant = opcode.variant();
|
||||
@ -74,7 +89,7 @@ pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
||||
// Generate field and modifier accessors
|
||||
let mut ins_fields = TokenStream::new();
|
||||
for field in &isa.fields {
|
||||
let Some(bits) = field.bits else {
|
||||
let Some(bits) = &field.bits else {
|
||||
continue;
|
||||
};
|
||||
// TODO get rid of .nz hack
|
||||
@ -82,56 +97,72 @@ pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut shift_right = bits.shift();
|
||||
let mut shift_left = field.shift_left;
|
||||
if shift_right == shift_left {
|
||||
// Optimization: these cancel each other out
|
||||
// Adjust subsequent operations to operate on the full value
|
||||
shift_right = 0;
|
||||
shift_left = 0;
|
||||
}
|
||||
// Optimization: offset all shifts to avoid unnecessary operations
|
||||
let shift_offset = field.shift_left.min(bits.shift());
|
||||
|
||||
// Shift right and mask
|
||||
let mut inner = quote! { self.code };
|
||||
// Extract the field bits from the instruction
|
||||
let mut operations = Vec::new();
|
||||
let mut bit_position = 0;
|
||||
for range in bits.0.iter().rev() {
|
||||
let mut shift_right = range.shift() - shift_offset;
|
||||
let mut shift_left = bit_position;
|
||||
|
||||
// Optimize shifts
|
||||
let common_shift = shift_right.min(shift_left);
|
||||
shift_right -= common_shift;
|
||||
shift_left -= common_shift;
|
||||
|
||||
// Shift right
|
||||
let mut result = quote! { self.code };
|
||||
if shift_right > 0 {
|
||||
let shift = Literal::u8_unsuffixed(shift_right);
|
||||
inner = quote! { (#inner >> #shift) };
|
||||
result = quote! { (#result >> #shift) };
|
||||
}
|
||||
let mask = HexLiteral(bits.mask() >> shift_right);
|
||||
inner = quote! { #inner & #mask };
|
||||
|
||||
// Mask
|
||||
let mask = HexLiteral(range.mask() >> shift_right);
|
||||
result = quote! { (#result & #mask) };
|
||||
|
||||
// Shift left
|
||||
if shift_left > 0 {
|
||||
let shift = Literal::u8_unsuffixed(shift_left);
|
||||
result = quote! { (#result << #shift) };
|
||||
}
|
||||
|
||||
bit_position += range.len();
|
||||
operations.push(result);
|
||||
}
|
||||
let mut inner = if operations.len() > 1 {
|
||||
quote! { (#(#operations)|*) }
|
||||
} else {
|
||||
operations.into_iter().next().unwrap()
|
||||
};
|
||||
|
||||
// Determine the smallest integer type that can hold the value
|
||||
let num_bits = bits.len() + field.shift_left;
|
||||
let (out_type, cast, sign_shift) = match (num_bits, field.signed) {
|
||||
(1..=8, false) => (ident!(u8), true, None),
|
||||
(9..=16, false) => (ident!(u16), true, None),
|
||||
(17..=32, false) => (ident!(u32), false, None),
|
||||
(1..=8, true) => (ident!(i8), true, NonZeroU8::new(8 - num_bits)),
|
||||
(9..=16, true) => (ident!(i16), true, NonZeroU8::new(16 - num_bits)),
|
||||
(17..=32, true) => (ident!(i32), true, NonZeroU8::new(32 - num_bits)),
|
||||
(1..=8, false) => (ident!(u8), true, 0),
|
||||
(9..=16, false) => (ident!(u16), true, 0),
|
||||
(17..=32, false) => (ident!(u32), false, 0),
|
||||
(1..=8, true) => (ident!(i8), true, 8 - bits.len()),
|
||||
(9..=16, true) => (ident!(i16), true, 16 - bits.len()),
|
||||
(17..=32, true) => (ident!(i32), true, 32 - bits.len()),
|
||||
(v, _) => bail!("Unsupported field size {v}"),
|
||||
};
|
||||
|
||||
// Handle sign extension
|
||||
if let Some(sign_shift) = sign_shift {
|
||||
let sign_shift = Literal::u8_unsuffixed(sign_shift.get());
|
||||
inner = quote! { (((#inner) << #sign_shift) as #out_type) >> #sign_shift };
|
||||
if sign_shift > shift_offset {
|
||||
let sign_shift = Literal::u8_unsuffixed(sign_shift - shift_offset);
|
||||
inner = quote! { ((#inner << #sign_shift) as #out_type) >> #sign_shift };
|
||||
} else if cast {
|
||||
inner = quote! { (#inner) as #out_type };
|
||||
inner = quote! { #inner as #out_type };
|
||||
}
|
||||
|
||||
// Handle left shift
|
||||
let shift_left = field.shift_left - shift_offset;
|
||||
if shift_left > 0 {
|
||||
let shift_left = Literal::u8_unsuffixed(shift_left);
|
||||
inner = quote! { (#inner) << #shift_left };
|
||||
}
|
||||
|
||||
// Swap 5-bit halves (SPR, TBR)
|
||||
if field.split {
|
||||
inner = quote! {
|
||||
let value = #inner;
|
||||
((value & 0b11111_00000) >> 5) | ((value & 0b00000_11111) << 5)
|
||||
};
|
||||
inner = quote! { #inner << #shift_left };
|
||||
}
|
||||
|
||||
// Write the accessor
|
||||
@ -230,9 +261,12 @@ pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
||||
let mut defs_uses_functions = TokenStream::new();
|
||||
let mut defs_refs = TokenStream::new();
|
||||
let mut uses_refs = TokenStream::new();
|
||||
|
||||
// Deduplicate equivalent functions
|
||||
let mut hash_to_fn = BTreeMap::<u64, Ident>::new();
|
||||
|
||||
for opcode in &sorted_ops {
|
||||
let mut defs = TokenStream::new();
|
||||
let mut uses = TokenStream::new();
|
||||
let mut defs_count = 0;
|
||||
for def in &opcode.defs {
|
||||
if isa.find_field(def).is_some_and(|f| f.arg.is_none()) {
|
||||
@ -242,6 +276,8 @@ pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
||||
defs.extend(quote! { #arg, });
|
||||
defs_count += 1;
|
||||
}
|
||||
|
||||
let mut uses = TokenStream::new();
|
||||
let mut use_count = 0;
|
||||
for use_ in &opcode.uses {
|
||||
if let Some(use_) = use_.strip_suffix(".nz") {
|
||||
@ -264,10 +300,23 @@ pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
||||
defs.extend(quote! { Argument::None, });
|
||||
}
|
||||
let defs_name = format_ident!("defs_{}", opcode.ident());
|
||||
|
||||
let mut hasher = DefaultHasher::default();
|
||||
opcode.defs.hash(&mut hasher);
|
||||
let defs_hash = hasher.finish();
|
||||
match hash_to_fn.entry(defs_hash) {
|
||||
btree_map::Entry::Vacant(e) => {
|
||||
e.insert(defs_name.clone());
|
||||
defs_uses_functions.extend(quote! {
|
||||
fn #defs_name(out: &mut Arguments, ins: Ins) { *out = [#defs]; }
|
||||
});
|
||||
defs_refs.extend(quote! { #defs_name, });
|
||||
}
|
||||
btree_map::Entry::Occupied(e) => {
|
||||
let ident = e.get();
|
||||
defs_refs.extend(quote! { #ident, });
|
||||
}
|
||||
}
|
||||
} else {
|
||||
defs_refs.extend(quote! { defs_uses_empty, });
|
||||
}
|
||||
@ -277,10 +326,23 @@ pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
||||
uses.extend(quote! { Argument::None, });
|
||||
}
|
||||
let uses_name = format_ident!("uses_{}", opcode.ident());
|
||||
|
||||
let mut hasher = DefaultHasher::default();
|
||||
opcode.uses.hash(&mut hasher);
|
||||
let uses_hash = hasher.finish();
|
||||
match hash_to_fn.entry(uses_hash) {
|
||||
btree_map::Entry::Vacant(e) => {
|
||||
e.insert(uses_name.clone());
|
||||
defs_uses_functions.extend(quote! {
|
||||
fn #uses_name(out: &mut Arguments, ins: Ins) { *out = [#uses]; }
|
||||
});
|
||||
uses_refs.extend(quote! { #uses_name, });
|
||||
}
|
||||
btree_map::Entry::Occupied(e) => {
|
||||
let ident = e.get();
|
||||
uses_refs.extend(quote! { #ident, });
|
||||
}
|
||||
}
|
||||
} else {
|
||||
uses_refs.extend(quote! { defs_uses_empty, });
|
||||
}
|
||||
@ -289,78 +351,145 @@ pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
||||
fn defs_uses_empty(out: &mut Arguments, _ins: Ins) { *out = EMPTY_ARGS; }
|
||||
});
|
||||
|
||||
// Filling the tables to 256 entries to avoid bounds checks
|
||||
for _ in sorted_ops.len()..256 {
|
||||
opcode_patterns.extend(quote! { (0, 0), });
|
||||
opcode_names.extend(quote! { "<illegal>", });
|
||||
basic_functions_ref.extend(quote! { mnemonic_illegal, });
|
||||
simplified_functions_ref.extend(quote! { mnemonic_illegal, });
|
||||
defs_refs.extend(quote! { defs_uses_empty, });
|
||||
uses_refs.extend(quote! { defs_uses_empty, });
|
||||
}
|
||||
|
||||
let mut none_args = TokenStream::new();
|
||||
for _ in 0..max_args {
|
||||
none_args.extend(quote! { Argument::None, });
|
||||
}
|
||||
|
||||
let extension_variants = isa
|
||||
.extensions
|
||||
.iter()
|
||||
.map(|(id, ext)| {
|
||||
let ident = format_ident!("{id}");
|
||||
let desc = format!(" {}", ext.name);
|
||||
quote! {
|
||||
#[doc = #desc]
|
||||
#ident,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let extension_requires = isa
|
||||
.extensions
|
||||
.iter()
|
||||
.filter_map(|(id, ext)| {
|
||||
if ext.requires.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let requires = ext
|
||||
.requires
|
||||
.iter()
|
||||
.map(|parent| {
|
||||
let ident = format_ident!("{parent}");
|
||||
quote! { Extension::#ident.bitmask() }
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let ident = format_ident!("{id}");
|
||||
Some(quote! { Extension::#ident => #(#requires)|*, })
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let extension_names = isa
|
||||
.extensions
|
||||
.iter()
|
||||
.map(|(id, ext)| {
|
||||
let ident = format_ident!("{id}");
|
||||
let name = &ext.name;
|
||||
Some(quote! { Extension::#ident => #name, })
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let extensions = quote! {
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(u32)]
|
||||
pub enum Extension {
|
||||
#(#extension_variants)*
|
||||
}
|
||||
impl Extension {
|
||||
#[inline]
|
||||
pub const fn bitmask(self) -> u32 {
|
||||
(1 << (self as u32)) | match self {
|
||||
#(#extension_requires)*
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
pub const fn name(self) -> &'static str {
|
||||
match self {
|
||||
#(#extension_names)*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let entries_count = Literal::usize_unsuffixed(entries.len());
|
||||
let opcode_count = Literal::usize_unsuffixed(sorted_ops.len());
|
||||
let max_args = Literal::usize_unsuffixed(max_args);
|
||||
Ok(quote! {
|
||||
#![allow(unused)]
|
||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||
#[comment = " Code generated by ppc750-genisa. DO NOT EDIT."]
|
||||
#[comment = " Code generated by powerpc-genisa. DO NOT EDIT."]
|
||||
use crate::disasm::*;
|
||||
#extensions
|
||||
#[doc = " The entry table allows us to quickly find the range of possible opcodes for a"]
|
||||
#[doc = " given 6-bit prefix. 2*64 bytes should fit in a cache line (or two)."]
|
||||
static OPCODE_ENTRIES: [(u8, u8); 64] = [#opcode_entries];
|
||||
static OPCODE_ENTRIES: [(u16, u16); #entries_count] = [#opcode_entries];
|
||||
#[derive(Copy, Clone)]
|
||||
struct OpcodePattern {
|
||||
bitmask: u32,
|
||||
pattern: u32,
|
||||
extensions: Extensions,
|
||||
}
|
||||
impl OpcodePattern {
|
||||
const fn base(bitmask: u32, pattern: u32) -> Self {
|
||||
Self { bitmask, pattern, extensions: Extensions::none() }
|
||||
}
|
||||
const fn extension(bitmask: u32, pattern: u32, extension: Extension) -> Self {
|
||||
Self { bitmask, pattern, extensions: Extensions::from_extension(extension) }
|
||||
}
|
||||
}
|
||||
#[doc = " The bitmask and pattern for each opcode."]
|
||||
static OPCODE_PATTERNS: [(u32, u32); 256] = [#opcode_patterns];
|
||||
static OPCODE_PATTERNS: [OpcodePattern; #opcode_count] = [#opcode_patterns];
|
||||
#[doc = " The name of each opcode."]
|
||||
static OPCODE_NAMES: [&str; 256] = [#opcode_names];
|
||||
static OPCODE_NAMES: [&str; #opcode_count] = [#opcode_names];
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
#[repr(u16)]
|
||||
#[non_exhaustive]
|
||||
pub enum Opcode {
|
||||
#[doc = " An illegal or unknown opcode"]
|
||||
#[default]
|
||||
Illegal = u8::MAX,
|
||||
Illegal = u16::MAX,
|
||||
#opcode_enum
|
||||
}
|
||||
impl Opcode {
|
||||
#[inline]
|
||||
pub fn _mnemonic(self) -> &'static str {
|
||||
OPCODE_NAMES[self as usize]
|
||||
pub fn mnemonic(self) -> &'static str {
|
||||
OPCODE_NAMES.get(self as usize).copied().unwrap_or("<illegal>")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn _detect(code: u32) -> Self {
|
||||
pub fn detect(code: u32, extensions: Extensions) -> Self {
|
||||
let entry = OPCODE_ENTRIES[(code >> 26) as usize];
|
||||
for i in entry.0..entry.1 {
|
||||
let pattern = OPCODE_PATTERNS[i as usize];
|
||||
if (code & pattern.0) == pattern.1 {
|
||||
#[comment = " Safety: The enum is repr(u8) and the value is within the enum's range"]
|
||||
return unsafe { core::mem::transmute::<u8, Opcode>(i) };
|
||||
let op = OPCODE_PATTERNS[i as usize];
|
||||
if extensions.contains_all(op.extensions) && (code & op.bitmask) == op.pattern {
|
||||
#[comment = " Safety: The enum is repr(u16) and the value is within the enum's range"]
|
||||
return unsafe { core::mem::transmute::<u16, Opcode>(i) };
|
||||
}
|
||||
}
|
||||
Self::Illegal
|
||||
}
|
||||
}
|
||||
impl From<u8> for Opcode {
|
||||
impl From<u16> for Opcode {
|
||||
#[inline]
|
||||
fn from(value: u8) -> Self {
|
||||
fn from(value: u16) -> Self {
|
||||
if value > #opcode_max {
|
||||
Self::Illegal
|
||||
} else {
|
||||
#[comment = " Safety: The enum is repr(u8) and the value is within the enum's range"]
|
||||
unsafe { core::mem::transmute::<u8, Self>(value) }
|
||||
#[comment = " Safety: The enum is repr(u16) and the value is within the enum's range"]
|
||||
unsafe { core::mem::transmute::<u16, Self>(value) }
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<Opcode> for u8 {
|
||||
impl From<Opcode> for u16 {
|
||||
#[inline]
|
||||
fn from(value: Opcode) -> Self {
|
||||
value as u8
|
||||
value as u16
|
||||
}
|
||||
}
|
||||
|
||||
@ -373,28 +502,36 @@ pub fn gen_disasm(isa: &Isa, max_args: usize) -> Result<TokenStream> {
|
||||
|
||||
type MnemonicFunction = fn(&mut ParsedIns, Ins);
|
||||
#mnemonic_functions
|
||||
static BASIC_MNEMONICS: [MnemonicFunction; 256] = [#basic_functions_ref];
|
||||
#[inline]
|
||||
pub fn parse_basic(out: &mut ParsedIns, ins: Ins) {
|
||||
BASIC_MNEMONICS[ins.op as usize](out, ins)
|
||||
static BASIC_MNEMONICS: [MnemonicFunction; #opcode_count] = [#basic_functions_ref];
|
||||
pub(crate) fn parse_basic(out: &mut ParsedIns, ins: Ins) {
|
||||
match BASIC_MNEMONICS.get(ins.op as usize) {
|
||||
Some(f) => f(out, ins),
|
||||
None => mnemonic_illegal(out, ins),
|
||||
}
|
||||
}
|
||||
static SIMPLIFIED_MNEMONICS: [MnemonicFunction; #opcode_count] = [#simplified_functions_ref];
|
||||
pub(crate) fn parse_simplified(out: &mut ParsedIns, ins: Ins) {
|
||||
match SIMPLIFIED_MNEMONICS.get(ins.op as usize) {
|
||||
Some(f) => f(out, ins),
|
||||
None => mnemonic_illegal(out, ins),
|
||||
}
|
||||
static SIMPLIFIED_MNEMONICS: [MnemonicFunction; 256] = [#simplified_functions_ref];
|
||||
#[inline]
|
||||
pub fn parse_simplified(out: &mut ParsedIns, ins: Ins) {
|
||||
SIMPLIFIED_MNEMONICS[ins.op as usize](out, ins)
|
||||
}
|
||||
|
||||
type DefsUsesFunction = fn(&mut Arguments, Ins);
|
||||
#defs_uses_functions
|
||||
static DEFS_FUNCTIONS: [DefsUsesFunction; 256] = [#defs_refs];
|
||||
#[inline]
|
||||
pub fn parse_defs(out: &mut Arguments, ins: Ins) {
|
||||
DEFS_FUNCTIONS[ins.op as usize](out, ins)
|
||||
static DEFS_FUNCTIONS: [DefsUsesFunction; #opcode_count] = [#defs_refs];
|
||||
pub(crate) fn parse_defs(out: &mut Arguments, ins: Ins) {
|
||||
match DEFS_FUNCTIONS.get(ins.op as usize) {
|
||||
Some(f) => f(out, ins),
|
||||
None => defs_uses_empty(out, ins),
|
||||
}
|
||||
}
|
||||
static USES_FUNCTIONS: [DefsUsesFunction; #opcode_count] = [#uses_refs];
|
||||
pub(crate) fn parse_uses(out: &mut Arguments, ins: Ins) {
|
||||
match USES_FUNCTIONS.get(ins.op as usize) {
|
||||
Some(f) => f(out, ins),
|
||||
None => defs_uses_empty(out, ins),
|
||||
}
|
||||
static USES_FUNCTIONS: [DefsUsesFunction; 256] = [#uses_refs];
|
||||
#[inline]
|
||||
pub fn parse_uses(out: &mut Arguments, ins: Ins) {
|
||||
USES_FUNCTIONS[ins.op as usize](out, ins)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -467,7 +604,7 @@ fn gen_mnemonic(
|
||||
}
|
||||
let names_len = Literal::usize_unsuffixed(names.len());
|
||||
Ok(quote! { {
|
||||
const MODIFIERS: [&str; #names_len] = [#(#names),*];
|
||||
static MODIFIERS: [&str; #names_len] = [#(#names),*];
|
||||
ParsedIns { mnemonic: MODIFIERS[#bitset], args: #arguments }
|
||||
} })
|
||||
}
|
||||
|
@ -1,17 +1,66 @@
|
||||
use std::collections::HashMap;
|
||||
use std::{fs::File, path::Path, str::FromStr};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use indexmap::IndexMap;
|
||||
use num_traits::PrimInt;
|
||||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use quote::{format_ident, ToTokens};
|
||||
use serde::de::IntoDeserializer;
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
|
||||
pub fn load_isa(path: &Path) -> Result<Isa> {
|
||||
let yaml_file =
|
||||
File::open(path).with_context(|| format!("Failed to open file {}", path.display()))?;
|
||||
let isa: Isa = serde_yaml::from_reader(yaml_file)
|
||||
let mut isa: Isa = serde_yaml::from_reader(yaml_file)
|
||||
.with_context(|| format!("While parsing file {}", path.display()))?;
|
||||
// Merge in all extensions
|
||||
for extension in isa.extensions.values() {
|
||||
for field in &extension.fields {
|
||||
if isa.find_field(&field.name).is_some() {
|
||||
bail!(
|
||||
"Field {} already exists (while applying extension {})",
|
||||
field.name,
|
||||
extension.name
|
||||
);
|
||||
} else {
|
||||
isa.fields.push(field.clone());
|
||||
}
|
||||
}
|
||||
for modifier in &extension.modifiers {
|
||||
if isa.find_modifier(&modifier.name).is_some() {
|
||||
bail!(
|
||||
"Modifier {} already exists (while applying extension {})",
|
||||
modifier.name,
|
||||
extension.name
|
||||
);
|
||||
} else {
|
||||
isa.modifiers.push(modifier.clone());
|
||||
}
|
||||
}
|
||||
for opcode in &extension.opcodes {
|
||||
if isa.find_opcode(&opcode.name).is_some() {
|
||||
bail!(
|
||||
"Opcode {} already exists (while applying extension {})",
|
||||
opcode.name,
|
||||
extension.name
|
||||
);
|
||||
} else {
|
||||
isa.opcodes.push(opcode.clone());
|
||||
}
|
||||
}
|
||||
for mnemonic in &extension.mnemonics {
|
||||
if isa.find_mnemonic(&mnemonic.name).is_some() {
|
||||
bail!(
|
||||
"Mnemonic {} already exists (while applying extension {})",
|
||||
mnemonic.name,
|
||||
extension.name
|
||||
);
|
||||
} else {
|
||||
isa.mnemonics.push(mnemonic.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(isa)
|
||||
}
|
||||
|
||||
@ -22,6 +71,18 @@ pub struct Isa {
|
||||
pub modifiers: Vec<Modifier>,
|
||||
pub opcodes: Vec<Opcode>,
|
||||
pub mnemonics: Vec<Mnemonic>,
|
||||
pub extensions: IndexMap<String, Extension>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone, Debug, Default)]
|
||||
#[serde(default)]
|
||||
pub struct Extension {
|
||||
pub name: String,
|
||||
pub requires: Vec<String>,
|
||||
pub fields: Vec<Field>,
|
||||
pub modifiers: Vec<Modifier>,
|
||||
pub opcodes: Vec<Opcode>,
|
||||
pub mnemonics: Vec<Mnemonic>,
|
||||
}
|
||||
|
||||
impl Isa {
|
||||
@ -36,6 +97,10 @@ impl Isa {
|
||||
pub fn find_opcode(&self, name: &str) -> Option<&Opcode> {
|
||||
self.opcodes.iter().find(|o| o.name == name)
|
||||
}
|
||||
|
||||
pub fn find_mnemonic(&self, name: &str) -> Option<&Mnemonic> {
|
||||
self.mnemonics.iter().find(|m| m.name == name)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone, Debug, Default)]
|
||||
@ -44,9 +109,8 @@ pub struct Field {
|
||||
pub name: String,
|
||||
pub desc: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub bits: Option<BitRange>,
|
||||
pub bits: Option<SplitBitRange>,
|
||||
pub signed: bool,
|
||||
pub split: bool,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub arg: Option<String>,
|
||||
pub shift_left: u8,
|
||||
@ -55,16 +119,12 @@ pub struct Field {
|
||||
impl Field {
|
||||
/// Calculate the field mask from its bit range
|
||||
pub fn mask(&self) -> u32 {
|
||||
self.bits.map(|b| b.mask()).unwrap_or(0)
|
||||
self.bits.as_ref().map(|b| b.mask()).unwrap_or(0)
|
||||
}
|
||||
|
||||
/// Shift and mask a value according to the field
|
||||
pub fn shift_value(&self, mut value: u32) -> u32 {
|
||||
if self.split {
|
||||
// Swap 5-bit halves (SPR, TBR)
|
||||
value = ((value & 0b11111_00000u32) >> 5) | ((value & 0b00000_11111u32) << 5);
|
||||
}
|
||||
self.bits.map(|b| b.shift_value(value >> self.shift_left)).unwrap_or(0)
|
||||
pub fn shift_value(&self, value: u32) -> u32 {
|
||||
self.bits.as_ref().map(|b| b.shift_value(value >> self.shift_left)).unwrap_or(0)
|
||||
}
|
||||
|
||||
pub fn ident(&self) -> Ident {
|
||||
@ -226,6 +286,7 @@ pub fn modifiers_iter<'a>(
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
#[repr(transparent)]
|
||||
pub struct BitRange(pub (u8, u8));
|
||||
|
||||
impl BitRange {
|
||||
@ -235,42 +296,42 @@ impl BitRange {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn start(&self) -> u8 {
|
||||
pub fn start(self) -> u8 {
|
||||
self.0 .0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn end(&self) -> u8 {
|
||||
pub fn end(self) -> u8 {
|
||||
self.0 .1
|
||||
}
|
||||
|
||||
/// Calculate the mask from the range
|
||||
#[inline]
|
||||
pub fn mask(&self) -> u32 {
|
||||
pub fn mask(self) -> u32 {
|
||||
self.max_value() << self.shift()
|
||||
}
|
||||
|
||||
/// Number of bits to shift
|
||||
#[inline]
|
||||
pub fn shift(&self) -> u8 {
|
||||
pub fn shift(self) -> u8 {
|
||||
32 - self.end()
|
||||
}
|
||||
|
||||
/// Number of bits in the range
|
||||
#[inline]
|
||||
pub fn len(&self) -> u8 {
|
||||
pub fn len(self) -> u8 {
|
||||
self.end() - self.start()
|
||||
}
|
||||
|
||||
/// Shift and mask a value according to the range
|
||||
#[inline]
|
||||
pub fn shift_value(&self, value: u32) -> u32 {
|
||||
pub fn shift_value(self, value: u32) -> u32 {
|
||||
(value & self.max_value()) << self.shift()
|
||||
}
|
||||
|
||||
/// Calculate the maximum value that can be represented by the range
|
||||
#[inline]
|
||||
pub fn max_value(&self) -> u32 {
|
||||
pub fn max_value(self) -> u32 {
|
||||
(1 << self.len()) - 1
|
||||
}
|
||||
}
|
||||
@ -282,12 +343,12 @@ impl<'de> Deserialize<'de> for BitRange {
|
||||
{
|
||||
let range_str: String = Deserialize::deserialize(deserializer)?;
|
||||
if let Some((start_str, end_str)) = range_str.split_once("..") {
|
||||
let start = start_str.parse::<u8>().map_err(serde::de::Error::custom)?;
|
||||
let end = end_str.parse::<u8>().map_err(serde::de::Error::custom)?;
|
||||
let start = start_str.trim().parse::<u8>().map_err(serde::de::Error::custom)?;
|
||||
let end = end_str.trim().parse::<u8>().map_err(serde::de::Error::custom)?;
|
||||
Ok(Self::new(start, end))
|
||||
} else {
|
||||
let bit_idx = range_str.parse::<u8>().map_err(serde::de::Error::custom)?;
|
||||
Ok(Self::new(bit_idx, bit_idx))
|
||||
let bit_idx = range_str.trim().parse::<u8>().map_err(serde::de::Error::custom)?;
|
||||
Ok(Self::new(bit_idx, bit_idx + 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -297,14 +358,100 @@ impl Serialize for BitRange {
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
if self.start() == self.end() {
|
||||
self.start().serialize(serializer)
|
||||
if self.start() + 1 == self.end() {
|
||||
self.start().to_string().serialize(serializer)
|
||||
} else {
|
||||
format!("{}..{}", self.start(), self.end()).serialize(serializer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A collection of bit ranges, used to represent a (possibly non-contiguous) set of bits
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct SplitBitRange(pub Vec<BitRange>);
|
||||
|
||||
impl SplitBitRange {
|
||||
#[inline]
|
||||
pub fn end(&self) -> u8 {
|
||||
self.iter().map(BitRange::end).max().unwrap_or(0)
|
||||
}
|
||||
|
||||
/// Calculate the mask from the range
|
||||
#[inline]
|
||||
pub fn mask(&self) -> u32 {
|
||||
self.iter().map(BitRange::mask).fold(0, |acc, m| acc | m)
|
||||
}
|
||||
|
||||
/// Number of bits to shift
|
||||
#[inline]
|
||||
pub fn shift(&self) -> u8 {
|
||||
32 - self.end()
|
||||
}
|
||||
|
||||
/// Number of bits in the range
|
||||
#[inline]
|
||||
pub fn len(&self) -> u8 {
|
||||
self.iter().map(BitRange::len).sum()
|
||||
}
|
||||
|
||||
/// Shift and mask a value according to the range
|
||||
#[inline]
|
||||
pub fn shift_value(&self, mut value: u32) -> u32 {
|
||||
let mut result = 0;
|
||||
for range in self.iter().rev() {
|
||||
result |= range.shift_value(value);
|
||||
value >>= range.len();
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Calculate the maximum value that can be represented by the range
|
||||
#[inline]
|
||||
pub fn max_value(&self) -> u32 {
|
||||
(1 << self.len()) - 1
|
||||
}
|
||||
|
||||
/// Create an iterator over the contained bit ranges
|
||||
#[inline]
|
||||
pub fn iter(&self) -> impl DoubleEndedIterator<Item = BitRange> + use<'_> {
|
||||
self.0.iter().copied()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for SplitBitRange {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let ranges_str: String = Deserialize::deserialize(deserializer)?;
|
||||
let mut ranges = Vec::new();
|
||||
for range_str in ranges_str.split(',') {
|
||||
ranges.push(BitRange::deserialize(range_str.trim().into_deserializer())?);
|
||||
}
|
||||
Ok(Self(ranges))
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for SplitBitRange {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let mut ranges_str = String::new();
|
||||
for (i, range) in self.0.iter().enumerate() {
|
||||
if i > 0 {
|
||||
ranges_str.push(',');
|
||||
}
|
||||
if range.start() + 1 == range.end() {
|
||||
ranges_str.push_str(&range.start().to_string());
|
||||
} else {
|
||||
ranges_str.push_str(&format!("{}..{}", range.start(), range.end()));
|
||||
}
|
||||
}
|
||||
serializer.serialize_str(&ranges_str)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_ident(key: &str) -> String {
|
||||
key.to_ascii_lowercase().replace('.', "_").replace('+', "p").replace('-', "m")
|
||||
}
|
||||
@ -321,7 +468,7 @@ pub fn to_variant(key: &str) -> String {
|
||||
s.push(match c {
|
||||
'a'..='z' => c.to_ascii_uppercase(),
|
||||
'A'..='Z' => c,
|
||||
_ => panic!("invalid identifier: {}", key),
|
||||
_ => panic!("invalid identifier: {key}"),
|
||||
});
|
||||
loop {
|
||||
let c = match chars.next() {
|
||||
@ -335,7 +482,7 @@ pub fn to_variant(key: &str) -> String {
|
||||
s.push('_');
|
||||
break;
|
||||
}
|
||||
_ => panic!("invalid character in variant: {}", key),
|
||||
_ => panic!("invalid character in variant: {key}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -383,7 +530,7 @@ where
|
||||
T: std::fmt::LowerHex,
|
||||
{
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let s = format!("{:#x}", self);
|
||||
let s = format!("{self:#x}");
|
||||
tokens.extend(TokenStream::from_str(&s).unwrap());
|
||||
}
|
||||
}
|
||||
@ -410,7 +557,34 @@ where
|
||||
T: PrimInt + std::fmt::LowerHex,
|
||||
{
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let s = format!("{:#x}", self);
|
||||
let s = format!("{self:#x}");
|
||||
tokens.extend(TokenStream::from_str(&s).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_split_bit_range() {
|
||||
let range = SplitBitRange(vec![BitRange::new(26, 27), BitRange::new(21, 26)]);
|
||||
assert_eq!(range.mask(), 0b0000_0000_0000_0000_0000_0111_1110_0000);
|
||||
assert_eq!(range.shift(), 5);
|
||||
assert_eq!(range.len(), 6);
|
||||
assert_eq!(range.max_value(), 0x3F);
|
||||
assert_eq!(range.shift_value(u32::MAX), 0b0000_0000_0000_0000_0000_0111_1110_0000);
|
||||
assert_eq!(range.shift_value(0x1F), 0b0000_0000_0000_0000_0000_0111_1100_0000);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_split_bit_range_non_contiguous() {
|
||||
let range = SplitBitRange(vec![BitRange::new(30, 31), BitRange::new(16, 21)]);
|
||||
assert_eq!(range.mask(), 0b0000_0000_0000_0000_1111_1000_0000_0010);
|
||||
assert_eq!(range.shift(), 1);
|
||||
assert_eq!(range.len(), 6);
|
||||
assert_eq!(range.max_value(), 0x3F);
|
||||
assert_eq!(range.shift_value(u32::MAX), 0b0000_0000_0000_0000_1111_1000_0000_0010);
|
||||
assert_eq!(range.shift_value(0x1F), 0b0000_0000_0000_0000_1111_1000_0000_0000);
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ mod isa;
|
||||
|
||||
use crate::asm::gen_asm;
|
||||
use crate::disasm::gen_disasm;
|
||||
use anyhow::{ensure, Context, Result};
|
||||
use anyhow::{Context, Result};
|
||||
use condition::{parse_conditions, ConditionOp, ConditionValue};
|
||||
use isa::load_isa;
|
||||
use std::path::Path;
|
||||
@ -19,11 +19,10 @@ macro_rules! ident {
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
simple_logger::SimpleLogger::new().env().init().unwrap();
|
||||
simple_logger::SimpleLogger::new().env().init()?;
|
||||
|
||||
let isa = load_isa(Path::new("isa.yaml"))?;
|
||||
// Make sure we can fit the opcodes into a u8
|
||||
ensure!(isa.opcodes.len() <= 255);
|
||||
log::info!("Opcode count: {}", isa.opcodes.len());
|
||||
|
||||
// Sanity check the opcodes and mnemonics
|
||||
// Calculate the bitmask for each opcode and compare it to the stored bitmask
|
||||
|
Loading…
x
Reference in New Issue
Block a user