ISLE: Allow shadowing in let expressions (#4562)

* Support shadowing in isle

* Re-run the isle build.rs if the examples change

* Print error messages when isle tests fail

* Move run tests

* Refactor `let` uses that don't need to introduce unique names
This commit is contained in:
Trevor Elliott
2022-08-01 14:10:28 -07:00
committed by GitHub
parent 25782b527e
commit 586ec95c11
10 changed files with 153 additions and 86 deletions

View File

@@ -1644,8 +1644,8 @@
(decl vec_rrrr_long (VecRRRLongOp Reg Reg Reg bool) Reg)
(rule (vec_rrrr_long op src1 src2 src3 high_half)
(let ((dst WritableReg (temp_writable_reg $I8X16))
(_1 Unit (emit (MInst.FpuMove128 dst src1)))
(_2 Unit (emit (MInst.VecRRRLong op dst src2 src3 high_half))))
(_ Unit (emit (MInst.FpuMove128 dst src1)))
(_ Unit (emit (MInst.VecRRRLong op dst src2 src3 high_half))))
dst))
;; Helper for emitting `MInst.VecRRNarrow` instructions.
@@ -1660,8 +1660,8 @@
(decl vec_rr_narrow_high (VecRRNarrowOp Reg Reg ScalarSize) Reg)
(rule (vec_rr_narrow_high op mod src size)
(let ((dst WritableReg (temp_writable_reg $I8X16))
(_1 Unit (emit (MInst.FpuMove128 dst mod)))
(_2 Unit (emit (MInst.VecRRNarrow op dst src $true size))))
(_ Unit (emit (MInst.FpuMove128 dst mod)))
(_ Unit (emit (MInst.VecRRNarrow op dst src $true size))))
dst))
;; Helper for emitting `MInst.VecRRLong` instructions.
@@ -1704,16 +1704,16 @@
(decl mov_to_vec (Reg Reg u8 VectorSize) Reg)
(rule (mov_to_vec src1 src2 lane size)
(let ((dst WritableReg (temp_writable_reg $I8X16))
(_1 Unit (emit (MInst.FpuMove128 dst src1)))
(_2 Unit (emit (MInst.MovToVec dst src2 lane size))))
(_ Unit (emit (MInst.FpuMove128 dst src1)))
(_ Unit (emit (MInst.MovToVec dst src2 lane size))))
dst))
;; Helper for emitting `MInst.VecMovElement` instructions.
(decl mov_vec_elem (Reg Reg u8 u8 VectorSize) Reg)
(rule (mov_vec_elem src1 src2 dst_idx src_idx size)
(let ((dst WritableReg (temp_writable_reg $I8X16))
(_1 Unit (emit (MInst.FpuMove128 dst src1)))
(_2 Unit (emit (MInst.VecMovElement dst src2 dst_idx src_idx size))))
(_ Unit (emit (MInst.FpuMove128 dst src1)))
(_ Unit (emit (MInst.VecMovElement dst src2 dst_idx src_idx size))))
dst))
;; Helper for emitting `MInst.MovFromVec` instructions.
@@ -2091,8 +2091,8 @@
(decl bsl (Type Reg Reg Reg) Reg)
(rule (bsl ty c x y)
(let ((dst WritableReg (temp_writable_reg ty))
(_1 Unit (emit (MInst.FpuMove128 dst c)))
(_2 Unit (emit (MInst.VecRRR (VecALUOp.Bsl) dst x y (vector_size ty)))))
(_ Unit (emit (MInst.FpuMove128 dst c)))
(_ Unit (emit (MInst.VecRRR (VecALUOp.Bsl) dst x y (vector_size ty)))))
dst))
;; Helper for generating a `udf` instruction.
@@ -2188,16 +2188,16 @@
(rule (trap_if_div_overflow ty x y)
(let (
;; Check RHS is -1.
(_1 Unit (emit (MInst.AluRRImm12 (ALUOp.AddS) (operand_size ty) (writable_zero_reg) y (u8_into_imm12 1))))
(_ Unit (emit (MInst.AluRRImm12 (ALUOp.AddS) (operand_size ty) (writable_zero_reg) y (u8_into_imm12 1))))
;; Check LHS is min_value, by subtracting 1 and branching if
;; there is overflow.
(_2 Unit (emit (MInst.CCmpImm (size_from_ty ty)
(_ Unit (emit (MInst.CCmpImm (size_from_ty ty)
x
(u8_into_uimm5 1)
(nzcv $false $false $false $false)
(Cond.Eq))))
(_3 Unit (emit (MInst.TrapIf (cond_br_cond (Cond.Vs))
(_ Unit (emit (MInst.TrapIf (cond_br_cond (Cond.Vs))
(trap_code_integer_overflow))))
)
x))
@@ -2371,8 +2371,8 @@
(rule (lse_atomic_cas addr expect replace ty)
(let (
(dst WritableReg (temp_writable_reg ty))
(_1 Unit (emit (MInst.Mov (operand_size ty) dst expect)))
(_2 Unit (emit (MInst.AtomicCAS dst replace addr ty)))
(_ Unit (emit (MInst.Mov (operand_size ty) dst expect)))
(_ Unit (emit (MInst.AtomicCAS dst replace addr ty)))
)
dst))

View File

@@ -1840,8 +1840,8 @@
(decl umul_wide (Reg Reg) RegPair)
(rule (umul_wide src1 src2)
(let ((dst WritableRegPair (temp_writable_regpair))
(_1 Unit (emit (MInst.Mov64 (writable_regpair_lo dst) src2)))
(_2 Unit (emit (MInst.UMulWide src1))))
(_ Unit (emit (MInst.Mov64 (writable_regpair_lo dst) src2)))
(_ Unit (emit (MInst.UMulWide src1))))
dst))
;; Helper for emitting `MInst.SDivMod32` instructions.
@@ -2425,8 +2425,8 @@
;; Push instructions to break out of the loop if condition is met.
(decl push_break_if (VecMInstBuilder ProducesFlags Cond) Reg)
(rule (push_break_if ib (ProducesFlags.ProducesFlagsSideEffect inst) cond)
(let ((_1 Unit (inst_builder_push ib inst))
(_2 Unit (inst_builder_push ib (MInst.CondBreak cond))))
(let ((_ Unit (inst_builder_push ib inst))
(_ Unit (inst_builder_push ib (MInst.CondBreak cond))))
(invalid_reg)))
;; Emit a `MInst.Loop` instruction holding a loop body instruction sequence.
@@ -2985,12 +2985,12 @@
(decl trap_if (ProducesFlags Cond TrapCode) Reg)
(rule (trap_if (ProducesFlags.ProducesFlagsReturnsReg inst result) cond trap_code)
(let ((_1 Unit (emit inst))
(_2 Unit (emit (MInst.TrapIf cond trap_code))))
(let ((_ Unit (emit inst))
(_ Unit (emit (MInst.TrapIf cond trap_code))))
result))
(rule (trap_if (ProducesFlags.ProducesFlagsSideEffect inst) cond trap_code)
(let ((_1 Unit (emit inst))
(_2 Unit (emit (MInst.TrapIf cond trap_code))))
(let ((_ Unit (emit inst))
(_ Unit (emit (MInst.TrapIf cond trap_code))))
(invalid_reg)))
(decl icmps_reg_and_trap (Type Reg Reg Cond TrapCode) Reg)
@@ -3058,18 +3058,18 @@
(decl select_bool_reg (Type ProducesBool Reg Reg) Reg)
(rule (select_bool_reg ty (ProducesBool.ProducesBool producer cond) reg_true reg_false)
(let ((dst WritableReg (temp_writable_reg ty))
(_1 Unit (emit_producer producer))
(_2 Unit (emit_mov ty dst reg_false))
(_3 Unit (emit_consumer (emit_cmov_reg ty dst cond reg_true))))
(_ Unit (emit_producer producer))
(_ Unit (emit_mov ty dst reg_false))
(_ Unit (emit_consumer (emit_cmov_reg ty dst cond reg_true))))
dst))
;; Use a boolean condition to select between two immediate values.
(decl select_bool_imm (Type ProducesBool i16 u64) Reg)
(rule (select_bool_imm ty (ProducesBool.ProducesBool producer cond) imm_true imm_false)
(let ((dst WritableReg (temp_writable_reg ty))
(_1 Unit (emit_producer producer))
(_2 Unit (emit_imm ty dst imm_false))
(_3 Unit (emit_consumer (emit_cmov_imm ty dst cond imm_true))))
(_ Unit (emit_producer producer))
(_ Unit (emit_imm ty dst imm_false))
(_ Unit (emit_consumer (emit_cmov_imm ty dst cond imm_true))))
dst))
;; Lower a boolean condition to a boolean type. The value used to represent
@@ -3129,8 +3129,8 @@
(result Reg (push_atomic_cas ib (ty_ext32 ty)
(casloop_val_reg) val aligned_mem))
;; Emit initial load followed by compare-and-swap loop.
(_1 Unit (emit_load (ty_ext32 ty) (casloop_val_reg) aligned_mem))
(_2 Unit (emit_loop ib (intcc_as_cond (IntCC.NotEqual)))))
(_ Unit (emit_load (ty_ext32 ty) (casloop_val_reg) aligned_mem))
(_ Unit (emit_loop ib (intcc_as_cond (IntCC.NotEqual)))))
result))
;; Compute the previous memory value after a (fullword) compare-and-swap loop.
@@ -3345,8 +3345,8 @@
;; conditional move, and because flogr returns a register pair.
(rule (clz_reg zeroval x)
(let ((dst WritableRegPair (temp_writable_regpair))
(_1 Unit (emit (MInst.Flogr x)))
(_2 Unit (emit (MInst.CMov64SImm16 (writable_regpair_hi dst)
(_ Unit (emit (MInst.Flogr x)))
(_ Unit (emit (MInst.CMov64SImm16 (writable_regpair_hi dst)
(intcc_as_cond (IntCC.Equal)) zeroval))))
dst))

View File

@@ -573,9 +573,9 @@
(ext_y Reg (put_in_reg_sext32 y))
(ext_ty Type (ty_ext32 ty))
;; Perform division-by-zero check (same as for `udiv`).
(_1 Reg (maybe_trap_if_zero_divisor DZcheck ext_ty ext_y))
(_ Reg (maybe_trap_if_zero_divisor DZcheck ext_ty ext_y))
;; Perform integer-overflow check if necessary.
(_2 Reg (maybe_trap_if_sdiv_overflow OFcheck ext_ty ty ext_x ext_y))
(_ Reg (maybe_trap_if_sdiv_overflow OFcheck ext_ty ty ext_x ext_y))
;; Emit the actual divide instruction.
(pair RegPair (sdivmod ext_ty ext_x ext_y)))
;; The quotient can be found in the low half of the result.
@@ -1504,14 +1504,14 @@
(fcvt_to_uint x @ (value_type src_ty))))
(let ((src Reg (put_in_reg x))
;; First, check whether the input is a NaN, and trap if so.
(_1 Reg (trap_if (fcmp_reg src_ty src src)
(_ Reg (trap_if (fcmp_reg src_ty src src)
(floatcc_as_cond (FloatCC.Unordered))
(trap_code_bad_conversion_to_integer)))
;; Now check whether the input is out of range for the target type.
(_2 Reg (trap_if (fcmp_reg src_ty src (fcvt_to_uint_ub src_ty dst_ty))
(_ Reg (trap_if (fcmp_reg src_ty src (fcvt_to_uint_ub src_ty dst_ty))
(floatcc_as_cond (FloatCC.GreaterThanOrEqual))
(trap_code_integer_overflow)))
(_3 Reg (trap_if (fcmp_reg src_ty src (fcvt_to_uint_lb src_ty))
(_ Reg (trap_if (fcmp_reg src_ty src (fcvt_to_uint_lb src_ty))
(floatcc_as_cond (FloatCC.LessThanOrEqual))
(trap_code_integer_overflow)))
;; Perform the conversion using the larger type size.
@@ -1528,14 +1528,14 @@
(fcvt_to_sint x @ (value_type src_ty))))
(let ((src Reg (put_in_reg x))
;; First, check whether the input is a NaN, and trap if so.
(_1 Reg (trap_if (fcmp_reg src_ty src src)
(_ Reg (trap_if (fcmp_reg src_ty src src)
(floatcc_as_cond (FloatCC.Unordered))
(trap_code_bad_conversion_to_integer)))
;; Now check whether the input is out of range for the target type.
(_2 Reg (trap_if (fcmp_reg src_ty src (fcvt_to_sint_ub src_ty dst_ty))
(_ Reg (trap_if (fcmp_reg src_ty src (fcvt_to_sint_ub src_ty dst_ty))
(floatcc_as_cond (FloatCC.GreaterThanOrEqual))
(trap_code_integer_overflow)))
(_3 Reg (trap_if (fcmp_reg src_ty src (fcvt_to_sint_lb src_ty dst_ty))
(_ Reg (trap_if (fcmp_reg src_ty src (fcvt_to_sint_lb src_ty dst_ty))
(floatcc_as_cond (FloatCC.LessThanOrEqual))
(trap_code_integer_overflow)))
;; Perform the conversion using the larger type size.
@@ -3571,27 +3571,27 @@
;; Direct call to an in-range function.
(rule (lower (call (func_ref_data sig_ref name (reloc_distance_near)) args))
(let ((abi ABISig (abi_sig sig_ref))
(_1 Unit (abi_accumulate_outgoing_args_size abi))
(_2 InstOutput (lower_call_args abi (range 0 (abi_num_args abi)) args))
(_3 InstOutput (side_effect (abi_call abi name (Opcode.Call)))))
(_ Unit (abi_accumulate_outgoing_args_size abi))
(_ InstOutput (lower_call_args abi (range 0 (abi_num_args abi)) args))
(_ InstOutput (side_effect (abi_call abi name (Opcode.Call)))))
(lower_call_rets abi (range 0 (abi_num_rets abi)) (output_builder_new))))
;; Direct call to an out-of-range function (implicitly via pointer).
(rule (lower (call (func_ref_data sig_ref name _) args))
(let ((abi ABISig (abi_sig sig_ref))
(_1 Unit (abi_accumulate_outgoing_args_size abi))
(_2 InstOutput (lower_call_args abi (range 0 (abi_num_args abi)) args))
(_ Unit (abi_accumulate_outgoing_args_size abi))
(_ InstOutput (lower_call_args abi (range 0 (abi_num_args abi)) args))
(target Reg (load_ext_name_far name 0))
(_3 InstOutput (side_effect (abi_call_ind abi target (Opcode.Call)))))
(_ InstOutput (side_effect (abi_call_ind abi target (Opcode.Call)))))
(lower_call_rets abi (range 0 (abi_num_rets abi)) (output_builder_new))))
;; Indirect call.
(rule (lower (call_indirect sig_ref ptr args))
(let ((abi ABISig (abi_sig sig_ref))
(target Reg (put_in_reg ptr))
(_1 Unit (abi_accumulate_outgoing_args_size abi))
(_2 InstOutput (lower_call_args abi (range 0 (abi_num_args abi)) args))
(_3 InstOutput (side_effect (abi_call_ind abi target (Opcode.CallIndirect)))))
(_ Unit (abi_accumulate_outgoing_args_size abi))
(_ InstOutput (lower_call_args abi (range 0 (abi_num_args abi)) args))
(_ InstOutput (side_effect (abi_call_ind abi target (Opcode.CallIndirect)))))
(lower_call_rets abi (range 0 (abi_num_rets abi)) (output_builder_new))))
;; Lower function arguments by loading them into registers / stack slots.

View File

@@ -1492,14 +1492,14 @@
(rule (make_i64x2_from_lanes lo hi)
(let ((dst_xmm WritableXmm (temp_writable_xmm))
(dst_reg WritableReg dst_xmm)
(_0 Unit (emit (MInst.XmmUninitializedValue dst_xmm)))
(_1 Unit (emit (MInst.XmmRmRImm (SseOpcode.Pinsrd)
(_ Unit (emit (MInst.XmmUninitializedValue dst_xmm)))
(_ Unit (emit (MInst.XmmRmRImm (SseOpcode.Pinsrd)
dst_reg
lo
dst_reg
0
(OperandSize.Size64))))
(_2 Unit (emit (MInst.XmmRmRImm (SseOpcode.Pinsrd)
(_ Unit (emit (MInst.XmmRmRImm (SseOpcode.Pinsrd)
dst_reg
hi
dst_reg

View File

@@ -551,8 +551,8 @@
(let ((_ Unit (emit inst)))
(output_none)))
(rule (side_effect (SideEffectNoResult.Inst2 inst1 inst2))
(let ((_1 Unit (emit inst1))
(_2 Unit (emit inst2)))
(let ((_ Unit (emit inst1))
(_ Unit (emit inst2)))
(output_none)))
(decl side_effect_concat (SideEffectNoResult SideEffectNoResult) SideEffectNoResult)

View File

@@ -2,6 +2,7 @@ use std::fmt::Write;
fn main() {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=isle_examples");
let out_dir = std::path::PathBuf::from(
std::env::var_os("OUT_DIR").expect("The OUT_DIR environment variable must be set"),

View File

@@ -0,0 +1,38 @@
(type u64 (primitive u64))
(decl foo (u64) u64)
(rule (foo x) x)
;; Shadowing of a global name
(decl test1 (u64) u64)
(rule (test1 x)
(let ((foo u64 x))
foo))
;; Shadowing of a parameter
(decl test2 (u64) u64)
(rule (test2 x)
(let ((x u64 x))
x))
;; Shadowing of this binding's name
(decl test3 (u64) u64)
(rule (test3 x)
(let ((test3 u64 x))
test3))
;; Shadowing another let-bound name
(decl test4 (u64) u64)
(rule (test4 x)
(let ((val u64 x)
(val u64 23))
val))
;; Shadowing a global with a parameter name
(decl test5 (u64) u64)
(rule (test5 foo) foo)
;; Using a previously shadowed global
(decl test6 (u64) u64)
(rule (test6 x) (foo x))

View File

@@ -0,0 +1,27 @@
mod let_shadowing;
struct Context;
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!(Some(20), let_shadowing::constructor_test2(&mut ctx, 20));
assert_eq!(Some(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!(Some(23), let_shadowing::constructor_test4(&mut ctx, 20));
assert_eq!(Some(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!(Some(20), let_shadowing::constructor_test6(&mut ctx, 20));
assert_eq!(Some(97), let_shadowing::constructor_test6(&mut ctx, 97));
}

View File

@@ -1951,9 +1951,6 @@ impl TermEnv {
for def in defs {
// Check that the given variable name does not already exist.
let name = tyenv.intern_mut(&def.var);
if bindings.vars.iter().any(|bv| bv.name == name) {
tyenv.report_error(pos, format!("Variable '{}' already bound", def.var.0));
}
// Look up the type.
let tysym = match tyenv.intern(&def.ty) {

View File

@@ -11,11 +11,15 @@ fn build(filename: &str) -> Result<String> {
}
pub fn run_pass(filename: &str) {
assert!(build(filename).is_ok());
if let Err(err) = build(filename) {
panic!("pass test failed:\n{}", err);
}
}
pub fn run_fail(filename: &str) {
assert!(build(filename).is_err());
if build(filename).is_ok() {
panic!("test {} passed unexpectedly", filename);
}
}
fn build_and_link_isle(isle_filename: &str) -> (tempfile::TempDir, std::path::PathBuf) {