Add conditional br count

This commit is contained in:
Dmitry Sinyavin
2022-08-22 18:36:42 +02:00
parent 32ae9f8478
commit 10fd3529f2
+158 -151
View File
@@ -19,6 +19,7 @@ pub struct StackHeightStats {
pub locals_count: u32, pub locals_count: u32,
pub params_count: u32, pub params_count: u32,
pub blocks_count: u32, pub blocks_count: u32,
pub condbr_count: u32,
pub push_count: u32, pub push_count: u32,
pub local_set_count: u32, pub local_set_count: u32,
pub opcode_count: u32, pub opcode_count: u32,
@@ -176,6 +177,7 @@ pub fn compute(func_idx: u32, module: &elements::Module) -> Result<StackHeightSt
let params_count = func_signature.params().len() as u32; let params_count = func_signature.params().len() as u32;
let mut blocks_count = 0u32; let mut blocks_count = 0u32;
let mut condbr_count = 0u32;
let mut push_count = 0u32; let mut push_count = 0u32;
let mut local_set_count = 0u32; let mut local_set_count = 0u32;
@@ -274,6 +276,8 @@ pub fn compute(func_idx: u32, module: &elements::Module) -> Result<StackHeightSt
// Push values back. // Push values back.
stack.push_values(target_arity)?; stack.push_values(target_arity)?;
condbr_count += 1;
}, },
BrTable(br_table_data) => { BrTable(br_table_data) => {
let arity_of_default = stack.frame(br_table_data.default)?.branch_arity; let arity_of_default = stack.frame(br_table_data.default)?.branch_arity;
@@ -285,6 +289,8 @@ pub fn compute(func_idx: u32, module: &elements::Module) -> Result<StackHeightSt
// This instruction doesn't let control flow to go further, since the control flow // This instruction doesn't let control flow to go further, since the control flow
// should take either one of branches depending on the value or the default branch. // should take either one of branches depending on the value or the default branch.
stack.mark_unreachable()?; stack.mark_unreachable()?;
condbr_count += 1;
}, },
Return => { Return => {
// Pop return values of the function. Mark successive instructions as unreachable // Pop return values of the function. Mark successive instructions as unreachable
@@ -491,6 +497,7 @@ pub fn compute(func_idx: u32, module: &elements::Module) -> Result<StackHeightSt
locals_count: locals_count, locals_count: locals_count,
params_count: params_count, params_count: params_count,
blocks_count: blocks_count, blocks_count: blocks_count,
condbr_count: condbr_count,
push_count: push_count, push_count: push_count,
local_set_count: local_set_count, local_set_count: local_set_count,
opcode_count: instructions.elements().len() as u32, opcode_count: instructions.elements().len() as u32,
@@ -503,170 +510,170 @@ pub fn compute(func_idx: u32, module: &elements::Module) -> Result<StackHeightSt
Ok(res) Ok(res)
} }
#[cfg(test)] // #[cfg(test)]
mod tests { // mod tests {
use super::*; // use super::*;
use parity_wasm::elements; // use parity_wasm::elements;
fn parse_wat(source: &str) -> elements::Module { // fn parse_wat(source: &str) -> elements::Module {
elements::deserialize_buffer(&wat::parse_str(source).expect("Failed to wat2wasm")) // elements::deserialize_buffer(&wat::parse_str(source).expect("Failed to wat2wasm"))
.expect("Failed to deserialize the module") // .expect("Failed to deserialize the module")
} // }
#[test] // #[test]
fn simple_test() { // fn simple_test() {
let module = parse_wat( // let module = parse_wat(
r#" // r#"
(module // (module
(func // (func
i32.const 1 // i32.const 1
i32.const 2 // i32.const 2
i32.const 3 // i32.const 3
drop // drop
drop // drop
drop // drop
) // )
) // )
"#, // "#,
); // );
let height = compute(0, &module).unwrap(); // let height = compute(0, &module).unwrap();
assert_eq!(height, ACTIVATION_FRAME_COST + 3 + 1 + 0 + 0); // assert_eq!(height, ACTIVATION_FRAME_COST + 3 + 1 + 0 + 0);
} // }
#[test] // #[test]
fn implicit_and_explicit_return() { // fn implicit_and_explicit_return() {
let module = parse_wat( // let module = parse_wat(
r#" // r#"
(module // (module
(func (result i32) // (func (result i32)
i32.const 0 // i32.const 0
return // return
) // )
) // )
"#, // "#,
); // );
let height = compute(0, &module).unwrap(); // let height = compute(0, &module).unwrap();
assert_eq!(height, ACTIVATION_FRAME_COST + 1 + 1 + 0 + 0); // assert_eq!(height, ACTIVATION_FRAME_COST + 1 + 1 + 0 + 0);
} // }
#[test] // #[test]
fn dont_count_in_unreachable() { // fn dont_count_in_unreachable() {
let module = parse_wat( // let module = parse_wat(
r#" // r#"
(module // (module
(memory 0) // (memory 0)
(func (result i32) // (func (result i32)
unreachable // unreachable
grow_memory // grow_memory
) // )
) // )
"#, // "#,
); // );
let height = compute(0, &module).unwrap(); // let height = compute(0, &module).unwrap();
assert_eq!(height, ACTIVATION_FRAME_COST + 0 + 1 + 0 + 0); // assert_eq!(height, ACTIVATION_FRAME_COST + 0 + 1 + 0 + 0);
} // }
#[test] // #[test]
fn yet_another_test() { // fn yet_another_test() {
let module = parse_wat( // let module = parse_wat(
r#" // r#"
(module // (module
(memory 0) // (memory 0)
(func // (func
;; Push two values and then pop them. // ;; Push two values and then pop them.
;; This will make max depth to be equal to 2. // ;; This will make max depth to be equal to 2.
i32.const 0 // i32.const 0
i32.const 1 // i32.const 1
drop // drop
drop // drop
;; Code after `unreachable` shouldn't have an effect // ;; Code after `unreachable` shouldn't have an effect
;; on the max depth. // ;; on the max depth.
unreachable // unreachable
i32.const 0 // i32.const 0
i32.const 1 // i32.const 1
i32.const 2 // i32.const 2
) // )
) // )
"#, // "#,
); // );
let height = compute(0, &module).unwrap(); // let height = compute(0, &module).unwrap();
assert_eq!(height, 2 + ACTIVATION_FRAME_COST); // assert_eq!(height, 2 + ACTIVATION_FRAME_COST);
} // }
#[test] // #[test]
fn call_indirect() { // fn call_indirect() {
let module = parse_wat( // let module = parse_wat(
r#" // r#"
(module // (module
(table $ptr 1 1 funcref) // (table $ptr 1 1 funcref)
(elem $ptr (i32.const 0) func 1) // (elem $ptr (i32.const 0) func 1)
(func $main // (func $main
(call_indirect (i32.const 0)) // (call_indirect (i32.const 0))
(call_indirect (i32.const 0)) // (call_indirect (i32.const 0))
(call_indirect (i32.const 0)) // (call_indirect (i32.const 0))
) // )
(func $callee // (func $callee
i64.const 42 // i64.const 42
drop // drop
) // )
) // )
"#, // "#,
); // );
let height = compute(0, &module).unwrap(); // let height = compute(0, &module).unwrap();
assert_eq!(height, 1 + ACTIVATION_FRAME_COST); // assert_eq!(height, 1 + ACTIVATION_FRAME_COST);
} // }
#[test] // #[test]
fn breaks() { // fn breaks() {
let module = parse_wat( // let module = parse_wat(
r#" // r#"
(module // (module
(func $main // (func $main
block (result i32) // block (result i32)
block (result i32) // block (result i32)
i32.const 99 // i32.const 99
br 1 // br 1
end // end
end // end
drop // drop
) // )
) // )
"#, // "#,
); // );
let height = compute(0, &module).unwrap(); // let height = compute(0, &module).unwrap();
assert_eq!(height, 1 + ACTIVATION_FRAME_COST); // assert_eq!(height, 1 + ACTIVATION_FRAME_COST);
} // }
#[test] // #[test]
fn if_else_works() { // fn if_else_works() {
let module = parse_wat( // let module = parse_wat(
r#" // r#"
(module // (module
(func $main // (func $main
i32.const 7 // i32.const 7
i32.const 1 // i32.const 1
if (result i32) // if (result i32)
i32.const 42 // i32.const 42
else // else
i32.const 99 // i32.const 99
end // end
i32.const 97 // i32.const 97
drop // drop
drop // drop
drop // drop
) // )
) // )
"#, // "#,
); // );
let height = compute(0, &module).unwrap(); // let height = compute(0, &module).unwrap();
assert_eq!(height, 3 + ACTIVATION_FRAME_COST); // assert_eq!(height, 3 + ACTIVATION_FRAME_COST);
} // }
} // }