Optimizes an enode by applying any matching mid-end rewrite rules (or store-to-load forwarding, which is a special case), unioning together all possible optimized (or rewritten) forms of this expression into an eclass and returning the `Value` that represents that eclass.
(&mut self, inst: Inst)
| 345 | /// of this expression into an eclass and returning the `Value` |
| 346 | /// that represents that eclass. |
| 347 | fn optimize_pure_enode(&mut self, inst: Inst) -> Value { |
| 348 | // A pure node always has exactly one result. |
| 349 | let orig_value = self.func.dfg.first_result(inst); |
| 350 | |
| 351 | let mut guard = TakeAndReplace::new(self, |x| &mut x.optimized_values); |
| 352 | let (ctx, optimized_values) = guard.get(); |
| 353 | |
| 354 | // Limit rewrite depth. When we apply optimization rules, they |
| 355 | // may create new nodes (values) and those are, recursively, |
| 356 | // optimized eagerly as soon as they are created. So we may |
| 357 | // have more than one ISLE invocation on the stack. (This is |
| 358 | // necessary so that as the toplevel builds the |
| 359 | // right-hand-side expression bottom-up, it uses the "latest" |
| 360 | // optimized values for all the constituent parts.) To avoid |
| 361 | // infinite or problematic recursion, we bound the rewrite |
| 362 | // depth to a small constant here. |
| 363 | const REWRITE_LIMIT: usize = 5; |
| 364 | if ctx.rewrite_depth >= REWRITE_LIMIT { |
| 365 | ctx.stats.rewrite_depth_limit += 1; |
| 366 | return orig_value; |
| 367 | } |
| 368 | ctx.rewrite_depth += 1; |
| 369 | trace!("Incrementing rewrite depth; now {}", ctx.rewrite_depth); |
| 370 | |
| 371 | // Invoke the ISLE toplevel constructor, getting all new |
| 372 | // values produced as equivalents to this value. |
| 373 | trace!("Calling into ISLE with original value {}", orig_value); |
| 374 | ctx.stats.rewrite_rule_invoked += 1; |
| 375 | debug_assert!(optimized_values.is_empty()); |
| 376 | crate::opts::generated_code::constructor_simplify( |
| 377 | &mut IsleContext { ctx }, |
| 378 | orig_value, |
| 379 | optimized_values, |
| 380 | ); |
| 381 | |
| 382 | ctx.stats.rewrite_rule_results += optimized_values.len() as u64; |
| 383 | |
| 384 | // It's not supposed to matter what order `simplify` returns values in. |
| 385 | ctx.ctrl_plane.shuffle(optimized_values); |
| 386 | |
| 387 | let num_matches = optimized_values.len(); |
| 388 | if num_matches > MATCHES_LIMIT { |
| 389 | trace!( |
| 390 | "Reached maximum matches limit; too many optimized values \ |
| 391 | ({num_matches} > {MATCHES_LIMIT}); ignoring rest.", |
| 392 | ); |
| 393 | optimized_values.truncate(MATCHES_LIMIT); |
| 394 | } |
| 395 | |
| 396 | // Sort and deduplicate optimized values, in case multiple |
| 397 | // rules produced the same simplification. |
| 398 | optimized_values.sort_unstable(); |
| 399 | optimized_values.dedup(); |
| 400 | |
| 401 | trace!(" -> returned from ISLE: {orig_value} -> {optimized_values:?}"); |
| 402 | |
| 403 | // Construct a union-node tree representing the new eclass |
| 404 | // that results from rewriting. If any returned value was |
no test coverage detected