From b0ed3641f302b92b7d010a5202e2dc73a3613b7c Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Tue, 15 Sep 2026 17:21:01 +0000 Subject: [PATCH] =?UTF-8?q?test:=20=E6=9E=84=E9=80=A0=E5=99=A8=E5=86=85?= =?UTF-8?q?=E8=81=94=E6=B5=8B=E8=AF=95=E7=A7=BB=E5=85=A5=20oxide=5Fir=20?= =?UTF-8?q?=E9=9B=86=E6=88=90=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_ir/src/inst.rs | 204 ------------------ .../oxide_ir/tests/inst_constructors_tests.rs | 204 ++++++++++++++++++ 2 files changed, 204 insertions(+), 204 deletions(-) create mode 100644 crates/oxide_ir/tests/inst_constructors_tests.rs diff --git a/crates/oxide_ir/src/inst.rs b/crates/oxide_ir/src/inst.rs index fb5c9b33..6ead67d3 100644 --- a/crates/oxide_ir/src/inst.rs +++ b/crates/oxide_ir/src/inst.rs @@ -524,207 +524,3 @@ fn spread_ext(words: &[u32]) -> SmallVec<[u32; 4]> { ext.extend_from_slice(words); ext } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn inst_new_has_empty_ext() { - let inst = Inst::new(OpCode::ADD, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)); - assert!(inst.ext.is_empty()); - assert_eq!(inst.op, OpCode::ADD); - } - - #[test] - fn ic_instructions_carry_ic_slots_zero_ext_words() { - let insts = [ - Inst::ic_get(Operand::Reg(1), Operand::Reg(2)), - Inst::ic_set(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)), - Inst::member_inc(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)), - Inst::member_dec(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)), - Inst::compound_member_add(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)), - Inst::compound_member_sub(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)), - Inst::compound_member_mul(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)), - Inst::compound_member_div(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)), - Inst::compound_member_mod(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)), - Inst::compound_member_exp(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)), - Inst::compound_member_bit_and(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)), - Inst::compound_member_bit_or(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)), - Inst::compound_member_bit_xor(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)), - Inst::compound_member_shl(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)), - Inst::compound_member_shr(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)), - Inst::compound_member_ushr(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)), - ]; - for inst in &insts { - assert_eq!( - inst.ext.as_slice(), - &[0; IC_EXT_WORDS], - "IC op {} must carry {IC_EXT_WORDS} zero ext words", - inst.op - ); - assert_eq!(inst.ext.len(), IC_EXT_WORDS); - } - } - - #[test] - fn call_instructions_carry_nargs() { - let call = Inst::call(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2), 3); - assert_eq!(call.ext.as_slice(), &[3]); - assert_eq!(call.rd, Operand::Reg(0)); - assert_eq!(call.a, Operand::Reg(1)); - assert_eq!(call.b, Operand::Reg(2)); - - let native = Inst::call_native(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2), 0); - assert_eq!(native.ext.as_slice(), &[0]); - - let new_expr = Inst::new_expression(Operand::Reg(3), Operand::Reg(0), Operand::Reg(1), 2); - assert_eq!(new_expr.ext.as_slice(), &[2]); - assert_eq!(new_expr.rd, Operand::Reg(3)); - assert_eq!(new_expr.a, Operand::Reg(0)); - - let super_call = Inst::super_call(Operand::Reg(3), Operand::Reg(1), 1); - assert_eq!(super_call.ext.as_slice(), &[1]); - assert_eq!(super_call.rd, Operand::Reg(3)); - assert_eq!(super_call.a, Operand::Reg(1)); - } - - #[test] - fn single_ext_word_instructions() { - let accessor = Inst::define_accessor(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2), 42); - assert_eq!(accessor.ext.as_slice(), &[42]); - assert_eq!(accessor.rd, Operand::Reg(0)); - assert_eq!(accessor.a, Operand::Reg(1)); - assert_eq!(accessor.b, Operand::Reg(2)); - - let dyn_accessor = Inst::define_accessor_dynamic(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2), 7); - assert_eq!(dyn_accessor.op, OpCode::DEFINE_ACCESSOR_DYNAMIC); - assert_eq!(dyn_accessor.ext.as_slice(), &[0x8000_0000 | 7]); - assert_eq!(dyn_accessor.rd, Operand::Reg(0)); - assert_eq!(dyn_accessor.a, Operand::Reg(1)); - assert_eq!(dyn_accessor.b, Operand::Reg(2)); - - let rest = Inst::rest_object(Operand::Reg(0), Operand::Reg(1), 7, None); - assert_eq!(rest.ext.as_slice(), &[7]); - - let spread = Inst::spread_object(Operand::Reg(0), Operand::Reg(1)); - assert_eq!(spread.op, OpCode::SPREAD_OBJECT); - assert_eq!(spread.rd, Operand::Reg(0)); - assert_eq!(spread.a, Operand::Reg(1)); - assert_eq!(spread.b, Operand::None); - assert!(spread.ext.is_empty()); - } - - #[test] - fn spread_call_constructors_carry_header_and_source_regs() { - let call = Inst::call_spread(Operand::Reg(0), Operand::Reg(1), &[10, 0x8000_0000 | 300]); - assert_eq!(call.op, OpCode::CALL_SPREAD); - assert_eq!(call.rd, Operand::Reg(0)); - assert_eq!(call.a, Operand::Reg(1)); - assert_eq!(call.b, Operand::None); - assert_eq!(call.ext.as_slice(), &[1 | (1 << 8), 10, 0x8000_0000 | 300]); - - let ne = Inst::new_expression_spread(Operand::Reg(3), Operand::Reg(0), &[0x8000_0000 | 7]); - assert_eq!(ne.op, OpCode::NEW_EXPRESSION_SPREAD); - assert_eq!(ne.rd, Operand::Reg(3)); - assert_eq!(ne.a, Operand::Reg(0)); - assert_eq!(ne.ext.as_slice(), &[1 << 8, 0x8000_0000 | 7]); - - let sc = Inst::super_call_spread(Operand::Reg(3), &[1, 0x8000_0000 | 5]); - assert_eq!(sc.op, OpCode::SUPER_CALL_SPREAD); - assert_eq!(sc.rd, Operand::Reg(3)); - assert_eq!(sc.a, Operand::None); - assert_eq!(sc.b, Operand::None); - assert_eq!(sc.ext.as_slice(), &[1 | (1 << 8), 1, 0x8000_0000 | 5]); - } - - #[test] - fn regalloc_constructors_carry_expected_slots() { - let mov = Inst::inst_mov(Operand::Reg(3), Operand::Reg(7)); - assert_eq!(mov.op, OpCode::MOV); - assert_eq!(mov.rd, Operand::Reg(3)); - assert_eq!(mov.a, Operand::Reg(7)); - assert_eq!(mov.b, Operand::None); - assert!(mov.ext.is_empty()); - - let spill = Inst::inst_spill(Operand::Reg(5), 42); - assert_eq!(spill.op, OpCode::SPILL); - assert_eq!(spill.rd, Operand::Reg(5)); - assert_eq!(spill.a, Operand::None); - assert_eq!(spill.b, Operand::None); - assert_eq!(spill.ext.as_slice(), &[42]); - - let unspill = Inst::inst_unspill(Operand::Reg(9), 0xFFFF); - assert_eq!(unspill.op, OpCode::UNSPILL); - assert_eq!(unspill.rd, Operand::Reg(9)); - assert_eq!(unspill.a, Operand::None); - assert_eq!(unspill.b, Operand::None); - assert_eq!(unspill.ext.as_slice(), &[0xFFFF]); - } - - #[test] - fn load_const_and_create_closure_keep_semantic_operands() { - let lc = Inst::load_const(Operand::Reg(4), 300); - assert_eq!(lc.a, Operand::Const(300)); - assert_eq!(lc.b, Operand::None); - assert!(lc.ext.is_empty()); - - let cc = Inst::create_closure(Operand::Reg(4), 5); - assert_eq!(cc.a, Operand::Imm(5)); - assert_eq!(cc.b, Operand::None); - assert!(cc.ext.is_empty()); - - let ca = Inst::create_arguments(Operand::Reg(6)); - assert_eq!(ca.op, OpCode::CREATE_ARGUMENTS); - assert_eq!(ca.rd, Operand::Reg(6)); - assert_eq!(ca.a, Operand::None); - assert_eq!(ca.b, Operand::None); - assert!(ca.ext.is_empty()); - } - - #[test] - fn jump_family_puts_label_in_b_slot() { - let jmp = Inst::jmp(9); - assert_eq!(jmp.b, Operand::Label(9)); - assert_eq!(jmp.rd, Operand::None); - - let cond = Inst::jmp_if_false(3, 9); - assert_eq!(cond.rd, Operand::Reg(3)); - assert_eq!(cond.b, Operand::Label(9)); - - let true_jmp = Inst::jmp_if_true(3, 9); - assert_eq!(true_jmp.b, Operand::Label(9)); - - let nullish = Inst::jmp_if_nullish(3, 9); - assert_eq!(nullish.b, Operand::Label(9)); - - let try_begin = Inst::try_begin(9); - assert_eq!(try_begin.b, Operand::Label(9)); - assert_eq!(try_begin.rd, Operand::None); - - let try_fin = Inst::try_finally_begin(9); - assert_eq!(try_fin.b, Operand::Label(9)); - } - - #[test] - fn template_str_packs_segment_count_and_hint() { - let inst = Inst::template_str(Operand::Reg(1), 3, 10, &[0x1234, 0x8000_0000 | 5]); - assert_eq!(inst.op, OpCode::TEMPLATE_STR); - assert_eq!(inst.rd, Operand::Reg(1)); - assert_eq!(inst.ext.len(), 3); - assert_eq!(inst.ext[0], (3 << 16) | 10); - assert_eq!(inst.ext[1], 0x1234); - assert_eq!(inst.ext[2], 0x8000_0000 | 5); - } - - #[test] - fn concat_n_packs_n_header_and_operand_regs() { - // 6 操作数:a 槽=op1,ext=[6, op2..op6] - let inst = Inst::concat_n(Operand::Reg(1), &[2, 5, 9, 12, 300, 7]); - assert_eq!(inst.op, OpCode::CONCAT_N); - assert_eq!(inst.rd, Operand::Reg(1)); - assert_eq!(inst.a, Operand::Reg(2)); - assert_eq!(inst.b, Operand::None); - assert_eq!(inst.ext.as_slice(), &[6, 5, 9, 12, 300, 7]); - } -} diff --git a/crates/oxide_ir/tests/inst_constructors_tests.rs b/crates/oxide_ir/tests/inst_constructors_tests.rs new file mode 100644 index 00000000..d3a55341 --- /dev/null +++ b/crates/oxide_ir/tests/inst_constructors_tests.rs @@ -0,0 +1,204 @@ +//! Inst 构造器 API 集成测试:各构造器族的 ext 字布局与操作数槽位断言。 + +use oxide_bytecode::opcode::{OpCode, IC_EXT_WORDS}; +use oxide_ir::inst::Inst; +use oxide_ir::operand::Operand; + +#[test] +fn inst_new_has_empty_ext() { + let inst = Inst::new(OpCode::ADD, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)); + assert!(inst.ext.is_empty()); + assert_eq!(inst.op, OpCode::ADD); +} + +#[test] +fn ic_instructions_carry_ic_slots_zero_ext_words() { + let insts = [ + Inst::ic_get(Operand::Reg(1), Operand::Reg(2)), + Inst::ic_set(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)), + Inst::member_inc(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)), + Inst::member_dec(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)), + Inst::compound_member_add(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)), + Inst::compound_member_sub(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)), + Inst::compound_member_mul(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)), + Inst::compound_member_div(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)), + Inst::compound_member_mod(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)), + Inst::compound_member_exp(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)), + Inst::compound_member_bit_and(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)), + Inst::compound_member_bit_or(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)), + Inst::compound_member_bit_xor(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)), + Inst::compound_member_shl(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)), + Inst::compound_member_shr(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)), + Inst::compound_member_ushr(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)), + ]; + for inst in &insts { + assert_eq!( + inst.ext.as_slice(), + &[0; IC_EXT_WORDS], + "IC op {} must carry {IC_EXT_WORDS} zero ext words", + inst.op + ); + assert_eq!(inst.ext.len(), IC_EXT_WORDS); + } +} + +#[test] +fn call_instructions_carry_nargs() { + let call = Inst::call(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2), 3); + assert_eq!(call.ext.as_slice(), &[3]); + assert_eq!(call.rd, Operand::Reg(0)); + assert_eq!(call.a, Operand::Reg(1)); + assert_eq!(call.b, Operand::Reg(2)); + + let native = Inst::call_native(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2), 0); + assert_eq!(native.ext.as_slice(), &[0]); + + let new_expr = Inst::new_expression(Operand::Reg(3), Operand::Reg(0), Operand::Reg(1), 2); + assert_eq!(new_expr.ext.as_slice(), &[2]); + assert_eq!(new_expr.rd, Operand::Reg(3)); + assert_eq!(new_expr.a, Operand::Reg(0)); + + let super_call = Inst::super_call(Operand::Reg(3), Operand::Reg(1), 1); + assert_eq!(super_call.ext.as_slice(), &[1]); + assert_eq!(super_call.rd, Operand::Reg(3)); + assert_eq!(super_call.a, Operand::Reg(1)); +} + +#[test] +fn single_ext_word_instructions() { + let accessor = Inst::define_accessor(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2), 42); + assert_eq!(accessor.ext.as_slice(), &[42]); + assert_eq!(accessor.rd, Operand::Reg(0)); + assert_eq!(accessor.a, Operand::Reg(1)); + assert_eq!(accessor.b, Operand::Reg(2)); + + let dyn_accessor = Inst::define_accessor_dynamic(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2), 7); + assert_eq!(dyn_accessor.op, OpCode::DEFINE_ACCESSOR_DYNAMIC); + assert_eq!(dyn_accessor.ext.as_slice(), &[0x8000_0000 | 7]); + assert_eq!(dyn_accessor.rd, Operand::Reg(0)); + assert_eq!(dyn_accessor.a, Operand::Reg(1)); + assert_eq!(dyn_accessor.b, Operand::Reg(2)); + + let rest = Inst::rest_object(Operand::Reg(0), Operand::Reg(1), 7, None); + assert_eq!(rest.ext.as_slice(), &[7]); + + let spread = Inst::spread_object(Operand::Reg(0), Operand::Reg(1)); + assert_eq!(spread.op, OpCode::SPREAD_OBJECT); + assert_eq!(spread.rd, Operand::Reg(0)); + assert_eq!(spread.a, Operand::Reg(1)); + assert_eq!(spread.b, Operand::None); + assert!(spread.ext.is_empty()); +} + +#[test] +fn spread_call_constructors_carry_header_and_source_regs() { + let call = Inst::call_spread(Operand::Reg(0), Operand::Reg(1), &[10, 0x8000_0000 | 300]); + assert_eq!(call.op, OpCode::CALL_SPREAD); + assert_eq!(call.rd, Operand::Reg(0)); + assert_eq!(call.a, Operand::Reg(1)); + assert_eq!(call.b, Operand::None); + assert_eq!(call.ext.as_slice(), &[1 | (1 << 8), 10, 0x8000_0000 | 300]); + + let ne = Inst::new_expression_spread(Operand::Reg(3), Operand::Reg(0), &[0x8000_0000 | 7]); + assert_eq!(ne.op, OpCode::NEW_EXPRESSION_SPREAD); + assert_eq!(ne.rd, Operand::Reg(3)); + assert_eq!(ne.a, Operand::Reg(0)); + assert_eq!(ne.ext.as_slice(), &[1 << 8, 0x8000_0000 | 7]); + + let sc = Inst::super_call_spread(Operand::Reg(3), &[1, 0x8000_0000 | 5]); + assert_eq!(sc.op, OpCode::SUPER_CALL_SPREAD); + assert_eq!(sc.rd, Operand::Reg(3)); + assert_eq!(sc.a, Operand::None); + assert_eq!(sc.b, Operand::None); + assert_eq!(sc.ext.as_slice(), &[1 | (1 << 8), 1, 0x8000_0000 | 5]); +} + +#[test] +fn regalloc_constructors_carry_expected_slots() { + let mov = Inst::inst_mov(Operand::Reg(3), Operand::Reg(7)); + assert_eq!(mov.op, OpCode::MOV); + assert_eq!(mov.rd, Operand::Reg(3)); + assert_eq!(mov.a, Operand::Reg(7)); + assert_eq!(mov.b, Operand::None); + assert!(mov.ext.is_empty()); + + let spill = Inst::inst_spill(Operand::Reg(5), 42); + assert_eq!(spill.op, OpCode::SPILL); + assert_eq!(spill.rd, Operand::Reg(5)); + assert_eq!(spill.a, Operand::None); + assert_eq!(spill.b, Operand::None); + assert_eq!(spill.ext.as_slice(), &[42]); + + let unspill = Inst::inst_unspill(Operand::Reg(9), 0xFFFF); + assert_eq!(unspill.op, OpCode::UNSPILL); + assert_eq!(unspill.rd, Operand::Reg(9)); + assert_eq!(unspill.a, Operand::None); + assert_eq!(unspill.b, Operand::None); + assert_eq!(unspill.ext.as_slice(), &[0xFFFF]); +} + +#[test] +fn load_const_and_create_closure_keep_semantic_operands() { + let lc = Inst::load_const(Operand::Reg(4), 300); + assert_eq!(lc.a, Operand::Const(300)); + assert_eq!(lc.b, Operand::None); + assert!(lc.ext.is_empty()); + + let cc = Inst::create_closure(Operand::Reg(4), 5); + assert_eq!(cc.a, Operand::Imm(5)); + assert_eq!(cc.b, Operand::None); + assert!(cc.ext.is_empty()); + + let ca = Inst::create_arguments(Operand::Reg(6)); + assert_eq!(ca.op, OpCode::CREATE_ARGUMENTS); + assert_eq!(ca.rd, Operand::Reg(6)); + assert_eq!(ca.a, Operand::None); + assert_eq!(ca.b, Operand::None); + assert!(ca.ext.is_empty()); +} + +#[test] +fn jump_family_puts_label_in_b_slot() { + let jmp = Inst::jmp(9); + assert_eq!(jmp.b, Operand::Label(9)); + assert_eq!(jmp.rd, Operand::None); + + let cond = Inst::jmp_if_false(3, 9); + assert_eq!(cond.rd, Operand::Reg(3)); + assert_eq!(cond.b, Operand::Label(9)); + + let true_jmp = Inst::jmp_if_true(3, 9); + assert_eq!(true_jmp.b, Operand::Label(9)); + + let nullish = Inst::jmp_if_nullish(3, 9); + assert_eq!(nullish.b, Operand::Label(9)); + + let try_begin = Inst::try_begin(9); + assert_eq!(try_begin.b, Operand::Label(9)); + assert_eq!(try_begin.rd, Operand::None); + + let try_fin = Inst::try_finally_begin(9); + assert_eq!(try_fin.b, Operand::Label(9)); +} + +#[test] +fn template_str_packs_segment_count_and_hint() { + let inst = Inst::template_str(Operand::Reg(1), 3, 10, &[0x1234, 0x8000_0000 | 5]); + assert_eq!(inst.op, OpCode::TEMPLATE_STR); + assert_eq!(inst.rd, Operand::Reg(1)); + assert_eq!(inst.ext.len(), 3); + assert_eq!(inst.ext[0], (3 << 16) | 10); + assert_eq!(inst.ext[1], 0x1234); + assert_eq!(inst.ext[2], 0x8000_0000 | 5); +} + +#[test] +fn concat_n_packs_n_header_and_operand_regs() { + // 6 操作数:a 槽=op1,ext=[6, op2..op6] + let inst = Inst::concat_n(Operand::Reg(1), &[2, 5, 9, 12, 300, 7]); + assert_eq!(inst.op, OpCode::CONCAT_N); + assert_eq!(inst.rd, Operand::Reg(1)); + assert_eq!(inst.a, Operand::Reg(2)); + assert_eq!(inst.b, Operand::None); + assert_eq!(inst.ext.as_slice(), &[6, 5, 9, 12, 300, 7]); +}