From 830bdd5127ce5fcab04105a33c576c309a5bf7b3 Mon Sep 17 00:00:00 2001 From: nalmt Date: Wed, 5 Feb 2020 18:12:56 +0100 Subject: [PATCH] Add maximum threshold for number of blocks per function #951 To fix this case that may take forever to compile: function %a(){ ebb477777777: } We decide to define a maximum threshold for the number of blocks in functions. Based on a large WASM program (https://github.com/mozilla/perf-automation/blob/master/benchmarks/wasm-misc/AngryBots.wasm), its IR functions does not exceed 1414 blocks. A number 100 times greater (100,000 blocks) seems (currently) enough to define our maximum threshold. To make this quick benchmark the cranelift-wasm/src/func_translator.rs file has been modified like this: static mut MAX: usize = 0; pub fn translate_from_reader(...) { [...] builder.finalize(); // the compiler is single threaded unsafe { if func.dfg.num_ebbs() > MAX { MAX = func.dfg.num_ebbs(); println!("MAX {}", MAX); } } Ok(()) } --- cranelift/reader/src/parser.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/cranelift/reader/src/parser.rs b/cranelift/reader/src/parser.rs index f0b866f472..6cedb25b0d 100644 --- a/cranelift/reader/src/parser.rs +++ b/cranelift/reader/src/parser.rs @@ -27,6 +27,9 @@ use std::str::FromStr; use std::{u16, u32}; use target_lexicon::Triple; +/// After some quick benchmarks a program should never have more than 100,000 blocks. +const MAX_BLOCKS_IN_A_FUNCTION: u32 = 100_000; + /// Parse the entire `text` into a list of functions. /// /// Any test commands or target declarations are ignored. @@ -1768,6 +1771,10 @@ impl<'a> Parser<'a> { let block_num = self.match_block("expected block header")?; let block = ctx.add_block(block_num, self.loc)?; + if block_num.as_u32() >= MAX_BLOCKS_IN_A_FUNCTION { + return Err(self.error("too many blocks")); + } + if !self.optional(Token::Colon) { // block-header ::= Block(block) [ * block-params ] ":" self.parse_block_params(ctx, block)?; @@ -2979,6 +2986,24 @@ mod tests { assert!(!is_warning); } + #[test] + fn number_of_blocks() { + let ParseError { + location, + message, + is_warning, + } = Parser::new( + "function %a() { + block100000:", + ) + .parse_function(None) + .unwrap_err(); + + assert_eq!(location.line_number, 2); + assert_eq!(message, "too many blocks"); + assert!(!is_warning); + } + #[test] fn duplicate_jt() { let ParseError {