1use crate::enums::ir_cmd::IrCmd;
2use crate::enums::ir_op_kind::IrOpKind;
3use crate::functions::byteswap::byteswap;
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::compare_ir_utils_alt_c::compare_i64_i64_ir_condition;
7use crate::functions::condition_op::condition_op;
8use crate::functions::countlz_bit_utils::countlz_u32;
9use crate::functions::countlz_bit_utils_alt_b::countlz_u64;
10use crate::functions::countrz_bit_utils::countrz_u32;
11use crate::functions::countrz_bit_utils_alt_b::countrz_u64;
12use crate::functions::get_op_ir_data::get_op_mut;
13use crate::functions::kill_ir_utils::kill_ir_function_ir_inst;
14use crate::functions::lrotate::lrotate;
15use crate::functions::replace_ir_utils::replace_ir_function_ir_op_ir_op;
16use crate::functions::replace_ir_utils_alt_b::replace_ir_function_ir_block_u32_ir_inst;
17use crate::functions::rrotate::rrotate;
18use crate::functions::substitute::substitute;
19use crate::functions::substitute_with_truncated_uint::substitute_with_truncated_uint;
20use crate::macros::codegen_assert::CODEGEN_ASSERT;
21use crate::records::ir_block::IrBlock;
22use crate::records::ir_builder::IrBuilder;
23use crate::records::ir_function::IrFunction;
24use crate::records::ir_inst::IrInst;
25use crate::records::ir_op::IrOp;
26use crate::type_aliases::ir_ops::IrOps;
27use luaur_vm::enums::lua_type::lua_Type;
28use luaur_vm::functions::luai_numidiv::luai_numidiv;
29use luaur_vm::functions::luai_nummod::luai_nummod;
30
31const K_DOUBLE_MAX_EXACT_INTEGER: f64 = 9007199254740992.0;
33
34const LUA_TNIL: u8 = lua_Type::LUA_TNIL as u8;
35const LUA_TBOOLEAN: u8 = lua_Type::LUA_TBOOLEAN as u8;
36const LUA_TNUMBER: u8 = lua_Type::LUA_TNUMBER as u8;
37const LUA_TINTEGER: u8 = lua_Type::LUA_TINTEGER as u8;
38
39fn make_inst(cmd: IrCmd, ops: &[IrOp]) -> IrInst {
40 let mut v = IrOps::new();
41 for &o in ops {
42 v.push(o);
43 }
44 IrInst {
45 cmd,
46 ops: v,
47 ..Default::default()
48 }
49}
50
51pub fn fold_constants(
52 build: &mut IrBuilder,
53 function: &mut IrFunction,
54 block: &mut IrBlock,
55 index: u32,
56) {
57 let inst_ptr: *mut IrInst = &mut function.instructions[index as usize];
58 let cmd = unsafe { (*inst_ptr).cmd };
59
60 let read = move |idx: usize| -> IrOp {
62 let s = unsafe { (*inst_ptr).ops.as_slice() };
63 if idx < s.len() {
64 s[idx]
65 } else {
66 IrOp::default()
67 }
68 };
69 let is_const = move |idx: usize| -> bool {
70 let s = unsafe { (*inst_ptr).ops.as_slice() };
71 let op = if idx < s.len() {
72 s[idx]
73 } else {
74 IrOp::default()
75 };
76 op.kind() == IrOpKind::Constant
77 };
78
79 match cmd {
80 IrCmd::ADD_INT => {
81 if is_const(0) && is_const(1) {
82 let lhs = function.int_op(read(0));
84 let rhs = function.int_op(read(1));
85 let sum = lhs.wrapping_add(rhs);
86 let c = build.const_int(sum);
87 substitute(function, unsafe { &mut *inst_ptr }, c);
88 }
89 }
90 IrCmd::SUB_INT => {
91 if is_const(0) && is_const(1) {
92 let lhs = function.int_op(read(0));
93 let rhs = function.int_op(read(1));
94 let sum = lhs.wrapping_sub(rhs);
95 let c = build.const_int(sum);
96 substitute(function, unsafe { &mut *inst_ptr }, c);
97 }
98 }
99 IrCmd::SEXTI8_INT => {
100 if is_const(0) {
101 let value = function.int_op(read(0)) as i8 as i32;
102 let c = build.const_int(value);
103 substitute(function, unsafe { &mut *inst_ptr }, c);
104 }
105 }
106 IrCmd::SEXTI16_INT => {
107 if is_const(0) {
108 let value = function.int_op(read(0)) as i16 as i32;
109 let c = build.const_int(value);
110 substitute(function, unsafe { &mut *inst_ptr }, c);
111 }
112 }
113 IrCmd::ADD_NUM => {
114 if is_const(0) && is_const(1) {
115 let v = function.double_op(read(0)) + function.double_op(read(1));
116 let c = build.const_double(v);
117 substitute(function, unsafe { &mut *inst_ptr }, c);
118 }
119 }
120 IrCmd::SUB_NUM => {
121 if is_const(0) && is_const(1) {
122 let v = function.double_op(read(0)) - function.double_op(read(1));
123 let c = build.const_double(v);
124 substitute(function, unsafe { &mut *inst_ptr }, c);
125 }
126 }
127 IrCmd::MUL_NUM => {
128 if is_const(0) && is_const(1) {
129 let v = function.double_op(read(0)) * function.double_op(read(1));
130 let c = build.const_double(v);
131 substitute(function, unsafe { &mut *inst_ptr }, c);
132 }
133 }
134 IrCmd::DIV_NUM => {
135 if is_const(0) && is_const(1) {
136 let v = function.double_op(read(0)) / function.double_op(read(1));
137 let c = build.const_double(v);
138 substitute(function, unsafe { &mut *inst_ptr }, c);
139 }
140 }
141 IrCmd::IDIV_NUM => {
142 if is_const(0) && is_const(1) {
143 let v = luai_numidiv(function.double_op(read(0)), function.double_op(read(1)));
144 let c = build.const_double(v);
145 substitute(function, unsafe { &mut *inst_ptr }, c);
146 }
147 }
148 IrCmd::MOD_NUM => {
149 if is_const(0) && is_const(1) {
150 let v = luai_nummod(function.double_op(read(0)), function.double_op(read(1)));
151 let c = build.const_double(v);
152 substitute(function, unsafe { &mut *inst_ptr }, c);
153 }
154 }
155 IrCmd::MIN_NUM => {
156 if is_const(0) && is_const(1) {
157 let a1 = function.double_op(read(0));
158 let a2 = function.double_op(read(1));
159 let c = build.const_double(if a1 < a2 { a1 } else { a2 });
160 substitute(function, unsafe { &mut *inst_ptr }, c);
161 }
162 }
163 IrCmd::MAX_NUM => {
164 if is_const(0) && is_const(1) {
165 let a1 = function.double_op(read(0));
166 let a2 = function.double_op(read(1));
167 let c = build.const_double(if a1 > a2 { a1 } else { a2 });
168 substitute(function, unsafe { &mut *inst_ptr }, c);
169 }
170 }
171 IrCmd::UNM_NUM => {
172 if is_const(0) {
173 let c = build.const_double(-function.double_op(read(0)));
174 substitute(function, unsafe { &mut *inst_ptr }, c);
175 }
176 }
177 IrCmd::FLOOR_NUM => {
178 if is_const(0) {
179 let c = build.const_double(function.double_op(read(0)).floor());
180 substitute(function, unsafe { &mut *inst_ptr }, c);
181 }
182 }
183 IrCmd::CEIL_NUM => {
184 if is_const(0) {
185 let c = build.const_double(function.double_op(read(0)).ceil());
186 substitute(function, unsafe { &mut *inst_ptr }, c);
187 }
188 }
189 IrCmd::ROUND_NUM => {
190 if is_const(0) {
191 let c = build.const_double(function.double_op(read(0)).round());
192 substitute(function, unsafe { &mut *inst_ptr }, c);
193 }
194 }
195 IrCmd::SQRT_NUM => {
196 if is_const(0) {
197 let c = build.const_double(function.double_op(read(0)).sqrt());
198 substitute(function, unsafe { &mut *inst_ptr }, c);
199 }
200 }
201 IrCmd::ABS_NUM => {
202 if is_const(0) {
203 let c = build.const_double(function.double_op(read(0)).abs());
204 substitute(function, unsafe { &mut *inst_ptr }, c);
205 }
206 }
207 IrCmd::SIGN_NUM => {
208 if is_const(0) {
209 let v = function.double_op(read(0));
210 let r = if v > 0.0 {
211 1.0
212 } else if v < 0.0 {
213 -1.0
214 } else {
215 0.0
216 };
217 let c = build.const_double(r);
218 substitute(function, unsafe { &mut *inst_ptr }, c);
219 }
220 }
221 IrCmd::ADD_FLOAT => {
222 if is_const(0) && is_const(1) {
223 let v = ((function.double_op(read(0)) as f32)
224 + (function.double_op(read(1)) as f32)) as f64;
225 let c = build.const_double(v);
226 substitute(function, unsafe { &mut *inst_ptr }, c);
227 }
228 }
229 IrCmd::SUB_FLOAT => {
230 if is_const(0) && is_const(1) {
231 let v = ((function.double_op(read(0)) as f32)
232 - (function.double_op(read(1)) as f32)) as f64;
233 let c = build.const_double(v);
234 substitute(function, unsafe { &mut *inst_ptr }, c);
235 }
236 }
237 IrCmd::MUL_FLOAT => {
238 if is_const(0) && is_const(1) {
239 let v = ((function.double_op(read(0)) as f32)
240 * (function.double_op(read(1)) as f32)) as f64;
241 let c = build.const_double(v);
242 substitute(function, unsafe { &mut *inst_ptr }, c);
243 }
244 }
245 IrCmd::DIV_FLOAT => {
246 if is_const(0) && is_const(1) {
247 let v = ((function.double_op(read(0)) as f32)
248 / (function.double_op(read(1)) as f32)) as f64;
249 let c = build.const_double(v);
250 substitute(function, unsafe { &mut *inst_ptr }, c);
251 }
252 }
253 IrCmd::MIN_FLOAT => {
254 if is_const(0) && is_const(1) {
255 let a1 = function.double_op(read(0)) as f32;
256 let a2 = function.double_op(read(1)) as f32;
257 let c = build.const_double((if a1 < a2 { a1 } else { a2 }) as f64);
258 substitute(function, unsafe { &mut *inst_ptr }, c);
259 }
260 }
261 IrCmd::MAX_FLOAT => {
262 if is_const(0) && is_const(1) {
263 let a1 = function.double_op(read(0)) as f32;
264 let a2 = function.double_op(read(1)) as f32;
265 let c = build.const_double((if a1 > a2 { a1 } else { a2 }) as f64);
266 substitute(function, unsafe { &mut *inst_ptr }, c);
267 }
268 }
269 IrCmd::UNM_FLOAT => {
270 if is_const(0) {
271 let c = build.const_double((-(function.double_op(read(0)) as f32)) as f64);
272 substitute(function, unsafe { &mut *inst_ptr }, c);
273 }
274 }
275 IrCmd::FLOOR_FLOAT => {
276 if is_const(0) {
277 let c = build.const_double((function.double_op(read(0)) as f32).floor() as f64);
278 substitute(function, unsafe { &mut *inst_ptr }, c);
279 }
280 }
281 IrCmd::CEIL_FLOAT => {
282 if is_const(0) {
283 let c = build.const_double((function.double_op(read(0)) as f32).ceil() as f64);
284 substitute(function, unsafe { &mut *inst_ptr }, c);
285 }
286 }
287 IrCmd::SQRT_FLOAT => {
288 if is_const(0) {
289 let c = build.const_double((function.double_op(read(0)) as f32).sqrt() as f64);
290 substitute(function, unsafe { &mut *inst_ptr }, c);
291 }
292 }
293 IrCmd::ABS_FLOAT => {
294 if is_const(0) {
295 let c = build.const_double((function.double_op(read(0)) as f32).abs() as f64);
296 substitute(function, unsafe { &mut *inst_ptr }, c);
297 }
298 }
299 IrCmd::SIGN_FLOAT => {
300 if is_const(0) {
301 let v = function.double_op(read(0)) as f32;
302 let r: f32 = if v > 0.0 {
303 1.0
304 } else if v < 0.0 {
305 -1.0
306 } else {
307 0.0
308 };
309 let c = build.const_double(r as f64);
310 substitute(function, unsafe { &mut *inst_ptr }, c);
311 }
312 }
313 IrCmd::SELECT_NUM => {
314 if is_const(2) && is_const(3) {
315 let c = function.double_op(read(2));
316 let d = function.double_op(read(3));
317 let repl = if c == d { read(1) } else { read(0) };
318 substitute(function, unsafe { &mut *inst_ptr }, repl);
319 } else if read(0) == read(1) {
320 let repl = read(0);
322 substitute(function, unsafe { &mut *inst_ptr }, repl);
323 }
324 }
325 IrCmd::SELECT_VEC => {
326 if read(0) == read(1) {
327 let repl = read(0);
328 substitute(function, unsafe { &mut *inst_ptr }, repl);
329 }
330 }
331 IrCmd::SELECT_IF_TRUTHY => {
332 if read(1) == read(2) {
333 let repl = read(1);
334 substitute(function, unsafe { &mut *inst_ptr }, repl);
335 }
336 }
337 IrCmd::NOT_ANY => {
338 if is_const(0) {
339 let a = function.tag_op(read(0));
340
341 if a == LUA_TNIL {
342 let c = build.const_int(1);
343 substitute(function, unsafe { &mut *inst_ptr }, c);
344 } else if a != LUA_TBOOLEAN {
345 let c = build.const_int(0);
346 substitute(function, unsafe { &mut *inst_ptr }, c);
347 } else if is_const(1) {
348 let c = build.const_int(if function.int_op(read(1)) == 1 { 0 } else { 1 });
349 substitute(function, unsafe { &mut *inst_ptr }, c);
350 }
351 }
352 }
353 IrCmd::CMP_INT => {
354 if is_const(0) && is_const(1) {
355 let res = compare_i32_i32_ir_condition(
356 function.int_op(read(0)),
357 function.int_op(read(1)),
358 condition_op(read(2)),
359 );
360 let c = build.const_int(if res { 1 } else { 0 });
361 substitute(function, unsafe { &mut *inst_ptr }, c);
362 }
363 }
364 IrCmd::CMP_INT64 => {
365 if is_const(0) && is_const(1) {
366 let lhs = function.int64_op(read(0));
367 let rhs = function.int64_op(read(1));
368 let res = compare_i64_i64_ir_condition(lhs, rhs, condition_op(read(2)));
369 let c = build.const_int(if res { 1 } else { 0 });
370 substitute(function, unsafe { &mut *inst_ptr }, c);
371 }
372 }
373 IrCmd::CMP_TAG => {
374 if is_const(0) && is_const(1) {
375 let cond = condition_op(read(2));
376 CODEGEN_ASSERT!(
377 cond == crate::enums::ir_condition::IrCondition::Equal
378 || cond == crate::enums::ir_condition::IrCondition::NotEqual
379 );
380
381 let same = function.tag_op(read(0)) == function.tag_op(read(1));
382 let val = if cond == crate::enums::ir_condition::IrCondition::Equal {
383 if same {
384 1
385 } else {
386 0
387 }
388 } else if !same {
389 1
390 } else {
391 0
392 };
393 let c = build.const_int(val);
394 substitute(function, unsafe { &mut *inst_ptr }, c);
395 }
396 }
397 IrCmd::CMP_SPLIT_TVALUE => {
398 CODEGEN_ASSERT!(is_const(1));
399
400 let cond = condition_op(read(4));
401 CODEGEN_ASSERT!(
402 cond == crate::enums::ir_condition::IrCondition::Equal
403 || cond == crate::enums::ir_condition::IrCondition::NotEqual
404 );
405
406 if cond == crate::enums::ir_condition::IrCondition::Equal {
407 if is_const(0) && function.tag_op(read(0)) != function.tag_op(read(1)) {
408 let c = build.const_int(0);
409 substitute(function, unsafe { &mut *inst_ptr }, c);
410 } else if is_const(2) && is_const(3) {
411 let known_same_tag = is_const(0);
413 let tag_b = function.tag_op(read(1));
414 let same_value;
415 if tag_b == LUA_TBOOLEAN {
416 same_value = compare_i32_i32_ir_condition(
417 function.int_op(read(2)),
418 function.int_op(read(3)),
419 crate::enums::ir_condition::IrCondition::Equal,
420 );
421 } else if tag_b == LUA_TNUMBER {
422 same_value = compare_f64_f64_ir_condition(
423 function.double_op(read(2)),
424 function.double_op(read(3)),
425 crate::enums::ir_condition::IrCondition::Equal,
426 );
427 } else if tag_b == LUA_TINTEGER {
428 let lhs = function.int64_op(read(2));
429 let rhs = function.int64_op(read(3));
430 same_value = compare_i64_i64_ir_condition(
431 lhs,
432 rhs,
433 crate::enums::ir_condition::IrCondition::Equal,
434 );
435 } else {
436 CODEGEN_ASSERT!(false, "unsupported type");
437 same_value = false;
438 }
439
440 if known_same_tag && same_value {
441 let c = build.const_int(1);
442 substitute(function, unsafe { &mut *inst_ptr }, c);
443 } else if same_value {
444 let r = make_inst(IrCmd::CMP_TAG, &[read(0), read(1), read(4)]);
445 replace_ir_function_ir_block_u32_ir_inst(function, block, index, r);
446 } else {
447 let c = build.const_int(0);
448 substitute(function, unsafe { &mut *inst_ptr }, c);
449 }
450 }
451 } else {
452 if is_const(0) && function.tag_op(read(0)) != function.tag_op(read(1)) {
453 let c = build.const_int(1);
454 substitute(function, unsafe { &mut *inst_ptr }, c);
455 } else if is_const(2) && is_const(3) {
456 let known_same_tag = is_const(0);
457 let tag_b = function.tag_op(read(1));
458 let different_value;
459 if tag_b == LUA_TBOOLEAN {
460 different_value = compare_i32_i32_ir_condition(
461 function.int_op(read(2)),
462 function.int_op(read(3)),
463 crate::enums::ir_condition::IrCondition::NotEqual,
464 );
465 } else if tag_b == LUA_TNUMBER {
466 different_value = compare_f64_f64_ir_condition(
467 function.double_op(read(2)),
468 function.double_op(read(3)),
469 crate::enums::ir_condition::IrCondition::NotEqual,
470 );
471 } else if tag_b == LUA_TINTEGER {
472 let lhs = function.int64_op(read(2));
473 let rhs = function.int64_op(read(3));
474 different_value = compare_i64_i64_ir_condition(
475 lhs,
476 rhs,
477 crate::enums::ir_condition::IrCondition::NotEqual,
478 );
479 } else {
480 CODEGEN_ASSERT!(false, "unsupported type");
481 different_value = false;
482 }
483
484 if different_value {
485 let c = build.const_int(1);
486 substitute(function, unsafe { &mut *inst_ptr }, c);
487 } else if known_same_tag {
488 let c = build.const_int(0);
489 substitute(function, unsafe { &mut *inst_ptr }, c);
490 } else {
491 let r = make_inst(IrCmd::CMP_TAG, &[read(0), read(1), read(4)]);
492 replace_ir_function_ir_block_u32_ir_inst(function, block, index, r);
493 }
494 }
495 }
496 }
497 IrCmd::JUMP_EQ_TAG => {
498 if is_const(0) && is_const(1) {
499 if function.tag_op(read(0)) == function.tag_op(read(1)) {
500 let r = make_inst(IrCmd::JUMP, &[read(2)]);
501 replace_ir_function_ir_block_u32_ir_inst(function, block, index, r);
502 } else {
503 let r = make_inst(IrCmd::JUMP, &[read(3)]);
504 replace_ir_function_ir_block_u32_ir_inst(function, block, index, r);
505 }
506 }
507 }
508 IrCmd::JUMP_CMP_INT => {
509 if is_const(0) && is_const(1) {
510 let res = compare_i32_i32_ir_condition(
511 function.int_op(read(0)),
512 function.int_op(read(1)),
513 condition_op(read(2)),
514 );
515 let r = if res {
516 make_inst(IrCmd::JUMP, &[read(3)])
517 } else {
518 make_inst(IrCmd::JUMP, &[read(4)])
519 };
520 replace_ir_function_ir_block_u32_ir_inst(function, block, index, r);
521 }
522 }
523 IrCmd::JUMP_CMP_NUM => {
524 if is_const(0) && is_const(1) {
525 let res = compare_f64_f64_ir_condition(
526 function.double_op(read(0)),
527 function.double_op(read(1)),
528 condition_op(read(2)),
529 );
530 let r = if res {
531 make_inst(IrCmd::JUMP, &[read(3)])
532 } else {
533 make_inst(IrCmd::JUMP, &[read(4)])
534 };
535 replace_ir_function_ir_block_u32_ir_inst(function, block, index, r);
536 }
537 }
538 IrCmd::JUMP_CMP_FLOAT => {
539 if is_const(0) && is_const(1) {
540 let a = function.double_op(read(0)) as f32 as f64;
541 let b = function.double_op(read(1)) as f32 as f64;
542 let res = compare_f64_f64_ir_condition(a, b, condition_op(read(2)));
543 let r = if res {
544 make_inst(IrCmd::JUMP, &[read(3)])
545 } else {
546 make_inst(IrCmd::JUMP, &[read(4)])
547 };
548 replace_ir_function_ir_block_u32_ir_inst(function, block, index, r);
549 }
550 }
551 IrCmd::TRY_NUM_TO_INDEX => {
552 if is_const(0) {
553 let value = function.double_op(read(0));
554
555 if value >= i32::MIN as f64 && value <= i32::MAX as f64 {
557 let arr_index = value as i32;
558
559 if arr_index as f64 == value {
560 let c = build.const_int(arr_index);
561 substitute(function, unsafe { &mut *inst_ptr }, c);
562 } else {
563 let r = make_inst(IrCmd::JUMP, &[read(1)]);
564 replace_ir_function_ir_block_u32_ir_inst(function, block, index, r);
565 }
566 } else {
567 let r = make_inst(IrCmd::JUMP, &[read(1)]);
568 replace_ir_function_ir_block_u32_ir_inst(function, block, index, r);
569 }
570 }
571 }
572 IrCmd::INT_TO_NUM => {
573 if is_const(0) {
574 let c = build.const_double(function.int_op(read(0)) as f64);
575 substitute(function, unsafe { &mut *inst_ptr }, c);
576 }
577 }
578 IrCmd::INT64_TO_NUM => {
579 if is_const(0) {
580 let v = function.int64_op(read(0)) as f64;
581 let c = build.const_double(v);
582 substitute(function, unsafe { &mut *inst_ptr }, c);
583 }
584 }
585 IrCmd::UINT_TO_NUM => {
586 if is_const(0) {
587 let c = build.const_double((function.int_op(read(0)) as u32) as f64);
588 substitute(function, unsafe { &mut *inst_ptr }, c);
589 }
590 }
591 IrCmd::UINT_TO_FLOAT => {
592 if is_const(0) {
593 let c = build.const_double(((function.int_op(read(0)) as u32) as f32) as f64);
594 substitute(function, unsafe { &mut *inst_ptr }, c);
595 }
596 }
597 IrCmd::NUM_TO_INT => {
598 if is_const(0) {
599 let value = function.double_op(read(0));
600
601 if value >= i32::MIN as f64 && value <= i32::MAX as f64 {
603 let c = build.const_int(value as i32);
604 substitute(function, unsafe { &mut *inst_ptr }, c);
605 }
606 }
607 }
608 IrCmd::NUM_TO_UINT => {
609 if is_const(0) {
610 let value = function.double_op(read(0));
611
612 if value >= -K_DOUBLE_MAX_EXACT_INTEGER && value <= K_DOUBLE_MAX_EXACT_INTEGER {
614 let c = build.const_int((value as i64 as u32) as i32);
615 substitute(function, unsafe { &mut *inst_ptr }, c);
616 }
617 }
618 }
619 IrCmd::NUM_TO_INT64 => {
620 if is_const(0) {
621 let value = function.double_op(read(0));
622
623 if value >= i64::MIN as f64 && value < i64::MAX as f64 {
624 let c = build.const_int_64(value as i64);
625 substitute(function, unsafe { &mut *inst_ptr }, c);
626 }
627 }
628 }
629 IrCmd::FLOAT_TO_NUM => {
630 if is_const(0) {
632 let c = build.const_double(function.double_op(read(0)));
633 substitute(function, unsafe { &mut *inst_ptr }, c);
634 }
635 }
636 IrCmd::NUM_TO_FLOAT => {
637 if is_const(0) {
639 let c = build.const_double((function.double_op(read(0)) as f32) as f64);
640 substitute(function, unsafe { &mut *inst_ptr }, c);
641 }
642 }
643 IrCmd::TRUNCATE_UINT => {
644 if is_const(0) {
646 let repl = read(0);
647 substitute(function, unsafe { &mut *inst_ptr }, repl);
648 }
649 }
650 IrCmd::CHECK_TAG => {
651 if is_const(0) && is_const(1) {
652 if function.tag_op(read(0)) == function.tag_op(read(1)) {
653 kill_ir_function_ir_inst(function, unsafe { &mut *inst_ptr });
654 } else {
655 let r = make_inst(IrCmd::JUMP, &[read(2)]); replace_ir_function_ir_block_u32_ir_inst(function, block, index, r);
657 }
658 }
659 }
660 IrCmd::CHECK_TRUTHY => {
661 if is_const(0) {
662 if function.tag_op(read(0)) == LUA_TNIL {
663 let r = make_inst(IrCmd::JUMP, &[read(2)]); replace_ir_function_ir_block_u32_ir_inst(function, block, index, r);
665 } else if function.tag_op(read(0)) == LUA_TBOOLEAN {
666 if is_const(1) {
667 if function.int_op(read(1)) == 0 {
668 let r = make_inst(IrCmd::JUMP, &[read(2)]); replace_ir_function_ir_block_u32_ir_inst(function, block, index, r);
670 } else {
671 kill_ir_function_ir_inst(function, unsafe { &mut *inst_ptr });
672 }
673 }
674 } else {
675 kill_ir_function_ir_inst(function, unsafe { &mut *inst_ptr });
676 }
677 }
678 }
679 IrCmd::CHECK_CMP_NUM => {
680 if is_const(0) && is_const(1) {
681 if compare_f64_f64_ir_condition(
682 function.double_op(read(0)),
683 function.double_op(read(1)),
684 condition_op(read(2)),
685 ) {
686 kill_ir_function_ir_inst(function, unsafe { &mut *inst_ptr });
687 } else {
688 let r = make_inst(IrCmd::JUMP, &[read(3)]);
689 replace_ir_function_ir_block_u32_ir_inst(function, block, index, r);
690 }
691 }
692 }
693 IrCmd::CHECK_CMP_INT => {
694 if is_const(0) && is_const(1) {
695 if compare_i32_i32_ir_condition(
696 function.int_op(read(0)),
697 function.int_op(read(1)),
698 condition_op(read(2)),
699 ) {
700 kill_ir_function_ir_inst(function, unsafe { &mut *inst_ptr });
701 } else {
702 let r = make_inst(IrCmd::JUMP, &[read(3)]); replace_ir_function_ir_block_u32_ir_inst(function, block, index, r);
704 }
705 }
706 }
707 IrCmd::ADD_INT64 => {
708 if is_const(0) && is_const(1) {
709 let lhs = function.int64_op(read(0));
710 let rhs = function.int64_op(read(1));
711 let c = build.const_int_64(lhs.wrapping_add(rhs));
712 substitute(function, unsafe { &mut *inst_ptr }, c);
713 }
714 }
715 IrCmd::SUB_INT64 => {
716 if is_const(0) && is_const(1) {
717 let lhs = function.int64_op(read(0));
718 let rhs = function.int64_op(read(1));
719 let c = build.const_int_64(lhs.wrapping_sub(rhs));
720 substitute(function, unsafe { &mut *inst_ptr }, c);
721 }
722 }
723 IrCmd::MUL_INT64 => {
724 if is_const(0) && is_const(1) {
725 let lhs = function.int64_op(read(0));
726 let rhs = function.int64_op(read(1));
727 let c = build.const_int_64(lhs.wrapping_mul(rhs));
728 substitute(function, unsafe { &mut *inst_ptr }, c);
729 }
730 }
731 IrCmd::DIV_INT64 => {
732 if is_const(0) && is_const(1) {
733 let lhs = function.int64_op(read(0));
734 let rhs = function.int64_op(read(1));
735 if rhs != 0 && !(lhs == i64::MIN && rhs == -1) {
736 let c = build.const_int_64(lhs / rhs);
737 substitute(function, unsafe { &mut *inst_ptr }, c);
738 }
739 }
740 }
741 IrCmd::IDIV_INT64 => {
742 if is_const(0) && is_const(1) {
743 let lhs = function.int64_op(read(0));
744 let rhs = function.int64_op(read(1));
745 if rhs != 0 && !(lhs == i64::MIN && rhs == -1) {
746 let mut q = lhs / rhs;
747 if (lhs ^ rhs) < 0 && q.wrapping_mul(rhs) != lhs {
749 q -= 1;
750 }
751 let c = build.const_int_64(q);
752 substitute(function, unsafe { &mut *inst_ptr }, c);
753 }
754 }
755 }
756 IrCmd::UDIV_INT64 => {
757 if is_const(0) && is_const(1) {
758 let lhs = function.int64_op(read(0)) as u64;
759 let rhs = function.int64_op(read(1)) as u64;
760 if rhs != 0 {
761 let c = build.const_int_64((lhs / rhs) as i64);
762 substitute(function, unsafe { &mut *inst_ptr }, c);
763 }
764 }
765 }
766 IrCmd::REM_INT64 => {
767 if is_const(0) && is_const(1) {
768 let lhs = function.int64_op(read(0));
769 let rhs = function.int64_op(read(1));
770 if rhs != 0 && !(lhs == i64::MIN && rhs == -1) {
771 let c = build.const_int_64(lhs % rhs);
772 substitute(function, unsafe { &mut *inst_ptr }, c);
773 }
774 }
775 }
776 IrCmd::UREM_INT64 => {
777 if is_const(0) && is_const(1) {
778 let lhs = function.int64_op(read(0)) as u64;
779 let rhs = function.int64_op(read(1)) as u64;
780 if rhs != 0 {
781 let c = build.const_int_64((lhs % rhs) as i64);
782 substitute(function, unsafe { &mut *inst_ptr }, c);
783 }
784 }
785 }
786 IrCmd::MOD_INT64 => {
787 if is_const(0) && is_const(1) {
788 let lhs = function.int64_op(read(0));
789 let rhs = function.int64_op(read(1));
790 if rhs != 0 && !(lhs == i64::MIN && rhs == -1) {
791 let mut rem = lhs % rhs;
792 if rem != 0 && (rem ^ rhs) < 0 {
794 rem += rhs;
795 }
796 let c = build.const_int_64(rem);
797 substitute(function, unsafe { &mut *inst_ptr }, c);
798 }
799 }
800 }
801 IrCmd::CHECK_DIV_INT64 => {
802 if is_const(0) && is_const(1) {
803 let lhs = function.int64_op(read(0));
804 let rhs = function.int64_op(read(1));
805 if rhs != 0 && !(lhs == i64::MIN && rhs == -1) {
806 kill_ir_function_ir_inst(function, unsafe { &mut *inst_ptr });
807 } else {
809 let r = make_inst(IrCmd::JUMP, &[read(2)]);
810 replace_ir_function_ir_block_u32_ir_inst(function, block, index, r);
811 }
812 }
813 }
814 IrCmd::CHECK_CMP_INT64 => {
815 if is_const(0) && is_const(1) {
816 let lhs = function.int64_op(read(0));
817 let rhs = function.int64_op(read(1));
818 if compare_i64_i64_ir_condition(lhs, rhs, condition_op(read(2))) {
819 kill_ir_function_ir_inst(function, unsafe { &mut *inst_ptr });
820 } else {
821 let r = make_inst(IrCmd::JUMP, &[read(3)]);
822 replace_ir_function_ir_block_u32_ir_inst(function, block, index, r);
823 }
824 }
825 }
826 IrCmd::BITAND_INT64 => {
827 if is_const(0) && is_const(1) {
828 let op1 = function.int64_op(read(0));
829 let op2 = function.int64_op(read(1));
830 let c = build.const_int_64(op1 & op2);
831 substitute(function, unsafe { &mut *inst_ptr }, c);
832 } else if is_const(0) && function.int64_op(read(0)) == 0 {
833 let c = build.const_int_64(0);
834 substitute(function, unsafe { &mut *inst_ptr }, c);
835 } else if is_const(0) && function.int64_op(read(0)) == -1 {
836 let repl = read(1);
837 substitute(function, unsafe { &mut *inst_ptr }, repl);
838 } else if is_const(1) && function.int64_op(read(1)) == 0 {
839 let c = build.const_int_64(0);
840 substitute(function, unsafe { &mut *inst_ptr }, c);
841 } else if is_const(1) && function.int64_op(read(1)) == -1 {
842 let repl = read(0);
843 substitute(function, unsafe { &mut *inst_ptr }, repl);
844 }
845 }
846 IrCmd::BITXOR_INT64 => {
847 if is_const(0) && is_const(1) {
848 let op1 = function.int64_op(read(0));
849 let op2 = function.int64_op(read(1));
850 let c = build.const_int_64(op1 ^ op2);
851 substitute(function, unsafe { &mut *inst_ptr }, c);
852 } else if is_const(0) && function.int64_op(read(0)) == 0 {
853 let repl = read(1);
854 substitute(function, unsafe { &mut *inst_ptr }, repl);
855 } else if is_const(1) && function.int64_op(read(1)) == 0 {
856 let repl = read(0);
857 substitute(function, unsafe { &mut *inst_ptr }, repl);
858 }
859 }
860 IrCmd::BITOR_INT64 => {
861 if is_const(0) && is_const(1) {
862 let op1 = function.int64_op(read(0));
863 let op2 = function.int64_op(read(1));
864 let c = build.const_int_64(op1 | op2);
865 substitute(function, unsafe { &mut *inst_ptr }, c);
866 } else if is_const(0) && function.int64_op(read(0)) == 0 {
867 let repl = read(1);
868 substitute(function, unsafe { &mut *inst_ptr }, repl);
869 } else if is_const(0) && function.int64_op(read(0)) == -1 {
870 let c = build.const_int_64(-1);
871 substitute(function, unsafe { &mut *inst_ptr }, c);
872 } else if is_const(1) && function.int64_op(read(1)) == 0 {
873 let repl = read(0);
874 substitute(function, unsafe { &mut *inst_ptr }, repl);
875 } else if is_const(1) && function.int64_op(read(1)) == -1 {
876 let c = build.const_int_64(-1);
877 substitute(function, unsafe { &mut *inst_ptr }, c);
878 }
879 }
880 IrCmd::BITNOT_INT64 => {
881 if is_const(0) {
882 let op1 = function.int64_op(read(0));
883 let c = build.const_int_64(!op1);
884 substitute(function, unsafe { &mut *inst_ptr }, c);
885 }
886 }
887 IrCmd::BITLSHIFT_INT64 => {
888 if is_const(0) && is_const(1) {
889 let n = function.int64_op(read(0)) as u64;
890 let i = function.int64_op(read(1));
891 let result = if (-63..=63).contains(&i) {
892 (if i < 0 {
893 n >> ((-i) as u32)
894 } else {
895 n << (i as u32)
896 }) as i64
897 } else {
898 0
899 };
900 let c = build.const_int_64(result);
901 substitute(function, unsafe { &mut *inst_ptr }, c);
902 }
903 }
904 IrCmd::BITRSHIFT_INT64 => {
905 if is_const(0) && is_const(1) {
906 let n = function.int64_op(read(0)) as u64;
907 let i = function.int64_op(read(1));
908 let result = if (-63..=63).contains(&i) {
909 (if i < 0 {
910 n << ((-i) as u32)
911 } else {
912 n >> (i as u32)
913 }) as i64
914 } else {
915 0
916 };
917 let c = build.const_int_64(result);
918 substitute(function, unsafe { &mut *inst_ptr }, c);
919 }
920 }
921 IrCmd::BITARSHIFT_INT64 => {
922 if is_const(0) && is_const(1) {
923 let n = function.int64_op(read(0));
924 let i = function.int64_op(read(1));
925 let result = if (-63..=63).contains(&i) {
926 if i < 0 {
927 ((n as u64) << ((-i) as u32)) as i64
928 } else {
929 n >> (i as u32) }
931 } else if i < -63 {
932 0
933 } else if n < 0 {
934 -1
935 } else {
936 0
937 };
938 let c = build.const_int_64(result);
939 substitute(function, unsafe { &mut *inst_ptr }, c);
940 }
941 }
942 IrCmd::BITLROTATE_INT64 => {
943 if is_const(0) && is_const(1) {
944 let n = function.int64_op(read(0)) as u64;
945 let s = ((function.int64_op(read(1)) as u64) % 64) as u32;
946 let r = if s != 0 {
947 (n << s) | (n >> (64 - s))
948 } else {
949 n
950 };
951 let c = build.const_int_64(r as i64);
952 substitute(function, unsafe { &mut *inst_ptr }, c);
953 }
954 }
955 IrCmd::BITRROTATE_INT64 => {
956 if is_const(0) && is_const(1) {
957 let n = function.int64_op(read(0)) as u64;
958 let s = ((function.int64_op(read(1)) as u64) % 64) as u32;
959 let r = if s != 0 {
960 (n >> s) | (n << (64 - s))
961 } else {
962 n
963 };
964 let c = build.const_int_64(r as i64);
965 substitute(function, unsafe { &mut *inst_ptr }, c);
966 }
967 }
968 IrCmd::BITCOUNTLZ_INT64 => {
969 if is_const(0) {
970 let n = function.int64_op(read(0)) as u64;
971 let c = build.const_int_64(countlz_u64(n) as i64);
972 substitute(function, unsafe { &mut *inst_ptr }, c);
973 }
974 }
975 IrCmd::BITCOUNTRZ_INT64 => {
976 if is_const(0) {
977 let n = function.int64_op(read(0)) as u64;
978 let c = build.const_int_64(countrz_u64(n) as i64);
979 substitute(function, unsafe { &mut *inst_ptr }, c);
980 }
981 }
982 IrCmd::BYTESWAP_INT64 => {
983 if is_const(0) {
984 let a = function.int64_op(read(0)) as u64;
985 let result = byteswap(a);
986 let c = build.const_int_64(result as i64);
987 substitute(function, unsafe { &mut *inst_ptr }, c);
988 }
989 }
990 IrCmd::BITAND_UINT => {
991 if is_const(0) && is_const(1) {
992 let op1 = function.int_op(read(0)) as u32;
993 let op2 = function.int_op(read(1)) as u32;
994 let c = build.const_int((op1 & op2) as i32);
995 substitute(function, unsafe { &mut *inst_ptr }, c);
996 } else if is_const(0) && function.int_op(read(0)) == 0 {
997 let c = build.const_int(0);
998 substitute(function, unsafe { &mut *inst_ptr }, c);
999 } else if is_const(0) && function.int_op(read(0)) == -1 {
1000 let op = read(1);
1001 substitute_with_truncated_uint(function, block, unsafe { &mut *inst_ptr }, op);
1002 } else if is_const(1) && function.int_op(read(1)) == 0 {
1003 let c = build.const_int(0);
1004 substitute(function, unsafe { &mut *inst_ptr }, c);
1005 } else if is_const(1) && function.int_op(read(1)) == -1 {
1006 let op = read(0);
1007 substitute_with_truncated_uint(function, block, unsafe { &mut *inst_ptr }, op);
1008 }
1009 }
1010 IrCmd::BITXOR_UINT => {
1011 if is_const(0) && is_const(1) {
1012 let op1 = function.int_op(read(0)) as u32;
1013 let op2 = function.int_op(read(1)) as u32;
1014 let c = build.const_int((op1 ^ op2) as i32);
1015 substitute(function, unsafe { &mut *inst_ptr }, c);
1016 } else if is_const(0) && function.int_op(read(0)) == 0 {
1017 let op = read(1);
1018 substitute_with_truncated_uint(function, block, unsafe { &mut *inst_ptr }, op);
1019 } else if is_const(0) && function.int_op(read(0)) == -1 {
1020 let r = make_inst(IrCmd::BITNOT_UINT, &[read(1)]);
1021 replace_ir_function_ir_block_u32_ir_inst(function, block, index, r);
1022 } else if is_const(1) && function.int_op(read(1)) == 0 {
1023 let op = read(0);
1024 substitute_with_truncated_uint(function, block, unsafe { &mut *inst_ptr }, op);
1025 } else if is_const(1) && function.int_op(read(1)) == -1 {
1026 let r = make_inst(IrCmd::BITNOT_UINT, &[read(0)]);
1027 replace_ir_function_ir_block_u32_ir_inst(function, block, index, r);
1028 }
1029 }
1030 IrCmd::BITOR_UINT => {
1031 if is_const(0) && is_const(1) {
1032 let op1 = function.int_op(read(0)) as u32;
1033 let op2 = function.int_op(read(1)) as u32;
1034 let c = build.const_int((op1 | op2) as i32);
1035 substitute(function, unsafe { &mut *inst_ptr }, c);
1036 } else if is_const(0) && function.int_op(read(0)) == 0 {
1037 let op = read(1);
1038 substitute_with_truncated_uint(function, block, unsafe { &mut *inst_ptr }, op);
1039 } else if is_const(0) && function.int_op(read(0)) == -1 {
1040 let c = build.const_int(-1);
1041 substitute(function, unsafe { &mut *inst_ptr }, c);
1042 } else if is_const(1) && function.int_op(read(1)) == 0 {
1043 let op = read(0);
1044 substitute_with_truncated_uint(function, block, unsafe { &mut *inst_ptr }, op);
1045 } else if is_const(1) && function.int_op(read(1)) == -1 {
1046 let c = build.const_int(-1);
1047 substitute(function, unsafe { &mut *inst_ptr }, c);
1048 }
1049 }
1050 IrCmd::BITNOT_UINT => {
1051 if is_const(0) {
1052 let c = build.const_int(!(function.int_op(read(0)) as u32) as i32);
1053 substitute(function, unsafe { &mut *inst_ptr }, c);
1054 }
1055 }
1056 IrCmd::BITLSHIFT_UINT => {
1057 if is_const(0) && is_const(1) {
1058 let op1 = function.int_op(read(0)) as u32;
1059 let op2 = function.int_op(read(1));
1060 let c = build.const_int((op1 << ((op2 & 31) as u32)) as i32);
1061 substitute(function, unsafe { &mut *inst_ptr }, c);
1062 } else if is_const(1) && function.int_op(read(1)) == 0 {
1063 let op = read(0);
1064 substitute_with_truncated_uint(function, block, unsafe { &mut *inst_ptr }, op);
1065 }
1066 }
1067 IrCmd::BITRSHIFT_UINT => {
1068 if is_const(0) && is_const(1) {
1069 let op1 = function.int_op(read(0)) as u32;
1070 let op2 = function.int_op(read(1));
1071 let c = build.const_int((op1 >> ((op2 & 31) as u32)) as i32);
1072 substitute(function, unsafe { &mut *inst_ptr }, c);
1073 } else if is_const(1) && function.int_op(read(1)) == 0 {
1074 let op = read(0);
1075 substitute_with_truncated_uint(function, block, unsafe { &mut *inst_ptr }, op);
1076 }
1077 }
1078 IrCmd::BITARSHIFT_UINT => {
1079 if is_const(0) && is_const(1) {
1080 let op1 = function.int_op(read(0));
1081 let op2 = function.int_op(read(1));
1082 let c = build.const_int(op1 >> ((op2 & 31) as u32));
1084 substitute(function, unsafe { &mut *inst_ptr }, c);
1085 } else if is_const(1) && function.int_op(read(1)) == 0 {
1086 let op = read(0);
1087 substitute_with_truncated_uint(function, block, unsafe { &mut *inst_ptr }, op);
1088 }
1089 }
1090 IrCmd::BITLROTATE_UINT => {
1091 if is_const(0) && is_const(1) {
1092 let c = build.const_int(lrotate(
1093 function.int_op(read(0)) as u32,
1094 function.int_op(read(1)),
1095 ));
1096 substitute(function, unsafe { &mut *inst_ptr }, c);
1097 } else if is_const(1) && function.int_op(read(1)) == 0 {
1098 let op = read(0);
1099 substitute_with_truncated_uint(function, block, unsafe { &mut *inst_ptr }, op);
1100 }
1101 }
1102 IrCmd::BITRROTATE_UINT => {
1103 if is_const(0) && is_const(1) {
1104 let c = build.const_int(rrotate(
1105 function.int_op(read(0)) as u32,
1106 function.int_op(read(1)),
1107 ));
1108 substitute(function, unsafe { &mut *inst_ptr }, c);
1109 } else if is_const(1) && function.int_op(read(1)) == 0 {
1110 let op = read(0);
1111 substitute_with_truncated_uint(function, block, unsafe { &mut *inst_ptr }, op);
1112 }
1113 }
1114 IrCmd::BITCOUNTLZ_UINT => {
1115 if is_const(0) {
1116 let c = build.const_int(countlz_u32(function.int_op(read(0)) as u32));
1117 substitute(function, unsafe { &mut *inst_ptr }, c);
1118 }
1119 }
1120 IrCmd::BITCOUNTRZ_UINT => {
1121 if is_const(0) {
1122 let c = build.const_int(countrz_u32(function.int_op(read(0)) as u32));
1123 substitute(function, unsafe { &mut *inst_ptr }, c);
1124 }
1125 }
1126 IrCmd::CHECK_BUFFER_LEN => {
1127 if is_const(1) && is_const(4) {
1128 if (function.int_op(read(1)) as f64) == function.double_op(read(4)) {
1130 let u = build.undef();
1131 replace_ir_function_ir_op_ir_op(
1133 function,
1134 get_op_mut(unsafe { &mut *inst_ptr }, 4),
1135 u,
1136 );
1137 } else {
1138 let r = make_inst(IrCmd::JUMP, &[read(5)]); replace_ir_function_ir_block_u32_ir_inst(function, block, index, r);
1140 }
1141 } else if read(1).kind() == IrOpKind::Inst && is_const(4) {
1142 let e_op = read(4);
1144 let inner = function.inst_op(read(1));
1145 let ok = inner.cmd == IrCmd::NUM_TO_INT
1146 && inner.ops.as_slice().get(0).copied().unwrap_or_default() == e_op;
1147 CODEGEN_ASSERT!(ok);
1148
1149 let r = make_inst(IrCmd::JUMP, &[read(5)]); replace_ir_function_ir_block_u32_ir_inst(function, block, index, r);
1151 }
1152 }
1153 _ => {}
1154 }
1155}