diff --git a/Cargo.toml b/Cargo.toml index a9c1fed..b29e6c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,10 +2,14 @@ name = "waffle" version = "0.0.1" description = "Wasm Analysis Framework For Lightweight Experiments" -author = ["Chris Fallin "] +authors = ["Chris Fallin "] license = "Apache-2.0 WITH LLVM-exception" edition = "2018" [dependencies] wasmparser = "0.81" wasm-encoder = "0.3" +anyhow = "1.0" +structopt = "0.3" +log = "0.4" +env_logger = "0.9" \ No newline at end of file diff --git a/src/bin/waffle-util.rs b/src/bin/waffle-util.rs new file mode 100644 index 0000000..eec4fb4 --- /dev/null +++ b/src/bin/waffle-util.rs @@ -0,0 +1,47 @@ +//! WAFFLE command-line tool. + +use anyhow::Result; +use log::debug; +use std::path::PathBuf; +use structopt::StructOpt; +use waffle::frontend; + +#[derive(Debug, StructOpt)] +#[structopt(name = "waffle-util", about = "WAFFLE utility.")] +struct Options { + #[structopt(short, long)] + debug: bool, + + #[structopt(subcommand)] + command: Command, +} + +#[derive(Debug, StructOpt)] +enum Command { + #[structopt(name = "print-ir", about = "Parse Wasm and print resulting IR")] + PrintIR { + #[structopt(help = "Wasm file to parse")] + wasm: PathBuf, + }, +} + +fn main() -> Result<()> { + let opts = Options::from_args(); + + let mut logger = env_logger::Builder::from_default_env(); + if opts.debug { + logger.filter_level(log::LevelFilter::Debug); + } + let _ = logger.try_init(); + + match opts.command { + Command::PrintIR { wasm } => { + let bytes = std::fs::read(wasm)?; + debug!("Loaded {} bytes of Wasm data", bytes.len()); + let module = frontend::wasm_to_ir(&bytes[..])?; + println!("{:?}", module); + } + } + + Ok(()) +} diff --git a/src/frontend.rs b/src/frontend.rs new file mode 100644 index 0000000..8670bee --- /dev/null +++ b/src/frontend.rs @@ -0,0 +1,39 @@ +//! Frontend: convert Wasm to IR. + +use crate::ir::*; +use anyhow::{bail, Result}; +use log::debug; +use wasmparser::{Chunk, Parser, Payload, TypeDef}; + +pub fn wasm_to_ir(bytes: &[u8]) -> Result { + let mut module = Module::default(); + let mut parser = Parser::new(0); + for chunk in parser.parse(bytes, /* eof = */ true) { + match chunk { + Chunk::NeedMoreData(_) => bail!("Unexpected EOF in Wasm"), + Chunk::Parsed { payload, .. } => handle_payload(&mut module, payload)?, + } + } + + Ok(module) +} + +fn handle_payload<'a>(module: &mut Module, payload: Payload<'a>) -> Result<()> { + debug!("Wasm parser item: {:?}", payload); + match payload { + Payload::TypeSection(mut reader) => { + for _ in 0..reader.get_count() { + let ty = reader.read()?; + match ty { + TypeDef::Func(fty) => { + module.signatures.push(fty); + } + _ => {} + } + } + } + _ => {} + } + + Ok(()) +} diff --git a/src/ir.rs b/src/ir.rs index 75329de..696bee1 100644 --- a/src/ir.rs +++ b/src/ir.rs @@ -1,13 +1,14 @@ //! Intermediate representation for Wasm. -use wasmparser::{FuncType, Type}; +use wasmparser::{FuncType, Operator, Type}; pub type SignatureId = usize; pub type FuncId = usize; pub type BlockId = usize; pub type InstId = usize; +pub type ValueId = usize; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct Module { pub funcs: Vec, pub signatures: Vec, @@ -19,8 +20,40 @@ pub enum FuncDecl { Body(SignatureId, FunctionBody), } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct FunctionBody { pub locals: Vec, pub blocks: Vec, + pub values: Vec, +} + +#[derive(Clone, Debug)] +pub struct ValueDef { + pub kind: ValueKind, + pub ty: Type, +} + +#[derive(Clone, Debug)] +pub enum ValueKind { + BlockParam(Block), + Inst(Block, Inst), +} + +#[derive(Clone, Debug, Default)] +pub struct Block { + pub params: Vec, + pub insts: Vec, +} + +#[derive(Clone, Debug)] +pub struct Inst { + pub operator: Operator<'static>, + pub outputs: Vec, + pub inputs: Vec, +} + +#[derive(Clone, Debug)] +pub enum Operand { + Value(ValueId), + Sub(Box), } diff --git a/src/lib.rs b/src/lib.rs index 01e38da..7a84d4b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,8 @@ // Re-export wasmparser and wasmencoder for easier use of the right // version by our embedders. -pub use wasmparser; pub use wasm_encoder; +pub use wasmparser; +pub mod frontend; pub mod ir;