Browse Source

cranelift-isle: Add "partial" flag for constructors (#5392)

* cranelift-isle: Add "partial" flag for constructors

Instead of tying fallibility of constructors to whether they're either
internal or pure, this commit assumes all constructors are infallible
unless tagged otherwise with a "partial" flag.

Internal constructors without the "partial" flag are not allowed to use
constructors which have the "partial" flag on the right-hand side of any
rules, because they have no way to report last-minute match failures.

Multi-constructors should never be "partial"; they report match failures
with an empty iterator instead. In turn this means you can't use partial
constructors on the right-hand side of internal multi-constructor rules.
However, you can use the same constructors on the left-hand side with
`if` or `if-let` instead.

In many cases, ISLE can already trivially prove that an internal
constructor always returns `Some`. With this commit, those cases are
largely unchanged, except for removing all the `Option`s and `Some`s
from the generated code for those terms.

However, for internal non-partial constructors where ISLE could not
prove that, it now emits an `unreachable!` panic as the last-resort,
instead of returning `None` like it used to do. Among the existing
backends, here's how many constructors have these panic cases:

- x64: 14% (53/374)
- aarch64: 15% (41/277)
- riscv64: 23% (26/114)
- s390x: 47% (268/567)

It's often possible to rewrite rules so that ISLE can tell the panic can
never be hit. Just ensure that there's a lowest-priority rule which has
no constraints on the left-hand side.

But in many of these constructors, it's difficult to statically prove
the unhandled cases are unreachable because that's only down to
knowledge about how they're called or other preconditions.

So this commit does not try to enforce that all terms have a last-resort
fallback rule.

* Check term flags while translating expressions

Instead of doing it in a separate pass afterward.

This involved threading all the term flags (pure, multi, partial)
through the recursive `translate_expr` calls, so I extracted the flags
to a new struct so they can all be passed together.

* Validate multi-term usage

Now that I've threaded the flags through `translate_expr`, it's easy to
check this case too, so let's just do it.

* Extract `ReturnKind` to use in `ExternalSig`

There are only three legal states for the combination of `multi` and
`infallible`, so replace those fields of `ExternalSig` with a
three-state enum.

* Remove `Option` wrapper from multi-extractors too

If we'd had any external multi-constructors this would correct their
signatures as well.

* Update ISLE tests

* Tag prelude constructors as pure where appropriate

I believe the only reason these weren't marked `pure` before was because
that would have implied that they're also partial. Now that those two
states are specified separately we apply this flag more places.

* Fix my changes to aarch64 `lower_bmask` and `imm` terms
pull/5407/head
Jamey Sharp 2 years ago
committed by GitHub
parent
commit
8726eeefb3
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 68
      cranelift/codegen/src/egraph.rs
  2. 23
      cranelift/codegen/src/isa/aarch64/inst.isle
  3. 4
      cranelift/codegen/src/isa/aarch64/lower.isle
  4. 6
      cranelift/codegen/src/isa/riscv64/inst.isle
  5. 2
      cranelift/codegen/src/isa/riscv64/lower.isle
  6. 10
      cranelift/codegen/src/isa/riscv64/lower/isle.rs
  7. 16
      cranelift/codegen/src/isa/s390x/inst.isle
  8. 4
      cranelift/codegen/src/isa/s390x/lower.isle
  9. 18
      cranelift/codegen/src/isa/s390x/lower/isle.rs
  10. 2
      cranelift/codegen/src/isa/x64/inst.isle
  11. 6
      cranelift/codegen/src/isa/x64/lower.isle
  12. 68
      cranelift/codegen/src/isle_prelude.rs
  13. 4
      cranelift/codegen/src/opts.rs
  14. 54
      cranelift/codegen/src/prelude.isle
  15. 12
      cranelift/codegen/src/prelude_lower.isle
  16. 2
      cranelift/isle/isle/isle_examples/link/borrows.isle
  17. 4
      cranelift/isle/isle/isle_examples/link/iflets.isle
  18. 34
      cranelift/isle/isle/isle_examples/link/multi_constructor_main.rs
  19. 4
      cranelift/isle/isle/isle_examples/link/multi_extractor_main.rs
  20. 4
      cranelift/isle/isle/isle_examples/run/iconst.isle
  21. 24
      cranelift/isle/isle/isle_examples/run/let_shadowing_main.rs
  22. 2
      cranelift/isle/isle/src/ast.rs
  23. 165
      cranelift/isle/isle/src/codegen.rs
  24. 2
      cranelift/isle/isle/src/overlap.rs
  25. 7
      cranelift/isle/isle/src/parser.rs
  26. 246
      cranelift/isle/isle/src/sema.rs

68
cranelift/codegen/src/egraph.rs

@ -229,48 +229,46 @@ where
// values produced as equivalents to this value.
trace!("Calling into ISLE with original value {}", orig_value);
isle_ctx.ctx.stats.rewrite_rule_invoked += 1;
let optimized_values =
let mut optimized_values =
crate::opts::generated_code::constructor_simplify(&mut isle_ctx, orig_value);
// Create a union of all new values with the original (or
// maybe just one new value marked as "subsuming" the
// original, if present.)
let mut union_value = orig_value;
if let Some(mut optimized_values) = optimized_values {
while let Some(optimized_value) = optimized_values.next(&mut isle_ctx) {
trace!(
"Returned from ISLE for {}, got {:?}",
orig_value,
optimized_value
);
if optimized_value == orig_value {
trace!(" -> same as orig value; skipping");
continue;
}
if isle_ctx.ctx.subsume_values.contains(&optimized_value) {
// Merge in the unionfind so canonicalization
// still works, but take *only* the subsuming
// value, and break now.
isle_ctx.ctx.eclasses.union(optimized_value, union_value);
union_value = optimized_value;
break;
}
let old_union_value = union_value;
union_value = isle_ctx
.ctx
.func
.dfg
.union(old_union_value, optimized_value);
isle_ctx.ctx.stats.union += 1;
trace!(" -> union: now {}", union_value);
isle_ctx.ctx.eclasses.add(union_value);
isle_ctx
.ctx
.eclasses
.union(old_union_value, optimized_value);
isle_ctx.ctx.eclasses.union(old_union_value, union_value);
while let Some(optimized_value) = optimized_values.next(&mut isle_ctx) {
trace!(
"Returned from ISLE for {}, got {:?}",
orig_value,
optimized_value
);
if optimized_value == orig_value {
trace!(" -> same as orig value; skipping");
continue;
}
if isle_ctx.ctx.subsume_values.contains(&optimized_value) {
// Merge in the unionfind so canonicalization
// still works, but take *only* the subsuming
// value, and break now.
isle_ctx.ctx.eclasses.union(optimized_value, union_value);
union_value = optimized_value;
break;
}
let old_union_value = union_value;
union_value = isle_ctx
.ctx
.func
.dfg
.union(old_union_value, optimized_value);
isle_ctx.ctx.stats.union += 1;
trace!(" -> union: now {}", union_value);
isle_ctx.ctx.eclasses.add(union_value);
isle_ctx
.ctx
.eclasses
.union(old_union_value, optimized_value);
isle_ctx.ctx.eclasses.union(old_union_value, union_value);
}
isle_ctx.ctx.rewrite_depth -= 1;

23
cranelift/codegen/src/isa/aarch64/inst.isle

@ -1599,7 +1599,7 @@
))
;; Extractors for target features ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(decl pure sign_return_address_disabled () Unit)
(decl pure partial sign_return_address_disabled () Unit)
(extern constructor sign_return_address_disabled sign_return_address_disabled)
(decl use_lse () Inst)
@ -1607,13 +1607,13 @@
;; Extractor helpers for various immmediate constants ;;;;;;;;;;;;;;;;;;;;;;;;;;
(decl pure imm_logic_from_u64 (Type u64) ImmLogic)
(decl pure partial imm_logic_from_u64 (Type u64) ImmLogic)
(extern constructor imm_logic_from_u64 imm_logic_from_u64)
(decl pure imm_logic_from_imm64 (Type Imm64) ImmLogic)
(decl pure partial imm_logic_from_imm64 (Type Imm64) ImmLogic)
(extern constructor imm_logic_from_imm64 imm_logic_from_imm64)
(decl pure imm_shift_from_imm64 (Type Imm64) ImmShift)
(decl pure partial imm_shift_from_imm64 (Type Imm64) ImmShift)
(extern constructor imm_shift_from_imm64 imm_shift_from_imm64)
(decl imm_shift_from_u8 (u8) ImmShift)
@ -1672,13 +1672,13 @@
(decl imm12_from_negated_u64 (Imm12) u64)
(extern extractor imm12_from_negated_u64 imm12_from_negated_u64)
(decl pure lshr_from_u64 (Type u64) ShiftOpAndAmt)
(decl pure partial lshr_from_u64 (Type u64) ShiftOpAndAmt)
(extern constructor lshr_from_u64 lshr_from_u64)
(decl pure lshl_from_imm64 (Type Imm64) ShiftOpAndAmt)
(decl pure partial lshl_from_imm64 (Type Imm64) ShiftOpAndAmt)
(extern constructor lshl_from_imm64 lshl_from_imm64)
(decl pure lshl_from_u64 (Type u64) ShiftOpAndAmt)
(decl pure partial lshl_from_u64 (Type u64) ShiftOpAndAmt)
(extern constructor lshl_from_u64 lshl_from_u64)
(decl integral_ty (Type) Type)
@ -1687,10 +1687,10 @@
(decl valid_atomic_transaction (Type) Type)
(extern extractor valid_atomic_transaction valid_atomic_transaction)
(decl pure is_zero_simm9 (SImm9) Unit)
(decl pure partial is_zero_simm9 (SImm9) Unit)
(extern constructor is_zero_simm9 is_zero_simm9)
(decl pure is_zero_uimm12 (UImm12Scaled) Unit)
(decl pure partial is_zero_uimm12 (UImm12Scaled) Unit)
(extern constructor is_zero_uimm12 is_zero_uimm12)
;; Helper to go directly from a `Value`, when it's an `iconst`, to an `Imm12`.
@ -3614,8 +3614,9 @@
;; csetm res, ne
(rule 3
(lower_bmask out_ty (fits_in_16 in_ty) val)
(let ((mask_bits ImmLogic (imm_logic_from_u64 $I32 (ty_mask in_ty)))
(masked Reg (and_imm $I32 (value_regs_get val 0) mask_bits)))
; This if-let can't fail due to ty_mask always producing 8/16 consecutive 1s.
(if-let mask_bits (imm_logic_from_u64 $I32 (ty_mask in_ty)))
(let ((masked Reg (and_imm $I32 (value_regs_get val 0) mask_bits)))
(lower_bmask out_ty $I32 masked)))
;; Exceptional `lower_icmp_into_flags` rules.

4
cranelift/codegen/src/isa/aarch64/lower.isle

@ -2,7 +2,7 @@
;; The main lowering constructor term: takes a clif `Inst` and returns the
;; register(s) within which the lowered instruction's result values live.
(decl lower (Inst) InstOutput)
(decl partial lower (Inst) InstOutput)
;; Variant of the main lowering constructor term, which receives an
;; additional argument (a vector of branch targets to be used) for
@ -12,7 +12,7 @@
;; blocks while we lower, the fallthrough in the new order is not (necessarily)
;; the same as the fallthrough in CLIF. So, we use the explicitly-provided
;; target.
(decl lower_branch (Inst VecMachLabel) InstOutput)
(decl partial lower_branch (Inst VecMachLabel) InstOutput)
;;;; Rules for `iconst` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

6
cranelift/codegen/src/isa/riscv64/inst.isle

@ -839,9 +839,9 @@
;; extend int if need.
(decl ext_int_if_need (bool ValueRegs Type) ValueRegs)
;;; for I8 and I16 ...
;;; for I8, I16, and I32 ...
(rule -1
(ext_int_if_need signed val (fits_in_32 ty))
(ext_int_if_need signed val ty)
(gen_extend val signed (ty_bits ty) 64))
;;; otherwise this is a I64 or I128
;;; no need to extend.
@ -1870,7 +1870,7 @@
(decl vec_label_get (VecMachLabel u8) MachLabel )
(extern constructor vec_label_get vec_label_get)
(decl lower_branch (Inst VecMachLabel) InstOutput)
(decl partial lower_branch (Inst VecMachLabel) InstOutput)
(rule (lower_branch (jump _ _) targets )
(side_effect (SideEffectNoResult.Inst (gen_jump (vec_label_get targets 0)))))

2
cranelift/codegen/src/isa/riscv64/lower.isle

@ -2,7 +2,7 @@
;; The main lowering constructor term: takes a clif `Inst` and returns the
;; register(s) within which the lowered instruction's result values live.
(decl lower (Inst) InstOutput)
(decl partial lower (Inst) InstOutput)
;;;; Rules for `iconst` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

10
cranelift/codegen/src/isa/riscv64/lower/isle.rs

@ -121,7 +121,7 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, Flags, IsaFlags, 6>
targets: &VecMachLabel,
ty: Type,
) -> InstOutput {
let test = generated_code::constructor_lower_icmp(self, cc, a, b, ty).unwrap();
let test = generated_code::constructor_lower_icmp(self, cc, a, b, ty);
self.emit(&MInst::CondBr {
taken: BranchTarget::Label(targets[0]),
not_taken: BranchTarget::Label(targets[1]),
@ -308,11 +308,11 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, Flags, IsaFlags, 6>
ValueRegs::two(shamt, len_sub_shamt)
}
fn has_b(&mut self) -> Option<bool> {
Some(self.isa_flags.has_b())
fn has_b(&mut self) -> bool {
self.isa_flags.has_b()
}
fn has_zbkb(&mut self) -> Option<bool> {
Some(self.isa_flags.has_zbkb())
fn has_zbkb(&mut self) -> bool {
self.isa_flags.has_zbkb()
}
fn valueregs_2_reg(&mut self, val: Value) -> Reg {

16
cranelift/codegen/src/isa/s390x/inst.isle

@ -1537,15 +1537,15 @@
;; Detect specific integer values
(decl pure i64_nonequal (i64 i64) i64)
(decl pure partial i64_nonequal (i64 i64) i64)
(extern constructor i64_nonequal i64_nonequal)
(decl pure i64_nonzero (i64) i64)
(decl pure partial i64_nonzero (i64) i64)
(rule (i64_nonzero x)
(if (i64_nonequal x 0))
x)
(decl pure i64_not_neg1 (i64) i64)
(decl pure partial i64_not_neg1 (i64) i64)
(rule (i64_not_neg1 x)
(if (i64_nonequal x -1))
x)
@ -1799,11 +1799,11 @@
;; Form the sum of two offset values, and check that the result is
;; a valid `MemArg::Symbol` offset (i.e. is even and fits into i32).
(decl pure memarg_symbol_offset_sum (i64 i64) i32)
(decl pure partial memarg_symbol_offset_sum (i64 i64) i32)
(extern constructor memarg_symbol_offset_sum memarg_symbol_offset_sum)
;; Likewise, but just check a single offset value.
(decl pure memarg_symbol_offset (i64) i32)
(decl pure partial memarg_symbol_offset (i64) i32)
(rule (memarg_symbol_offset x)
(memarg_symbol_offset_sum x 0))
@ -1837,7 +1837,7 @@
;; Test whether a `load` address will be lowered to a `MemArg::Symbol`.
(decl pure load_sym (Inst) Inst)
(decl pure partial load_sym (Inst) Inst)
(rule (load_sym inst)
(if-let (load _ (symbol_value (symbol_value_data _ (reloc_distance_near) sym_offset))
(i64_from_offset load_offset))
@ -1845,7 +1845,7 @@
(if (memarg_symbol_offset_sum sym_offset load_offset))
inst)
(decl pure uload16_sym (Inst) Inst)
(decl pure partial uload16_sym (Inst) Inst)
(rule (uload16_sym inst)
(if-let (uload16 _ (symbol_value (symbol_value_data _ (reloc_distance_near) sym_offset))
(i64_from_offset load_offset))
@ -2752,7 +2752,7 @@
;; Similarly, because we cannot allocate temp registers, if an instruction
;; requires matching source and destination registers, this needs to be handled
;; by the user. Another helper to verify that constraint.
(decl pure same_reg (WritableReg Reg) Reg)
(decl pure partial same_reg (WritableReg Reg) Reg)
(extern constructor same_reg same_reg)
;; Push a `MInst.AluRRR` instruction to a sequence.

4
cranelift/codegen/src/isa/s390x/lower.isle

@ -2,12 +2,12 @@
;; The main lowering constructor term: takes a clif `Inst` and returns the
;; register(s) within which the lowered instruction's result values live.
(decl lower (Inst) InstOutput)
(decl partial lower (Inst) InstOutput)
;; A variant of the main lowering constructor term, used for branches.
;; The only difference is that it gets an extra argument holding a vector
;; of branch targets to be used.
(decl lower_branch (Inst VecMachLabel) InstOutput)
(decl partial lower_branch (Inst VecMachLabel) InstOutput)
;;;; Rules for `iconst` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

18
cranelift/codegen/src/isa/s390x/lower/isle.rs

@ -526,15 +526,13 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, Flags, IsaFlags, 6>
}
#[inline]
fn lane_order(&mut self) -> Option<LaneOrder> {
Some(lane_order_for_call_conv(
self.lower_ctx.abi().call_conv(self.lower_ctx.sigs()),
))
fn lane_order(&mut self) -> LaneOrder {
lane_order_for_call_conv(self.lower_ctx.abi().call_conv(self.lower_ctx.sigs()))
}
#[inline]
fn be_lane_idx(&mut self, ty: Type, idx: u8) -> u8 {
match self.lane_order().unwrap() {
match self.lane_order() {
LaneOrder::LittleEndian => ty.lane_count() as u8 - 1 - idx,
LaneOrder::BigEndian => idx,
}
@ -542,7 +540,7 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, Flags, IsaFlags, 6>
#[inline]
fn be_vec_const(&mut self, ty: Type, n: u128) -> u128 {
match self.lane_order().unwrap() {
match self.lane_order() {
LaneOrder::LittleEndian => n,
LaneOrder::BigEndian => {
let lane_count = ty.lane_count();
@ -568,7 +566,7 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, Flags, IsaFlags, 6>
#[inline]
fn shuffle_mask_from_u128(&mut self, idx: u128) -> (u128, u16) {
let bytes = match self.lane_order().unwrap() {
let bytes = match self.lane_order() {
LaneOrder::LittleEndian => idx.to_be_bytes().map(|x| {
if x < 16 {
15 - x
@ -590,7 +588,7 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, Flags, IsaFlags, 6>
let inst = self.lower_ctx.dfg().value_def(val).inst()?;
let constant = self.lower_ctx.get_constant(inst)?;
let ty = self.lower_ctx.output_ty(inst, 0);
Some(zero_extend_to_u64(constant, self.ty_bits(ty).unwrap()))
Some(zero_extend_to_u64(constant, self.ty_bits(ty)))
}
#[inline]
@ -598,7 +596,7 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, Flags, IsaFlags, 6>
let inst = self.lower_ctx.dfg().value_def(val).inst()?;
let constant = self.lower_ctx.get_constant(inst)?;
let ty = self.lower_ctx.output_ty(inst, 0);
Some(zero_extend_to_u64(!constant, self.ty_bits(ty).unwrap()))
Some(zero_extend_to_u64(!constant, self.ty_bits(ty)))
}
#[inline]
@ -620,7 +618,7 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, Flags, IsaFlags, 6>
let inst = self.lower_ctx.dfg().value_def(val).inst()?;
let constant = self.lower_ctx.get_constant(inst)?;
let ty = self.lower_ctx.output_ty(inst, 0);
Some(sign_extend_to_u64(constant, self.ty_bits(ty).unwrap()))
Some(sign_extend_to_u64(constant, self.ty_bits(ty)))
}
#[inline]

2
cranelift/codegen/src/isa/x64/inst.isle

@ -865,7 +865,7 @@
;; A helper to both check that the `Imm64` and `Offset32` values sum to less
;; than 32-bits AND return this summed `u32` value. Also, the `Imm64` will be
;; zero-extended from `Type` up to 64 bits. This is useful for `to_amode`.
(decl pure sum_extend_fits_in_32_bits (Type Imm64 Offset32) u32)
(decl pure partial sum_extend_fits_in_32_bits (Type Imm64 Offset32) u32)
(extern constructor sum_extend_fits_in_32_bits sum_extend_fits_in_32_bits)
;;;; Amode lowering ;;;;

6
cranelift/codegen/src/isa/x64/lower.isle

@ -2,12 +2,12 @@
;; The main lowering constructor term: takes a clif `Inst` and returns the
;; register(s) within which the lowered instruction's result values live.
(decl lower (Inst) InstOutput)
(decl partial lower (Inst) InstOutput)
;; A variant of the main lowering constructor term, used for branches.
;; The only difference is that it gets an extra argument holding a vector
;; of branch targets to be used.
(decl lower_branch (Inst MachLabelSlice) InstOutput)
(decl partial lower_branch (Inst MachLabelSlice) InstOutput)
;;;; Rules for `iconst` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -1309,7 +1309,7 @@
if_true
if_false))
(decl pure all_ones_or_all_zeros (Value) bool)
(decl pure partial all_ones_or_all_zeros (Value) bool)
(rule (all_ones_or_all_zeros (and (icmp _ _ _) (value_type (multi_lane _ _)))) $true)
(rule (all_ones_or_all_zeros (and (fcmp _ _ _) (value_type (multi_lane _ _)))) $true)
(rule (all_ones_or_all_zeros (vconst (vconst_all_ones_or_all_zeros))) $true)

68
cranelift/codegen/src/isle_prelude.rs

@ -14,43 +14,43 @@ macro_rules! isle_common_prelude_methods {
}
#[inline]
fn u8_as_u32(&mut self, x: u8) -> Option<u32> {
Some(x.into())
fn u8_as_u32(&mut self, x: u8) -> u32 {
x.into()
}
#[inline]
fn u8_as_u64(&mut self, x: u8) -> Option<u64> {
Some(x.into())
fn u8_as_u64(&mut self, x: u8) -> u64 {
x.into()
}
#[inline]
fn u16_as_u64(&mut self, x: u16) -> Option<u64> {
Some(x.into())
fn u16_as_u64(&mut self, x: u16) -> u64 {
x.into()
}
#[inline]
fn u32_as_u64(&mut self, x: u32) -> Option<u64> {
Some(x.into())
fn u32_as_u64(&mut self, x: u32) -> u64 {
x.into()
}
#[inline]
fn i64_as_u64(&mut self, x: i64) -> Option<u64> {
Some(x as u64)
fn i64_as_u64(&mut self, x: i64) -> u64 {
x as u64
}
#[inline]
fn u64_add(&mut self, x: u64, y: u64) -> Option<u64> {
Some(x.wrapping_add(y))
fn u64_add(&mut self, x: u64, y: u64) -> u64 {
x.wrapping_add(y)
}
#[inline]
fn u64_sub(&mut self, x: u64, y: u64) -> Option<u64> {
Some(x.wrapping_sub(y))
fn u64_sub(&mut self, x: u64, y: u64) -> u64 {
x.wrapping_sub(y)
}
#[inline]
fn u64_mul(&mut self, x: u64, y: u64) -> Option<u64> {
Some(x.wrapping_mul(y))
fn u64_mul(&mut self, x: u64, y: u64) -> u64 {
x.wrapping_mul(y)
}
#[inline]
@ -66,23 +66,23 @@ macro_rules! isle_common_prelude_methods {
}
#[inline]
fn u64_and(&mut self, x: u64, y: u64) -> Option<u64> {
Some(x & y)
fn u64_and(&mut self, x: u64, y: u64) -> u64 {
x & y
}
#[inline]
fn u64_or(&mut self, x: u64, y: u64) -> Option<u64> {
Some(x | y)
fn u64_or(&mut self, x: u64, y: u64) -> u64 {
x | y
}
#[inline]
fn u64_xor(&mut self, x: u64, y: u64) -> Option<u64> {
Some(x ^ y)
fn u64_xor(&mut self, x: u64, y: u64) -> u64 {
x ^ y
}
#[inline]
fn u64_not(&mut self, x: u64) -> Option<u64> {
Some(!x)
fn u64_not(&mut self, x: u64) -> u64 {
!x
}
#[inline]
@ -91,24 +91,24 @@ macro_rules! isle_common_prelude_methods {
}
#[inline]
fn u64_is_odd(&mut self, x: u64) -> Option<bool> {
Some(x & 1 == 1)
fn u64_is_odd(&mut self, x: u64) -> bool {
x & 1 == 1
}
#[inline]
fn u64_sextend_u32(&mut self, x: u64) -> Option<u64> {
Some(x as u32 as i32 as i64 as u64)
fn u64_sextend_u32(&mut self, x: u64) -> u64 {
x as u32 as i32 as i64 as u64
}
#[inline]
fn u64_uextend_u32(&mut self, x: u64) -> Option<u64> {
Some(x & 0xffff_ffff)
fn u64_uextend_u32(&mut self, x: u64) -> u64 {
x & 0xffff_ffff
}
#[inline]
fn ty_bits(&mut self, ty: Type) -> Option<u8> {
fn ty_bits(&mut self, ty: Type) -> u8 {
use std::convert::TryInto;
Some(ty.bits().try_into().unwrap())
ty.bits().try_into().unwrap()
}
#[inline]
@ -505,8 +505,8 @@ macro_rules! isle_common_prelude_methods {
}
#[inline]
fn imm64(&mut self, x: u64) -> Option<Imm64> {
Some(Imm64::new(x as i64))
fn imm64(&mut self, x: u64) -> Imm64 {
Imm64::new(x as i64)
}
#[inline]

4
cranelift/codegen/src/opts.rs

@ -90,8 +90,8 @@ impl<'a, 'b, 'c> generated_code::Context for IsleContext<'a, 'b, 'c> {
type inst_data_etor_iter = InstDataEtorIter<'a, 'b, 'c>;
fn inst_data_etor(&mut self, eclass: Value) -> Option<InstDataEtorIter<'a, 'b, 'c>> {
Some(InstDataEtorIter::new(eclass))
fn inst_data_etor(&mut self, eclass: Value) -> InstDataEtorIter<'a, 'b, 'c> {
InstDataEtorIter::new(eclass)
}
fn make_inst_ctor(&mut self, ty: Type, op: &InstructionData) -> Value {

54
cranelift/codegen/src/prelude.isle

@ -9,7 +9,7 @@
;; `()`
(type Unit (primitive Unit))
(decl unit () Unit)
(decl pure unit () Unit)
(extern constructor unit unit)
(type bool (primitive bool))
@ -42,12 +42,12 @@
(decl value_type (Type) Value)
(extern extractor infallible value_type value_type)
(decl u32_add (u32 u32) u32)
(decl pure u32_add (u32 u32) u32)
(extern constructor u32_add u32_add)
;; Pure/fallible constructor that tries to add two `u32`s, interpreted
;; as signed values, and fails to match on overflow.
(decl pure s32_add_fallible (u32 u32) u32)
(decl pure partial s32_add_fallible (u32 u32) u32)
(extern constructor s32_add_fallible s32_add_fallible)
;; Extractor that matches a `u32` only if non-negative.
@ -61,17 +61,17 @@
;; Pure/fallible constructor that tests if one u32 is less than or
;; equal to another.
(decl pure u32_lteq (u32 u32) Unit)
(decl pure partial u32_lteq (u32 u32) Unit)
(extern constructor u32_lteq u32_lteq)
;; Pure/fallible constructor that tests if one u8 is less than or
;; equal to another.
(decl pure u8_lteq (u8 u8) Unit)
(decl pure partial u8_lteq (u8 u8) Unit)
(extern constructor u8_lteq u8_lteq)
;; Pure/fallible constructor that tests if one u8 is strictly less
;; than another.
(decl pure u8_lt (u8 u8) Unit)
(decl pure partial u8_lt (u8 u8) Unit)
(extern constructor u8_lt u8_lt)
;; Get a signed 32-bit immediate in an u32 from an Imm64, if possible.
@ -82,7 +82,7 @@
(decl uimm8 (u8) Imm64)
(extern extractor uimm8 uimm8)
(decl u8_and (u8 u8) u8)
(decl pure u8_and (u8 u8) u8)
(extern constructor u8_and u8_and)
;;;; Primitive Type Conversions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -113,10 +113,10 @@
(decl pure u64_mul (u64 u64) u64)
(extern constructor u64_mul u64_mul)
(decl pure u64_sdiv (u64 u64) u64)
(decl pure partial u64_sdiv (u64 u64) u64)
(extern constructor u64_sdiv u64_sdiv)
(decl pure u64_udiv (u64 u64) u64)
(decl pure partial u64_udiv (u64 u64) u64)
(extern constructor u64_udiv u64_udiv)
(decl pure u64_and (u64 u64) u64)
@ -181,29 +181,29 @@
(extern constructor ty_bits ty_bits)
;; Get the bit width of a given type.
(decl ty_bits_u16 (Type) u16)
(decl pure ty_bits_u16 (Type) u16)
(extern constructor ty_bits_u16 ty_bits_u16)
;; Get the bit width of a given type.
(decl ty_bits_u64 (Type) u64)
(decl pure ty_bits_u64 (Type) u64)
(extern constructor ty_bits_u64 ty_bits_u64)
;; Get a mask for the width of a given type.
(decl ty_mask (Type) u64)
(decl pure ty_mask (Type) u64)
(extern constructor ty_mask ty_mask)
;; Get the byte width of a given type.
(decl ty_bytes (Type) u16)
(decl pure ty_bytes (Type) u16)
(extern constructor ty_bytes ty_bytes)
;; Get the type of each lane in the given type.
(decl lane_type (Type) Type)
(decl pure lane_type (Type) Type)
(extern constructor lane_type lane_type)
;;;; `cranelift_codegen::ir::MemFlags ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; `MemFlags::trusted`
(decl mem_flags_trusted () MemFlags)
(decl pure mem_flags_trusted () MemFlags)
(extern constructor mem_flags_trusted mem_flags_trusted)
;;;; Helpers for Working with Flags ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -252,7 +252,7 @@
;; A pure constructor that only matches scalar integers, and references that can
;; fit in 64 bits.
(decl pure ty_int_ref_scalar_64 (Type) Type)
(decl pure partial ty_int_ref_scalar_64 (Type) Type)
(extern constructor ty_int_ref_scalar_64 ty_int_ref_scalar_64)
;; An extractor that matches 32- and 64-bit types only.
@ -284,16 +284,16 @@
(extern extractor ty_float_or_vec ty_float_or_vec)
;; A pure constructor that only matches vector floating-point types.
(decl pure ty_vector_float (Type) Type)
(decl pure partial ty_vector_float (Type) Type)
(extern constructor ty_vector_float ty_vector_float)
;; A pure constructor that only matches vector types with lanes which
;; are not floating-point.
(decl pure ty_vector_not_float (Type) Type)
(decl pure partial ty_vector_not_float (Type) Type)
(extern constructor ty_vector_not_float ty_vector_not_float)
;; A pure constructor/extractor that only matches 64-bit vector types.
(decl pure ty_vec64 (Type) Type)
(decl pure partial ty_vec64 (Type) Type)
(extern constructor ty_vec64 ty_vec64_ctor)
(extern extractor ty_vec64 ty_vec64)
@ -322,7 +322,7 @@
(extern extractor ty_vec128_int ty_vec128_int)
;; A pure constructor that matches everything except vectors with size 32X2.
(decl pure not_vec32x2 (Type) Type)
(decl pure partial not_vec32x2 (Type) Type)
(extern constructor not_vec32x2 not_vec32x2)
;; An extractor that matches everything except I64X2
@ -388,28 +388,28 @@
(extern extractor ty_dyn128_int ty_dyn128_int)
;; Convert an `Offset32` to a primitive number.
(decl offset32_to_u32 (Offset32) u32)
(decl pure offset32_to_u32 (Offset32) u32)
(extern constructor offset32_to_u32 offset32_to_u32)
;; This is a direct import of `IntCC::unsigned`.
;; Get the corresponding IntCC with the signed component removed.
;; For conditions without a signed component, this is a no-op.
(decl intcc_unsigned (IntCC) IntCC)
(decl pure intcc_unsigned (IntCC) IntCC)
(extern constructor intcc_unsigned intcc_unsigned)
;; Pure constructor that only matches signed integer cond codes.
(decl pure signed_cond_code (IntCC) IntCC)
(decl pure partial signed_cond_code (IntCC) IntCC)
(extern constructor signed_cond_code signed_cond_code)
;;;; Helpers for Working with TrapCode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(decl trap_code_division_by_zero () TrapCode)
(decl pure trap_code_division_by_zero () TrapCode)
(extern constructor trap_code_division_by_zero trap_code_division_by_zero)
(decl trap_code_integer_overflow () TrapCode)
(decl pure trap_code_integer_overflow () TrapCode)
(extern constructor trap_code_integer_overflow trap_code_integer_overflow)
(decl trap_code_bad_conversion_to_integer () TrapCode)
(decl pure trap_code_bad_conversion_to_integer () TrapCode)
(extern constructor trap_code_bad_conversion_to_integer trap_code_bad_conversion_to_integer)
;;;; Helpers for tail recursion loops ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -418,7 +418,7 @@
(type Range (primitive Range))
;; Create a new range from `start` through `end` (exclusive).
(decl range (usize usize) Range)
(decl pure range (usize usize) Range)
(extern constructor range range)
;; A view on the current state of the range.

12
cranelift/codegen/src/prelude_lower.isle

@ -227,11 +227,11 @@
(def_inst (iconst (u64_from_imm64 x))))
;; Match any zero value for iconst, fconst32, fconst64, vconst and splat.
(decl pure zero_value (Value) Value)
(decl pure partial zero_value (Value) Value)
(extern constructor zero_value zero_value)
;; Match a sinkable instruction from a value operand.
(decl pure is_sinkable_inst (Value) Inst)
(decl pure partial is_sinkable_inst (Value) Inst)
(extern constructor is_sinkable_inst is_sinkable_inst)
;; Instruction creation helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -531,16 +531,16 @@
(decl tls_model (TlsModel) Type)
(extern extractor infallible tls_model tls_model)
(decl pure tls_model_is_elf_gd () Unit)
(decl pure partial tls_model_is_elf_gd () Unit)
(extern constructor tls_model_is_elf_gd tls_model_is_elf_gd)
(decl pure tls_model_is_macho () Unit)
(decl pure partial tls_model_is_macho () Unit)
(extern constructor tls_model_is_macho tls_model_is_macho)
(decl pure tls_model_is_coff () Unit)
(decl pure partial tls_model_is_coff () Unit)
(extern constructor tls_model_is_coff tls_model_is_coff)
(decl pure preserve_frame_pointers () Unit)
(decl pure partial preserve_frame_pointers () Unit)
(extern constructor preserve_frame_pointers preserve_frame_pointers)
;;;; Helpers for accessing instruction data ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

2
cranelift/isle/isle/isle_examples/link/borrows.isle

@ -4,7 +4,7 @@
(decl get_a (A) u32)
(extern extractor get_a get_a)
(decl pure u32_pure (u32) u32)
(decl pure partial u32_pure (u32) u32)
(extern constructor u32_pure u32_pure)
(decl entry (u32) u32)

4
cranelift/isle/isle/isle_examples/link/iflets.isle

@ -1,12 +1,12 @@
(type u32 (primitive u32))
(decl pure A (u32 u32) u32)
(decl pure partial A (u32 u32) u32)
(extern constructor A A)
(decl B (u32 u32) u32)
(extern extractor B B)
(decl C (u32 u32 u32 u32) u32)
(decl partial C (u32 u32 u32 u32) u32)
(decl pure predicate () u32)
(rule (predicate) 1)

34
cranelift/isle/isle/isle_examples/link/multi_constructor_main.rs

@ -25,22 +25,28 @@ impl multi_constructor::ContextIter for It {
impl multi_constructor::Context for Context {
type etor_C_iter = It;
fn etor_C(&mut self, value: u32) -> Option<It> {
Some(It { i: 0, limit: value })
fn etor_C(&mut self, value: u32) -> It {
It { i: 0, limit: value }
}
type ctor_B_iter = multi_constructor::ContextIterWrapper<u32, std::vec::IntoIter<u32>, Context>;
fn ctor_B(&mut self, value: u32) -> Option<Self::ctor_B_iter> {
Some((0..value).rev().collect::<Vec<_>>().into_iter().into())
fn ctor_B(&mut self, value: u32) -> Self::ctor_B_iter {
(0..value).rev().collect::<Vec<_>>().into_iter().into()
}
}
struct IterWithContext<'a, Item, I: multi_constructor::ContextIter<Output = Item, Context = Context>> {
struct IterWithContext<
'a,
Item,
I: multi_constructor::ContextIter<Output = Item, Context = Context>,
> {
ctx: &'a mut Context,
it: I,
}
impl<'a, Item, I: multi_constructor::ContextIter<Output = Item, Context = Context>> Iterator for IterWithContext<'a, Item, I> {
impl<'a, Item, I: multi_constructor::ContextIter<Output = Item, Context = Context>> Iterator
for IterWithContext<'a, Item, I>
{
type Item = Item;
fn next(&mut self) -> Option<Self::Item> {
self.it.next(self.ctx)
@ -49,9 +55,17 @@ impl<'a, Item, I: multi_constructor::ContextIter<Output = Item, Context = Contex
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::<Vec<_>>();
let l2 = IterWithContext { ctx: &mut ctx, it: l2 }.collect::<Vec<_>>();
let l1 = multi_constructor::constructor_A(&mut ctx, 10);
let l2 = multi_constructor::constructor_D(&mut ctx, 5);
let l1 = IterWithContext {
ctx: &mut ctx,
it: l1,
}
.collect::<Vec<_>>();
let l2 = IterWithContext {
ctx: &mut ctx,
it: l2,
}
.collect::<Vec<_>>();
println!("l1 = {:?} l2 = {:?}", l1, l2);
}

4
cranelift/isle/isle/isle_examples/link/multi_extractor_main.rs

@ -33,8 +33,8 @@ impl multi_extractor::ContextIter for It {
struct Context;
impl multi_extractor::Context for Context {
type e1_etor_iter = It;
fn e1_etor(&mut self, arg0: u32) -> Option<It> {
Some(It { i: 0, arg: arg0 })
fn e1_etor(&mut self, arg0: u32) -> It {
It { i: 0, arg: arg0 }
}
}

4
cranelift/isle/isle/isle_examples/run/iconst.isle

@ -1,6 +1,6 @@
(type i64 (primitive i64))
(decl X (i64) i64)
(decl partial X (i64) i64)
(rule (X -1) -2)
(rule (X -2) -3)
(rule (X 0x7fff_ffff_ffff_ffff) 0x8000_0000_0000_0000)
@ -8,7 +8,7 @@
(type i128 (primitive i128))
(decl Y (i128) i128)
(decl partial Y (i128) i128)
(rule (Y 0x1000_0000_0000_0000_1234_5678_9abc_def0) -1)
(rule (Y 0xffff_ffff_ffff_ffff_ffff_ffff_ffff_ffff) 3)

24
cranelift/isle/isle/isle_examples/run/let_shadowing_main.rs

@ -7,21 +7,21 @@ impl let_shadowing::Context for Context {}
fn main() {
let mut ctx = Context;
assert_eq!(Some(20), let_shadowing::constructor_test1(&mut ctx, 20));
assert_eq!(Some(97), let_shadowing::constructor_test1(&mut ctx, 97));
assert_eq!(20, let_shadowing::constructor_test1(&mut ctx, 20));
assert_eq!(97, let_shadowing::constructor_test1(&mut ctx, 97));
assert_eq!(Some(20), let_shadowing::constructor_test2(&mut ctx, 20));
assert_eq!(Some(97), let_shadowing::constructor_test2(&mut ctx, 97));
assert_eq!(20, let_shadowing::constructor_test2(&mut ctx, 20));
assert_eq!(97, let_shadowing::constructor_test2(&mut ctx, 97));
assert_eq!(Some(20), let_shadowing::constructor_test3(&mut ctx, 20));
assert_eq!(Some(97), let_shadowing::constructor_test3(&mut ctx, 97));
assert_eq!(20, let_shadowing::constructor_test3(&mut ctx, 20));
assert_eq!(97, let_shadowing::constructor_test3(&mut ctx, 97));
assert_eq!(Some(23), let_shadowing::constructor_test4(&mut ctx, 20));
assert_eq!(Some(23), let_shadowing::constructor_test4(&mut ctx, 97));
assert_eq!(23, let_shadowing::constructor_test4(&mut ctx, 20));
assert_eq!(23, let_shadowing::constructor_test4(&mut ctx, 97));
assert_eq!(Some(20), let_shadowing::constructor_test5(&mut ctx, 20));
assert_eq!(Some(97), let_shadowing::constructor_test5(&mut ctx, 97));
assert_eq!(20, let_shadowing::constructor_test5(&mut ctx, 20));
assert_eq!(97, let_shadowing::constructor_test5(&mut ctx, 97));
assert_eq!(Some(20), let_shadowing::constructor_test6(&mut ctx, 20));
assert_eq!(Some(97), let_shadowing::constructor_test6(&mut ctx, 97));
assert_eq!(20, let_shadowing::constructor_test6(&mut ctx, 20));
assert_eq!(97, let_shadowing::constructor_test6(&mut ctx, 97));
}

2
cranelift/isle/isle/src/ast.rs

@ -83,6 +83,8 @@ pub struct Decl {
/// extractor or a constructor that matches multiple times, or
/// produces multiple values.
pub multi: bool,
/// Whether this term's constructor can fail to match.
pub partial: bool,
pub pos: Pos,
}

165
cranelift/isle/isle/src/codegen.rs

@ -2,10 +2,10 @@
use crate::ir::{ExprInst, InstId, PatternInst, Value};
use crate::log;
use crate::sema::ExternalSig;
use crate::sema::{TermEnv, TermId, Type, TypeEnv, TypeId, Variant};
use crate::sema::{ExternalSig, ReturnKind, TermEnv, TermId, Type, TypeEnv, TypeId, Variant};
use crate::trie::{TrieEdge, TrieNode, TrieSymbol};
use crate::{StableMap, StableSet};
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::fmt::Write;
@ -111,14 +111,21 @@ impl<'a> Codegen<'a> {
close_paren = if sig.ret_tys.len() != 1 { ")" } else { "" },
);
let ret_ty = match (sig.multi, sig.infallible) {
(false, false) => format!("Option<{}>", ret_tuple),
(false, true) => format!("{}", ret_tuple),
(true, false) => format!("Option<Self::{}_iter>", sig.func_name),
_ => panic!(
"Unsupported multiplicity/infallible combo: {:?}, {}",
sig.multi, sig.infallible
),
if sig.ret_kind == ReturnKind::Iterator {
writeln!(
code,
"{indent}type {name}_iter: ContextIter<Context = Self, Output = {output}>;",
indent = indent,
name = sig.func_name,
output = ret_tuple,
)
.unwrap();
}
let ret_ty = match sig.ret_kind {
ReturnKind::Plain => ret_tuple,
ReturnKind::Option => format!("Option<{}>", ret_tuple),
ReturnKind::Iterator => format!("Self::{}_iter", sig.func_name),
};
writeln!(
@ -136,17 +143,6 @@ impl<'a> Codegen<'a> {
ret_ty = ret_ty,
)
.unwrap();
if sig.multi {
writeln!(
code,
"{indent}type {name}_iter: ContextIter<Context = Self, Output = {output}>;",
indent = indent,
name = sig.func_name,
output = ret_tuple,
)
.unwrap();
}
}
fn generate_ctx_trait(&self, code: &mut String) {
@ -357,27 +353,29 @@ impl<'a> Codegen<'a> {
.collect::<Vec<_>>()
.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<Context = C, Output = {}>", ret)
} else {
ret
let ret = match sig.ret_kind {
ReturnKind::Iterator => format!("impl ContextIter<Context = C, Output = {}>", ret),
ReturnKind::Option => format!("Option<{}>", ret),
ReturnKind::Plain => ret,
};
let term_name = &self.typeenv.syms[termdata.name.index()];
writeln!(
code,
"\n// Generated as internal constructor for term {}.",
self.typeenv.syms[termdata.name.index()],
term_name,
)
.unwrap();
writeln!(
code,
"pub fn {}<C: Context>(ctx: &mut C, {}) -> Option<{}> {{",
"pub fn {}<C: Context>(ctx: &mut C, {}) -> {} {{",
sig.func_name, args, ret,
)
.unwrap();
if sig.multi {
if sig.ret_kind == ReturnKind::Iterator {
writeln!(code, "let mut returns = ConstructorVec::new();").unwrap();
}
@ -388,18 +386,23 @@ impl<'a> Codegen<'a> {
trie,
" ",
&mut body_ctx,
sig.multi,
sig.ret_kind,
);
if !returned {
if sig.multi {
writeln!(
code,
" return Some(ContextIterWrapper::from(returns.into_iter()));"
)
.unwrap();
} else {
writeln!(code, " return None;").unwrap();
}
let ret_expr = match sig.ret_kind {
ReturnKind::Plain => Cow::from(format!(
"unreachable!(\"no rule matched for term {{}} at {{}}; should it be partial?\", {:?}, {:?})",
term_name,
termdata
.decl_pos
.pretty_print_line(&self.typeenv.filenames[..])
)),
ReturnKind::Option => Cow::from("None"),
ReturnKind::Iterator => {
Cow::from("ContextIterWrapper::from(returns.into_iter())")
}
};
write!(code, " return {};", ret_expr).unwrap();
}
writeln!(code, "}}").unwrap();
@ -542,7 +545,7 @@ impl<'a> Codegen<'a> {
} else {
writeln!(
code,
"{}let mut it = {}(ctx, {})?;",
"{}let mut iter = {}(ctx, {});",
indent,
sig.full_name,
input_exprs.join(", "),
@ -550,7 +553,7 @@ impl<'a> Codegen<'a> {
.unwrap();
writeln!(
code,
"{}while let Some({}) = it.next(ctx) {{",
"{}while let Some({}) = iter.next(ctx) {{",
indent, outputname,
)
.unwrap();
@ -717,49 +720,20 @@ impl<'a> Codegen<'a> {
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)
}
if multi {
writeln!(code, "{indent}let mut iter = {etor_call};").unwrap();
writeln!(
code,
"{indent}while let Some({bind_pattern}) = iter.next(ctx) {{",
)
.unwrap();
(false, 1)
} else if infallible {
writeln!(code, "{indent}let {bind_pattern} = {etor_call};").unwrap();
(true, 0)
} else {
writeln!(code, "{indent}if let Some({bind_pattern}) = {etor_call} {{").unwrap();
(false, 1)
}
}
&PatternInst::Expr {
@ -833,7 +807,7 @@ impl<'a> Codegen<'a> {
trie: &TrieNode,
indent: &str,
ctx: &mut BodyContext,
is_multi: bool,
ret_kind: ReturnKind,
) -> bool {
log!("generate_body:\n{}", trie.pretty());
let mut returned = false;
@ -865,17 +839,18 @@ impl<'a> Codegen<'a> {
}
assert_eq!(returns.len(), 1);
if is_multi {
writeln!(code, "{}returns.push({});", indent, returns[0].1).unwrap();
} else {
writeln!(code, "{}return Some({});", indent, returns[0].1).unwrap();
}
let (before, after) = match ret_kind {
ReturnKind::Plain => ("return ", ""),
ReturnKind::Option => ("return Some(", ")"),
ReturnKind::Iterator => ("returns.push(", ")"),
};
writeln!(code, "{}{}{}{};", indent, before, returns[0].1, after).unwrap();
for _ in 0..scopes {
writeln!(code, "{}}}", orig_indent).unwrap();
}
returned = !is_multi;
returned = ret_kind != ReturnKind::Iterator;
}
&TrieNode::Decision { ref edges } => {
@ -930,7 +905,7 @@ impl<'a> Codegen<'a> {
&edges[i..last],
indent,
ctx,
is_multi,
ret_kind,
);
i = last;
continue;
@ -950,7 +925,7 @@ impl<'a> Codegen<'a> {
node,
indent,
ctx,
is_multi,
ret_kind,
);
}
&TrieSymbol::Match { ref op } => {
@ -967,7 +942,7 @@ impl<'a> Codegen<'a> {
node,
&subindent[..],
ctx,
is_multi,
ret_kind,
);
for _ in 0..new_scopes {
writeln!(code, "{}}}", indent).unwrap();
@ -993,7 +968,7 @@ impl<'a> Codegen<'a> {
edges: &[TrieEdge],
indent: &str,
ctx: &mut BodyContext,
is_multi: bool,
ret_kind: ReturnKind,
) {
let (input, input_ty) = match &edges[0].symbol {
&TrieSymbol::Match {
@ -1058,7 +1033,7 @@ impl<'a> Codegen<'a> {
)
.unwrap();
let subindent = format!("{} ", indent);
self.generate_body(code, depth + 1, node, &subindent, ctx, is_multi);
self.generate_body(code, depth + 1, node, &subindent, ctx, ret_kind);
writeln!(code, "{} }}", indent).unwrap();
}

2
cranelift/isle/isle/src/overlap.rs

@ -112,7 +112,7 @@ fn check_overlaps(terms: Vec<(TermId, trie_again::RuleSet)>, env: &TermEnv) -> E
let mut errs = Errors::default();
for (tid, ruleset) in terms {
let is_multi_ctor = match &env.terms[tid.index()].kind {
&TermKind::Decl { multi, .. } => multi,
TermKind::Decl { flags, .. } => flags.multi,
_ => false,
};
if is_multi_ctor {

7
cranelift/isle/isle/src/parser.rs

@ -307,6 +307,12 @@ impl<'a> Parser<'a> {
} else {
false
};
let partial = if self.is_sym_str("partial") {
self.symbol()?;
true
} else {
false
};
let term = self.parse_ident()?;
@ -325,6 +331,7 @@ impl<'a> Parser<'a> {
ret_ty,
pure,
multi,
partial,
pos,
})
}

246
cranelift/isle/isle/src/sema.rs

@ -223,6 +223,17 @@ pub struct Term {
pub kind: TermKind,
}
/// Flags from a term's declaration with `(decl ...)`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TermFlags {
/// Whether the term is marked as `pure`.
pub pure: bool,
/// Whether the term is marked as `multi`.
pub multi: bool,
/// Whether the term is marked as `partial`.
pub partial: bool,
}
/// The kind of a term.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TermKind {
@ -234,10 +245,8 @@ pub enum TermKind {
},
/// A term declared via a `(decl ...)` form.
Decl {
/// Whether the term is marked as `pure`.
pure: bool,
/// Whether the term is marked as `multi`.
multi: bool,
/// Flags from the term's declaration.
flags: TermFlags,
/// The kind of this term's constructor, if any.
constructor_kind: Option<ConstructorKind>,
/// The kind of this term's extractor, if any.
@ -279,6 +288,17 @@ pub enum ExtractorKind {
},
}
/// How many values a function can return.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ReturnKind {
/// Exactly one return value.
Plain,
/// Zero or one return values.
Option,
/// Zero or more return values.
Iterator,
}
/// An external function signature.
#[derive(Clone, Debug)]
pub struct ExternalSig {
@ -290,11 +310,8 @@ pub struct ExternalSig {
pub param_tys: Vec<TypeId>,
/// The types of this function signature's results.
pub ret_tys: Vec<TypeId>,
/// Whether this signature is infallible or not.
pub infallible: bool,
/// "Multiplicity": does the function return multiple values (via
/// an iterator)?
pub multi: bool,
/// How many values can this function return?
pub ret_kind: ReturnKind,
}
impl Term {
@ -372,20 +389,28 @@ impl Term {
pub fn extractor_sig(&self, tyenv: &TypeEnv) -> Option<ExternalSig> {
match &self.kind {
TermKind::Decl {
multi,
flags,
extractor_kind:
Some(ExtractorKind::ExternalExtractor {
name, infallible, ..
}),
..
} => Some(ExternalSig {
func_name: tyenv.syms[name.index()].clone(),
full_name: format!("C::{}", tyenv.syms[name.index()]),
param_tys: vec![self.ret_ty],
ret_tys: self.arg_tys.clone(),
infallible: *infallible && !*multi,
multi: *multi,
}),
} => {
let ret_kind = if flags.multi {
ReturnKind::Iterator
} else if *infallible {
ReturnKind::Plain
} else {
ReturnKind::Option
};
Some(ExternalSig {
func_name: tyenv.syms[name.index()].clone(),
full_name: format!("C::{}", tyenv.syms[name.index()]),
param_tys: vec![self.ret_ty],
ret_tys: self.arg_tys.clone(),
ret_kind,
})
}
_ => None,
}
}
@ -394,35 +419,33 @@ impl Term {
pub fn constructor_sig(&self, tyenv: &TypeEnv) -> Option<ExternalSig> {
match &self.kind {
TermKind::Decl {
constructor_kind: Some(ConstructorKind::ExternalConstructor { name }),
multi,
pure,
..
} => Some(ExternalSig {
func_name: tyenv.syms[name.index()].clone(),
full_name: format!("C::{}", tyenv.syms[name.index()]),
param_tys: self.arg_tys.clone(),
ret_tys: vec![self.ret_ty],
infallible: !pure && !*multi,
multi: *multi,
}),
TermKind::Decl {
constructor_kind: Some(ConstructorKind::InternalConstructor { .. }),
multi,
constructor_kind: Some(kind),
flags,
..
} => {
let name = format!("constructor_{}", tyenv.syms[self.name.index()]);
let (func_name, full_name) = match kind {
ConstructorKind::InternalConstructor => {
let name = format!("constructor_{}", tyenv.syms[self.name.index()]);
(name.clone(), name)
}
ConstructorKind::ExternalConstructor { name } => (
tyenv.syms[name.index()].clone(),
format!("C::{}", tyenv.syms[name.index()]),
),
};
let ret_kind = if flags.multi {
ReturnKind::Iterator
} else if flags.partial {
ReturnKind::Option
} else {
ReturnKind::Plain
};
Some(ExternalSig {
func_name: name.clone(),
full_name: name,
func_name,
full_name,
param_tys: self.arg_tys.clone(),
ret_tys: vec![self.ret_ty],
// Internal constructors are always fallible, even
// if not pure, because ISLE allows partial
// matching at the toplevel (an entry point can
// fail to rewrite).
infallible: false,
multi: *multi,
ret_kind,
})
}
_ => None,
@ -625,7 +648,7 @@ impl Pattern {
panic!("Should have been expanded away")
}
TermKind::Decl {
multi,
flags,
extractor_kind: Some(ExtractorKind::ExternalExtractor { infallible, .. }),
..
} => {
@ -638,8 +661,8 @@ impl Pattern {
termdata.ret_ty,
output_tys,
term,
*infallible && !*multi,
*multi,
*infallible && !flags.multi,
flags.multi,
)
}
};
@ -737,30 +760,16 @@ impl Expr {
visitor.add_create_variant(arg_values_tys, ty, *variant)
}
TermKind::Decl {
constructor_kind: Some(ConstructorKind::InternalConstructor),
multi,
..
} => {
visitor.add_construct(
arg_values_tys,
ty,
term,
/* infallible = */ false,
*multi,
)
}
TermKind::Decl {
constructor_kind: Some(ConstructorKind::ExternalConstructor { .. }),
pure,
multi,
constructor_kind: Some(_),
flags,
..
} => {
visitor.add_construct(
arg_values_tys,
ty,
term,
/* infallible = */ !pure,
*multi,
/* infallible = */ !flags.partial,
flags.multi,
)
}
TermKind::Decl {
@ -1167,6 +1176,13 @@ impl TermEnv {
);
}
if decl.multi && decl.partial {
tyenv.report_error(
decl.pos,
format!("Term '{}' can't be both multi and partial", decl.term.0),
);
}
let arg_tys = decl
.arg_tys
.iter()
@ -1196,6 +1212,11 @@ impl TermEnv {
let tid = TermId(self.terms.len());
self.term_map.insert(name, tid);
let flags = TermFlags {
pure: decl.pure,
multi: decl.multi,
partial: decl.partial,
};
self.terms.push(Term {
id: tid,
decl_pos: decl.pos,
@ -1203,10 +1224,9 @@ impl TermEnv {
arg_tys,
ret_ty,
kind: TermKind::Decl {
flags,
constructor_kind: None,
extractor_kind: None,
pure: decl.pure,
multi: decl.multi,
},
});
}
@ -1364,12 +1384,12 @@ impl TermEnv {
continue;
}
TermKind::Decl {
multi,
flags,
extractor_kind,
..
} => match extractor_kind {
None => {
if *multi {
if flags.multi {
tyenv.report_error(
ext.pos,
"A term declared with `multi` cannot have an internal extractor.".to_string());
@ -1658,8 +1678,8 @@ impl TermEnv {
let termdata = &self.terms[root_term.index()];
let pure = match &termdata.kind {
&TermKind::Decl { pure, .. } => pure,
let flags = match &termdata.kind {
TermKind::Decl { flags, .. } => flags,
_ => {
tyenv.report_error(
pos,
@ -1676,14 +1696,17 @@ impl TermEnv {
let iflets = rule
.iflets
.iter()
.filter_map(|iflet| self.translate_iflet(tyenv, iflet, &mut bindings))
.filter_map(|iflet| {
self.translate_iflet(tyenv, iflet, &mut bindings, flags)
})
.collect();
let rhs = unwrap_or_continue!(self.translate_expr(
tyenv,
&rule.expr,
Some(termdata.ret_ty),
&mut bindings,
pure,
flags,
/* on_lhs */ false,
));
let rid = RuleId(self.rules.len());
@ -2058,7 +2081,8 @@ impl TermEnv {
expr: &ast::Expr,
ty: Option<TypeId>,
bindings: &mut Bindings,
pure: bool,
root_flags: &TermFlags,
on_lhs: bool,
) -> Option<Expr> {
log!("translate_expr: {:?}", expr);
match expr {
@ -2101,7 +2125,14 @@ impl TermEnv {
if let Some(expanded_expr) =
self.maybe_implicit_convert_expr(tyenv, expr, ret_ty, ty.unwrap())
{
return self.translate_expr(tyenv, &expanded_expr, ty, bindings, pure);
return self.translate_expr(
tyenv,
&expanded_expr,
ty,
bindings,
root_flags,
on_lhs,
);
}
tyenv.report_error(
@ -2116,9 +2147,11 @@ impl TermEnv {
ret_ty
};
// Check that the term's constructor is pure.
if pure {
if let TermKind::Decl { pure: false, .. } = &termdata.kind {
if let TermKind::Decl { flags, .. } = &termdata.kind {
// On the left-hand side of a rule or in a pure term, only pure terms may be
// used.
let pure_required = on_lhs || root_flags.pure;
if pure_required && !flags.pure {
tyenv.report_error(
pos,
format!(
@ -2127,6 +2160,36 @@ impl TermEnv {
),
);
}
// Multi-terms may only be used inside other multi-terms.
if !root_flags.multi && flags.multi {
tyenv.report_error(
pos,
format!(
"Used multi-term '{}' but this rule is not in a multi-term",
sym.0
),
);
}
// Partial terms may always be used on the left-hand side of a rule. On the
// right-hand side they may only be used inside other partial terms.
let partial_allowed = on_lhs || root_flags.partial;
if !partial_allowed && flags.partial {
tyenv.report_error(
pos,
format!(
"Rule can't use partial constructor '{}' on RHS; \
try moving it to if-let{}",
sym.0,
if root_flags.multi {
""
} else {
" or make this rule's term partial too"
}
),
);
}
}
termdata.check_args_count(args, tyenv, pos, sym);
@ -2136,7 +2199,7 @@ impl TermEnv {
.iter()
.zip(termdata.arg_tys.iter())
.filter_map(|(arg, &arg_ty)| {
self.translate_expr(tyenv, arg, Some(arg_ty), bindings, pure)
self.translate_expr(tyenv, arg, Some(arg_ty), bindings, root_flags, on_lhs)
})
.collect();
@ -2159,7 +2222,14 @@ impl TermEnv {
if let Some(expanded_expr) =
self.maybe_implicit_convert_expr(tyenv, expr, bv.ty, ty.unwrap())
{
return self.translate_expr(tyenv, &expanded_expr, ty, bindings, pure);
return self.translate_expr(
tyenv,
&expanded_expr,
ty,
bindings,
root_flags,
on_lhs,
);
}
tyenv.report_error(
@ -2251,7 +2321,8 @@ impl TermEnv {
&def.val,
Some(tid),
bindings,
pure
root_flags,
on_lhs,
)));
// Bind the var with the given type.
@ -2260,7 +2331,8 @@ impl TermEnv {
}
// Evaluate the body, expecting the type of the overall let-expr.
let body = Box::new(self.translate_expr(tyenv, body, ty, bindings, pure)?);
let body =
Box::new(self.translate_expr(tyenv, body, ty, bindings, root_flags, on_lhs)?);
let body_ty = body.ty();
// Pop the bindings.
@ -2280,10 +2352,18 @@ impl TermEnv {
tyenv: &mut TypeEnv,
iflet: &ast::IfLet,
bindings: &mut Bindings,
root_flags: &TermFlags,
) -> Option<IfLet> {
// Translate the expr first. Ensure it's pure.
let rhs =
self.translate_expr(tyenv, &iflet.expr, None, bindings, /* pure = */ true)?;
// Translate the expr first. The `if-let` and `if` forms are part of the left-hand side of
// the rule.
let rhs = self.translate_expr(
tyenv,
&iflet.expr,
None,
bindings,
root_flags,
/* on_lhs */ true,
)?;
let ty = rhs.ty();
let (lhs, _lhs_ty) = self.translate_pattern(tyenv, &iflet.pattern, Some(ty), bindings)?;

Loading…
Cancel
Save