1use crate::common::condition::Condition;
2use crate::common::mc::instr::MinecraftInstr;
3use crate::common::mc::modifier::{MIRModifier, StoreModLocation};
4use crate::common::ty::{DataTypeContents, NBTTypeContents, ScoreTypeContents};
5use crate::common::val::MutableValue;
6use crate::common::{val::Value, DeclareBinding};
7use crate::mir::{MIRBlock, MIRInstrKind};
8use crate::passes::opt::are_blocks_equivalent;
9use crate::passes::util::RunAgain;
10use crate::passes::{MIRPass, MIRPassData, Pass};
11use crate::project::{OptimizationLevel, ProjectSettings};
12use crate::util::{remove_indices, HashSetEmptyTracker, Only};
13
14use num_traits::Zero;
15
16pub struct MIRSimplifyPass;
17
18impl Pass for MIRSimplifyPass {
19 fn get_name(&self) -> &'static str {
20 "simplify_mir"
21 }
22
23 fn should_run(&self, proj: &ProjectSettings) -> bool {
24 proj.op_level >= OptimizationLevel::Basic
25 }
26}
27
28impl MIRPass for MIRSimplifyPass {
29 fn run_pass(&mut self, data: &mut MIRPassData) -> anyhow::Result<()> {
30 for func in data.mir.functions.values_mut() {
31 simplify_block(&mut func.block, true);
32 }
33
34 Ok(())
35 }
36}
37
38fn simplify_block(block: &mut MIRBlock, is_root: bool) -> RunAgain {
39 let mut out = RunAgain::new();
40 let mut instrs_to_remove = HashSetEmptyTracker::new();
41 loop {
42 let run_again = run_iter(block, &mut instrs_to_remove, is_root);
43 out.merge(run_again);
44 if !run_again {
45 break;
46 }
47 }
48 remove_indices(&mut block.contents, &instrs_to_remove);
49 out
50}
51
52fn run_iter(
53 block: &mut MIRBlock,
54 instrs_to_remove: &mut HashSetEmptyTracker<usize>,
55 is_root: bool,
56) -> RunAgain {
57 let mut run_again = RunAgain::new();
58
59 for (i, instr) in block.contents.iter_mut().enumerate() {
60 let remove = match &instr.kind {
61 MIRInstrKind::Assign {
64 left,
65 right: DeclareBinding::Value(Value::Mutable(right)),
66 }
67 | MIRInstrKind::Swap { left, right }
68 | MIRInstrKind::Min {
69 left,
70 right: Value::Mutable(right),
71 }
72 | MIRInstrKind::Max {
73 left,
74 right: Value::Mutable(right),
75 } if left.is_same_val(right) => true,
76 MIRInstrKind::Add {
78 right: Value::Constant(DataTypeContents::Score(val)),
79 ..
80 }
81 | MIRInstrKind::Sub {
82 right: Value::Constant(DataTypeContents::Score(val)),
83 ..
84 } if val.get_i32() == 0 => true,
85 MIRInstrKind::Mul {
87 left: _,
88 right: Value::Constant(DataTypeContents::Score(score)),
89 }
90 | MIRInstrKind::Div {
91 left: _,
92 right: Value::Constant(DataTypeContents::Score(score)),
93 } if score.get_i32() == 1 => true,
94 MIRInstrKind::Div {
96 left: _,
97 right: Value::Constant(DataTypeContents::Score(score)),
98 }
99 | MIRInstrKind::Mod {
100 left: _,
101 right: Value::Constant(DataTypeContents::Score(score)),
102 } if score.get_i32() == 0 => true,
103 MIRInstrKind::Or {
105 left: _,
106 right: Value::Constant(DataTypeContents::Score(score)),
107 } if score.get_i32() == 0 => true,
108 MIRInstrKind::And {
110 left: _,
111 right: Value::Constant(DataTypeContents::Score(score)),
112 } if score.get_i32() == 1 => true,
113 MIRInstrKind::And {
115 left,
116 right: Value::Mutable(right),
117 }
118 | MIRInstrKind::Or {
119 left,
120 right: Value::Mutable(right),
121 } if left.is_same_val(right) => true,
122 MIRInstrKind::MC(MinecraftInstr::AddTime { time }) if time.amount.is_zero() => true,
124 MIRInstrKind::MC(
125 MinecraftInstr::AddXP { amount, .. } | MinecraftInstr::TriggerAdd { amount, .. },
126 ) if amount.is_zero() => true,
127 MIRInstrKind::MC(MinecraftInstr::WorldBorderAdd { dist, .. }) if *dist == 0.0 => true,
128 MIRInstrKind::Merge {
130 right: Value::Constant(DataTypeContents::NBT(NBTTypeContents::Compound(_, comp))),
131 ..
132 } if comp.is_empty() => true,
133 MIRInstrKind::GetConst { .. }
136 | MIRInstrKind::Get { .. }
137 | MIRInstrKind::MC(
138 MinecraftInstr::GetAttribute { .. }
139 | MinecraftInstr::GetAttributeBase { .. }
140 | MinecraftInstr::GetAttributeModifier { .. }
141 | MinecraftInstr::GetDifficulty
142 | MinecraftInstr::GetGamerule { .. }
143 | MinecraftInstr::GetTime { .. }
144 | MinecraftInstr::GetXP { .. },
145 ) => is_root,
146 MIRInstrKind::If { body, .. } => body.contents.is_empty(),
148 MIRInstrKind::NoOp => true,
149 _ => false,
150 };
151
152 if remove {
153 let is_new = instrs_to_remove.insert(i);
154 if is_new {
155 run_again.yes();
156 }
157
158 continue;
159 }
160
161 let kind_repl = match &instr.kind {
163 MIRInstrKind::Assign {
165 left,
166 right: DeclareBinding::Condition(Condition::Bool(right)),
167 } => Some(MIRInstrKind::Assign {
168 left: left.clone(),
169 right: DeclareBinding::Value(right.clone()),
170 }),
171 MIRInstrKind::Div {
173 left,
174 right: Value::Constant(DataTypeContents::Score(score)),
175 } if score.get_i32() == -1 => Some(MIRInstrKind::Mul {
176 left: left.clone(),
177 right: Value::Constant(DataTypeContents::Score(ScoreTypeContents::Score(-1))),
178 }),
179 MIRInstrKind::Pow { base, exp: 0 } => Some(MIRInstrKind::Assign {
181 left: base.clone(),
182 right: DeclareBinding::Value(Value::Constant(DataTypeContents::Score(
183 ScoreTypeContents::Score(1),
184 ))),
185 }),
186 MIRInstrKind::Sub {
188 left,
189 right: Value::Mutable(right),
190 } if left.is_same_val(right) => Some(MIRInstrKind::Assign {
191 left: left.clone(),
192 right: DeclareBinding::Value(Value::Constant(DataTypeContents::Score(
193 ScoreTypeContents::Score(0),
194 ))),
195 }),
196 MIRInstrKind::Add {
198 left,
199 right: Value::Mutable(right),
200 } if left.is_same_val(right) => Some(MIRInstrKind::Mul {
201 left: left.clone(),
202 right: Value::Constant(DataTypeContents::Score(ScoreTypeContents::Score(2))),
203 }),
204 MIRInstrKind::Mul {
206 left,
207 right: Value::Mutable(right),
208 } if left.is_same_val(right) => Some(MIRInstrKind::Pow {
209 base: left.clone(),
210 exp: 2,
211 }),
212 MIRInstrKind::Merge {
214 left,
215 right: Value::Constant(DataTypeContents::NBT(NBTTypeContents::Compound(_, right))),
216 } if right.0.len() == 1 => {
217 let right = right.0.iter().next().expect("Len should be 1");
218 Some(MIRInstrKind::Assign {
219 left: MutableValue::Property(Box::new(left.clone()), right.0.clone()),
220 right: DeclareBinding::Value(Value::Constant(DataTypeContents::NBT(
221 right.1.clone(),
222 ))),
223 })
224 }
225 MIRInstrKind::And {
227 left,
228 right: Value::Constant(DataTypeContents::Score(score)),
229 } if score.get_i32() == 0 => Some(MIRInstrKind::Assign {
230 left: left.clone(),
231 right: DeclareBinding::Value(Value::Constant(DataTypeContents::Score(
232 ScoreTypeContents::Bool(false),
233 ))),
234 }),
235 MIRInstrKind::Or {
237 left,
238 right: Value::Constant(DataTypeContents::Score(score)),
239 } if score.get_i32() == 1 => Some(MIRInstrKind::Assign {
240 left: left.clone(),
241 right: DeclareBinding::Value(Value::Constant(DataTypeContents::Score(
242 ScoreTypeContents::Bool(true),
243 ))),
244 }),
245 MIRInstrKind::IfElse {
247 condition,
248 first,
249 second,
250 } if first.contents.is_empty() => Some(MIRInstrKind::If {
251 condition: Condition::Not(Box::new(condition.clone())),
252 body: second.clone(),
253 }),
254 MIRInstrKind::IfElse {
256 condition,
257 first,
258 second,
259 } if second.contents.is_empty() => Some(MIRInstrKind::If {
260 condition: condition.clone(),
261 body: first.clone(),
262 }),
263 MIRInstrKind::IfElse {
265 condition,
266 first,
267 second,
268 } if are_blocks_equivalent(first, second) => Some(MIRInstrKind::If {
269 condition: condition.clone(),
270 body: first.clone(),
271 }),
272 MIRInstrKind::If {
274 condition: Condition::Equal(Value::Mutable(left1), right1),
275 body,
276 } => match body.contents.only().map(|x| &x.kind) {
277 Some(MIRInstrKind::Assign {
278 left: left2,
279 right: DeclareBinding::Value(right2),
280 }) if left1.is_same_val(left2) && right1.is_value_eq(right2) => Some(MIRInstrKind::Assign {
281 left: left1.clone(),
282 right: DeclareBinding::Value(right1.clone()),
283 }),
284 _ => None,
285 },
286 MIRInstrKind::If {
288 condition: Condition::Not(condition),
289 body,
290 } => match condition.as_ref() {
291 Condition::Equal(Value::Mutable(left1), right1) => {
293 match body.contents.only().map(|x| &x.kind) {
294 Some(MIRInstrKind::Assign {
295 left: left2,
296 right: DeclareBinding::Value(right2),
297 }) if left1.is_same_val(left2) && right1.is_value_eq(right2) => Some(MIRInstrKind::Assign {
298 left: left1.clone(),
299 right: DeclareBinding::Value(right1.clone()),
300 }),
301 _ => None,
302 }
303 }
304 Condition::Bool(b) => match body.contents.only().map(|x| &x.kind) {
306 Some(
307 MIRInstrKind::Mul {
308 left,
309 right: Value::Constant(DataTypeContents::Score(val)),
310 }
311 | MIRInstrKind::Assign {
312 left,
313 right:
314 DeclareBinding::Value(Value::Constant(DataTypeContents::Score(val))),
315 },
316 ) if val.get_i32() == 0 => Some(MIRInstrKind::Mul {
317 left: left.clone(),
318 right: b.clone(),
319 }),
320 _ => None,
321 },
322 _ => None,
323 },
324 MIRInstrKind::If {
325 condition:
326 Condition::GreaterThan(Value::Mutable(left1), right1)
327 | Condition::GreaterThanOrEqual(Value::Mutable(left1), right1),
328 body,
329 } => {
330 match body.contents.only().map(|x| &x.kind) {
331 Some(MIRInstrKind::Assign {
333 left: left2,
334 right: DeclareBinding::Value(right2),
335 }) if left1.is_same_val(left2) && right1.is_value_eq(right2) => Some(MIRInstrKind::Min {
336 left: left1.clone(),
337 right: right1.clone(),
338 }),
339 _ => None,
340 }
341 }
342 MIRInstrKind::If {
343 condition:
344 Condition::LessThan(Value::Mutable(left1), right1)
345 | Condition::LessThanOrEqual(Value::Mutable(left1), right1),
346 body,
347 } => {
348 match body.contents.only().map(|x| &x.kind) {
349 Some(MIRInstrKind::Assign {
351 left: left2,
352 right: DeclareBinding::Value(right2),
353 }) if left1.is_same_val(left2) && right1.is_value_eq(right2) => Some(MIRInstrKind::Max {
354 left: left1.clone(),
355 right: right1.clone(),
356 }),
357 _ => None,
358 }
359 }
360 MIRInstrKind::If {
361 condition: Condition::Bool(b),
362 body,
363 } => match body.contents.only().map(|x| &x.kind) {
364 Some(MIRInstrKind::Add {
366 left,
367 right: Value::Constant(DataTypeContents::Score(val)),
368 }) if val.get_i32() == 1 => Some(MIRInstrKind::Add {
369 left: left.clone(),
370 right: b.clone(),
371 }),
372 Some(MIRInstrKind::Sub {
374 left,
375 right: Value::Constant(DataTypeContents::Score(val)),
376 }) if val.get_i32() == 1 => Some(MIRInstrKind::Sub {
377 left: left.clone(),
378 right: b.clone(),
379 }),
380 Some(MIRInstrKind::Assign {
382 left,
383 right: DeclareBinding::Value(Value::Constant(DataTypeContents::Score(val))),
384 }) if val.get_i32() == 1 => Some(MIRInstrKind::Or {
385 left: left.clone(),
386 right: b.clone(),
387 }),
388 _ => None,
389 },
390 MIRInstrKind::If {
391 condition: Condition::NotBool(b),
392 body,
393 } => match body.contents.only().map(|x| &x.kind) {
394 Some(MIRInstrKind::Assign {
396 left,
397 right: DeclareBinding::Value(Value::Constant(DataTypeContents::Score(val))),
398 }) if val.get_i32() == 0 => Some(MIRInstrKind::And {
399 left: left.clone(),
400 right: b.clone(),
401 }),
402 _ => None,
403 },
404 MIRInstrKind::Modify {
405 modifier: MIRModifier::StoreResult(StoreModLocation::Reg(left, left_scale)),
406 body,
407 } => {
408 match body.contents.only().map(|x| &x.kind) {
409 Some(MIRInstrKind::Get {
411 value: right,
412 scale: right_scale,
413 }) => {
414 if left_scale * right_scale == 1.0 {
415 Some(MIRInstrKind::Assign {
416 left: MutableValue::Reg(left.clone()),
417 right: DeclareBinding::Value(Value::Mutable(right.clone())),
418 })
419 } else {
420 None
421 }
422 }
423 _ => None,
424 }
425 }
426 MIRInstrKind::Modify {
427 modifier: MIRModifier::StoreSuccess(StoreModLocation::Reg(left, left_scale)),
428 body,
429 } if left_scale == &1.0 => {
430 match body.contents.only().map(|x| &x.kind) {
431 Some(MIRInstrKind::If { condition, body }) => {
433 match body.contents.only().map(|x| &x.kind) {
434 Some(MIRInstrKind::NoOp) | None => Some(MIRInstrKind::Assign {
435 left: MutableValue::Reg(left.clone()),
436 right: DeclareBinding::Condition(condition.clone()),
437 }),
438 _ => None,
439 }
440 }
441 _ => None,
442 }
443 }
444 _ => None,
445 };
446
447 if let Some(kind_repl) = kind_repl {
448 instr.kind = kind_repl;
449 run_again.yes();
450 }
451
452 if let Some(condition) = instr.kind.get_condition_mut() {
453 simplify_condition(condition, &mut run_again);
454 }
455
456 if let MIRInstrKind::If { condition, body } = &mut instr.kind {
458 if let Some(MIRInstrKind::If {
459 condition: condition2,
460 body: body2,
461 }) = body.contents.only().map(|x| &x.kind)
462 {
463 let condition2 = condition2.clone();
464 *body = body2.clone();
465 *condition = Condition::And(Box::new(condition.clone()), Box::new(condition2));
466 }
467 }
468
469 for body in instr.kind.get_bodies_mut() {
470 run_again.merge(simplify_block(body, false));
471 }
472 }
473
474 run_again
475}
476
477fn simplify_condition(condition: &mut Condition, run_again: &mut RunAgain) {
478 match condition {
479 Condition::Not(inner) => match inner.as_ref() {
480 Condition::Bool(b) => *condition = Condition::NotBool(b.clone()),
482 Condition::Not(inner) => {
484 *condition = *inner.clone();
485 run_again.yes();
486 }
487 _ => simplify_condition(inner, run_again),
488 },
489 Condition::And(l, r) => match (l.as_mut(), r.as_mut()) {
490 (Condition::Bool(Value::Constant(DataTypeContents::Score(b))), inner)
491 | (inner, Condition::Bool(Value::Constant(DataTypeContents::Score(b)))) => {
492 if b.get_i32() == 1 {
493 *condition = inner.clone();
494 } else {
495 *condition = Condition::Bool(Value::Constant(DataTypeContents::Score(
496 ScoreTypeContents::Bool(true),
497 )));
498 }
499 run_again.yes();
500 }
501 (Condition::NotBool(Value::Constant(DataTypeContents::Score(b))), inner)
502 | (inner, Condition::NotBool(Value::Constant(DataTypeContents::Score(b)))) => {
503 if b.get_i32() == 0 {
504 *condition = inner.clone();
505 } else {
506 *condition = Condition::Bool(Value::Constant(DataTypeContents::Score(
507 ScoreTypeContents::Bool(true),
508 )));
509 }
510 run_again.yes();
511 }
512 (Condition::Not(boxed), cond) | (cond, Condition::Not(boxed))
513 if boxed.as_ref() == cond =>
514 {
515 *condition = Condition::Bool(Value::Constant(DataTypeContents::Score(
516 ScoreTypeContents::Bool(false),
517 )));
518 run_again.yes();
519 }
520 (l, r) => {
521 if l == r {
522 *condition = l.clone();
523 run_again.yes();
524 } else {
525 simplify_condition(l, run_again);
526 simplify_condition(r, run_again);
527 }
528 }
529 },
530 Condition::Or(l, r) => match (l.as_mut(), r.as_mut()) {
531 (Condition::Bool(Value::Constant(DataTypeContents::Score(b))), inner)
532 | (inner, Condition::Bool(Value::Constant(DataTypeContents::Score(b)))) => {
533 if b.get_i32() == 1 {
534 *condition = Condition::Bool(Value::Constant(DataTypeContents::Score(
535 ScoreTypeContents::Bool(true),
536 )));
537 } else {
538 *condition = inner.clone();
539 }
540 run_again.yes();
541 }
542 (Condition::NotBool(Value::Constant(DataTypeContents::Score(b))), inner)
543 | (inner, Condition::NotBool(Value::Constant(DataTypeContents::Score(b)))) => {
544 if b.get_i32() == 0 {
545 *condition = Condition::Bool(Value::Constant(DataTypeContents::Score(
546 ScoreTypeContents::Bool(true),
547 )));
548 } else {
549 *condition = inner.clone();
550 }
551 run_again.yes();
552 }
553 (Condition::Not(boxed), cond) | (cond, Condition::Not(boxed))
554 if boxed.as_ref() == cond =>
555 {
556 *condition = Condition::Bool(Value::Constant(DataTypeContents::Score(
557 ScoreTypeContents::Bool(true),
558 )));
559 run_again.yes();
560 }
561 (l, r) => {
562 if l == r {
563 *condition = l.clone();
564 run_again.yes();
565 } else {
566 simplify_condition(l, run_again);
567 simplify_condition(r, run_again);
568 }
569 }
570 },
571 Condition::Xor(l, r) => {
572 if l == r {
573 *condition = Condition::Bool(Value::Constant(DataTypeContents::Score(
574 ScoreTypeContents::Bool(true),
575 )));
576 run_again.yes();
577 } else {
578 simplify_condition(l, run_again);
579 simplify_condition(r, run_again);
580 }
581 }
582 _ => {}
583 }
584}