WIP: command line utility and start of Wasm-parsing frontend.

This commit is contained in:
Chris Fallin 2021-11-13 00:52:35 -08:00
parent 8a2e3e5eed
commit 1cca77ec37
5 changed files with 129 additions and 5 deletions

View file

@ -2,10 +2,14 @@
name = "waffle"
version = "0.0.1"
description = "Wasm Analysis Framework For Lightweight Experiments"
author = ["Chris Fallin <chris@cfallin.org>"]
authors = ["Chris Fallin <chris@cfallin.org>"]
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"

47
src/bin/waffle-util.rs Normal file
View file

@ -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(())
}

39
src/frontend.rs Normal file
View file

@ -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<Module> {
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(())
}

View file

@ -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<FuncDecl>,
pub signatures: Vec<FuncType>,
@ -19,8 +20,40 @@ pub enum FuncDecl {
Body(SignatureId, FunctionBody),
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Default)]
pub struct FunctionBody {
pub locals: Vec<Type>,
pub blocks: Vec<Block>,
pub values: Vec<ValueDef>,
}
#[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<Type>,
pub insts: Vec<Inst>,
}
#[derive(Clone, Debug)]
pub struct Inst {
pub operator: Operator<'static>,
pub outputs: Vec<ValueId>,
pub inputs: Vec<Operand>,
}
#[derive(Clone, Debug)]
pub enum Operand {
Value(ValueId),
Sub(Box<Inst>),
}

View file

@ -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;