feat: implement arch, initial arm prototype
This commit is contained in:
parent
4a4d49f7de
commit
ebf798e22e
6 changed files with 1704 additions and 187 deletions
112
Cargo.lock
generated
112
Cargo.lock
generated
|
|
@ -70,12 +70,53 @@ version = "2.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitvec"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
|
||||||
|
dependencies = [
|
||||||
|
"funty",
|
||||||
|
"radium",
|
||||||
|
"tap",
|
||||||
|
"wyz",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.20.2"
|
version = "3.20.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
|
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "capstone"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f442ae0f2f3f1b923334b4a5386c95c69c1cfa072bafa23d6fae6d9682eb1dd4"
|
||||||
|
dependencies = [
|
||||||
|
"capstone-sys",
|
||||||
|
"static_assertions",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "capstone-sys"
|
||||||
|
version = "0.18.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4e8087cab6731295f5a2a2bd82989ba4f41d3a428aab2e7c98d8f4db38aac05"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.2.59"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283"
|
||||||
|
dependencies = [
|
||||||
|
"find-msvc-tools",
|
||||||
|
"shlex",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
|
|
@ -188,6 +229,12 @@ version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "find-msvc-tools"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fixedbitset"
|
name = "fixedbitset"
|
||||||
version = "0.5.7"
|
version = "0.5.7"
|
||||||
|
|
@ -206,6 +253,12 @@ version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
|
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "funty"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.15.5"
|
version = "0.15.5"
|
||||||
|
|
@ -361,6 +414,12 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "radium"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "raw-cpuid"
|
name = "raw-cpuid"
|
||||||
version = "11.6.0"
|
version = "11.6.0"
|
||||||
|
|
@ -411,15 +470,24 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slonik"
|
name = "slonik"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"capstone",
|
||||||
"clap",
|
"clap",
|
||||||
"cranelift-entity",
|
"cranelift-entity",
|
||||||
"egg",
|
"egg",
|
||||||
"petgraph",
|
"petgraph",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
|
"yaxpeax-arch",
|
||||||
|
"yaxpeax-arm",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -428,6 +496,12 @@ version = "1.15.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "static_assertions"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.11.1"
|
version = "0.11.1"
|
||||||
|
|
@ -462,6 +536,12 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tap"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.69"
|
version = "1.0.69"
|
||||||
|
|
@ -601,3 +681,35 @@ checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-link",
|
"windows-link",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wyz"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
|
||||||
|
dependencies = [
|
||||||
|
"tap",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yaxpeax-arch"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "36274fcc5403da2a7636ffda4d02eca12a1b2b8267b9d2e04447bd2ccfc72082"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yaxpeax-arm"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d8aa9155f0d727d10e91e5a94f68f415ec24c7a5faab4eac2386a1069e4a02d7"
|
||||||
|
dependencies = [
|
||||||
|
"bitvec",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"yaxpeax-arch",
|
||||||
|
]
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,11 @@ version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
capstone = "0.14.0"
|
||||||
clap = { version = "4.6.0", features = ["derive"] }
|
clap = { version = "4.6.0", features = ["derive"] }
|
||||||
cranelift-entity = "0.130.0"
|
cranelift-entity = "0.130.0"
|
||||||
egg = "0.11.0"
|
egg = "0.11.0"
|
||||||
petgraph = "0.8.3"
|
petgraph = "0.8.3"
|
||||||
smallvec = "1.15.1"
|
smallvec = "1.15.1"
|
||||||
|
yaxpeax-arch = "0.3.2"
|
||||||
|
yaxpeax-arm = "0.4.0"
|
||||||
|
|
|
||||||
118
src/arch.rs
118
src/arch.rs
|
|
@ -1,30 +1,98 @@
|
||||||
// pub trait Architecture {
|
//! Architecture-facing interfaces.
|
||||||
// type Mode: Copy + Eq + Send + Sync + 'static;
|
|
||||||
// type DecodedInstruction: Send + Sync + 'static;
|
|
||||||
|
|
||||||
// fn name(&self) -> &'static str;
|
pub mod arm;
|
||||||
|
|
||||||
// fn decode(
|
use crate::{
|
||||||
// &self,
|
flow::FlowInfo,
|
||||||
// bytes: &[u8],
|
ir::{Block, FrontendBuilder},
|
||||||
// addr: u64,
|
};
|
||||||
// mode: Self::Mode,
|
|
||||||
// ) -> Result<(usize, Self::DecodedInstruction), DecodeError>;
|
|
||||||
|
|
||||||
// fn lift(
|
/// Flat generic register metadata.
|
||||||
// &self,
|
///
|
||||||
// insn: &Self::DecodedInstruction,
|
/// This type intentionally stays small.
|
||||||
// addr: u64,
|
/// Architecture-specific aliasing or overlap semantics belong in the concrete
|
||||||
// mode: Self::Mode,
|
/// architecture module, not here.
|
||||||
// b: &mut Builder,
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
// ) -> Result<InstructionInfo, LiftError>;
|
pub struct RegisterDesc<R> {
|
||||||
|
/// Architecture-defined register identity.
|
||||||
|
pub reg: R,
|
||||||
|
|
||||||
// fn registers(&self) -> &'static [RegisterDesc];
|
/// Human-readable register name.
|
||||||
// fn flags(&self) -> &'static [FlagDesc];
|
pub name: &'static str,
|
||||||
// }
|
|
||||||
|
|
||||||
// pub struct InsnInfo {
|
/// Register width in bits.
|
||||||
// pub len: u8,
|
pub bits: u16,
|
||||||
// pub branches: smallvec::SmallVec<[Branch; 2]>,
|
}
|
||||||
// pub terminates_block: bool,
|
|
||||||
// }
|
/// Stable architecture protocol.
|
||||||
|
pub trait Arch {
|
||||||
|
/// Architecture execution mode.
|
||||||
|
type Mode: Copy + Eq;
|
||||||
|
|
||||||
|
/// Decoded instruction type.
|
||||||
|
type Inst;
|
||||||
|
|
||||||
|
/// Architecture-specific register identity.
|
||||||
|
type Reg: Copy + Eq;
|
||||||
|
|
||||||
|
/// Decode-time error.
|
||||||
|
type DecodeError;
|
||||||
|
|
||||||
|
/// Architecture-specific disassembly output.
|
||||||
|
type Disasm;
|
||||||
|
|
||||||
|
/// Stable architecture name.
|
||||||
|
fn name(&self) -> &'static str;
|
||||||
|
|
||||||
|
/// Pointer width in bytes for the given mode.
|
||||||
|
fn address_size(&self, mode: Self::Mode) -> u8;
|
||||||
|
|
||||||
|
/// Maximum instruction length in bytes.
|
||||||
|
fn max_instruction_len(&self) -> u8;
|
||||||
|
|
||||||
|
/// Architectural register set.
|
||||||
|
fn registers(&self) -> &'static [RegisterDesc<Self::Reg>];
|
||||||
|
|
||||||
|
/// Stack pointer register, if the architecture has one.
|
||||||
|
fn stack_pointer(&self) -> Option<Self::Reg>;
|
||||||
|
|
||||||
|
/// Decode one instruction from `bytes`.
|
||||||
|
fn decode(
|
||||||
|
&self,
|
||||||
|
bytes: &[u8],
|
||||||
|
mode: Self::Mode,
|
||||||
|
) -> Result<(usize, Self::Inst), Self::DecodeError>;
|
||||||
|
|
||||||
|
/// Return instruction-level control-flow facts for one decoded instruction.
|
||||||
|
fn flow_info(&self, inst: &Self::Inst, pc: u64, mode: Self::Mode) -> FlowInfo;
|
||||||
|
|
||||||
|
/// Render one decoded instruction as disassembly.
|
||||||
|
fn disasm(&self, inst: &Self::Inst, pc: u64, mode: Self::Mode) -> Self::Disasm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Translation-session knowledge available during lifting.
|
||||||
|
pub trait LiftEnv {
|
||||||
|
/// Returns the IR block associated with a statically known target address,
|
||||||
|
/// if the current translation session has created one.
|
||||||
|
fn block_for_target(&self, addr: u64) -> Option<Block>;
|
||||||
|
|
||||||
|
/// Returns the fallthrough block for the current instruction, if one exists
|
||||||
|
/// in the current translation session.
|
||||||
|
fn fallthrough_block(&self) -> Option<Block>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extension trait for architectures that can lift into Slonik IR.
|
||||||
|
pub trait LiftArch: Arch {
|
||||||
|
/// Lift-time error.
|
||||||
|
type LiftError;
|
||||||
|
|
||||||
|
/// Per-lift mutable context.
|
||||||
|
///
|
||||||
|
/// This is where the large mutable lifting state belongs.
|
||||||
|
type LiftCtx<'a>
|
||||||
|
where
|
||||||
|
Self: 'a;
|
||||||
|
|
||||||
|
/// Lift one decoded instruction into Slonik IR.
|
||||||
|
fn lift(&self, cx: &mut Self::LiftCtx<'_>, inst: &Self::Inst) -> Result<(), Self::LiftError>;
|
||||||
|
}
|
||||||
|
|
|
||||||
1341
src/arch/arm.rs
Normal file
1341
src/arch/arm.rs
Normal file
File diff suppressed because it is too large
Load diff
104
src/flow.rs
Normal file
104
src/flow.rs
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
//! Instruction-level control-flow facts.
|
||||||
|
|
||||||
|
/// A statically described branch or call target.
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum FlowTarget {
|
||||||
|
/// A statically known target address.
|
||||||
|
Direct(u64),
|
||||||
|
|
||||||
|
/// A dynamically computed target.
|
||||||
|
Indirect,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The control-flow behavior of one decoded instruction.
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum FlowKind {
|
||||||
|
/// Execution continues at the next instruction.
|
||||||
|
FallThrough,
|
||||||
|
|
||||||
|
/// Unconditional jump.
|
||||||
|
Jump { target: FlowTarget },
|
||||||
|
|
||||||
|
/// Conditional jump.
|
||||||
|
///
|
||||||
|
/// A conditional jump always has a taken edge and a fallthrough path.
|
||||||
|
CondJump { target: FlowTarget },
|
||||||
|
|
||||||
|
/// Call instruction.
|
||||||
|
///
|
||||||
|
/// `returns` tells later analyses whether the architecture/lifter believes
|
||||||
|
/// execution may continue at the next instruction after the call.
|
||||||
|
Call { target: FlowTarget, returns: bool },
|
||||||
|
|
||||||
|
/// Return from the current routine.
|
||||||
|
Return,
|
||||||
|
|
||||||
|
/// Trap, breakpoint, or other terminal fault-like instruction.
|
||||||
|
Trap,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flow facts for one decoded instruction.
|
||||||
|
///
|
||||||
|
/// This structure is the architecture-facing summary used by later CFG-building
|
||||||
|
/// or region-building code.
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct FlowInfo {
|
||||||
|
/// How many bytes does this instruction occupy
|
||||||
|
len: u8,
|
||||||
|
/// What kind of control-flow behaviour does it have?
|
||||||
|
kind: FlowKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FlowInfo {
|
||||||
|
/// Creates a new flow summary.
|
||||||
|
pub const fn new(len: u8, kind: FlowKind) -> Self {
|
||||||
|
Self { len, kind }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the instruction length in bytes.
|
||||||
|
pub const fn len(self) -> u8 {
|
||||||
|
self.len
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the control-flow kind.
|
||||||
|
pub const fn kind(self) -> FlowKind {
|
||||||
|
self.kind
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether this instruction ends the current basic block.
|
||||||
|
///
|
||||||
|
/// This is still an instruction-level fact, not whole-program CFG recovery.
|
||||||
|
pub const fn terminates_block(self) -> bool {
|
||||||
|
match self.kind {
|
||||||
|
FlowKind::FallThrough => false,
|
||||||
|
FlowKind::Jump { .. } => true,
|
||||||
|
FlowKind::CondJump { .. } => true,
|
||||||
|
FlowKind::Call { returns, .. } => !returns,
|
||||||
|
FlowKind::Return => true,
|
||||||
|
FlowKind::Trap => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether execution may continue at the next instruction.
|
||||||
|
pub const fn has_fallthrough(self) -> bool {
|
||||||
|
match self.kind {
|
||||||
|
FlowKind::FallThrough => true,
|
||||||
|
FlowKind::Jump { .. } => false,
|
||||||
|
FlowKind::CondJump { .. } => true,
|
||||||
|
FlowKind::Call { returns, .. } => returns,
|
||||||
|
FlowKind::Return => false,
|
||||||
|
FlowKind::Trap => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the explicit non-fallthrough target carried by the instruction,
|
||||||
|
/// if any.
|
||||||
|
pub const fn target(self) -> Option<FlowTarget> {
|
||||||
|
match self.kind {
|
||||||
|
FlowKind::Jump { target } => Some(target),
|
||||||
|
FlowKind::CondJump { target } => Some(target),
|
||||||
|
FlowKind::Call { target, .. } => Some(target),
|
||||||
|
FlowKind::FallThrough | FlowKind::Return | FlowKind::Trap => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
213
src/main.rs
213
src/main.rs
|
|
@ -1,4 +1,5 @@
|
||||||
mod arch;
|
mod arch;
|
||||||
|
mod flow;
|
||||||
mod ir;
|
mod ir;
|
||||||
|
|
||||||
use clap::{
|
use clap::{
|
||||||
|
|
@ -10,7 +11,13 @@ use clap::{
|
||||||
};
|
};
|
||||||
use cranelift_entity::EntityRef;
|
use cranelift_entity::EntityRef;
|
||||||
|
|
||||||
use crate::ir::*;
|
use crate::{
|
||||||
|
arch::{
|
||||||
|
Arch, LiftArch,
|
||||||
|
arm::{Arm, ArmLiftCtx, ArmMode, DummyEnv},
|
||||||
|
},
|
||||||
|
ir::*,
|
||||||
|
};
|
||||||
|
|
||||||
const STYLES: Styles = Styles::styled()
|
const STYLES: Styles = Styles::styled()
|
||||||
.header(AnsiColor::Green.on_default().effects(Effects::BOLD))
|
.header(AnsiColor::Green.on_default().effects(Effects::BOLD))
|
||||||
|
|
@ -26,175 +33,57 @@ const STYLES: Styles = Styles::styled()
|
||||||
struct Args {}
|
struct Args {}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// let args = Args::parse();
|
let arm = Arm::default();
|
||||||
|
|
||||||
|
let bytes: &[u8] = &[
|
||||||
|
0x01, 0x00, 0xA0, 0xE3, // mov r0, #1
|
||||||
|
0x02, 0x10, 0xA0, 0xE3, // mov r1, #2
|
||||||
|
0x01, 0x00, 0x80, 0xE0, // add r0, r0, r1
|
||||||
|
0x1E, 0xFF, 0x2F, 0xE1, // bx lr
|
||||||
|
];
|
||||||
|
|
||||||
|
let arm = crate::arch::arm::Arm::default();
|
||||||
|
let mut body = crate::ir::Body::new();
|
||||||
|
let mut fb_ctx = crate::ir::FrontendBuilderContext::new();
|
||||||
|
let env = DummyEnv;
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut body = Body::new();
|
let mut fb = crate::ir::FrontendBuilder::new(&mut body, &mut fb_ctx);
|
||||||
let mut cx = FrontendBuilderContext::new();
|
let entry = fb.create_block();
|
||||||
{
|
fb.switch_to_block(entry);
|
||||||
let mut b = FrontendBuilder::new(&mut body, &mut cx);
|
fb.seal_block(entry);
|
||||||
|
|
||||||
let entry = b.create_block();
|
let mut pc = 0x1000u64;
|
||||||
let then_block = b.create_block();
|
let mut rest = bytes;
|
||||||
let else_block = b.create_block();
|
|
||||||
let merge_block = b.create_block();
|
|
||||||
|
|
||||||
let x = Variable::new(0);
|
let mut cx = crate::arch::arm::ArmLiftCtx::new(
|
||||||
let y = Variable::new(1);
|
&arm,
|
||||||
let sum = Variable::new(2);
|
&env,
|
||||||
let acc = Variable::new(3);
|
&mut fb,
|
||||||
|
pc,
|
||||||
|
crate::arch::arm::ArmMode::Arm,
|
||||||
|
);
|
||||||
|
|
||||||
b.declare_var(x, Type::i32());
|
while !rest.is_empty() {
|
||||||
b.declare_var(y, Type::i32());
|
let (len, inst) = arm.decode(rest, crate::arch::arm::ArmMode::Arm).unwrap();
|
||||||
b.declare_var(sum, Type::i32());
|
println!(
|
||||||
b.declare_var(acc, Type::i32());
|
"{:#x}: {}",
|
||||||
|
pc,
|
||||||
|
arm.disasm(&inst, pc, crate::arch::arm::ArmMode::Arm)
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
" flow: {:?}",
|
||||||
|
arm.flow_info(&inst, pc, crate::arch::arm::ArmMode::Arm)
|
||||||
|
);
|
||||||
|
|
||||||
// entry:
|
cx.pc = pc;
|
||||||
//
|
arm.lift(&mut cx, &inst).unwrap();
|
||||||
// x = 7
|
|
||||||
// y = 5
|
|
||||||
// sum = x + y
|
|
||||||
// if (sum > 10) goto then else goto else
|
|
||||||
//
|
|
||||||
b.switch_to_block(entry);
|
|
||||||
b.seal_block(entry);
|
|
||||||
|
|
||||||
let c7 = b.iconst(Type::i32(), 7);
|
pc += len as u64;
|
||||||
let c5 = b.iconst(Type::i32(), 5);
|
rest = &rest[len..];
|
||||||
let c10 = b.iconst(Type::i32(), 10);
|
}
|
||||||
|
|
||||||
b.def_var(x, c7);
|
|
||||||
b.def_var(y, c5);
|
|
||||||
|
|
||||||
let lhs = b.use_var(x);
|
|
||||||
let rhs = b.use_var(y);
|
|
||||||
let sum_val = b.iadd(lhs, rhs, Type::i32());
|
|
||||||
b.def_var(sum, sum_val);
|
|
||||||
|
|
||||||
let lhs1 = b.use_var(sum);
|
|
||||||
let cond = b.icmp(IntCC::Sgt, lhs1, c10);
|
|
||||||
b.br_if(cond, then_block, else_block);
|
|
||||||
|
|
||||||
// Both then/else now have their predecessor set.
|
|
||||||
b.seal_block(then_block);
|
|
||||||
b.seal_block(else_block);
|
|
||||||
|
|
||||||
// then:
|
|
||||||
//
|
|
||||||
// acc = x * 2
|
|
||||||
// goto merge
|
|
||||||
//
|
|
||||||
b.switch_to_block(then_block);
|
|
||||||
|
|
||||||
let c2 = b.iconst(Type::i32(), 2);
|
|
||||||
let lhs2 = b.use_var(x);
|
|
||||||
let doubled = b.imul(lhs2, c2, Type::i32());
|
|
||||||
b.def_var(acc, doubled);
|
|
||||||
b.jump(merge_block);
|
|
||||||
|
|
||||||
// else:
|
|
||||||
//
|
|
||||||
// acc = 0 - y
|
|
||||||
// goto merge
|
|
||||||
//
|
|
||||||
b.switch_to_block(else_block);
|
|
||||||
|
|
||||||
let c0 = b.iconst(Type::i32(), 0);
|
|
||||||
let rhs1 = b.use_var(y);
|
|
||||||
let neg_y = b.isub(c0, rhs1, Type::i32());
|
|
||||||
b.def_var(acc, neg_y);
|
|
||||||
b.jump(merge_block);
|
|
||||||
|
|
||||||
// merge:
|
|
||||||
//
|
|
||||||
// acc and sum should come in as block params synthesized by use_var()
|
|
||||||
// result = acc + sum
|
|
||||||
// return result
|
|
||||||
//
|
|
||||||
b.seal_block(merge_block);
|
|
||||||
b.switch_to_block(merge_block);
|
|
||||||
|
|
||||||
let merged_acc = b.use_var(acc);
|
|
||||||
let merged_sum = b.use_var(sum);
|
|
||||||
let result = b.iadd(merged_acc, merged_sum, Type::i32());
|
|
||||||
|
|
||||||
b.ret(&[result]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
verify(&body).unwrap();
|
crate::ir::verify(&body).unwrap();
|
||||||
println!("{body}");
|
println!("{body}");
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
let mut body = Body::new();
|
|
||||||
let mut cx = FrontendBuilderContext::new();
|
|
||||||
|
|
||||||
{
|
|
||||||
let mut b = FrontendBuilder::new(&mut body, &mut cx);
|
|
||||||
|
|
||||||
let entry = b.create_block();
|
|
||||||
let loop_header = b.create_block();
|
|
||||||
let loop_body = b.create_block();
|
|
||||||
let exit = b.create_block();
|
|
||||||
|
|
||||||
let i = Variable::new(0);
|
|
||||||
b.declare_var(i, Type::i32());
|
|
||||||
|
|
||||||
// entry:
|
|
||||||
// i = 0
|
|
||||||
// br loop_header
|
|
||||||
b.switch_to_block(entry);
|
|
||||||
b.seal_block(entry);
|
|
||||||
|
|
||||||
let c0 = b.iconst(Type::i32(), 0);
|
|
||||||
b.def_var(i, c0);
|
|
||||||
b.jump(loop_header);
|
|
||||||
|
|
||||||
// loop_header:
|
|
||||||
// if (i < 4) goto loop_body else goto exit
|
|
||||||
//
|
|
||||||
// IMPORTANT:
|
|
||||||
// Do not seal this block yet. We still have a backedge coming from
|
|
||||||
// loop_body, and we want use_var(i) here to synthesize a block param.
|
|
||||||
b.switch_to_block(loop_header);
|
|
||||||
|
|
||||||
let iv = b.use_var(i);
|
|
||||||
let c4 = b.iconst(Type::i32(), 4);
|
|
||||||
let cond = b.icmp(IntCC::Slt, iv, c4);
|
|
||||||
b.br_if(cond, loop_body, exit);
|
|
||||||
|
|
||||||
// loop_body now has its predecessor.
|
|
||||||
b.seal_block(loop_body);
|
|
||||||
|
|
||||||
// loop_body:
|
|
||||||
// i = i + 1
|
|
||||||
// br loop_header
|
|
||||||
b.switch_to_block(loop_body);
|
|
||||||
|
|
||||||
let iv = b.use_var(i);
|
|
||||||
let c1 = b.iconst(Type::i32(), 1);
|
|
||||||
let next = b.iadd(iv, c1, Type::i32());
|
|
||||||
b.def_var(i, next);
|
|
||||||
b.jump(loop_header);
|
|
||||||
|
|
||||||
// Now loop_header has all of its predecessors:
|
|
||||||
// - entry
|
|
||||||
// - loop_body
|
|
||||||
b.seal_block(loop_header);
|
|
||||||
|
|
||||||
// exit:
|
|
||||||
// return i
|
|
||||||
//
|
|
||||||
// exit already has one predecessor from loop_header's br_if, so using
|
|
||||||
// `i` here should synthesize a block param and patch that edge.
|
|
||||||
b.seal_block(exit);
|
|
||||||
b.switch_to_block(exit);
|
|
||||||
|
|
||||||
let out = b.use_var(i);
|
|
||||||
b.ret(&[out]);
|
|
||||||
}
|
|
||||||
|
|
||||||
verify(&body).unwrap();
|
|
||||||
println!("{body}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue