1use crate::enums::ir_cmd::IrCmd;
2use crate::enums::ir_const_kind::IrConstKind;
3use crate::enums::ir_op_kind::IrOpKind;
4use crate::functions::compare_ir_utils::compare_f64_f64_ir_condition;
5use crate::functions::compare_ir_utils_alt_b::compare_i32_i32_ir_condition;
6use crate::functions::condition_op::condition_op;
7use crate::functions::fold_constants::fold_constants;
8use crate::functions::handle_builtin_effects::handle_builtin_effects;
9use crate::functions::is_gco::is_gco;
10use crate::functions::produces_dirty_high_register_bits::produces_dirty_high_register_bits;
11use crate::functions::replace_ir_utils::replace_ir_function_ir_op_ir_op;
12use crate::functions::replace_ir_utils_alt_b::replace_ir_function_ir_block_u32_ir_inst;
13use crate::functions::safe_integer_constant::safe_integer_constant;
14use crate::functions::substitute::substitute;
15use crate::functions::substitute_with_truncated_uint::substitute_with_truncated_uint;
16use crate::functions::try_get_operand_tag::try_get_operand_tag;
17use crate::functions::try_get_tag_for_typename::try_get_tag_for_typename;
18use crate::functions::vm_const_op::vm_const_op;
19use crate::macros::has_op_c::HAS_OP_C;
20use crate::macros::op_a::op_a;
21use crate::macros::op_b::op_b;
22use crate::macros::op_c::op_c;
23use crate::macros::op_d::op_d;
24use crate::macros::op_e::op_e;
25use crate::macros::op_f::op_f;
26use crate::macros::op_g::op_g;
27use crate::macros::opt_op_b::OPT_OP_B;
28use crate::macros::opt_op_c::OPT_OP_C;
29use crate::macros::opt_op_d::OPT_OP_D;
30use crate::records::array_value_entry::ArrayValueEntry;
31use crate::records::const_prop_state::ConstPropState;
32use crate::records::ir_block::IrBlock;
33use crate::records::ir_builder::IrBuilder;
34use crate::records::ir_function::IrFunction;
35use crate::records::ir_inst::IrInst;
36use crate::records::ir_op::IrOp;
37use crate::records::node_slot_state::NodeSlotState;
38use crate::records::numbered_instruction::NumberedInstruction;
39use crate::type_aliases::ir_ops::IrOps;
40use luaur_common::enums::luau_builtin_function::LuauBuiltinFunction;
41use luaur_vm::enums::lua_type::lua_Type;
42use luaur_vm::macros::getstr::getstr;
43use luaur_vm::macros::tsvalue::tsvalue;
44use luaur_vm::macros::ttisvector::ttisvector;
45use luaur_vm::macros::vvalue::vvalue;
46use luaur_vm::type_aliases::t_value::TValue;
47
48fn const_prop_make_inst(cmd: IrCmd, ops: &[IrOp]) -> IrInst {
49 let mut ir_ops = IrOps::new();
50 for &op in ops {
51 ir_ops.push(op);
52 }
53
54 IrInst {
55 cmd,
56 ops: ir_ops,
57 ..IrInst::default()
58 }
59}
60
61fn tag_for_vm_const_typename(function: &IrFunction, source: IrOp, for_typeof: bool) -> Option<u8> {
62 if source.kind() != IrOpKind::VmConst || function.proto.is_null() {
63 return None;
64 }
65
66 unsafe {
67 let constants = (*function.proto).k;
68 if constants.is_null() {
69 return None;
70 }
71
72 let value = constants.add(vm_const_op(source) as usize);
73 if (*value).tt != lua_Type::LUA_TSTRING as core::ffi::c_int {
74 return None;
75 }
76
77 let string = tsvalue!(value as *const TValue);
78 let name = core::ffi::CStr::from_ptr(getstr(string)).to_str().ok()?;
79 let tag = try_get_tag_for_typename(name, for_typeof);
80
81 if tag == 0xff {
82 None
83 } else {
84 Some(tag)
85 }
86 }
87}
88
89fn type_name_tag_comparison(
90 function: &IrFunction,
91 lhs_op: IrOp,
92 rhs_op: IrOp,
93) -> Option<(IrOp, u8)> {
94 if lhs_op.kind() != IrOpKind::Inst || rhs_op.kind() != IrOpKind::Inst {
95 return None;
96 }
97
98 let mut lhs = function.instructions.get(lhs_op.index() as usize)?.clone();
99 let mut rhs = function.instructions.get(rhs_op.index() as usize)?.clone();
100
101 if lhs.cmd != IrCmd::GET_TYPE && lhs.cmd != IrCmd::GET_TYPEOF {
102 return None;
103 }
104
105 if rhs.cmd != IrCmd::LOAD_POINTER {
106 return None;
107 }
108
109 let source = op_a(&mut lhs);
110 let tag = tag_for_vm_const_typename(function, op_a(&mut rhs), lhs.cmd == IrCmd::GET_TYPEOF)?;
111
112 Some((source, tag))
113}
114
115pub fn const_prop_in_inst(
116 state: &mut ConstPropState,
117 build: &mut IrBuilder,
118 function: &mut IrFunction,
119 block: &mut IrBlock,
120 inst: &mut IrInst,
121 index: u32,
122) {
123 match inst.cmd {
124 IrCmd::LOAD_TAG => {
125 let source = op_a(inst);
126 let tag = state.try_get_tag(source);
127 if tag != 0xff {
128 let tag_op = build.const_tag(tag);
129 substitute(function, inst, tag_op);
130 } else if source.kind() == IrOpKind::VmReg {
131 if state.substitute_tag_load_with_t_value_data(build, inst) {
132 return;
133 }
134
135 if luaur_common::FFlag::LuauCodegenLoadPropagateOrigin.get() {
136 state.try_redirect_vm_reg_load_to_t_value_origin(inst);
137 }
138
139 state.substitute_or_record_vm_reg_load(inst);
140 }
141 }
142 IrCmd::LOAD_POINTER => {
143 let source = op_a(inst);
144 if source.kind() == IrOpKind::VmReg {
145 if state.substitute_or_record_value_load_with_t_value_data(build, inst) {
146 return;
147 }
148
149 if luaur_common::FFlag::LuauCodegenLoadPropagateOrigin.get() {
150 state.try_redirect_vm_reg_load_to_t_value_origin(inst);
151 }
152
153 state.substitute_or_record_vm_reg_load(inst);
154 }
155 }
156 IrCmd::LOAD_DOUBLE => {
157 let source = op_a(inst);
158 let value = state.try_get_value(source);
159
160 if function.as_double_op(value).is_some() {
161 substitute(function, inst, value);
162 } else if source.kind() == IrOpKind::VmReg {
163 if state.substitute_or_record_value_load_with_t_value_data(build, inst) {
164 return;
165 }
166
167 if luaur_common::FFlag::LuauCodegenLoadPropagateOrigin.get() {
168 state.try_redirect_vm_reg_load_to_t_value_origin(inst);
169 }
170
171 state.substitute_or_record_vm_reg_load(inst);
172 } else {
173 state.substitute_or_record(inst, index);
174 }
175 }
176 IrCmd::LOAD_INT => {
177 let source = op_a(inst);
178 let value = state.try_get_value(source);
179
180 if function.as_int_op(value).is_some() {
181 substitute(function, inst, value);
182 } else if source.kind() == IrOpKind::VmReg {
183 if state.substitute_or_record_value_load_with_t_value_data(build, inst) {
184 return;
185 }
186
187 if luaur_common::FFlag::LuauCodegenLoadPropagateOrigin.get() {
188 state.try_redirect_vm_reg_load_to_t_value_origin(inst);
189 }
190
191 state.substitute_or_record_vm_reg_load(inst);
192 } else {
193 state.substitute_or_record(inst, index);
194 }
195 }
196 IrCmd::LOAD_INT64 => {
197 let source = op_a(inst);
198 let value = state.try_get_value(source);
199
200 if function.as_int_64_op(value).is_some() {
201 substitute(function, inst, value);
202 } else if source.kind() == IrOpKind::VmReg {
203 if state.substitute_or_record_value_load_with_t_value_data(build, inst) {
204 return;
205 }
206
207 if luaur_common::FFlag::LuauCodegenLoadPropagateOrigin.get() {
208 state.try_redirect_vm_reg_load_to_t_value_origin(inst);
209 }
210
211 state.substitute_or_record_vm_reg_load(inst);
212 } else {
213 state.substitute_or_record(inst, index);
214 }
215 }
216 IrCmd::LOAD_TVALUE => {
217 let source = op_a(inst);
218 if source.kind() == IrOpKind::VmReg {
219 if !state.substitute_or_record_vm_reg_load(inst) && !HAS_OP_C!(inst) {
220 let tag = state.try_get_tag(source);
221 if tag != 0xff {
222 let offset = build.const_int(0);
223 let tag_op = build.const_tag(tag);
224 let mut ops = crate::type_aliases::ir_ops::IrOps::new();
225 ops.push(source);
226 ops.push(offset);
227 ops.push(tag_op);
228 replace_ir_function_ir_block_u32_ir_inst(
229 function,
230 block,
231 index,
232 IrInst {
233 cmd: IrCmd::LOAD_TVALUE,
234 ops,
235 ..IrInst::default()
236 },
237 );
238 }
239 }
240 } else if source.kind() == IrOpKind::Inst {
241 let source_inst = function.instructions[source.index() as usize].clone();
242
243 if source_inst.cmd == IrCmd::GET_SLOT_NODE_ADDR {
244 if let Some(prev_idx) = state.hash_value_cache.find(&source.index()).copied() {
245 if prev_idx != crate::records::ir_data::k_invalid_inst_idx {
246 let prev = function.instructions[prev_idx as usize].clone();
247
248 if prev.cmd == IrCmd::LOAD_TVALUE {
249 if prev.use_count != 0 {
250 substitute(
251 function,
252 inst,
253 IrOp::ir_op_ir_op_kind_u32(IrOpKind::Inst, prev_idx),
254 );
255 }
256 } else if prev.cmd == IrCmd::STORE_SPLIT_TVALUE {
257 state
258 .inst_tag
259 .try_insert(index, function.tag_op(op_b(prev.clone())));
260 state.inst_value.try_insert(index, op_c(prev));
261 } else if luaur_common::FFlag::LuauCodegenExtraTableOpts.get()
262 && prev.cmd == IrCmd::STORE_TVALUE
263 {
264 let arg = function.as_inst_op(op_b(prev.clone()));
265 if !arg.is_null() && unsafe { (*arg).use_count } != 0 {
266 substitute(function, inst, op_b(prev));
267 }
268 }
269
270 return;
271 }
272 }
273
274 *state.hash_value_cache.get_or_insert(source.index()) = index;
275 } else if source_inst.cmd == IrCmd::GET_ARR_ADDR {
276 let mut source_addr = source_inst;
277 let offset_op = state.get_combined_array_load_offset_op(
278 &mut source_addr,
279 OPT_OP_B(inst.clone()),
280 );
281
282 if let Some(entry) = state
283 .array_value_cache
284 .iter()
285 .find(|entry| entry.pointer == source.index() && entry.offset == offset_op)
286 .copied()
287 {
288 if entry.value != crate::records::ir_data::k_invalid_inst_idx {
289 let prev = function.instructions[entry.value as usize].clone();
290
291 if prev.cmd == IrCmd::LOAD_TVALUE {
292 if prev.use_count != 0 {
293 substitute(
294 function,
295 inst,
296 IrOp::ir_op_ir_op_kind_u32(IrOpKind::Inst, entry.value),
297 );
298 }
299 } else if prev.cmd == IrCmd::STORE_SPLIT_TVALUE {
300 state
301 .inst_tag
302 .try_insert(index, function.tag_op(op_b(prev.clone())));
303 state.inst_value.try_insert(index, op_c(prev));
304 } else if luaur_common::FFlag::LuauCodegenExtraTableOpts.get()
305 && prev.cmd == IrCmd::STORE_TVALUE
306 {
307 let arg = function.as_inst_op(op_b(prev.clone()));
308 if !arg.is_null() && unsafe { (*arg).use_count } != 0 {
309 substitute(function, inst, op_b(prev));
310 }
311 }
312
313 return;
314 }
315 }
316
317 state.array_value_cache.push(ArrayValueEntry {
318 pointer: source.index(),
319 offset: offset_op,
320 value: index,
321 });
322 } else {
323 state.substitute_or_record(inst, index);
324 }
325 }
326 }
327 IrCmd::LOAD_FLOAT => {
328 let source = op_a(inst);
329 if source.kind() == IrOpKind::VmReg {
330 let offset = function.int_op(op_b(inst.clone()));
331
332 if let Some(subst) =
333 state.find_substitute_component_load_from_store_vector(build, source, offset)
334 {
335 substitute(function, inst, subst);
336 return;
337 }
338
339 if let Some(prev_idx_ptr) =
340 state.get_previous_versioned_load_index(IrCmd::LOAD_TVALUE, source)
341 {
342 let mut prev_idx = unsafe { *prev_idx_ptr };
343 let prev = function.instructions[prev_idx as usize].clone();
344
345 if prev.cmd == IrCmd::TAG_VECTOR {
346 let mut prev_for_arg = prev.clone();
347 let prev_arg = op_a(&mut prev_for_arg);
348 if !function.as_inst_op(prev_arg).is_null() {
349 prev_idx = prev_arg.index();
350 }
351 }
352
353 let value = function.instructions[prev_idx as usize].clone();
354 let byte_offset = offset as u32;
355 crate::macros::codegen_assert::CODEGEN_ASSERT!(byte_offset % 4 == 0);
356
357 let component = byte_offset / 4;
358 crate::macros::codegen_assert::CODEGEN_ASSERT!(component <= 3);
359
360 if value.cmd == IrCmd::LOAD_TVALUE {
361 let mut value_for_source = value.clone();
362 let value_source = op_a(&mut value_for_source);
363
364 if value_source.kind() == IrOpKind::VmConst && !function.proto.is_null() {
365 let tv = unsafe {
366 (*function.proto).k.add(vm_const_op(value_source) as usize)
367 };
368
369 if ttisvector!(tv as *const TValue) {
370 let v = unsafe { vvalue!(tv as *const TValue) };
371 let subst = build.const_double(v[component as usize] as f64);
372 substitute(function, inst, subst);
373 return;
374 }
375 } else if value_source.kind() == IrOpKind::VmReg {
376 let prev_op = IrOp::ir_op_ir_op_kind_u32(IrOpKind::Inst, prev_idx);
377 if state.try_get_reg_link(prev_op).is_some() {
378 if let Some(subst) = state
379 .find_substitute_component_load_from_store_vector(
380 build,
381 value_source,
382 offset,
383 )
384 {
385 substitute(function, inst, subst);
386 return;
387 }
388 }
389 }
390 }
391
392 let mut ops = crate::type_aliases::ir_ops::IrOps::new();
393 ops.push(IrOp::ir_op_ir_op_kind_u32(IrOpKind::Inst, prev_idx));
394 ops.push(build.const_int(component as i32));
395
396 replace_ir_function_ir_block_u32_ir_inst(
397 function,
398 block,
399 index,
400 IrInst {
401 cmd: IrCmd::EXTRACT_VEC,
402 ops,
403 ..IrInst::default()
404 },
405 );
406
407 state.substitute_or_record(&mut function.instructions[index as usize], index);
408 return;
409 }
410
411 state.substitute_or_record_vm_reg_load(inst);
412 } else {
413 state.substitute_or_record(inst, index);
414 }
415 }
416 IrCmd::STORE_TAG => {
417 let target = op_a(inst);
418 if target.kind() == IrOpKind::VmReg {
419 let mut active_load_cmd = IrCmd::NOP;
420 let mut active_load_value = !0u32;
421 let value = op_b(inst.clone());
422 if value.kind() == IrOpKind::Constant {
423 let tag = function.tag_op(value);
424 (active_load_cmd, active_load_value) =
425 state.get_previous_versioned_load_for_tag(tag, target);
426
427 if state.try_get_tag(target) == tag {
428 crate::functions::kill_ir_utils::kill_ir_function_ir_inst(function, inst);
429 } else {
430 state.save_tag(target, tag);
431 if tag == lua_Type::LUA_TNIL as u8 {
432 state.invalidate_value(target);
433 }
434 }
435 } else {
436 state.invalidate_tag(target);
437 }
438
439 if active_load_value != !0u32 {
440 let key = state.versioned_vm_reg_load_ir_cmd_ir_op(active_load_cmd, target);
441 *state.value_map.get_or_insert(key) = active_load_value;
442 }
443 }
444 }
445 IrCmd::STORE_POINTER => {
446 let target = op_a(inst);
447 if target.kind() == IrOpKind::VmReg {
448 let value = op_b(inst.clone());
449 if value.kind() == IrOpKind::Inst {
450 if let Some(prev_idx) =
451 state.get_previous_versioned_load_index(IrCmd::LOAD_POINTER, target)
452 {
453 if unsafe { *prev_idx } == value.index() {
454 crate::functions::kill_ir_utils::kill_ir_function_ir_inst(
455 function, inst,
456 );
457 return;
458 }
459 }
460 }
461
462 state.invalidate_value(target);
463 if value.kind() == IrOpKind::Inst {
464 state.forward_vm_reg_store_to_load(inst, IrCmd::LOAD_POINTER);
465
466 let value_ptr = function.as_inst_op(value);
467 if !luaur_common::FFlag::LuauCodegenExtraTableOpts.get()
468 && !value_ptr.is_null()
469 && unsafe { (*value_ptr).cmd } == IrCmd::NEW_TABLE
470 {
471 if let Some(info) = state.try_get_register_info(target) {
472 unsafe {
473 let array_size_op = (&(*value_ptr).ops)[0];
474 (*info).known_not_readonly_deprecated = true;
475 (*info).known_no_metatable_deprecated = true;
476 (*info).known_table_array_size_deprecated =
477 function.uint_op(array_size_op) as i32;
478 }
479 }
480 }
481 }
482 }
483 }
484 IrCmd::STORE_DOUBLE => {
485 let target = op_a(inst);
486 if target.kind() == IrOpKind::VmReg {
487 let value = op_b(inst.clone());
488 if value.kind() == IrOpKind::Constant {
489 if state.try_get_value(target) == value {
490 crate::functions::kill_ir_utils::kill_ir_function_ir_inst(function, inst);
491 } else {
492 state.save_value(target, value);
493 }
494 } else {
495 if value.kind() == IrOpKind::Inst {
496 if let Some(prev_idx) =
497 state.get_previous_versioned_load_index(IrCmd::LOAD_DOUBLE, target)
498 {
499 if unsafe { *prev_idx } == value.index() {
500 crate::functions::kill_ir_utils::kill_ir_function_ir_inst(
501 function, inst,
502 );
503 return;
504 }
505 }
506 }
507
508 state.invalidate_value(target);
509 state.forward_vm_reg_store_to_load(inst, IrCmd::LOAD_DOUBLE);
510 }
511 }
512 }
513 IrCmd::STORE_INT => {
514 let stored = op_b(inst.clone());
515 let stored_inst = function.as_inst_op(stored);
516 if !stored_inst.is_null() && unsafe { (*stored_inst).cmd } == IrCmd::TRUNCATE_UINT {
517 let mut stored_inst_clone = unsafe { (*stored_inst).clone() };
518 let replacement = op_a(&mut stored_inst_clone);
519 replace_ir_function_ir_op_ir_op(function, &mut inst.ops[1], replacement);
520 }
521
522 let target = op_a(inst);
523 if target.kind() == IrOpKind::VmReg {
524 let value = op_b(inst.clone());
525 if value.kind() == IrOpKind::Constant {
526 if state.try_get_value(target) == value {
527 crate::functions::kill_ir_utils::kill_ir_function_ir_inst(function, inst);
528 } else {
529 state.save_value(target, value);
530 }
531 } else {
532 if value.kind() == IrOpKind::Inst {
533 if let Some(prev_idx) =
534 state.get_previous_versioned_load_index(IrCmd::LOAD_INT, target)
535 {
536 if unsafe { *prev_idx } == value.index() {
537 crate::functions::kill_ir_utils::kill_ir_function_ir_inst(
538 function, inst,
539 );
540 return;
541 }
542 }
543 }
544
545 state.invalidate_value(target);
546 state.forward_vm_reg_store_to_load(inst, IrCmd::LOAD_INT);
547 }
548 }
549 }
550 IrCmd::STORE_INT64 => {
551 let target = op_a(inst);
552 if target.kind() == IrOpKind::VmReg {
553 let value = op_b(inst.clone());
554 if value.kind() == IrOpKind::Constant {
555 if state.try_get_value(target) == value {
556 crate::functions::kill_ir_utils::kill_ir_function_ir_inst(function, inst);
557 } else {
558 state.save_value(target, value);
559 }
560 } else {
561 if value.kind() == IrOpKind::Inst {
562 if let Some(prev_idx) =
563 state.get_previous_versioned_load_index(IrCmd::LOAD_INT64, target)
564 {
565 if unsafe { *prev_idx } == value.index() {
566 crate::functions::kill_ir_utils::kill_ir_function_ir_inst(
567 function, inst,
568 );
569 return;
570 }
571 }
572 }
573
574 state.invalidate_value(target);
575 state.forward_vm_reg_store_to_load(inst, IrCmd::LOAD_INT64);
576 }
577 }
578 }
579 IrCmd::STORE_VECTOR => {
580 let target = op_a(inst);
581 if target.kind() == IrOpKind::VmReg {
582 state.invalidate_value(target);
583
584 let reg = crate::functions::vm_reg_op::vm_reg_op(target) as usize;
585 let captured_regs = unsafe { &(*state.function).cfg.captured.regs };
586 if (captured_regs[reg / 64] & (1u64 << (reg % 64))) == 0 {
587 let key = state.versioned_vm_reg_load_ir_cmd_ir_op(IrCmd::LOAD_FLOAT, target);
588 *state.value_map.get_or_insert(key) = index;
589 }
590 }
591 }
592 IrCmd::STORE_TVALUE => {
593 let target = op_a(inst);
594 if target.kind() == IrOpKind::VmReg || target.kind() == IrOpKind::Inst {
595 let stored = op_b(inst.clone());
596
597 if target.kind() == IrOpKind::VmReg {
598 if stored.kind() == IrOpKind::Inst {
599 if let Some(prev_idx) =
600 state.get_previous_versioned_load_index(IrCmd::LOAD_TVALUE, target)
601 {
602 if unsafe { *prev_idx } == stored.index() {
603 crate::functions::kill_ir_utils::kill_ir_function_ir_inst(
604 function, inst,
605 );
606 return;
607 }
608 }
609 }
610
611 state.invalidate_ir_op(target);
612 }
613
614 let mut tag = state.try_get_tag(stored);
615 if tag == 0xff {
616 tag = try_get_operand_tag(function, stored).unwrap_or(0xff);
617 }
618
619 let mut value = state.try_get_value(stored);
620
621 if target.kind() == IrOpKind::VmReg {
622 if tag != 0xff {
623 state.save_tag(target, tag);
624 }
625
626 if value.kind() != IrOpKind::None {
627 state.save_value(target, value);
628 }
629 }
630
631 if target.kind() == IrOpKind::Inst {
632 let target_inst = function.instructions[target.index() as usize].clone();
633 state.invalidate_table_store_location(target_inst, OPT_OP_C(inst.clone()), tag);
634 }
635
636 let mut active_load_cmd = IrCmd::NOP;
637 let mut active_load_value = !0u32;
638
639 if tag != 0xff
640 && value.kind() == IrOpKind::None
641 && state.try_get_reg_link(stored).is_some()
642 {
643 let arg_ptr = function.as_inst_op(stored);
644 if !arg_ptr.is_null() {
645 let mut arg = unsafe { (*arg_ptr).clone() };
646 let arg_source = op_a(&mut arg);
647 if arg.cmd == IrCmd::LOAD_TVALUE && arg_source.kind() == IrOpKind::VmReg {
648 let (cmd, idx) =
649 state.get_previous_versioned_load_for_tag(tag, arg_source);
650 if idx != !0u32 {
651 active_load_cmd = cmd;
652 active_load_value = idx;
653 value = crate::records::ir_op::IrOp::ir_op_ir_op_kind_u32(
654 IrOpKind::Inst,
655 idx,
656 );
657 }
658 }
659 }
660 }
661
662 let can_split_tvalue_store = if tag == lua_Type::LUA_TBOOLEAN as u8 {
663 value.kind() == IrOpKind::Inst || function.as_int_op(value).is_some()
664 } else if tag == lua_Type::LUA_TNUMBER as u8 {
665 value.kind() == IrOpKind::Inst || function.as_double_op(value).is_some()
666 } else if tag == lua_Type::LUA_TINTEGER as u8 {
667 value.kind() == IrOpKind::Inst || function.as_int_64_op(value).is_some()
668 } else {
669 tag != 0xff && is_gco(tag) && value.kind() == IrOpKind::Inst
670 };
671
672 if can_split_tvalue_store {
673 let tag_op = build.const_tag(tag);
674 let mut ops = crate::type_aliases::ir_ops::IrOps::new();
675 ops.push(target);
676 ops.push(tag_op);
677 ops.push(value);
678 if HAS_OP_C!(inst) {
679 ops.push(op_c(inst.clone()));
680 }
681
682 let replacement = IrInst {
683 cmd: IrCmd::STORE_SPLIT_TVALUE,
684 ops,
685 ..IrInst::default()
686 };
687 let replacement_offset = OPT_OP_D(replacement.clone());
688
689 replace_ir_function_ir_block_u32_ir_inst(function, block, index, replacement);
690
691 if target.kind() == IrOpKind::VmReg && active_load_value != !0u32 {
692 let reg = crate::functions::vm_reg_op::vm_reg_op(target) as usize;
693 let versioned_reg = crate::records::ir_op::IrOp::ir_op_ir_op_kind_u32(
694 IrOpKind::VmReg,
695 (reg as u32) | (state.regs[reg].version << 8),
696 );
697 let mut ops = crate::type_aliases::ir_ops::IrOps::new();
698 ops.push(versioned_reg);
699 let key = IrInst {
700 cmd: active_load_cmd,
701 ops,
702 ..IrInst::default()
703 };
704 *state.value_map.get_or_insert(key) = active_load_value;
705 }
706
707 if target.kind() == IrOpKind::Inst {
708 let target_ptr =
709 &mut function.instructions[target.index() as usize] as *mut IrInst;
710 unsafe {
711 state.forward_table_store_to_load(
712 &mut *target_ptr,
713 replacement_offset,
714 index,
715 );
716 }
717 }
718 } else if target.kind() == IrOpKind::VmReg {
719 state.forward_vm_reg_store_to_load(inst, IrCmd::LOAD_TVALUE);
720 } else if luaur_common::FFlag::LuauCodegenExtraTableOpts.get()
721 && target.kind() == IrOpKind::Inst
722 {
723 let offset = OPT_OP_C(inst.clone());
724 let target_ptr =
725 &mut function.instructions[target.index() as usize] as *mut IrInst;
726 unsafe {
727 state.forward_table_store_to_load(&mut *target_ptr, offset, index);
728 }
729 }
730 }
731 }
732 IrCmd::STORE_SPLIT_TVALUE => {
733 let target = op_a(inst);
734 if target.kind() == IrOpKind::VmReg {
735 state.invalidate_ir_op(target);
736
737 let tag = function.tag_op(op_b(inst.clone()));
738 state.save_tag(target, tag);
739
740 let value = op_c(inst.clone());
741 if value.kind() == IrOpKind::Constant {
742 state.save_value(target, value);
743 }
744 } else if target.kind() == IrOpKind::Inst {
745 let tag = function.tag_op(op_b(inst.clone()));
746 let offset = OPT_OP_D(inst.clone());
747 let target_inst = function.instructions[target.index() as usize].clone();
748 state.invalidate_table_store_location(target_inst, offset, tag);
749
750 let target_ptr = &mut function.instructions[target.index() as usize] as *mut IrInst;
751 unsafe {
752 state.forward_table_store_to_load(&mut *target_ptr, offset, index);
753 }
754 }
755 }
756 IrCmd::GET_UPVALUE => {
757 state.substitute_or_record_vm_upvalue_load(inst);
758 }
759 IrCmd::SET_UPVALUE => {
760 state.forward_vm_upvalue_store_to_load(inst);
761
762 let source = op_b(inst.clone());
763 let tag = state.try_get_tag(source);
764 if tag != 0xff {
765 let tag_op = build.const_tag(tag);
766 replace_ir_function_ir_op_ir_op(function, &mut inst.ops[2], tag_op);
767 }
768 }
769 IrCmd::INT64_TO_NUM | IrCmd::INT_TO_NUM => {
770 state.substitute_or_record(inst, index);
771 }
772 IrCmd::ADD_NUM | IrCmd::SUB_NUM => {
773 let rhs = op_b(inst.clone());
774 let rhs = if rhs.kind() == IrOpKind::Constant {
775 rhs
776 } else {
777 state.try_get_value(rhs)
778 };
779
780 if let Some(k) = function.as_double_op(rhs) {
781 if k == 0.0 && k.is_sign_negative() == (inst.cmd == IrCmd::ADD_NUM) {
782 let lhs = op_a(inst);
783 substitute(function, inst, lhs);
784 } else {
785 state.substitute_or_record(inst, index);
786 }
787 } else {
788 state.substitute_or_record(inst, index);
789 }
790 }
791 IrCmd::MUL_NUM => {
792 let rhs = op_b(inst.clone());
793 let rhs = if rhs.kind() == IrOpKind::Constant {
794 rhs
795 } else {
796 state.try_get_value(rhs)
797 };
798
799 if let Some(k) = function.as_double_op(rhs) {
800 if k == 1.0 {
801 let lhs = op_a(inst);
802 substitute(function, inst, lhs);
803 } else if k == 2.0 {
804 let lhs = op_a(inst);
805 let mut ops = crate::type_aliases::ir_ops::IrOps::new();
806 ops.push(lhs);
807 ops.push(lhs);
808 replace_ir_function_ir_block_u32_ir_inst(
809 function,
810 block,
811 index,
812 IrInst {
813 cmd: IrCmd::ADD_NUM,
814 ops,
815 ..IrInst::default()
816 },
817 );
818 } else if k == -1.0 {
819 let lhs = op_a(inst);
820 let mut ops = crate::type_aliases::ir_ops::IrOps::new();
821 ops.push(lhs);
822 replace_ir_function_ir_block_u32_ir_inst(
823 function,
824 block,
825 index,
826 IrInst {
827 cmd: IrCmd::UNM_NUM,
828 ops,
829 ..IrInst::default()
830 },
831 );
832 } else {
833 state.substitute_or_record(inst, index);
834 }
835 } else {
836 state.substitute_or_record(inst, index);
837 }
838 }
839 IrCmd::DIV_NUM => {
840 let rhs = op_b(inst.clone());
841 let rhs = if rhs.kind() == IrOpKind::Constant {
842 rhs
843 } else {
844 state.try_get_value(rhs)
845 };
846
847 if let Some(k) = function.as_double_op(rhs) {
848 if k == 1.0 {
849 let lhs = op_a(inst);
850 substitute(function, inst, lhs);
851 } else if k == -1.0 {
852 let lhs = op_a(inst);
853 let mut ops = crate::type_aliases::ir_ops::IrOps::new();
854 ops.push(lhs);
855 replace_ir_function_ir_block_u32_ir_inst(
856 function,
857 block,
858 index,
859 IrInst {
860 cmd: IrCmd::UNM_NUM,
861 ops,
862 ..IrInst::default()
863 },
864 );
865 } else {
866 let exp = k.log2();
867 if k > 0.0
868 && k.is_finite()
869 && exp.fract() == 0.0
870 && (-1000.0..=1000.0).contains(&exp)
871 {
872 let lhs = op_a(inst);
873 let reciprocal = build.const_double(1.0 / k);
874 let mut ops = crate::type_aliases::ir_ops::IrOps::new();
875 ops.push(lhs);
876 ops.push(reciprocal);
877 replace_ir_function_ir_block_u32_ir_inst(
878 function,
879 block,
880 index,
881 IrInst {
882 cmd: IrCmd::MUL_NUM,
883 ops,
884 ..IrInst::default()
885 },
886 );
887 } else {
888 state.substitute_or_record(inst, index);
889 }
890 }
891 } else {
892 state.substitute_or_record(inst, index);
893 }
894 }
895 IrCmd::ADD_FLOAT | IrCmd::SUB_FLOAT => {
896 let rhs = op_b(inst.clone());
897 let rhs = if rhs.kind() == IrOpKind::Constant {
898 rhs
899 } else {
900 state.try_get_value(rhs)
901 };
902
903 if let Some(k) = function.as_double_op(rhs) {
904 let kf = k as f32;
905 if kf == 0.0 && kf.is_sign_negative() == (inst.cmd == IrCmd::ADD_FLOAT) {
906 let lhs = op_a(inst);
907 substitute(function, inst, lhs);
908 } else {
909 state.substitute_or_record(inst, index);
910 }
911 } else {
912 state.substitute_or_record(inst, index);
913 }
914 }
915 IrCmd::MUL_FLOAT => {
916 let rhs = op_b(inst.clone());
917 let rhs = if rhs.kind() == IrOpKind::Constant {
918 rhs
919 } else {
920 state.try_get_value(rhs)
921 };
922
923 if let Some(k) = function.as_double_op(rhs) {
924 let kf = k as f32;
925 if kf == 1.0 {
926 let lhs = op_a(inst);
927 substitute(function, inst, lhs);
928 } else if kf == 2.0 {
929 let lhs = op_a(inst);
930 let mut ops = IrOps::new();
931 ops.push(lhs);
932 ops.push(lhs);
933 replace_ir_function_ir_block_u32_ir_inst(
934 function,
935 block,
936 index,
937 IrInst {
938 cmd: IrCmd::ADD_FLOAT,
939 ops,
940 ..IrInst::default()
941 },
942 );
943 } else if kf == -1.0 {
944 let lhs = op_a(inst);
945 let mut ops = IrOps::new();
946 ops.push(lhs);
947 replace_ir_function_ir_block_u32_ir_inst(
948 function,
949 block,
950 index,
951 IrInst {
952 cmd: IrCmd::UNM_FLOAT,
953 ops,
954 ..IrInst::default()
955 },
956 );
957 } else {
958 state.substitute_or_record(inst, index);
959 }
960 } else {
961 state.substitute_or_record(inst, index);
962 }
963 }
964 IrCmd::DIV_FLOAT => {
965 let rhs = op_b(inst.clone());
966 let rhs = if rhs.kind() == IrOpKind::Constant {
967 rhs
968 } else {
969 state.try_get_value(rhs)
970 };
971
972 if let Some(k) = function.as_double_op(rhs) {
973 let kf = k as f32;
974 if kf == 1.0 {
975 let lhs = op_a(inst);
976 substitute(function, inst, lhs);
977 } else if kf == -1.0 {
978 let lhs = op_a(inst);
979 let mut ops = IrOps::new();
980 ops.push(lhs);
981 replace_ir_function_ir_block_u32_ir_inst(
982 function,
983 block,
984 index,
985 IrInst {
986 cmd: IrCmd::UNM_FLOAT,
987 ops,
988 ..IrInst::default()
989 },
990 );
991 } else {
992 let exp = kf.log2();
993 if kf > 0.0
994 && kf.is_finite()
995 && exp.fract() == 0.0
996 && (-1000.0..=1000.0).contains(&exp)
997 {
998 let lhs = op_a(inst);
999 let reciprocal = build.const_double((1.0f32 / kf) as f64);
1000 let mut ops = IrOps::new();
1001 ops.push(lhs);
1002 ops.push(reciprocal);
1003 replace_ir_function_ir_block_u32_ir_inst(
1004 function,
1005 block,
1006 index,
1007 IrInst {
1008 cmd: IrCmd::MUL_FLOAT,
1009 ops,
1010 ..IrInst::default()
1011 },
1012 );
1013 } else {
1014 state.substitute_or_record(inst, index);
1015 }
1016 }
1017 } else {
1018 state.substitute_or_record(inst, index);
1019 }
1020 }
1021 IrCmd::MIN_FLOAT
1022 | IrCmd::MAX_FLOAT
1023 | IrCmd::UNM_FLOAT
1024 | IrCmd::FLOOR_FLOAT
1025 | IrCmd::CEIL_FLOAT
1026 | IrCmd::SQRT_FLOAT
1027 | IrCmd::ABS_FLOAT
1028 | IrCmd::SIGN_FLOAT => {
1029 state.substitute_or_record(inst, index);
1030 }
1031 IrCmd::IDIV_NUM
1032 | IrCmd::MULADD_NUM
1033 | IrCmd::MOD_NUM
1034 | IrCmd::MIN_NUM
1035 | IrCmd::MAX_NUM
1036 | IrCmd::UNM_NUM
1037 | IrCmd::FLOOR_NUM
1038 | IrCmd::CEIL_NUM
1039 | IrCmd::ROUND_NUM
1040 | IrCmd::SQRT_NUM
1041 | IrCmd::ABS_NUM
1042 | IrCmd::SIGN_NUM
1043 | IrCmd::SELECT_NUM
1044 | IrCmd::SELECT_INT64
1045 | IrCmd::SELECT_VEC
1046 | IrCmd::MULADD_VEC
1047 | IrCmd::EXTRACT_VEC
1048 | IrCmd::NOT_ANY => {
1049 state.substitute_or_record(inst, index);
1050 }
1051 IrCmd::SELECT_IF_TRUTHY => {
1052 let tag = state.try_get_tag(op_a(inst));
1053
1054 if tag == lua_Type::LUA_TNIL as u8 {
1055 let replacement = op_c(inst.clone());
1056 substitute(function, inst, replacement);
1057 } else if tag != 0xff && tag != lua_Type::LUA_TBOOLEAN as u8 {
1058 let replacement = op_b(inst.clone());
1059 substitute(function, inst, replacement);
1060 }
1061 }
1062 IrCmd::UINT_TO_NUM | IrCmd::UINT_TO_FLOAT => {
1063 let src = function.as_inst_op(op_a(inst));
1064 if !src.is_null() {
1065 let mut src_clone = unsafe { (*src).clone() };
1066 if src_clone.cmd == IrCmd::TRUNCATE_UINT {
1067 let src_source = op_a(&mut src_clone);
1068 let src_of_src = function.as_inst_op(src_source);
1069 if !src_of_src.is_null() && unsafe { (*src_of_src).cmd } == IrCmd::NUM_TO_UINT {
1070 replace_ir_function_ir_op_ir_op(function, &mut inst.ops[0], src_source);
1071 }
1072 }
1073 }
1074
1075 state.substitute_or_record(inst, index);
1076 }
1077 IrCmd::NUM_TO_INT => {
1078 let src = function.as_inst_op(op_a(inst));
1079 if !src.is_null() {
1080 let src_clone = unsafe { (*src).clone() };
1081
1082 if src_clone.cmd == IrCmd::INT_TO_NUM {
1083 let mut src_clone = src_clone;
1084 substitute(function, inst, op_a(&mut src_clone));
1085 return;
1086 }
1087
1088 if src_clone.cmd == IrCmd::ADD_NUM {
1089 if let Some(arg) = function.as_double_op(op_b(src_clone.clone())) {
1090 if arg == 0.0 {
1091 let mut src_clone = src_clone.clone();
1092 replace_ir_function_ir_op_ir_op(
1093 function,
1094 &mut inst.ops[0],
1095 op_a(&mut src_clone),
1096 );
1097 state.substitute_or_record(inst, index);
1098 return;
1099 }
1100 }
1101
1102 let mut src_clone_for_a = src_clone.clone();
1103 let src_op_a = op_a(&mut src_clone_for_a);
1104 if let Some(arg) = function.as_double_op(src_op_a) {
1105 if arg == 0.0 {
1106 replace_ir_function_ir_op_ir_op(
1107 function,
1108 &mut inst.ops[0],
1109 op_b(src_clone),
1110 );
1111 state.substitute_or_record(inst, index);
1112 return;
1113 }
1114 }
1115 }
1116
1117 if src_clone.cmd == IrCmd::UINT_TO_NUM {
1118 let mut src_clone = src_clone;
1119 let src_source = op_a(&mut src_clone);
1120 if src_source.kind() != IrOpKind::Constant {
1121 substitute_with_truncated_uint(function, block, inst, src_source);
1122 return;
1123 }
1124 }
1125 }
1126
1127 state.substitute_or_record(inst, index);
1128 }
1129 IrCmd::NUM_TO_UINT => {
1130 let src = function.as_inst_op(op_a(inst));
1131 if !src.is_null() {
1132 let src_clone = unsafe { (*src).clone() };
1133
1134 if src_clone.cmd == IrCmd::UINT_TO_NUM {
1135 let mut src_clone = src_clone;
1136 substitute_with_truncated_uint(function, block, inst, op_a(&mut src_clone));
1137 return;
1138 }
1139
1140 if src_clone.cmd == IrCmd::INT_TO_NUM {
1141 let mut src_clone = src_clone.clone();
1142 let src_source = op_a(&mut src_clone);
1143 if src_source.kind() != IrOpKind::Constant {
1144 substitute(function, inst, src_source);
1145 return;
1146 }
1147 }
1148
1149 if src_clone.cmd == IrCmd::ADD_NUM || src_clone.cmd == IrCmd::SUB_NUM {
1150 let mut src_clone_a = src_clone.clone();
1151 let src_a = op_a(&mut src_clone_a);
1152 let src_b = op_b(src_clone.clone());
1153 let add_src_1 = function.as_inst_op(src_a);
1154 let add_num_1 = function.as_double_op(src_a);
1155 let add_src_2 = function.as_inst_op(src_b);
1156 let add_num_2 = function.as_double_op(src_b);
1157
1158 let replacement_cmd = if src_clone.cmd == IrCmd::ADD_NUM {
1159 IrCmd::ADD_INT
1160 } else {
1161 IrCmd::SUB_INT
1162 };
1163
1164 if !add_src_1.is_null()
1165 && unsafe { (*add_src_1).cmd } == IrCmd::UINT_TO_NUM
1166 && !add_src_2.is_null()
1167 && unsafe { (*add_src_2).cmd } == IrCmd::UINT_TO_NUM
1168 {
1169 let mut add_src_1_clone = unsafe { (*add_src_1).clone() };
1170 let mut add_src_2_clone = unsafe { (*add_src_2).clone() };
1171 let mut ops = crate::type_aliases::ir_ops::IrOps::new();
1172 ops.push(op_a(&mut add_src_1_clone));
1173 ops.push(op_a(&mut add_src_2_clone));
1174 replace_ir_function_ir_block_u32_ir_inst(
1175 function,
1176 block,
1177 index,
1178 IrInst {
1179 cmd: replacement_cmd,
1180 ops,
1181 ..IrInst::default()
1182 },
1183 );
1184 return;
1185 } else if let Some(add_num_1) = add_num_1 {
1186 if safe_integer_constant(add_num_1)
1187 && !add_src_2.is_null()
1188 && unsafe { (*add_src_2).cmd } == IrCmd::UINT_TO_NUM
1189 {
1190 let mut add_src_2_clone = unsafe { (*add_src_2).clone() };
1191 let mut ops = crate::type_aliases::ir_ops::IrOps::new();
1192 ops.push(build.const_int((add_num_1 as i64 as u32) as i32));
1193 ops.push(op_a(&mut add_src_2_clone));
1194 replace_ir_function_ir_block_u32_ir_inst(
1195 function,
1196 block,
1197 index,
1198 IrInst {
1199 cmd: replacement_cmd,
1200 ops,
1201 ..IrInst::default()
1202 },
1203 );
1204 return;
1205 }
1206 } else if !add_src_1.is_null()
1207 && unsafe { (*add_src_1).cmd } == IrCmd::UINT_TO_NUM
1208 {
1209 if let Some(add_num_2) = add_num_2 {
1210 if safe_integer_constant(add_num_2) {
1211 let mut add_src_1_clone = unsafe { (*add_src_1).clone() };
1212 let mut ops = crate::type_aliases::ir_ops::IrOps::new();
1213 ops.push(op_a(&mut add_src_1_clone));
1214 ops.push(build.const_int((add_num_2 as i64 as u32) as i32));
1215 replace_ir_function_ir_block_u32_ir_inst(
1216 function,
1217 block,
1218 index,
1219 IrInst {
1220 cmd: replacement_cmd,
1221 ops,
1222 ..IrInst::default()
1223 },
1224 );
1225 return;
1226 }
1227 }
1228 }
1229 }
1230 }
1231
1232 state.substitute_or_record(inst, index);
1233 }
1234 IrCmd::TRUNCATE_UINT => {
1235 let src = function.as_inst_op(op_a(inst));
1236 if !src.is_null() && !produces_dirty_high_register_bits(unsafe { (*src).cmd }) {
1237 let source = op_a(inst);
1238 substitute(function, inst, source);
1239 } else {
1240 state.substitute_or_record(inst, index);
1241 }
1242 }
1243 IrCmd::FLOAT_TO_NUM => {
1244 state.substitute_or_record(inst, index);
1245 }
1246 IrCmd::NUM_TO_FLOAT => {
1247 let src = function.as_inst_op(op_a(inst));
1248 if !src.is_null() {
1249 let src_clone = unsafe { (*src).clone() };
1250 if src_clone.cmd == IrCmd::FLOAT_TO_NUM {
1251 let mut src_clone = src_clone;
1252 substitute(function, inst, op_a(&mut src_clone));
1253 } else if src_clone.cmd == IrCmd::UINT_TO_NUM {
1254 let mut src_clone = src_clone;
1255 let mut ops = IrOps::new();
1256 ops.push(op_a(&mut src_clone));
1257 replace_ir_function_ir_block_u32_ir_inst(
1258 function,
1259 block,
1260 index,
1261 IrInst {
1262 cmd: IrCmd::UINT_TO_FLOAT,
1263 ops,
1264 ..IrInst::default()
1265 },
1266 );
1267 } else {
1268 state.substitute_or_record(inst, index);
1269 }
1270 } else {
1271 state.substitute_or_record(inst, index);
1272 }
1273 }
1274 IrCmd::JUMP_IF_TRUTHY => {
1275 let tag = state.try_get_tag(op_a(inst));
1276 if tag != 0xff {
1277 if tag == lua_Type::LUA_TNIL as u8 {
1278 let mut ops = crate::type_aliases::ir_ops::IrOps::new();
1279 ops.push(op_c(inst.clone()));
1280 replace_ir_function_ir_block_u32_ir_inst(
1281 function,
1282 block,
1283 index,
1284 IrInst {
1285 cmd: IrCmd::JUMP,
1286 ops,
1287 ..IrInst::default()
1288 },
1289 );
1290 } else if tag != lua_Type::LUA_TBOOLEAN as u8 {
1291 let mut ops = crate::type_aliases::ir_ops::IrOps::new();
1292 ops.push(op_b(inst.clone()));
1293 replace_ir_function_ir_block_u32_ir_inst(
1294 function,
1295 block,
1296 index,
1297 IrInst {
1298 cmd: IrCmd::JUMP,
1299 ops,
1300 ..IrInst::default()
1301 },
1302 );
1303 }
1304 }
1305 }
1306 IrCmd::JUMP_IF_FALSY => {
1307 let tag = state.try_get_tag(op_a(inst));
1308 if tag != 0xff {
1309 if tag == lua_Type::LUA_TNIL as u8 {
1310 let mut ops = crate::type_aliases::ir_ops::IrOps::new();
1311 ops.push(op_b(inst.clone()));
1312 replace_ir_function_ir_block_u32_ir_inst(
1313 function,
1314 block,
1315 index,
1316 IrInst {
1317 cmd: IrCmd::JUMP,
1318 ops,
1319 ..IrInst::default()
1320 },
1321 );
1322 } else if tag != lua_Type::LUA_TBOOLEAN as u8 {
1323 let mut ops = crate::type_aliases::ir_ops::IrOps::new();
1324 ops.push(op_c(inst.clone()));
1325 replace_ir_function_ir_block_u32_ir_inst(
1326 function,
1327 block,
1328 index,
1329 IrInst {
1330 cmd: IrCmd::JUMP,
1331 ops,
1332 ..IrInst::default()
1333 },
1334 );
1335 }
1336 }
1337 }
1338 IrCmd::CMP_ANY => {
1339 state.invalidate_user_call();
1340 }
1341 IrCmd::CMP_SPLIT_TVALUE => {
1342 let tag_a_op = op_a(inst);
1343 let tag_b_op = op_b(inst.clone());
1344 let tag_a = if tag_a_op.kind() == IrOpKind::Constant {
1345 function.tag_op(tag_a_op)
1346 } else {
1347 state.try_get_tag(tag_a_op)
1348 };
1349 let tag_b = if tag_b_op.kind() == IrOpKind::Constant {
1350 function.tag_op(tag_b_op)
1351 } else {
1352 state.try_get_tag(tag_b_op)
1353 };
1354
1355 if tag_a == lua_Type::LUA_TSTRING as u8 && tag_b == lua_Type::LUA_TSTRING as u8 {
1356 if let Some((source, tag)) =
1357 type_name_tag_comparison(function, op_c(inst.clone()), op_d(inst.clone()))
1358 {
1359 let tag_op = build.const_tag(tag);
1360 let replacement =
1361 const_prop_make_inst(IrCmd::CMP_TAG, &[source, tag_op, op_e(inst.clone())]);
1362
1363 replace_ir_function_ir_block_u32_ir_inst(function, block, index, replacement);
1364 fold_constants(build, function, block, index);
1365 return;
1366 }
1367 }
1368 }
1369 IrCmd::JUMP_EQ_POINTER => {
1370 if let Some((source, tag)) =
1371 type_name_tag_comparison(function, op_a(inst), op_b(inst.clone()))
1372 {
1373 let tag_op = build.const_tag(tag);
1374 let replacement = const_prop_make_inst(
1375 IrCmd::JUMP_EQ_TAG,
1376 &[source, tag_op, op_c(inst.clone()), op_d(inst.clone())],
1377 );
1378
1379 replace_ir_function_ir_block_u32_ir_inst(function, block, index, replacement);
1380 fold_constants(build, function, block, index);
1381 return;
1382 }
1383 }
1384 IrCmd::JUMP_EQ_TAG => {
1385 let a = op_a(inst);
1386 let b = op_b(inst.clone());
1387 let tag_a = if a.kind() == IrOpKind::Constant {
1388 function.tag_op(a)
1389 } else {
1390 state.try_get_tag(a)
1391 };
1392 let tag_b = if b.kind() == IrOpKind::Constant {
1393 function.tag_op(b)
1394 } else {
1395 state.try_get_tag(b)
1396 };
1397
1398 if tag_a != 0xff && tag_b != 0xff {
1399 let target = if tag_a == tag_b {
1400 op_c(inst.clone())
1401 } else {
1402 op_d(inst.clone())
1403 };
1404 let mut ops = crate::type_aliases::ir_ops::IrOps::new();
1405 ops.push(target);
1406 replace_ir_function_ir_block_u32_ir_inst(
1407 function,
1408 block,
1409 index,
1410 IrInst {
1411 cmd: IrCmd::JUMP,
1412 ops,
1413 ..IrInst::default()
1414 },
1415 );
1416 } else if a == b {
1417 let mut ops = crate::type_aliases::ir_ops::IrOps::new();
1418 ops.push(op_c(inst.clone()));
1419 replace_ir_function_ir_block_u32_ir_inst(
1420 function,
1421 block,
1422 index,
1423 IrInst {
1424 cmd: IrCmd::JUMP,
1425 ops,
1426 ..IrInst::default()
1427 },
1428 );
1429 }
1430 }
1431 IrCmd::JUMP_CMP_INT => {
1432 let a = op_a(inst);
1433 let b = op_b(inst.clone());
1434 let value_a = function.as_int_op(if a.kind() == IrOpKind::Constant {
1435 a
1436 } else {
1437 state.try_get_value(a)
1438 });
1439 let value_b = function.as_int_op(if b.kind() == IrOpKind::Constant {
1440 b
1441 } else {
1442 state.try_get_value(b)
1443 });
1444
1445 if let (Some(value_a), Some(value_b)) = (value_a, value_b) {
1446 let target = if compare_i32_i32_ir_condition(
1447 value_a,
1448 value_b,
1449 condition_op(op_c(inst.clone())),
1450 ) {
1451 op_d(inst.clone())
1452 } else {
1453 op_e(inst.clone())
1454 };
1455 let mut ops = crate::type_aliases::ir_ops::IrOps::new();
1456 ops.push(target);
1457 replace_ir_function_ir_block_u32_ir_inst(
1458 function,
1459 block,
1460 index,
1461 IrInst {
1462 cmd: IrCmd::JUMP,
1463 ops,
1464 ..IrInst::default()
1465 },
1466 );
1467 }
1468 }
1469 IrCmd::JUMP_CMP_NUM => {
1470 let a = op_a(inst);
1471 let b = op_b(inst.clone());
1472 let value_a = function.as_double_op(if a.kind() == IrOpKind::Constant {
1473 a
1474 } else {
1475 state.try_get_value(a)
1476 });
1477 let value_b = function.as_double_op(if b.kind() == IrOpKind::Constant {
1478 b
1479 } else {
1480 state.try_get_value(b)
1481 });
1482
1483 if let (Some(value_a), Some(value_b)) = (value_a, value_b) {
1484 let target = if compare_f64_f64_ir_condition(
1485 value_a,
1486 value_b,
1487 condition_op(op_c(inst.clone())),
1488 ) {
1489 op_d(inst.clone())
1490 } else {
1491 op_e(inst.clone())
1492 };
1493 let mut ops = crate::type_aliases::ir_ops::IrOps::new();
1494 ops.push(target);
1495 replace_ir_function_ir_block_u32_ir_inst(
1496 function,
1497 block,
1498 index,
1499 IrInst {
1500 cmd: IrCmd::JUMP,
1501 ops,
1502 ..IrInst::default()
1503 },
1504 );
1505 }
1506 }
1507 IrCmd::CHECK_TAG => {
1508 let target = op_a(inst);
1509 let expected = function.tag_op(op_b(inst.clone()));
1510 let mut tag = state.try_get_tag(target);
1511
1512 if tag == 0xff {
1513 let value = state.try_get_value(target);
1514 if value.kind() == IrOpKind::Constant {
1515 let constant = function.const_op(value);
1516 if constant.kind == IrConstKind::Double {
1517 tag = lua_Type::LUA_TNUMBER as u8;
1518 } else if constant.kind == IrConstKind::Int64 {
1519 tag = lua_Type::LUA_TINTEGER as u8;
1520 }
1521 }
1522 }
1523
1524 if tag != 0xff {
1525 if tag == expected {
1526 crate::functions::kill_ir_utils::kill_ir_function_ir_inst(function, inst);
1527 } else {
1528 let mut ops = crate::type_aliases::ir_ops::IrOps::new();
1529 ops.push(op_c(inst.clone()));
1530 replace_ir_function_ir_block_u32_ir_inst(
1531 function,
1532 block,
1533 index,
1534 IrInst {
1535 cmd: IrCmd::JUMP,
1536 ops,
1537 ..IrInst::default()
1538 },
1539 );
1540 }
1541 } else {
1542 let lhs = function.as_inst_op(target);
1543 if !lhs.is_null() {
1544 let mut lhs_inst = unsafe { (*lhs).clone() };
1545 let lhs_source = op_a(&mut lhs_inst);
1546 if lhs_inst.cmd == IrCmd::LOAD_TAG && lhs_source.kind() == IrOpKind::VmReg {
1547 if let Some(prev_idx) =
1548 state.get_previous_versioned_load_index(IrCmd::LOAD_TVALUE, lhs_source)
1549 {
1550 state.inst_tag.try_insert(unsafe { *prev_idx }, expected);
1551 }
1552 }
1553 }
1554
1555 state.update_tag(target, expected);
1556 }
1557 }
1558 IrCmd::NUM_TO_INT64 => {
1559 let src = function.as_inst_op(op_a(inst));
1561 if !src.is_null() {
1562 let src_clone = unsafe { (*src).clone() };
1563 let src_cmd = src_clone.cmd;
1564 if src_cmd == IrCmd::INT64_TO_NUM {
1565 let src_op_a = op_a(&mut { src_clone });
1566 substitute(function, inst, src_op_a);
1567 return;
1568 }
1569 if src_cmd == IrCmd::ADD_NUM {
1570 let src_op_b = op_b(src_clone.clone());
1571 if let Some(arg) = function.as_double_op(src_op_b) {
1572 if arg == 0.0 {
1573 let src_op_a = op_a(&mut { src_clone.clone() });
1574 inst.ops[0] = src_op_a;
1575 state.substitute_or_record(inst, index);
1576 return;
1577 }
1578 }
1579 let src_op_a = op_a(&mut { src_clone.clone() });
1580 if let Some(arg) = function.as_double_op(src_op_a) {
1581 if arg == 0.0 {
1582 let src_op_b = op_b(src_clone);
1583 inst.ops[0] = src_op_b;
1584 state.substitute_or_record(inst, index);
1585 return;
1586 }
1587 }
1588 }
1589 }
1590 state.substitute_or_record(inst, index);
1591 }
1592 IrCmd::LOAD_ENV => {
1593 if luaur_common::FFlag::LuauCodegenExtraTableOpts.get() {
1594 if state.load_env_idx != crate::records::ir_data::k_invalid_inst_idx {
1595 substitute(
1596 function,
1597 inst,
1598 IrOp::ir_op_ir_op_kind_u32(IrOpKind::Inst, state.load_env_idx),
1599 );
1600 } else {
1601 state.load_env_idx = index;
1602 }
1603 }
1604 }
1605 IrCmd::GET_SLOT_NODE_ADDR => {
1606 for i in 0..state.get_slot_node_cache.len() {
1607 let prev_idx = state.get_slot_node_cache[i].inst_idx;
1608 let mut prev = function.instructions[prev_idx as usize].clone();
1609
1610 if op_a(&mut prev) == op_a(inst) && op_c(prev.clone()) == op_c(inst.clone()) {
1611 let limit = luaur_common::FInt::LuauCodeGenLiveSlotReuseLimit.get();
1612
1613 if state.get_slot_node_cache.len() as i32 > limit {
1614 let mut cache = state.get_slot_node_cache.clone();
1615 if state.get_max_internal_overlap(&mut cache, i) > limit {
1616 return;
1617 }
1618 }
1619
1620 state.get_slot_node_cache[i].finish_pos = state.inst_pos;
1621
1622 substitute(
1623 function,
1624 inst,
1625 IrOp::ir_op_ir_op_kind_u32(IrOpKind::Inst, prev_idx),
1626 );
1627 return;
1628 }
1629 }
1630
1631 if (state.get_slot_node_cache.len() as i32)
1632 < luaur_common::FInt::LuauCodeGenReuseSlotLimit.get()
1633 {
1634 state.get_slot_node_cache.push(NumberedInstruction {
1635 inst_idx: index,
1636 start_pos: state.inst_pos,
1637 finish_pos: state.inst_pos,
1638 });
1639 }
1640 }
1641 IrCmd::GET_ARR_ADDR => {
1642 for prev_idx in state.get_arr_addr_cache.iter().copied() {
1643 let mut prev = function.instructions[prev_idx as usize].clone();
1644
1645 if op_a(&mut prev) == op_a(inst) && op_b(prev.clone()) == op_b(inst.clone()) {
1646 substitute(
1647 function,
1648 inst,
1649 IrOp::ir_op_ir_op_kind_u32(IrOpKind::Inst, prev_idx),
1650 );
1651 return;
1652 }
1653 }
1654
1655 if (state.get_arr_addr_cache.len() as i32)
1656 < luaur_common::FInt::LuauCodeGenReuseSlotLimit.get()
1657 {
1658 state.get_arr_addr_cache.push(index);
1659 }
1660 }
1661 IrCmd::ADD_INT
1662 | IrCmd::SUB_INT
1663 | IrCmd::ADD_INT64
1664 | IrCmd::SUB_INT64
1665 | IrCmd::MUL_INT64
1666 | IrCmd::DIV_INT64
1667 | IrCmd::IDIV_INT64
1668 | IrCmd::CHECK_DIV_INT64
1669 | IrCmd::UDIV_INT64
1670 | IrCmd::REM_INT64
1671 | IrCmd::UREM_INT64
1672 | IrCmd::MOD_INT64
1673 | IrCmd::SEXTI8_INT
1674 | IrCmd::SEXTI16_INT => {
1675 state.substitute_or_record(inst, index);
1676 }
1677 IrCmd::TRY_NUM_TO_INDEX => {
1678 for prev_idx in state.try_num_to_index_cache.iter().copied() {
1679 let mut prev = function.instructions[prev_idx as usize].clone();
1680
1681 if op_a(&mut prev) == op_a(inst) {
1682 substitute(
1683 function,
1684 inst,
1685 IrOp::ir_op_ir_op_kind_u32(IrOpKind::Inst, prev_idx),
1686 );
1687 return;
1688 }
1689 }
1690
1691 if (state.try_num_to_index_cache.len() as i32)
1692 < luaur_common::FInt::LuauCodeGenReuseSlotLimit.get()
1693 {
1694 state.try_num_to_index_cache.push(index);
1695 }
1696 }
1697 IrCmd::CHECK_SLOT_MATCH => {
1698 for el in &mut state.check_slot_match_cache {
1699 let mut prev = function.instructions[el.pointer as usize].clone();
1700
1701 if op_a(&mut prev) == op_a(inst) && op_b(prev.clone()) == op_b(inst.clone()) {
1702 if let Some(info) = state.inst_tag.find(&op_a(inst).index()) {
1703 if *info != lua_Type::LUA_TNIL as u8 {
1704 el.knownToNotBeNil = true;
1705 }
1706 }
1707
1708 if el.knownToNotBeNil {
1709 crate::functions::kill_ir_utils::kill_ir_function_ir_inst(function, inst);
1710 } else {
1711 let mut ops = crate::type_aliases::ir_ops::IrOps::new();
1712 ops.push(op_a(inst));
1713 ops.push(op_c(inst.clone()));
1714 replace_ir_function_ir_block_u32_ir_inst(
1715 function,
1716 block,
1717 index,
1718 IrInst {
1719 cmd: IrCmd::CHECK_NODE_VALUE,
1720 ops,
1721 ..IrInst::default()
1722 },
1723 );
1724 }
1725
1726 el.knownToNotBeNil = true;
1727 return;
1728 }
1729 }
1730
1731 if (state.check_slot_match_cache.len() as i32)
1732 < luaur_common::FInt::LuauCodeGenReuseSlotLimit.get()
1733 {
1734 state.check_slot_match_cache.push(NodeSlotState {
1735 pointer: index,
1736 knownToNotBeNil: true,
1737 });
1738 }
1739 }
1740 IrCmd::CHECK_SAFE_ENV => {
1741 if state.in_safe_env {
1742 crate::functions::kill_ir_utils::kill_ir_function_ir_inst(function, inst);
1743 } else {
1744 state.in_safe_env = true;
1745 }
1746 }
1747 IrCmd::CHECK_READONLY => {
1748 let target = op_a(inst);
1749 if luaur_common::FFlag::LuauCodegenExtraTableOpts.get()
1750 && target.kind() == IrOpKind::Inst
1751 {
1752 let target_idx = target.index();
1753 if state.inst_not_readonly.find(&target_idx).is_some() {
1754 crate::functions::kill_ir_utils::kill_ir_function_ir_inst(function, inst);
1755 return;
1756 }
1757 state.inst_not_readonly.insert(target_idx);
1758 } else if !luaur_common::FFlag::LuauCodegenExtraTableOpts.get() {
1759 if let Some(info) = state.try_get_register_info(target) {
1760 unsafe {
1761 if (*info).known_not_readonly_deprecated {
1762 crate::functions::kill_ir_utils::kill_ir_function_ir_inst(
1763 function, inst,
1764 );
1765 } else {
1766 (*info).known_not_readonly_deprecated = true;
1767 }
1768 }
1769 }
1770 }
1771 }
1772 IrCmd::CHECK_NO_METATABLE => {
1773 let target = op_a(inst);
1774 if luaur_common::FFlag::LuauCodegenExtraTableOpts.get()
1775 && target.kind() == IrOpKind::Inst
1776 {
1777 let target_idx = target.index();
1778 if state.inst_no_metatable.find(&target_idx).is_some() {
1779 crate::functions::kill_ir_utils::kill_ir_function_ir_inst(function, inst);
1780 return;
1781 }
1782 state.inst_no_metatable.insert(target_idx);
1783 } else if !luaur_common::FFlag::LuauCodegenExtraTableOpts.get() {
1784 if let Some(info) = state.try_get_register_info(target) {
1785 unsafe {
1786 if (*info).known_no_metatable_deprecated {
1787 crate::functions::kill_ir_utils::kill_ir_function_ir_inst(
1788 function, inst,
1789 );
1790 } else {
1791 (*info).known_no_metatable_deprecated = true;
1792 }
1793 }
1794 }
1795 }
1796 }
1797 IrCmd::BUFFER_READI8 => {
1798 state.substitute_or_record_buffer_load(block, index, inst, 1);
1799 }
1800 IrCmd::BUFFER_READU8 => {
1801 state.substitute_or_record_buffer_load(block, index, inst, 1);
1802 }
1803 IrCmd::BUFFER_WRITEI8 => {
1804 let src = function.as_inst_op(op_c(inst.clone()));
1805 if !src.is_null() {
1806 let src_inst = unsafe { (*src).clone() };
1807 let int_src_b = function.as_int_op(OPT_OP_B(src_inst.clone()));
1808
1809 if src_inst.cmd == IrCmd::SEXTI8_INT {
1810 let replacement = op_a(&mut src_inst.clone());
1811 replace_ir_function_ir_op_ir_op(function, &mut inst.ops[2], replacement);
1812 } else if src_inst.cmd == IrCmd::BITAND_UINT && int_src_b == Some(0xff) {
1813 let replacement = op_a(&mut src_inst.clone());
1814 replace_ir_function_ir_op_ir_op(function, &mut inst.ops[2], replacement);
1815 }
1816 }
1817
1818 state.forward_buffer_store_to_load(inst, IrCmd::BUFFER_READI8, 1);
1819 }
1820 IrCmd::BUFFER_READI16 => {
1821 state.substitute_or_record_buffer_load(block, index, inst, 2);
1822 }
1823 IrCmd::BUFFER_READU16 => {
1824 state.substitute_or_record_buffer_load(block, index, inst, 2);
1825 }
1826 IrCmd::BUFFER_WRITEI16 => {
1827 let src = function.as_inst_op(op_c(inst.clone()));
1828 if !src.is_null() {
1829 let src_inst = unsafe { (*src).clone() };
1830 let int_src_b = function.as_int_op(OPT_OP_B(src_inst.clone()));
1831
1832 if src_inst.cmd == IrCmd::SEXTI16_INT {
1833 let replacement = op_a(&mut src_inst.clone());
1834 replace_ir_function_ir_op_ir_op(function, &mut inst.ops[2], replacement);
1835 } else if src_inst.cmd == IrCmd::BITAND_UINT && int_src_b == Some(0xffff) {
1836 let replacement = op_a(&mut src_inst.clone());
1837 replace_ir_function_ir_op_ir_op(function, &mut inst.ops[2], replacement);
1838 }
1839 }
1840
1841 state.forward_buffer_store_to_load(inst, IrCmd::BUFFER_READI16, 2);
1842 }
1843 IrCmd::BUFFER_READI32 => {
1844 state.substitute_or_record_buffer_load(block, index, inst, 4);
1845 }
1846 IrCmd::BUFFER_WRITEI32 => {
1847 let src = function.as_inst_op(op_c(inst.clone()));
1848 if !src.is_null() {
1849 let src_inst = unsafe { (*src).clone() };
1850
1851 if src_inst.cmd == IrCmd::TRUNCATE_UINT {
1852 let replacement = op_a(&mut src_inst.clone());
1853 replace_ir_function_ir_op_ir_op(function, &mut inst.ops[2], replacement);
1854 }
1855 }
1856
1857 state.forward_buffer_store_to_load(inst, IrCmd::BUFFER_READI32, 4);
1858 }
1859 IrCmd::BUFFER_READF32 => {
1860 state.substitute_or_record_buffer_load(block, index, inst, 4);
1861 }
1862 IrCmd::BUFFER_WRITEF32 => {
1863 state.forward_buffer_store_to_load(inst, IrCmd::BUFFER_READF32, 4);
1864 }
1865 IrCmd::BUFFER_READF64 => {
1866 state.substitute_or_record_buffer_load(block, index, inst, 8);
1867 }
1868 IrCmd::BUFFER_WRITEF64 => {
1869 state.forward_buffer_store_to_load(inst, IrCmd::BUFFER_READF64, 8);
1870 }
1871 IrCmd::BUFFER_READI64 => {
1872 state.substitute_or_record_buffer_load(block, index, inst, 8);
1873 }
1874 IrCmd::BUFFER_WRITEI64 => {
1875 state.forward_buffer_store_to_load(inst, IrCmd::BUFFER_READI64, 8);
1876 }
1877 IrCmd::CHECK_GC => {
1878 if state.checked_gc {
1879 crate::functions::kill_ir_utils::kill_ir_function_ir_inst(function, inst);
1880 } else {
1881 state.checked_gc = true;
1882 state.invalidate_heap_table_data();
1883 }
1884 }
1885 IrCmd::BARRIER_OBJ | IrCmd::BARRIER_TABLE_FORWARD => {
1886 let value = op_b(inst.clone());
1887 if value.kind() == IrOpKind::VmReg {
1888 let tag = state.try_get_tag(value);
1889 if tag != 0xff && !is_gco(tag) {
1890 crate::functions::kill_ir_utils::kill_ir_function_ir_inst(function, inst);
1891 }
1892 }
1893 }
1894 IrCmd::NEW_TABLE => {
1895 if luaur_common::FFlag::LuauCodegenExtraTableOpts.get() {
1896 let array_size = function.uint_op(op_a(inst)) as i32;
1897 state.inst_not_readonly.insert(index);
1898 state.inst_no_metatable.insert(index);
1899 state.inst_array_size.try_insert(index, array_size);
1900 }
1901 }
1902 IrCmd::CHECK_ARRAY_SIZE => {
1903 let target = op_a(inst);
1904 let boundary = op_b(inst.clone());
1905 let boundary_value = if boundary.kind() == IrOpKind::Constant {
1906 function.as_int_op(boundary)
1907 } else {
1908 function.as_int_op(state.try_get_value(boundary))
1909 };
1910
1911 if let Some(array_index) = boundary_value {
1912 if array_index < 0 {
1913 let mut ops = crate::type_aliases::ir_ops::IrOps::new();
1914 ops.push(op_c(inst.clone()));
1915 replace_ir_function_ir_block_u32_ir_inst(
1916 function,
1917 block,
1918 index,
1919 IrInst {
1920 cmd: IrCmd::JUMP,
1921 ops,
1922 ..IrInst::default()
1923 },
1924 );
1925 return;
1926 }
1927
1928 if luaur_common::FFlag::LuauCodegenExtraTableOpts.get()
1929 && target.kind() == IrOpKind::Inst
1930 {
1931 if let Some(known_array_size) = state.inst_array_size.find(&target.index()) {
1932 if *known_array_size >= 0 {
1933 if (array_index as u32) < (*known_array_size as u32) {
1934 crate::functions::kill_ir_utils::kill_ir_function_ir_inst(
1935 function, inst,
1936 );
1937 } else {
1938 let mut ops = crate::type_aliases::ir_ops::IrOps::new();
1939 ops.push(op_c(inst.clone()));
1940 replace_ir_function_ir_block_u32_ir_inst(
1941 function,
1942 block,
1943 index,
1944 IrInst {
1945 cmd: IrCmd::JUMP,
1946 ops,
1947 ..IrInst::default()
1948 },
1949 );
1950 }
1951 return;
1952 }
1953 }
1954 }
1955
1956 if let Some(info) = state.try_get_register_info(target) {
1957 unsafe {
1958 if (*info).known_table_array_size_deprecated >= 0 {
1959 if (array_index as u32)
1960 < ((*info).known_table_array_size_deprecated as u32)
1961 {
1962 crate::functions::kill_ir_utils::kill_ir_function_ir_inst(
1963 function, inst,
1964 );
1965 } else {
1966 let mut ops = crate::type_aliases::ir_ops::IrOps::new();
1967 ops.push(op_c(inst.clone()));
1968 replace_ir_function_ir_block_u32_ir_inst(
1969 function,
1970 block,
1971 index,
1972 IrInst {
1973 cmd: IrCmd::JUMP,
1974 ops,
1975 ..IrInst::default()
1976 },
1977 );
1978 }
1979 return;
1980 }
1981 }
1982 }
1983 }
1984
1985 for prev_idx in state.check_array_size_cache.iter().copied() {
1986 let mut prev = function.instructions[prev_idx as usize].clone();
1987
1988 if op_a(&mut prev) != op_a(inst) {
1989 continue;
1990 }
1991
1992 let prev_boundary = op_b(prev.clone());
1993 let boundary = op_b(inst.clone());
1994 let mut same_boundary = prev_boundary == boundary;
1995
1996 if !same_boundary
1997 && boundary.kind() == IrOpKind::Constant
1998 && prev_boundary.kind() == IrOpKind::Constant
1999 && (function.int_op(boundary) as u32) < (function.int_op(prev_boundary) as u32)
2000 {
2001 same_boundary = true;
2002 }
2003
2004 if same_boundary {
2005 crate::functions::kill_ir_utils::kill_ir_function_ir_inst(function, inst);
2006 return;
2007 }
2008 }
2009
2010 if (state.check_array_size_cache.len() as i32)
2011 < luaur_common::FInt::LuauCodeGenReuseSlotLimit.get()
2012 {
2013 state.check_array_size_cache.push(index);
2014 }
2015 }
2016 IrCmd::CHECK_BUFFER_LEN => {
2017 let buffer_offset_op = op_b(inst.clone());
2018 let buffer_offset = if buffer_offset_op.kind() == IrOpKind::Constant {
2019 function.as_int_op(buffer_offset_op)
2020 } else {
2021 function.as_int_op(state.try_get_value(buffer_offset_op))
2022 };
2023
2024 let min_offset = function.int_op(op_c(inst.clone()));
2025 let max_offset = function.int_op(op_d(inst.clone()));
2026 crate::macros::codegen_assert::CODEGEN_ASSERT!(min_offset < max_offset);
2027 let access_size = max_offset - min_offset;
2028 crate::macros::codegen_assert::CODEGEN_ASSERT!(access_size > 0);
2029
2030 if let Some(buffer_offset) = buffer_offset {
2031 if buffer_offset < 0
2032 || (buffer_offset as u32).wrapping_add(access_size as u32) >= i32::MAX as u32
2033 {
2034 let mut ops = crate::type_aliases::ir_ops::IrOps::new();
2035 ops.push(op_f(inst.clone()));
2036 replace_ir_function_ir_block_u32_ir_inst(
2037 function,
2038 block,
2039 index,
2040 IrInst {
2041 cmd: IrCmd::JUMP,
2042 ops,
2043 ..IrInst::default()
2044 },
2045 );
2046 return;
2047 }
2048 }
2049
2050 for prev_idx in state.check_buffer_len_cache.clone() {
2051 let prev_ptr = &mut function.instructions[prev_idx as usize] as *mut IrInst;
2052 let prev = unsafe { &mut *prev_ptr };
2053
2054 if prev.cmd != IrCmd::CHECK_BUFFER_LEN {
2055 continue;
2056 }
2057
2058 if op_a(prev) == op_a(inst)
2059 && op_b(prev.clone()) == op_b(inst.clone())
2060 && op_c(prev.clone()) == op_c(inst.clone())
2061 && op_d(prev.clone()) == op_d(inst.clone())
2062 {
2063 if luaur_common::FFlag::DebugLuauAbortingChecks.get() {
2064 let replacement = build.undef();
2065 replace_ir_function_ir_op_ir_op(function, &mut inst.ops[5], replacement);
2066 } else {
2067 crate::functions::kill_ir_utils::kill_ir_function_ir_inst(function, inst);
2068 }
2069 return;
2070 }
2071
2072 if op_a(prev) == op_a(inst)
2073 && op_b(inst.clone()).kind() == IrOpKind::Constant
2074 && op_b(prev.clone()).kind() == IrOpKind::Constant
2075 {
2076 let curr_bound = function.int_op(op_b(inst.clone()));
2077 let prev_bound = function.int_op(op_b(prev.clone()));
2078 crate::macros::codegen_assert::CODEGEN_ASSERT!(curr_bound >= 0);
2079 crate::macros::codegen_assert::CODEGEN_ASSERT!(prev_bound >= 0);
2080
2081 let extra_offset = curr_bound - prev_bound;
2082 if state.try_merge_and_kill_buffer_length_check(
2083 build,
2084 block,
2085 inst,
2086 prev,
2087 extra_offset,
2088 ) {
2089 return;
2090 }
2091
2092 continue;
2093 }
2094
2095 if state.try_merge_buffer_range_check(build, block, inst, prev) {
2096 return;
2097 }
2098 }
2099
2100 if (state.check_buffer_len_cache.len() as i32)
2101 < luaur_common::FInt::LuauCodeGenReuseSlotLimit.get()
2102 {
2103 state.check_buffer_len_cache.push(index);
2104 }
2105 }
2106 IrCmd::ADD_VEC
2107 | IrCmd::SUB_VEC
2108 | IrCmd::MUL_VEC
2109 | IrCmd::DIV_VEC
2110 | IrCmd::IDIV_VEC
2111 | IrCmd::DOT_VEC
2112 | IrCmd::MIN_VEC
2113 | IrCmd::MAX_VEC => {
2114 let a = op_a(inst);
2115 let a_ptr = function.as_inst_op(a);
2116 if !a_ptr.is_null() {
2117 let a_inst = unsafe { (*a_ptr).clone() };
2118 if a_inst.cmd == IrCmd::TAG_VECTOR {
2119 let replacement = op_a(&mut a_inst.clone());
2120 replace_ir_function_ir_op_ir_op(function, &mut inst.ops[0], replacement);
2121 }
2122 }
2123
2124 let b = op_b(inst.clone());
2125 let b_ptr = function.as_inst_op(b);
2126 if !b_ptr.is_null() {
2127 let b_inst = unsafe { (*b_ptr).clone() };
2128 if b_inst.cmd == IrCmd::TAG_VECTOR {
2129 let replacement = op_a(&mut b_inst.clone());
2130 replace_ir_function_ir_op_ir_op(function, &mut inst.ops[1], replacement);
2131 }
2132 }
2133
2134 state.substitute_or_record(inst, index);
2135 }
2136 IrCmd::UNM_VEC | IrCmd::FLOOR_VEC | IrCmd::CEIL_VEC | IrCmd::ABS_VEC => {
2137 let a = op_a(inst);
2138 let a_ptr = function.as_inst_op(a);
2139 if !a_ptr.is_null() {
2140 let a_inst = unsafe { (*a_ptr).clone() };
2141 if a_inst.cmd == IrCmd::TAG_VECTOR {
2142 let replacement = op_a(&mut a_inst.clone());
2143 replace_ir_function_ir_op_ir_op(function, &mut inst.ops[0], replacement);
2144 }
2145 }
2146
2147 state.substitute_or_record(inst, index);
2148 }
2149 IrCmd::FLOAT_TO_VEC | IrCmd::TAG_VECTOR => {
2150 state.substitute_or_record(inst, index);
2151 }
2152 IrCmd::INVOKE_LIBM => {
2153 state.substitute_or_record(inst, index);
2154 }
2155 IrCmd::DO_ARITH => {
2156 let target = op_a(inst);
2157 state.invalidate_ir_op(target);
2158 state.invalidate_user_call();
2159 }
2160 IrCmd::DO_LEN => {
2161 let target = op_a(inst);
2162 state.invalidate_ir_op(target);
2163 state.invalidate_user_call();
2164 state.save_tag(target, lua_Type::LUA_TNUMBER as u8);
2165 }
2166 IrCmd::GET_TABLE => {
2167 let target = op_a(inst);
2168 state.invalidate_ir_op(target);
2169 state.invalidate_user_call();
2170 }
2171 IrCmd::SET_TABLE => {
2172 state.invalidate_user_call();
2173 }
2174 IrCmd::GET_CACHED_IMPORT => {
2175 let target = op_a(inst);
2176 state.invalidate_ir_op(target);
2177
2178 if state.in_safe_env {
2179 state.invalidate_value_propagation();
2180 } else {
2181 state.invalidate_user_call();
2182 }
2183 }
2184 IrCmd::SETLIST => {
2185 if luaur_common::FFlag::LuauCodegenExtraTableOpts.get() {
2186 if let Some(load_idx) =
2187 state.get_previous_versioned_load_index(IrCmd::LOAD_POINTER, op_b(inst.clone()))
2188 {
2189 let load_idx = unsafe { *load_idx };
2190 if let Some(known_array_size) = state.inst_array_size.find(&load_idx) {
2191 if *known_array_size >= 0 {
2192 let replacement = build.const_uint(*known_array_size as u32);
2193 replace_ir_function_ir_op_ir_op(
2194 function,
2195 &mut inst.ops[5],
2196 replacement,
2197 );
2198 }
2199 }
2200 }
2201 } else if let Some(info) = state.try_get_register_info(op_b(inst.clone())) {
2202 unsafe {
2203 if (*info).known_table_array_size_deprecated >= 0 {
2204 let replacement =
2205 build.const_uint((*info).known_table_array_size_deprecated as u32);
2206 replace_ir_function_ir_op_ir_op(function, &mut inst.ops[5], replacement);
2207 }
2208 }
2209 }
2210
2211 state.invalidate_value_propagation();
2212 state.invalidate_heap_table_data();
2213 state.invalidate_heap_buffer_data();
2214 }
2215 IrCmd::TABLE_SETNUM => {
2216 state.invalidate_table_array_size();
2217 }
2218 IrCmd::CONCAT => {
2219 let first_reg = crate::functions::vm_reg_op::vm_reg_op(op_a(inst));
2220 let count = function.uint_op(op_b(inst.clone())) as i32;
2221 state.invalidate_register_range(first_reg, count);
2222 state.invalidate_user_call();
2223 }
2224 IrCmd::FALLBACK_GETVARARGS => {
2225 let first_reg = crate::functions::vm_reg_op::vm_reg_op(op_b(inst.clone()));
2226 let count = function.int_op(op_c(inst.clone()));
2227 state.invalidate_register_range(first_reg, count);
2228 }
2229 IrCmd::FASTCALL => {
2230 let bfid = unsafe {
2231 core::mem::transmute::<u8, LuauBuiltinFunction>(function.uint_op(op_a(inst)) as u8)
2232 };
2233 let first_return_reg = crate::functions::vm_reg_op::vm_reg_op(op_b(inst.clone()));
2234 let nresults = function.int_op(op_d(inst.clone()));
2235
2236 handle_builtin_effects(state, bfid, first_return_reg as u32, nresults);
2237
2238 match bfid {
2239 LuauBuiltinFunction::LBF_MATH_MODF | LuauBuiltinFunction::LBF_MATH_FREXP => {
2240 let target =
2241 IrOp::ir_op_ir_op_kind_u32(IrOpKind::VmReg, first_return_reg as u32);
2242 state.update_tag(target, lua_Type::LUA_TNUMBER as u8);
2243
2244 if nresults > 1 {
2245 let target = IrOp::ir_op_ir_op_kind_u32(
2246 IrOpKind::VmReg,
2247 (first_return_reg + 1) as u32,
2248 );
2249 state.update_tag(target, lua_Type::LUA_TNUMBER as u8);
2250 }
2251 }
2252 _ => {}
2253 }
2254 }
2255 IrCmd::INVOKE_FASTCALL => {
2256 let bfid = unsafe {
2257 core::mem::transmute::<u8, LuauBuiltinFunction>(function.uint_op(op_a(inst)) as u8)
2258 };
2259 let first_return_reg =
2260 crate::functions::vm_reg_op::vm_reg_op(op_b(inst.clone())) as u32;
2261 let nresults = function.int_op(op_g(inst.clone()));
2262 handle_builtin_effects(state, bfid, first_return_reg, nresults);
2263 }
2264 IrCmd::CALL => {
2265 let first_reg = crate::functions::vm_reg_op::vm_reg_op(op_a(inst));
2266 state.invalidate_registers_from(first_reg);
2267 state.invalidate_user_call();
2268 }
2269 IrCmd::FALLBACK_GETGLOBAL => {
2270 state.invalidate_ir_op(op_b(inst.clone()));
2271 state.invalidate_user_call();
2272 }
2273 IrCmd::FALLBACK_SETGLOBAL | IrCmd::FALLBACK_SETTABLEKS => {
2274 state.invalidate_user_call();
2275 }
2276 IrCmd::FALLBACK_GETTABLEKS => {
2277 state.invalidate_ir_op(op_b(inst.clone()));
2278 state.invalidate_user_call();
2279 }
2280 IrCmd::FALLBACK_NAMECALL => {
2281 let target = op_b(inst.clone());
2282 state.invalidate_ir_op(target);
2283 state.invalidate_ir_op(IrOp::ir_op_ir_op_kind_u32(
2284 target.kind(),
2285 target.index() + 1,
2286 ));
2287 state.invalidate_user_call();
2288 }
2289 IrCmd::FALLBACK_PREPVARARGS => {}
2290 IrCmd::FALLBACK_DUPCLOSURE => {
2291 state.invalidate_ir_op(op_b(inst.clone()));
2292 state.invalidate_heap_table_data();
2293 }
2294 IrCmd::FALLBACK_FORGPREP => {
2295 let target = op_b(inst.clone());
2296 state.invalidate_ir_op(target);
2297 state.invalidate_ir_op(IrOp::ir_op_ir_op_kind_u32(
2298 target.kind(),
2299 target.index() + 1,
2300 ));
2301 state.invalidate_ir_op(IrOp::ir_op_ir_op_kind_u32(
2302 target.kind(),
2303 target.index() + 2,
2304 ));
2305 state.invalidate_user_call();
2306 }
2307 _ => {}
2308 }
2309}