├── .gitignore ├── .gitmodules ├── .vscode └── settings.json ├── Cargo.lock ├── Cargo.toml ├── PORTING.md ├── README.md ├── benches └── x86.rs ├── examples ├── factorial.rs └── reloc.rs ├── meta ├── __pycache__ │ ├── constants.cpython-312.pyc │ └── rustbuilder.cpython-312.pyc ├── arm.py ├── armdesc.txt ├── armfeat.txt ├── constants.py ├── ppc.py ├── ppc.txt ├── riscv.py ├── rustbuilder.py ├── x86.py └── x86.txt └── src ├── aarch64 ├── assembler.rs ├── classifier.rs ├── decoder.rs ├── emitter.rs ├── inst_info.rs ├── mod.rs └── opcodes.rs ├── core ├── arch_traits.rs ├── buffer.rs ├── emitter.rs ├── formatter.rs ├── globals.rs ├── jit_allocator.rs ├── mod.rs ├── operand.rs ├── sink.rs ├── support.rs ├── target.rs └── types.rs ├── lib.rs ├── main.rs ├── ppc ├── mod.rs └── opc.c ├── riscv ├── assembler.rs ├── decode.rs ├── emitter.rs ├── formatter.rs ├── mod.rs ├── opcodes.rs └── operands.rs ├── util ├── mod.rs ├── os.rs └── virtual_memory.rs └── x86 ├── arch_traits.rs ├── assembler.rs ├── decode.rs ├── decode_tab.rs ├── emitter.rs ├── formatter.rs ├── macroassembler.rs ├── mod.rs ├── opcodes.rs └── operands.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playX18/asmkit/d46946af9eec2e3e3a70391b76cd296691f3ae26/.gitmodules -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "compare": "cpp", 4 | "cstdint": "cpp", 5 | "netfwd": "cpp", 6 | "format": "cpp" 7 | } 8 | } -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.1.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "anes" 16 | version = "0.1.6" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" 19 | 20 | [[package]] 21 | name = "anstyle" 22 | version = "1.0.10" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" 25 | 26 | [[package]] 27 | name = "anyhow" 28 | version = "1.0.91" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8" 31 | 32 | [[package]] 33 | name = "asmkit-rs" 34 | version = "0.1.0" 35 | dependencies = [ 36 | "cfgenius", 37 | "criterion", 38 | "derive_more", 39 | "errno", 40 | "iced-x86", 41 | "intrusive-collections", 42 | "libc", 43 | "num-traits", 44 | "paste", 45 | "smallvec", 46 | "wasmtime-jit-icache-coherence", 47 | ] 48 | 49 | [[package]] 50 | name = "autocfg" 51 | version = "1.4.0" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 54 | 55 | [[package]] 56 | name = "bumpalo" 57 | version = "3.16.0" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 60 | 61 | [[package]] 62 | name = "cast" 63 | version = "0.3.0" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" 66 | 67 | [[package]] 68 | name = "cfg-if" 69 | version = "1.0.0" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 72 | 73 | [[package]] 74 | name = "cfgenius" 75 | version = "0.1.1" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "4ae118db0db0e2137671e9b916de8af5d3be65a5fb1ad3a9c7925c38b6644cfb" 78 | 79 | [[package]] 80 | name = "ciborium" 81 | version = "0.2.2" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" 84 | dependencies = [ 85 | "ciborium-io", 86 | "ciborium-ll", 87 | "serde", 88 | ] 89 | 90 | [[package]] 91 | name = "ciborium-io" 92 | version = "0.2.2" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" 95 | 96 | [[package]] 97 | name = "ciborium-ll" 98 | version = "0.2.2" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" 101 | dependencies = [ 102 | "ciborium-io", 103 | "half", 104 | ] 105 | 106 | [[package]] 107 | name = "clap" 108 | version = "4.5.21" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" 111 | dependencies = [ 112 | "clap_builder", 113 | ] 114 | 115 | [[package]] 116 | name = "clap_builder" 117 | version = "4.5.21" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" 120 | dependencies = [ 121 | "anstyle", 122 | "clap_lex", 123 | ] 124 | 125 | [[package]] 126 | name = "clap_lex" 127 | version = "0.7.3" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" 130 | 131 | [[package]] 132 | name = "criterion" 133 | version = "0.5.1" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" 136 | dependencies = [ 137 | "anes", 138 | "cast", 139 | "ciborium", 140 | "clap", 141 | "criterion-plot", 142 | "is-terminal", 143 | "itertools", 144 | "num-traits", 145 | "once_cell", 146 | "oorandom", 147 | "plotters", 148 | "rayon", 149 | "regex", 150 | "serde", 151 | "serde_derive", 152 | "serde_json", 153 | "tinytemplate", 154 | "walkdir", 155 | ] 156 | 157 | [[package]] 158 | name = "criterion-plot" 159 | version = "0.5.0" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" 162 | dependencies = [ 163 | "cast", 164 | "itertools", 165 | ] 166 | 167 | [[package]] 168 | name = "crossbeam-deque" 169 | version = "0.8.5" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" 172 | dependencies = [ 173 | "crossbeam-epoch", 174 | "crossbeam-utils", 175 | ] 176 | 177 | [[package]] 178 | name = "crossbeam-epoch" 179 | version = "0.9.18" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" 182 | dependencies = [ 183 | "crossbeam-utils", 184 | ] 185 | 186 | [[package]] 187 | name = "crossbeam-utils" 188 | version = "0.8.20" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" 191 | 192 | [[package]] 193 | name = "crunchy" 194 | version = "0.2.2" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 197 | 198 | [[package]] 199 | name = "derive_more" 200 | version = "1.0.0" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" 203 | dependencies = [ 204 | "derive_more-impl", 205 | ] 206 | 207 | [[package]] 208 | name = "derive_more-impl" 209 | version = "1.0.0" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" 212 | dependencies = [ 213 | "proc-macro2", 214 | "quote", 215 | "syn", 216 | ] 217 | 218 | [[package]] 219 | name = "either" 220 | version = "1.13.0" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" 223 | 224 | [[package]] 225 | name = "errno" 226 | version = "0.3.9" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" 229 | dependencies = [ 230 | "libc", 231 | "windows-sys 0.52.0", 232 | ] 233 | 234 | [[package]] 235 | name = "half" 236 | version = "2.4.1" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" 239 | dependencies = [ 240 | "cfg-if", 241 | "crunchy", 242 | ] 243 | 244 | [[package]] 245 | name = "hermit-abi" 246 | version = "0.4.0" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" 249 | 250 | [[package]] 251 | name = "iced-x86" 252 | version = "1.21.0" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | checksum = "7c447cff8c7f384a7d4f741cfcff32f75f3ad02b406432e8d6c878d56b1edf6b" 255 | dependencies = [ 256 | "lazy_static", 257 | ] 258 | 259 | [[package]] 260 | name = "intrusive-collections" 261 | version = "0.9.7" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "189d0897e4cbe8c75efedf3502c18c887b05046e59d28404d4d8e46cbc4d1e86" 264 | dependencies = [ 265 | "memoffset", 266 | ] 267 | 268 | [[package]] 269 | name = "is-terminal" 270 | version = "0.4.13" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" 273 | dependencies = [ 274 | "hermit-abi", 275 | "libc", 276 | "windows-sys 0.52.0", 277 | ] 278 | 279 | [[package]] 280 | name = "itertools" 281 | version = "0.10.5" 282 | source = "registry+https://github.com/rust-lang/crates.io-index" 283 | checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" 284 | dependencies = [ 285 | "either", 286 | ] 287 | 288 | [[package]] 289 | name = "itoa" 290 | version = "1.0.11" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 293 | 294 | [[package]] 295 | name = "js-sys" 296 | version = "0.3.72" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" 299 | dependencies = [ 300 | "wasm-bindgen", 301 | ] 302 | 303 | [[package]] 304 | name = "lazy_static" 305 | version = "1.5.0" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 308 | 309 | [[package]] 310 | name = "libc" 311 | version = "0.2.162" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" 314 | 315 | [[package]] 316 | name = "log" 317 | version = "0.4.22" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 320 | 321 | [[package]] 322 | name = "memchr" 323 | version = "2.7.4" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 326 | 327 | [[package]] 328 | name = "memoffset" 329 | version = "0.9.1" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" 332 | dependencies = [ 333 | "autocfg", 334 | ] 335 | 336 | [[package]] 337 | name = "num-traits" 338 | version = "0.2.19" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 341 | dependencies = [ 342 | "autocfg", 343 | ] 344 | 345 | [[package]] 346 | name = "once_cell" 347 | version = "1.20.2" 348 | source = "registry+https://github.com/rust-lang/crates.io-index" 349 | checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" 350 | 351 | [[package]] 352 | name = "oorandom" 353 | version = "11.1.4" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" 356 | 357 | [[package]] 358 | name = "paste" 359 | version = "1.0.15" 360 | source = "registry+https://github.com/rust-lang/crates.io-index" 361 | checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" 362 | 363 | [[package]] 364 | name = "plotters" 365 | version = "0.3.7" 366 | source = "registry+https://github.com/rust-lang/crates.io-index" 367 | checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" 368 | dependencies = [ 369 | "num-traits", 370 | "plotters-backend", 371 | "plotters-svg", 372 | "wasm-bindgen", 373 | "web-sys", 374 | ] 375 | 376 | [[package]] 377 | name = "plotters-backend" 378 | version = "0.3.7" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" 381 | 382 | [[package]] 383 | name = "plotters-svg" 384 | version = "0.3.7" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" 387 | dependencies = [ 388 | "plotters-backend", 389 | ] 390 | 391 | [[package]] 392 | name = "proc-macro2" 393 | version = "1.0.89" 394 | source = "registry+https://github.com/rust-lang/crates.io-index" 395 | checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" 396 | dependencies = [ 397 | "unicode-ident", 398 | ] 399 | 400 | [[package]] 401 | name = "quote" 402 | version = "1.0.37" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 405 | dependencies = [ 406 | "proc-macro2", 407 | ] 408 | 409 | [[package]] 410 | name = "rayon" 411 | version = "1.10.0" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" 414 | dependencies = [ 415 | "either", 416 | "rayon-core", 417 | ] 418 | 419 | [[package]] 420 | name = "rayon-core" 421 | version = "1.12.1" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" 424 | dependencies = [ 425 | "crossbeam-deque", 426 | "crossbeam-utils", 427 | ] 428 | 429 | [[package]] 430 | name = "regex" 431 | version = "1.11.1" 432 | source = "registry+https://github.com/rust-lang/crates.io-index" 433 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 434 | dependencies = [ 435 | "aho-corasick", 436 | "memchr", 437 | "regex-automata", 438 | "regex-syntax", 439 | ] 440 | 441 | [[package]] 442 | name = "regex-automata" 443 | version = "0.4.9" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 446 | dependencies = [ 447 | "aho-corasick", 448 | "memchr", 449 | "regex-syntax", 450 | ] 451 | 452 | [[package]] 453 | name = "regex-syntax" 454 | version = "0.8.5" 455 | source = "registry+https://github.com/rust-lang/crates.io-index" 456 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 457 | 458 | [[package]] 459 | name = "ryu" 460 | version = "1.0.18" 461 | source = "registry+https://github.com/rust-lang/crates.io-index" 462 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 463 | 464 | [[package]] 465 | name = "same-file" 466 | version = "1.0.6" 467 | source = "registry+https://github.com/rust-lang/crates.io-index" 468 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 469 | dependencies = [ 470 | "winapi-util", 471 | ] 472 | 473 | [[package]] 474 | name = "serde" 475 | version = "1.0.215" 476 | source = "registry+https://github.com/rust-lang/crates.io-index" 477 | checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" 478 | dependencies = [ 479 | "serde_derive", 480 | ] 481 | 482 | [[package]] 483 | name = "serde_derive" 484 | version = "1.0.215" 485 | source = "registry+https://github.com/rust-lang/crates.io-index" 486 | checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" 487 | dependencies = [ 488 | "proc-macro2", 489 | "quote", 490 | "syn", 491 | ] 492 | 493 | [[package]] 494 | name = "serde_json" 495 | version = "1.0.132" 496 | source = "registry+https://github.com/rust-lang/crates.io-index" 497 | checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" 498 | dependencies = [ 499 | "itoa", 500 | "memchr", 501 | "ryu", 502 | "serde", 503 | ] 504 | 505 | [[package]] 506 | name = "smallvec" 507 | version = "1.13.2" 508 | source = "registry+https://github.com/rust-lang/crates.io-index" 509 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 510 | 511 | [[package]] 512 | name = "syn" 513 | version = "2.0.87" 514 | source = "registry+https://github.com/rust-lang/crates.io-index" 515 | checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" 516 | dependencies = [ 517 | "proc-macro2", 518 | "quote", 519 | "unicode-ident", 520 | ] 521 | 522 | [[package]] 523 | name = "tinytemplate" 524 | version = "1.2.1" 525 | source = "registry+https://github.com/rust-lang/crates.io-index" 526 | checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" 527 | dependencies = [ 528 | "serde", 529 | "serde_json", 530 | ] 531 | 532 | [[package]] 533 | name = "unicode-ident" 534 | version = "1.0.13" 535 | source = "registry+https://github.com/rust-lang/crates.io-index" 536 | checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" 537 | 538 | [[package]] 539 | name = "walkdir" 540 | version = "2.5.0" 541 | source = "registry+https://github.com/rust-lang/crates.io-index" 542 | checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 543 | dependencies = [ 544 | "same-file", 545 | "winapi-util", 546 | ] 547 | 548 | [[package]] 549 | name = "wasm-bindgen" 550 | version = "0.2.95" 551 | source = "registry+https://github.com/rust-lang/crates.io-index" 552 | checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" 553 | dependencies = [ 554 | "cfg-if", 555 | "once_cell", 556 | "wasm-bindgen-macro", 557 | ] 558 | 559 | [[package]] 560 | name = "wasm-bindgen-backend" 561 | version = "0.2.95" 562 | source = "registry+https://github.com/rust-lang/crates.io-index" 563 | checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" 564 | dependencies = [ 565 | "bumpalo", 566 | "log", 567 | "once_cell", 568 | "proc-macro2", 569 | "quote", 570 | "syn", 571 | "wasm-bindgen-shared", 572 | ] 573 | 574 | [[package]] 575 | name = "wasm-bindgen-macro" 576 | version = "0.2.95" 577 | source = "registry+https://github.com/rust-lang/crates.io-index" 578 | checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" 579 | dependencies = [ 580 | "quote", 581 | "wasm-bindgen-macro-support", 582 | ] 583 | 584 | [[package]] 585 | name = "wasm-bindgen-macro-support" 586 | version = "0.2.95" 587 | source = "registry+https://github.com/rust-lang/crates.io-index" 588 | checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" 589 | dependencies = [ 590 | "proc-macro2", 591 | "quote", 592 | "syn", 593 | "wasm-bindgen-backend", 594 | "wasm-bindgen-shared", 595 | ] 596 | 597 | [[package]] 598 | name = "wasm-bindgen-shared" 599 | version = "0.2.95" 600 | source = "registry+https://github.com/rust-lang/crates.io-index" 601 | checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" 602 | 603 | [[package]] 604 | name = "wasmtime-jit-icache-coherence" 605 | version = "26.0.1" 606 | source = "registry+https://github.com/rust-lang/crates.io-index" 607 | checksum = "da47fba49af72581bc0dc67c8faaf5ee550e6f106e285122a184a675193701a5" 608 | dependencies = [ 609 | "anyhow", 610 | "cfg-if", 611 | "libc", 612 | "windows-sys 0.59.0", 613 | ] 614 | 615 | [[package]] 616 | name = "web-sys" 617 | version = "0.3.72" 618 | source = "registry+https://github.com/rust-lang/crates.io-index" 619 | checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" 620 | dependencies = [ 621 | "js-sys", 622 | "wasm-bindgen", 623 | ] 624 | 625 | [[package]] 626 | name = "winapi-util" 627 | version = "0.1.9" 628 | source = "registry+https://github.com/rust-lang/crates.io-index" 629 | checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 630 | dependencies = [ 631 | "windows-sys 0.59.0", 632 | ] 633 | 634 | [[package]] 635 | name = "windows-sys" 636 | version = "0.52.0" 637 | source = "registry+https://github.com/rust-lang/crates.io-index" 638 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 639 | dependencies = [ 640 | "windows-targets", 641 | ] 642 | 643 | [[package]] 644 | name = "windows-sys" 645 | version = "0.59.0" 646 | source = "registry+https://github.com/rust-lang/crates.io-index" 647 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 648 | dependencies = [ 649 | "windows-targets", 650 | ] 651 | 652 | [[package]] 653 | name = "windows-targets" 654 | version = "0.52.6" 655 | source = "registry+https://github.com/rust-lang/crates.io-index" 656 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 657 | dependencies = [ 658 | "windows_aarch64_gnullvm", 659 | "windows_aarch64_msvc", 660 | "windows_i686_gnu", 661 | "windows_i686_gnullvm", 662 | "windows_i686_msvc", 663 | "windows_x86_64_gnu", 664 | "windows_x86_64_gnullvm", 665 | "windows_x86_64_msvc", 666 | ] 667 | 668 | [[package]] 669 | name = "windows_aarch64_gnullvm" 670 | version = "0.52.6" 671 | source = "registry+https://github.com/rust-lang/crates.io-index" 672 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 673 | 674 | [[package]] 675 | name = "windows_aarch64_msvc" 676 | version = "0.52.6" 677 | source = "registry+https://github.com/rust-lang/crates.io-index" 678 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 679 | 680 | [[package]] 681 | name = "windows_i686_gnu" 682 | version = "0.52.6" 683 | source = "registry+https://github.com/rust-lang/crates.io-index" 684 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 685 | 686 | [[package]] 687 | name = "windows_i686_gnullvm" 688 | version = "0.52.6" 689 | source = "registry+https://github.com/rust-lang/crates.io-index" 690 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 691 | 692 | [[package]] 693 | name = "windows_i686_msvc" 694 | version = "0.52.6" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 697 | 698 | [[package]] 699 | name = "windows_x86_64_gnu" 700 | version = "0.52.6" 701 | source = "registry+https://github.com/rust-lang/crates.io-index" 702 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 703 | 704 | [[package]] 705 | name = "windows_x86_64_gnullvm" 706 | version = "0.52.6" 707 | source = "registry+https://github.com/rust-lang/crates.io-index" 708 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 709 | 710 | [[package]] 711 | name = "windows_x86_64_msvc" 712 | version = "0.52.6" 713 | source = "registry+https://github.com/rust-lang/crates.io-index" 714 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 715 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "asmkit-rs" 3 | version = "0.1.0" 4 | edition = "2021" 5 | license = "MIT OR Apache-2.0" 6 | readme = "README.md" 7 | description = "Portable assembler toolkit: decoding and encoding of various architectures" 8 | repository = "https://github.com/playx18/asmkit" 9 | keywords = ["assembler", "asm", "jit", "riscv", "x86"] 10 | 11 | [lib] 12 | name = "asmkit" 13 | 14 | [dependencies] 15 | smallvec = "1.13" 16 | libc = { version = "0.2.162", optional = true } 17 | cfgenius = "0.1" 18 | intrusive-collections = "0.9" 19 | errno = { version = "0.3.9", optional = true } 20 | paste = "1.0" 21 | num-traits = "0.2.19" 22 | derive_more = { version = "1.0.0", features = [ 23 | "deref", 24 | "deref_mut", 25 | "try_from", 26 | "add", 27 | ] } 28 | 29 | 30 | [target.'cfg(target_arch="riscv64")'.dependencies] 31 | wasmtime-jit-icache-coherence = { version = "26.0.1", optional = true } 32 | [features] 33 | default = ["jit", "riscv", "x86"] 34 | x86 = [] 35 | riscv = [] 36 | jit = ["wasmtime-jit-icache-coherence", "libc", "errno"] 37 | 38 | 39 | [dev-dependencies] 40 | criterion = { version = "0.5", features = ["html_reports"] } 41 | iced-x86 = { version = "1.21", features = ["code_asm"] } 42 | 43 | 44 | [[bench]] 45 | name = "x86" 46 | harness = false 47 | 48 | 49 | 50 | [profile.release] 51 | panic = "abort" 52 | -------------------------------------------------------------------------------- /PORTING.md: -------------------------------------------------------------------------------- 1 | # Porting 2 | 3 | Porting asmkit to other architectures requires providing opcode table and a code generator which would generate 4 | encoder and decoder from such opcode-table. You can look at example tables in `meta/` directory. For RISC-V we use `riscv-opcodes` 5 | repo directly and it is cloned if we want to rebuild encodings. 6 | 7 | Code-generator can be written in any language you like. Python is used because of simplicity by default. 8 | 9 | ## Code generator 10 | 11 | The job of codegen is to generate two parts of asmkit port: encoder and decoder. Encoder is used to generate assembler code 12 | while decoder is used to disassemble code. Decoder is optional part while encoder is a must have. To make life simpler you should 13 | consider emitting `EmitterExplicit` directly which then ivnokes some internal functionality to that backend to actually do 14 | encoding. 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # asmkit 2 | 3 | `#![no_std]` assembler library. 4 | 5 | # Features 6 | - X64, RISC-V, PPC, and ARM (WIP) assemblers 7 | - Small and portable library. 8 | - Tiny amount of dependencies: 9 | - `libc`, `intrusive-collections`: for JIT support 10 | - `paste`, `derive-more`: makes our life simpler when declaring arch-specific stuff over and over 11 | - `smallvec`: for code generation to not heap allocate often 12 | - Relocations are provided by CodeBuffer interface and assembler will use them if you use symbols in API. 13 | 14 | # Goals 15 | - Auto-generated assemblers for as many as possible platform. 16 | - Portability: library should built & run on any platform (even if it does not provide assembler for one), and assemblers on its own 17 | must not be dependent on platform we built `asmkit` on. 18 | 19 | 20 | 21 | # TODO 22 | - [ ] Add support for ARM64 23 | - [ ] Add support for PPC64 24 | - [ ] Add support for OpenPOWER (POWER9/POWER10) 25 | - [ ] Add support for RW info and implicit operand info for all opcodes 26 | - [ ] Cross-platform helpers to perform calls 27 | - [ ] JSC/SpiderMonkey-like `MacroAssembler` to help generate assembly without worrying about target architecture 28 | - [ ] Compiler/Builder interface: emit instructions as `Inst` structure and allow modifying them before emitting, 29 | and also possibly to have regalloc pass over them. 30 | 31 | # Related projects 32 | 33 | - [AsmJit](https://github.com/asmjit/asmjit): Core API, JIT API, and operands are ported from AsmJit, 34 | the overall idea of making portable assembler in Rust comes from AsmJit 35 | - [fadec](https://github.com/aengelke/fadec): x86/64 encoding/decoding library. We use opcode tables provided by fadec 36 | for x86/64 support and `encode.c` is partially used for emitting code. 37 | - [disarm](https://github.com/aengelke/disarm): AARch64 encoding/decoding library. We use opcode tables from disarm 38 | to generate AArch64 encodigns 39 | - [riscv-opcodes](https://github.com/riscv/riscv-opcodes): Opcode table for RISC-V, used to generate RISC-V assembler/disassembler. 40 | - [GDB](https://sourceware.org/gdb/): GDB is a debugger but we use its `ppc-opc.c` as an opcode table for PowerPC support. -------------------------------------------------------------------------------- /benches/x86.rs: -------------------------------------------------------------------------------- 1 | use std::hint::black_box; 2 | use asmkit::core::buffer::CodeBuffer; 3 | use criterion::{criterion_group, criterion_main, Criterion}; 4 | 5 | 6 | fn emit_factorial_benchmark(c: &mut Criterion) { 7 | 8 | c.bench_function("asmkit", |b| b.iter_with_large_drop(|| { 9 | use asmkit::x86::*; 10 | let mut buf = CodeBuffer::new(); 11 | let mut asm = Assembler::new(&mut buf); 12 | 13 | let label = asm.get_label(); 14 | let fac = asm.get_label(); 15 | 16 | asm.bind_label(fac); 17 | asm.mov64ri(RAX, imm(1)); 18 | asm.test64rr(RDI, RDI); 19 | asm.jnz(label); 20 | asm.ret(); 21 | 22 | { 23 | asm.bind_label(label); 24 | asm.pushr(RBX); 25 | asm.mov64rr(RBX, RDI); 26 | asm.lea64rm(RDI, ptr64(RDI, -1)); 27 | asm.call(fac); 28 | asm.mov64rr(RDX, RAX); 29 | asm.mov64rr(RAX, RBX); 30 | asm.imul64rr(RAX, RDX); 31 | asm.popr(RBX); 32 | asm.ret(); 33 | } 34 | 35 | let result = buf.finish(); 36 | 37 | result 38 | })); 39 | 40 | 41 | } 42 | 43 | criterion_group!(benches, emit_factorial_benchmark); 44 | criterion_main!(benches); -------------------------------------------------------------------------------- /examples/factorial.rs: -------------------------------------------------------------------------------- 1 | 2 | use asmkit::core::jit_allocator::{JitAllocator, JitAllocatorOptions}; 3 | 4 | fn main() { 5 | { 6 | use asmkit::core::buffer::CodeBuffer; 7 | use asmkit::x86::*; 8 | use formatter::pretty_disassembler; 9 | let mut buf = CodeBuffer::new(); 10 | let mut asm = Assembler::new(&mut buf); 11 | 12 | let label = asm.get_label(); 13 | let fac = asm.get_label(); 14 | 15 | asm.bind_label(fac); 16 | asm.mov64ri(RAX, imm(1)); 17 | asm.test64rr(RDI, RDI); 18 | asm.jnz(label); 19 | asm.ret(); 20 | 21 | { 22 | asm.bind_label(label); 23 | asm.pushr(RBX); 24 | asm.mov64rr(RBX, RDI); 25 | asm.lea64rm(RDI, ptr64(RDI, -1)); 26 | asm.call(fac); 27 | asm.mov64rr(RDX, RAX); 28 | asm.mov64rr(RAX, RBX); 29 | asm.imul64rr(RAX, RDX); 30 | asm.popr(RBX); 31 | asm.ret(); 32 | } 33 | 34 | let result = buf.finish(); 35 | 36 | let mut jit = JitAllocator::new(JitAllocatorOptions::default()); 37 | 38 | let mut span = jit 39 | .alloc(result.data().len()) 40 | .expect("failed to allocate code"); 41 | unsafe { 42 | jit.write(&mut span, |span| { 43 | span.rw() 44 | .copy_from_nonoverlapping(result.data().as_ptr(), result.data().len()); 45 | }) 46 | .unwrap(); 47 | let mut out = String::new(); 48 | pretty_disassembler(&mut out, 64, result.data(), span.rx() as _).unwrap(); 49 | println!("{}", out); 50 | #[cfg(target_arch = "x86_64")] 51 | { 52 | let f: extern "C" fn(u64) -> u64 = std::mem::transmute(span.rx()); 53 | 54 | println!("X86 factorial(5) = {:?}", f(5)); 55 | } 56 | } 57 | } 58 | 59 | { 60 | use asmkit::core::buffer::CodeBuffer; 61 | use asmkit::riscv::*; 62 | use formatter::pretty_disassembler; 63 | let mut buf = CodeBuffer::new(); 64 | let mut asm = Assembler::new(&mut buf); 65 | 66 | let label = asm.get_label(); 67 | let fac = asm.get_label(); 68 | asm.bind_label(fac); 69 | asm.bnez(A0, label); 70 | asm.addi(A0, ZERO, imm(1)); 71 | asm.ret(); 72 | { 73 | asm.bind_label(label); 74 | asm.addi(SP, SP, imm(-16)); 75 | asm.sd(SP, RA, imm(8)); 76 | asm.sd(SP, S0, imm(0)); 77 | asm.mv(S0, A0); 78 | asm.addi(A0, A0, imm(-1)); 79 | 80 | asm.call(fac); 81 | asm.mul(A0, S0, A0); 82 | asm.ld(RA, SP, imm(8)); 83 | asm.ld(S0, SP, imm(0)); 84 | asm.addi(SP, SP, imm(16)); 85 | asm.ret(); 86 | } 87 | 88 | let result = buf.finish(); 89 | 90 | let mut jit = JitAllocator::new(JitAllocatorOptions::default()); 91 | 92 | let mut span = jit 93 | .alloc(result.data().len()) 94 | .expect("failed to allocate code"); 95 | unsafe { 96 | jit.write(&mut span, |span| { 97 | span.rw() 98 | .copy_from_nonoverlapping(result.data().as_ptr(), result.data().len()); 99 | }) 100 | .unwrap(); 101 | 102 | 103 | let mut out = String::new(); 104 | pretty_disassembler(&mut out, 64, result.data(), span.rx() as _).unwrap(); 105 | println!("{}", out); 106 | #[cfg(target_arch = "riscv64")] 107 | { 108 | let f: extern "C" fn(u64) -> u64 = std::mem::transmute(span.rx()); 109 | 110 | println!("RV64 factorial(5) = {:?}", f(5)); 111 | } 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /examples/reloc.rs: -------------------------------------------------------------------------------- 1 | use asmkit::core::buffer::{perform_relocations, CodeBuffer, ExternalName, RelocDistance}; 2 | use asmkit::core::jit_allocator::JitAllocator; 3 | 4 | extern "C" { 5 | fn puts(_: *const i8); 6 | } 7 | 8 | fn main() { 9 | { 10 | use asmkit::x86::*; 11 | use formatter::pretty_disassembler; 12 | 13 | let mut buf = CodeBuffer::new(); 14 | let mut asm = Assembler::new(&mut buf); 15 | 16 | let str_constant = asm.add_constant("Hello, World!\0"); 17 | let puts_sym = asm 18 | .buffer 19 | .add_symbol(ExternalName::Symbol("puts".into()), RelocDistance::Far); 20 | 21 | asm.lea64rm(RDI, ptr64_label(str_constant, 0)); 22 | asm.callm(ptr64_sym(puts_sym, 0)); 23 | asm.ret(); 24 | 25 | let result = buf.finish(); 26 | 27 | for reloc in result.relocs() { 28 | println!("{:?}", reloc); 29 | } 30 | 31 | let mut jit = JitAllocator::new(Default::default()); 32 | 33 | // allocate memory for GOT table and for code itself 34 | let mut span = jit 35 | .alloc(result.data().len() + result.relocs().len() * 8) 36 | .unwrap(); 37 | 38 | let mut got_addr_rx = std::ptr::null(); 39 | 40 | unsafe { 41 | jit.write(&mut span, |span| { 42 | span.rw() 43 | .copy_from_nonoverlapping(result.data().as_ptr(), result.data().len()); 44 | got_addr_rx = span.rx().add(result.data().len()); 45 | span.rw() 46 | .add(result.data().len()) 47 | .cast::() 48 | .write(puts as *const u8 as usize); 49 | // we only link to one symbol in GOT table, don't bother with anything else... 50 | perform_relocations( 51 | span.rw(), 52 | span.rx(), 53 | &result.relocs(), 54 | |_| unreachable!(), 55 | |_| got_addr_rx, 56 | |_| unreachable!(), 57 | ); 58 | }) 59 | .unwrap(); 60 | 61 | let mut out = String::new(); 62 | pretty_disassembler( 63 | &mut out, 64 | 64, 65 | std::slice::from_raw_parts(span.rx(), result.data().len()), 66 | span.rx() as _, 67 | ) 68 | .unwrap(); 69 | 70 | println!("{}", out); 71 | #[cfg(target_arch = "x86_64")] 72 | { 73 | let f: extern "C" fn() = std::mem::transmute(span.rx()); 74 | 75 | f(); 76 | } 77 | } 78 | } 79 | 80 | { 81 | use asmkit::riscv::*; 82 | use formatter::pretty_disassembler; 83 | 84 | let mut buf = CodeBuffer::new(); 85 | buf.env_mut().set_pic(false); 86 | let mut asm = Assembler::new(&mut buf); 87 | 88 | let str_constant = asm.add_constant("Hello, World!\0"); 89 | let puts_sym = asm 90 | .buffer 91 | .add_symbol(ExternalName::Symbol("puts".into()), RelocDistance::Far); 92 | asm.addi(SP, SP, imm(-16)); 93 | asm.sd(SP, RA, imm(8)); 94 | asm.sd(SP, S0, imm(0)); 95 | asm.la(A0, str_constant); 96 | asm.la(A1, puts_sym); 97 | asm.call(A1); 98 | asm.ld(RA, SP, imm(8)); 99 | asm.ld(S0, SP, imm(0)); 100 | asm.addi(SP, SP, imm(16)); 101 | asm.ret(); 102 | 103 | let result = buf.finish(); 104 | 105 | for reloc in result.relocs() { 106 | println!("{:?}", reloc); 107 | } 108 | 109 | let mut jit = JitAllocator::new(Default::default()); 110 | 111 | // allocate memory for GOT table and for code itself 112 | let mut span = jit 113 | .alloc(result.data().len() + result.relocs().len() * 8) 114 | .unwrap(); 115 | 116 | let mut got_addr_rx = std::ptr::null(); 117 | 118 | unsafe { 119 | jit.write(&mut span, |span| { 120 | span.rw() 121 | .copy_from_nonoverlapping(result.data().as_ptr(), result.data().len()); 122 | got_addr_rx = span.rx().add(result.data().len()); 123 | span.rw() 124 | .add(result.data().len()) 125 | .cast::() 126 | .write(puts as *const u8 as usize); 127 | // we only link to one symbol in GOT table, don't bother with anything else... 128 | perform_relocations( 129 | span.rw(), 130 | span.rx(), 131 | &result.relocs(), 132 | |_| puts as *const u8, 133 | |_| got_addr_rx, 134 | |_| unreachable!(), 135 | ); 136 | }) 137 | .unwrap(); 138 | 139 | let mut out = String::new(); 140 | pretty_disassembler( 141 | &mut out, 142 | 64, 143 | std::slice::from_raw_parts(span.rx(), result.data().len()), 144 | span.rx() as _, 145 | ) 146 | .unwrap(); 147 | 148 | println!("{}", out); 149 | 150 | 151 | #[cfg(target_arch = "riscv64")] 152 | { 153 | let f: extern "C" fn()= std::mem::transmute(span.rx()); 154 | 155 | f(); 156 | } 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /meta/__pycache__/constants.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playX18/asmkit/d46946af9eec2e3e3a70391b76cd296691f3ae26/meta/__pycache__/constants.cpython-312.pyc -------------------------------------------------------------------------------- /meta/__pycache__/rustbuilder.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playX18/asmkit/d46946af9eec2e3e3a70391b76cd296691f3ae26/meta/__pycache__/rustbuilder.cpython-312.pyc -------------------------------------------------------------------------------- /meta/armfeat.txt: -------------------------------------------------------------------------------- 1 | 2 | # Copyright (c) 2024, Alexis Engelke 3 | # All rights reserved. 4 | # 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions are met: 7 | # 8 | # 1. Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 11 | # 2. Redistributions in binary form must reproduce the above copyright notice, 12 | # this list of conditions and the following disclaimer in the documentation 13 | # and/or other materials provided with the distribution. 14 | # 15 | # 3. Neither the name of the copyright holder nor the names of its contributors 16 | # may be used to endorse or promote products derived from this software 17 | # without specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | # POSSIBILITY OF SUCH DAMAGE. 30 | 31 | # Features introduced in Armv8.0 32 | [AES] 33 | armname: FEAT_AES 34 | llvmname: aes 35 | availopt: armv8.0a 36 | [CRC32] 37 | armname: FEAT_CRC32 38 | llvmname: crc 39 | availopt: armv8.0a 40 | avail: armv8.1a 41 | [EL3] 42 | armname: FEAT_EL3 43 | llvmname: el3 44 | availopt: armv8.0a 45 | [PMULL] 46 | armname: FEAT_PMULL 47 | llvmname: aes 48 | availopt: armv8.0a 49 | requires: AES 50 | [SHA1] 51 | armname: FEAT_SHA1 52 | llvmname: sha2 53 | availopt: armv8.0a 54 | [SHA256] 55 | armname: FEAT_SHA256 56 | llvmname: sha2 57 | availopt: armv8.0a 58 | requires: SHA1 59 | 60 | # Features introduced in Armv8.1 61 | [LOR] 62 | armname: FEAT_LOR 63 | llvmname: lor 64 | availopt: armv8.0a 65 | avail: armv8.1a 66 | [LSE] 67 | armname: FEAT_LSE 68 | llvmname: lse 69 | availopt: armv8.0a 70 | avail: armv8.1a 71 | [PAN] 72 | armname: FEAT_PAN 73 | llvmname: pan 74 | availopt: armv8.0a 75 | avail: armv8.1a 76 | [RDM] 77 | armname: FEAT_RDM 78 | llvmname: rdm 79 | availopt: armv8.0a 80 | avail: armv8.1a 81 | 82 | # Features introduced in Armv8.2 83 | [DPB] 84 | armname: FEAT_DPB 85 | llvmname: ccpp 86 | availopt: armv8.1a 87 | avail: armv8.2a 88 | [F32MM] 89 | armname: FEAT_F32MM 90 | llvmname: f32mm 91 | availopt: armv8.2a 92 | requires: SVE 93 | incomplete: yes 94 | [F64MM] 95 | armname: FEAT_F64MM 96 | llvmname: f64mm 97 | availopt: armv8.2a 98 | requires: SVE 99 | incomplete: yes 100 | [FP16] 101 | armname: FEAT_FP16 102 | llvmname: fullfp16 103 | availopt: armv8.2a 104 | requires: !armv8.4a||FHM 105 | [I8MM] 106 | armname: FEAT_I8MM 107 | llvmname: i8mm 108 | availopt: armv8.1a 109 | avail: armv8.6a 110 | [PAN2] 111 | armname: FEAT_PAN2 112 | llvmname: pan-rwv 113 | availopt: armv8.1a 114 | avail: armv8.2a 115 | requires: PAN 116 | [RAS] 117 | armname: FEAT_RAS 118 | llvmname: ras 119 | availopt: armv8.0a 120 | avail: armv8.2a 121 | [SHA3] 122 | armname: FEAT_SHA3 123 | llvmname: sha3 124 | availopt: armv8.1a 125 | requires: SHA256;SHA1 126 | [SHA512] 127 | armname: FEAT_SHA512 128 | llvmname: sha3 129 | availopt: armv8.1a 130 | requires: SHA256;SHA1 131 | [SM3] 132 | armname: FEAT_SM3 133 | llvmname: sm4 134 | availopt: armv8.1a 135 | [SM4] 136 | armname: FEAT_SM4 137 | llvmname: sm4 138 | availopt: armv8.1a 139 | [SVE] 140 | armname: FEAT_SVE 141 | llvmname: sve 142 | availopt: armv8.2a 143 | requires: FP16;FCMA 144 | incomplete: yes, very 145 | [UAO] 146 | armname: FEAT_UAO 147 | llvmname: uaops 148 | availopt: armv8.1a 149 | avail: armv8.2a 150 | 151 | # Features introduced in Armv8.3 152 | [FCMA] 153 | armname: FEAT_FCMA 154 | llvmname: complxnum 155 | availopt: armv8.2a 156 | avail: armv8.3a 157 | [JSCVT] 158 | armname: FEAT_JSCVT 159 | llvmname: jsconv 160 | availopt: armv8.2a 161 | avail: armv8.3a 162 | [LRCPC] 163 | armname: FEAT_LRCPC 164 | llvmname: rcpc 165 | availopt: armv8.2a 166 | avail: armv8.3a 167 | [PAuth] 168 | armname: FEAT_PAuth 169 | llvmname: pauth 170 | availopt: armv8.2a 171 | avail: armv8.3a 172 | 173 | # Features introduced in Armv8.4 174 | [DIT] 175 | armname: FEAT_DIT 176 | llvmname: dit 177 | availopt: armv8.3a 178 | avail: armv8.4a 179 | [DotProd] 180 | armname: FEAT_DotProd 181 | llvmname: dotprod 182 | availopt: armv8.1a 183 | avail: armv8.4a 184 | [FHM] 185 | armname: FEAT_FHM 186 | llvmname: fp16fml 187 | availopt: armv8.1a 188 | requires: FP16 189 | [FlagM] 190 | armname: FEAT_FlagM 191 | llvmname: flagm 192 | availopt: armv8.1a 193 | avail: armv8.4a 194 | [LRCPC2] 195 | armname: FEAT_LRCPC2 196 | llvmname: rcpc-immo 197 | availopt: armv8.2a 198 | avail: armv8.4a 199 | [LSE2] 200 | armname: FEAT_LSE2 201 | llvmname: lse2 202 | availopt: armv8.2 203 | avail: armv8.4 204 | [TLBIOS] 205 | armname: FEAT_TLBIOS 206 | llvmname: tlb-rmi 207 | availopt: armv8.3a 208 | avail: armv8.4a 209 | [TLBIRANGE] 210 | armname: FEAT_TLBIRANGE 211 | llvmname: tlb-rmi 212 | availopt: armv8.3a 213 | avail: armv8.4a 214 | 215 | # Features introduced in Armv8.5 216 | [BTI] 217 | armname: FEAT_BTI 218 | llvmname: bti 219 | availopt: armv8.4a 220 | avail: armv8.5a 221 | [DPB2] 222 | armname: FEAT_DPB2 223 | llvmname: ccdp 224 | availopt: armv8.1a 225 | avail: armv8.5a 226 | requires: DPB 227 | [FRINTTS] 228 | armname: FEAT_FRINTTS 229 | llvmname: fptoint 230 | availopt: armv8.4a 231 | avail: armv8.5a 232 | [FlagM2] 233 | armname: FEAT_FlagM2 234 | llvmname: altnzcv 235 | availopt: armv8.4a 236 | requires: FlagM 237 | [MTE] 238 | armname: FEAT_MTE 239 | llvmname: mte 240 | availopt: armv8.4a 241 | [MTE2] 242 | armname: FEAT_MTE2 243 | llvmname: mte 244 | availopt: armv8.4a 245 | requires: MTE 246 | [SB] 247 | armname: FEAT_SB 248 | llvmname: sb 249 | availopt: armv8.0a 250 | avail: armv8.5a 251 | [SSBS] 252 | armname: FEAT_SSBS 253 | llvmname: ssbs 254 | availopt: armv8.0a 255 | [SSBS2] 256 | armname: FEAT_SSBS2 257 | llvmname: ssbs 258 | availopt: armv8.0a 259 | requires: SSBS 260 | 261 | # Features introduced in Armv8.6 262 | [BF16] 263 | armname: FEAT_BF16 264 | llvmname: bf16 265 | availopt: armv8.2a 266 | avail: armv8.6a 267 | [DGH] 268 | armname: FEAT_DGH 269 | availopt: armv8.0a 270 | note: Hint instruction always available 271 | 272 | # Features introduced in Armv8.7 273 | [AFP] 274 | armname: FEAT_AFP 275 | availopt: armv8.6a 276 | avail: armv8.7a 277 | [LS64] 278 | armname: FEAT_LS64 279 | llvmname: ls64 280 | availopt: armv8.6a 281 | [LS64_V] 282 | armname: FEAT_LS64_V 283 | llvmname: ls64 284 | availopt: armv8.6a 285 | requires: LS64 286 | [LS64_ACCDATA] 287 | armname: FEAT_LS64_ACCDATA 288 | llvmname: ls64 289 | availopt: armv8.6a 290 | requires: LS64_V 291 | [WFxT] 292 | armname: FEAT_WFxT 293 | llvmname: wfxt 294 | availopt: armv8.6a 295 | avail: armv8.7a 296 | 297 | # Features introduced in Armv8.8 298 | [HBC] 299 | armname: FEAT_HBC 300 | llvmname: hbc 301 | availopt: armv8.7a 302 | avail: armv8.8a 303 | [MOPS] 304 | armname: FEAT_MOPS 305 | llvmname: mops 306 | availopt: armv8.7a 307 | avail: armv8.8a 308 | [NMI] 309 | armname: FEAT_NMI 310 | llvmname: nmi 311 | availopt: armv8.7a 312 | avail: armv8.8a 313 | 314 | # Features introduced in Armv8.9 315 | [CLRBHB] 316 | armname: FEAT_CLRBHB 317 | llvmname: clrbhb 318 | availopt: armv8.0a 319 | avail: armv8.9a 320 | note: Hint instruction always available 321 | [CSSC] 322 | armname: FEAT_CSSC 323 | llvmname: cssc 324 | availopt: armv8.7a 325 | avail: armv8.9a 326 | [LRCPC3] 327 | armname: FEAT_LRCPC3 328 | llvmname: rcpc3 329 | availopt: armv8.2a 330 | requires: LRCPC2 331 | incomplete: yes 332 | [RPRFM] 333 | armname: FEAT_RPRFM 334 | availopt: armv8.0a 335 | incomplete: yes 336 | [THE] 337 | armname: FEAT_THE 338 | llvmname: the 339 | availopt: armv8.8a 340 | incomplete: yes 341 | 342 | # Features introduced in Armv9.0 343 | # TODO: FEAT_SVE2 344 | # TODO: FEAT_SVE_AES 345 | # TODO: FEAT_SVE_BitPerm 346 | # TODO: FEAT_SVE_PMULL128 347 | # TODO: FEAT_SVE_SHA3 348 | # TODO: FEAT_SVE_SM4 349 | [TME] 350 | armname: FEAT_TME 351 | llvmname: tme 352 | availopt: armv9.0a 353 | 354 | # Features introduced in Armv9.2 355 | [SME] 356 | armname: FEAT_SME 357 | llvmname: sme 358 | availopt: armv9.2a 359 | requires: FCMA;FP16;BF16;FHM 360 | incomplete: yes 361 | # TODO: FEAT_SME_F64F64 362 | # TODO: FEAT_SME_FA64 363 | # TODO: FEAT_SME_I16I64 364 | 365 | # Features introduced in Armv9.3 366 | # TODO: SME2 367 | 368 | # Features introduced in Armv9.4 369 | [CHK] 370 | armname: FEAT_CHK 371 | llvmname: chk 372 | avail: armv8.0a 373 | availopt: armv9.4a 374 | note: Hint instruction always available 375 | # TODO: D128 376 | [EBEP] 377 | armname: FEAT_EBEP 378 | availopt: armv9.3a 379 | [GCS] 380 | armname: FEAT_GCS 381 | avail: armv9.3a 382 | availopt: armv9.4a 383 | requires: CHK;S1PIE 384 | note: Hint instruction always available 385 | incomplete: yes GCSSTR GCSSTTR, SYS decode/encode 386 | # TODO: ITE (SYS TRCIT) 387 | # TODO: LSE128 388 | # TODO: SME2p1 389 | # TODO: SME_F16F16 390 | # TODO: SVE2p1 391 | # TODO: SVE_B16B16 392 | # TODO: SYSINSTR128 393 | # TODO: SYSREG128 394 | 395 | # availability TBD 396 | [FAMINMAX] 397 | armname: FEAT_FAMINMAX 398 | llvmname: faminmax 399 | incomplete: yes 400 | [FP8] 401 | armname: FEAT_FP8 402 | llvmname: fp8 403 | incomplete: yes 404 | [FP8DOT4] 405 | armname: FEAT_FP8DOT4 406 | incomplete: yes 407 | [FP8DOT2] 408 | armname: FEAT_FP8DOT2 409 | incomplete: yes 410 | [FP8FMA] 411 | armname: FEAT_FP8FMA 412 | llvmname: fp8fma 413 | incomplete: yes 414 | [SPE] 415 | incomplete: yes 416 | [TRF] 417 | incomplete: yes 418 | [MEC] 419 | incomplete: yes 420 | [RME] 421 | incomplete: yes 422 | [TLBIW] 423 | incomplete: yes 424 | [XS] 425 | incomplete: yes 426 | [BRBE] 427 | incomplete: yes 428 | [SPECRES] 429 | incomplete: yes 430 | [SPECRES2] 431 | incomplete: yes 432 | [ATS1A] 433 | incomplete: yes 434 | -------------------------------------------------------------------------------- /meta/constants.py: -------------------------------------------------------------------------------- 1 | import csv 2 | import re 3 | import os 4 | overlapping_extensions = { 5 | "rv_zcmt": {"rv_c_d"}, 6 | "rv_zcmp": {"rv_c_d"}, 7 | "rv_c": {"rv_zcmop"}, 8 | } 9 | 10 | overlapping_instructions = { 11 | "c_addi": {"c_nop"}, 12 | "c_lui": {"c_addi16sp"}, 13 | "c_mv": {"c_jr"}, 14 | "c_jalr": {"c_ebreak"}, 15 | "c_add": {"c_ebreak", "c_jalr"}, 16 | } 17 | 18 | isa_regex = re.compile( 19 | "^RV(32|64|128)[IE]+[ABCDEFGHJKLMNPQSTUVX]*(Zicsr|Zifencei|Zihintpause|Zam|Ztso|Zkne|Zknd|Zknh|Zkse|Zksh|Zkg|Zkb|Zkr|Zks|Zkn|Zba|Zbc|Zbb|Zbp|Zbr|Zbm|Zbs|Zbe|Zbf|Zbt|Zmmul|Zbpbo|Zca|Zcf|Zcd|Zcb|Zcmp|Zcmt){,1}(_Zicsr){,1}(_Zifencei){,1}(_Zihintpause){,1}(_Zmmul){,1}(_Zam){,1}(_Zba){,1}(_Zbb){,1}(_Zbc){,1}(_Zbe){,1}(_Zbf){,1}(_Zbm){,1}(_Zbp){,1}(_Zbpbo){,1}(_Zbr){,1}(_Zbs){,1}(_Zbt){,1}(_Zkb){,1}(_Zkg){,1}(_Zkr){,1}(_Zks){,1}(_Zkn){,1}(_Zknd){,1}(_Zkne){,1}(_Zknh){,1}(_Zkse){,1}(_Zksh){,1}(_Ztso){,1}(_Zca){,1}(_Zcf){,1}(_Zcd){,1}(_Zcb){,1}(_Zcmp){,1}(_Zcmt){,1}$" 20 | ) 21 | 22 | # regex to find ..= patterns in instruction 23 | fixed_ranges = re.compile( 24 | "\s*(?P\d+.?)\.\.(?P\d+.?)\s*=\s*(?P\d[\w]*)[\s$]*", re.M 25 | ) 26 | 27 | # regex to find = patterns in instructions 28 | # single_fixed = re.compile('\s+(?P\d+)=(?P[\w\d]*)[\s$]*', re.M) 29 | single_fixed = re.compile("(?:^|[\s])(?P\d+)=(?P[\w]*)((?=\s|$))", re.M) 30 | 31 | # regex to find the overloading condition variable 32 | var_regex = re.compile("(?P[a-zA-Z][\w\d]*)\s*=\s*.*?[\s$]*", re.M) 33 | 34 | # regex for pseudo op instructions returns the dependent filename, dependent 35 | # instruction, the pseudo op name and the encoding string 36 | pseudo_regex = re.compile( 37 | "^\$pseudo_op\s+(?Prv[\d]*_[\w].*)::\s*(?P.*?)\s+(?P.*?)\s+(?P.*)$", 38 | re.M, 39 | ) 40 | 41 | imported_regex = re.compile( 42 | "^\s*\$import\s*(?P.*)\s*::\s*(?P.*)", re.M 43 | ) 44 | 45 | 46 | def read_csv(filename): 47 | """ 48 | Reads a CSV file and returns a list of tuples. 49 | Each tuple contains an integer value (from the first column) and a string (from the second column). 50 | 51 | Args: 52 | filename (str): The name of the CSV file to read. 53 | 54 | Returns: 55 | list of tuple: A list of (int, str) tuples extracted from the CSV file. 56 | """ 57 | with open(filename) as f: 58 | csv_reader = csv.reader(f, skipinitialspace=True) 59 | return [(int(row[0], 0), row[1]) for row in csv_reader] 60 | 61 | RISCV_OPCODES = os.environ["RISCV_OPCODES"] 62 | 63 | causes = read_csv(RISCV_OPCODES + "/causes.csv") 64 | csrs = read_csv(RISCV_OPCODES + "/csrs.csv") 65 | csrs32 = read_csv(RISCV_OPCODES + "/csrs32.csv") 66 | 67 | # Load the argument lookup table (arg_lut) from a CSV file, mapping argument names to their bit positions 68 | arg_lut = { 69 | row[0]: (int(row[1]), int(row[2])) 70 | for row in csv.reader(open(RISCV_OPCODES + "/arg_lut.csv"), skipinitialspace=True) 71 | } 72 | 73 | # for mop 74 | arg_lut["mop_r_t_30"] = (30, 30) 75 | arg_lut["mop_r_t_27_26"] = (27, 26) 76 | arg_lut["mop_r_t_21_20"] = (21, 20) 77 | arg_lut["mop_rr_t_30"] = (30, 30) 78 | arg_lut["mop_rr_t_27_26"] = (27, 26) 79 | arg_lut["c_mop_t"] = (10, 8) 80 | 81 | # dictionary containing the mapping of the argument to the what the fields in 82 | # the latex table should be 83 | latex_mapping = {} 84 | latex_mapping["imm12"] = "imm[11:0]" 85 | latex_mapping["rs1"] = "rs1" 86 | latex_mapping["rs2"] = "rs2" 87 | latex_mapping["rd"] = "rd" 88 | latex_mapping["imm20"] = "imm[31:12]" 89 | latex_mapping["bimm12hi"] = "imm[12$\\vert$10:5]" 90 | latex_mapping["bimm12lo"] = "imm[4:1$\\vert$11]" 91 | latex_mapping["imm12hi"] = "imm[11:5]" 92 | latex_mapping["imm12lo"] = "imm[4:0]" 93 | latex_mapping["jimm20"] = "imm[20$\\vert$10:1$\\vert$11$\\vert$19:12]" 94 | latex_mapping["zimm"] = "uimm" 95 | latex_mapping["shamtw"] = "shamt" 96 | latex_mapping["shamtd"] = "shamt" 97 | latex_mapping["shamtq"] = "shamt" 98 | latex_mapping["rd_p"] = "rd\\,$'$" 99 | latex_mapping["rs1_p"] = "rs1\\,$'$" 100 | latex_mapping["rs2_p"] = "rs2\\,$'$" 101 | latex_mapping["rd_rs1_n0"] = "rd/rs$\\neq$0" 102 | latex_mapping["rd_rs1_p"] = "rs1\\,$'$/rs2\\,$'$" 103 | latex_mapping["c_rs2"] = "rs2" 104 | latex_mapping["c_rs2_n0"] = "rs2$\\neq$0" 105 | latex_mapping["rd_n0"] = "rd$\\neq$0" 106 | latex_mapping["rs1_n0"] = "rs1$\\neq$0" 107 | latex_mapping["c_rs1_n0"] = "rs1$\\neq$0" 108 | latex_mapping["rd_rs1"] = "rd/rs1" 109 | latex_mapping["zimm6hi"] = "uimm[5]" 110 | latex_mapping["zimm6lo"] = "uimm[4:0]" 111 | latex_mapping["c_nzuimm10"] = "nzuimm[5:4$\\vert$9:6$\\vert$2$\\vert$3]" 112 | latex_mapping["c_uimm7lo"] = "uimm[2$\\vert$6]" 113 | latex_mapping["c_uimm7hi"] = "uimm[5:3]" 114 | latex_mapping["c_uimm8lo"] = "uimm[7:6]" 115 | latex_mapping["c_uimm8hi"] = "uimm[5:3]" 116 | latex_mapping["c_uimm9lo"] = "uimm[7:6]" 117 | latex_mapping["c_uimm9hi"] = "uimm[5:4$\\vert$8]" 118 | latex_mapping["c_nzimm6lo"] = "nzimm[4:0]" 119 | latex_mapping["c_nzimm6hi"] = "nzimm[5]" 120 | latex_mapping["c_imm6lo"] = "imm[4:0]" 121 | latex_mapping["c_imm6hi"] = "imm[5]" 122 | latex_mapping["c_nzimm10hi"] = "nzimm[9]" 123 | latex_mapping["c_nzimm10lo"] = "nzimm[4$\\vert$6$\\vert$8:7$\\vert$5]" 124 | latex_mapping["c_nzimm18hi"] = "nzimm[17]" 125 | latex_mapping["c_nzimm18lo"] = "nzimm[16:12]" 126 | latex_mapping["c_imm12"] = ( 127 | "imm[11$\\vert$4$\\vert$9:8$\\vert$10$\\vert$6$\\vert$7$\\vert$3:1$\\vert$5]" 128 | ) 129 | latex_mapping["c_bimm9lo"] = "imm[7:6$\\vert$2:1$\\vert$5]" 130 | latex_mapping["c_bimm9hi"] = "imm[8$\\vert$4:3]" 131 | latex_mapping["c_nzuimm5"] = "nzuimm[4:0]" 132 | latex_mapping["c_nzuimm6lo"] = "nzuimm[4:0]" 133 | latex_mapping["c_nzuimm6hi"] = "nzuimm[5]" 134 | latex_mapping["c_uimm8splo"] = "uimm[4:2$\\vert$7:6]" 135 | latex_mapping["c_uimm8sphi"] = "uimm[5]" 136 | latex_mapping["c_uimm8sp_s"] = "uimm[5:2$\\vert$7:6]" 137 | latex_mapping["c_uimm10splo"] = "uimm[4$\\vert$9:6]" 138 | latex_mapping["c_uimm10sphi"] = "uimm[5]" 139 | latex_mapping["c_uimm9splo"] = "uimm[4:3$\\vert$8:6]" 140 | latex_mapping["c_uimm9sphi"] = "uimm[5]" 141 | latex_mapping["c_uimm10sp_s"] = "uimm[5:4$\\vert$9:6]" 142 | latex_mapping["c_uimm9sp_s"] = "uimm[5:3$\\vert$8:6]" 143 | 144 | # created a dummy instruction-dictionary like dictionary for all the instruction 145 | # types so that the same logic can be used to create their tables 146 | latex_inst_type = {} 147 | latex_inst_type["R-type"] = {} 148 | latex_inst_type["R-type"]["variable_fields"] = [ 149 | "opcode", 150 | "rd", 151 | "funct3", 152 | "rs1", 153 | "rs2", 154 | "funct7", 155 | ] 156 | latex_inst_type["R4-type"] = {} 157 | latex_inst_type["R4-type"]["variable_fields"] = [ 158 | "opcode", 159 | "rd", 160 | "funct3", 161 | "rs1", 162 | "rs2", 163 | "funct2", 164 | "rs3", 165 | ] 166 | latex_inst_type["I-type"] = {} 167 | latex_inst_type["I-type"]["variable_fields"] = [ 168 | "opcode", 169 | "rd", 170 | "funct3", 171 | "rs1", 172 | "imm12", 173 | ] 174 | latex_inst_type["S-type"] = {} 175 | latex_inst_type["S-type"]["variable_fields"] = [ 176 | "opcode", 177 | "imm12lo", 178 | "funct3", 179 | "rs1", 180 | "rs2", 181 | "imm12hi", 182 | ] 183 | latex_inst_type["B-type"] = {} 184 | latex_inst_type["B-type"]["variable_fields"] = [ 185 | "opcode", 186 | "bimm12lo", 187 | "funct3", 188 | "rs1", 189 | "rs2", 190 | "bimm12hi", 191 | ] 192 | latex_inst_type["U-type"] = {} 193 | latex_inst_type["U-type"]["variable_fields"] = ["opcode", "rd", "imm20"] 194 | latex_inst_type["J-type"] = {} 195 | latex_inst_type["J-type"]["variable_fields"] = ["opcode", "rd", "jimm20"] 196 | latex_fixed_fields = [] 197 | latex_fixed_fields.append((31, 25)) 198 | latex_fixed_fields.append((24, 20)) 199 | latex_fixed_fields.append((19, 15)) 200 | latex_fixed_fields.append((14, 12)) 201 | latex_fixed_fields.append((11, 7)) 202 | latex_fixed_fields.append((6, 0)) 203 | 204 | # Pseudo-ops present in the generated encodings. 205 | # By default pseudo-ops are not listed as they are considered aliases 206 | # of their base instruction. 207 | emitted_pseudo_ops = [ 208 | "pause", 209 | "prefetch_i", 210 | "prefetch_r", 211 | "prefetch_w", 212 | "rstsa16", 213 | "rstsa32", 214 | "srli32_u", 215 | "slli_rv32", 216 | "srai_rv32", 217 | "srli_rv32", 218 | "umax32", 219 | "c_mop_1", 220 | "c_sspush_x1", 221 | "c_mop_3", 222 | "c_mop_5", 223 | "c_sspopchk_x5", 224 | "c_mop_7", 225 | "c_mop_9", 226 | "c_mop_11", 227 | "c_mop_13", 228 | "c_mop_15", 229 | "mop_r_0", 230 | "mop_r_1", 231 | "mop_r_2", 232 | "mop_r_3", 233 | "mop_r_4", 234 | "mop_r_5", 235 | "mop_r_6", 236 | "mop_r_7", 237 | "mop_r_8", 238 | "mop_r_9", 239 | "mop_r_10", 240 | "mop_r_11", 241 | "mop_r_12", 242 | "mop_r_13", 243 | "mop_r_14", 244 | "mop_r_15", 245 | "mop_r_16", 246 | "mop_r_17", 247 | "mop_r_18", 248 | "mop_r_19", 249 | "mop_r_20", 250 | "mop_r_21", 251 | "mop_r_22", 252 | "mop_r_23", 253 | "mop_r_24", 254 | "mop_r_25", 255 | "mop_r_26", 256 | "mop_r_27", 257 | "mop_r_28", 258 | "sspopchk_x1", 259 | "sspopchk_x5", 260 | "ssrdp", 261 | "mop_r_29", 262 | "mop_r_30", 263 | "mop_r_31", 264 | "mop_r_32", 265 | "mop_rr_0", 266 | "mop_rr_1", 267 | "mop_rr_2", 268 | "mop_rr_3", 269 | "mop_rr_4", 270 | "mop_rr_5", 271 | "mop_rr_6", 272 | "mop_rr_7", 273 | "sspush_x1", 274 | "sspush_x5", 275 | "lpad", 276 | "bclri.rv32", 277 | "bexti.rv32", 278 | "binvi.rv32", 279 | "bseti.rv32", 280 | "zext.h.rv32", 281 | "rev8.h.rv32", 282 | "rori.rv32", 283 | ] 284 | -------------------------------------------------------------------------------- /meta/ppc.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import re 3 | 4 | remspaces = re.compile(r"[\n\t\s]*") 5 | # fetch opcode name from the line, return name + line after the opcode 6 | # format of the line: `, rest...` 7 | # format of the output: `rest...` 8 | def opname(line): 9 | # Remove leading and trailing spaces, tabs, and newlines 10 | line = remspaces.sub("", line) 11 | 12 | # Split the line by the first comma to get the opcode name and the rest 13 | parts = line.split(",", 1) 14 | 15 | # Ensure the line has an opcode name and a rest part 16 | if len(parts) != 2: 17 | raise ValueError("Invalid line format: expected ', rest...'") 18 | 19 | # Return the opcode name and the rest of the line 20 | opcode_name, rest = parts[0], parts[1] 21 | return opcode_name, rest 22 | 23 | # Matches a closing bracket in string `s` starting `from` the given index. 24 | # It behaves like `s.indexOf()`, but uses a counter and skips all nested 25 | # matches. 26 | def match_closing_char(s, from_index): 27 | len_s = len(s) 28 | opening = ord(s[from_index]) 29 | closing = 41 if opening == 40 else 62 if opening == 60 else 93 if opening == 91 else 125 if opening == 123 else 0 30 | 31 | i = from_index 32 | pending = 1 33 | while pending: 34 | i += 1 35 | if i >= len_s: 36 | break 37 | c = ord(s[i]) 38 | pending += 1 if c == opening else 0 39 | pending -= 1 if c == closing else 0 40 | 41 | return i 42 | 43 | 44 | def opcode(opc): 45 | if opc.startswith('OP'): 46 | clos = match_closing_char(opc, 2) 47 | opn = int(opc[2:clos]) 48 | return (opn & 0x3f) << 26 49 | elif opc.startswith('X'): 50 | # XOP(op, xop) 51 | clos = match_closing_char(opc, 1) 52 | s = opc[2:clos].split(',', maxsplit=1) 53 | op = int(s[0]) 54 | xop = int(s[1]) 55 | 56 | return (op & 0x3f) << 26 | ((xop & 0x3ff) << 1) 57 | else: 58 | return 0 59 | 60 | 61 | if __name__ == "__main__": 62 | parser = argparse.ArgumentParser() 63 | parser.add_argument("--opcodes", type=argparse.FileType('r')) 64 | parser.add_argument('out_opcodes', type=argparse.FileType('w')) 65 | args =parser.parse_args() 66 | for line in args.opcodes.read().splitlines(): 67 | 68 | pattern = re.match(r'(\w+),\s*(\S+)\s*\((\d+),\s*(\d+)\),\s*(\w+),\s*(\w+),\s*(\d+),\s*\{([^\}]+)\}', line) 69 | if pattern: 70 | for m in pattern.groups(): 71 | print(m) 72 | -------------------------------------------------------------------------------- /meta/rustbuilder.py: -------------------------------------------------------------------------------- 1 | from typing import Self 2 | from collections.abc import Callable 3 | id = 0 4 | 5 | class Formatter: 6 | 7 | def __init__(self): 8 | self.dst = "" 9 | self.spaces = 0 10 | self.indent_ = 4 11 | def isStartOfLine(self) -> bool: 12 | return len(self.dst) == 0 or self.dst.endswith('\n') 13 | def pushSpaces(self): 14 | for _ in range(self.spaces): 15 | self.dst += " " 16 | 17 | def indent[R](self, f: Callable[[Self], R]) -> R: 18 | self.spaces += self.indent_ 19 | ret = f(self) 20 | self.spaces -= self.indent_ 21 | ret 22 | def indentStart(self): 23 | self.spaces += self.indent_ 24 | 25 | def indentEnd(self): 26 | self.spaces -= self.indent_ 27 | def block(self, f: Callable[[Self], None]): 28 | if not self.isStartOfLine(): 29 | self += " " 30 | 31 | self.write("{") 32 | self.indent(f) 33 | self.write("}\n") 34 | 35 | def __str__(self): 36 | return self.dst 37 | 38 | def __add__(self, other: str): 39 | self.write(other) 40 | return self 41 | 42 | def write(self, s: str): 43 | first = True 44 | should_indent = self.isStartOfLine() 45 | 46 | for line in s.splitlines(): 47 | if not first: 48 | self.dst += "\n" 49 | first = False 50 | 51 | do_indent = should_indent and len(line) != 0 and not line.endswith('\n') 52 | 53 | if do_indent: 54 | self.pushSpaces() 55 | should_indent = True 56 | self.dst += line 57 | if s.endswith('\n'): 58 | self.dst += "\n" 59 | 60 | class Expr: 61 | def fmt(self, fmt: Formatter): 62 | pass 63 | 64 | class Block(Expr): 65 | 66 | 67 | def __init__(self): 68 | self.before_ = None 69 | self.after_ = None 70 | self.body = list() 71 | 72 | def line(self, line: str | Expr) -> Self: 73 | self.body.append(line) 74 | return self 75 | 76 | def pushBlock(self, block: Self) -> Self: 77 | self.body.append(block) 78 | return self 79 | 80 | def after(self, after: str | Expr) -> Self: 81 | self.after_ = after 82 | return self 83 | 84 | def before(self, before: str| Expr) -> Self: 85 | self.before_ = before 86 | return self 87 | 88 | def fmt(self, fmt: Formatter): 89 | if self.before_: 90 | fmt.write(str(self.before_)) 91 | 92 | if not fmt.isStartOfLine(): 93 | fmt.write(" ") 94 | 95 | fmt.write("{\n") 96 | fmt.indentStart() 97 | 98 | for b in self.body: 99 | if isinstance(b, Expr): 100 | b.fmt(fmt) 101 | else: 102 | fmt.write(f"{b}\n") 103 | fmt.indentEnd() 104 | fmt.write("}") 105 | 106 | if self.after_: 107 | fmt.write(str(self.after_)) 108 | fmt.write("\n") 109 | 110 | 111 | class MatchArm(Expr): 112 | 113 | def default(): 114 | this = MatchArm(None, None, Block()) 115 | return this 116 | 117 | def __init__(self, pat, where, body): 118 | self.pat = pat 119 | self.where = where 120 | self.body = body 121 | 122 | def fmt(self, fmt: Formatter): 123 | pat = "_" if self.pat is None else self.pat 124 | where = f" if {self.where}" if self.where else "" 125 | 126 | fmt.write(f"{pat}{where} => ") 127 | if isinstance(self.body, Expr): 128 | self.body.fmt(fmt) 129 | else: 130 | fmt.write(str(self.body)) 131 | 132 | 133 | class Match(Expr): 134 | 135 | def __init__(self, expr): 136 | self.expr = expr 137 | self.arms = list() 138 | 139 | 140 | def append(self, arm: MatchArm): 141 | self.arms.append(arm) 142 | 143 | def fmt(self, fmt: Formatter): 144 | fmt.write(f"match {self.expr} {{\n") 145 | fmt.indentStart() 146 | print(f"{len(self.arms)} arms for {self.expr}", ) 147 | total = 0 148 | for arm in self.arms: 149 | fmt.dst += f"{arm.pat}{arm.where if arm.where else ""} =>" 150 | arm.body.fmt(fmt) 151 | total += 1 152 | if total == 512: 153 | break 154 | fmt.indentEnd() 155 | fmt.write("}") 156 | 157 | # A conditional statement. If there are conds produced it will emit `if` and conds otherwise it emits expression itself 158 | class Cond(Expr): 159 | 160 | def __init__(self) -> None: 161 | # conds: list[(cond, or_and)] 162 | self.body_ = None 163 | self.conds: list[(str, bool)] = list() 164 | 165 | def body(self, body: str | Block): 166 | if isinstance(body, str): 167 | b = Block() 168 | b.line(body) 169 | body = b 170 | self.body_ = body 171 | def and_(self, expr: str): 172 | self.conds.append((expr, True)) 173 | return self 174 | def or_(self, expr: str): 175 | self.conds.append((expr, False)) 176 | 177 | def fmt(self, fmt: Formatter): 178 | """ 179 | Emit cond as `if`. 180 | `len(conds) == 0` => ` {self.body.fmt(fmt)} 181 | `len(conds) == 1` => `if {self.conds[0][0]} { {self.body} } 182 | else => `if {self.conds[0]} "and" if self.conds[0][1] else "or" ... 183 | """ 184 | if len(self.conds) == 0: 185 | self.body_.fmt(fmt) 186 | elif len(self.conds) == 1: 187 | cond, _ = self.conds[0] 188 | fmt.write(f"if {cond} ") 189 | if isinstance(self.body_, Expr): 190 | self.body_.fmt(fmt) 191 | else: 192 | fmt.write(str(self.body_)) 193 | else: 194 | 195 | fmt.write("if ") 196 | for i, (cond, is_and) in enumerate(self.conds): 197 | fmt.write(cond) 198 | 199 | if i < len(self.conds) - 1: 200 | is_and = self.conds[i+1][1] 201 | fmt.write(" && " if is_and else " || ") 202 | fmt.write(" ") 203 | if isinstance(self.body_, Expr): 204 | self.body_.fmt(fmt) 205 | else: 206 | fmt.write(str(self.body_)) 207 | 208 | class Type(Expr): 209 | def __init__(self, name): 210 | self.name = name 211 | self.generics: list[Self] = list() 212 | 213 | def generic(self, t): 214 | self.generics.append(t) 215 | 216 | def fmt(self, fmt: Formatter): 217 | fmt.write(self.name) 218 | if len(self.generics) != 0: 219 | fmt.write("<") 220 | for i, ty in enumerate(self.generics): 221 | if i != 0: 222 | fmt.write(", ") 223 | fmt.write(ty) 224 | fmt.write(">") 225 | 226 | class Field(Expr): 227 | def __init__(self, name, ty): 228 | self.name = name 229 | self.ty = ty 230 | 231 | def fmt(self, fmt: Formatter): 232 | fmt.write(f"{self.name}: {self.ty}") 233 | class Function(Expr): 234 | def __init__(self, name) -> None: 235 | self.name = name 236 | self.args: list[Field] = list() 237 | self.vis = None 238 | self.arg_self = None 239 | self._ret = None 240 | self._body = None 241 | 242 | def pub(self): 243 | self.vis = "pub" 244 | return self 245 | def priv(self): 246 | self.vis = None 247 | return self 248 | def self(self): 249 | self.arg_self = "&self" 250 | return self 251 | def self_mut(self): 252 | self.arg_self = "&mut self" 253 | return self 254 | def self_copy(self): 255 | self.arg_self = "self" 256 | return self 257 | def ret(self, ty: Type): 258 | self._ret = ty 259 | return self 260 | 261 | def arg(self, arg: Field): 262 | self.args.append(arg) 263 | return self 264 | def body(self, block: Block): 265 | self._body = block 266 | 267 | def line(self, line: str | Expr): 268 | if not self._body: 269 | self._body = Block() 270 | self._body.line(line) 271 | 272 | def fmt(self, fmt: Formatter): 273 | fmt.write(f"{(self.vis + " ") if self.vis else ""}fn {self.name}({self.arg_self if self.arg_self else ""}") 274 | if self.arg_self: 275 | fmt.write(",") 276 | for i, arg in enumerate(self.args): 277 | arg.fmt(fmt) 278 | if i != len(self.args) - 1: 279 | fmt.write(",") 280 | fmt.write(f") -> {self._ret if self._ret else ""}") 281 | self._body.fmt(fmt) 282 | 283 | class Impl(Expr): 284 | def __init__(self, name) -> None: 285 | self.name = name 286 | self.items = list() 287 | 288 | def append(self, item: Expr): 289 | self.items.append(item) 290 | 291 | def fmt(self, fmt: Formatter): 292 | fmt.write(f"impl {self.name} {{\n") 293 | fmt.indentStart() 294 | for i in self.items: 295 | i.fmt(fmt) 296 | fmt.indentEnd() 297 | fmt.write("}") 298 | 299 | class Const(Expr): 300 | def __init__(self, name, ty=None, init: str | Expr = ""): 301 | self.name = name 302 | self.ty = ty 303 | self.init = init 304 | self.vis = None 305 | 306 | def pub(self): 307 | self.vis = "pub" 308 | 309 | def pub_crate(self): 310 | self.vis = "pub(crate)" 311 | def pub_super(self): 312 | self.vis = "pub(super)" 313 | 314 | def fmt(self, fmt: Formatter): 315 | fmt.write(f"{self.vis if self.vis else ""} const {self.name}: ") 316 | self.ty.fmt(fmt) 317 | fmt.write(" = ") 318 | if isinstance(self.init, Expr): 319 | self.init.fmt(fmt) 320 | else: 321 | fmt.write(self.init) 322 | fmt.write(";\n") 323 | 324 | class TopCode: 325 | items: list[Expr] 326 | 327 | def __init__(self) -> None: 328 | self.items = [] 329 | 330 | def fmt(self, fmt): 331 | for item in self.items: 332 | item.fmt(fmt) 333 | 334 | class Trait(Expr): 335 | items: list[Expr] 336 | name: str 337 | 338 | def __init__(self, name) -> None: 339 | self.name = name 340 | self.items = [] 341 | 342 | def fmt(self, fmt): 343 | fmt.write(f"pub trait {self.name} {{\n") 344 | fmt.indentStart() 345 | for item in self.items: 346 | item.fmt(fmt) 347 | fmt.indentEnd() 348 | fmt.write("}\n") -------------------------------------------------------------------------------- /src/aarch64/decoder.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_parens)] 2 | use super::opcodes::*; 3 | use derive_more::TryFrom; 4 | 5 | #[derive(Copy, Clone, PartialEq, Eq, Debug, TryFrom,)] 6 | #[repr(u8)] 7 | #[try_from(repr)] 8 | pub enum Cond { 9 | EQ = 0x0, 10 | NE = 0x1, 11 | 12 | HS = 0x2, 13 | 14 | LO = 0x3, 15 | MI = 0x4, 16 | PL = 0x5, 17 | VS = 0x6, 18 | VC = 0x7, 19 | HI = 0x8, 20 | LS = 0x9, 21 | GE = 0xa, 22 | LT = 0xb, 23 | GT = 0xc, 24 | LE = 0xd, 25 | AL = 0xe, 26 | NV = 0xf, 27 | } 28 | 29 | impl Cond { 30 | pub const CS: Cond = Cond::HS; 31 | pub const CC: Cond = Cond::LO; 32 | } 33 | 34 | #[derive(Copy, Clone, PartialEq, Eq, TryFrom, Debug)] 35 | #[repr(u8)] 36 | #[try_from(repr)] 37 | pub enum Ext { 38 | Uxtb = 0, 39 | Uxth = 1, 40 | Uxtw = 2, 41 | Uxtx = 3, 42 | Sxtb = 4, 43 | Sxth = 5, 44 | Sxtw = 6, 45 | Sxtx = 7, 46 | Lsl = 8, 47 | Lsr = 9, 48 | Asr = 10, 49 | Ror = 11, 50 | } 51 | 52 | #[derive(Copy, Clone, PartialEq, Eq, TryFrom, Debug)] 53 | #[repr(u8)] 54 | #[try_from(repr)] 55 | pub enum VectorArrangement { 56 | Va8b = 0, 57 | Va16b = 1, 58 | Va4h = 2, 59 | Va8h = 3, 60 | Va2s = 4, 61 | Va4s = 5, 62 | Va1d = 6, 63 | Va2d = 7, 64 | Va2h = 8, 65 | Va1q = 9, 66 | } 67 | #[derive(Copy, Clone, PartialEq, Eq, TryFrom, Debug)] 68 | #[repr(u8)] 69 | #[try_from(repr)] 70 | pub enum OpType { 71 | None = 0, 72 | RegGp, 73 | RegGpInc, 74 | RegGpExt, 75 | RegSp, 76 | RegFp, 77 | RegVec, 78 | RegVtbl, 79 | RegVidx, 80 | RegVtblIdx, 81 | MemUoff, 82 | MemSoff, 83 | MemSoffPre, 84 | MemSoffPost, 85 | MemReg, 86 | MemRegPost, 87 | MemInc, 88 | Cond, 89 | Prfop, 90 | Sysreg, 91 | ImmSmall, 92 | Simm, 93 | Uimm, 94 | UimmShift, 95 | ImmLarge, 96 | ImmFloat, 97 | } 98 | 99 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] 100 | pub struct Op { 101 | op_type: OpType, 102 | value: OpValue, 103 | detail: OpDetail, 104 | } 105 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] 106 | pub enum OpValue { 107 | Reg(u8), 108 | Prfop(u8), 109 | ImmShift { mask: bool, shift: u8 }, 110 | } 111 | 112 | impl Default for Op { 113 | fn default() -> Self { 114 | Self { 115 | op_type: OpType::None, 116 | value: OpValue::Reg(0), 117 | detail: OpDetail::Gp { sf: false }, 118 | } 119 | } 120 | } 121 | 122 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] 123 | pub enum OpDetail { 124 | Gp { 125 | sf: bool, 126 | }, 127 | GpPExt { 128 | sf: bool, 129 | ext: Ext, 130 | shift: u8, 131 | }, 132 | Fp { 133 | size: u8, 134 | }, 135 | Vec { 136 | va: VectorArrangement, 137 | }, 138 | Vidx { 139 | // 0=b, 1=h, 2=s, 3=d, (4=q), 5=2b, 6=4b, 7=2h 140 | esize: u8, 141 | elem: u8, 142 | }, 143 | Vtbl { 144 | va: VectorArrangement, 145 | cnt: u8, 146 | }, 147 | VtblIdx { 148 | esize: u8, 149 | elem: u8, 150 | cnt: u8, 151 | }, 152 | MemReg { 153 | sc: u32, 154 | ext: Ext, 155 | shift: u8, 156 | offreg: u8, 157 | }, 158 | Sysreg(u16), 159 | Uimm16(u16), 160 | Simm16(i16), 161 | Cond(Cond), 162 | } 163 | 164 | pub struct Inst { 165 | pub mnem: InstKind, 166 | pub ops: [Op; 5], 167 | pub imm: Imm, 168 | } 169 | 170 | impl Default for Inst { 171 | fn default() -> Self { 172 | Self { 173 | mnem: InstKind::Unknown, 174 | ops: [Op::default(); 5], 175 | imm: Imm { 176 | imm64: 0 177 | } 178 | } 179 | } 180 | } 181 | 182 | pub union Imm { 183 | imm64: u64, 184 | float8: f64, 185 | } 186 | 187 | pub fn classify(inst: u32) -> InstKind { 188 | InstKind::try_from(classify_impl(inst) as u16).unwrap() 189 | } 190 | 191 | fn ctz(v: u32) -> u32 { 192 | v.trailing_zeros() as _ 193 | } 194 | 195 | fn clz(v: u32, sz: usize) -> u32 { 196 | if v != 0 { 197 | v.leading_zeros() + sz as u32 - 32 198 | } else { 199 | sz as _ 200 | } 201 | } 202 | 203 | fn sext(imm: i32, bits: usize) -> i32 { 204 | let sign = 1 << (bits - 1); 205 | if imm & sign != 0 { 206 | ((imm ^ sign) - sign) as _ 207 | } else { 208 | imm as _ 209 | } 210 | } 211 | 212 | fn immlogical(sf: u32, n: u32, immr: u32, imms: u32) -> u32 { 213 | if (n == 0) && (imms == 0x3f) { 214 | return 0; 215 | } 216 | 217 | let len = 31 - (imms.count_ones() as u32); 218 | let levels = (1 << len) - 1; 219 | let s = imms & levels; 220 | let r = immr & levels; 221 | let esize = 1 << len; 222 | let mut welem = ((1 << (s + 1)) - 1) as u64; 223 | 224 | if r != 0 { 225 | welem = (welem >> r) | (welem << (esize - r)); 226 | } 227 | 228 | if esize < 64 { 229 | welem &= ((1 << esize) - 1) as u64; 230 | } 231 | 232 | let mut wmask = 0 as u64; 233 | for i in (0..(!sf as u32 * 32)).step_by(esize as usize) { 234 | wmask |= welem << i; 235 | } 236 | 237 | wmask as u32 238 | } 239 | 240 | fn opreggp(idx: u32, sf: u32) -> Op { 241 | Op { 242 | op_type: OpType::RegGp, 243 | value: OpValue::Reg(idx as u8), 244 | detail: OpDetail::Gp { sf: sf != 0 }, 245 | } 246 | } 247 | 248 | fn opreggpinc(idx: u32) -> Op { 249 | Op { 250 | op_type: OpType::RegGpInc, 251 | value: OpValue::Reg(idx as u8), 252 | detail: OpDetail::Gp { sf: true }, 253 | } 254 | } 255 | 256 | fn opreggpsp(idx: u32, sf: u32) -> Op { 257 | Op { 258 | op_type: if idx != 31 { 259 | OpType::RegGp 260 | } else { 261 | OpType::RegSp 262 | }, 263 | value: OpValue::Reg(idx as u8), 264 | detail: OpDetail::Gp { sf: sf != 0 }, 265 | } 266 | } 267 | 268 | fn opreggpmaysp(maysp: u32, idx: u32, sf: u32) -> Op { 269 | if idx < 31 || maysp == 0 { 270 | Op { 271 | op_type: OpType::RegGp, 272 | value: OpValue::Reg(idx as u8), 273 | detail: OpDetail::Gp { sf: sf != 0 }, 274 | } 275 | } else { 276 | Op { 277 | op_type: OpType::RegSp, 278 | value: OpValue::Reg(idx as u8), 279 | detail: OpDetail::Gp { sf: sf != 0 }, 280 | } 281 | } 282 | } 283 | 284 | fn opreggpprf(isprf: u32, idx: u32, sf: u32) -> Op { 285 | Op { 286 | op_type: if isprf!= 0 { OpType::Prfop } else { OpType::RegGp }, 287 | value: OpValue::Reg(idx as u8), 288 | detail: OpDetail::Gp { sf: sf!= 0 }, 289 | } 290 | 291 | } 292 | 293 | fn opreggpext(idx: u32, sf: u32, ext: u32, shift: u32) -> Op { 294 | Op { 295 | op_type: OpType::RegGpExt, 296 | value: OpValue::Reg(idx as u8), 297 | detail: OpDetail::GpPExt { 298 | sf: sf!= 0, 299 | ext: Ext::try_from(ext as u8).unwrap(), 300 | shift: shift as u8, 301 | }, 302 | } 303 | 304 | } 305 | 306 | fn opregfp(idx: u32, size: u32) -> Op { 307 | Op { 308 | op_type: OpType::RegFp, 309 | value: OpValue::Reg(idx as u8), 310 | detail: OpDetail::Fp { size: size as u8 }, 311 | } 312 | 313 | } 314 | 315 | fn opregvec(idx: u32, esize: u32, q: u32) -> Op { 316 | Op { 317 | op_type: OpType::RegVec, 318 | value: OpValue::Reg(idx as u8), 319 | detail: OpDetail::Vec { 320 | va: VectorArrangement::try_from(((esize as u8) << 1) + q as u8).unwrap(), 321 | }, 322 | } 323 | 324 | } 325 | 326 | fn opregvidx(idx: u32, esize: u32, elem: u32) -> Op { 327 | Op { 328 | op_type: OpType::RegVidx, 329 | value: OpValue::Reg(idx as u8), 330 | detail: OpDetail::Vidx { 331 | esize: esize as u8, 332 | elem: elem as u8, 333 | }, 334 | } 335 | 336 | } 337 | 338 | fn opregvtbl(idx: u32, esize: u32, q: u32, cnt: u32) -> Op { 339 | Op { 340 | op_type: OpType::RegVtbl, 341 | value: OpValue::Reg(idx as u8), 342 | detail: OpDetail::Vtbl { 343 | va: VectorArrangement::try_from(((esize as u8) << 1) + q as u8).unwrap(), 344 | cnt: cnt as u8, 345 | }, 346 | } 347 | 348 | } 349 | 350 | fn opregvtblidx(idx: u32, esize: u32, elem: u32, cnt: u32) -> Op { 351 | Op { 352 | op_type: OpType::RegVtblIdx, 353 | value: OpValue::Reg(idx as u8), 354 | detail: OpDetail::VtblIdx { 355 | esize: esize as u8, 356 | elem: elem as u8, 357 | cnt: cnt as u8, 358 | }, 359 | } 360 | } 361 | 362 | pub fn opmemuoff(idx: u32, off: u32) -> Op { 363 | Op { 364 | op_type: OpType::MemUoff, 365 | value: OpValue::Reg(idx as u8), 366 | detail: OpDetail::Uimm16(off as _), 367 | } 368 | } 369 | 370 | pub fn opmemsoff(idx: u32, off: i32) -> Op { 371 | Op { 372 | op_type: OpType::MemSoff, 373 | value: OpValue::Reg(idx as u8), 374 | detail: OpDetail::Simm16(off as _), 375 | } 376 | } 377 | 378 | pub fn opmemsoffpre(idx: u32, off: i32) -> Op { 379 | Op { 380 | op_type: OpType::MemSoffPre, 381 | value: OpValue::Reg(idx as u8), 382 | detail: OpDetail::Simm16(off as _), 383 | } 384 | } 385 | 386 | pub fn opmemsoffpost(idx: u32, off: i32) -> Op { 387 | Op { 388 | op_type: OpType::MemSoffPost, 389 | value: OpValue::Reg(idx as u8), 390 | detail: OpDetail::Simm16(off as _), 391 | } 392 | } 393 | 394 | pub fn opmemreg(idx: u32, offreg: u32, ext: u32, scale: u32, shift: u32) -> Op { 395 | Op { 396 | op_type: OpType::MemReg, 397 | value: OpValue::Reg(idx as u8), 398 | detail: OpDetail::MemReg { 399 | sc: scale, 400 | ext: Ext::try_from(ext as u8).unwrap(), 401 | shift: shift as u8, 402 | offreg: offreg as u8, 403 | }, 404 | } 405 | } 406 | 407 | pub fn opmemregsimdpost(idx: u32, offreg: u32, constoff: u32) -> Op { 408 | if offreg == 31 { 409 | opmemsoffpost(idx, constoff as _) 410 | } else { 411 | Op { 412 | op_type: OpType::MemRegPost, 413 | value: OpValue::Reg(idx as u8), 414 | detail: OpDetail::MemReg { 415 | sc: 0, 416 | ext: Ext::Uxtx, 417 | shift: 0, 418 | offreg: offreg as u8, 419 | }, 420 | } 421 | } 422 | } 423 | 424 | pub fn opmeminc(idx: u32) -> Op { 425 | Op { 426 | op_type: OpType::MemInc, 427 | value: OpValue::Reg(idx as u8), 428 | detail: OpDetail::Uimm16(0), 429 | } 430 | } 431 | 432 | pub fn opimmsmall(imm6: u32) -> Op { 433 | Op { 434 | op_type: OpType::ImmSmall, 435 | value: OpValue::ImmShift { 436 | mask: false, 437 | shift: 0, 438 | }, 439 | detail: OpDetail::Uimm16(imm6 as u16), 440 | } 441 | } 442 | 443 | pub fn opsimm(imm: i32) -> Op { 444 | Op { 445 | op_type: OpType::Simm, 446 | value: OpValue::ImmShift { 447 | mask: false, 448 | shift: 0, 449 | }, 450 | detail: OpDetail::Simm16(imm as _), 451 | } 452 | } 453 | 454 | pub fn opuimm(imm: u32) -> Op { 455 | Op { 456 | op_type: OpType::Uimm, 457 | value: OpValue::ImmShift { 458 | mask: false, 459 | shift: 0, 460 | }, 461 | detail: OpDetail::Uimm16(imm as _), 462 | } 463 | } 464 | 465 | pub fn opuimmshift(imm: u32, msl: u32, shift: u32) -> Op { 466 | Op { 467 | op_type: OpType::UimmShift, 468 | value: OpValue::ImmShift { 469 | mask: msl != 0, 470 | shift: shift as u8, 471 | }, 472 | detail: OpDetail::Uimm16(imm as _), 473 | } 474 | } 475 | 476 | pub fn opreladdr(ddi: &mut Inst, imm: i32) -> Op { 477 | ddi.imm = Imm { 478 | imm64: imm as i32 as i64 as u64, 479 | }; 480 | Op { 481 | op_type: OpType::ImmLarge, 482 | value: OpValue::ImmShift { 483 | mask: false, 484 | shift: 0, 485 | }, 486 | detail: OpDetail::Uimm16(0), 487 | } 488 | } 489 | 490 | pub fn opimmlogical(ddi: &mut Inst, sf: u32, n: u32, immr: u32, imms: u32) -> Op { 491 | ddi.imm = Imm { 492 | imm64: immlogical(sf, n, immr, imms) as u64, 493 | }; 494 | Op { 495 | op_type: OpType::ImmLarge, 496 | value: OpValue::ImmShift { 497 | mask: false, 498 | shift: 0, 499 | }, 500 | detail: OpDetail::Uimm16(sf as u16), 501 | } 502 | } 503 | 504 | pub fn opimmsimdmask(ddi: &mut Inst, imm8: u32) -> Op { 505 | let mut res = 0u64; 506 | for i in 0..8 { 507 | if (imm8 & (1 << i)) != 0 { 508 | res |= 0xff << (i * 8); 509 | } 510 | } 511 | ddi.imm = Imm { imm64: res }; 512 | Op { 513 | op_type: OpType::ImmLarge, 514 | value: OpValue::ImmShift { 515 | mask: false, 516 | shift: 0, 517 | }, 518 | detail: OpDetail::Uimm16(1), 519 | } 520 | } 521 | 522 | pub fn opimmfloatzero(ddi: &mut Inst) -> Op { 523 | ddi.imm = Imm { float8: 0.0 }; 524 | Op { 525 | op_type: OpType::ImmFloat, 526 | value: OpValue::ImmShift { 527 | mask: false, 528 | shift: 0, 529 | }, 530 | detail: OpDetail::Uimm16(0x100), 531 | } 532 | } 533 | 534 | pub fn opimmfloat(ddi: &mut Inst, imm8: u32) -> Op { 535 | let res = (imm8 as u32 & 0x80) << 24 536 | | if (imm8 & 0x40) != 0 { 537 | 0x3e000000 538 | } else { 539 | 0x40000000 540 | } 541 | | ((imm8 & 0x3f) as u32) << 19; 542 | ddi.imm = Imm { 543 | float8: f64::from_bits(res as u64), 544 | }; 545 | Op { 546 | op_type: OpType::ImmFloat, 547 | value: OpValue::ImmShift { 548 | mask: false, 549 | shift: 0, 550 | }, 551 | detail: OpDetail::Uimm16(imm8 as u16), 552 | } 553 | } 554 | 555 | pub fn opsysreg(reg: u32) -> Op { 556 | Op { 557 | op_type: OpType::Sysreg, 558 | value: OpValue::ImmShift { 559 | mask: false, 560 | shift: 0, 561 | }, 562 | detail: OpDetail::Sysreg(reg as u16), 563 | } 564 | } 565 | 566 | pub fn opcond(cond: u32) -> Op { 567 | Op { 568 | op_type: OpType::Cond, 569 | value: OpValue::ImmShift { 570 | mask: false, 571 | shift: 0, 572 | }, 573 | detail: OpDetail::Cond(Cond::try_from(cond as u8).unwrap()), 574 | } 575 | } 576 | 577 | include!("classifier.rs"); 578 | -------------------------------------------------------------------------------- /src/aarch64/inst_info.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/aarch64/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_upper_case_globals)] 2 | pub mod assembler; 3 | pub mod emitter; 4 | pub mod inst_info; 5 | pub mod opcodes; 6 | pub mod decoder; 7 | 8 | pub use assembler::*; 9 | pub use emitter::*; -------------------------------------------------------------------------------- /src/core/arch_traits.rs: -------------------------------------------------------------------------------- 1 | use crate::x86::arch_traits::{X64_ARCH_TRAITS, X86_ARCH_TRAITS}; 2 | 3 | use super::operand::{OperandSignature, RegGroup, RegType}; 4 | use super::types::TypeId; 5 | 6 | pub struct ArchTraits { 7 | pub sp_reg_id: u8, 8 | pub fp_reg_id: u8, 9 | pub link_reg_id: u8, 10 | pub ip_reg_id: u8, 11 | pub hw_stack_alignment: u8, 12 | pub min_stack_offset: u32, 13 | pub max_stack_offset: u32, 14 | pub regs_signature: [OperandSignature; RegType::MaxValue as usize + 1], 15 | pub reg_type_to_type_id: [TypeId; RegType::MaxValue as usize + 1], 16 | pub type_id_to_reg_type: [RegType; 32], 17 | } 18 | 19 | pub const ARCH_X86: usize = if cfg!(any(target_arch = "x86", target_arch = "x86_64")) { 20 | size_of::() * 8 21 | } else { 22 | 0 23 | }; 24 | 25 | pub const ARCH_ARM: usize = if cfg!(any(target_arch = "aarch64", target_arch = "arm")) { 26 | size_of::() * 8 27 | } else { 28 | 0 29 | }; 30 | pub const ARCH_RISCV: usize = if cfg!(any(target_arch = "riscv64", target_arch = "riscv32")) { 31 | size_of::() * 8 32 | } else { 33 | 0 34 | }; 35 | 36 | /// Instruction set architecture (ISA). 37 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 38 | pub enum Arch { 39 | /// Unknown or uninitialized ISA. 40 | Unknown = 0, 41 | 42 | /// 32-bit X86 ISA. 43 | X86 = 1, 44 | /// 64-bit X86 ISA alsonown as X64, X86_64, and AMD64. 45 | X64 = 2, 46 | 47 | /// 32-bit RISC-V ISA. 48 | RISCV32 = 3, 49 | /// 64-bit RISC-V ISA. 50 | RISCV64 = 4, 51 | 52 | /// 32-bit ARM ISA (little endian). 53 | ARM = 5, 54 | /// 64-bit ARM ISA in (little endian). 55 | AArch64 = 6, 56 | /// 32-bit ARM ISA in Thumb mode (little endian). 57 | Thumb = 7, 58 | 59 | // 8 is not used at the moment, even numbers are 64-bit architectures. 60 | /// 32-bit MIPS ISA in (little endian). 61 | MIPS32LE = 9, 62 | /// 64-bit MIPS ISA in (little endian). 63 | MIPS64LE = 10, 64 | 65 | /// 32-bit ARM ISA (big endian). 66 | ARMBE = 11, 67 | /// 64-bit ARM ISA in (big endian). 68 | AArch64BE = 12, 69 | /// 32-bit ARM ISA in Thumb mode (big endian). 70 | ThumbBE = 13, 71 | 72 | // 14 is not used at the moment, even numbers are 64-bit architectures. 73 | /// 32-bit MIPS ISA in (big endian). 74 | MIPS32BE = 15, 75 | /// 64-bit MIPS ISA in (big endian). 76 | MIPS64BE = 16, 77 | 78 | Max, 79 | } 80 | 81 | impl Arch { 82 | pub const HOST: Arch = { 83 | if ARCH_X86 == 32 { 84 | Arch::X86 85 | } else if ARCH_X86 == 64 { 86 | Arch::X64 87 | } else if ARCH_RISCV == 32 { 88 | Arch::RISCV32 89 | } else if ARCH_RISCV == 64 { 90 | Arch::RISCV64 91 | } else if ARCH_ARM == 32 && cfg!(target_endian = "little") { 92 | Arch::ARM 93 | } else if ARCH_ARM == 32 && cfg!(target_endian = "big") { 94 | Arch::ARMBE 95 | } else if ARCH_ARM == 64 && cfg!(target_endian = "little") { 96 | Arch::AArch64 97 | } else if ARCH_ARM == 64 && cfg!(target_endian = "big") { 98 | Arch::AArch64BE 99 | } else { 100 | Arch::Unknown 101 | } 102 | }; 103 | } 104 | 105 | impl Default for Arch { 106 | fn default() -> Self { 107 | Self::HOST 108 | } 109 | } 110 | 111 | const NO_ARCH_TRAITS: ArchTraits = ArchTraits { 112 | fp_reg_id: 0xff, 113 | sp_reg_id: 0xff, 114 | link_reg_id: 0xff, 115 | ip_reg_id: 0xff, 116 | hw_stack_alignment: 0, 117 | min_stack_offset: 0, 118 | max_stack_offset: 0, 119 | regs_signature: [OperandSignature::new(0); 32], 120 | reg_type_to_type_id: [TypeId::Void; 32], 121 | type_id_to_reg_type: [RegType::None; 32], 122 | }; 123 | 124 | #[rustfmt::skip] 125 | const ARCH_TRAITS: [ArchTraits; Arch::Max as usize] =[ 126 | NO_ARCH_TRAITS, 127 | X86_ARCH_TRAITS, 128 | X64_ARCH_TRAITS, 129 | NO_ARCH_TRAITS, 130 | NO_ARCH_TRAITS, 131 | NO_ARCH_TRAITS, 132 | NO_ARCH_TRAITS, 133 | NO_ARCH_TRAITS, 134 | NO_ARCH_TRAITS, 135 | NO_ARCH_TRAITS, 136 | NO_ARCH_TRAITS, 137 | NO_ARCH_TRAITS, 138 | NO_ARCH_TRAITS, 139 | NO_ARCH_TRAITS, 140 | NO_ARCH_TRAITS, 141 | NO_ARCH_TRAITS, 142 | NO_ARCH_TRAITS, 143 | ]; 144 | 145 | impl ArchTraits { 146 | /// Returns stack pointer register id. 147 | pub const fn sp_reg_id(&self) -> u32 { 148 | self.sp_reg_id as _ 149 | } 150 | 151 | /// Returns stack frame register id. 152 | pub const fn fp_reg_id(&self) -> u32 { 153 | self.fp_reg_id as _ 154 | } 155 | 156 | /// Returns link register id, if the architecture provides it. 157 | pub const fn link_reg_id(&self) -> u32 { 158 | self.link_reg_id as _ 159 | } 160 | 161 | /// Returns instruction pointer register id, if the architecture provides it. 162 | pub const fn ip_reg_id(&self) -> u32 { 163 | self.ip_reg_id as _ 164 | } 165 | /// Returns a hardware stack alignment requirement. 166 | /// 167 | /// ## Note 168 | /// This is a hardware constraint. Architectures that don't constrain it would return the lowest alignment 169 | /// (1), however, some architectures may constrain the alignment, for example AArch64 requires 16-byte alignment. 170 | pub const fn hw_stack_alignment(&self) -> u32 { 171 | self.hw_stack_alignment as _ 172 | } 173 | 174 | /// Tests whether the architecture provides link register, which is used across function calls. If the link 175 | /// register is not provided then a function call pushes the return address on stack (X86/X64). 176 | pub const fn has_link_reg(&self) -> bool { 177 | self.link_reg_id != 0xff 178 | } 179 | 180 | /// Returns minimum addressable offset on stack guaranteed for all instructions. 181 | pub const fn min_stack_offset(&self) -> u32 { 182 | self.min_stack_offset 183 | } 184 | 185 | /// Returns maximum addressable offset on stack depending on specific instruction. 186 | pub const fn max_stack_offset(&self) -> u32 { 187 | self.max_stack_offset 188 | } 189 | 190 | pub const fn has_reg_type(&self, typ: RegType) -> bool { 191 | (typ as u32) <= RegType::MaxValue as u32 && self.regs_signature[typ as usize].is_valid() 192 | } 193 | 194 | pub const fn reg_type_to_signature(&self, typ: RegType) -> OperandSignature { 195 | self.regs_signature[typ as usize] 196 | } 197 | 198 | pub fn reg_type_to_group(&self, typ: RegType) -> RegGroup { 199 | self.reg_type_to_signature(typ).reg_group() 200 | } 201 | 202 | pub fn reg_type_to_size(&self, typ: RegType) -> u32 { 203 | self.reg_type_to_signature(typ).size() 204 | } 205 | 206 | pub const fn reg_type_to_type_id(&self, typ: RegType) -> TypeId { 207 | self.reg_type_to_type_id[typ as usize] 208 | } 209 | 210 | pub const fn by_arch(arch: Arch) -> &'static Self { 211 | &ARCH_TRAITS[arch as usize] 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /src/core/emitter.rs: -------------------------------------------------------------------------------- 1 | use super::operand::Operand; 2 | 3 | const NOREG: Operand = Operand::new(); 4 | 5 | pub trait Emitter { 6 | fn emit(&mut self, opcode: i64, op0: &Operand, op1: &Operand, op2: &Operand, op3: &Operand); 7 | 8 | fn emit_n(&mut self, opcode: i64, ops: &[&Operand]) { 9 | match ops { 10 | [op0] => self.emit(opcode, op0, &NOREG, &NOREG, &NOREG), 11 | 12 | [op0, op1] => self.emit(opcode, op0, op1, &NOREG, &NOREG), 13 | 14 | [op0, op1, op2] => self.emit(opcode, op0, op1, op2, &NOREG), 15 | [op0, op1, op2, op3] => self.emit(opcode, op0, op1, op2, op3), 16 | _ => unreachable!(), 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/core/formatter.rs: -------------------------------------------------------------------------------- 1 | //! Formatter core functionality 2 | 3 | 4 | /// An output for formatter. 5 | 6 | pub trait FormatterOutput { 7 | fn write_str(&mut self, s: &str); 8 | fn write_fmt(&mut self, args: core::fmt::Arguments<'_>); 9 | } 10 | 11 | impl FormatterOutput for String { 12 | fn write_str(&mut self, s: &str) { 13 | self.push_str(s); 14 | } 15 | 16 | fn write_fmt(&mut self, args: core::fmt::Arguments<'_>) { 17 | core::fmt::write(self, args).unwrap(); 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /src/core/globals.rs: -------------------------------------------------------------------------------- 1 | pub const ALLOC_OVERHEAD: u32 = core::mem::size_of::() as u32 * 4; 2 | pub const ALLOC_ALIGNMENT: u32 = 8; 3 | pub const GROW_THRESHOLD: u32 = 1024 * 1024 * 16; 4 | pub const MAX_TREE_HEIGHT: u32 = if cfg!(target_pointer_width = "32") { 5 | 30 6 | } else { 7 | 61 8 | } + 1; 9 | pub const MAX_OP_COUNT: usize = 6; 10 | pub const MAX_FUNC_ARGS: u32 = 32; 11 | pub const MAX_VALUE_PACK: u32 = 4; 12 | pub const MAX_PHYS_REGS: u32 = 32; 13 | pub const MAX_ALIGNMENT: u32 = 64; 14 | pub const MAX_LABEL_NAME_SIZE: u32 = 2048; 15 | pub const MAX_SECTION_NAME_SIZE: u32 = 35; 16 | pub const MAX_COMMENT_SIZE: u32 = 1024; 17 | pub const INVALID_ID: u32 = 0xFFFFFFFFu32; 18 | pub const NOT_FOUND: u32 = 0xFFFFFFFFu32; 19 | pub const NO_BASE_ADDRESS: u64 = !0u64; 20 | pub const NUM_VIRT_GROUPS: usize = 4; 21 | -------------------------------------------------------------------------------- /src/core/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod arch_traits; 2 | pub mod buffer; 3 | pub mod emitter; 4 | pub mod globals; 5 | pub mod jit_allocator; 6 | pub mod operand; 7 | pub mod sink; 8 | pub mod support; 9 | pub mod target; 10 | pub mod types; 11 | pub mod formatter; 12 | -------------------------------------------------------------------------------- /src/core/sink.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/core/support.rs: -------------------------------------------------------------------------------- 1 | pub const fn lsb_mask_usize(n: usize) -> usize { 2 | if n != 0 { 3 | usize::MAX >> (size_of::() * 8 - n) 4 | } else { 5 | 0 6 | } 7 | } 8 | 9 | pub const fn lsb_mask32(n: u32) -> usize { 10 | if size_of::() < size_of::() { 11 | (1 << n as usize) - 1 12 | } else { 13 | if n != 0 { 14 | u32::MAX as usize >> (size_of::() * 8 - n as usize) 15 | } else { 16 | 0 17 | } 18 | } 19 | } 20 | 21 | pub const fn lsb_mask64(n: u64) -> usize { 22 | if size_of::() < size_of::() { 23 | (1 << n as usize) - 1 24 | } else { 25 | if n != 0 { 26 | u64::MAX as usize >> (size_of::() * 8 - n as usize) 27 | } else { 28 | 0 29 | } 30 | } 31 | } 32 | 33 | pub fn bitmask_from_bool(src: bool) -> u32 { 34 | 0u32.wrapping_sub(src as u32) 35 | } 36 | 37 | pub fn align_up_usize(addr: usize, align: usize) -> usize { 38 | let mask = align - 1; 39 | (addr + mask) & !mask 40 | } 41 | 42 | pub fn is_between(x: T, a: T, b: T) -> bool 43 | where 44 | T: PartialOrd + Ord, 45 | { 46 | x >= a && x <= b 47 | } 48 | 49 | #[cfg(not(windows))] 50 | pub fn lookup_with_dlsym(name: &str) -> *const u8 { 51 | use alloc::ffi::CString; 52 | use core::ptr::null; 53 | 54 | let c_str = CString::new(name).unwrap(); 55 | let c_str_ptr = c_str.as_ptr(); 56 | let sym = unsafe { libc::dlsym(libc::RTLD_DEFAULT, c_str_ptr) }; 57 | if sym.is_null() { 58 | null() 59 | } else { 60 | sym as *const u8 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/core/target.rs: -------------------------------------------------------------------------------- 1 | use super::arch_traits::Arch; 2 | 3 | /// Object format. 4 | #[repr(u8)] 5 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 6 | pub enum ObjectFormat { 7 | /// Unknown or uninitialized object format. 8 | Unknown = 0, 9 | /// JIT code generation object. 10 | JIT, 11 | /// Executable and linkable format (ELF). 12 | ELF, 13 | /// Common object file format. 14 | COFF, 15 | /// Extended COFF object format. 16 | XCOFF, 17 | /// Mach object file format. 18 | MachO, 19 | /// Maximum value of `ObjectFormat`. 20 | MaxValue, 21 | } 22 | 23 | /// Platform ABI (application binary interface). 24 | #[repr(u8)] 25 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 26 | pub enum PlatformABI { 27 | /// Unknown or uninitialized environment. 28 | Unknown = 0, 29 | /// Microsoft ABI. 30 | MSVC, 31 | /// GNU ABI. 32 | GNU, 33 | /// Android Environment / ABI. 34 | Android, 35 | /// Cygwin ABI. 36 | Cygwin, 37 | /// Darwin ABI. 38 | Darwin, 39 | /// Maximum value of `PlatformABI`. 40 | MaxValue, 41 | } 42 | 43 | #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)] 44 | pub struct Environment { 45 | arch: Arch, 46 | is_pic: bool, 47 | } 48 | 49 | impl Environment { 50 | pub const fn new(arch: Arch) -> Self { 51 | Self { 52 | arch, 53 | is_pic: false, 54 | } 55 | } 56 | 57 | pub const fn host() -> Self { 58 | Self { 59 | arch: Arch::HOST, 60 | is_pic: false, 61 | } 62 | } 63 | 64 | /// Enable or disable PIC (Position independent code). 65 | /// 66 | /// If enabled code for calls and jumps to symbols will emit near relocations 67 | pub fn set_pic(&mut self, value: bool) { 68 | self.is_pic = value; 69 | } 70 | 71 | pub fn pic(&self) -> bool { 72 | self.is_pic 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/core/types.rs: -------------------------------------------------------------------------------- 1 | /// Type identifier provides a minimalist type system used across AsmJit library. 2 | /// 3 | /// This is an additional information that can be used to describe a value-type of physical or virtual register. It's 4 | /// used mostly by BaseCompiler to describe register representation (the group of data stored in the register and the 5 | /// width used) and it's also used by APIs that allow to describe and work with function signatures. 6 | #[repr(u8)] 7 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] 8 | pub enum TypeId { 9 | /// Void type. 10 | Void = 0, 11 | 12 | /// Abstract signed integer type that has a native size. 13 | IntPtr = 32, 14 | /// Abstract unsigned integer type that has a native size. 15 | UIntPtr = 33, 16 | 17 | /// 8-bit signed integer type. 18 | Int8 = 34, 19 | /// 8-bit unsigned integer type. 20 | UInt8 = 35, 21 | /// 16-bit signed integer type. 22 | Int16 = 36, 23 | /// 16-bit unsigned integer type. 24 | UInt16 = 37, 25 | /// 32-bit signed integer type. 26 | Int32 = 38, 27 | /// 32-bit unsigned integer type. 28 | UInt32 = 39, 29 | /// 64-bit signed integer type. 30 | Int64 = 40, 31 | /// 64-bit unsigned integer type. 32 | UInt64 = 41, 33 | 34 | /// 32-bit floating point type. 35 | Float32 = 42, 36 | /// 64-bit floating point type. 37 | Float64 = 43, 38 | /// 80-bit floating point type. 39 | Float80 = 44, 40 | 41 | /// 8-bit opmask register (K). 42 | Mask8 = 45, 43 | /// 16-bit opmask register (K). 44 | Mask16 = 46, 45 | /// 32-bit opmask register (K). 46 | Mask32 = 47, 47 | /// 64-bit opmask register (K). 48 | Mask64 = 48, 49 | 50 | /// 64-bit MMX register only used for 32 bits. 51 | Mmx32 = 49, 52 | /// 64-bit MMX register. 53 | Mmx64 = 50, 54 | 55 | Int8x4 = 51, 56 | UInt8x4 = 52, 57 | Int16x2 = 53, 58 | UInt16x2 = 54, 59 | Int32x1 = 55, 60 | UInt32x1 = 56, 61 | Float32x1 = 59, 62 | 63 | Int8x8 = 61, 64 | UInt8x8 = 62, 65 | Int16x4 = 63, 66 | UInt16x4 = 64, 67 | Int32x2 = 65, 68 | UInt32x2 = 66, 69 | Int64x1 = 67, 70 | UInt64x1 = 68, 71 | Float32x2 = 69, 72 | Float64x1 = 70, 73 | 74 | Int8x16 = 71, 75 | UInt8x16 = 72, 76 | Int16x8 = 73, 77 | UInt16x8 = 74, 78 | Int32x4 = 75, 79 | UInt32x4 = 76, 80 | Int64x2 = 77, 81 | UInt64x2 = 78, 82 | Float32x4 = 79, 83 | Float64x2 = 80, 84 | 85 | Int8x32 = 81, 86 | UInt8x32 = 82, 87 | Int16x16 = 83, 88 | UInt16x16 = 84, 89 | Int32x8 = 85, 90 | UInt32x8 = 86, 91 | Int64x4 = 87, 92 | UInt64x4 = 88, 93 | Float32x8 = 89, 94 | Float64x4 = 90, 95 | 96 | Int8x64 = 91, 97 | UInt8x64 = 92, 98 | Int16x32 = 93, 99 | UInt16x32 = 94, 100 | Int32x16 = 95, 101 | UInt32x16 = 96, 102 | Int64x8 = 97, 103 | UInt64x8 = 98, 104 | Float32x16 = 99, 105 | Float64x8 = 100, 106 | 107 | MaxValue = 255, 108 | } 109 | 110 | impl TypeId { 111 | pub const VEC128_END: u32 = 80; 112 | pub const VEC128_START: u32 = 71; 113 | 114 | pub const VEC256_END: u32 = 90; 115 | pub const VEC256_START: u32 = 81; 116 | 117 | pub const VEC512_END: u32 = 100; 118 | pub const VEC512_START: u32 = 91; 119 | 120 | pub const VEC32_START: u32 = 51; 121 | pub const VEC32_END: u32 = 60; 122 | pub const VEC64_START: u32 = 61; 123 | pub const VEC64_END: u32 = 70; 124 | 125 | pub const MMX_START: u32 = 49; 126 | pub const MMX_END: u32 = 50; 127 | pub const MASK_START: u32 = 45; 128 | pub const MASK_END: u32 = 48; 129 | 130 | pub const FLOAT_START: u32 = 42; 131 | pub const FLOAT_END: u32 = 44; 132 | 133 | pub const INT_START: u32 = 32; 134 | pub const INT_END: u32 = 41; 135 | 136 | pub fn size(&self) -> usize { 137 | match self { 138 | Self::UIntPtr | Self::IntPtr => size_of::(), 139 | Self::Int8 | Self::UInt8 => 1, 140 | Self::Int16 | Self::UInt16 => 2, 141 | Self::Int32 | Self::UInt32 | Self::Float32 => 4, 142 | Self::Int64 | Self::UInt64 | Self::Float64 => 8, 143 | Self::Float80 => 10, 144 | Self::Mask8 => 1, 145 | Self::Mask16 => 2, 146 | Self::Mask32 => 4, 147 | Self::Mask64 => 8, 148 | Self::Mmx32 | Self::Mmx64 => 8, 149 | Self::Int8x4 150 | | Self::UInt8x4 151 | | Self::Int16x2 152 | | Self::UInt16x2 153 | | Self::Int32x1 154 | | Self::UInt32x1 155 | | Self::Float32x1 => 4, 156 | Self::Int8x8 157 | | Self::UInt8x8 158 | | Self::Int16x4 159 | | Self::UInt16x4 160 | | Self::Int32x2 161 | | Self::UInt32x2 162 | | Self::Int64x1 163 | | Self::UInt64x1 164 | | Self::Float32x2 165 | | Self::Float64x1 => 8, 166 | Self::Int8x16 167 | | Self::UInt8x16 168 | | Self::Int16x8 169 | | Self::UInt16x8 170 | | Self::Int32x4 171 | | Self::UInt32x4 172 | | Self::Int64x2 173 | | Self::UInt64x2 174 | | Self::Float32x4 175 | | Self::Float64x2 => 16, 176 | Self::Int8x32 177 | | Self::UInt8x32 178 | | Self::Int16x16 179 | | Self::UInt16x16 180 | | Self::Int32x8 181 | | Self::UInt32x8 182 | | Self::Int64x4 183 | | Self::UInt64x4 184 | | Self::Float32x8 185 | | Self::Float64x4 => 32, 186 | Self::Int8x64 187 | | Self::UInt8x64 188 | | Self::Int16x32 189 | | Self::UInt16x32 190 | | Self::Int32x16 191 | | Self::UInt32x16 192 | | Self::Int64x8 193 | | Self::UInt64x8 194 | | Self::Float32x16 195 | | Self::Float64x8 => 64, 196 | _ => 0, 197 | } 198 | } 199 | 200 | pub fn deabstract(&self, register_size: usize) -> Self { 201 | match self { 202 | Self::IntPtr => { 203 | if register_size == 8 { 204 | Self::Int64 205 | } else { 206 | Self::Int32 207 | } 208 | } 209 | Self::UIntPtr => { 210 | if register_size == 8 { 211 | Self::UInt64 212 | } else { 213 | Self::UInt32 214 | } 215 | } 216 | _ => *self, 217 | } 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # asmkit 2 | //! 3 | //! ### Overview 4 | //! asmkit is a portable assembler toolkit designed for decoding and encoding various assembly architectures. It aims to provide a small, efficient, and cross-platform library that can be used to build and manipulate assembly code without being tied to a specific platform or architecture. The library is written in Rust and supports several architectures including X64, RISC-V, and ARM (work-in-progress). Key features include: 5 | //! - **Multi-Architecture Support**: Supports multiple architectures such as X64, RISC-V, ARM (WIP), PPC (WIP), and plans to support PPC64 and OpenPOWER in the future. 6 | //! - **Minimal Dependencies**: Relies on a minimal set of dependencies to ensure portability and efficiency: 7 | //! - - `libc` and `intrusive-collections`` - For JIT support. 8 | //! - - `paste` and `derive-more` - Utility crates that simplify repetitive code. 9 | //! - - `smallvec` - A crate used to manage collections that avoid too frequent heap allocations during code generation. 10 | //! 11 | //! - **Code Relocations**: Provides a CodeBuffer interface to handle relocations, allowing the insertion of symbols into the API seamlessly. 12 | //! - **Auto-Generated Assemblers**: The goal is to support a wide range of platforms and provide auto-generated assemblers for as many architectures as possible. 13 | //! - **Portability**: Built to run on any platform, with the architecture-specific parts of the library being independent of the platform on which asmkit is built. 14 | //! #![no_std] 15 | //! 16 | //! 17 | //! ### Usage 18 | //! 19 | //! To use the library simply import a module for architecture you want to emit code for e.g `use asmkit::x86::*;`; This would 20 | //! include all the required code to generate code for platform. 21 | //! 22 | //! Example: 23 | //! 24 | //! ```rust 25 | //! use asmkit::core::buffer::CodeBuffer; 26 | //! use asmkit::core::jit_allocator::JitAllocator; 27 | //! use asmkit::x86::*; 28 | //! 29 | //! fn main() { 30 | //! let mut buf = CodeBuffer::new(); 31 | //! let mut asm = Assembler::new(&mut buf); 32 | //! 33 | //! let dst = RDI; 34 | //! let arg0 = RSI; 35 | //! let arg1 = RDX; 36 | //! 37 | //! asm.sse_movdqurm(XMM0, ptr64(arg0, 0)); // load 4 ints from [arg0] to XMM0 38 | //! asm.sse_movdqurm(XMM1, ptr64(arg1, 0)); // load 4 ints from [arg1] to XMM1 39 | //! asm.sse_paddwrr(XMM0, XMM1); // add 4 ints 40 | //! asm.sse_movdqumr(ptr64(dst, 0), XMM0); // store result in [dst] 41 | //! asm.ret(); // return from function 42 | //! 43 | //! let result = buf.finish(); 44 | //! let mut jit = JitAllocator::new(Default::default()); 45 | //! // you can also use jit.alloc + jit.write manually. 46 | //! let span = result 47 | //! .allocate(&mut jit) 48 | //! .expect("failed to allocate JIT-code"); 49 | //! 50 | //! // JIT Allocator uses dual-mapping: it allocates two pages which map to same physical space 51 | //! // and you write to executable code through `span.rw()` pointer while you can execute `span.rx()`. 52 | //! let f: extern "C" fn(*mut i32, *const i32, *const i32) = unsafe { std::mem::transmute(span.rx()) }; 53 | //! #[cfg(all(unix, target_arch="x86_64"))] // can run only on x64 and on SystemV platforms. 54 | //! { 55 | //! let mut res = [0; 4]; 56 | //! f(res.as_mut_ptr(), [4, 3, 2, 1].as_ptr(), [1, 5, 2, 8].as_ptr()); 57 | //! 58 | //! println!("{:?}", res); 59 | //! } 60 | //! } 61 | 62 | //!``` 63 | 64 | extern crate alloc; 65 | 66 | pub mod core; 67 | pub mod aarch64; 68 | pub mod ppc; 69 | pub mod riscv; 70 | pub mod util; 71 | pub mod x86; 72 | 73 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] 74 | pub enum AsmError { 75 | InvalidPrefix, 76 | InvalidOperand, 77 | InvalidInstruction, 78 | OutOfMemory, 79 | InvalidState, 80 | TooManyHandles, 81 | InvalidArgument, 82 | FailedToOpenAnonymousMemory, 83 | TooLarge, 84 | } 85 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() {} -------------------------------------------------------------------------------- /src/ppc/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/riscv/decode.rs: -------------------------------------------------------------------------------- 1 | use opcodes::{ALL_OPCODES, OPCODE32_MASK, OPCODE32_MATCH, OPCODE64_MASK, OPCODE64_MATCH, SHORT_OPCODES}; 2 | 3 | use super::*; 4 | pub struct Decoder<'a> { 5 | buf: &'a [u8], 6 | cursor: usize, 7 | address: u64, 8 | mode: Mode, 9 | } 10 | 11 | #[derive(Copy, Clone, PartialEq, Eq)] 12 | enum Mode { 13 | Decode32, 14 | Decode64, 15 | } 16 | 17 | impl Mode { 18 | pub fn is_64(self) -> bool { 19 | self == Self::Decode64 20 | } 21 | } 22 | 23 | impl TryFrom for Mode { 24 | type Error = (); 25 | 26 | fn try_from(value: usize) -> Result { 27 | match value { 28 | 32 => Ok(Self::Decode32), 29 | 64 => Ok(Self::Decode64), 30 | _ => Err(()), 31 | } 32 | } 33 | } 34 | pub struct Instruction { 35 | pub code: Opcode, 36 | pub value: InstructionValue, 37 | pub len: usize, 38 | pub address: u64, 39 | } 40 | 41 | impl Default for Instruction { 42 | fn default() -> Self { 43 | Self { 44 | len: 0, 45 | code: Opcode::Invalid, 46 | value: InstructionValue::new(0), 47 | address: 0, 48 | } 49 | } 50 | } 51 | 52 | /// Return the length (in bytes) of an instruction given the low 16 bits of it. 53 | /// 54 | /// The current spec reserves a bit pattern for instructions of length >= 192 bits, but for 55 | /// simplicity this function just returns 24 in that case. The largest instructions currently 56 | /// defined are 4 bytes so it will likely be a long time until this diffence matters. 57 | fn instruction_length(i: u16) -> usize { 58 | if i & 0b11 != 0b11 { 59 | 2 60 | } else if i & 0b11100 != 0b11100 { 61 | 4 62 | } else if i & 0b111111 == 0b011111 { 63 | 6 64 | } else if i & 0b1111111 == 0b011111 { 65 | 8 66 | } else { 67 | 10 + 2 * ((i >> 12) & 0b111) as usize 68 | } 69 | } 70 | 71 | impl<'a> Decoder<'a> { 72 | pub fn new(bitness: usize, buf: &'a [u8], address: u64) -> Self { 73 | Self { 74 | buf, 75 | cursor: 0, 76 | address, 77 | mode: Mode::try_from(bitness).expect("only 32 and 64 bit bitness supported"), 78 | } 79 | } 80 | 81 | pub fn can_decode(&self) -> bool { 82 | self.cursor < self.buf.len() 83 | } 84 | 85 | fn read_u8(&mut self) -> u8 { 86 | let val = self.buf[self.cursor]; 87 | self.cursor += 1; 88 | val 89 | } 90 | 91 | pub fn decode(&mut self) -> Instruction { 92 | let mut inst = Instruction::default(); 93 | self.decode_out(&mut inst); 94 | inst 95 | } 96 | 97 | pub fn decode_out(&mut self, inst: &mut Instruction) { 98 | if self.cursor >= self.buf.len() { 99 | return; 100 | } 101 | 102 | let start = self.cursor; 103 | 104 | let b0 = self.read_u8(); 105 | let b1 = self.read_u8(); 106 | 107 | let short = u16::from_le_bytes([b0, b1]); 108 | let len = instruction_length(short); 109 | let mut val = InstructionValue::new(short as _); 110 | let masks = if self.mode.is_64() { 111 | &OPCODE64_MASK 112 | } else { 113 | &OPCODE32_MASK 114 | }; 115 | 116 | let matches = if self.mode.is_64() { 117 | &OPCODE64_MATCH 118 | } else { 119 | &OPCODE32_MATCH 120 | }; 121 | let mut opc = None; 122 | 123 | if len == 2 { 124 | let mut zero_mask = None; 125 | 126 | for &op in SHORT_OPCODES.iter() { 127 | let mask = masks[op as usize]; 128 | let match_ = matches[op as usize]; 129 | if short as u32 & mask == match_ { 130 | if mask == 0 { 131 | zero_mask = Some(op); 132 | continue; 133 | } 134 | 135 | opc = Some(op); 136 | break; 137 | } 138 | } 139 | 140 | if opc.is_none() { 141 | opc = zero_mask; 142 | } 143 | } else if len == 4 { 144 | let b2 = self.read_u8(); 145 | let b3 = self.read_u8(); 146 | let long = u32::from_le_bytes([b0, b1, b2, b3]); 147 | for &op in ALL_OPCODES.iter() { 148 | let mask = masks[op as usize]; 149 | let match_ = matches[op as usize]; 150 | if long & mask == match_ { 151 | opc = Some(op); 152 | val = InstructionValue::new(long); 153 | break; 154 | } 155 | } 156 | } else { 157 | self.cursor = start + len; 158 | return; 159 | } 160 | 161 | let opc = opc.unwrap_or(Opcode::Invalid); 162 | inst.code = opc; 163 | inst.value = val; 164 | inst.len = len; 165 | inst.address = self.address + start as u64; 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/riscv/formatter.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{self, Write}; 2 | 3 | use super::{ 4 | decode::{Decoder, Instruction}, 5 | opcodes::Encoding, 6 | opcodes::OPCODE_STR, 7 | Opcode, 8 | }; 9 | 10 | pub struct Formatter { 11 | abi_names: bool, 12 | } 13 | 14 | impl Formatter { 15 | pub const fn new() -> Self { 16 | Self { abi_names: !true } 17 | } 18 | 19 | fn write_reg(&self, out: &mut W, f: bool, vec: bool, reg: u32) -> fmt::Result { 20 | if vec && self.abi_names { 21 | match reg { 22 | 0..=7 => write!(out, "v{}", reg), 23 | 8..=15 => write!(out, "vs{}", reg - 8), 24 | 16..=31 => write!(out, "v{}", reg), 25 | _ => write!(out, ""), 26 | }?; 27 | } else if vec { 28 | write!(out, "v{}", reg)?; 29 | } 30 | 31 | if f && self.abi_names { 32 | match reg { 33 | 0 => write!(out, "ft0"), 34 | 1 => write!(out, "ft1"), 35 | 2 => write!(out, "ft2"), 36 | 3 => write!(out, "ft3"), 37 | 4 => write!(out, "ft4"), 38 | 5 => write!(out, "ft5"), 39 | 6 => write!(out, "ft6"), 40 | 7 => write!(out, "ft7"), 41 | 8 => write!(out, "fs0"), 42 | 9 => write!(out, "fs1"), 43 | 10 => write!(out, "fa0"), 44 | 11 => write!(out, "fa1"), 45 | 12 => write!(out, "fa2"), 46 | 13 => write!(out, "fa3"), 47 | 14 => write!(out, "fa4"), 48 | 15 => write!(out, "fa5"), 49 | 16 => write!(out, "fa6"), 50 | 17 => write!(out, "fa7"), 51 | 18 => write!(out, "fs2"), 52 | 19 => write!(out, "fs3"), 53 | 20 => write!(out, "fs4"), 54 | 21 => write!(out, "fs5"), 55 | 22 => write!(out, "fs6"), 56 | 23 => write!(out, "fs7"), 57 | 24 => write!(out, "fs8"), 58 | 25 => write!(out, "fs9"), 59 | 26 => write!(out, "fs10"), 60 | 27 => write!(out, "fs11"), 61 | 28 => write!(out, "ft8"), 62 | 29 => write!(out, "ft9"), 63 | 30 => write!(out, "ft10"), 64 | 31 => write!(out, "ft11"), 65 | _ => write!(out, ""), 66 | } 67 | } else if f { 68 | write!(out, "f{}", reg) 69 | } else { 70 | if self.abi_names { 71 | match reg { 72 | 0 => write!(out, "zero"), 73 | 1 => write!(out, "ra"), 74 | 2 => write!(out, "sp"), 75 | 3 => write!(out, "gp"), 76 | 4 => write!(out, "tp"), 77 | 5 => write!(out, "t0"), 78 | 6 => write!(out, "t1"), 79 | 7 => write!(out, "t2"), 80 | 8 => write!(out, "s0"), 81 | 9 => write!(out, "s1"), 82 | 10 => write!(out, "a0"), 83 | 11 => write!(out, "a1"), 84 | 12 => write!(out, "a2"), 85 | 13 => write!(out, "a3"), 86 | 14 => write!(out, "a4"), 87 | 15 => write!(out, "a5"), 88 | 16 => write!(out, "a6"), 89 | 17 => write!(out, "a7"), 90 | 18 => write!(out, "s2"), 91 | 19 => write!(out, "s3"), 92 | 20 => write!(out, "s4"), 93 | 21 => write!(out, "s5"), 94 | 22 => write!(out, "s6"), 95 | 23 => write!(out, "s7"), 96 | 24 => write!(out, "s8"), 97 | 25 => write!(out, "s9"), 98 | 26 => write!(out, "s10"), 99 | 27 => write!(out, "s11"), 100 | 28 => write!(out, "t3"), 101 | 29 => write!(out, "t4"), 102 | 30 => write!(out, "t5"), 103 | 31 => write!(out, "t6"), 104 | _ => write!(out, ""), 105 | } 106 | } else { 107 | write!(out, "x{}", reg) 108 | } 109 | } 110 | } 111 | 112 | fn write_rlist(&self, out: &mut W, rlist: u32) -> fmt::Result { 113 | for i in [27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 9, 8, 1] { 114 | if rlist & (1 << i) != 0 { 115 | self.write_reg(out, false, false, i)?; 116 | write!(out, " ")?; 117 | } 118 | } 119 | 120 | Ok(()) 121 | } 122 | 123 | pub fn format(&self, out: &mut W, inst: &Instruction) -> fmt::Result { 124 | let encoding = inst.code.encoding(); 125 | let mnem = OPCODE_STR[inst.code as usize]; 126 | 127 | write!(out, "{}", mnem)?; 128 | 129 | match encoding { 130 | Encoding::Bimm12HiRs1Bimm12lo => { 131 | write!(out, " ")?; 132 | self.write_reg(out, false, false, inst.value.rs1())?; 133 | let addr = 134 | inst.address as isize as isize + inst.value.bimm12lohi() as isize; 135 | write!(out, ", {:x}", addr)?; 136 | } 137 | 138 | Encoding::Bimm12HiRs1Rs2Bimm12lo => { 139 | write!(out, " ")?; 140 | self.write_reg(out, false, false, inst.value.rs1())?; 141 | write!(out, ", ")?; 142 | self.write_reg(out, false, false, inst.value.rs2())?; 143 | let addr = 144 | inst.address as isize + inst.value.bimm12lohi() as isize; 145 | write!(out, ", {:x}", addr)?; 146 | } 147 | 148 | Encoding::Bimm12HiRs2Bimm12lo => { 149 | write!(out, " ")?; 150 | self.write_reg(out, false, false, inst.value.rs2())?; 151 | let addr = 152 | inst.address as isize + inst.len as isize + inst.value.bimm12lohi() as isize; 153 | write!(out, ", {:x}", addr)?; 154 | } 155 | 156 | Encoding::Bimm12HiRs2Rs1Bimm12lo => { 157 | write!(out, " ")?; 158 | self.write_reg(out, false, false, inst.value.rs2())?; 159 | write!(out, ", ")?; 160 | self.write_reg(out, false, false, inst.value.rs1())?; 161 | let addr = 162 | inst.address as isize + inst.len as isize + inst.value.bimm12lohi() as isize; 163 | write!(out, ", {:x}", addr)?; 164 | } 165 | 166 | Encoding::CImm12 => { 167 | write!(out, " ")?; 168 | let addr = inst.address as i64 + inst.value.c_imm12() as i64; 169 | write!(out, "{:x}", addr)?; 170 | } 171 | 172 | Encoding::CIndex => { 173 | write!(out, " ")?; 174 | write!(out, "{}", inst.value.c_index())?; 175 | } 176 | 177 | Encoding::CMopT => { 178 | write!(out, " ")?; 179 | write!(out, "{}", inst.value.c_mop_t())?; 180 | } 181 | 182 | Encoding::CNzimm10hiCNzimm10lo => { 183 | write!(out, " ")?; 184 | write!(out, "{}", inst.value.c_nzimm10lohi())?; 185 | } 186 | 187 | Encoding::CNzimm6hiCNzimm6lo => { 188 | write!(out, " ")?; 189 | write!(out, "{}", inst.value.c_nzimm6lohi())?; 190 | } 191 | 192 | Encoding::CRlistCSpimm => { 193 | write!(out, " ")?; 194 | let rlist = inst.value.c_rlist(); 195 | self.write_rlist(out, rlist)?; 196 | write!(out, " {}", inst.value.c_spimm())?; 197 | } 198 | 199 | Encoding::CRs1N0 => { 200 | write!(out, " ")?; 201 | self.write_reg(out, false, false, inst.value.c_rs1_n0())?; 202 | } 203 | 204 | Encoding::CRs2CUimm8spS => { 205 | write!(out, " ")?; 206 | self.write_reg(out, false, false, inst.value.c_rs2())?; 207 | write!(out, ", ")?; 208 | write!(out, "{}", inst.value.c_uimm8sp_s())?; 209 | } 210 | 211 | Encoding::CRs2CUimm9spS => { 212 | write!(out, " ")?; 213 | self.write_reg(out, false, false, inst.value.c_rs2())?; 214 | write!(out, ", ")?; 215 | write!(out, "{}", inst.value.c_uimm9sp_s())?; 216 | } 217 | 218 | Encoding::CSreg1CSreg2 => { 219 | // TODO: Broken, implement sreg decoding/encoding 220 | write!(out, " ")?; 221 | self.write_reg(out, false, false, inst.value.c_sreg1())?; 222 | write!(out, ", ")?; 223 | self.write_reg(out, false, false, inst.value.c_sreg2())?; 224 | } 225 | 226 | Encoding::CsrZimm => { 227 | let zimm = inst.value.zimm(); 228 | write!(out, " ")?; 229 | write!(out, "csr{}, {}", inst.value.csr(), zimm)?; 230 | } 231 | 232 | Encoding::Empty => {} 233 | Encoding::FmPredSuccRs1Rd => { 234 | write!(out, " ")?; 235 | self.write_reg(out, false, false, inst.value.rd())?; 236 | write!(out, ", ")?; 237 | self.write_reg(out, false, false, inst.value.rs1())?; 238 | write!(out, ", ")?; 239 | write!(out, "{}", inst.value.pred())?; 240 | write!(out, ", ")?; 241 | write!(out, "{}", inst.value.succ())?; 242 | } 243 | 244 | // rs2, offset(rs1) 245 | Encoding::Imm12HiRs1Rs2Imm12lo => { 246 | write!(out, " ")?; 247 | let f = matches!( 248 | inst.code, 249 | Opcode::FSD | Opcode::FSH | Opcode::FSQ | Opcode::FSW 250 | ); 251 | self.write_reg(out, f, false, inst.value.rs2())?; 252 | 253 | write!(out, ", {}(", inst.value.imm12lohi())?; 254 | self.write_reg(out, false, false, inst.value.rs1())?; 255 | write!(out, ")")?; 256 | } 257 | 258 | Encoding::Imm12Rs1Rd => { 259 | write!(out, " ")?; 260 | self.write_reg(out, false, false, inst.value.rd())?; 261 | write!(out, ", ")?; 262 | self.write_reg(out, false, false, inst.value.rs1())?; 263 | write!(out, ", ")?; 264 | write!(out, "{}", inst.value.imm12())?; 265 | } 266 | 267 | Encoding::Jimm20 => { 268 | write!(out, " ")?; 269 | let addr = inst.address as i64 + inst.value.jimm20() as i64; 270 | write!(out, "{:x}", addr)?; 271 | } 272 | Encoding::RdJimm20 => { 273 | write!(out, " ")?; 274 | self.write_reg(out, false, false, inst.value.rd())?; 275 | write!(out, ", ")?; 276 | let addr = inst.address as i64 + inst.value.jimm20() as i64; 277 | write!(out, "{:x}", addr)?; 278 | } 279 | Encoding::MopRT30MopRT2726MopRT2120RdRs1 => { 280 | write!(out, " ")?; 281 | self.write_reg(out, false, false, inst.value.rd())?; 282 | write!(out, ", ")?; 283 | self.write_reg(out, false, false, inst.value.rs1())?; 284 | } 285 | 286 | Encoding::MopRrT30MopRrT2726RdRs1Rs2 => { 287 | todo!() 288 | } 289 | 290 | Encoding::NfVmRs1Vd => { 291 | let vd = inst.value.vd(); 292 | let vm = inst.value.vm(); 293 | let rs1 = inst.value.rs1(); 294 | 295 | write!(out, " ")?; 296 | self.write_reg(out, false, true, vd)?; 297 | write!(out, ", (")?; 298 | self.write_reg(out, false, false, rs1)?; 299 | write!(out, ")")?; 300 | if vm == 0 { 301 | write!(out, ", ")?; 302 | write!(out, "v{}.t", vm)?; 303 | } 304 | } 305 | 306 | Encoding::NfVmRs1Vs3 => { 307 | let vs3 = inst.value.vs3(); 308 | let vm = inst.value.vm(); 309 | let rs1 = inst.value.rs1(); 310 | 311 | write!(out, " ")?; 312 | self.write_reg(out, false, true, vs3)?; 313 | write!(out, ", (")?; 314 | self.write_reg(out, false, false, rs1)?; 315 | write!(out, ")")?; 316 | if vm == 0 { 317 | write!(out, ", ")?; 318 | write!(out, "v{}.t", vm)?; 319 | } 320 | } 321 | 322 | // vd, (rs1), rs2, vm 323 | Encoding::NfVmRs2Rs1Vd => { 324 | let vd = inst.value.vd(); 325 | let vm = inst.value.vm(); 326 | let rs1 = inst.value.rs1(); 327 | let rs2 = inst.value.rs2(); 328 | 329 | write!(out, " ")?; 330 | self.write_reg(out, false, true, vd)?; 331 | write!(out, ", (")?; 332 | self.write_reg(out, false, false, rs1)?; 333 | write!(out, "), ")?; 334 | self.write_reg(out, false, false, rs2)?; 335 | if vm == 0 { 336 | write!(out, ", ")?; 337 | write!(out, "v{}.t", vm)?; 338 | } 339 | } 340 | 341 | // v3, (rs1), rs2, vm 342 | Encoding::NfVmRs2Rs1Vs3 => { 343 | let vs3 = inst.value.vs3(); 344 | let vm = inst.value.vm(); 345 | let rs1 = inst.value.rs1(); 346 | let rs2 = inst.value.rs2(); 347 | 348 | write!(out, " ")?; 349 | self.write_reg(out, false, true, vs3)?; 350 | write!(out, ", (")?; 351 | self.write_reg(out, false, false, rs1)?; 352 | write!(out, "), ")?; 353 | self.write_reg(out, false, false, rs2)?; 354 | if vm == 0 { 355 | write!(out, ", ")?; 356 | write!(out, "v{}.t", vm)?; 357 | } 358 | } 359 | 360 | // vd, (rs1), vs2 361 | Encoding::NfVmVs2Rs1Vd => { 362 | let vd = inst.value.vd(); 363 | let vm = inst.value.vm(); 364 | let rs1 = inst.value.rs1(); 365 | let vs2 = inst.value.vs2(); 366 | 367 | write!(out, " ")?; 368 | self.write_reg(out, false, true, vd)?; 369 | write!(out, ", (")?; 370 | self.write_reg(out, false, false, rs1)?; 371 | write!(out, "), ")?; 372 | self.write_reg(out, false, true, vs2)?; 373 | if vm == 0 { 374 | write!(out, ", ")?; 375 | write!(out, "v{}.t", vm)?; 376 | } 377 | } 378 | 379 | Encoding::NfVmVs2Rs1Vs3 => { 380 | let vs3 = inst.value.vs3(); 381 | let vm = inst.value.vm(); 382 | let rs1 = inst.value.rs1(); 383 | let vs2 = inst.value.vs2(); 384 | 385 | write!(out, " ")?; 386 | self.write_reg(out, false, true, vs3)?; 387 | write!(out, ", (")?; 388 | self.write_reg(out, false, false, rs1)?; 389 | write!(out, "), ")?; 390 | self.write_reg(out, false, true, vs2)?; 391 | if vm == 0 { 392 | write!(out, ", ")?; 393 | write!(out, "v{}.t", vm)?; 394 | } 395 | } 396 | 397 | Encoding::Rd => { 398 | write!(out, " ")?; 399 | self.write_reg(out, false, false, inst.value.rd())?; 400 | } 401 | 402 | Encoding::RdCUimm9sphiCUimm9splo => { 403 | write!(out, " ")?; 404 | self.write_reg(out, false, false, inst.value.rd())?; 405 | write!(out, ", ")?; 406 | write!(out, "{}", inst.value.c_uimm9splohi())?; 407 | } 408 | 409 | Encoding::RdCsrZimm => { 410 | write!(out, " ")?; 411 | self.write_reg(out, false, false, inst.value.rd())?; 412 | write!(out, ", ")?; 413 | write!(out, "csr{}, {}", inst.value.csr(), inst.value.zimm())?; 414 | } 415 | 416 | Encoding::RdImm20 => { 417 | write!(out, " ")?; 418 | self.write_reg(out, false, false, inst.value.rd())?; 419 | write!(out, ", ")?; 420 | write!(out, "{}", inst.value.imm20())?; 421 | } 422 | 423 | Encoding::RdN0CImm6loCImm6hi => { 424 | write!(out, " ")?; 425 | self.write_reg(out, false, false, inst.value.rd())?; 426 | write!(out, ", ")?; 427 | write!(out, "{}", inst.value.c_imm6lohi())?; 428 | } 429 | 430 | Encoding::RdN0CRs2N0 => { 431 | write!(out, " ")?; 432 | self.write_reg(out, false, false, inst.value.rd())?; 433 | write!(out, ", ")?; 434 | self.write_reg(out, false, false, inst.value.rs2())?; 435 | } 436 | 437 | Encoding::RdN0CUimm8sphiCUimm8splo => { 438 | write!(out, " ")?; 439 | self.write_reg(out, false, false, inst.value.rd())?; 440 | write!(out, ", ")?; 441 | write!(out, "{}", inst.value.c_uimm8splohi())?; 442 | } 443 | 444 | Encoding::RdN0CUimm9sphiCUimm9splo => { 445 | write!(out, " ")?; 446 | self.write_reg(out, false, false, inst.value.rd())?; 447 | write!(out, ", ")?; 448 | write!(out, "{}", inst.value.c_uimm9splohi())?; 449 | } 450 | 451 | Encoding::RdN2CNzimm18hiCNzimm18lo => { 452 | write!(out, " ")?; 453 | self.write_reg(out, false, false, inst.value.rd())?; 454 | write!(out, ", ")?; 455 | write!(out, "{}", inst.value.c_nzimm18lohi())?; 456 | } 457 | 458 | Encoding::RdPCNzuimm10 => { 459 | write!(out, " ")?; 460 | self.write_reg(out, false, false, inst.value.rd())?; 461 | write!(out, ", ")?; 462 | write!(out, "{}", inst.value.c_nzuimm10())?; 463 | } 464 | 465 | Encoding::RdPRs1PCUimm1 => { 466 | write!(out, " ")?; 467 | self.write_reg(out, false, false, inst.value.rd())?; 468 | write!(out, ", ")?; 469 | self.write_reg(out, false, false, inst.value.rs1())?; 470 | write!(out, ", ")?; 471 | write!(out, "{}", inst.value.c_uimm1())?; 472 | } 473 | 474 | Encoding::RdPRs1PCUimm2 => { 475 | write!(out, " ")?; 476 | self.write_reg(out, false, false, inst.value.rd())?; 477 | write!(out, ", ")?; 478 | self.write_reg(out, false, false, inst.value.rs1())?; 479 | write!(out, ", ")?; 480 | write!(out, "{}", inst.value.c_uimm2())?; 481 | } 482 | 483 | Encoding::RdPRs1PCUimm7loCUimm7hi => { 484 | write!(out, " ")?; 485 | self.write_reg(out, false, false, inst.value.rd())?; 486 | write!(out, ", ")?; 487 | self.write_reg(out, false, false, inst.value.rs1())?; 488 | write!(out, ", {}", inst.value.c_uimm7lohi())?; 489 | } 490 | 491 | Encoding::RdPRs1PCUimm8loCUimm8hi => { 492 | write!(out, " ")?; 493 | self.write_reg(out, false, false, inst.value.rd_p() + 8)?; 494 | write!(out, ", ")?; 495 | 496 | self.write_reg( 497 | out, 498 | matches!(inst.code, Opcode::CFLD), 499 | false, 500 | inst.value.rs1_p() + 8, 501 | )?; 502 | write!(out, ", {}", inst.value.c_uimm8lohi())?; 503 | } 504 | 505 | Encoding::RdRs1 => match inst.code { 506 | Opcode::FLID | Opcode::FLIQ | Opcode::FLIH | Opcode::FLIS => { 507 | self.format_fli(out, inst)?; 508 | } 509 | 510 | Opcode::FMVDX | Opcode::FMVHX | Opcode::FMVPQX | Opcode::FMVSX => { 511 | write!(out, " ")?; 512 | self.write_reg(out, true, false, inst.value.rd())?; 513 | write!(out, ", ")?; 514 | self.write_reg(out, false, false, inst.value.rs1())?; 515 | } 516 | 517 | Opcode::FMVXD | Opcode::FMVXS | Opcode::FMVXH | Opcode::FMVXW => { 518 | write!(out, " ")?; 519 | self.write_reg(out, false, false, inst.value.rd())?; 520 | write!(out, ", ")?; 521 | self.write_reg(out, true, false, inst.value.rs1())?; 522 | } 523 | 524 | Opcode::FCLASSD | Opcode::FCLASSH | Opcode::FCLASSQ | Opcode::FCLASSS => { 525 | write!(out, " ")?; 526 | self.write_reg(out, false, false, inst.value.rd())?; 527 | write!(out, ", ")?; 528 | self.write_reg(out, true, false, inst.value.rs1())?; 529 | } 530 | 531 | _ => { 532 | write!(out, " ")?; 533 | self.write_reg(out, false, false, inst.value.rd())?; 534 | write!(out, ", ")?; 535 | self.write_reg(out, false, false, inst.value.rs1())?; 536 | } 537 | }, 538 | 539 | Encoding::RdRs1AqRl => { 540 | write!(out, " ")?; 541 | self.write_reg(out, false, false, inst.value.rd())?; 542 | write!(out, ", ")?; 543 | self.write_reg(out, false, false, inst.value.rs1())?; 544 | } 545 | // rd, csr, rs1 546 | Encoding::RdRs1Csr => { 547 | write!(out, " ")?; 548 | self.write_reg(out, false, false, inst.value.rd())?; 549 | write!(out, ", ")?; 550 | write!(out, "csr{}, ", inst.value.csr())?; 551 | self.write_reg(out, false, false, inst.value.rs1())?; 552 | } 553 | 554 | // rd, offset(rs1) 555 | Encoding::RdRs1Imm12 => { 556 | let is_float = matches!( 557 | inst.code, 558 | Opcode::FLD | Opcode::FLH | Opcode::FLQ | Opcode::FLW 559 | ); 560 | 561 | write!(out, " ")?; 562 | self.write_reg(out, is_float, false, inst.value.rd())?; 563 | write!(out, ", ")?; 564 | /*self.write_reg(out, is_float, false, inst.value.rs1())?; 565 | write!(out, ", {}", inst.value.imm12())?; 566 | */ 567 | match inst.code { 568 | Opcode::FLD 569 | | Opcode::FLH 570 | | Opcode::FLQ 571 | | Opcode::FLW 572 | | Opcode::LB 573 | | Opcode::LBU 574 | | Opcode::LD 575 | | Opcode::LH 576 | | Opcode::LHU 577 | | Opcode::LW 578 | | Opcode::LWU => { 579 | write!(out, "{}(", inst.value.imm12())?; 580 | self.write_reg(out, false, false, inst.value.rs1())?; 581 | write!(out, ")")?; 582 | } 583 | 584 | _ => { 585 | self.write_reg(out, false, false, inst.value.rs1())?; 586 | write!(out, ", {}", inst.value.imm12())?; 587 | } 588 | } 589 | } 590 | 591 | // rd, rs1, imm6lohi 592 | Encoding::RdRs1N0CImm6loCImm6hi => { 593 | write!(out, " ")?; 594 | self.write_reg(out, false, false, inst.value.rd())?; 595 | write!(out, ", ")?; 596 | self.write_reg(out, false, false, inst.value.rs1())?; 597 | write!(out, ", ")?; 598 | write!(out, "{}", inst.value.c_imm6lohi())?; 599 | } 600 | 601 | // rd, rs1, nzimm6lohi 602 | Encoding::RdRs1N0CNzimm6loCNzimm6hi => { 603 | write!(out, " ")?; 604 | self.write_reg(out, false, false, inst.value.rd())?; 605 | write!(out, ", ")?; 606 | self.write_reg(out, false, false, inst.value.rs1())?; 607 | write!(out, ", ")?; 608 | write!(out, "{}", inst.value.c_nzimm6lohi())?; 609 | } 610 | 611 | // rd, rs1, nzuimm6lohi 612 | Encoding::RdRs1N0CNzuimm6hiCNzuimm6lo => { 613 | write!(out, " ")?; 614 | self.write_reg(out, false, false, inst.value.rd())?; 615 | write!(out, ", ")?; 616 | self.write_reg(out, false, false, inst.value.rs1())?; 617 | write!(out, ", ")?; 618 | write!(out, "{}", inst.value.c_nzuimm6lohi())?; 619 | } 620 | 621 | Encoding::RdRs1N0CRs2N0 => { 622 | write!(out, " ")?; 623 | self.write_reg(out, false, false, inst.value.rd())?; 624 | write!(out, ", ")?; 625 | self.write_reg(out, false, false, inst.value.rs1())?; 626 | write!(out, ", ")?; 627 | self.write_reg(out, false, false, inst.value.c_rs2())?; 628 | } 629 | 630 | Encoding::RdRs1P => { 631 | write!(out, " ")?; 632 | self.write_reg(out, false, false, inst.value.rd())?; 633 | write!(out, ", ")?; 634 | self.write_reg(out, false, false, inst.value.rs1_p())?; 635 | } 636 | 637 | Encoding::RdRs1PCImm6hiCImm6lo => { 638 | write!(out, " ")?; 639 | self.write_reg(out, false, false, inst.value.rd())?; 640 | write!(out, ", ")?; 641 | self.write_reg(out, false, false, inst.value.rs1())?; 642 | write!(out, ", ")?; 643 | write!(out, "{}", inst.value.c_imm6lohi())?; 644 | } 645 | 646 | Encoding::RdRs1PRs2P => { 647 | write!(out, " ")?; 648 | self.write_reg(out, false, false, inst.value.rd())?; 649 | write!(out, ", ")?; 650 | self.write_reg(out, false, false, inst.value.rs1_p())?; 651 | write!(out, ", ")?; 652 | self.write_reg(out, false, false, inst.value.rs2_p())?; 653 | } 654 | 655 | Encoding::RdRs1Rm => { 656 | let (rd_is_f, rs1_is_f) = match inst.code { 657 | Opcode::FCVTDH => (true, true), 658 | Opcode::FCVTDL => (true, false), 659 | Opcode::FCVTDLU => (true, false), 660 | Opcode::FCVTDQ => (true, true), 661 | Opcode::FCVTDS => (true, true), 662 | Opcode::FCVTDW => (true, false), 663 | Opcode::FCVTDWU => (true, false), 664 | Opcode::FCVTHD => (true, true), 665 | Opcode::FCVTHL => (true, false), 666 | Opcode::FCVTHLU => (true, false), 667 | Opcode::FCVTHQ => (true, true), 668 | Opcode::FCVTHS => (true, true), 669 | Opcode::FCVTHW => (true, false), 670 | Opcode::FCVTHWU => (true, false), 671 | Opcode::FCVTLD => (false, true), 672 | Opcode::FCVTLUD => (false, true), 673 | Opcode::FCVTLUH => (false, true), 674 | 675 | Opcode::FCVTLH => (false, true), 676 | Opcode::FCVTLQ => (false, true), 677 | Opcode::FCVTLS => (false, true), 678 | Opcode::FCVTLUQ => (false, true), 679 | Opcode::FCVTLUS => (false, true), 680 | Opcode::FCVTQD => (true, true), 681 | Opcode::FCVTQH => (true, true), 682 | Opcode::FCVTQL => (true, false), 683 | Opcode::FCVTQLU => (true, false), 684 | Opcode::FCVTQS => (true, true), 685 | Opcode::FCVTQW => (true, false), 686 | Opcode::FCVTQWU => (true, false), 687 | Opcode::FCVTSD => (true, true), 688 | Opcode::FCVTSH => (true, true), 689 | Opcode::FCVTSL => (true, false), 690 | Opcode::FCVTSLU => (true, false), 691 | Opcode::FCVTSQ => (true, true), 692 | Opcode::FCVTSW => (true, false), 693 | Opcode::FCVTSWU => (true, false), 694 | Opcode::FCVTWD => (false, true), 695 | Opcode::FCVTWH => (false, true), 696 | Opcode::FCVTWQ => (false, true), 697 | Opcode::FCVTWS => (false, true), 698 | Opcode::FCVTWUD => (false, true), 699 | Opcode::FCVTWUH => (false, true), 700 | Opcode::FCVTWUQ => (false, true), 701 | Opcode::FCVTWUS => (false, true), 702 | _ => (true, true), 703 | }; 704 | 705 | write!(out, " ")?; 706 | self.write_reg(out, rd_is_f, false, inst.value.rd())?; 707 | write!(out, ", ")?; 708 | self.write_reg(out, rs1_is_f, false, inst.value.rs1())?; 709 | } 710 | Encoding::RdRs1Rs2 => { 711 | write!(out, " ")?; 712 | let is_f = mnem.starts_with("f"); 713 | 714 | self.write_reg(out, is_f, false, inst.value.rd())?; 715 | write!(out, ", ")?; 716 | self.write_reg(out, is_f, false, inst.value.rs1())?; 717 | write!(out, ", ")?; 718 | self.write_reg(out, is_f, false, inst.value.rs2())?; 719 | } 720 | Encoding::RdRs1Shamtd => { 721 | write!(out, " ")?; 722 | self.write_reg(out, false, false, inst.value.rd())?; 723 | write!(out, ", ")?; 724 | self.write_reg(out, false, false, inst.value.rs1())?; 725 | write!(out, ", {}", inst.value.shamtd())?; 726 | } 727 | _ => todo!("{:?}", encoding), 728 | } 729 | 730 | Ok(()) 731 | } 732 | 733 | fn format_fli(&self, out: &mut W, inst: &Instruction) -> fmt::Result { 734 | let rs1 = inst.value.rs1(); 735 | let c = match rs1 { 736 | 0 => "-1.0", 737 | 1 => "0.0", 738 | 2 => "1.0*2^-16", 739 | 3 => "1.0*2^-15", 740 | 4 => "1.0*2^-8", 741 | 5 => "1.0*2^-7", 742 | 6 => "0.0625", 743 | 7 => "0.125", 744 | 8 => "0.25", 745 | 9 => "0.3125", 746 | 10 => "0.375", 747 | 11 => "0.4375", 748 | 12 => "0.5", 749 | 13 => "0.625", 750 | 14 => "0.75", 751 | 15 => "0.875", 752 | 16 => "1.0", 753 | 17 => "1.25", 754 | 18 => "1.5", 755 | 19 => "1.75", 756 | 20 => "2.0", 757 | 21 => "2.5", 758 | 22 => "3", 759 | 23 => "4", 760 | 24 => "8", 761 | 25 => "16", 762 | 26 => "128", 763 | 27 => "256", 764 | 28 => "2^15", 765 | 29 => "2^16", 766 | 30 => "+inf", 767 | 31 => "NaN", 768 | _ => "", 769 | }; 770 | 771 | write!(out, " ")?; 772 | self.write_reg(out, true, false, inst.value.rd())?; 773 | write!(out, ", {}", c) 774 | } 775 | } 776 | 777 | pub fn pretty_disassembler( 778 | out: &mut W, 779 | bitness: usize, 780 | data: &[u8], 781 | address: u64, 782 | ) -> fmt::Result { 783 | let mut decoder = Decoder::new(bitness, data, address); 784 | let mut fmt = Formatter::new(); 785 | fmt.abi_names = true; 786 | 787 | let mut inst = Instruction::default(); 788 | 789 | while decoder.can_decode() { 790 | decoder.decode_out(&mut inst); 791 | 792 | write!(out, "{:016x}: {:08x} ", inst.address, inst.value.value)?; 793 | 794 | fmt.format(out, &inst)?; 795 | write!(out, "\n")?; 796 | } 797 | Ok(()) 798 | } 799 | -------------------------------------------------------------------------------- /src/riscv/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod assembler; 2 | pub mod emitter; 3 | pub mod opcodes; 4 | pub mod operands; 5 | pub mod decode; 6 | pub mod formatter; 7 | 8 | pub use crate::core::operand::imm; 9 | pub use assembler::*; 10 | pub use emitter::EmitterExplicit; 11 | pub use opcodes::{Inst, InstructionValue, Opcode}; 12 | pub use operands::*; 13 | pub use regs::*; 14 | 15 | /// Return the length (in bytes) of an instruction given the low 16 bits of it. 16 | /// 17 | /// The current spec reserves a bit pattern for instructions of length >= 192 bits, but for 18 | /// simplicity this function just returns 24 in that case. The largest instructions currently 19 | /// defined are 4 bytes so it will likely be a long time until this diffence matters. 20 | pub fn instruction_length(i: u16) -> usize { 21 | if i & 0b11 != 0b11 { 22 | 2 23 | } else if i & 0b11100 != 0b11100 { 24 | 4 25 | } else if i & 0b111111 == 0b011111 { 26 | 6 27 | } else if i & 0b1111111 == 0b011111 { 28 | 8 29 | } else { 30 | 10 + 2 * ((i >> 12) & 0b111) as usize 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/riscv/operands.rs: -------------------------------------------------------------------------------- 1 | 2 | //! RISC-V Operands definition. 3 | use crate::{ 4 | core::{ 5 | arch_traits::{Arch, ArchTraits}, 6 | operand::*, 7 | types::TypeId, 8 | }, 9 | define_abstract_reg, define_final_reg, define_operand_cast, define_reg_traits, 10 | }; 11 | 12 | use derive_more::derive::{Deref, DerefMut}; 13 | 14 | #[derive(Deref, DerefMut, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)] 15 | pub struct Reg(pub BaseReg); 16 | 17 | define_abstract_reg!(Reg, BaseReg); 18 | 19 | define_reg_traits!(RISCV32Gp, RegGroup::Gp, 32, TypeId::Int32); 20 | define_reg_traits!(RISCV64Gp, RegGroup::Gp, 64, TypeId::Int64); 21 | define_reg_traits!(RISCVPC, RegGroup::PC, 64, TypeId::Int64); 22 | define_reg_traits!(RISCVVec, RegGroup::Vec, 128, TypeId::Int32x4); 23 | define_reg_traits!(RISCVFp, RegGroup::Vec, 64, TypeId::Float64); 24 | 25 | impl Reg { 26 | pub const fn signature_of(typ: RegType) -> OperandSignature { 27 | ArchTraits::by_arch(Arch::RISCV64).reg_type_to_signature(typ) 28 | } 29 | 30 | pub fn is_gp(&self) -> bool { 31 | self.is_reg_group_of(RegGroup::Gp) 32 | } 33 | 34 | pub fn is_fp(&self) -> bool { 35 | self.has_base_signature(RISCVFp::SIGNATURE) 36 | } 37 | 38 | pub fn is_vec(&self) -> bool { 39 | self.has_base_signature(RISCVFp::SIGNATURE) 40 | } 41 | 42 | pub fn set_reg_t(&mut self, rid: u32) { 43 | self.set_signature(T::SIGNATURE.into()); 44 | self.set_id(rid); 45 | } 46 | 47 | pub fn set_type_and_id(&mut self, typ: RegType, id: u32) { 48 | self.set_signature(Self::signature_of(typ)); 49 | self.set_id(id); 50 | } 51 | pub const SIGNATURE: u32 = BaseReg::SIGNATURE; 52 | } 53 | 54 | #[derive(Deref, DerefMut, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)] 55 | pub struct Gp(pub Reg); 56 | 57 | impl Gp { 58 | pub const fn signature_of(typ: RegType) -> OperandSignature { 59 | ArchTraits::by_arch(Arch::RISCV64).reg_type_to_signature(typ) 60 | } 61 | 62 | pub const X0: u32 = 0; 63 | pub const X1: u32 = 1; 64 | pub const X2: u32 = 2; 65 | pub const X3: u32 = 3; 66 | pub const X4: u32 = 4; 67 | pub const X5: u32 = 5; 68 | pub const X6: u32 = 6; 69 | pub const X7: u32 = 7; 70 | pub const X8: u32 = 8; 71 | pub const X9: u32 = 9; 72 | pub const X10: u32 = 10; 73 | pub const X11: u32 = 11; 74 | pub const X12: u32 = 12; 75 | pub const X13: u32 = 13; 76 | pub const X14: u32 = 14; 77 | pub const X15: u32 = 15; 78 | pub const X16: u32 = 16; 79 | pub const X17: u32 = 17; 80 | pub const X18: u32 = 18; 81 | pub const X19: u32 = 19; 82 | pub const X20: u32 = 20; 83 | pub const X21: u32 = 21; 84 | pub const X22: u32 = 22; 85 | pub const X23: u32 = 23; 86 | pub const X24: u32 = 24; 87 | pub const X25: u32 = 25; 88 | pub const X26: u32 = 26; 89 | pub const X27: u32 = 27; 90 | pub const X28: u32 = 28; 91 | pub const X29: u32 = 29; 92 | pub const X30: u32 = 30; 93 | pub const X31: u32 = 31; 94 | } 95 | 96 | define_final_reg!(Gp, Reg, RISCV64Gp); 97 | 98 | #[derive(Deref, DerefMut, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)] 99 | pub struct Fp(pub Reg); 100 | 101 | define_final_reg!(Fp, Reg, RISCVFp); 102 | 103 | impl Fp { 104 | pub const fn signature_of(typ: RegType) -> OperandSignature { 105 | ArchTraits::by_arch(Arch::RISCV64).reg_type_to_signature(typ) 106 | } 107 | 108 | pub const F0: u32 = 0; 109 | pub const F1: u32 = 1; 110 | pub const F2: u32 = 2; 111 | pub const F3: u32 = 3; 112 | pub const F4: u32 = 4; 113 | pub const F5: u32 = 5; 114 | pub const F6: u32 = 6; 115 | pub const F7: u32 = 7; 116 | pub const F8: u32 = 8; 117 | pub const F9: u32 = 9; 118 | pub const F10: u32 = 10; 119 | pub const F11: u32 = 11; 120 | pub const F12: u32 = 12; 121 | pub const F13: u32 = 13; 122 | pub const F14: u32 = 14; 123 | pub const F15: u32 = 15; 124 | pub const F16: u32 = 16; 125 | pub const F17: u32 = 17; 126 | pub const F18: u32 = 18; 127 | pub const F19: u32 = 19; 128 | pub const F20: u32 = 20; 129 | pub const F21: u32 = 21; 130 | pub const F22: u32 = 22; 131 | pub const F23: u32 = 23; 132 | pub const F24: u32 = 24; 133 | pub const F25: u32 = 25; 134 | pub const F26: u32 = 26; 135 | pub const F27: u32 = 27; 136 | pub const F28: u32 = 28; 137 | pub const F29: u32 = 29; 138 | pub const F30: u32 = 30; 139 | pub const F31: u32 = 31; 140 | } 141 | 142 | #[derive(Deref, DerefMut, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)] 143 | pub struct Vp(pub Reg); 144 | 145 | define_final_reg!(Vp, Reg, RISCVVec); 146 | 147 | impl Vp { 148 | pub const fn signature_of(typ: RegType) -> OperandSignature { 149 | ArchTraits::by_arch(Arch::RISCV64).reg_type_to_signature(typ) 150 | } 151 | 152 | pub const V0: u32 = 0; 153 | pub const V1: u32 = 1; 154 | pub const V2: u32 = 2; 155 | pub const V3: u32 = 3; 156 | pub const V4: u32 = 4; 157 | pub const V5: u32 = 5; 158 | pub const V6: u32 = 6; 159 | pub const V7: u32 = 7; 160 | pub const V8: u32 = 8; 161 | pub const V9: u32 = 9; 162 | pub const V10: u32 = 10; 163 | pub const V11: u32 = 11; 164 | pub const V12: u32 = 12; 165 | pub const V13: u32 = 13; 166 | pub const V14: u32 = 14; 167 | pub const V15: u32 = 15; 168 | pub const V16: u32 = 16; 169 | pub const V17: u32 = 17; 170 | pub const V18: u32 = 18; 171 | pub const V19: u32 = 19; 172 | pub const V20: u32 = 20; 173 | pub const V21: u32 = 21; 174 | pub const V22: u32 = 22; 175 | pub const V23: u32 = 23; 176 | pub const V24: u32 = 24; 177 | pub const V25: u32 = 25; 178 | pub const V26: u32 = 26; 179 | pub const V27: u32 = 27; 180 | pub const V28: u32 = 28; 181 | pub const V29: u32 = 29; 182 | pub const V30: u32 = 30; 183 | pub const V31: u32 = 31; 184 | } 185 | 186 | pub mod regs { 187 | pub use super::*; 188 | 189 | pub const fn x(id: u32) -> Gp { 190 | Gp::from_id(id) 191 | } 192 | 193 | pub const fn f(id: u32) -> Fp { 194 | Fp::from_id(id) 195 | } 196 | 197 | pub const fn v(id: u32) -> Vp { 198 | Vp::from_id(id) 199 | } 200 | 201 | pub const X0: Gp = x(0); 202 | pub const X1: Gp = x(1); 203 | pub const X2: Gp = x(2); 204 | pub const X3: Gp = x(3); 205 | pub const X4: Gp = x(4); 206 | pub const X5: Gp = x(5); 207 | pub const X6: Gp = x(6); 208 | pub const X7: Gp = x(7); 209 | pub const X8: Gp = x(8); 210 | pub const X9: Gp = x(9); 211 | pub const X10: Gp = x(10); 212 | pub const X11: Gp = x(11); 213 | pub const X12: Gp = x(12); 214 | pub const X13: Gp = x(13); 215 | pub const X14: Gp = x(14); 216 | pub const X15: Gp = x(15); 217 | pub const X16: Gp = x(16); 218 | pub const X17: Gp = x(17); 219 | pub const X18: Gp = x(18); 220 | pub const X19: Gp = x(19); 221 | pub const X20: Gp = x(20); 222 | pub const X21: Gp = x(21); 223 | pub const X22: Gp = x(22); 224 | pub const X23: Gp = x(23); 225 | pub const X24: Gp = x(24); 226 | pub const X25: Gp = x(25); 227 | pub const X26: Gp = x(26); 228 | pub const X27: Gp = x(27); 229 | pub const X28: Gp = x(28); 230 | pub const X29: Gp = x(29); 231 | pub const X30: Gp = x(30); 232 | pub const X31: Gp = x(31); 233 | 234 | pub const ZERO: Gp = X0; 235 | pub const RA: Gp = X1; 236 | pub const SP: Gp = X2; 237 | pub const GP: Gp = X3; 238 | pub const TP: Gp = X4; 239 | pub const T0: Gp = X5; 240 | pub const T1: Gp = X6; 241 | pub const T2: Gp = X7; 242 | pub const S0: Gp = X8; 243 | pub const S1: Gp = X9; 244 | pub const A0: Gp = X10; 245 | pub const A1: Gp = X11; 246 | pub const A2: Gp = X12; 247 | pub const A3: Gp = X13; 248 | pub const A4: Gp = X14; 249 | pub const A5: Gp = X15; 250 | pub const A6: Gp = X16; 251 | pub const A7: Gp = X17; 252 | pub const S2: Gp = X18; 253 | pub const S3: Gp = X19; 254 | pub const S4: Gp = X20; 255 | pub const S5: Gp = X21; 256 | pub const S6: Gp = X22; 257 | pub const S7: Gp = X23; 258 | pub const S8: Gp = X24; 259 | pub const S9: Gp = X25; 260 | pub const S10: Gp = X26; 261 | pub const S11: Gp = X27; 262 | pub const T3: Gp = X28; 263 | pub const T4: Gp = X29; 264 | pub const T5: Gp = X30; 265 | pub const T6: Gp = X31; 266 | 267 | pub const F0: Fp = f(0); 268 | pub const F1: Fp = f(1); 269 | pub const F2: Fp = f(2); 270 | pub const F3: Fp = f(3); 271 | pub const F4: Fp = f(4); 272 | pub const F5: Fp = f(5); 273 | pub const F6: Fp = f(6); 274 | pub const F7: Fp = f(7); 275 | pub const F8: Fp = f(8); 276 | pub const F9: Fp = f(9); 277 | pub const F10: Fp = f(10); 278 | pub const F11: Fp = f(11); 279 | pub const F12: Fp = f(12); 280 | pub const F13: Fp = f(13); 281 | pub const F14: Fp = f(14); 282 | pub const F15: Fp = f(15); 283 | pub const F16: Fp = f(16); 284 | pub const F17: Fp = f(17); 285 | pub const F18: Fp = f(18); 286 | pub const F19: Fp = f(19); 287 | pub const F20: Fp = f(20); 288 | pub const F21: Fp = f(21); 289 | pub const F22: Fp = f(22); 290 | pub const F23: Fp = f(23); 291 | pub const F24: Fp = f(24); 292 | pub const F25: Fp = f(25); 293 | pub const F26: Fp = f(26); 294 | pub const F27: Fp = f(27); 295 | pub const F28: Fp = f(28); 296 | pub const F29: Fp = f(29); 297 | pub const F30: Fp = f(30); 298 | pub const F31: Fp = f(31); 299 | 300 | pub const V0: Vp = v(0); 301 | pub const V1: Vp = v(1); 302 | pub const V2: Vp = v(2); 303 | pub const V3: Vp = v(3); 304 | pub const V4: Vp = v(4); 305 | pub const V5: Vp = v(5); 306 | pub const V6: Vp = v(6); 307 | pub const V7: Vp = v(7); 308 | pub const V8: Vp = v(8); 309 | pub const V9: Vp = v(9); 310 | pub const V10: Vp = v(10); 311 | pub const V11: Vp = v(11); 312 | pub const V12: Vp = v(12); 313 | pub const V13: Vp = v(13); 314 | pub const V14: Vp = v(14); 315 | pub const V15: Vp = v(15); 316 | pub const V16: Vp = v(16); 317 | pub const V17: Vp = v(17); 318 | pub const V18: Vp = v(18); 319 | pub const V19: Vp = v(19); 320 | pub const V20: Vp = v(20); 321 | pub const V21: Vp = v(21); 322 | pub const V22: Vp = v(22); 323 | pub const V23: Vp = v(23); 324 | pub const V24: Vp = v(24); 325 | pub const V25: Vp = v(25); 326 | pub const V26: Vp = v(26); 327 | pub const V27: Vp = v(27); 328 | pub const V28: Vp = v(28); 329 | pub const V29: Vp = v(29); 330 | pub const V30: Vp = v(30); 331 | pub const V31: Vp = v(31); 332 | } 333 | -------------------------------------------------------------------------------- /src/util/mod.rs: -------------------------------------------------------------------------------- 1 | use core::mem::size_of; 2 | 3 | pub fn align_down(x: usize, alignment: usize) -> usize { 4 | x & !(alignment - 1) 5 | } 6 | 7 | pub fn align_up(x: usize, alignment: usize) -> usize { 8 | x.wrapping_add(alignment.wrapping_sub(1)) & !(alignment - 1) 9 | } 10 | 11 | pub fn bit_vector_get_bit(buf: &[u32], index: usize) -> bool { 12 | let vec_index = index / 32; 13 | let bit_index = index % 32; 14 | 15 | ((buf[vec_index] >> bit_index) & 1) != 0 16 | } 17 | 18 | pub fn bit_vector_set_bit(buf: &mut [u32], index: usize, value: bool) { 19 | let vec_index = index / 32; 20 | let bit_index = index % 32; 21 | 22 | if value { 23 | buf[vec_index] |= 1 << bit_index; 24 | } else { 25 | buf[vec_index] &= !(1 << bit_index); 26 | } 27 | } 28 | 29 | pub fn bit_vector_flip_bit(buf: &mut [u32], index: usize) { 30 | let vec_index = index / 32; 31 | let bit_index = index % 32; 32 | 33 | buf[vec_index] ^= 1 << bit_index; 34 | } 35 | 36 | macro_rules! bitvector_op { 37 | ($name: ident, $op: expr, $opf: expr) => { 38 | pub fn $name(mut buf: &mut [u32], index: usize, mut count: usize) { 39 | if count == 0 { 40 | return; 41 | } 42 | 43 | let vec_index = index / 32; 44 | let bit_index = index % 32; 45 | 46 | buf = &mut buf[vec_index..]; 47 | 48 | const FILL_MASK: u32 = u32::MAX; 49 | 50 | let first_n_bits = (32usize.wrapping_sub(bit_index)).min(count); 51 | 52 | buf[0] = $op( 53 | buf[0], 54 | (FILL_MASK >> (32 - first_n_bits as u32)) << bit_index as u32, 55 | ); 56 | 57 | buf = &mut buf[1..]; 58 | 59 | count -= first_n_bits; 60 | 61 | while count >= 32 { 62 | buf[0] = $opf(buf[0], FILL_MASK); 63 | buf = &mut buf[1..]; 64 | count -= 32; 65 | } 66 | 67 | if count != 0 { 68 | buf[0] = $op(buf[0], FILL_MASK >> (32 - count as u32)); 69 | } 70 | } 71 | }; 72 | } 73 | 74 | bitvector_op!(bit_vector_fill, |x, y| x | y, |_, x| x); 75 | bitvector_op!(bit_vector_clear, |x: u32, y: u32| x & !y, |_, y: u32| !y); 76 | 77 | pub fn bit_vector_index_of(buf: &[u32], start: usize, value: bool) -> usize { 78 | let vec_index = start / 32; 79 | let bit_index = start % 32; 80 | 81 | let mut p = &buf[vec_index..]; 82 | const FILL_MASK: u32 = u32::MAX; 83 | 84 | let flip_mask = if value { 0 } else { FILL_MASK }; 85 | 86 | let mut bits = (p[0] ^ flip_mask) & (FILL_MASK << bit_index as u32); 87 | 88 | loop { 89 | if bits != 0 { 90 | return ((p.as_ptr() as usize - buf.as_ptr() as usize) / size_of::()) * 32 91 | + bits.trailing_zeros() as usize; 92 | } 93 | 94 | p = &p[1..]; 95 | 96 | if p.is_empty() { 97 | return buf.len() * 32; 98 | } 99 | 100 | bits = p[0] ^ flip_mask; 101 | } 102 | } 103 | 104 | #[cfg(feature = "jit")] 105 | pub mod os; 106 | #[cfg(feature = "jit")] 107 | pub mod virtual_memory; 108 | -------------------------------------------------------------------------------- /src/util/os.rs: -------------------------------------------------------------------------------- 1 | pub fn get_tick_count() -> u32 { 2 | cfgenius::cond! { 3 | if cfg(windows) { 4 | extern "C" { 5 | fn GetTickCount() -> u32; 6 | } 7 | 8 | unsafe { GetTickCount() } 9 | } else { 10 | use core::mem::MaybeUninit; 11 | let mut ts: MaybeUninit = MaybeUninit::zeroed(); 12 | 13 | unsafe { 14 | if libc::clock_gettime(libc::CLOCK_MONOTONIC, ts.as_mut_ptr()) != 0 { 15 | return 0; 16 | } 17 | let ts = ts.assume_init(); 18 | let t = ((ts.tv_sec as u64) * 1000) + (ts.tv_nsec as u64 / 1000000); 19 | (t & 0xFFFFFFFF) as u32 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/x86/arch_traits.rs: -------------------------------------------------------------------------------- 1 | use crate::core::{ 2 | arch_traits::*, 3 | operand::{OperandSignature, RegTraits, RegType}, 4 | types::TypeId, 5 | }; 6 | 7 | use super::operands::{ 8 | X86Bnd, X86CReg, X86DReg, X86GpbHi, X86GpbLo, X86Gpd, X86Gpq, X86Gpw, X86KReg, X86Mm, X86Rip, 9 | X86SReg, X86St, X86Tmm, X86Xmm, X86Ymm, X86Zmm, 10 | }; 11 | 12 | const SIGNATURES: [OperandSignature; 32] = [ 13 | OperandSignature::new(0), 14 | OperandSignature::new(0), 15 | OperandSignature::new(X86Rip::SIGNATURE), 16 | OperandSignature::new(X86GpbLo::SIGNATURE), 17 | OperandSignature::new(X86GpbHi::SIGNATURE), 18 | OperandSignature::new(X86Gpw::SIGNATURE), 19 | OperandSignature::new(X86Gpd::SIGNATURE), 20 | OperandSignature::new(X86Gpq::SIGNATURE), 21 | OperandSignature::new(0), 22 | OperandSignature::new(0), 23 | OperandSignature::new(0), 24 | OperandSignature::new(X86Xmm::SIGNATURE), 25 | OperandSignature::new(X86Ymm::SIGNATURE), 26 | OperandSignature::new(X86Zmm::SIGNATURE), 27 | OperandSignature::new(0), 28 | OperandSignature::new(X86KReg::SIGNATURE), 29 | OperandSignature::new(X86Mm::SIGNATURE), 30 | OperandSignature::new(X86SReg::SIGNATURE), 31 | OperandSignature::new(X86CReg::SIGNATURE), 32 | OperandSignature::new(X86DReg::SIGNATURE), 33 | OperandSignature::new(X86St::SIGNATURE), 34 | OperandSignature::new(X86Bnd::SIGNATURE), 35 | OperandSignature::new(X86Tmm::SIGNATURE), 36 | OperandSignature::new(0), 37 | OperandSignature::new(0), 38 | OperandSignature::new(0), 39 | OperandSignature::new(0), 40 | OperandSignature::new(0), 41 | OperandSignature::new(0), 42 | OperandSignature::new(0), 43 | OperandSignature::new(0), 44 | OperandSignature::new(0), 45 | ]; 46 | 47 | pub const X86_ARCH_TRAITS: ArchTraits = ArchTraits { 48 | fp_reg_id: 0xff, 49 | sp_reg_id: 0xff, 50 | ip_reg_id: 0xff, 51 | link_reg_id: 0xff, 52 | hw_stack_alignment: 1, 53 | min_stack_offset: 0x7FFFFFFF, 54 | max_stack_offset: 0x7FFFFFFF, 55 | regs_signature: SIGNATURES, 56 | reg_type_to_type_id: [TypeId::Void; 32], 57 | type_id_to_reg_type: [RegType::Gp32; 32], 58 | }; 59 | 60 | pub const X64_ARCH_TRAITS: ArchTraits = ArchTraits { 61 | fp_reg_id: 0xff, 62 | sp_reg_id: 0xff, 63 | ip_reg_id: 0xff, 64 | link_reg_id: 0xff, 65 | hw_stack_alignment: 1, 66 | min_stack_offset: 0x7FFFFFFF, 67 | max_stack_offset: 0x7FFFFFFF, 68 | regs_signature: SIGNATURES, 69 | reg_type_to_type_id: [TypeId::Void; 32], 70 | type_id_to_reg_type: [RegType::Gp32; 32], 71 | }; 72 | -------------------------------------------------------------------------------- /src/x86/formatter.rs: -------------------------------------------------------------------------------- 1 | use super::decode::{Decoder, Instruction, OperandType, RegType}; 2 | use super::decode_tab::{Opcode, MNEMONIC_LENS, MNEMONIC_OFFS, MNEMONIC_STR}; 3 | 4 | use core::fmt::{self, Write}; 5 | 6 | fn strpcatnum(w: &mut W, mut val: u64) -> fmt::Result { 7 | let lz = (val | 1).leading_zeros() as usize; 8 | let numbytes = 16 - (lz / 4); 9 | let mut dst = [0u8; 32]; 10 | let mut idx = numbytes + 2; 11 | loop { 12 | idx -= 1; 13 | dst[idx] = b"0123456789abcdef"[(val % 16) as usize]; 14 | val /= 16; 15 | if val == 0 { 16 | break; 17 | } 18 | } 19 | 20 | dst[0] = b'0'; 21 | dst[1] = b'x'; 22 | 23 | write!(w, "{}", unsafe { 24 | core::str::from_utf8_unchecked(&dst[..numbytes + 2]) 25 | }) 26 | } 27 | 28 | pub fn strpcatreg(w: &mut W, rt: RegType, ri: usize, size: usize) -> fmt::Result { 29 | let nametab = [ 30 | 2, 97, 108, 4, 98, 110, 100, 48, 2, 99, 108, 4, 98, 110, 100, 49, 2, 100, 108, 4, 98, 110, 31 | 100, 50, 2, 98, 108, 4, 98, 110, 100, 51, 3, 115, 112, 108, 0, 32, 32, 32, 3, 98, 112, 108, 32 | 0, 32, 32, 32, 3, 115, 105, 108, 0, 32, 32, 32, 3, 100, 105, 108, 0, 32, 32, 32, 3, 114, 33 | 56, 98, 0, 32, 32, 32, 3, 114, 57, 98, 0, 32, 32, 32, 4, 114, 49, 48, 98, 0, 32, 32, 4, 34 | 114, 49, 49, 98, 0, 32, 32, 4, 114, 49, 50, 98, 2, 97, 104, 4, 114, 49, 51, 98, 2, 99, 104, 35 | 4, 114, 49, 52, 98, 2, 100, 104, 4, 114, 49, 53, 98, 2, 98, 104, 0, 0, 32, 32, 32, 32, 32, 36 | 32, 2, 97, 120, 4, 116, 109, 109, 48, 2, 99, 120, 4, 116, 109, 109, 49, 2, 100, 120, 4, 37 | 116, 109, 109, 50, 2, 98, 120, 4, 116, 109, 109, 51, 2, 115, 112, 4, 116, 109, 109, 52, 2, 38 | 98, 112, 4, 116, 109, 109, 53, 2, 115, 105, 4, 116, 109, 109, 54, 2, 100, 105, 4, 116, 109, 39 | 109, 55, 3, 114, 56, 119, 32, 2, 101, 115, 3, 114, 57, 119, 32, 2, 99, 115, 4, 114, 49, 48, 40 | 119, 2, 115, 115, 4, 114, 49, 49, 119, 2, 100, 115, 4, 114, 49, 50, 119, 2, 102, 115, 4, 41 | 114, 49, 51, 119, 2, 103, 115, 4, 114, 49, 52, 119, 0, 32, 32, 4, 114, 49, 53, 119, 0, 32, 42 | 32, 2, 105, 112, 0, 32, 32, 32, 32, 3, 101, 97, 120, 3, 109, 109, 48, 3, 101, 99, 120, 3, 43 | 109, 109, 49, 3, 101, 100, 120, 3, 109, 109, 50, 3, 101, 98, 120, 3, 109, 109, 51, 3, 101, 44 | 115, 112, 3, 109, 109, 52, 3, 101, 98, 112, 3, 109, 109, 53, 3, 101, 115, 105, 3, 109, 109, 45 | 54, 3, 101, 100, 105, 3, 109, 109, 55, 3, 114, 56, 100, 32, 2, 107, 48, 3, 114, 57, 100, 46 | 32, 2, 107, 49, 4, 114, 49, 48, 100, 2, 107, 50, 4, 114, 49, 49, 100, 2, 107, 51, 4, 114, 47 | 49, 50, 100, 2, 107, 52, 4, 114, 49, 51, 100, 2, 107, 53, 4, 114, 49, 52, 100, 2, 107, 54, 48 | 4, 114, 49, 53, 100, 2, 107, 55, 3, 101, 105, 112, 0, 32, 32, 32, 3, 114, 97, 120, 3, 99, 49 | 114, 48, 3, 114, 99, 120, 0, 32, 32, 32, 3, 114, 100, 120, 3, 99, 114, 50, 3, 114, 98, 120, 50 | 3, 99, 114, 51, 3, 114, 115, 112, 3, 99, 114, 52, 3, 114, 98, 112, 0, 32, 32, 32, 3, 114, 51 | 115, 105, 0, 32, 32, 32, 3, 114, 100, 105, 0, 32, 32, 32, 2, 114, 56, 32, 3, 99, 114, 56, 52 | 2, 114, 57, 32, 3, 100, 114, 48, 3, 114, 49, 48, 3, 100, 114, 49, 3, 114, 49, 49, 3, 100, 53 | 114, 50, 3, 114, 49, 50, 3, 100, 114, 51, 3, 114, 49, 51, 3, 100, 114, 52, 3, 114, 49, 52, 54 | 3, 100, 114, 53, 3, 114, 49, 53, 3, 100, 114, 54, 3, 114, 105, 112, 3, 100, 114, 55, 5, 55 | 115, 116, 40, 48, 41, 0, 32, 5, 115, 116, 40, 49, 41, 0, 32, 5, 115, 116, 40, 50, 41, 0, 56 | 32, 5, 115, 116, 40, 51, 41, 0, 32, 5, 115, 116, 40, 52, 41, 0, 32, 5, 115, 116, 40, 53, 57 | 41, 0, 32, 5, 115, 116, 40, 54, 41, 0, 32, 5, 115, 116, 40, 55, 41, 0, 32, 4, 120, 109, 58 | 109, 48, 0, 32, 32, 4, 120, 109, 109, 49, 0, 32, 32, 4, 120, 109, 109, 50, 0, 32, 32, 4, 59 | 120, 109, 109, 51, 0, 32, 32, 4, 120, 109, 109, 52, 0, 32, 32, 4, 120, 109, 109, 53, 0, 32, 60 | 32, 4, 120, 109, 109, 54, 0, 32, 32, 4, 120, 109, 109, 55, 0, 32, 32, 4, 120, 109, 109, 56, 61 | 0, 32, 32, 4, 120, 109, 109, 57, 0, 32, 32, 5, 120, 109, 109, 49, 48, 0, 32, 5, 120, 109, 62 | 109, 49, 49, 0, 32, 5, 120, 109, 109, 49, 50, 0, 32, 5, 120, 109, 109, 49, 51, 0, 32, 5, 63 | 120, 109, 109, 49, 52, 0, 32, 5, 120, 109, 109, 49, 53, 0, 32, 5, 120, 109, 109, 49, 54, 0, 64 | 32, 5, 120, 109, 109, 49, 55, 0, 32, 5, 120, 109, 109, 49, 56, 0, 32, 5, 120, 109, 109, 49, 65 | 57, 0, 32, 5, 120, 109, 109, 50, 48, 0, 32, 5, 120, 109, 109, 50, 49, 0, 32, 5, 120, 109, 66 | 109, 50, 50, 0, 32, 5, 120, 109, 109, 50, 51, 0, 32, 5, 120, 109, 109, 50, 52, 0, 32, 5, 67 | 120, 109, 109, 50, 53, 0, 32, 5, 120, 109, 109, 50, 54, 0, 32, 5, 120, 109, 109, 50, 55, 0, 68 | 32, 5, 120, 109, 109, 50, 56, 0, 32, 5, 120, 109, 109, 50, 57, 0, 32, 5, 120, 109, 109, 51, 69 | 48, 0, 32, 5, 120, 109, 109, 51, 49, 0, 32, 0, 70 | ]; 71 | 72 | let nametab_idx = [608u16, 0, 69, 205, 544, 276, 139, 341, 3, 412, 484]; 73 | let idx = if rt == RegType::Gpl { 74 | size * 17 * 8 75 | } else { 76 | nametab_idx[rt as usize] as usize 77 | }; 78 | 79 | let mut dst = [0; 16]; 80 | let name = &nametab[idx + 8 * ri..]; 81 | 82 | for i in 0..8 { 83 | dst[i] = name[i + 1]; 84 | } 85 | 86 | if rt == RegType::Vec && size > 4 { 87 | dst[0] += size as u8 - 4; 88 | } 89 | 90 | write!( 91 | w, 92 | "{}", 93 | core::str::from_utf8(&dst[..name[0] as usize]).unwrap() 94 | ) 95 | } 96 | 97 | fn mnemonic(w: &mut W, inst: &Instruction) -> fmt::Result { 98 | let mut mnem = &MNEMONIC_STR[MNEMONIC_OFFS[inst.typ as usize] as usize..]; 99 | let mut mnem_len = MNEMONIC_LENS[inst.code() as usize]; 100 | 101 | let mut prefix_xacq_xrel = false; 102 | let mut prefix_segment = false; 103 | 104 | let mut sizesuffix = [0u8; 4]; 105 | let mut sizesuffixlen = 0; 106 | 107 | if inst.op_type(0) == OperandType::Off && inst.op_size_log(0) == 1 { 108 | sizesuffix[0] = b'w'; 109 | sizesuffixlen = 0; 110 | } 111 | 112 | match inst.code() { 113 | Opcode::C_SEP => { 114 | mnem = &mnem[inst.opsize() & 0xc..]; 115 | mnem_len = 3; 116 | } 117 | 118 | Opcode::C_EX => { 119 | mnem = &mnem[inst.opsize() & 0xc..]; 120 | mnem_len = if inst.opsize() < 4 { 3 } else { 4 }; 121 | } 122 | 123 | Opcode::CMPXCHGD => match inst.opsize_log() { 124 | 2 => { 125 | sizesuffix[0] = b'8'; 126 | sizesuffix[1] = b'b'; 127 | sizesuffixlen = 2; 128 | } 129 | 3 => { 130 | sizesuffix[0] = b'1'; 131 | sizesuffix[1] = b'6'; 132 | sizesuffix[2] = b'b'; 133 | sizesuffixlen = 3; 134 | } 135 | 136 | _ => (), 137 | }, 138 | 139 | Opcode::JCXZ => { 140 | mnem_len = if inst.addrsize_log() == 1 { 4 } else { 5 }; 141 | mnem = &mnem[5 * (inst.addrsize_log() - 1)..]; 142 | } 143 | 144 | Opcode::PUSH => { 145 | if inst.op_size_log(0) == 1 && inst.op_type(0) == OperandType::Imm { 146 | sizesuffix[0] = b'w'; 147 | sizesuffixlen = 1; 148 | } 149 | if inst.op_size_log(0) == 1 150 | && inst.op_type(0) == OperandType::Reg 151 | && inst.op_reg_type(0) == Some(RegType::Seg) 152 | { 153 | sizesuffix[0] = b'w'; 154 | sizesuffixlen = 1; 155 | } 156 | } 157 | 158 | Opcode::POP => { 159 | if inst.op_size_log(0) == 1 160 | && inst.op_type(0) == OperandType::Reg 161 | && inst.op_reg_type(0) == Some(RegType::Seg) 162 | { 163 | sizesuffix[0] = b'w'; 164 | sizesuffixlen = 1; 165 | } 166 | } 167 | 168 | Opcode::MOV => { 169 | if inst.has_rep() 170 | && inst.op_type(0) == OperandType::Mem 171 | && inst.op_type(1) == OperandType::Imm 172 | { 173 | prefix_xacq_xrel = true; 174 | } 175 | } 176 | 177 | Opcode::FXSAVE 178 | | Opcode::FXRSTOR 179 | | Opcode::XSAVE 180 | | Opcode::XSAVEC 181 | | Opcode::XSAVEOPT 182 | | Opcode::XSAVES 183 | | Opcode::XRSTOR 184 | | Opcode::XRSTORS => { 185 | if inst.opsize_log() == 3 { 186 | sizesuffix[0] = b'6'; 187 | sizesuffix[1] = b'4'; 188 | sizesuffixlen = 2; 189 | } 190 | } 191 | 192 | Opcode::EVX_MOV_G2X | Opcode::EVX_MOV_X2G => { 193 | sizesuffix[0] = b"bwdq"[inst.op_size_log(0)]; 194 | sizesuffixlen = 1; 195 | } 196 | 197 | Opcode::EVX_PBROADCAST => { 198 | sizesuffix[0] = b"bwdq"[inst.op_size_log(1)]; 199 | sizesuffixlen = 1; 200 | } 201 | 202 | Opcode::EVX_PINSR => { 203 | sizesuffix[0] = b"bwdq"[inst.op_size_log(2)]; 204 | sizesuffixlen = 1; 205 | } 206 | 207 | Opcode::RET | Opcode::ENTER | Opcode::LEAVE => { 208 | if inst.opsize_log() == 1 { 209 | sizesuffix[0] = b'w'; 210 | sizesuffixlen = 1; 211 | } 212 | } 213 | 214 | Opcode::LODS | Opcode::MOVS | Opcode::CMPS | Opcode::OUTS => { 215 | prefix_segment = true; 216 | if inst.has_rep() { 217 | write!(w, "rep ")?; 218 | } 219 | 220 | if inst.has_repnz() { 221 | write!(w, "repnz ")?; 222 | } 223 | 224 | if inst.is_64() && inst.addrsize_log() == 2 { 225 | write!(w, "addr32 ")?; 226 | } 227 | 228 | if !inst.is_64() && inst.addrsize_log() == 1 { 229 | write!(w, "addr16 ")?; 230 | } 231 | } 232 | 233 | Opcode::STOS | Opcode::SCAS | Opcode::INS => { 234 | if inst.has_rep() { 235 | write!(w, "rep ")?; 236 | } 237 | 238 | if inst.has_repnz() { 239 | write!(w, "repnz ")?; 240 | } 241 | 242 | if inst.is_64() && inst.addrsize_log() == 2 { 243 | write!(w, "addr32 ")?; 244 | } 245 | 246 | if !inst.is_64() && inst.addrsize_log() == 1 { 247 | write!(w, "addr16 ")?; 248 | } 249 | } 250 | 251 | Opcode::PUSHA 252 | | Opcode::POPA 253 | | Opcode::PUSHF 254 | | Opcode::POPF 255 | | Opcode::RETF 256 | | Opcode::IRET 257 | | Opcode::IN 258 | | Opcode::OUT => { 259 | sizesuffix[0] = b"bwdq"[inst.opsize_log()]; 260 | sizesuffixlen = 1; 261 | } 262 | 263 | _ => (), 264 | } 265 | 266 | if prefix_xacq_xrel || inst.has_lock() { 267 | if inst.has_rep() { 268 | write!(w, "xrelease ")?; 269 | } 270 | 271 | if inst.has_repnz() { 272 | write!(w, "xacquire ")?; 273 | } 274 | } 275 | 276 | if inst.has_lock() { 277 | write!(w, "lock ")?; 278 | } 279 | 280 | if prefix_segment && inst.segment().is_some() { 281 | write!(w, "{}s ", b"ecsdfg"[inst.segment as usize & 7])?; 282 | } 283 | 284 | for c in mnem[..mnem_len as usize].chars() { 285 | write!(w, "{}", c.to_lowercase())?; 286 | } 287 | 288 | write!( 289 | w, 290 | "{}", 291 | core::str::from_utf8(&sizesuffix[..sizesuffixlen]).unwrap() 292 | )?; 293 | Ok(()) 294 | } 295 | 296 | pub struct Formatter {} 297 | 298 | impl Formatter { 299 | pub const fn new() -> Self { 300 | Self {} 301 | } 302 | 303 | pub fn format(&self, out: &mut W, inst: &Instruction) -> fmt::Result { 304 | mnemonic(out, inst)?; 305 | 306 | for i in 0..4 { 307 | let op_type = inst.op_type(i); 308 | if op_type == OperandType::None { 309 | break; 310 | } 311 | 312 | if i > 0 { 313 | write!(out, ",")?; 314 | } 315 | 316 | write!(out, " ")?; 317 | 318 | let mut size = inst.op_size_log(i); 319 | if size == 0 { 320 | size = inst.addrsize_log(); 321 | } 322 | 323 | if op_type == OperandType::Reg { 324 | let typ = inst.op_reg_type(i).unwrap(); 325 | let idx = inst.operands[i].reg as usize; 326 | strpcatreg(out, typ, idx as _, size)?; 327 | } else if op_type == OperandType::Mem || op_type == OperandType::MemBCST { 328 | let mut idx_rt = RegType::Gpl; 329 | let mut idx_sz = inst.addrsize_log(); 330 | use Opcode::*; 331 | match inst.code() { 332 | CMPXCHGD => { 333 | size = inst.opsize_log() + 1; 334 | } 335 | BOUND => { 336 | size += 1; 337 | } 338 | 339 | JMPF | CALLF | LDS | LES | LFS | LGS | LSS => size += 6, 340 | FLD | FSTP | FBLD | FBSTP => { 341 | size = if size != 0 { size } else { 9 }; 342 | } 343 | 344 | VPGATHERQD | VGATHERQPS | EVX_PGATHERQD | EVX_GATHERQPS => { 345 | idx_rt = RegType::Vec; 346 | idx_sz = inst.op_size_log(0) + 1; 347 | } 348 | 349 | EVX_PSCATTERQD | EVX_SCATTERQPS => { 350 | idx_rt = RegType::Vec; 351 | idx_sz = inst.op_size_log(1) + 1; 352 | } 353 | 354 | VPGATHERDD | VPGATHERQQ | VGATHERDPS | VGATHERQPD | EVX_PGATHERDD 355 | | EVX_PGATHERQQ | EVX_GATHERDPS | EVX_GATHERQPD => { 356 | idx_rt = RegType::Vec; 357 | idx_sz = inst.op_size_log(0); 358 | } 359 | 360 | EVX_PSCATTERDD | EVX_PSCATTERQQ | EVX_SCATTERDPS | EVX_SCATTERQPD => { 361 | idx_rt = RegType::Vec; 362 | idx_sz = inst.op_size_log(1); 363 | } 364 | _ => (), 365 | } 366 | 367 | if op_type == OperandType::MemBCST { 368 | size = inst.op_bcstsz_log(i); 369 | } 370 | 371 | static PTR_SIZES: [u8; 177] = [ 372 | 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 9, 98, 121, 116, 373 | 101, 32, 112, 116, 114, 32, 32, 32, 32, 32, 32, 32, 9, 119, 111, 114, 100, 32, 374 | 112, 116, 114, 32, 32, 32, 32, 32, 32, 32, 10, 100, 119, 111, 114, 100, 32, 375 | 112, 116, 114, 32, 32, 32, 32, 32, 32, 10, 113, 119, 111, 114, 100, 32, 112, 376 | 116, 114, 32, 32, 32, 32, 32, 32, 12, 120, 109, 109, 119, 111, 114, 100, 32, 377 | 112, 116, 114, 32, 32, 32, 32, 12, 121, 109, 109, 119, 111, 114, 100, 32, 112, 378 | 116, 114, 32, 32, 32, 32, 12, 122, 109, 109, 119, 111, 114, 100, 32, 112, 116, 379 | 114, 32, 32, 32, 32, 10, 100, 119, 111, 114, 100, 32, 112, 116, 114, 32, 32, 380 | 32, 32, 32, 32, 10, 102, 119, 111, 114, 100, 32, 112, 116, 114, 32, 32, 32, 32, 381 | 32, 32, 10, 116, 98, 121, 116, 101, 32, 112, 116, 114, 32, 32, 32, 32, 32, 32, 382 | 0, 383 | ]; 384 | 385 | let ptrsize = &PTR_SIZES[16 * (size + 1)..]; 386 | let len = ptrsize[0]; 387 | write!( 388 | out, 389 | "{}", 390 | core::str::from_utf8(&ptrsize[1..len as usize]).unwrap() 391 | )?; 392 | 393 | if let Some(seg) = inst.segment() { 394 | write!(out, "{}s:", b"ecsdfg\0"[seg as usize & 7] as char)? 395 | } 396 | 397 | write!(out, "[")?; 398 | 399 | let has_base = inst.op_base(i).is_some(); 400 | let has_idx = inst.op_index(i).is_some(); 401 | 402 | if has_base { 403 | strpcatreg( 404 | out, 405 | RegType::Gpl, 406 | inst.op_base(i).unwrap() as _, 407 | inst.addrsize_log(), 408 | )?; 409 | } 410 | 411 | if has_idx { 412 | if has_base { 413 | write!(out, "+")? 414 | } 415 | 416 | write!(out, "0{}", char::from_u32(1 << inst.op_scale(i)).unwrap())?; 417 | write!(out, "*")?; 418 | 419 | strpcatreg(out, idx_rt, inst.op_index(i).unwrap() as _, idx_sz)?; 420 | } 421 | 422 | let mut disp = inst.op_disp(i); 423 | 424 | if disp != 0 && (has_base || has_idx) { 425 | write!(out, "{}", if disp < 0 { "-" } else { "+" })?; 426 | 427 | if disp < 0 { 428 | disp = disp.wrapping_neg(); 429 | } 430 | 431 | if inst.addrsize_log() == 1 { 432 | disp &= 0xffff; 433 | } else if inst.addrsize_log() == 2 { 434 | disp &= 0xffffffff; 435 | } 436 | 437 | if disp != 0 || (!has_base && !has_idx) { 438 | strpcatnum(out, disp as _)?; 439 | } 440 | 441 | 442 | } 443 | write!(out, "]")?; 444 | } else if op_type == OperandType::Imm || op_type == OperandType::Off { 445 | let mut immediate = inst.op_imm(i) as u64; 446 | use Opcode::*; 447 | match inst.code() { 448 | SSE_EXTRQ | SSE_INSERTQ => { 449 | write!(out, "0x{:x}", immediate & 0xff)?; 450 | write!(out, ", ")?; 451 | immediate = (immediate >> 8) & 0xff; 452 | } 453 | 454 | ENTER => { 455 | write!(out, "0x{:x}, ", immediate & 0xffff)?; 456 | immediate = (immediate >> 16) & 0xff; 457 | } 458 | 459 | JMPF | CALLF => { 460 | write!(out, "0x{:x}:", (immediate >> (8 << size)) & 0xffff)?; 461 | } 462 | 463 | _ => (), 464 | } 465 | 466 | if op_type == OperandType::Off { 467 | immediate += inst.address + inst.size() as u64; 468 | } 469 | 470 | if size == 0 { 471 | immediate &= 0xff; 472 | } else if size == 1 { 473 | immediate &= 0xffff; 474 | } else if size == 2 { 475 | immediate &= 0xffffffff; 476 | } 477 | 478 | write!(out, "{:x}", immediate)?; 479 | } 480 | 481 | if i == 0 && inst.maskreg().is_some() { 482 | write!(out, "{{")?; 483 | strpcatreg(out, RegType::Mask, inst.maskreg().unwrap() as usize, 0)?; 484 | write!(out, "}}")?; 485 | if inst.maskzero() { 486 | write!(out, "{{z}}")?; 487 | } 488 | } 489 | } 490 | 491 | Ok(()) 492 | } 493 | } 494 | use alloc::format; 495 | use alloc::vec::Vec; 496 | pub fn pretty_disassembler( 497 | out: &mut W, 498 | bitness: usize, 499 | data: &[u8], 500 | address: u64, 501 | ) -> fmt::Result { 502 | let mut decoder = Decoder::new(bitness as _, data, address); 503 | let fmt = Formatter::new(); 504 | 505 | let mut inst = Instruction::default(); 506 | let start = address; 507 | while decoder.can_decode() { 508 | decoder.decode_out(&mut inst); 509 | let ix = (inst.address - start) as usize; 510 | 511 | let instr_bytes = data[ix..ix + inst.size()] 512 | .iter() 513 | .map(|x| format!("{:02X}", x)) 514 | .collect::>() 515 | .join(" "); 516 | 517 | let mut outs = alloc::string::String::new(); 518 | fmt.format(&mut outs, &inst)?; 519 | write!(out, "{:<15.016x} {:<20} {}\n", inst.address, instr_bytes, outs)?; 520 | } 521 | Ok(()) 522 | } 523 | -------------------------------------------------------------------------------- /src/x86/macroassembler.rs: -------------------------------------------------------------------------------- 1 | //! MacroAssembler for X86/64 2 | 3 | use crate::core::{ 4 | buffer::CodeBuffer, 5 | operand::{Imm, OperandCast}, 6 | }; 7 | 8 | use super::*; 9 | 10 | pub struct MacroAssembler<'a> { 11 | pub asm: Assembler<'a>, 12 | } 13 | 14 | impl<'a> MacroAssembler<'a> { 15 | pub const SCRATCH_REGISTER: Gpq = R11; 16 | pub const FP_TEMP_REGISTER: Xmm = XMM15; 17 | 18 | pub fn new(buffer: &'a mut CodeBuffer) -> Self { 19 | Self { 20 | asm: Assembler::new(buffer), 21 | } 22 | } 23 | 24 | pub fn supports_avx(&self) -> bool { 25 | true 26 | } 27 | 28 | #[cold] 29 | #[inline(never)] 30 | fn unsupported_operands(&self, s: &str) { 31 | { 32 | unreachable!("{:?}", s); 33 | } 34 | } 35 | 36 | pub fn swap32(&mut self, src1: impl OperandCast, src2: impl OperandCast) { 37 | let src1 = *src1.as_operand(); 38 | let src2 = *src2.as_operand(); 39 | 40 | if src1.is_gp() && src2.is_gp() { 41 | if src1.id() == src2.id() { 42 | return; 43 | } 44 | self.asm.xchg32rr(src1, src2); 45 | } else if src1.is_mem() && src2.is_gp() { 46 | self.asm.xchg32mr(src1, src2); 47 | } else if src1.is_vec() && src2.is_vec() { 48 | if src1.id() == src2.id() { 49 | return; 50 | } 51 | self.mov64(Self::FP_TEMP_REGISTER, src1); 52 | self.mov64(src1, src2); 53 | self.mov64(src2, Self::FP_TEMP_REGISTER); 54 | } else { 55 | self.unsupported_operands("RR or RM pair expected"); 56 | } 57 | } 58 | 59 | pub fn swap64(&mut self, src1: impl OperandCast, src2: impl OperandCast) { 60 | let src1 = *src1.as_operand(); 61 | let src2 = *src2.as_operand(); 62 | 63 | if src1.is_gp() && src2.is_gp() { 64 | if src1.id() == src2.id() { 65 | return; 66 | } 67 | self.asm.xchg64rr(src1, src2); 68 | } else if src1.is_mem() && src2.is_gp() { 69 | self.asm.xchg64mr(src1, src2); 70 | } else if src1.is_vec() && src2.is_vec() { 71 | if src1.id() == src2.id() { 72 | return; 73 | } 74 | self.mov64(Self::FP_TEMP_REGISTER, src1); 75 | self.mov64(src1, src2); 76 | self.mov64(src2, Self::FP_TEMP_REGISTER); 77 | } else { 78 | self.unsupported_operands("RR or RM pair expected"); 79 | } 80 | } 81 | 82 | pub fn mov32(&mut self, dst: impl OperandCast, src: impl OperandCast) { 83 | let dst = *dst.as_operand(); 84 | let src = *src.as_operand(); 85 | if dst.is_gp() && src.is_gp() { 86 | if dst.id() == src.id() { 87 | return; 88 | } 89 | self.asm.mov32rr(dst, src); 90 | } else if dst.is_gp() && src.is_imm() { 91 | self.asm.mov32ri(dst, src); 92 | } else if dst.is_vec() && src.is_vec() { 93 | if dst.id() == src.id() { 94 | return; 95 | } 96 | if self.supports_avx() { 97 | self.asm.vmovaps128rr(dst, src); 98 | } else { 99 | self.asm.sse_movapsrr(dst, src); 100 | } 101 | } else if dst.is_vec() && src.is_gp() { 102 | if self.supports_avx() { 103 | self.asm.vmovd_g2xrr(dst, src); 104 | } else { 105 | self.asm.sse_movd_g2xrr(dst, src); 106 | } 107 | } else if dst.is_gp() && src.is_vec() { 108 | if self.supports_avx() { 109 | self.asm.vmovd_x2grr(dst, src); 110 | } else { 111 | self.asm.sse_movd_x2grr(dst, src); 112 | } 113 | } else { 114 | self.unsupported_operands("RR or RI pair expected"); 115 | } 116 | } 117 | 118 | pub fn mov64(&mut self, dst: impl OperandCast, src: impl OperandCast) { 119 | let dst = *dst.as_operand(); 120 | let src = *src.as_operand(); 121 | if dst.is_reg() && src.is_reg() { 122 | if dst.id() == src.id() { 123 | return; 124 | } 125 | self.asm.mov64rr(dst, src); 126 | } else if dst.is_reg() && src.is_imm() { 127 | self.asm.mov64ri(dst, src); 128 | } else if dst.is_vec() && src.is_vec() { 129 | if dst.id() == src.id() { 130 | return; 131 | } 132 | if self.supports_avx() { 133 | self.asm.vmovaps128rr(dst, src); 134 | } else { 135 | self.asm.sse_movapsrr(dst, src); 136 | } 137 | } else if dst.is_vec() && src.is_gp() { 138 | if self.supports_avx() { 139 | self.asm.vmovq_g2xrr(dst, src); 140 | } else { 141 | self.asm.sse_movq_g2xrr(dst, src); 142 | } 143 | } else if dst.is_gp() && src.is_vec() { 144 | if self.supports_avx() { 145 | self.asm.vmovq_x2grr(dst, src); 146 | } else { 147 | self.asm.sse_movq_x2grr(dst, src); 148 | } 149 | } else { 150 | self.unsupported_operands("RR or RI pair expected"); 151 | } 152 | } 153 | 154 | pub fn load8(&mut self, dst: impl OperandCast, src: impl OperandCast) { 155 | let dst = *dst.as_operand(); 156 | let src = *src.as_operand(); 157 | if dst.is_reg() && src.is_mem() { 158 | self.asm.mov8rm(dst, src); 159 | } else { 160 | self.unsupported_operands("RM or MR pair expected"); 161 | } 162 | } 163 | 164 | pub fn load16(&mut self, dst: impl OperandCast, src: impl OperandCast) { 165 | let dst = *dst.as_operand(); 166 | let src = *src.as_operand(); 167 | if dst.is_reg() && src.is_mem() { 168 | self.asm.mov16rm(dst, src); 169 | } else { 170 | self.unsupported_operands("RM or MR pair expected"); 171 | } 172 | } 173 | 174 | pub fn load32(&mut self, dst: impl OperandCast, src: impl OperandCast) { 175 | let dst = *dst.as_operand(); 176 | let src = *src.as_operand(); 177 | if dst.is_reg() && src.is_mem() { 178 | self.asm.mov32rm(dst, src); 179 | } else if dst.is_vec() && src.is_mem() { 180 | if !self.supports_avx() { 181 | self.asm.sse_movssmr(dst, src); 182 | } else { 183 | self.asm.vmovssmr(dst, src); 184 | } 185 | } else { 186 | self.unsupported_operands("RM or MR pair expected"); 187 | } 188 | } 189 | 190 | pub fn load64(&mut self, dst: impl OperandCast, src: impl OperandCast) { 191 | let dst = *dst.as_operand(); 192 | let src = *src.as_operand(); 193 | if dst.is_reg() && src.is_mem() { 194 | self.asm.mov64rm(dst, src); 195 | } else if dst.is_vec() && src.is_mem() { 196 | if self.supports_avx() { 197 | self.asm.vmovsdrm(dst, src); 198 | } else { 199 | self.asm.sse_movsdrm(dst, src); 200 | } 201 | } else { 202 | self.unsupported_operands("RM or MR pair expected"); 203 | } 204 | } 205 | 206 | pub fn store8(&mut self, dst: impl OperandCast, src: impl OperandCast) { 207 | let dst = *dst.as_operand(); 208 | let src = *src.as_operand(); 209 | if dst.is_mem() && src.is_reg() { 210 | self.asm.mov8mr(dst, src); 211 | } else if dst.is_mem() && src.is_imm() { 212 | self.asm.mov8mi(dst, src); 213 | } else { 214 | self.unsupported_operands("MR or MI pair expected"); 215 | } 216 | } 217 | 218 | pub fn store16(&mut self, dst: impl OperandCast, src: impl OperandCast) { 219 | let dst = *dst.as_operand(); 220 | let src = *src.as_operand(); 221 | if dst.is_mem() && src.is_reg() { 222 | self.asm.mov16mr(dst, src); 223 | } else if dst.is_mem() && src.is_imm() { 224 | self.asm.mov16mi(dst, src); 225 | } else { 226 | self.unsupported_operands("MR or MI pair expected"); 227 | } 228 | } 229 | 230 | pub fn store32(&mut self, dst: impl OperandCast, src: impl OperandCast) { 231 | let dst = *dst.as_operand(); 232 | let src = *src.as_operand(); 233 | if dst.is_mem() && src.is_reg() { 234 | self.asm.mov32mr(dst, src); 235 | } else if dst.is_mem() && src.is_imm() { 236 | self.asm.mov32mi(dst, src); 237 | } else { 238 | self.unsupported_operands("MR or MI pair expected"); 239 | } 240 | } 241 | 242 | pub fn store64(&mut self, dst: impl OperandCast, src: impl OperandCast) { 243 | let dst = *dst.as_operand(); 244 | let src = *src.as_operand(); 245 | if dst.is_mem() && src.is_reg() { 246 | self.asm.mov64mr(dst, src); 247 | } else if dst.is_mem() && src.is_imm() { 248 | self.asm.mov64mi(dst, src); 249 | } else { 250 | self.unsupported_operands("MR or MI pair expected"); 251 | } 252 | } 253 | 254 | pub fn zero32(&mut self, dst: impl OperandCast) { 255 | let dst = *dst.as_operand(); 256 | 257 | if dst.is_gp() { 258 | self.asm.xor32rr(dst, dst); 259 | } else if dst.is_vec() { 260 | if self.supports_avx() { 261 | self.asm.vxorps128rrr(dst, dst, dst); 262 | } else { 263 | self.asm.sse_xorpsrr(dst, dst); 264 | } 265 | } 266 | } 267 | 268 | pub fn zero64(&mut self, dst: impl OperandCast) { 269 | let dst = *dst.as_operand(); 270 | if dst.is_gp() { 271 | self.asm.xor64rr(dst, dst); 272 | } else if dst.is_vec() { 273 | if self.supports_avx() { 274 | self.asm.vxorps128rrr(dst, dst, dst); 275 | } else { 276 | self.asm.sse_xorpsrr(dst, dst); 277 | } 278 | } else { 279 | self.unsupported_operands("GP or VEC expected"); 280 | } 281 | } 282 | 283 | pub fn add32(&mut self, dst: impl OperandCast, src1: impl OperandCast, src2: impl OperandCast) { 284 | let dst = *dst.as_operand(); 285 | let src1 = *src1.as_operand(); 286 | let src2 = *src2.as_operand(); 287 | 288 | if dst.is_gp() && src1.is_gp() && src2.is_gp() { 289 | self.x86_lea32(dst, ptr32_index(src1.as_::(), src2.as_::(), 0, 0)); 290 | } else if dst.is_gp() && src1.is_gp() && src2.is_imm() { 291 | if src1.id() == dst.id() { 292 | self.asm.add32ri(dst, src2); 293 | } else { 294 | self.x86_lea32( 295 | dst, 296 | ptr32(src1.as_::(), src2.as_::().value() as i32), 297 | ); 298 | } 299 | } else if dst.is_mem() && src1.is_gp() && src2.is_reg() { 300 | self.asm.mov32mr(dst, src1); 301 | self.asm.add32mr(dst, src2); 302 | } else if dst.is_mem() && src1.is_gp() && src2.is_imm() { 303 | self.asm.mov32mr(dst, src1); 304 | self.asm.add32mi(dst, src2); 305 | } else if dst.is_vec() && src1.is_vec() && src2.is_vec() { 306 | if self.supports_avx() { 307 | self.asm.vaddssrrr(dst, src1, src2); 308 | } else { 309 | if dst.id() == src1.id() { 310 | self.asm.sse_addssrr(dst, src2); 311 | } else { 312 | self.mov64(dst, src1); 313 | self.asm.sse_addssrr(dst, src2); 314 | } 315 | } 316 | } else if dst.is_vec() && src1.is_vec() && src2.is_mem() { 317 | if self.supports_avx() { 318 | self.asm.vaddssrrm(dst, src1, src2); 319 | } else { 320 | self.mov64(dst, src1); 321 | self.asm.sse_addsdrm(dst, src2); 322 | } 323 | } else { 324 | self.unsupported_operands("RRR, RRI, or VVV expected"); 325 | } 326 | } 327 | 328 | pub fn add64(&mut self, dst: impl OperandCast, src1: impl OperandCast, src2: impl OperandCast) { 329 | let dst = *dst.as_operand(); 330 | let src1 = *src1.as_operand(); 331 | let src2 = *src2.as_operand(); 332 | 333 | if dst.is_gp() && src1.is_gp() && src2.is_gp() { 334 | self.x86_lea64(dst, ptr64_index(src1.as_::(), src2.as_::(), 0, 0)); 335 | } else if dst.is_gp() && src1.is_gp() && src2.is_imm() { 336 | if src1.id() == dst.id() { 337 | self.asm.add64ri(dst, src2); 338 | } else { 339 | self.x86_lea64( 340 | dst, 341 | ptr64(src1.as_::(), src2.as_::().value() as i32), 342 | ); 343 | } 344 | } else if dst.is_gp() && src1.is_gp() && src2.is_mem() { 345 | if src1.id() == dst.id() { 346 | self.asm.add64rm(dst, src2); 347 | } else { 348 | self.mov64(dst, src1); 349 | self.asm.add64rm(dst, src2); 350 | } 351 | } else if dst.is_mem() && src1.is_gp() && src2.is_reg() { 352 | self.asm.mov64mr(dst, src1); 353 | self.asm.add64mr(dst, src2); 354 | } else if dst.is_mem() && src1.is_gp() && src2.is_imm() { 355 | self.asm.mov64mr(dst, src1); 356 | self.asm.add64mi(dst, src2); 357 | } else if dst.is_vec() && src1.is_vec() && src2.is_vec() { 358 | if self.supports_avx() { 359 | self.asm.vaddsdrrr(dst, src1, src2); 360 | } else { 361 | if dst.id() == src1.id() { 362 | self.asm.sse_addsdrr(dst, src2); 363 | } else { 364 | self.mov64(dst, src1); 365 | self.asm.sse_addsdrr(dst, src2); 366 | } 367 | } 368 | } else if dst.is_vec() && src1.is_vec() && src2.is_vec() { 369 | if self.supports_avx() { 370 | self.asm.vaddsdrrm(dst, src1, src2); 371 | } else { 372 | if dst.id() == src1.id() { 373 | self.asm.sse_addsdrm(dst, src2); 374 | } else { 375 | self.mov64(dst, src1); 376 | self.asm.sse_addsdrm(dst, src2); 377 | } 378 | } 379 | } else { 380 | self.unsupported_operands("GP and GP or GP and IMM expected"); 381 | } 382 | } 383 | 384 | pub fn x86_lea32(&mut self, dst: impl OperandCast, src: impl OperandCast) { 385 | let dst = *dst.as_operand(); 386 | let src = src.as_operand().as_::(); 387 | 388 | if !src.has_offset() && !src.has_shift() { 389 | if src.base_id() == dst.id() { 390 | self.asm.add32rr(dst, Gpq::from_id(src.index_id())); 391 | return; 392 | } 393 | 394 | if src.index_id() == dst.id() { 395 | self.asm.add32rr(dst, Gpq::from_id(src.base_id())); 396 | return; 397 | } 398 | } 399 | 400 | self.asm.lea32rm(dst, src); 401 | } 402 | 403 | pub fn x86_lea64(&mut self, dst: impl OperandCast, src: impl OperandCast) { 404 | let dst = *dst.as_operand(); 405 | let src = src.as_operand().as_::(); 406 | 407 | if !src.has_offset() && !src.has_shift() { 408 | if src.base_id() == dst.id() { 409 | self.asm.add64rr(dst, Gpq::from_id(src.index_id())); 410 | return; 411 | } 412 | 413 | if src.index_id() == dst.id() { 414 | self.asm.add64rr(dst, Gpq::from_id(src.base_id())); 415 | return; 416 | } 417 | } 418 | 419 | self.asm.lea64rm(dst, src); 420 | } 421 | 422 | pub fn sub32(&mut self, dst: impl OperandCast, src1: impl OperandCast, src2: impl OperandCast) { 423 | let dst = *dst.as_operand(); 424 | let src1 = *src1.as_operand(); 425 | let src2 = *src2.as_operand(); 426 | 427 | if dst.is_gp() && src1.is_gp() && src2.is_gp() { 428 | if dst.id() == src1.id() { 429 | self.neg32(dst); 430 | self.add32(dst, dst, src2); 431 | } else { 432 | self.mov32(dst, src1); 433 | self.asm.sub32rr(dst, src2); 434 | } 435 | } else if dst.is_gp() && src1.is_gp() && src2.is_imm() { 436 | if dst.id() == src1.id() { 437 | self.asm.sub32ri(dst, src2); 438 | } else { 439 | self.x86_lea32( 440 | dst, 441 | ptr32( 442 | src1.as_::(), 443 | (src2.as_::().value() as i32).wrapping_neg(), 444 | ), 445 | ); 446 | } 447 | } else if dst.is_gp() && src1.is_gp() && src2.is_mem() { 448 | if dst.id() == src1.id() { 449 | self.asm.sub32rm(dst, src2); 450 | } else { 451 | self.asm.mov32rr(dst, src1); 452 | self.asm.sub32rm(dst, src2); 453 | } 454 | } else if dst.is_mem() && src1.is_gp() && src2.is_gp() { 455 | self.asm.mov32mr(dst, src1); 456 | self.asm.sub32mr(dst, src2); 457 | } else if dst.is_mem() && src1.is_gp() && src2.is_imm() { 458 | self.asm.mov32mr(dst, src1); 459 | self.asm.sub32mi(dst, src2); 460 | } else if dst.is_vec() && src1.is_vec() && src2.is_vec() { 461 | if self.supports_avx() { 462 | self.asm.vsubssrrr(dst, src1, src2); 463 | } else { 464 | // B := A - B is invalid. 465 | if src1.id() != dst.id() && src2.id() == dst.id() { 466 | self.mov32(Self::FP_TEMP_REGISTER, src2); 467 | self.mov64(dst, src1); 468 | self.asm.sse_subssrr(dst, Self::FP_TEMP_REGISTER); 469 | } else { 470 | self.mov64(dst, src1); 471 | self.asm.sse_subssrr(dst, src2); 472 | } 473 | } 474 | } else if dst.is_vec() && src1.is_vec() && src2.is_vec() { 475 | if self.supports_avx() { 476 | self.asm.vsubssrrm(dst, src1, src2); 477 | } else { 478 | self.mov64(dst, src1); 479 | self.asm.sse_subssrm(dst, src2); 480 | } 481 | } else { 482 | self.unsupported_operands("RRR or RRI expected"); 483 | } 484 | } 485 | 486 | pub fn sub64(&mut self, dst: impl OperandCast, src1: impl OperandCast, src2: impl OperandCast) { 487 | let dst = *dst.as_operand(); 488 | let src1 = *src1.as_operand(); 489 | let src2 = *src2.as_operand(); 490 | 491 | if dst.is_gp() && src1.is_gp() && src2.is_gp() { 492 | if dst.id() == src1.id() { 493 | self.neg64(dst); 494 | self.add64(dst, dst, src2); 495 | } else { 496 | self.mov64(dst, src1); 497 | self.asm.sub64rr(dst, src2); 498 | } 499 | } else if dst.is_gp() && src1.is_gp() && src2.is_imm() { 500 | if dst.id() == src1.id() { 501 | self.asm.sub64ri(dst, src2); 502 | } else { 503 | self.x86_lea64( 504 | dst, 505 | ptr64( 506 | src1.as_::(), 507 | (src2.as_::().value() as i32).wrapping_neg(), 508 | ), 509 | ); 510 | } 511 | } else if dst.is_gp() && src1.is_gp() && src2.is_mem() { 512 | if dst.id() == src1.id() { 513 | self.asm.sub64rm(dst, src2); 514 | } else { 515 | self.asm.mov64rr(dst, src1); 516 | self.asm.sub64rm(dst, src2); 517 | } 518 | } else if dst.is_mem() && src1.is_gp() && src2.is_gp() { 519 | self.asm.mov64mr(dst, src1); 520 | self.asm.sub64mr(dst, src2); 521 | } else if dst.is_mem() && src1.is_gp() && src2.is_imm() { 522 | self.asm.mov64mr(dst, src1); 523 | self.asm.sub64mi(dst, src2); 524 | } else if dst.is_vec() && src1.is_vec() && src2.is_vec() { 525 | if self.supports_avx() { 526 | self.asm.vsubsdrrr(dst, src1, src2); 527 | } else { 528 | // B := A - B is invalid. 529 | if src1.id() != dst.id() && src2.id() == dst.id() { 530 | self.mov64(Self::FP_TEMP_REGISTER, src2); 531 | self.mov64(dst, src1); 532 | self.asm.sse_subsdrr(dst, Self::FP_TEMP_REGISTER); 533 | } else { 534 | self.mov64(dst, src1); 535 | self.asm.sse_subsdrr(dst, src2); 536 | } 537 | } 538 | } else if dst.is_vec() && src1.is_vec() && src2.is_mem() { 539 | if self.supports_avx() { 540 | self.asm.vsubsdrrm(dst, src1, src2); 541 | } else { 542 | self.mov64(dst, src1); 543 | self.asm.sse_subsdrm(dst, src2); 544 | } 545 | } else { 546 | self.unsupported_operands("RRR or RRI expected"); 547 | } 548 | } 549 | 550 | pub fn neg32(&mut self, srcdst: impl OperandCast) { 551 | let srcdst = *srcdst.as_operand(); 552 | if srcdst.is_gp() { 553 | self.asm.neg32r(srcdst); 554 | } else if srcdst.is_mem() { 555 | self.asm.neg32m(srcdst); 556 | } else { 557 | self.unsupported_operands("GP or MEM expected"); 558 | } 559 | } 560 | 561 | pub fn neg64(&mut self, srcdst: impl OperandCast) { 562 | let srcdst = *srcdst.as_operand(); 563 | if srcdst.is_gp() { 564 | self.asm.neg64r(srcdst); 565 | } else if srcdst.is_mem() { 566 | self.asm.neg64m(srcdst); 567 | } else { 568 | self.unsupported_operands("GP or MEM expected"); 569 | } 570 | } 571 | 572 | pub fn mul64(&mut self, dst: impl OperandCast, src1: impl OperandCast, src2: impl OperandCast) { 573 | let dst = *dst.as_operand(); 574 | let src1 = *src1.as_operand(); 575 | let src2 = *src2.as_operand(); 576 | 577 | if dst.is_gp() && src1.is_gp() && src2.is_gp() { 578 | if dst.id() == src1.id() { 579 | self.asm.imul64rr(dst, src2); 580 | } else if dst.id() == src2.id() { 581 | self.asm.imul64rr(dst, src1); 582 | } else { 583 | self.mov64(dst, src1); 584 | self.asm.imul64rr(dst, src2); 585 | } 586 | } else if dst.is_gp() && src1.is_gp() && src2.is_mem() { 587 | self.mov64(dst, src1); 588 | self.asm.imul64rm(dst, src2); 589 | } else if dst.is_gp() && src1.is_mem() && src2.is_gp() { 590 | self.mov64(dst, src2); 591 | self.asm.imul64rm(dst, src1); 592 | } else if dst.is_gp() && src1.is_gp() && src2.is_imm() { 593 | self.asm.mov64ri(dst, src2); 594 | self.asm.imul64rr(dst, src1); 595 | } else if dst.is_vec() && src1.is_vec() && src2.is_vec() { 596 | if self.supports_avx() { 597 | self.asm.vmulsdrrr(dst, src1, src2); 598 | } else { 599 | self.mov64(dst, src1); 600 | self.asm.sse_mulsdrr(dst, src2); 601 | } 602 | } else if dst.is_vec() && src1.is_vec() && src2.is_mem() { 603 | if self.supports_avx() { 604 | self.asm.vmulsdrrm(dst, src1, src2); 605 | } else { 606 | self.mov64(dst, src1); 607 | self.asm.sse_mulsdrm(dst, src2); 608 | } 609 | } else { 610 | self.unsupported_operands("RRR expected"); 611 | } 612 | } 613 | 614 | pub fn mul32(&mut self, dst: impl OperandCast, src1: impl OperandCast, src2: impl OperandCast) { 615 | let dst = *dst.as_operand(); 616 | let src1 = *src1.as_operand(); 617 | let src2 = *src2.as_operand(); 618 | 619 | if dst.is_gp() && src1.is_gp() && src2.is_gp() { 620 | if dst.id() == src1.id() { 621 | self.asm.imul32rr(dst, src2); 622 | } else if dst.id() == src2.id() { 623 | self.asm.imul32rr(dst, src1); 624 | } else { 625 | self.mov32(dst, src1); 626 | self.asm.imul32rr(dst, src2); 627 | } 628 | } else if dst.is_gp() && src1.is_gp() && src2.is_mem() { 629 | self.mov32(dst, src1); 630 | self.asm.imul32rm(dst, src2); 631 | } else if dst.is_gp() && src1.is_mem() && src2.is_gp() { 632 | self.mov32(dst, src2); 633 | self.asm.imul32rm(dst, src1); 634 | } else if dst.is_gp() && src1.is_gp() && src2.is_imm() { 635 | self.asm.mov32ri(dst, src2); 636 | self.asm.imul32rr(dst, src1); 637 | } else if dst.is_vec() && src1.is_vec() && src2.is_vec() { 638 | if self.supports_avx() { 639 | self.asm.vmulssrrr(dst, src1, src2); 640 | } else { 641 | self.mov32(dst, src1); 642 | self.asm.sse_mulssrr(dst, src2); 643 | } 644 | } else if dst.is_vec() && src1.is_vec() && src2.is_mem() { 645 | if self.supports_avx() { 646 | self.asm.vmulssrrm(dst, src1, src2); 647 | } else { 648 | self.mov32(dst, src1); 649 | self.asm.sse_mulssrm(dst, src2); 650 | } 651 | } else { 652 | self.unsupported_operands("RRR expected"); 653 | } 654 | } 655 | 656 | pub fn div64(&mut self, dst: impl OperandCast, src1: impl OperandCast, src2: impl OperandCast) { 657 | let dst = *dst.as_operand(); 658 | let src1 = *src1.as_operand(); 659 | let src2 = *src2.as_operand(); 660 | 661 | if dst.is_gp() && src1.is_gp() && src2.is_gp() { 662 | self.mov64(RAX, src1); 663 | self.asm.cqo(); 664 | self.asm.idiv64r(src2); 665 | self.mov64(dst, RAX); 666 | } else if dst.is_gp() && src1.is_gp() && src2.is_mem() { 667 | self.mov64(RAX, src1); 668 | self.asm.cqo(); 669 | self.asm.idiv64m(src2); 670 | self.mov64(dst, RAX); 671 | } else if dst.is_gp() && src1.is_mem() && src2.is_gp() { 672 | self.asm.mov64rm(RAX, src1); 673 | self.asm.cqo(); 674 | self.asm.idiv64r(src2); 675 | self.mov64(dst, RAX); 676 | } else if dst.is_vec() && src1.is_vec() && src2.is_vec() { 677 | if self.supports_avx() { 678 | self.asm.vdivsdrrr(dst, src1, src2); 679 | } else { 680 | self.mov64(dst, src1); 681 | self.asm.sse_divsdrr(dst, src2); 682 | } 683 | } else if dst.is_vec() && src1.is_vec() && src2.is_mem() { 684 | if self.supports_avx() { 685 | self.asm.vdivsdrrm(dst, src1, src2); 686 | } else { 687 | self.mov64(dst, src1); 688 | self.asm.sse_divsdrm(dst, src2); 689 | } 690 | } else { 691 | self.unsupported_operands("RRR or RRI expected"); 692 | } 693 | } 694 | } 695 | -------------------------------------------------------------------------------- /src/x86/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod arch_traits; 2 | pub mod assembler; 3 | pub mod emitter; 4 | pub mod opcodes; 5 | pub mod operands; 6 | pub mod decode; 7 | pub mod decode_tab; 8 | pub mod formatter; 9 | pub mod macroassembler; 10 | 11 | pub use crate::core::operand::imm; 12 | pub use assembler::*; 13 | pub use emitter::X86EmitterExplicit; 14 | pub use operands::*; 15 | --------------------------------------------------------------------------------