From 26cfbafb32c4056353abe36debf9e3bcd65ada6f Mon Sep 17 00:00:00 2001 From: "Nicolas B. Pierron" Date: Mon, 16 Sep 2019 15:46:20 +0200 Subject: [PATCH] Add ebb macro to insert a new blocks in legalization output. --- cranelift/codegen/meta/src/cdsl/ast.rs | 68 ++++++++++++++++++++- cranelift/codegen/meta/src/cdsl/xform.rs | 58 ++++++++++++++---- cranelift/codegen/meta/src/gen_legalizer.rs | 40 ++++++++++-- 3 files changed, 148 insertions(+), 18 deletions(-) diff --git a/cranelift/codegen/meta/src/cdsl/ast.rs b/cranelift/codegen/meta/src/cdsl/ast.rs index 910ffe24b2..5618927bb4 100644 --- a/cranelift/codegen/meta/src/cdsl/ast.rs +++ b/cranelift/codegen/meta/src/cdsl/ast.rs @@ -4,9 +4,10 @@ use crate::cdsl::operands::{OperandKind, OperandKindFields}; use crate::cdsl::types::ValueType; use crate::cdsl::typevar::{TypeSetBuilder, TypeVar}; -use cranelift_entity::{entity_impl, PrimaryMap}; +use cranelift_entity::{entity_impl, PrimaryMap, SparseMap, SparseMapValue}; use std::fmt; +use std::iter::IntoIterator; pub(crate) enum Expr { Var(VarIndex), @@ -82,7 +83,7 @@ impl DefPool { pub fn next_index(&self) -> DefIndex { self.pool.next_key() } - pub fn create(&mut self, apply: Apply, defined_vars: Vec) -> DefIndex { + pub fn create_inst(&mut self, apply: Apply, defined_vars: Vec) -> DefIndex { self.pool.push(Def { apply, defined_vars, @@ -94,6 +95,55 @@ impl DefPool { pub(crate) struct DefIndex(u32); entity_impl!(DefIndex); +/// A definition which would lead to generate a block creation. +#[derive(Clone)] +pub(crate) struct Block { + /// Instruction index after which the block entry is set. + pub location: DefIndex, + /// Variable holding the new created block. + pub name: VarIndex, +} + +pub(crate) struct BlockPool { + pool: SparseMap, +} + +impl SparseMapValue for Block { + fn key(&self) -> DefIndex { + self.location + } +} + +impl BlockPool { + pub fn new() -> Self { + Self { + pool: SparseMap::new(), + } + } + pub fn get(&self, index: DefIndex) -> Option<&Block> { + self.pool.get(index) + } + pub fn create_block(&mut self, name: VarIndex, location: DefIndex) { + if self.pool.contains_key(location) { + panic!("Attempt to insert 2 blocks after the same instruction") + } + self.pool.insert(Block { location, name }); + } + pub fn is_empty(&self) -> bool { + self.pool.is_empty() + } +} + +// Implement IntoIterator such that we can iterate over blocks which are in the block pool. +impl<'a> IntoIterator for &'a BlockPool { + type Item = <&'a SparseMap as IntoIterator>::Item; + type IntoIter = <&'a SparseMap as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.pool.into_iter() + } +} + #[derive(Clone, Debug)] pub(crate) enum Literal { /// A value of an enumerated immediate operand. @@ -518,6 +568,7 @@ pub(crate) enum DummyExpr { Var(DummyVar), Literal(Literal), Apply(InstSpec, Vec), + Block(DummyVar), } #[derive(Clone)] @@ -561,6 +612,11 @@ impl ExprBuilder { defined_vars, } } + + pub fn block(name: DummyVar) -> Self { + let expr = DummyExpr::Block(name); + Self { expr } + } } macro_rules! def_rhs { @@ -592,3 +648,11 @@ macro_rules! def { def_rhs!($($tt)*).assign_to(Vec::new()) } } + +// Helper macro to define legalization recipes. +macro_rules! ebb { + // An basic block definition, splitting the current block in 2. + ($block: ident) => { + ExprBuilder::block($block).assign_to(Vec::new()) + }; +} diff --git a/cranelift/codegen/meta/src/cdsl/xform.rs b/cranelift/codegen/meta/src/cdsl/xform.rs index d0a543baec..6304469718 100644 --- a/cranelift/codegen/meta/src/cdsl/xform.rs +++ b/cranelift/codegen/meta/src/cdsl/xform.rs @@ -1,5 +1,6 @@ use crate::cdsl::ast::{ - Apply, DefIndex, DefPool, DummyDef, DummyExpr, Expr, PatternPosition, VarIndex, VarPool, + Apply, BlockPool, DefIndex, DefPool, DummyDef, DummyExpr, Expr, PatternPosition, VarIndex, + VarPool, }; use crate::cdsl::instructions::Instruction; use crate::cdsl::type_inference::{infer_transform, TypeEnvironment}; @@ -22,6 +23,7 @@ pub(crate) struct Transform { pub dst: Vec, pub var_pool: VarPool, pub def_pool: DefPool, + pub block_pool: BlockPool, pub type_env: TypeEnvironment, } @@ -31,6 +33,7 @@ impl Transform { fn new(src: DummyDef, dst: Vec) -> Self { let mut var_pool = VarPool::new(); let mut def_pool = DefPool::new(); + let mut block_pool = BlockPool::new(); let mut input_vars: Vec = Vec::new(); let mut defined_vars: Vec = Vec::new(); @@ -47,6 +50,7 @@ impl Transform { &mut defined_vars, &mut var_pool, &mut def_pool, + &mut block_pool, )[0]; let num_src_inputs = input_vars.len(); @@ -59,6 +63,7 @@ impl Transform { &mut defined_vars, &mut var_pool, &mut def_pool, + &mut block_pool, ); // Sanity checks. @@ -122,6 +127,7 @@ impl Transform { dst, var_pool, def_pool, + block_pool, type_env, } } @@ -224,6 +230,9 @@ fn rewrite_expr( DummyExpr::Apply(..) => { panic!("Recursive apply is not allowed."); } + DummyExpr::Block(_block) => { + panic!("Blocks are not valid arguments."); + } } } @@ -238,8 +247,18 @@ fn rewrite_def_list( defined_vars: &mut Vec, var_pool: &mut VarPool, def_pool: &mut DefPool, + block_pool: &mut BlockPool, ) -> Vec { let mut new_defs = Vec::new(); + // Register variable names of new blocks first as a block name can be used to jump forward. Thus + // the name has to be registered first to avoid misinterpreting it as an input-var. + for dummy_def in dummy_defs.iter() { + if let DummyExpr::Block(ref var) = dummy_def.expr { + var_index(var.name, symbol_table, defined_vars, var_pool); + } + } + + // Iterate over the definitions and blocks, to map variables names to inputs or outputs. for dummy_def in dummy_defs { let def_index = def_pool.next_index(); @@ -251,19 +270,34 @@ fn rewrite_def_list( defined_vars, var_pool, ); - let new_apply = rewrite_expr(position, dummy_def.expr, symbol_table, input_vars, var_pool); + if let DummyExpr::Block(var) = dummy_def.expr { + let var_index = *symbol_table + .get(var.name) + .or_else(|| { + panic!( + "Block {} was not registered during the first visit", + var.name + ) + }) + .unwrap(); + var_pool.get_mut(var_index).set_def(position, def_index); + block_pool.create_block(var_index, def_index); + } else { + let new_apply = + rewrite_expr(position, dummy_def.expr, symbol_table, input_vars, var_pool); - assert!( - def_pool.next_index() == def_index, - "shouldn't have created new defs in the meanwhile" - ); - assert_eq!( - new_apply.inst.value_results.len(), - new_defined_vars.len(), - "number of Var results in instruction is incorrect" - ); + assert!( + def_pool.next_index() == def_index, + "shouldn't have created new defs in the meanwhile" + ); + assert_eq!( + new_apply.inst.value_results.len(), + new_defined_vars.len(), + "number of Var results in instruction is incorrect" + ); - new_defs.push(def_pool.create(new_apply, new_defined_vars)); + new_defs.push(def_pool.create_inst(new_apply, new_defined_vars)); + } } new_defs } diff --git a/cranelift/codegen/meta/src/gen_legalizer.rs b/cranelift/codegen/meta/src/gen_legalizer.rs index 4153fb8c32..bceed3fac6 100644 --- a/cranelift/codegen/meta/src/gen_legalizer.rs +++ b/cranelift/codegen/meta/src/gen_legalizer.rs @@ -401,14 +401,30 @@ fn gen_transform<'a>( // Guard the actual expansion by `predicate`. fmt.line("if predicate {"); fmt.indent(|fmt| { + // If we are adding some blocks, we need to recall the original block, such that we can + // recompute it. + if !transform.block_pool.is_empty() { + fmt.line("let orig_ebb = pos.current_ebb().unwrap();"); + } + // If we're going to delete `inst`, we need to detach its results first so they can be // reattached during pattern expansion. if !replace_inst { fmt.line("pos.func.dfg.clear_results(inst);"); } + // Emit new block creation. + for block in &transform.block_pool { + let var = transform.var_pool.get(block.name); + fmtln!(fmt, "let {} = pos.func.dfg.make_ebb();", var.name); + } + // Emit the destination pattern. for &def_index in &transform.dst { + if let Some(block) = transform.block_pool.get(def_index) { + let var = transform.var_pool.get(block.name); + fmtln!(fmt, "pos.insert_ebb({});", var.name); + } emit_dst_inst( transform.def_pool.get(def_index), &transform.def_pool, @@ -417,16 +433,32 @@ fn gen_transform<'a>( ); } + // Insert a new block after the last instruction, if needed. + let def_next_index = transform.def_pool.next_index(); + if let Some(block) = transform.block_pool.get(def_next_index) { + let var = transform.var_pool.get(block.name); + fmtln!(fmt, "pos.insert_ebb({});", var.name); + } + // Delete the original instruction if we didn't have an opportunity to replace it. if !replace_inst { fmt.line("let removed = pos.remove_inst();"); fmt.line("debug_assert_eq!(removed, inst);"); } - if transform.def_pool.get(transform.src).apply.inst.is_branch { - // A branch might have been legalized into multiple branches, so we need to recompute - // the cfg. - fmt.line("cfg.recompute_ebb(pos.func, pos.current_ebb().unwrap());"); + if transform.block_pool.is_empty() { + if transform.def_pool.get(transform.src).apply.inst.is_branch { + // A branch might have been legalized into multiple branches, so we need to recompute + // the cfg. + fmt.line("cfg.recompute_ebb(pos.func, pos.current_ebb().unwrap());"); + } + } else { + // Update CFG for the new blocks. + fmt.line("cfg.recompute_ebb(pos.func, orig_ebb);"); + for block in &transform.block_pool { + let var = transform.var_pool.get(block.name); + fmtln!(fmt, "cfg.recompute_ebb(pos.func, {});", var.name); + } } fmt.line("return true;");