Skip to main content

luaur_compiler/methods/
compiler_compile_stat_for.rs

1use crate::records::compiler::Compiler;
2use crate::records::reg_scope::RegScope;
3use luaur_ast::records::ast_stat_for::AstStatFor;
4use luaur_common::enums::luau_opcode::LuauOpcode;
5
6impl Compiler {
7    pub fn compile_stat_for(&mut self, stat: *mut AstStatFor) {
8        unsafe {
9            let stat_ref = &*stat;
10            let mut rs = self.reg_scope_compiler();
11
12            if self.options.optimization_level >= 2
13                && self.is_constant(stat_ref.to)
14                && self.is_constant(stat_ref.from)
15                && (stat_ref.step.is_null() || self.is_constant(stat_ref.step))
16            {
17                // C++ passes FInt::LuauCompileLoopUnrollThreshold / ...MaxBoost; the
18                // hardcoded 100/100 ignored both the configured defaults and test
19                // ScopedFastInt overrides, so loops with more iterations than the
20                // threshold (e.g. for i=1,100 under threshold 25) wrongly unrolled.
21                if self.try_compile_unrolled_for(
22                    stat,
23                    luaur_common::FInt::LuauCompileLoopUnrollThreshold.get(),
24                    luaur_common::FInt::LuauCompileLoopUnrollThresholdMaxBoost.get(),
25                ) {
26                    return;
27                }
28            }
29
30            let old_locals = self.local_stack.len();
31            let old_jumps = self.loop_jumps.len();
32
33            self.loops.push(crate::records::r#loop::Loop {
34                local_offset: old_locals,
35                local_offset_continue: old_locals,
36                continue_used: core::ptr::null_mut(),
37            });
38            self.has_loops = true;
39
40            let regs = self.alloc_reg(stat as *mut _, 3);
41            let varregallocpc = (*self.bytecode).get_debug_pc();
42            let mut varreg = regs + 2;
43
44            if let Some(il) = self.variables.find(&stat_ref.var) {
45                if il.written {
46                    varreg = self.alloc_reg(stat as *mut _, 1);
47                }
48            }
49
50            self.compile_expr_temp(stat_ref.from, regs + 2);
51            self.compile_expr_temp(stat_ref.to, regs + 0);
52
53            if !stat_ref.step.is_null() {
54                self.compile_expr_temp(stat_ref.step, regs + 1);
55            } else {
56                (*self.bytecode).emit_abc(LuauOpcode::LOP_LOADN, regs + 1, 1, 0);
57            }
58
59            let for_label = (*self.bytecode).emit_label();
60            (*self.bytecode).emit_ad(LuauOpcode::LOP_FORNPREP, regs, 0);
61            let loop_label = (*self.bytecode).emit_label();
62
63            if varreg != regs + 2 {
64                (*self.bytecode).emit_abc(LuauOpcode::LOP_MOVE, varreg, regs + 2, 0);
65            }
66
67            self.push_local(stat_ref.var, varreg, varregallocpc);
68            self.compile_stat(stat_ref.body as *mut _);
69
70            self.close_locals(old_locals);
71            self.pop_locals(old_locals);
72            self.set_debug_line_ast_node(stat as *mut _);
73
74            let cont_label = (*self.bytecode).emit_label();
75            let back_label = (*self.bytecode).emit_label();
76            (*self.bytecode).emit_ad(LuauOpcode::LOP_FORNLOOP, regs, 0);
77            let end_label = (*self.bytecode).emit_label();
78
79            self.patch_jump(stat as *mut _, for_label, end_label);
80            self.patch_jump(stat as *mut _, back_label, loop_label);
81
82            self.patch_loop_jumps(stat as *mut _, old_jumps, end_label, cont_label);
83            self.loop_jumps.resize(
84                old_jumps,
85                crate::records::loop_jump::LoopJump {
86                    r#type: crate::enums::type_compiler::Type::Break,
87                    label: 0,
88                },
89            );
90
91            self.loops.pop();
92        }
93    }
94}