initial commit

This commit is contained in:
Igor kehrazy 2026-04-06 22:57:19 +03:00
commit 38d2bc6e45
11 changed files with 1581 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

603
Cargo.lock generated Normal file
View file

@ -0,0 +1,603 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "allocator-api2"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "anstream"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
[[package]]
name = "anstyle-parse"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
dependencies = [
"anstyle",
"once_cell_polyfill",
"windows-sys",
]
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "bitflags"
version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
[[package]]
name = "bumpalo"
version = "3.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "clap"
version = "4.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
[[package]]
name = "colorchoice"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
[[package]]
name = "cranelift-bitset"
version = "0.130.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81fb5b134a12b559ff0c0f5af0fcd755ad380723b5016c4e0d36f74d39485340"
dependencies = [
"wasmtime-internal-core",
]
[[package]]
name = "cranelift-entity"
version = "0.130.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "463feed5d46cf8763f3ba3045284cf706dd161496e20ec9c14afbb4ba09b9e66"
dependencies = [
"cranelift-bitset",
"wasmtime-internal-core",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "egg"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd40cfd4196d7a8f882ace95d623d4d6734588e502b4e188675a7c2f55eb4fb4"
dependencies = [
"env_logger",
"hashbrown 0.15.5",
"indexmap",
"log",
"num-bigint",
"num-traits",
"quanta",
"rustc-hash",
"smallvec",
"symbol_table",
"symbolic_expressions",
"thiserror",
]
[[package]]
name = "env_logger"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7"
dependencies = [
"log",
]
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "fixedbitset"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "foldhash"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
[[package]]
name = "hashbrown"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"allocator-api2",
"equivalent",
"foldhash 0.1.5",
]
[[package]]
name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
dependencies = [
"foldhash 0.2.0",
]
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "indexmap"
version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff"
dependencies = [
"equivalent",
"hashbrown 0.16.1",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
[[package]]
name = "js-sys"
version = "0.3.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9"
dependencies = [
"once_cell",
"wasm-bindgen",
]
[[package]]
name = "libc"
version = "0.2.184"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af"
[[package]]
name = "libm"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
[[package]]
name = "log"
version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "num-bigint"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
dependencies = [
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "once_cell_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "petgraph"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455"
dependencies = [
"fixedbitset",
"hashbrown 0.15.5",
"indexmap",
"serde",
]
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quanta"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3ab5a9d756f0d97bdc89019bd2e4ea098cf9cde50ee7564dde6b81ccc8f06c7"
dependencies = [
"crossbeam-utils",
"libc",
"once_cell",
"raw-cpuid",
"wasi",
"web-sys",
"winapi",
]
[[package]]
name = "quote"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
[[package]]
name = "raw-cpuid"
version = "11.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186"
dependencies = [
"bitflags",
]
[[package]]
name = "rustc-hash"
version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe"
[[package]]
name = "rustversion"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "slonik"
version = "0.1.0"
dependencies = [
"clap",
"cranelift-entity",
"egg",
"petgraph",
"smallvec",
]
[[package]]
name = "smallvec"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "symbol_table"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f19bffd69fb182e684d14e3c71d04c0ef33d1641ac0b9e81c712c734e83703bc"
dependencies = [
"crossbeam-utils",
"foldhash 0.1.5",
"hashbrown 0.15.5",
]
[[package]]
name = "symbolic_expressions"
version = "5.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c68d531d83ec6c531150584c42a4290911964d5f0d79132b193b67252a23b71"
[[package]]
name = "syn"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasm-bindgen"
version = "0.2.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2"
dependencies = [
"bumpalo",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b"
dependencies = [
"unicode-ident",
]
[[package]]
name = "wasmtime-internal-core"
version = "43.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e671917bb6856ae360cb59d7aaf26f1cfd042c7b924319dd06fd380739fc0b2e"
dependencies = [
"hashbrown 0.16.1",
"libm",
]
[[package]]
name = "web-sys"
version = "0.3.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd70027e39b12f0849461e08ffc50b9cd7688d942c1c8e3c7b22273236b4dd0a"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]

11
Cargo.toml Normal file
View file

@ -0,0 +1,11 @@
[package]
name = "slonik"
version = "0.1.0"
edition = "2024"
[dependencies]
clap = { version = "4.6.0", features = ["derive"] }
cranelift-entity = "0.130.0"
egg = "0.11.0"
petgraph = "0.8.3"
smallvec = "1.15.1"

30
src/arch.rs Normal file
View file

@ -0,0 +1,30 @@
// pub trait Architecture {
// type Mode: Copy + Eq + Send + Sync + 'static;
// type DecodedInstruction: Send + Sync + 'static;
// fn name(&self) -> &'static str;
// fn decode(
// &self,
// bytes: &[u8],
// addr: u64,
// mode: Self::Mode,
// ) -> Result<(usize, Self::DecodedInstruction), DecodeError>;
// fn lift(
// &self,
// insn: &Self::DecodedInstruction,
// addr: u64,
// mode: Self::Mode,
// b: &mut Builder,
// ) -> Result<InstructionInfo, LiftError>;
// fn registers(&self) -> &'static [RegisterDesc];
// fn flags(&self) -> &'static [FlagDesc];
// }
// pub struct InsnInfo {
// pub len: u8,
// pub branches: smallvec::SmallVec<[Branch; 2]>,
// pub terminates_block: bool,
// }

42
src/ir.rs Normal file
View file

@ -0,0 +1,42 @@
//! Slonik normalized SSA IR.
mod dfg;
mod inst;
mod ty;
pub use dfg::*;
pub use inst::*;
pub use ty::*;
/// Defines a thin `u32` entity handle.
///
/// Slonik uses dense numeric handles instead of pointer-heavy nodes. This keeps
/// the IR compact, stable under mutation, and easy to store in `PrimaryMap` /
/// `SecondaryMap`.
macro_rules! entity {
(
$(
$(#[$meta:meta])*
$vis:vis struct $name:ident = $prefix:literal;
)*
) => {
$(
$(#[$meta])*
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
$vis struct $name(u32);
cranelift_entity::entity_impl!($name, $prefix);
)*
};
}
entity! {
/// A handle to a basic block in a `Body`.
pub struct Block = "block";
/// A handle to an instruction in a `Body`.
pub struct Inst = "inst";
/// A handle to an SSA value.
pub struct Value = "v";
}

1
src/ir/dfg.rs Normal file
View file

@ -0,0 +1 @@

413
src/ir/inst.rs Normal file
View file

@ -0,0 +1,413 @@
//! Instruction opcodes and instruction data formats for Slonik.
use core::fmt;
use cranelift_entity::EntityList;
use crate::ir::{Block, Inst, Type, Value};
/// A compact list of SSA values.
///
/// These lists are stored in a `ListPool` owned by the data-flow graph.
pub type ValueList = EntityList<Value>;
/// A compact list of blocks.
///
/// This is primarily useful for generic control-flow helpers and side tables.
pub type BlockList = EntityList<Block>;
/// A target block together with the SSA arguments passed to that block.
///
/// Slonik IR uses block arguments instead of phi nodes. A terminator that
/// transfers control to a block also provides the values for that block's
/// parameters.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct BlockCall {
/// The destination block.
pub block: Block,
/// The arguments passed to the destination block.
pub args: ValueList,
}
/// Integer comparison condition codes.
///
/// These are used by `Opcode::Icmp`.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum IntCC {
/// Equal.
Eq,
/// Not equal.
Ne,
/// Unsigned less than.
Ult,
/// Unsigned less than or equal.
Ule,
/// Unsigned greater than.
Ugt,
/// Unsigned greater than or equal.
Uge,
/// Signed less than.
Slt,
/// Signed less than or equal.
Sle,
/// Signed greater than.
Sgt,
/// Signed greater than or equal.
Sge,
}
impl IntCC {
/// Returns the logical inverse of this condition code.
pub const fn invert(self) -> Self {
match self {
Self::Eq => Self::Ne,
Self::Ne => Self::Eq,
Self::Ult => Self::Uge,
Self::Ule => Self::Ugt,
Self::Ugt => Self::Ule,
Self::Uge => Self::Ult,
Self::Slt => Self::Sge,
Self::Sle => Self::Sgt,
Self::Sgt => Self::Sle,
Self::Sge => Self::Slt,
}
}
/// Returns the condition code with operands swapped.
pub const fn swap_args(self) -> Self {
match self {
Self::Eq => Self::Eq,
Self::Ne => Self::Ne,
Self::Ult => Self::Ugt,
Self::Ule => Self::Uge,
Self::Ugt => Self::Ult,
Self::Uge => Self::Ule,
Self::Slt => Self::Sgt,
Self::Sle => Self::Sge,
Self::Sgt => Self::Slt,
Self::Sge => Self::Sle,
}
}
}
impl fmt::Display for IntCC {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Self::Eq => "eq",
Self::Ne => "ne",
Self::Ult => "ult",
Self::Ule => "ule",
Self::Ugt => "ugt",
Self::Uge => "uge",
Self::Slt => "slt",
Self::Sle => "sle",
Self::Sgt => "sgt",
Self::Sge => "sge",
};
f.write_str(s)
}
}
/// The set of operations that generic analysis and decompilation passes
/// are expected to understand.
///
/// If a frontend encounters something that does not fit naturally here, that is
/// a sign it should stay in the lifted IR longer or be lowered through a helper
/// sequence before entering this IR.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Opcode {
Iconst,
F32const,
F64const,
Bconst,
Zext,
Sext,
Trunc,
Bitcast,
Iadd,
Isub,
Imul,
Udiv,
Sdiv,
Urem,
Srem,
And,
Or,
Xor,
Not,
Shl,
Lshr,
Ashr,
Fadd,
Fsub,
Fmul,
Fdiv,
Fneg,
Icmp,
Select,
Load,
Store,
Jump,
BrIf,
Call,
Return,
}
impl Opcode {
/// Returns the textual name of this opcode.
pub const fn name(&self) -> &'static str {
match self {
Self::Iconst => "iconst",
Self::F32const => "f32const",
Self::F64const => "f64const",
Self::Bconst => "bconst",
Self::Zext => "zext",
Self::Sext => "sext",
Self::Trunc => "trunc",
Self::Bitcast => "bitcast",
Self::Iadd => "iadd",
Self::Isub => "isub",
Self::Imul => "imul",
Self::Udiv => "udiv",
Self::Sdiv => "sdiv",
Self::Urem => "urem",
Self::Srem => "srem",
Self::And => "and",
Self::Or => "or",
Self::Xor => "xor",
Self::Not => "not",
Self::Shl => "shl",
Self::Lshr => "lshr",
Self::Ashr => "ashr",
Self::Fadd => "fadd",
Self::Fsub => "fsub",
Self::Fmul => "fmul",
Self::Fdiv => "fdiv",
Self::Fneg => "fneg",
Self::Icmp => "icmp",
Self::Select => "select",
Self::Load => "load",
Self::Store => "store",
Self::Jump => "jump",
Self::BrIf => "br_if",
Self::Call => "call",
Self::Return => "return",
}
}
/// Returns whether this opcode is a terminator.
pub const fn is_terminator(self) -> bool {
matches!(self, Self::Jump | Self::BrIf | Self::Return)
}
/// Returns whether this opcode may read memory.
pub const fn may_read_memory(self) -> bool {
matches!(self, Self::Load | Self::Call)
}
/// Returns whether this opcode may write memory.
pub const fn may_write_memory(self) -> bool {
matches!(self, Self::Store | Self::Call)
}
/// Returns whether this opcode is side-effecting.
pub const fn has_side_effects(self) -> bool {
matches!(
self,
Self::Store | Self::Jump | Self::BrIf | Self::Call | Self::Return
)
}
}
impl fmt::Display for Opcode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
/// A memory access size in bytes.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum MemSize {
S1,
S2,
S4,
S8,
S16,
}
impl MemSize {
/// Returns the memory access width in bytes.
pub const fn bytes(self) -> u8 {
match self {
Self::S1 => 1,
Self::S2 => 2,
Self::S4 => 4,
Self::S8 => 8,
Self::S16 => 16,
}
}
/// Returns the memory access width in bits.
pub const fn bits(self) -> u16 {
// TODO: Some machines may have different byte sizes.
(self.bytes() as u16) * 8
}
}
impl fmt::Display for MemSize {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}B", self.bytes())
}
}
/// The payload of an instruction.
#[derive(Clone, Debug, PartialEq)]
pub enum InstructionData {
/// A zero-operand instruction with no embedded immediate.
Nullary { opcode: Opcode },
/// A constant instruction holding an `i64` immediate.
Iconst { opcode: Opcode, imm: i64 },
/// A constant instruction holding a 32-bit floating-point bit pattern.
///
/// The value is stored as raw IEEE-754 bits.
F32const { opcode: Opcode, bits: u32 },
/// A constant instruction holding a 64-bit floating-point bit pattern.
///
/// The value is stored as raw IEEE-754 bits.
F64const { opcode: Opcode, bits: u64 },
/// A boolean constant.
Bconst { opcode: Opcode, value: bool },
/// A one-argument instruction.
Unary { opcode: Opcode, arg: Value },
/// A two-argument instruction.
Binary { opcode: Opcode, args: [Value; 2] },
/// A three-argument instruction.
///
/// This is primarily useful for operations like `select`.
Ternary { opcode: Opcode, args: [Value; 3] },
/// An integer comparison instruction.
Icmp {
opcode: Opcode,
cc: IntCC,
args: [Value; 2],
},
/// A load from memory.
Load {
opcode: Opcode,
addr: Value,
size: MemSize,
},
/// A store to memory.
Store {
opcode: Opcode,
addr: Value,
value: Value,
size: MemSize,
},
/// An unconditional branch to another block with block arguments.
Jump { opcode: Opcode, dst: BlockCall },
/// A conditional branch with explicit true/false targets.
// TODO: Introduce direct call references
BrIf {
opcode: Opcode,
cond: Value,
then_dst: BlockCall,
else_dst: BlockCall,
},
/// A call through a value.
Call {
opcode: Opcode,
callee: Value,
args: ValueList,
},
/// A return from the current body.
Return { opcode: Opcode, values: ValueList },
}
impl InstructionData {
/// Returns the opcode of this instruction.
pub const fn opcode(&self) -> Opcode {
match *self {
Self::Nullary { opcode }
| Self::Iconst { opcode, .. }
| Self::F32const { opcode, .. }
| Self::F64const { opcode, .. }
| Self::Bconst { opcode, .. }
| Self::Unary { opcode, .. }
| Self::Binary { opcode, .. }
| Self::Ternary { opcode, .. }
| Self::Icmp { opcode, .. }
| Self::Load { opcode, .. }
| Self::Store { opcode, .. }
| Self::Jump { opcode, .. }
| Self::BrIf { opcode, .. }
| Self::Call { opcode, .. }
| Self::Return { opcode, .. } => opcode,
}
}
/// Returns whether this instruction is a terminator.
pub const fn is_terminator(&self) -> bool {
self.opcode().is_terminator()
}
}
/// The definition site of an SSA value.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ValueDef {
/// A value produced as result `index` of an instruction.
Result(Inst, u16),
/// A block parameter at position `index`.
Param(Block, u16),
}
/// Type information attached to a value in the data-flow graph.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct ValueData {
/// The semantic type of the value.
pub ty: Type,
/// Where the value was defined.
pub def: ValueDef,
}

451
src/ir/ty.rs Normal file
View file

@ -0,0 +1,451 @@
//! Compact semantic types for Slonik IR.
use core::fmt;
/// A semantic type in Slonik.
///
/// `Type` describes the meaning and shape of an SSA value.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Type {
/// The absence of a value.
///
/// This should generally not appear as the type of an SSA `Value`.
Void,
/// A one-bit boolean.
Bool,
/// A signedness-agnostic integer bitvector.
Int(IntType),
/// A floating-point scalar.
Float(FloatType),
/// A raw bitvector with no numeric interpretation yet.
Bits(BitsType),
/// A pointer value.
Ptr(PtrType),
/// A fixed-length or scalable SIMD vector.
Vector(VectorType),
}
/// A fixed-width integer type.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct IntType {
bits: u16,
}
impl IntType {
/// Creates a new integer type with the given width in bits.
///
/// # Panics
///
/// Panics if `bits == 0`.
pub const fn new(bits: u16) -> Self {
assert!(bits != 0, "integer type must have nonzero width");
Self { bits }
}
/// Returns the integer width in bits.
pub const fn bits(self) -> u16 {
self.bits
}
}
/// A floating-point scalar type.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum FloatType {
F16,
F32,
F64,
F80,
F128,
}
impl FloatType {
/// Returns the width of this floating-point type in bits.
pub const fn bits(self) -> u16 {
match self {
Self::F16 => 16,
Self::F32 => 32,
Self::F64 => 64,
Self::F80 => 80,
Self::F128 => 128,
}
}
}
/// A raw uninterpreted bitvector type.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct BitsType {
bits: u16,
}
impl BitsType {
/// Creates a new raw bitvector type with the given width.
///
/// # Panics
///
/// Panics if `bits == 0`.
pub const fn new(bits: u16) -> Self {
assert!(bits != 0, "bitvector type must have nonzero width");
Self { bits }
}
/// Returns the width in bits.
pub const fn bits(self) -> u16 {
self.bits
}
}
/// A pointer type.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct PtrType {
bits: u16,
addr_space: u16,
}
impl PtrType {
/// Creates a new pointer type in address space 0.
///
/// # Panics
///
/// Panics if `bits == 0`.
pub const fn new(bits: u16) -> Self {
Self::with_addr_space(bits, 0)
}
/// Creates a new pointer type with an explicit address space.
///
/// # Panics
///
/// Panics if `bits == 0`.
pub const fn with_addr_space(bits: u16, addr_space: u16) -> Self {
assert!(bits != 0, "pointer type must have nonzero width");
Self { bits, addr_space }
}
/// Returns the pointer width in bits.
pub const fn bits(self) -> u16 {
self.bits
}
/// Returns the address space.
pub const fn addr_space(self) -> u16 {
self.addr_space
}
}
/// A scalar lane type for a vector.
///
/// Vector lanes are semantic scalar elements, not storage slots.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum LaneType {
Bool,
Int(IntType),
Float(FloatType),
Bits(BitsType),
Ptr(PtrType),
}
impl LaneType {
/// Returns the width of one lane in bits.
pub const fn bits(self) -> u16 {
match self {
Self::Bool => 1,
Self::Int(ty) => ty.bits(),
Self::Float(ty) => ty.bits(),
Self::Bits(ty) => ty.bits(),
Self::Ptr(ty) => ty.bits(),
}
}
/// Converts this lane type into the corresponding scalar `Type`.
pub const fn as_type(self) -> Type {
match self {
Self::Bool => Type::Bool,
Self::Int(ty) => Type::Int(ty),
Self::Float(ty) => Type::Float(ty),
Self::Bits(ty) => Type::Bits(ty),
Self::Ptr(ty) => Type::Ptr(ty),
}
}
}
/// A SIMD vector type.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct VectorType {
lanes: u16,
lane: LaneType,
scalable: bool,
}
impl VectorType {
/// Creates a new fixed-length vector type.
///
/// # Panics
///
/// Panics if `lanes == 0`.
pub const fn new(lane: LaneType, lanes: u16) -> Self {
assert!(lanes != 0, "vector type must have at least one lane");
Self {
lanes,
lane,
scalable: false,
}
}
/// Creates a new vector type with explicit scalability.
///
/// # Panics
///
/// Panics if `lanes == 0`.
pub const fn with_scalable(lane: LaneType, lanes: u16, scalable: bool) -> Self {
assert!(lanes != 0, "vector type must have at least one lane");
Self {
lanes,
lane,
scalable,
}
}
/// Returns the number of lanes.
pub const fn lanes(self) -> u16 {
self.lanes
}
/// Returns the lane type.
pub const fn lane(self) -> LaneType {
self.lane
}
/// Returns whether this is a scalable vector.
pub const fn scalable(self) -> bool {
self.scalable
}
/// Returns the width of one lane in bits.
pub const fn lane_bits(self) -> u16 {
self.lane.bits()
}
/// Returns the minimum number of bits in the vector.
///
/// For scalable vectors, this is the known minimum width.
pub const fn min_bits(self) -> u32 {
(self.lanes as u32) * (self.lane.bits() as u32)
}
}
impl Type {
/// Returns `void`.
pub const fn void() -> Self {
Self::Void
}
/// Returns `bool`.
pub const fn bool() -> Self {
Self::Bool
}
/// Returns an integer type with the given width.
pub const fn int(bits: u16) -> Self {
Self::Int(IntType::new(bits))
}
/// Returns a floating-point type.
pub const fn float(kind: FloatType) -> Self {
Self::Float(kind)
}
/// Returns a raw bitvector type with the given width.
pub const fn bits(bits: u16) -> Self {
Self::Bits(BitsType::new(bits))
}
/// Returns a pointer type in address space 0.
pub const fn ptr(bits: u16) -> Self {
Self::Ptr(PtrType::new(bits))
}
/// Returns a pointer type with an explicit address space.
pub const fn ptr_in(bits: u16, addr_space: u16) -> Self {
Self::Ptr(PtrType::with_addr_space(bits, addr_space))
}
/// Returns a fixed-length vector type.
pub const fn vector(lane: LaneType, lanes: u16) -> Self {
Self::Vector(VectorType::new(lane, lanes))
}
/// Returns a vector type with explicit scalability.
pub const fn vector_with_scalable(lane: LaneType, lanes: u16, scalable: bool) -> Self {
Self::Vector(VectorType::with_scalable(lane, lanes, scalable))
}
/// Returns whether this is `void`.
pub const fn is_void(self) -> bool {
matches!(self, Self::Void)
}
/// Returns whether this is `bool`.
pub const fn is_bool(self) -> bool {
matches!(self, Self::Bool)
}
/// Returns whether this is an integer.
pub const fn is_int(self) -> bool {
matches!(self, Self::Int(_))
}
/// Returns whether this is a float.
pub const fn is_float(self) -> bool {
matches!(self, Self::Float(_))
}
/// Returns whether this is a raw bitvector.
pub const fn is_bits(self) -> bool {
matches!(self, Self::Bits(_))
}
/// Returns whether this is a pointer.
pub const fn is_ptr(self) -> bool {
matches!(self, Self::Ptr(_))
}
/// Returns whether this is a vector.
pub const fn is_vector(self) -> bool {
matches!(self, Self::Vector(_))
}
/// Returns whether this is any scalar type.
pub const fn is_scalar(self) -> bool {
!matches!(self, Self::Vector(_))
}
/// Returns the scalar width in bits, if this is a scalar type.
pub const fn bit_size(self) -> Option<u16> {
match self {
Self::Void => None,
Self::Bool => Some(1),
Self::Int(ty) => Some(ty.bits()),
Self::Float(ty) => Some(ty.bits()),
Self::Bits(ty) => Some(ty.bits()),
Self::Ptr(ty) => Some(ty.bits()),
Self::Vector(_) => None,
}
}
/// Returns the vector description, if this is a vector.
pub const fn as_vector(self) -> Option<VectorType> {
match self {
Self::Vector(ty) => Some(ty),
_ => None,
}
}
/// Returns the scalar lane type if this is a vector, otherwise `None`.
pub const fn lane_type(self) -> Option<LaneType> {
match self {
Self::Vector(ty) => Some(ty.lane()),
_ => None,
}
}
/// Returns the number of lanes if this is a vector.
pub const fn lanes(self) -> Option<u16> {
match self {
Self::Vector(ty) => Some(ty.lanes()),
_ => None,
}
}
/// Convenience constructor for `i8`.
pub const fn i8() -> Self {
Self::int(8)
}
/// Convenience constructor for `i16`.
pub const fn i16() -> Self {
Self::int(16)
}
/// Convenience constructor for `i32`.
pub const fn i32() -> Self {
Self::int(32)
}
/// Convenience constructor for `i64`.
pub const fn i64() -> Self {
Self::int(64)
}
/// Convenience constructor for `i128`.
pub const fn i128() -> Self {
Self::int(128)
}
/// Convenience constructor for `f32`.
pub const fn f32() -> Self {
Self::float(FloatType::F32)
}
/// Convenience constructor for `f64`.
pub const fn f64() -> Self {
Self::float(FloatType::F64)
}
}
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Type::Void => f.write_str("void"),
Type::Bool => f.write_str("bool"),
Type::Int(ty) => write!(f, "i{}", ty.bits()),
Type::Float(FloatType::F16) => f.write_str("f16"),
Type::Float(FloatType::F32) => f.write_str("f32"),
Type::Float(FloatType::F64) => f.write_str("f64"),
Type::Float(FloatType::F80) => f.write_str("f80"),
Type::Float(FloatType::F128) => f.write_str("f128"),
Type::Bits(ty) => write!(f, "b{}", ty.bits()),
Type::Ptr(ty) => {
if ty.addr_space() == 0 {
write!(f, "ptr{}", ty.bits())
} else {
write!(f, "ptr{}@{}", ty.bits(), ty.addr_space())
}
}
Type::Vector(ty) => {
if ty.scalable() {
write!(f, "vec<?x{} x {}>", ty.lanes(), ty.lane())
} else {
write!(f, "vec<{} x {}>", ty.lanes(), ty.lane())
}
}
}
}
}
impl fmt::Display for LaneType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LaneType::Bool => f.write_str("bool"),
LaneType::Int(ty) => write!(f, "i{}", ty.bits()),
LaneType::Float(FloatType::F16) => f.write_str("f16"),
LaneType::Float(FloatType::F32) => f.write_str("f32"),
LaneType::Float(FloatType::F64) => f.write_str("f64"),
LaneType::Float(FloatType::F80) => f.write_str("f80"),
LaneType::Float(FloatType::F128) => f.write_str("f128"),
LaneType::Bits(ty) => write!(f, "b{}", ty.bits()),
LaneType::Ptr(ty) => {
if ty.addr_space() == 0 {
write!(f, "ptr{}", ty.bits())
} else {
write!(f, "ptr{}@{}", ty.bits(), ty.addr_space())
}
}
}
}
}

27
src/main.rs Normal file
View file

@ -0,0 +1,27 @@
mod arch;
mod ir;
use clap::{
Parser,
builder::{
Styles,
styling::{AnsiColor, Effects},
},
};
const STYLES: Styles = Styles::styled()
.header(AnsiColor::Green.on_default().effects(Effects::BOLD))
.usage(AnsiColor::Green.on_default().effects(Effects::BOLD))
.literal(AnsiColor::Cyan.on_default().effects(Effects::BOLD))
.placeholder(AnsiColor::Cyan.on_default());
#[derive(Parser)]
#[command(name = "slonik", author)]
#[command(about = "an experimental decompiler")]
#[command(disable_version_flag = true, arg_required_else_help = true)]
#[command(styles=STYLES)]
struct Args {}
fn main() {
let args = Args::parse();
}

1
src/x86.rs Normal file
View file

@ -0,0 +1 @@
mod state;

1
src/x86/state.rs Normal file
View file

@ -0,0 +1 @@