diff --git a/cranelift/codegen/src/isa/aarch64/lower/isle/generated_code.rs b/cranelift/codegen/src/isa/aarch64/lower/isle/generated_code.rs index f9fc2d34e6..70a097f897 100644 --- a/cranelift/codegen/src/isa/aarch64/lower/isle/generated_code.rs +++ b/cranelift/codegen/src/isa/aarch64/lower/isle/generated_code.rs @@ -4,6 +4,6 @@ // mod generated_code;` trick either. #![allow(dead_code, unreachable_code, unreachable_patterns)] #![allow(unused_imports, unused_variables, non_snake_case, unused_mut)] -#![allow(irrefutable_let_patterns)] +#![allow(irrefutable_let_patterns, unused_assignments, non_camel_case_types)] include!(concat!(env!("ISLE_DIR"), "/isle_aarch64.rs")); diff --git a/cranelift/codegen/src/isa/s390x/lower/isle/generated_code.rs b/cranelift/codegen/src/isa/s390x/lower/isle/generated_code.rs index f9e3a767a3..d1f8767e9d 100644 --- a/cranelift/codegen/src/isa/s390x/lower/isle/generated_code.rs +++ b/cranelift/codegen/src/isa/s390x/lower/isle/generated_code.rs @@ -4,6 +4,6 @@ // mod generated_code;` trick either. #![allow(dead_code, unreachable_code, unreachable_patterns)] #![allow(unused_imports, unused_variables, non_snake_case, unused_mut)] -#![allow(irrefutable_let_patterns)] +#![allow(irrefutable_let_patterns, unused_assignments, non_camel_case_types)] include!(concat!(env!("ISLE_DIR"), "/isle_s390x.rs")); diff --git a/cranelift/codegen/src/isa/x64/lower/isle/generated_code.rs b/cranelift/codegen/src/isa/x64/lower/isle/generated_code.rs index b33af0ac8b..dc85f2916b 100644 --- a/cranelift/codegen/src/isa/x64/lower/isle/generated_code.rs +++ b/cranelift/codegen/src/isa/x64/lower/isle/generated_code.rs @@ -4,6 +4,6 @@ // mod generated_code;` trick either. #![allow(dead_code, unreachable_code, unreachable_patterns)] #![allow(unused_imports, unused_variables, non_snake_case, unused_mut)] -#![allow(irrefutable_let_patterns)] +#![allow(irrefutable_let_patterns, unused_assignments, non_camel_case_types)] include!(concat!(env!("ISLE_DIR"), "/isle_x64.rs")); diff --git a/cranelift/isle/isle/isle_examples/fail/multi_internal_etor.isle b/cranelift/isle/isle/isle_examples/fail/multi_internal_etor.isle new file mode 100644 index 0000000000..c2f6289454 --- /dev/null +++ b/cranelift/isle/isle/isle_examples/fail/multi_internal_etor.isle @@ -0,0 +1,4 @@ +(type u32 (primitive u32)) + +(decl multi A (u32) u32) +(extractor (A x) x) diff --git a/cranelift/isle/isle/isle_examples/link/multi_constructor.isle b/cranelift/isle/isle/isle_examples/link/multi_constructor.isle new file mode 100644 index 0000000000..45919fe221 --- /dev/null +++ b/cranelift/isle/isle/isle_examples/link/multi_constructor.isle @@ -0,0 +1,15 @@ +(type u32 (primitive u32)) + +(decl multi A (u32) u32) +(decl multi B (u32) u32) +(decl multi C (u32) u32) +(decl multi D (u32) u32) + +(extern constructor B ctor_B) +(extern extractor C etor_C) + +(rule (A x) + (B x)) + +(rule (D (C x)) + (B x)) diff --git a/cranelift/isle/isle/isle_examples/link/multi_constructor_main.rs b/cranelift/isle/isle/isle_examples/link/multi_constructor_main.rs new file mode 100644 index 0000000000..8a92a0beb1 --- /dev/null +++ b/cranelift/isle/isle/isle_examples/link/multi_constructor_main.rs @@ -0,0 +1,57 @@ +mod multi_constructor; + +pub(crate) type ConstructorVec = Vec; + +struct Context; + +struct It { + i: u32, + limit: u32, +} + +impl multi_constructor::ContextIter for It { + type Context = Context; + type Output = u32; + fn next(&mut self, _ctx: &mut Self::Context) -> Option { + if self.i >= self.limit { + None + } else { + let i = self.i; + self.i += 1; + Some(i) + } + } +} + +impl multi_constructor::Context for Context { + type etor_C_iter = It; + fn etor_C(&mut self, value: u32) -> Option { + Some(It { i: 0, limit: value }) + } + + type ctor_B_iter = multi_constructor::ContextIterWrapper, Context>; + fn ctor_B(&mut self, value: u32) -> Option { + Some((0..value).rev().collect::>().into_iter().into()) + } +} + +struct IterWithContext<'a, Item, I: multi_constructor::ContextIter> { + ctx: &'a mut Context, + it: I, +} + +impl<'a, Item, I: multi_constructor::ContextIter> Iterator for IterWithContext<'a, Item, I> { + type Item = Item; + fn next(&mut self) -> Option { + self.it.next(self.ctx) + } +} + +fn main() { + let mut ctx = Context; + let l1 = multi_constructor::constructor_A(&mut ctx, 10).unwrap(); + let l2 = multi_constructor::constructor_D(&mut ctx, 5).unwrap(); + let l1 = IterWithContext { ctx: &mut ctx, it: l1 }.collect::>(); + let l2 = IterWithContext { ctx: &mut ctx, it: l2 }.collect::>(); + println!("l1 = {:?} l2 = {:?}", l1, l2); +} diff --git a/cranelift/isle/isle/isle_examples/link/multi_extractor.isle b/cranelift/isle/isle/isle_examples/link/multi_extractor.isle new file mode 100644 index 0000000000..84fd15c00b --- /dev/null +++ b/cranelift/isle/isle/isle_examples/link/multi_extractor.isle @@ -0,0 +1,14 @@ +(type u32 (primitive u32)) +(type A extern (enum (B) (C))) + +(decl multi E1 (A u32) u32) + +(extern extractor E1 e1_etor) + +(decl Rule (u32) u32) + +(rule (Rule (E1 a idx)) + (if-let (A.B) a) + idx) +(rule (Rule _) + 32) diff --git a/cranelift/isle/isle/isle_examples/link/multi_extractor_main.rs b/cranelift/isle/isle/isle_examples/link/multi_extractor_main.rs new file mode 100644 index 0000000000..0e774e7680 --- /dev/null +++ b/cranelift/isle/isle/isle_examples/link/multi_extractor_main.rs @@ -0,0 +1,46 @@ +mod multi_extractor; + +#[derive(Clone)] +pub enum A { + B, + C, +} + +struct It { + i: u32, + arg: u32, +} + +impl multi_extractor::ContextIter for It { + type Context = Context; + type Output = (A, u32); + fn next(&mut self, _ctx: &mut Self::Context) -> Option { + if self.i >= 32 { + None + } else { + let idx = self.i; + self.i += 1; + let a = if self.arg & (1u32 << idx) != 0 { + A::B + } else { + A::C + }; + Some((a, idx)) + } + } +} + +struct Context; +impl multi_extractor::Context for Context { + type e1_etor_iter = It; + fn e1_etor(&mut self, arg0: u32) -> Option { + Some(It { i: 0, arg: arg0 }) + } +} + +fn main() { + let mut ctx = Context; + let x = multi_extractor::constructor_Rule(&mut ctx, 0xf0); + let y = multi_extractor::constructor_Rule(&mut ctx, 0); + println!("x = {:?} y = {:?}", x, y); +} diff --git a/cranelift/isle/isle/src/ast.rs b/cranelift/isle/isle/src/ast.rs index 5882f21331..c6f5a50d95 100644 --- a/cranelift/isle/isle/src/ast.rs +++ b/cranelift/isle/isle/src/ast.rs @@ -72,6 +72,10 @@ pub struct Decl { pub ret_ty: Ident, /// Whether this term's constructor is pure. pub pure: bool, + /// Whether this term can exist with some multiplicity: an + /// extractor or a constructor that matches multiple times, or + /// produces multiple values. + pub multi: bool, pub pos: Pos, } diff --git a/cranelift/isle/isle/src/codegen.rs b/cranelift/isle/isle/src/codegen.rs index 63219efe42..284afcdb7a 100644 --- a/cranelift/isle/isle/src/codegen.rs +++ b/cranelift/isle/isle/src/codegen.rs @@ -87,35 +87,66 @@ impl<'a> Codegen<'a> { "#![allow(unused_imports, unused_variables, non_snake_case, unused_mut)]" ) .unwrap(); - writeln!(code, "#![allow(irrefutable_let_patterns)]").unwrap(); + writeln!( + code, + "#![allow(irrefutable_let_patterns, unused_assignments, non_camel_case_types)]" + ) + .unwrap(); } writeln!(code, "\nuse super::*; // Pulls in all external types.").unwrap(); + writeln!(code, "use std::marker::PhantomData;").unwrap(); } fn generate_trait_sig(&self, code: &mut String, indent: &str, sig: &ExternalSig) { - writeln!( - code, - "{indent}fn {name}(&mut self, {params}) -> {opt_start}{open_paren}{rets}{close_paren}{opt_end};", - indent = indent, - name = sig.func_name, - params = sig.param_tys - .iter() - .enumerate() - .map(|(i, &ty)| format!("arg{}: {}", i, self.type_name(ty, /* by_ref = */ true))) - .collect::>() - .join(", "), - opt_start = if sig.infallible { "" } else { "Option<" }, + let ret_tuple = format!( + "{open_paren}{rets}{close_paren}", open_paren = if sig.ret_tys.len() != 1 { "(" } else { "" }, - rets = sig.ret_tys + rets = sig + .ret_tys .iter() .map(|&ty| self.type_name(ty, /* by_ref = */ false)) .collect::>() .join(", "), close_paren = if sig.ret_tys.len() != 1 { ")" } else { "" }, - opt_end = if sig.infallible { "" } else { ">" }, + ); + + let ret_ty = match (sig.multi, sig.infallible) { + (false, false) => format!("Option<{}>", ret_tuple), + (false, true) => format!("{}", ret_tuple), + (true, false) => format!("Option", sig.func_name), + _ => panic!( + "Unsupported multiplicity/infallible combo: {:?}, {}", + sig.multi, sig.infallible + ), + }; + + writeln!( + code, + "{indent}fn {name}(&mut self, {params}) -> {ret_ty};", + indent = indent, + name = sig.func_name, + params = sig + .param_tys + .iter() + .enumerate() + .map(|(i, &ty)| format!("arg{}: {}", i, self.type_name(ty, /* by_ref = */ true))) + .collect::>() + .join(", "), + ret_ty = ret_ty, ) .unwrap(); + + if sig.multi { + writeln!( + code, + "{indent}type {name}_iter: ContextIter;", + indent = indent, + name = sig.func_name, + output = ret_tuple, + ) + .unwrap(); + } } fn generate_ctx_trait(&self, code: &mut String) { @@ -147,6 +178,34 @@ impl<'a> Codegen<'a> { } } writeln!(code, "}}").unwrap(); + writeln!( + code, + r#" + pub trait ContextIter {{ + type Context; + type Output; + fn next(&mut self, ctx: &mut Self::Context) -> Option; + }} + + pub struct ContextIterWrapper, C: Context> {{ + iter: I, + _ctx: PhantomData, + }} + impl, C: Context> From for ContextIterWrapper {{ + fn from(iter: I) -> Self {{ + Self {{ iter, _ctx: PhantomData }} + }} + }} + impl, C: Context> ContextIter for ContextIterWrapper {{ + type Context = C; + type Output = Item; + fn next(&mut self, _ctx: &mut Self::Context) -> Option {{ + self.iter.next() + }} + }} + "#, + ) + .unwrap(); } fn generate_internal_types(&self, code: &mut String) { @@ -299,6 +358,11 @@ impl<'a> Codegen<'a> { .join(", "); assert_eq!(sig.ret_tys.len(), 1); let ret = self.type_name(sig.ret_tys[0], false); + let ret = if sig.multi { + format!("impl ContextIter", ret) + } else { + ret + }; writeln!( code, @@ -313,11 +377,29 @@ impl<'a> Codegen<'a> { ) .unwrap(); + if sig.multi { + writeln!(code, "let mut returns = ConstructorVec::new();").unwrap(); + } + let mut body_ctx: BodyContext = Default::default(); - let returned = - self.generate_body(code, /* depth = */ 0, trie, " ", &mut body_ctx); + let returned = self.generate_body( + code, + /* depth = */ 0, + trie, + " ", + &mut body_ctx, + sig.multi, + ); if !returned { - writeln!(code, " return None;").unwrap(); + if sig.multi { + writeln!( + code, + " return Some(ContextIterWrapper::from(returns.into_iter()));" + ) + .unwrap(); + } else { + writeln!(code, " return None;").unwrap(); + } } writeln!(code, "}}").unwrap(); @@ -332,8 +414,9 @@ impl<'a> Codegen<'a> { indent: &str, ctx: &mut BodyContext, returns: &mut Vec<(usize, String)>, - ) { + ) -> bool { log!("generate_expr_inst: {:?}", inst); + let mut new_scope = false; match inst { &ExprInst::ConstInt { ty, val } => { let value = Value::Expr { @@ -422,6 +505,7 @@ impl<'a> Codegen<'a> { ref inputs, term, infallible, + multi, .. } => { let mut input_exprs = vec![]; @@ -442,17 +526,36 @@ impl<'a> Codegen<'a> { let termdata = &self.termenv.terms[term.index()]; let sig = termdata.constructor_sig(self.typeenv).unwrap(); assert_eq!(input_exprs.len(), sig.param_tys.len()); - let fallible_try = if infallible { "" } else { "?" }; - writeln!( - code, - "{}let {} = {}(ctx, {}){};", - indent, - outputname, - sig.full_name, - input_exprs.join(", "), - fallible_try, - ) - .unwrap(); + + if !multi { + let fallible_try = if infallible { "" } else { "?" }; + writeln!( + code, + "{}let {} = {}(ctx, {}){};", + indent, + outputname, + sig.full_name, + input_exprs.join(", "), + fallible_try, + ) + .unwrap(); + } else { + writeln!( + code, + "{}let mut it = {}(ctx, {})?;", + indent, + sig.full_name, + input_exprs.join(", "), + ) + .unwrap(); + writeln!( + code, + "{}while let Some({}) = it.next(ctx) {{", + indent, outputname, + ) + .unwrap(); + new_scope = true; + } self.define_val(&output, ctx, /* is_ref = */ false, termdata.ret_ty); } &ExprInst::Return { @@ -462,6 +565,8 @@ impl<'a> Codegen<'a> { returns.push((index, value_expr)); } } + + new_scope } fn match_variant_binders( @@ -489,7 +594,7 @@ impl<'a> Codegen<'a> { } /// Returns a `bool` indicating whether this pattern inst is - /// infallible. + /// infallible, and the number of scopes opened. fn generate_pattern_inst( &self, code: &mut String, @@ -497,7 +602,7 @@ impl<'a> Codegen<'a> { inst: &PatternInst, indent: &str, ctx: &mut BodyContext, - ) -> bool { + ) -> (bool, usize) { match inst { &PatternInst::Arg { index, ty } => { let output = Value::Pattern { @@ -519,13 +624,13 @@ impl<'a> Codegen<'a> { is_ref, ty, ); - true + (true, 0) } &PatternInst::MatchEqual { ref a, ref b, .. } => { let a = self.value_by_ref(a, ctx); let b = self.value_by_ref(b, ctx); writeln!(code, "{}if {} == {} {{", indent, a, b).unwrap(); - false + (false, 1) } &PatternInst::MatchInt { ref input, @@ -536,13 +641,13 @@ impl<'a> Codegen<'a> { let int_val = self.const_int(int_val, ty); let input = self.value_by_val(input, ctx); writeln!(code, "{}if {} == {} {{", indent, input, int_val).unwrap(); - false + (false, 1) } &PatternInst::MatchPrim { ref input, val, .. } => { let input = self.value_by_val(input, ctx); let sym = &self.typeenv.syms[val.index()]; writeln!(code, "{}if {} == {} {{", indent, input, sym).unwrap(); - false + (false, 1) } &PatternInst::MatchVariant { ref input, @@ -570,13 +675,14 @@ impl<'a> Codegen<'a> { indent, ty_name, variantname, args, input ) .unwrap(); - false + (false, 1) } &PatternInst::Extract { ref inputs, ref output_tys, term, infallible, + multi, .. } => { let termdata = &self.termenv.terms[term.index()]; @@ -599,32 +705,61 @@ impl<'a> Codegen<'a> { }) .collect::>(); - if infallible { - writeln!( - code, - "{indent}let {open_paren}{vars}{close_paren} = {name}(ctx, {args});", - indent = indent, - open_paren = if output_binders.len() == 1 { "" } else { "(" }, - vars = output_binders.join(", "), - close_paren = if output_binders.len() == 1 { "" } else { ")" }, - name = sig.full_name, - args = input_values.join(", "), - ) - .unwrap(); - true - } else { - writeln!( - code, - "{indent}if let Some({open_paren}{vars}{close_paren}) = {name}(ctx, {args}) {{", - indent = indent, - open_paren = if output_binders.len() == 1 { "" } else { "(" }, - vars = output_binders.join(", "), - close_paren = if output_binders.len() == 1 { "" } else { ")" }, - name = sig.full_name, - args = input_values.join(", "), - ) - .unwrap(); - false + let bind_pattern = format!( + "{open_paren}{vars}{close_paren}", + open_paren = if output_binders.len() == 1 { "" } else { "(" }, + vars = output_binders.join(", "), + close_paren = if output_binders.len() == 1 { "" } else { ")" } + ); + let etor_call = format!( + "{name}(ctx, {args})", + name = sig.full_name, + args = input_values.join(", ") + ); + + match (infallible, multi) { + (_, true) => { + writeln!( + code, + "{indent}if let Some(mut iter) = {etor_call} {{", + indent = indent, + etor_call = etor_call, + ) + .unwrap(); + writeln!( + code, + "{indent} while let Some({bind_pattern}) = iter.next(ctx) {{", + indent = indent, + bind_pattern = bind_pattern, + ) + .unwrap(); + + (false, 2) + } + (false, false) => { + writeln!( + code, + "{indent}if let Some({bind_pattern}) = {etor_call} {{", + indent = indent, + bind_pattern = bind_pattern, + etor_call = etor_call, + ) + .unwrap(); + + (false, 1) + } + (true, false) => { + writeln!( + code, + "{indent}let {bind_pattern} = {etor_call};", + indent = indent, + bind_pattern = bind_pattern, + etor_call = etor_call, + ) + .unwrap(); + + (true, 0) + } } } &PatternInst::Expr { @@ -646,7 +781,7 @@ impl<'a> Codegen<'a> { ) .unwrap(); self.define_val(&output, ctx, /* is_ref = */ false, ty); - true + (true, 0) } &PatternInst::Expr { ref seq, output_ty, .. @@ -658,7 +793,15 @@ impl<'a> Codegen<'a> { let mut returns = vec![]; for (id, inst) in seq.insts.iter().enumerate() { let id = InstId(id); - self.generate_expr_inst(code, id, inst, &subindent, &mut subctx, &mut returns); + let new_scope = self.generate_expr_inst( + code, + id, + inst, + &subindent, + &mut subctx, + &mut returns, + ); + assert!(!new_scope); } assert_eq!(returns.len(), 1); writeln!(code, "{}return Some({});", subindent, returns[0].1).unwrap(); @@ -678,7 +821,7 @@ impl<'a> Codegen<'a> { .unwrap(); self.define_val(&output, ctx, /* is_ref = */ false, output_ty); - false + (false, 1) } } } @@ -690,6 +833,7 @@ impl<'a> Codegen<'a> { trie: &TrieNode, indent: &str, ctx: &mut BodyContext, + is_multi: bool, ) -> bool { log!("generate_body:\n{}", trie.pretty()); let mut returned = false; @@ -704,21 +848,37 @@ impl<'a> Codegen<'a> { output.pos.pretty_print_line(&self.typeenv.filenames[..]) ) .unwrap(); + // If this is a leaf node, generate the ExprSequence and return. let mut returns = vec![]; + let mut scopes = 0; + let mut indent = indent.to_string(); + let orig_indent = indent.clone(); for (id, inst) in output.insts.iter().enumerate() { let id = InstId(id); - self.generate_expr_inst(code, id, inst, indent, ctx, &mut returns); + let new_scope = + self.generate_expr_inst(code, id, inst, &indent[..], ctx, &mut returns); + if new_scope { + scopes += 1; + indent.push_str(" "); + } } assert_eq!(returns.len(), 1); - writeln!(code, "{}return Some({});", indent, returns[0].1).unwrap(); + if is_multi { + writeln!(code, "{}returns.push({});", indent, returns[0].1).unwrap(); + } else { + writeln!(code, "{}return Some({});", indent, returns[0].1).unwrap(); + } - returned = true; + for _ in 0..scopes { + writeln!(code, "{}}}", orig_indent).unwrap(); + } + + returned = !is_multi; } &TrieNode::Decision { ref edges } => { - let subindent = format!("{} ", indent); // If this is a decision node, generate each match op // in turn (in priority order). Gather together // adjacent MatchVariant ops with the same input and @@ -764,7 +924,14 @@ impl<'a> Codegen<'a> { // (possibly an empty one). Only use a `match` form if there // are at least two adjacent options. if last - i > 1 { - self.generate_body_matches(code, depth, &edges[i..last], indent, ctx); + self.generate_body_matches( + code, + depth, + &edges[i..last], + indent, + ctx, + is_multi, + ); i = last; continue; } else { @@ -777,16 +944,32 @@ impl<'a> Codegen<'a> { match symbol { &TrieSymbol::EndOfMatch => { - returned = self.generate_body(code, depth + 1, node, indent, ctx); + returned = self.generate_body( + code, + depth + 1, + node, + indent, + ctx, + is_multi, + ); } &TrieSymbol::Match { ref op } => { let id = InstId(depth); - let infallible = + let (infallible, new_scopes) = self.generate_pattern_inst(code, id, op, indent, ctx); - let i = if infallible { indent } else { &subindent[..] }; - let sub_returned = - self.generate_body(code, depth + 1, node, i, ctx); - if !infallible { + let mut subindent = indent.to_string(); + for _ in 0..new_scopes { + subindent.push_str(" "); + } + let sub_returned = self.generate_body( + code, + depth + 1, + node, + &subindent[..], + ctx, + is_multi, + ); + for _ in 0..new_scopes { writeln!(code, "{}}}", indent).unwrap(); } if infallible && sub_returned { @@ -810,6 +993,7 @@ impl<'a> Codegen<'a> { edges: &[TrieEdge], indent: &str, ctx: &mut BodyContext, + is_multi: bool, ) { let (input, input_ty) = match &edges[0].symbol { &TrieSymbol::Match { @@ -874,12 +1058,12 @@ impl<'a> Codegen<'a> { ) .unwrap(); let subindent = format!("{} ", indent); - self.generate_body(code, depth + 1, node, &subindent, ctx); + self.generate_body(code, depth + 1, node, &subindent, ctx, is_multi); writeln!(code, "{} }}", indent).unwrap(); } // Always add a catchall, because we don't do exhaustiveness - // checking on the MatcHVariants. + // checking on the MatchVariants. writeln!(code, "{} _ => {{}}", indent).unwrap(); writeln!(code, "{}}}", indent).unwrap(); diff --git a/cranelift/isle/isle/src/ir.rs b/cranelift/isle/isle/src/ir.rs index 37665c6edf..4eb23f044b 100644 --- a/cranelift/isle/isle/src/ir.rs +++ b/cranelift/isle/isle/src/ir.rs @@ -105,6 +105,8 @@ pub enum PatternInst { output_tys: Vec, /// This extractor's term. term: TermId, + /// Is this a multi-extractor? + multi: bool, }, // NB: This has to go last, since it is infallible, so that when we sort @@ -162,6 +164,8 @@ pub enum ExprInst { term: TermId, /// Whether this constructor is infallible or not. infallible: bool, + /// Is this a multi-constructor? + multi: bool, }, /// Set the Nth return value. Produces no values. @@ -305,6 +309,7 @@ impl PatternSequence { output_tys: Vec, term: TermId, infallible: bool, + multi: bool, ) -> Vec { let inst = InstId(self.insts.len()); let mut outs = vec![]; @@ -319,6 +324,7 @@ impl PatternSequence { output_tys, term, infallible, + multi, }); outs } @@ -429,10 +435,11 @@ impl PatternSequence { panic!("Should have been expanded away") } TermKind::Decl { - extractor_kind: - Some(ExtractorKind::ExternalExtractor { infallible, .. }), + extractor_kind: Some(ExtractorKind::ExternalExtractor { .. }), .. } => { + let ext_sig = termdata.extractor_sig(typeenv).unwrap(); + // Evaluate all `input` args. let mut inputs = vec![]; let mut input_tys = vec![]; @@ -451,7 +458,8 @@ impl PatternSequence { input_tys, output_tys, term, - *infallible, + ext_sig.infallible, + ext_sig.multi, ); for (pat, &val) in output_pats.iter().zip(arg_values.iter()) { @@ -521,6 +529,7 @@ impl ExprSequence { ty: TypeId, term: TermId, infallible: bool, + multi: bool, ) -> Value { let inst = InstId(self.insts.len()); let inputs = inputs.iter().cloned().collect(); @@ -529,6 +538,7 @@ impl ExprSequence { ty, term, infallible, + multi, }); Value::Expr { inst, output: 0 } } @@ -581,6 +591,7 @@ impl ExprSequence { } TermKind::Decl { constructor_kind: Some(ConstructorKind::InternalConstructor), + multi, .. } => { self.add_construct( @@ -588,11 +599,13 @@ impl ExprSequence { ty, term, /* infallible = */ false, + *multi, ) } TermKind::Decl { constructor_kind: Some(ConstructorKind::ExternalConstructor { .. }), pure, + multi, .. } => { self.add_construct( @@ -600,6 +613,7 @@ impl ExprSequence { ty, term, /* infallible = */ !pure, + *multi, ) } TermKind::Decl { diff --git a/cranelift/isle/isle/src/parser.rs b/cranelift/isle/isle/src/parser.rs index 495366391b..68fb3adb2d 100644 --- a/cranelift/isle/isle/src/parser.rs +++ b/cranelift/isle/isle/src/parser.rs @@ -290,6 +290,12 @@ impl<'a> Parser<'a> { } else { false }; + let multi = if self.is_sym_str("multi") { + self.symbol()?; + true + } else { + false + }; let term = self.parse_ident()?; @@ -307,6 +313,7 @@ impl<'a> Parser<'a> { arg_tys, ret_ty, pure, + multi, pos, }) } diff --git a/cranelift/isle/isle/src/sema.rs b/cranelift/isle/isle/src/sema.rs index 68c20097c0..5732ba300c 100644 --- a/cranelift/isle/isle/src/sema.rs +++ b/cranelift/isle/isle/src/sema.rs @@ -236,6 +236,8 @@ pub enum TermKind { Decl { /// Whether the term is marked as `pure`. pure: bool, + /// Whether the term is marked as `multi`. + multi: bool, /// The kind of this term's constructor, if any. constructor_kind: Option, /// The kind of this term's extractor, if any. @@ -290,6 +292,9 @@ pub struct ExternalSig { pub ret_tys: Vec, /// Whether this signature is infallible or not. pub infallible: bool, + /// "Multiplicity": does the function return multiple values (via + /// an iterator)? + pub multi: bool, } impl Term { @@ -353,6 +358,7 @@ impl Term { pub fn extractor_sig(&self, tyenv: &TypeEnv) -> Option { match &self.kind { TermKind::Decl { + multi, extractor_kind: Some(ExtractorKind::ExternalExtractor { name, infallible, .. @@ -363,7 +369,8 @@ impl Term { full_name: format!("C::{}", tyenv.syms[name.index()]), param_tys: vec![self.ret_ty], ret_tys: self.arg_tys.clone(), - infallible: *infallible, + infallible: *infallible && !*multi, + multi: *multi, }), _ => None, } @@ -374,6 +381,7 @@ impl Term { match &self.kind { TermKind::Decl { constructor_kind: Some(ConstructorKind::ExternalConstructor { name }), + multi, pure, .. } => Some(ExternalSig { @@ -381,10 +389,12 @@ impl Term { full_name: format!("C::{}", tyenv.syms[name.index()]), param_tys: self.arg_tys.clone(), ret_tys: vec![self.ret_ty], - infallible: !pure, + infallible: !pure && !*multi, + multi: *multi, }), TermKind::Decl { constructor_kind: Some(ConstructorKind::InternalConstructor { .. }), + multi, .. } => { let name = format!("constructor_{}", tyenv.syms[self.name.index()]); @@ -398,6 +408,7 @@ impl Term { // matching at the toplevel (an entry point can // fail to rewrite). infallible: false, + multi: *multi, }) } _ => None, @@ -854,6 +865,7 @@ impl TermEnv { constructor_kind: None, extractor_kind: None, pure: decl.pure, + multi: decl.multi, }, }); } @@ -1014,8 +1026,18 @@ impl TermEnv { ); continue; } - TermKind::Decl { extractor_kind, .. } => match extractor_kind { + TermKind::Decl { + multi, + extractor_kind, + .. + } => match extractor_kind { None => { + if *multi { + tyenv.report_error( + ext.pos, + "A term declared with `multi` cannot have an internal extractor.".to_string()); + continue; + } *extractor_kind = Some(ExtractorKind::InternalExtractor { template }); } Some(ext_kind) => {