diff --git a/cranelift/isle/.gitmodules b/cranelift/isle/.gitmodules new file mode 100644 index 0000000000..dd607ad0d9 --- /dev/null +++ b/cranelift/isle/.gitmodules @@ -0,0 +1,3 @@ +[submodule "wasmtime"] + path = wasmtime + url = https://github.com/cfallin/wasmtime diff --git a/cranelift/isle/Cargo.lock b/cranelift/isle/Cargo.lock index b890d9e546..f24658108c 100644 --- a/cranelift/isle/Cargo.lock +++ b/cranelift/isle/Cargo.lock @@ -62,6 +62,7 @@ version = "0.1.0" dependencies = [ "env_logger", "log", + "peepmatic-automata", "thiserror", ] @@ -86,6 +87,10 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" +[[package]] +name = "peepmatic-automata" +version = "0.75.0" + [[package]] name = "proc-macro2" version = "1.0.28" diff --git a/cranelift/isle/Cargo.toml b/cranelift/isle/Cargo.toml index 8774800064..590d8e69bb 100644 --- a/cranelift/isle/Cargo.toml +++ b/cranelift/isle/Cargo.toml @@ -9,3 +9,4 @@ license = "Apache-2.0 WITH LLVM-exception" log = "0.4" env_logger = "0.8" thiserror = "1.0" +peepmatic-automata = { version = "*", path = "wasmtime/cranelift/peepmatic/crates/automata" } diff --git a/cranelift/isle/src/codegen.rs b/cranelift/isle/src/codegen.rs new file mode 100644 index 0000000000..e6fb4605b2 --- /dev/null +++ b/cranelift/isle/src/codegen.rs @@ -0,0 +1,123 @@ +//! Generate Rust code from a series of Sequences. + +use crate::ir::{lower_rule, ExprSequence, PatternInst, PatternSequence, Value}; +use crate::sema::{RuleId, TermEnv, TermId, TypeEnv}; +use peepmatic_automata::{Automaton, Builder as AutomatonBuilder}; +use std::collections::HashMap; + +// TODO: automata built by output term as well + +/// Builder context for one function in generated code corresponding +/// to one root input term. +struct TermFunctionBuilder { + root_term: TermId, + automaton: AutomatonBuilder, +} + +impl TermFunctionBuilder { + fn new(root_term: TermId) -> Self { + TermFunctionBuilder { + root_term, + automaton: AutomatonBuilder::new(), + } + } + + fn add_rule(&mut self, pattern_seq: PatternSequence, expr_seq: ExprSequence) { + let mut insertion = self.automaton.insert(); + + let mut out_idx = 0; + for (i, inst) in pattern_seq.insts.into_iter().enumerate() { + // Determine how much of the output we can emit at this + // stage (with the `Value`s that will be defined so far, + // given input insts 0..=i). + let out_start = out_idx; + let mut out_end = out_start; + while out_end < expr_seq.insts.len() { + let mut max_input_inst = 0; + expr_seq.insts[out_end].visit_values(|val| { + if let Value::Pattern { inst, .. } = val { + max_input_inst = std::cmp::max(max_input_inst, inst.index()); + } + }); + if max_input_inst > i { + break; + } + out_end += 1; + } + + // Create an ExprSequence for the instructions that we can + // output at this point. + let out_insts = expr_seq.insts[out_start..out_end] + .iter() + .cloned() + .collect::>(); + let out_seq = ExprSequence { insts: out_insts }; + out_idx = out_end; + + insertion.next(inst, out_seq); + } + + insertion.finish(); + } +} + +struct TermFunctionsBuilder<'a> { + typeenv: &'a TypeEnv, + termenv: &'a TermEnv, + builders_by_input: HashMap, + builders_by_output: HashMap, +} + +impl<'a> TermFunctionsBuilder<'a> { + fn new(typeenv: &'a TypeEnv, termenv: &'a TermEnv) -> Self { + Self { + builders_by_input: HashMap::new(), + builders_by_output: HashMap::new(), + typeenv, + termenv, + } + } + + fn build(&mut self) { + for rule in 0..self.termenv.rules.len() { + let rule = RuleId(rule); + let (lhs_root, pattern, rhs_root, expr) = lower_rule(self.typeenv, self.termenv, rule); + if let Some(input_root_term) = lhs_root { + self.builders_by_input + .entry(input_root_term) + .or_insert_with(|| TermFunctionBuilder::new(input_root_term)) + .add_rule(pattern.clone(), expr.clone()); + } + if let Some(output_root_term) = rhs_root { + self.builders_by_output + .entry(output_root_term) + .or_insert_with(|| TermFunctionBuilder::new(output_root_term)) + .add_rule(pattern, expr); + } + } + } + + fn create_automata(self) -> Automata { + let automata_by_input = self + .builders_by_input + .into_iter() + .map(|(k, mut v)| (k, v.automaton.finish())) + .collect::>(); + let automata_by_output = self + .builders_by_output + .into_iter() + .map(|(k, mut v)| (k, v.automaton.finish())) + .collect::>(); + Automata { + automata_by_input, + automata_by_output, + } + } +} + +pub struct Automata { + pub automata_by_input: HashMap>, + pub automata_by_output: HashMap>, +} + +impl Automata {} diff --git a/cranelift/isle/src/compile.rs b/cranelift/isle/src/compile.rs index 0aeea048b9..1544b4c075 100644 --- a/cranelift/isle/src/compile.rs +++ b/cranelift/isle/src/compile.rs @@ -1,111 +1 @@ //! Compilation process, from AST to Sema to Sequences of Insts. - -use crate::error::*; -use crate::{ast, ir, sema}; -use std::collections::HashMap; - -/// A Compiler manages the compilation pipeline from AST to Sequences. -pub struct Compiler<'a> { - ast: &'a ast::Defs, - type_env: sema::TypeEnv, - term_env: sema::TermEnv, - seqs: Vec, - // TODO: if this becomes a perf issue, then build a better data - // structure. For now we index on root term/variant. - // - // TODO: index at callsites (extractors/constructors) too. We'll - // need tree-summaries of arg and expected return value at each - // callsite. - term_db: HashMap, -} - -#[derive(Clone, Debug, Default)] -struct TermData { - producers: Vec<(ir::TreeSummary, sema::RuleId)>, - consumers: Vec<(ir::TreeSummary, sema::RuleId)>, - has_constructor: bool, - has_extractor: bool, -} - -pub type CompileResult = Result; - -impl<'a> Compiler<'a> { - pub fn new(ast: &'a ast::Defs) -> CompileResult> { - let mut type_env = sema::TypeEnv::from_ast(ast)?; - let term_env = sema::TermEnv::from_ast(&mut type_env, ast)?; - Ok(Compiler { - ast, - type_env, - term_env, - seqs: vec![], - term_db: HashMap::new(), - }) - } - - pub fn build_sequences(&mut self) -> CompileResult<()> { - for rid in 0..self.term_env.rules.len() { - let rid = sema::RuleId(rid); - let seq = ir::Sequence::from_rule(&self.type_env, &self.term_env, rid); - self.seqs.push(seq); - } - Ok(()) - } - - pub fn collect_tree_summaries(&mut self) -> CompileResult<()> { - // For each rule, compute summaries of its LHS and RHS, then - // index it in the appropriate TermData. - for (i, seq) in self.seqs.iter().enumerate() { - let rule_id = sema::RuleId(i); - let consumer_summary = seq.input_tree_summary(); - let producer_summary = seq.output_tree_summary(); - if let Some(consumer_root_term) = consumer_summary.root() { - let consumer_termdb = self - .term_db - .entry(consumer_root_term.clone()) - .or_insert_with(|| Default::default()); - consumer_termdb.consumers.push((consumer_summary, rule_id)); - } - if let Some(producer_root_term) = producer_summary.root() { - let producer_termdb = self - .term_db - .entry(producer_root_term.clone()) - .or_insert_with(|| Default::default()); - producer_termdb.consumers.push((producer_summary, rule_id)); - } - } - - // For each term, if a constructor and/or extractor is - // present, note that. - for term in &self.term_env.terms { - if let sema::TermKind::Regular { - extractor, - constructor, - } = term.kind - { - if !extractor.is_some() && !constructor.is_some() { - continue; - } - let entry = self - .term_db - .entry(ir::TermOrVariant::Term(term.id)) - .or_insert_with(|| Default::default()); - if extractor.is_some() { - entry.has_extractor = true; - } - if constructor.is_some() { - entry.has_constructor = true; - } - } - } - - Ok(()) - } - - pub fn inline_internal_terms(&mut self) -> CompileResult<()> { - unimplemented!() - } - - pub fn to_sequences(self) -> Vec { - self.seqs - } -} diff --git a/cranelift/isle/src/ir.rs b/cranelift/isle/src/ir.rs index 3bf6c59a4c..850200263c 100644 --- a/cranelift/isle/src/ir.rs +++ b/cranelift/isle/src/ir.rs @@ -2,23 +2,24 @@ use crate::declare_id; use crate::sema::*; -use std::collections::hash_map::Entry as HashEntry; use std::collections::HashMap; declare_id!(InstId); #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Value(InstId, usize); +pub enum Value { + /// A value produced by an instruction in the Pattern (LHS). + Pattern { inst: InstId, output: usize }, + /// A value produced by an instruction in the Expr (RHS). + Expr { inst: InstId, output: usize }, +} -/// A single node in the sea-of-nodes. Each node produces one value. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub enum Inst { +/// A single Pattern instruction. +#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum PatternInst { /// Get the input root-term value. Arg { ty: TypeId }, - /// Set the return value. Produces no values. - Return { ty: TypeId, value: Value }, - /// Match a value as equal to another value. Produces no values. MatchEqual { a: Value, b: Value, ty: TypeId }, @@ -46,7 +47,11 @@ pub enum Inst { arg_tys: Vec, term: TermId, }, +} +/// A single Expr instruction. +#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum ExprInst { /// Produce a constant integer. ConstInt { ty: TypeId, val: i64 }, @@ -64,120 +69,50 @@ pub enum Inst { term: TermId, }, - /// Copy a value. Used mainly when rewriting/inlining. - Copy { ty: TypeId, val: Value }, - - /// A non-operation (nop). Used to "nop out" unused instructions - /// without renumbering all values. - Nop, + /// Set the return value. Produces no values. + Return { ty: TypeId, value: Value }, } -impl Inst { - fn map_values Value>(&self, f: F) -> Self { +impl ExprInst { + pub fn visit_values(&self, mut f: F) { match self { - &Inst::Arg { ty } => Inst::Arg { ty }, - &Inst::Return { ty, value } => Inst::Return { - ty, - value: f(value), - }, - &Inst::MatchEqual { a, b, ty } => Inst::MatchEqual { - a: f(a), - b: f(b), - ty, - }, - &Inst::MatchInt { input, ty, int_val } => Inst::MatchInt { - input: f(input), - ty, - int_val, - }, - &Inst::MatchVariant { - input, - input_ty, - ref arg_tys, - variant, - } => Inst::MatchVariant { - input: f(input), - input_ty, - arg_tys: arg_tys.clone(), - variant, - }, - &Inst::Extract { - input, - input_ty, - ref arg_tys, - term, - } => Inst::Extract { - input: f(input), - input_ty, - arg_tys: arg_tys.clone(), - term, - }, - &Inst::ConstInt { ty, val } => Inst::ConstInt { ty, val }, - &Inst::CreateVariant { - ref inputs, - ty, - variant, - } => Inst::CreateVariant { - inputs: inputs - .iter() - .map(|(i, ty)| (f(*i), *ty)) - .collect::>(), - ty, - variant, - }, - &Inst::Construct { - ref inputs, - ty, - term, - } => Inst::Construct { - inputs: inputs - .iter() - .map(|(i, ty)| (f(*i), *ty)) - .collect::>(), - ty, - term, - }, - &Inst::Copy { ty, val } => Inst::Copy { ty, val: f(val) }, - &Inst::Nop => Inst::Nop, - } - } - - fn map_insts InstId>(&self, f: F) -> Self { - self.map_values(|val| Value(f(val.0), val.1)) - } - - fn num_results(&self) -> usize { - match self { - &Inst::Arg { .. } - | &Inst::ConstInt { .. } - | &Inst::Construct { .. } - | &Inst::CreateVariant { .. } - | &Inst::Copy { .. } => 1, - &Inst::Return { .. } | &Inst::MatchEqual { .. } | &Inst::MatchInt { .. } => 0, - &Inst::Extract { ref arg_tys, .. } | &Inst::MatchVariant { ref arg_tys, .. } => { - arg_tys.len() + &ExprInst::ConstInt { .. } => {} + &ExprInst::Construct { ref inputs, .. } + | &ExprInst::CreateVariant { ref inputs, .. } => { + for (input, _ty) in inputs { + f(*input); + } + } + &ExprInst::Return { value, .. } => { + f(value); } - &Inst::Nop => 0, } } } -impl Value { - fn map_inst InstId>(&self, f: F) -> Self { - Value(f(self.0), self.1) - } -} - -/// A linear sequence of instructions that either convert an input -/// value to an output value, or fail. +/// A linear sequence of instructions that match on and destructure an +/// argument. A pattern is fallible (may not match). If it does not +/// fail, its result consists of the values produced by the +/// `PatternInst`s, which may be used by a subsequent `Expr`. #[derive(Clone, Debug, PartialEq, Eq, Hash, Default)] -pub struct Sequence { - /// Instruction sequence. InstId indexes into this sequence. - insts: Vec, +pub struct PatternSequence { + /// Instruction sequence for pattern. InstId indexes into this + /// sequence for `Value::Pattern` values. + pub insts: Vec, } -impl Sequence { - fn add_inst(&mut self, inst: Inst) -> InstId { +/// A linear sequence of instructions that produce a new value from +/// the right-hand side of a rule, given bindings that come from a +/// `Pattern` derived from the left-hand side. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)] +pub struct ExprSequence { + /// Instruction sequence for expression. InstId indexes into this + /// sequence for `Value::Expr` values. + pub insts: Vec, +} + +impl PatternSequence { + fn add_inst(&mut self, inst: PatternInst) -> InstId { let id = InstId(self.insts.len()); self.insts.push(inst); id @@ -185,20 +120,16 @@ impl Sequence { fn add_arg(&mut self, ty: TypeId) -> Value { let inst = InstId(self.insts.len()); - self.add_inst(Inst::Arg { ty }); - Value(inst, 0) - } - - fn add_return(&mut self, ty: TypeId, value: Value) { - self.add_inst(Inst::Return { ty, value }); + self.add_inst(PatternInst::Arg { ty }); + Value::Pattern { inst, output: 0 } } fn add_match_equal(&mut self, a: Value, b: Value, ty: TypeId) { - self.add_inst(Inst::MatchEqual { a, b, ty }); + self.add_inst(PatternInst::MatchEqual { a, b, ty }); } fn add_match_int(&mut self, input: Value, ty: TypeId, int_val: i64) { - self.add_inst(Inst::MatchInt { input, ty, int_val }); + self.add_inst(PatternInst::MatchInt { input, ty, int_val }); } fn add_match_variant( @@ -211,11 +142,11 @@ impl Sequence { let inst = InstId(self.insts.len()); let mut outs = vec![]; for (i, _arg_ty) in arg_tys.iter().enumerate() { - let val = Value(inst, i); + let val = Value::Pattern { inst, output: i }; outs.push(val); } let arg_tys = arg_tys.iter().cloned().collect(); - self.add_inst(Inst::MatchVariant { + self.add_inst(PatternInst::MatchVariant { input, input_ty, arg_tys, @@ -234,11 +165,11 @@ impl Sequence { let inst = InstId(self.insts.len()); let mut outs = vec![]; for (i, _arg_ty) in arg_tys.iter().enumerate() { - let val = Value(inst, i); + let val = Value::Pattern { inst, output: i }; outs.push(val); } let arg_tys = arg_tys.iter().cloned().collect(); - self.add_inst(Inst::Extract { + self.add_inst(PatternInst::Extract { input, input_ty, arg_tys, @@ -247,61 +178,39 @@ impl Sequence { outs } - fn add_const_int(&mut self, ty: TypeId, val: i64) -> Value { - let inst = InstId(self.insts.len()); - self.add_inst(Inst::ConstInt { ty, val }); - Value(inst, 0) - } - - fn add_create_variant( - &mut self, - inputs: &[(Value, TypeId)], - ty: TypeId, - variant: VariantId, - ) -> Value { - let inst = InstId(self.insts.len()); - let inputs = inputs.iter().cloned().collect(); - self.add_inst(Inst::CreateVariant { - inputs, - ty, - variant, - }); - Value(inst, 0) - } - - fn add_construct(&mut self, inputs: &[(Value, TypeId)], ty: TypeId, term: TermId) -> Value { - let inst = InstId(self.insts.len()); - let inputs = inputs.iter().cloned().collect(); - self.add_inst(Inst::Construct { inputs, ty, term }); - Value(inst, 0) - } - + /// Generate PatternInsts to match the given (sub)pattern. Works + /// recursively down the AST. Returns the root term matched by + /// this pattern, if any. fn gen_pattern( &mut self, input: Value, typeenv: &TypeEnv, termenv: &TermEnv, pat: &Pattern, - vars: &mut HashMap, - ) { + vars: &mut HashMap, Value)>, + ) -> Option { match pat { &Pattern::BindPattern(_ty, var, ref subpat) => { // Bind the appropriate variable and recurse. assert!(!vars.contains_key(&var)); - vars.insert(var, input); - self.gen_pattern(input, typeenv, termenv, &*subpat, vars); + vars.insert(var, (None, input)); // bind first, so subpat can use it + let root_term = self.gen_pattern(input, typeenv, termenv, &*subpat, vars); + vars.get_mut(&var).unwrap().0 = root_term; + root_term } &Pattern::Var(ty, var) => { // Assert that the value matches the existing bound var. - let var_val = vars + let (var_val_term, var_val) = vars .get(&var) .cloned() .expect("Variable should already be bound"); self.add_match_equal(input, var_val, ty); + var_val_term } &Pattern::ConstInt(ty, value) => { // Assert that the value matches the constant integer. self.add_match_int(input, ty, value); + None } &Pattern::Term(ty, term, ref args) => { // Determine whether the term has an external extractor or not. @@ -313,35 +222,83 @@ impl Sequence { for (subpat, value) in args.iter().zip(arg_values.into_iter()) { self.gen_pattern(value, typeenv, termenv, subpat, vars); } + None } &TermKind::Regular { .. } => { let arg_values = self.add_extract(input, ty, arg_tys, term); for (subpat, value) in args.iter().zip(arg_values.into_iter()) { self.gen_pattern(value, typeenv, termenv, subpat, vars); } + Some(term) } } } &Pattern::Wildcard(_ty) => { // Nothing! + None } } } +} +impl ExprSequence { + fn add_inst(&mut self, inst: ExprInst) -> InstId { + let id = InstId(self.insts.len()); + self.insts.push(inst); + id + } + + fn add_const_int(&mut self, ty: TypeId, val: i64) -> Value { + let inst = InstId(self.insts.len()); + self.add_inst(ExprInst::ConstInt { ty, val }); + Value::Expr { inst, output: 0 } + } + + fn add_create_variant( + &mut self, + inputs: &[(Value, TypeId)], + ty: TypeId, + variant: VariantId, + ) -> Value { + let inst = InstId(self.insts.len()); + let inputs = inputs.iter().cloned().collect(); + self.add_inst(ExprInst::CreateVariant { + inputs, + ty, + variant, + }); + Value::Expr { inst, output: 0 } + } + + fn add_construct(&mut self, inputs: &[(Value, TypeId)], ty: TypeId, term: TermId) -> Value { + let inst = InstId(self.insts.len()); + let inputs = inputs.iter().cloned().collect(); + self.add_inst(ExprInst::Construct { inputs, ty, term }); + Value::Expr { inst, output: 0 } + } + + fn add_return(&mut self, ty: TypeId, value: Value) { + self.add_inst(ExprInst::Return { ty, value }); + } + + /// Creates a sequence of ExprInsts to generate the given + /// expression value. Returns the value ID as well as the root + /// term ID, if any. fn gen_expr( &mut self, typeenv: &TypeEnv, termenv: &TermEnv, expr: &Expr, - vars: &HashMap, - ) -> Value { + vars: &HashMap, Value)>, + ) -> (Option, Value) { match expr { - &Expr::ConstInt(ty, val) => self.add_const_int(ty, val), + &Expr::ConstInt(ty, val) => (None, self.add_const_int(ty, val)), &Expr::Let(_ty, ref bindings, ref subexpr) => { let mut vars = vars.clone(); for &(var, _var_ty, ref var_expr) in bindings { - let var_value = self.gen_expr(typeenv, termenv, &*var_expr, &vars); - vars.insert(var, var_value); + let (var_value_term, var_value) = + self.gen_expr(typeenv, termenv, &*var_expr, &vars); + vars.insert(var, (var_value_term, var_value)); } self.gen_expr(typeenv, termenv, &*subexpr, &vars) } @@ -351,739 +308,102 @@ impl Sequence { let mut arg_values_tys = vec![]; for (arg_ty, arg_expr) in termdata.arg_tys.iter().cloned().zip(arg_exprs.iter()) { arg_values_tys - .push((self.gen_expr(typeenv, termenv, &*arg_expr, &vars), arg_ty)); + .push((self.gen_expr(typeenv, termenv, &*arg_expr, &vars).1, arg_ty)); } match &termdata.kind { - &TermKind::EnumVariant { variant } => { - self.add_create_variant(&arg_values_tys[..], ty, variant) - } - &TermKind::Regular { .. } => self.add_construct(&arg_values_tys[..], ty, term), + &TermKind::EnumVariant { variant } => ( + None, + self.add_create_variant(&arg_values_tys[..], ty, variant), + ), + &TermKind::Regular { .. } => ( + Some(termdata.id), + self.add_construct(&arg_values_tys[..], ty, term), + ), } } } } } -impl Sequence { - /// Build a sequence from a rule. - pub fn from_rule(tyenv: &TypeEnv, termenv: &TermEnv, rule: RuleId) -> Sequence { - let mut seq: Sequence = Default::default(); +/// Build a sequence from a rule. +pub fn lower_rule( + tyenv: &TypeEnv, + termenv: &TermEnv, + rule: RuleId, +) -> ( + Option, + PatternSequence, + Option, + ExprSequence, +) { + let mut pattern_seq: PatternSequence = Default::default(); + let mut expr_seq: ExprSequence = Default::default(); - // Lower the pattern, starting from the root input value. - let ruledata = &termenv.rules[rule.index()]; - let input_ty = ruledata.lhs.ty(); - let input = seq.add_arg(input_ty); - let mut vars = HashMap::new(); - seq.gen_pattern(input, tyenv, termenv, &ruledata.lhs, &mut vars); + // Lower the pattern, starting from the root input value. + let ruledata = &termenv.rules[rule.index()]; + let input_ty = ruledata.lhs.ty(); + let input = pattern_seq.add_arg(input_ty); + let mut vars = HashMap::new(); + let lhs_root_term = pattern_seq.gen_pattern(input, tyenv, termenv, &ruledata.lhs, &mut vars); - // Lower the expression, making use of the bound variables - // from the pattern. - let rhs_root = seq.gen_expr(tyenv, termenv, &ruledata.rhs, &vars); - // Return the root RHS value. - let output_ty = ruledata.rhs.ty(); - seq.add_return(output_ty, rhs_root); + // Lower the expression, making use of the bound variables + // from the pattern. + let (rhs_root_term, rhs_root) = expr_seq.gen_expr(tyenv, termenv, &ruledata.rhs, &vars); + // Return the root RHS value. + let output_ty = ruledata.rhs.ty(); + expr_seq.add_return(output_ty, rhs_root); - seq - } - - /// Inline sequence(s) in place of given instructions. - pub fn inline(&self, inlines: Vec<(InstId, &'_ Sequence)>) -> Sequence { - let mut seq: Sequence = Default::default(); - // Map from inst ID in this seq to inst ID in final seq. - let mut inst_map: HashMap = HashMap::new(); - - let mut next_inline = 0; - for (id, inst) in self.insts.iter().enumerate() { - let orig_inst_id = InstId(id); - - // If this is an inlining point, do the inlining. The - // inlining point must be at a Construct or Extract call. - // - // - For a Construct inlining, we emit the Construct - // *first*, taking its output value as the arg for the - // invoked sequence. The returned value will in turn be - // substituted for that value at the end of inlining. - // - // - For an Extract inlining, we emit the sequence first, - // taking the input of the Extract as the arg for the - // invoked sequence. The returned value will then be the - // new input to the Extract. - - if next_inline < inlines.len() && inlines[next_inline].0 == orig_inst_id { - let inlined_seq = &inlines[next_inline].1; - next_inline += 1; - - let (arg, arg_ty) = match inst { - &Inst::Construct { ty, .. } => { - // Emit the Construct, mapping its input - // values across the mapping, and saving its - // output as the arg for the inlined sequence. - let inst = inst.map_insts(|id| { - inst_map - .get(&id) - .cloned() - .expect("Should have inst mapping") - }); - let new_inst_id = seq.add_inst(inst); - (Value(new_inst_id, 0), ty) - } - &Inst::Extract { - input, input_ty, .. - } => { - // Map the input and save it as the arg, but - // don't emit the Extract yet. - ( - input.map_inst(|id| { - inst_map - .get(&id) - .cloned() - .expect("Should have inst mapping") - }), - input_ty, - ) - } - _ => panic!("Unexpected instruction {:?} at inlining point", inst), - }; - - // Copy the inlined insts into the output sequence. We - // map `Arg` to the input, and save the `Ret`, which - // must come last. - let mut inlined_inst_map: HashMap = HashMap::new(); - let mut ret: Option<(InstId, TypeId)> = None; - for (i, inst) in inlined_seq.insts.iter().enumerate() { - let inlined_orig_inst_id = InstId(i); - let new_inst_id = InstId(seq.insts.len()); - let inst = match inst { - &Inst::Return { ty, value } => { - let value = - value.map_inst(|id| inlined_inst_map.get(&id).cloned().unwrap()); - ret = Some((new_inst_id, ty)); - Inst::Copy { ty, val: value } - } - &Inst::Arg { ty } => { - assert_eq!(ty, arg_ty); - Inst::Copy { ty, val: arg } - } - _ => inst.map_insts(|id| inlined_inst_map.get(&id).cloned().unwrap()), - }; - let new_id = seq.add_inst(inst); - inlined_inst_map.insert(inlined_orig_inst_id, new_id); - } - - // Now, emit the Extract if appropriate (it comes - // after the inlined sequence, while Construct goes - // before), and map the old inst ID to the resulting - // output of either the Extract or the return above. - let final_inst_id = match inst { - &Inst::Extract { - input_ty, - ref arg_tys, - term, - .. - } => { - let input = Value(ret.unwrap().0, 0); - seq.add_inst(Inst::Extract { - input, - input_ty, - arg_tys: arg_tys.clone(), - term, - }) - } - &Inst::Construct { .. } => ret.unwrap().0, - _ => unreachable!(), - }; - - inst_map.insert(orig_inst_id, final_inst_id); - } else { - // Non-inlining-point instruction. Just copy over, - // mapping values as appropriate. - let inst = inst.map_insts(|id| { - inst_map - .get(&id) - .cloned() - .expect("inst ID should be present") - }); - let new_id = seq.add_inst(inst); - inst_map.insert(orig_inst_id, new_id); - } - } - - seq - } - - /// Perform constant-propagation / simplification across - /// construct/extract pairs, variants and integer values, and - /// copies. - pub fn simplify(&self) -> Option { - #[derive(Clone, Debug)] - enum SymbolicValue { - Value(Value), - ConstInt(Value, i64), - Variant(Value, VariantId, Vec), - Term(Value, TermId, Vec), - } - impl SymbolicValue { - fn to_value(&self) -> Value { - match self { - &SymbolicValue::Value(v) => v, - &SymbolicValue::ConstInt(v, ..) => v, - &SymbolicValue::Variant(v, ..) => v, - &SymbolicValue::Term(v, ..) => v, - } - } - } - let mut value_map: HashMap = HashMap::new(); - let mut seq: Sequence = Default::default(); - - for (i, inst) in self.insts.iter().enumerate() { - let orig_inst_id = InstId(i); - match inst { - &Inst::Arg { .. } => { - let new_inst = seq.add_inst(inst.clone()); - value_map.insert( - Value(orig_inst_id, 0), - SymbolicValue::Value(Value(new_inst, 0)), - ); - } - &Inst::Return { ty, value } => { - let inst = Inst::Return { - ty, - value: value_map.get(&value).unwrap().to_value(), - }; - seq.add_inst(inst); - } - &Inst::MatchEqual { a, b, ty } => { - let sym_a = value_map.get(&a).unwrap(); - let sym_b = value_map.get(&b).unwrap(); - match (sym_a, sym_b) { - ( - &SymbolicValue::ConstInt(_, int_a), - &SymbolicValue::ConstInt(_, int_b), - ) => { - if int_a == int_b { - // No-op -- we can skip it. - continue; - } else { - // We can't possibly match! - return None; - } - } - ( - &SymbolicValue::Term(_, term_a, _), - &SymbolicValue::Term(_, term_b, _), - ) => { - if term_a != term_b { - return None; - } - } - ( - &SymbolicValue::Variant(_, var_a, _), - &SymbolicValue::Variant(_, var_b, _), - ) => { - if var_a != var_b { - return None; - } - } - _ => {} - } - let val_a = sym_a.to_value(); - let val_b = sym_b.to_value(); - seq.add_inst(Inst::MatchEqual { - a: val_a, - b: val_b, - ty, - }); - } - &Inst::MatchInt { input, int_val, ty } => { - let sym_input = value_map.get(&input).unwrap(); - match sym_input { - &SymbolicValue::ConstInt(_, const_val) => { - if int_val == const_val { - // No runtime check needed -- we can continue. - continue; - } else { - // Static mismatch, so we can remove this - // whole Sequence. - return None; - } - } - _ => {} - } - let val_input = sym_input.to_value(); - seq.add_inst(Inst::MatchInt { - input: val_input, - int_val, - ty, - }); - } - &Inst::MatchVariant { - input, - input_ty, - variant, - ref arg_tys, - } => { - let sym_input = value_map.get(&input).unwrap(); - match sym_input { - &SymbolicValue::Variant(_, val_variant, ref args) => { - if val_variant != variant { - return None; - } - // Variant matches: unpack args' symbolic values into results. - let args = args.clone(); - for (i, arg) in args.iter().enumerate() { - let val = Value(orig_inst_id, i); - value_map.insert(val, arg.clone()); - } - } - _ => { - let val_input = sym_input.to_value(); - let new_inst = seq.add_inst(Inst::MatchVariant { - input: val_input, - input_ty, - variant, - arg_tys: arg_tys.clone(), - }); - for i in 0..arg_tys.len() { - let val = Value(orig_inst_id, i); - let sym = SymbolicValue::Value(Value(new_inst, i)); - value_map.insert(val, sym); - } - } - } - } - &Inst::Extract { - input, - input_ty, - term, - ref arg_tys, - } => { - let sym_input = value_map.get(&input).unwrap(); - match sym_input { - &SymbolicValue::Term(_, val_term, ref args) => { - if val_term != term { - return None; - } - // Term matches: unpack args' symbolic values into results. - let args = args.clone(); - for (i, arg) in args.iter().enumerate() { - let val = Value(orig_inst_id, i); - value_map.insert(val, arg.clone()); - } - } - _ => { - let val_input = sym_input.to_value(); - let new_inst = seq.add_inst(Inst::Extract { - input: val_input, - input_ty, - term, - arg_tys: arg_tys.clone(), - }); - for i in 0..arg_tys.len() { - let val = Value(orig_inst_id, i); - let sym = SymbolicValue::Value(Value(new_inst, i)); - value_map.insert(val, sym); - } - } - } - } - &Inst::ConstInt { ty, val } => { - let new_inst = seq.add_inst(Inst::ConstInt { ty, val }); - value_map.insert( - Value(orig_inst_id, 0), - SymbolicValue::ConstInt(Value(new_inst, 0), val), - ); - } - &Inst::CreateVariant { - ref inputs, - variant, - ty, - } => { - let sym_inputs = inputs - .iter() - .map(|input| value_map.get(&input.0).cloned().unwrap()) - .collect::>(); - let inputs = sym_inputs - .iter() - .zip(inputs.iter()) - .map(|(si, (_, ty))| (si.to_value(), *ty)) - .collect::>(); - let new_inst = seq.add_inst(Inst::CreateVariant { - inputs, - variant, - ty, - }); - value_map.insert( - Value(orig_inst_id, 0), - SymbolicValue::Variant(Value(new_inst, 0), variant, sym_inputs), - ); - } - &Inst::Construct { - ref inputs, - term, - ty, - } => { - let sym_inputs = inputs - .iter() - .map(|input| value_map.get(&input.0).cloned().unwrap()) - .collect::>(); - let inputs = sym_inputs - .iter() - .zip(inputs.iter()) - .map(|(si, (_, ty))| (si.to_value(), *ty)) - .collect::>(); - let new_inst = seq.add_inst(Inst::Construct { inputs, term, ty }); - value_map.insert( - Value(orig_inst_id, 0), - SymbolicValue::Term(Value(new_inst, 0), term, sym_inputs), - ); - } - &Inst::Copy { val, .. } => { - let sym_value = value_map.get(&val).cloned().unwrap(); - value_map.insert(Value(orig_inst_id, 0), sym_value); - } - &Inst::Nop => {} - }; - } - - // Now do a pass backward to track which instructions are used. - let mut used = vec![false; seq.insts.len()]; - for (id, inst) in seq.insts.iter().enumerate().rev() { - // Mark roots as used unconditionally: Return, MatchEqual, - // MatchInt, MatchVariant. - match inst { - &Inst::Return { .. } - | &Inst::MatchEqual { .. } - | &Inst::MatchInt { .. } - | &Inst::MatchVariant { .. } => used[id] = true, - _ => {} - } - // If this instruction is not used, continue. - if !used[id] { - continue; - } - // Otherwise, mark all inputs as used as well. - match inst { - &Inst::Return { value, .. } => used[value.0.index()] = true, - &Inst::MatchEqual { a, b, .. } => { - used[a.0.index()] = true; - used[b.0.index()] = true; - } - &Inst::MatchInt { input, .. } - | &Inst::MatchVariant { input, .. } - | &Inst::Extract { input, .. } => { - used[input.0.index()] = true; - } - &Inst::CreateVariant { ref inputs, .. } | Inst::Construct { ref inputs, .. } => { - for input in inputs { - used[input.0 .0.index()] = true; - } - } - &Inst::Copy { val, .. } => { - used[val.0.index()] = true; - } - &Inst::Arg { .. } | &Inst::ConstInt { .. } => {} - &Inst::Nop => {} - } - } - - // Now, remove any non-used instructions. - for id in 0..seq.insts.len() { - if !used[id] { - seq.insts[id] = Inst::Nop; - } - } - - Some(seq) - } - - /// Build a tree summary of the output produced by a sequence. - pub fn output_tree_summary(&self) -> TreeSummary { - // Scan forward, building a TreeSummary for what is known - // about each value (a "lower bound" on its shape). - let mut value_summaries: HashMap = HashMap::new(); - for (id, inst) in self.insts.iter().enumerate() { - let inst_id = InstId(id); - match inst { - &Inst::Arg { .. } => { - value_summaries.insert(Value(inst_id, 0), TreeSummary::Other); - } - &Inst::Return { value, .. } => { - return value_summaries - .get(&value) - .cloned() - .unwrap_or(TreeSummary::Other); - } - &Inst::MatchEqual { .. } - | &Inst::MatchInt { .. } - | &Inst::MatchVariant { .. } - | &Inst::Extract { .. } => {} - &Inst::ConstInt { val, .. } => { - value_summaries.insert(Value(inst_id, 0), TreeSummary::ConstInt(val)); - } - &Inst::CreateVariant { - ref inputs, - variant, - .. - } => { - let args = inputs - .iter() - .map(|(val, _)| { - value_summaries - .get(&val) - .cloned() - .unwrap_or(TreeSummary::Other) - }) - .collect::>(); - value_summaries.insert(Value(inst_id, 0), TreeSummary::Variant(variant, args)); - } - &Inst::Construct { - ref inputs, term, .. - } => { - let args = inputs - .iter() - .map(|(val, _)| { - value_summaries - .get(&val) - .cloned() - .unwrap_or(TreeSummary::Other) - }) - .collect::>(); - value_summaries.insert(Value(inst_id, 0), TreeSummary::Term(term, args)); - } - &Inst::Copy { val, .. } => { - // Copy summary from input to output. - let input_value = value_summaries - .get(&val) - .cloned() - .unwrap_or(TreeSummary::Other); - value_summaries.insert(Value(inst_id, 0), input_value); - } - &Inst::Nop => {} - } - } - - panic!("Sequence did not end in Return") - } - - /// Build a tree summary of the input expected by a sequence. - pub fn input_tree_summary(&self) -> TreeSummary { - // Scan backward, building a TreeSummary for each value (a - // "lower bound" on what it must be to satisfy the sequence's - // conditions). - let mut value_summaries: HashMap = HashMap::new(); - for (id, inst) in self.insts.iter().enumerate().rev() { - let inst_id = InstId(id); - match inst { - &Inst::Arg { .. } => { - // Must *start* with Arg; otherwise we might have missed some condition. - assert_eq!(id, 0); - return value_summaries - .get(&Value(inst_id, 0)) - .cloned() - .unwrap_or(TreeSummary::Other); - } - &Inst::Return { .. } => {} - - &Inst::MatchEqual { a, b, .. } => { - if value_summaries.contains_key(&a) && !value_summaries.contains_key(&b) { - let val = value_summaries.get(&a).cloned().unwrap(); - value_summaries.insert(b, val); - } else if value_summaries.contains_key(&b) && !value_summaries.contains_key(&a) - { - let val = value_summaries.get(&b).cloned().unwrap(); - value_summaries.insert(a, val); - } else if value_summaries.contains_key(&a) && value_summaries.contains_key(&b) { - let val_a = value_summaries.get(&a).cloned().unwrap(); - let val_b = value_summaries.get(&b).cloned().unwrap(); - let combined = TreeSummary::Conjunction(vec![val_a, val_b]); - value_summaries.insert(a, combined.clone()); - value_summaries.insert(b, combined); - } - } - &Inst::MatchInt { input, int_val, .. } => { - value_summaries.insert(input, TreeSummary::ConstInt(int_val)); - } - &Inst::MatchVariant { - input, - variant, - ref arg_tys, - .. - } => { - let args = (0..arg_tys.len()) - .map(|i| Value(inst_id, i)) - .map(|val| { - value_summaries - .get(&val) - .cloned() - .unwrap_or(TreeSummary::Other) - }) - .collect::>(); - let summary = TreeSummary::Variant(variant, args); - match value_summaries.entry(input) { - HashEntry::Vacant(v) => { - v.insert(summary); - } - HashEntry::Occupied(mut o) => { - let combined = TreeSummary::Conjunction(vec![ - summary, - std::mem::replace(o.get_mut(), TreeSummary::Other), - ]); - *o.get_mut() = combined; - } - } - } - - &Inst::Extract { - input, - term, - ref arg_tys, - .. - } => { - let args = (0..arg_tys.len()) - .map(|i| Value(inst_id, i)) - .map(|val| { - value_summaries - .get(&val) - .cloned() - .unwrap_or(TreeSummary::Other) - }) - .collect::>(); - let summary = TreeSummary::Term(term, args); - match value_summaries.entry(input) { - HashEntry::Vacant(v) => { - v.insert(summary); - } - HashEntry::Occupied(mut o) => { - let combined = TreeSummary::Conjunction(vec![ - summary, - std::mem::replace(o.get_mut(), TreeSummary::Other), - ]); - *o.get_mut() = combined; - } - } - } - - &Inst::ConstInt { .. } | &Inst::CreateVariant { .. } | &Inst::Construct { .. } => {} - - &Inst::Copy { val, .. } => { - // Copy summary from output to input. - let output_value = value_summaries - .get(&Value(inst_id, 0)) - .cloned() - .unwrap_or(TreeSummary::Other); - value_summaries.insert(val, output_value); - } - - &Inst::Nop => {} - } - } - - panic!("Sequence did not start with Arg") - } + (lhs_root_term, pattern_seq, rhs_root_term, expr_seq) } -/// A "summary" of a tree shape -- a template that describes a tree of -/// terms and constant integer values. -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum TreeSummary { - /// A known term, with given subtrees. - Term(TermId, Vec), - /// A known enum variant, with given subtrees. - Variant(VariantId, Vec), - /// A known constant integer value. - ConstInt(i64), - /// All of a list of summaries: represents a combined list of - /// requirements. The "provides" relation is satisfied if the - /// provider provides *all* of the providee's summaries in the - /// conjunction. A conjunction on the provider side (i.e., as an - /// "output summary") is illegal. - Conjunction(Vec), - /// Something else. - Other, -} +impl peepmatic_automata::Output for ExprSequence { + fn empty() -> ExprSequence { + Default::default() + } -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum TreeSummaryOverlap { - Never, - Sometimes, - Always, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum TermOrVariant { - Term(TermId), - Variant(VariantId), -} - -impl TreeSummary { - /// Does a term tree matching this summary "provide" the shape - /// described/expected by another summary? Answer can be "always", - /// "possibly", or "no". - pub fn provides(&self, other: &TreeSummary) -> TreeSummaryOverlap { - match (self, other) { - (_, &TreeSummary::Other) => TreeSummaryOverlap::Always, - (&TreeSummary::Other, _) => TreeSummaryOverlap::Sometimes, - - (&TreeSummary::Conjunction(..), _) => { - panic!("Conjunction on LHS of `provides` relation") + fn prefix(a: &Self, b: &Self) -> Self { + let mut prefix = vec![]; + for (a_inst, b_inst) in a.insts.iter().zip(b.insts.iter()) { + if *a_inst != *b_inst { + break; } - (this, &TreeSummary::Conjunction(ref args)) => args + prefix.push(a_inst.clone()); + } + ExprSequence { insts: prefix } + } + + fn difference(a: &Self, b: &Self) -> Self { + debug_assert!( + a.insts .iter() - .map(|arg| this.provides(arg)) - .min() - .unwrap_or(TreeSummaryOverlap::Always), - - ( - &TreeSummary::Term(self_term, ref self_args), - &TreeSummary::Term(other_term, ref other_args), - ) => { - if self_term != other_term { - TreeSummaryOverlap::Never - } else { - assert_eq!(self_args.len(), other_args.len()); - self_args - .iter() - .zip(other_args.iter()) - .map(|(self_arg, other_arg)| self_arg.provides(other_arg)) - .min() - .unwrap_or(TreeSummaryOverlap::Always) - } - } - - ( - &TreeSummary::Variant(self_var, ref self_args), - &TreeSummary::Variant(other_var, ref other_args), - ) => { - if self_var != other_var { - TreeSummaryOverlap::Never - } else { - assert_eq!(self_args.len(), other_args.len()); - self_args - .iter() - .zip(other_args.iter()) - .map(|(self_arg, other_arg)| self_arg.provides(other_arg)) - .min() - .unwrap_or(TreeSummaryOverlap::Always) - } - } - - (&TreeSummary::ConstInt(i1), &TreeSummary::ConstInt(i2)) => { - if i1 != i2 { - TreeSummaryOverlap::Never - } else { - TreeSummaryOverlap::Always - } - } - - _ => TreeSummaryOverlap::Never, + .zip(b.insts.iter()) + .all(|(a_inst, b_inst)| *a_inst == *b_inst), + "b ({:?}) is not a prefix of a ({:?})", + b, + a + ); + ExprSequence { + insts: a + .insts + .iter() + .cloned() + .skip(b.insts.len()) + .collect::>(), } } - pub fn root(&self) -> Option { - match self { - &TreeSummary::Term(term, ..) => Some(TermOrVariant::Term(term)), - &TreeSummary::Variant(variant, ..) => Some(TermOrVariant::Variant(variant)), - _ => None, + fn concat(a: &Self, b: &Self) -> Self { + ExprSequence { + insts: a + .insts + .iter() + .cloned() + .chain(b.insts.iter().cloned()) + .collect::>(), } } + + fn is_empty(&self) -> bool { + self.insts.is_empty() + } } diff --git a/cranelift/isle/src/main.rs b/cranelift/isle/src/main.rs index b9364551e8..7ba3cce903 100644 --- a/cranelift/isle/src/main.rs +++ b/cranelift/isle/src/main.rs @@ -4,6 +4,7 @@ use std::io::stdin; use std::io::Read; mod ast; +mod codegen; mod compile; mod error; mod ir; @@ -16,13 +17,7 @@ fn main() -> Result<(), error::Error> { let mut input = String::new(); stdin().read_to_string(&mut input)?; let mut parser = parser::Parser::new("", &input[..]); - let defs = parser.parse_defs()?; - let mut compiler = compile::Compiler::new(&defs)?; - compiler.build_sequences()?; - compiler.collect_tree_summaries()?; + let _defs = parser.parse_defs()?; - for seq in compiler.to_sequences() { - println!("---\nsequence\n---\n{:?}\n", seq); - } Ok(()) } diff --git a/cranelift/isle/wasmtime b/cranelift/isle/wasmtime new file mode 160000 index 0000000000..e4d4b09243 --- /dev/null +++ b/cranelift/isle/wasmtime @@ -0,0 +1 @@ +Subproject commit e4d4b092431d12d0f0c6dff1cff923572a6fbb77