cranelift-wasm hooks to instrument wasm operators (#861)

* cranelift-wasm hooks to instrument wasm operators
This commit is contained in:
Andy Wortman
2019-07-25 09:36:17 -07:00
committed by GitHub
parent feecd23967
commit b7a9d65458
4 changed files with 84 additions and 54 deletions

View File

@@ -42,7 +42,7 @@ use wasmparser::{MemoryImmediate, Operator};
/// Translates wasm operators into Cranelift IR instructions. Returns `true` if it inserted
/// a return.
pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
op: Operator,
op: &Operator,
builder: &mut FunctionBuilder,
state: &mut TranslationState,
environ: &mut FE,
@@ -59,28 +59,28 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
* disappear in the Cranelift Code
***********************************************************************************/
Operator::GetLocal { local_index } => {
let val = builder.use_var(Variable::with_u32(local_index));
let val = builder.use_var(Variable::with_u32(*local_index));
state.push1(val);
let label = ValueLabel::from_u32(local_index);
let label = ValueLabel::from_u32(*local_index);
builder.set_val_label(val, label);
}
Operator::SetLocal { local_index } => {
let val = state.pop1();
builder.def_var(Variable::with_u32(local_index), val);
let label = ValueLabel::from_u32(local_index);
builder.def_var(Variable::with_u32(*local_index), val);
let label = ValueLabel::from_u32(*local_index);
builder.set_val_label(val, label);
}
Operator::TeeLocal { local_index } => {
let val = state.peek1();
builder.def_var(Variable::with_u32(local_index), val);
let label = ValueLabel::from_u32(local_index);
builder.def_var(Variable::with_u32(*local_index), val);
let label = ValueLabel::from_u32(*local_index);
builder.set_val_label(val, label);
}
/********************************** Globals ****************************************
* `get_global` and `set_global` are handled by the environment.
***********************************************************************************/
Operator::GetGlobal { global_index } => {
let val = match state.get_global(builder.func, global_index, environ)? {
let val = match state.get_global(builder.func, *global_index, environ)? {
GlobalVariable::Const(val) => val,
GlobalVariable::Memory { gv, offset, ty } => {
let addr = builder.ins().global_value(environ.pointer_type(), gv);
@@ -91,8 +91,8 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
state.push1(val);
}
Operator::SetGlobal { global_index } => {
match state.get_global(builder.func, global_index, environ)? {
GlobalVariable::Const(_) => panic!("global #{} is a constant", global_index),
match state.get_global(builder.func, *global_index, environ)? {
GlobalVariable::Const(_) => panic!("global #{} is a constant", *global_index),
GlobalVariable::Memory { gv, offset, ty } => {
let addr = builder.ins().global_value(environ.pointer_type(), gv);
let flags = ir::MemFlags::trusted();
@@ -132,19 +132,19 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
***********************************************************************************/
Operator::Block { ty } => {
let next = builder.create_ebb();
if let Ok(ty_cre) = blocktype_to_type(ty) {
if let Ok(ty_cre) = blocktype_to_type(*ty) {
builder.append_ebb_param(next, ty_cre);
}
state.push_block(next, num_return_values(ty)?);
state.push_block(next, num_return_values(*ty)?);
}
Operator::Loop { ty } => {
let loop_body = builder.create_ebb();
let next = builder.create_ebb();
if let Ok(ty_cre) = blocktype_to_type(ty) {
if let Ok(ty_cre) = blocktype_to_type(*ty) {
builder.append_ebb_param(next, ty_cre);
}
builder.ins().jump(loop_body, &[]);
state.push_loop(loop_body, next, num_return_values(ty)?);
state.push_loop(loop_body, next, num_return_values(*ty)?);
builder.switch_to_block(loop_body);
environ.translate_loop_header(builder.cursor())?;
}
@@ -158,10 +158,10 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
// and we add nothing;
// - either the If have an Else clause, in that case the destination of this jump
// instruction will be changed later when we translate the Else operator.
if let Ok(ty_cre) = blocktype_to_type(ty) {
if let Ok(ty_cre) = blocktype_to_type(*ty) {
builder.append_ebb_param(if_not, ty_cre);
}
state.push_if(jump_inst, if_not, num_return_values(ty)?);
state.push_if(jump_inst, if_not, num_return_values(*ty)?);
}
Operator::Else => {
// We take the control frame pushed by the if, use its ebb as the else body
@@ -235,7 +235,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
* `br_table`.
***********************************************************************************/
Operator::Br { relative_depth } => {
let i = state.control_stack.len() - 1 - (relative_depth as usize);
let i = state.control_stack.len() - 1 - (*relative_depth as usize);
let (return_count, br_destination) = {
let frame = &mut state.control_stack[i];
// We signal that all the code that follows until the next End is unreachable
@@ -253,7 +253,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
state.popn(return_count);
state.reachable = false;
}
Operator::BrIf { relative_depth } => translate_br_if(relative_depth, builder, state),
Operator::BrIf { relative_depth } => translate_br_if(*relative_depth, builder, state),
Operator::BrTable { table } => {
let (depths, default) = table.read_table()?;
let mut min_depth = default;
@@ -357,10 +357,10 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
* argument referring to an index in the external functions table of the module.
************************************************************************************/
Operator::Call { function_index } => {
let (fref, num_args) = state.get_direct_func(builder.func, function_index, environ)?;
let (fref, num_args) = state.get_direct_func(builder.func, *function_index, environ)?;
let call = environ.translate_call(
builder.cursor(),
FuncIndex::from_u32(function_index),
FuncIndex::from_u32(*function_index),
fref,
state.peekn(num_args),
)?;
@@ -378,14 +378,14 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
Operator::CallIndirect { index, table_index } => {
// `index` is the index of the function's signature and `table_index` is the index of
// the table to search the function in.
let (sigref, num_args) = state.get_indirect_sig(builder.func, index, environ)?;
let table = state.get_table(builder.func, table_index, environ)?;
let (sigref, num_args) = state.get_indirect_sig(builder.func, *index, environ)?;
let table = state.get_table(builder.func, *table_index, environ)?;
let callee = state.pop1();
let call = environ.translate_call_indirect(
builder.cursor(),
TableIndex::from_u32(table_index),
TableIndex::from_u32(*table_index),
table,
SignatureIndex::from_u32(index),
SignatureIndex::from_u32(*index),
sigref,
callee,
state.peekn(num_args),
@@ -406,14 +406,14 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
Operator::MemoryGrow { reserved } => {
// The WebAssembly MVP only supports one linear memory, but we expect the reserved
// argument to be a memory index.
let heap_index = MemoryIndex::from_u32(reserved);
let heap = state.get_heap(builder.func, reserved, environ)?;
let heap_index = MemoryIndex::from_u32(*reserved);
let heap = state.get_heap(builder.func, *reserved, environ)?;
let val = state.pop1();
state.push1(environ.translate_memory_grow(builder.cursor(), heap_index, heap, val)?)
}
Operator::MemorySize { reserved } => {
let heap_index = MemoryIndex::from_u32(reserved);
let heap = state.get_heap(builder.func, reserved, environ)?;
let heap_index = MemoryIndex::from_u32(*reserved);
let heap = state.get_heap(builder.func, *reserved, environ)?;
state.push1(environ.translate_memory_size(builder.cursor(), heap_index, heap)?);
}
/******************************* Load instructions ***********************************
@@ -423,72 +423,72 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
Operator::I32Load8U {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_load(offset, ir::Opcode::Uload8, I32, builder, state, environ)?;
translate_load(*offset, ir::Opcode::Uload8, I32, builder, state, environ)?;
}
Operator::I32Load16U {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_load(offset, ir::Opcode::Uload16, I32, builder, state, environ)?;
translate_load(*offset, ir::Opcode::Uload16, I32, builder, state, environ)?;
}
Operator::I32Load8S {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_load(offset, ir::Opcode::Sload8, I32, builder, state, environ)?;
translate_load(*offset, ir::Opcode::Sload8, I32, builder, state, environ)?;
}
Operator::I32Load16S {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_load(offset, ir::Opcode::Sload16, I32, builder, state, environ)?;
translate_load(*offset, ir::Opcode::Sload16, I32, builder, state, environ)?;
}
Operator::I64Load8U {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_load(offset, ir::Opcode::Uload8, I64, builder, state, environ)?;
translate_load(*offset, ir::Opcode::Uload8, I64, builder, state, environ)?;
}
Operator::I64Load16U {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_load(offset, ir::Opcode::Uload16, I64, builder, state, environ)?;
translate_load(*offset, ir::Opcode::Uload16, I64, builder, state, environ)?;
}
Operator::I64Load8S {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_load(offset, ir::Opcode::Sload8, I64, builder, state, environ)?;
translate_load(*offset, ir::Opcode::Sload8, I64, builder, state, environ)?;
}
Operator::I64Load16S {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_load(offset, ir::Opcode::Sload16, I64, builder, state, environ)?;
translate_load(*offset, ir::Opcode::Sload16, I64, builder, state, environ)?;
}
Operator::I64Load32S {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_load(offset, ir::Opcode::Sload32, I64, builder, state, environ)?;
translate_load(*offset, ir::Opcode::Sload32, I64, builder, state, environ)?;
}
Operator::I64Load32U {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_load(offset, ir::Opcode::Uload32, I64, builder, state, environ)?;
translate_load(*offset, ir::Opcode::Uload32, I64, builder, state, environ)?;
}
Operator::I32Load {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_load(offset, ir::Opcode::Load, I32, builder, state, environ)?;
translate_load(*offset, ir::Opcode::Load, I32, builder, state, environ)?;
}
Operator::F32Load {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_load(offset, ir::Opcode::Load, F32, builder, state, environ)?;
translate_load(*offset, ir::Opcode::Load, F32, builder, state, environ)?;
}
Operator::I64Load {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_load(offset, ir::Opcode::Load, I64, builder, state, environ)?;
translate_load(*offset, ir::Opcode::Load, I64, builder, state, environ)?;
}
Operator::F64Load {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_load(offset, ir::Opcode::Load, F64, builder, state, environ)?;
translate_load(*offset, ir::Opcode::Load, F64, builder, state, environ)?;
}
/****************************** Store instructions ***********************************
* Wasm specifies an integer alignment flag but we drop it in Cranelift.
@@ -506,7 +506,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
| Operator::F64Store {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_store(offset, ir::Opcode::Store, builder, state, environ)?;
translate_store(*offset, ir::Opcode::Store, builder, state, environ)?;
}
Operator::I32Store8 {
memarg: MemoryImmediate { flags: _, offset },
@@ -514,7 +514,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
| Operator::I64Store8 {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_store(offset, ir::Opcode::Istore8, builder, state, environ)?;
translate_store(*offset, ir::Opcode::Istore8, builder, state, environ)?;
}
Operator::I32Store16 {
memarg: MemoryImmediate { flags: _, offset },
@@ -522,21 +522,21 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
| Operator::I64Store16 {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_store(offset, ir::Opcode::Istore16, builder, state, environ)?;
translate_store(*offset, ir::Opcode::Istore16, builder, state, environ)?;
}
Operator::I64Store32 {
memarg: MemoryImmediate { flags: _, offset },
} => {
translate_store(offset, ir::Opcode::Istore32, builder, state, environ)?;
translate_store(*offset, ir::Opcode::Istore32, builder, state, environ)?;
}
/****************************** Nullary Operators ************************************/
Operator::I32Const { value } => state.push1(builder.ins().iconst(I32, i64::from(value))),
Operator::I64Const { value } => state.push1(builder.ins().iconst(I64, value)),
Operator::I32Const { value } => state.push1(builder.ins().iconst(I32, i64::from(*value))),
Operator::I64Const { value } => state.push1(builder.ins().iconst(I64, *value)),
Operator::F32Const { value } => {
state.push1(builder.ins().f32const(f32_translation(value)));
state.push1(builder.ins().f32const(f32_translation(*value)));
}
Operator::F64Const { value } => {
state.push1(builder.ins().f64const(f64_translation(value)));
state.push1(builder.ins().f64const(f64_translation(*value)));
}
/******************************* Unary Operators *************************************/
Operator::I32Clz | Operator::I64Clz => {

View File

@@ -14,9 +14,11 @@ use cranelift_codegen::cursor::FuncCursor;
use cranelift_codegen::ir::immediates::Offset32;
use cranelift_codegen::ir::{self, InstBuilder};
use cranelift_codegen::isa::TargetFrontendConfig;
use cranelift_frontend::FunctionBuilder;
use failure_derive::Fail;
use std::boxed::Box;
use wasmparser::BinaryReaderError;
use wasmparser::Operator;
/// The value of a WebAssembly global variable.
#[derive(Clone, Copy)]
@@ -253,6 +255,26 @@ pub trait FuncEnvironment {
// By default, don't emit anything.
Ok(())
}
/// Optional callback for the `FunctionEnvironment` performing this translation to maintain
/// internal state or prepare custom state for the operator to translate
fn before_translate_operator(
&mut self,
_op: &Operator,
_builder: &mut FunctionBuilder,
) -> WasmResult<()> {
Ok(())
}
/// Optional callback for the `FunctionEnvironment` performing this translation to maintain
/// internal state or finalize custom state for the operator that was translated
fn after_translate_operator(
&mut self,
_op: &Operator,
_builder: &mut FunctionBuilder,
) -> WasmResult<()> {
Ok(())
}
}
/// An object satisfying the `ModuleEnvironment` trait can be passed as argument to the

View File

@@ -207,7 +207,9 @@ fn parse_function_body<FE: FuncEnvironment + ?Sized>(
while !state.control_stack.is_empty() {
builder.set_srcloc(cur_srcloc(&reader));
let op = reader.read_operator()?;
translate_operator(op, builder, state, environ)?;
environ.before_translate_operator(&op, builder)?;
translate_operator(&op, builder, state, environ)?;
environ.after_translate_operator(&op, builder)?;
}
// The final `End` operator left us in the exit block where we need to manually add a return

View File

@@ -130,8 +130,13 @@ impl ControlStackFrame {
/// - The depth of the two unreachable control blocks stacks, that are manipulated when translating
/// unreachable code;
pub struct TranslationState {
/// A stack of values corresponding to the active values in the input wasm function at this
/// point.
pub stack: Vec<Value>,
/// A stack of active control flow operations at this point in the input wasm function.
pub control_stack: Vec<ControlStackFrame>,
/// Is the current translation state still reachable? This is false when translating operators
/// like End, Return, or Unreachable.
pub reachable: bool,
// Map of global variables that have already been created by `FuncEnvironment::make_global`.
@@ -155,6 +160,7 @@ pub struct TranslationState {
}
impl TranslationState {
/// Construct a new, empty, `TranslationState`
pub fn new() -> Self {
Self {
stack: Vec::new(),
@@ -242,7 +248,7 @@ impl TranslationState {
&self.stack[self.stack.len() - n..]
}
// Push a block on the control stack.
/// Push a block on the control stack.
pub fn push_block(&mut self, following_code: Ebb, num_result_types: usize) {
self.control_stack.push(ControlStackFrame::Block {
destination: following_code,
@@ -252,7 +258,7 @@ impl TranslationState {
});
}
// Push a loop on the control stack.
/// Push a loop on the control stack.
pub fn push_loop(&mut self, header: Ebb, following_code: Ebb, num_result_types: usize) {
self.control_stack.push(ControlStackFrame::Loop {
header,
@@ -262,7 +268,7 @@ impl TranslationState {
});
}
// Push an if on the control stack.
/// Push an if on the control stack.
pub fn push_if(&mut self, branch_inst: Inst, following_code: Ebb, num_result_types: usize) {
self.control_stack.push(ControlStackFrame::If {
branch_inst,