Skip to main content

luaur_code_gen/functions/
translate_inst_for_n_loop.rs

1use crate::enums::ir_cmd::IrCmd;
2use crate::enums::ir_condition::IrCondition;
3use crate::enums::ir_op_kind::IrOpKind;
4use crate::records::ir_builder::IrBuilder;
5use crate::type_aliases::instruction_ir_builder::Instruction;
6
7use luaur_common::enums::luau_opcode::LuauOpcode;
8use luaur_common::macros::luau_insn_a::LUAU_INSN_A;
9use luaur_common::macros::luau_insn_op::LUAU_INSN_OP;
10
11use crate::functions::get_jump_target::get_jump_target;
12use crate::functions::get_op_length::get_op_length;
13use crate::macros::codegen_assert::CODEGEN_ASSERT;
14
15pub fn translate_inst_for_n_loop(build: &mut IrBuilder, pc: *const Instruction, pcpos: i32) {
16    let ra = LUAU_INSN_A(unsafe { *pc }) as u8;
17
18    let repeat_jump_target = get_jump_target(unsafe { *pc }, pcpos as u32);
19    let loop_repeat = build.block_at_inst(repeat_jump_target as u32);
20    let op = LuauOpcode::from(LUAU_INSN_OP(unsafe { *pc }) as u8);
21    let op_length = get_op_length(op);
22    let loop_exit = build.block_at_inst((pcpos + op_length) as u32);
23
24    CODEGEN_ASSERT!(!build.numeric_loop_stack.is_empty());
25    let loop_info = build.numeric_loop_stack.last().copied().unwrap();
26    let step_k = loop_info.step;
27
28    if repeat_jump_target != loop_info.startpc {
29        let pcpos_op = build.const_uint(pcpos as u32);
30        build.inst_ir_cmd_ir_op(IrCmd::INTERRUPT, pcpos_op);
31    }
32
33    let reg_limit = build.vm_reg(ra);
34    let limit = build.inst_ir_cmd_ir_op(IrCmd::LOAD_DOUBLE, reg_limit);
35    let step = if step_k.kind() == IrOpKind::Undef {
36        let reg_step = build.vm_reg(ra + 1);
37        build.inst_ir_cmd_ir_op(IrCmd::LOAD_DOUBLE, reg_step)
38    } else {
39        step_k
40    };
41
42    let reg_idx = build.vm_reg(ra + 2);
43    let mut idx = build.inst_ir_cmd_ir_op(IrCmd::LOAD_DOUBLE, reg_idx);
44    idx = build.inst_ir_cmd_ir_op_ir_op(IrCmd::ADD_NUM, idx, step);
45    let reg_idx = build.vm_reg(ra + 2);
46    build.inst_ir_cmd_ir_op_ir_op(IrCmd::STORE_DOUBLE, reg_idx, idx);
47
48    if step_k.kind() == IrOpKind::Undef {
49        build.inst_ir_cmd_ir_op_ir_op_ir_op_ir_op_ir_op(
50            IrCmd::JUMP_FORN_LOOP_COND,
51            idx,
52            limit,
53            step,
54            loop_repeat,
55            loop_exit,
56        );
57    } else {
58        let step_n = build.function.double_op(step_k);
59
60        let reg_step = build.vm_reg(ra + 1);
61        let one = build.const_int(1);
62        build.inst_ir_cmd_ir_op_ir_op(IrCmd::MARK_USED, reg_step, one);
63
64        let cond = if step_n > 0.0 {
65            IrCondition::LessEqual
66        } else {
67            IrCondition::LessEqual
68        };
69        let cond_op = build.cond(cond);
70
71        if step_n > 0.0 {
72            build.inst_ir_cmd_ir_op_ir_op_ir_op_ir_op_ir_op(
73                IrCmd::JUMP_CMP_NUM,
74                idx,
75                limit,
76                cond_op,
77                loop_repeat,
78                loop_exit,
79            );
80        } else {
81            build.inst_ir_cmd_ir_op_ir_op_ir_op_ir_op_ir_op(
82                IrCmd::JUMP_CMP_NUM,
83                limit,
84                idx,
85                cond_op,
86                loop_repeat,
87                loop_exit,
88            );
89        }
90    }
91
92    if build.is_internal_block(loop_exit) {
93        build.begin_block(loop_exit);
94    }
95}