1use crate::instruction_set::InstructionSet;
4use crate::linear::{bool_to_match_result, Action, Else, MatchOp, MatchResult};
5use crate::optimizations::PeepholeOptimizations;
6use crate::part::{Constant, Part};
7use crate::r#type::{BitWidth, Type};
8use crate::unquote::UnquoteOperator;
9use peepmatic_automata::State;
10use std::convert::TryFrom;
11use std::fmt::{self, Debug};
12use std::mem;
13use std::num::NonZeroU32;
14
15pub struct PeepholeOptimizer<'peep, 'ctx, TInstructionSet>
25where
26 TInstructionSet: InstructionSet<'ctx>,
27{
28 pub(crate) peep_opt: &'peep PeepholeOptimizations<TInstructionSet::Operator>,
29 pub(crate) instr_set: TInstructionSet,
30 pub(crate) left_hand_sides: Vec<Part<TInstructionSet::Instruction>>,
31 pub(crate) right_hand_sides: Vec<Part<TInstructionSet::Instruction>>,
32 pub(crate) actions: Vec<Action<TInstructionSet::Operator>>,
33 pub(crate) backtracking_states: Vec<(State, usize, usize)>,
34}
35
36impl<'peep, 'ctx, TInstructionSet> Debug for PeepholeOptimizer<'peep, 'ctx, TInstructionSet>
37where
38 TInstructionSet: InstructionSet<'ctx>,
39{
40 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
41 let PeepholeOptimizer {
42 peep_opt,
43 instr_set: _,
44 left_hand_sides,
45 right_hand_sides,
46 actions,
47 backtracking_states,
48 } = self;
49 f.debug_struct("PeepholeOptimizer")
50 .field("peep_opt", peep_opt)
51 .field("instr_set", &"_")
52 .field("left_hand_sides", left_hand_sides)
53 .field("right_hand_sides", right_hand_sides)
54 .field("actions", actions)
55 .field("backtracking_states", backtracking_states)
56 .finish()
57 }
58}
59
60impl<'peep, 'ctx, TInstructionSet> PeepholeOptimizer<'peep, 'ctx, TInstructionSet>
61where
62 TInstructionSet: InstructionSet<'ctx>,
63{
64 fn eval_unquote_1(&self, operator: UnquoteOperator, a: Constant) -> Constant {
65 use Constant::*;
66
67 macro_rules! map_int {
68 ( $c:expr , | $x:ident | $e:expr ) => {
69 match $c {
70 Int($x, w) => Int($e, w),
71 Bool(..) => panic!("not an integer"),
72 }
73 };
74 }
75
76 match operator {
77 UnquoteOperator::Log2 => map_int!(a, |x| x.trailing_zeros() as _),
78 UnquoteOperator::Neg => map_int!(a, |x| x.wrapping_neg()),
79 UnquoteOperator::Band
80 | UnquoteOperator::Bor
81 | UnquoteOperator::Bxor
82 | UnquoteOperator::Iadd
83 | UnquoteOperator::Imul
84 | UnquoteOperator::Isub => unreachable!("not a unary unquote operator: {:?}", operator),
85 }
86 }
87
88 fn eval_unquote_2(&self, operator: UnquoteOperator, a: Constant, b: Constant) -> Constant {
89 use Constant::*;
90
91 macro_rules! fold_ints {
92 ( $c1:expr , $c2:expr , | $x:ident , $y:ident | $e:expr ) => {
93 match ($c1, $c2) {
94 (Int($x, w1), Int($y, w2)) if w1 == w2 => Int($e, w1),
95 _ => panic!("not two integers of the same width"),
96 }
97 };
98 }
99
100 match operator {
101 UnquoteOperator::Band => fold_ints!(a, b, |x, y| x & y),
102 UnquoteOperator::Bor => fold_ints!(a, b, |x, y| x | y),
103 UnquoteOperator::Bxor => fold_ints!(a, b, |x, y| x ^ y),
104 UnquoteOperator::Iadd => fold_ints!(a, b, |x, y| x.wrapping_add(y)),
105 UnquoteOperator::Imul => fold_ints!(a, b, |x, y| x.wrapping_mul(y)),
106 UnquoteOperator::Isub => fold_ints!(a, b, |x, y| x.wrapping_sub(y)),
107 UnquoteOperator::Log2 | UnquoteOperator::Neg => {
108 unreachable!("not a binary unquote operator: {:?}", operator)
109 }
110 }
111 }
112
113 fn eval_actions(
114 &mut self,
115 context: &mut TInstructionSet::Context,
116 root: TInstructionSet::Instruction,
117 ) {
118 let mut actions = mem::replace(&mut self.actions, vec![]);
119
120 for action in actions.drain(..) {
121 log::trace!("Evaluating action: {:?}", action);
122 match action {
123 Action::GetLhs { lhs } => {
124 let lhs = self.left_hand_sides[lhs.0 as usize];
125 self.right_hand_sides.push(lhs);
126 }
127 Action::UnaryUnquote { operator, operand } => {
128 let operand = self.right_hand_sides[operand.0 as usize];
129 let operand = match operand {
130 Part::Instruction(i) => self
131 .instr_set
132 .instruction_to_constant(context, i)
133 .expect("cannot convert instruction to constant for unquote operand"),
134 Part::Constant(c) => c,
135 Part::ConditionCode(_) => {
136 panic!("cannot use a condition code as an unquote operand")
137 }
138 };
139 let result = self.eval_unquote_1(operator, operand);
140 self.right_hand_sides.push(result.into());
141 }
142 Action::BinaryUnquote { operator, operands } => {
143 let a = self.right_hand_sides[operands[0].0 as usize];
144 let a = match a {
145 Part::Instruction(i) => self
146 .instr_set
147 .instruction_to_constant(context, i)
148 .expect("cannot convert instruction to constant for unquote operand"),
149 Part::Constant(c) => c,
150 Part::ConditionCode(_) => {
151 panic!("cannot use a condition code as an unquote operand")
152 }
153 };
154
155 let b = self.right_hand_sides[operands[1].0 as usize];
156 let b = match b {
157 Part::Instruction(i) => self
158 .instr_set
159 .instruction_to_constant(context, i)
160 .expect("cannot convert instruction to constant for unquote operand"),
161 Part::Constant(c) => c,
162 Part::ConditionCode(_) => {
163 panic!("cannot use a condition code as an unquote operand")
164 }
165 };
166
167 let result = self.eval_unquote_2(operator, a, b);
168 self.right_hand_sides.push(result.into());
169 }
170 Action::MakeIntegerConst {
171 value,
172 mut bit_width,
173 } => {
174 let value = self.peep_opt.integers.lookup(value);
175 if bit_width.is_polymorphic() {
176 bit_width = BitWidth::try_from(
177 self.instr_set.instruction_result_bit_width(context, root),
178 )
179 .unwrap();
180 }
181 self.right_hand_sides
182 .push(Constant::Int(value, bit_width).into());
183 }
184 Action::MakeBooleanConst {
185 value,
186 mut bit_width,
187 } => {
188 if bit_width.is_polymorphic() {
189 bit_width = BitWidth::try_from(
190 self.instr_set.instruction_result_bit_width(context, root),
191 )
192 .unwrap();
193 }
194 self.right_hand_sides
195 .push(Constant::Bool(value, bit_width).into());
196 }
197 Action::MakeConditionCode { cc } => {
198 self.right_hand_sides.push(Part::ConditionCode(cc));
199 }
200 Action::MakeUnaryInst {
201 operator,
202 r#type:
203 Type {
204 kind,
205 mut bit_width,
206 },
207 operand,
208 } => {
209 if bit_width.is_polymorphic() {
210 bit_width = BitWidth::try_from(
211 self.instr_set.instruction_result_bit_width(context, root),
212 )
213 .unwrap();
214 }
215 let ty = Type { kind, bit_width };
216 let operand = self.right_hand_sides[operand.0 as usize];
217 let inst = self
218 .instr_set
219 .make_inst_1(context, root, operator, ty, operand);
220 self.right_hand_sides.push(Part::Instruction(inst));
221 }
222 Action::MakeBinaryInst {
223 operator,
224 r#type:
225 Type {
226 kind,
227 mut bit_width,
228 },
229 operands,
230 } => {
231 if bit_width.is_polymorphic() {
232 bit_width = BitWidth::try_from(
233 self.instr_set.instruction_result_bit_width(context, root),
234 )
235 .unwrap();
236 }
237 let ty = Type { kind, bit_width };
238 let a = self.right_hand_sides[operands[0].0 as usize];
239 let b = self.right_hand_sides[operands[1].0 as usize];
240 let inst = self
241 .instr_set
242 .make_inst_2(context, root, operator, ty, a, b);
243 self.right_hand_sides.push(Part::Instruction(inst));
244 }
245 Action::MakeTernaryInst {
246 operator,
247 r#type:
248 Type {
249 kind,
250 mut bit_width,
251 },
252 operands,
253 } => {
254 if bit_width.is_polymorphic() {
255 bit_width = BitWidth::try_from(
256 self.instr_set.instruction_result_bit_width(context, root),
257 )
258 .unwrap();
259 }
260 let ty = Type { kind, bit_width };
261 let a = self.right_hand_sides[operands[0].0 as usize];
262 let b = self.right_hand_sides[operands[1].0 as usize];
263 let c = self.right_hand_sides[operands[2].0 as usize];
264 let inst = self
265 .instr_set
266 .make_inst_3(context, root, operator, ty, a, b, c);
267 self.right_hand_sides.push(Part::Instruction(inst));
268 }
269 }
270 }
271
272 self.actions = actions;
274 }
275
276 fn eval_match_op(
277 &mut self,
278 context: &mut TInstructionSet::Context,
279 root: TInstructionSet::Instruction,
280 match_op: MatchOp,
281 ) -> MatchResult {
282 use crate::linear::MatchOp::*;
283
284 log::trace!("Evaluating match operation: {:?}", match_op);
285 let result: MatchResult = (|| match match_op {
286 Opcode(id) => {
287 let part = self.left_hand_sides[id.0 as usize];
288 let inst = part.as_instruction().ok_or(Else)?;
289 let op = self
290 .instr_set
291 .operator(context, inst, &mut self.left_hand_sides)
292 .ok_or(Else)?;
293 Ok(op.into())
294 }
295 IsConst(id) => {
296 let part = self.left_hand_sides[id.0 as usize];
297 let is_const = match part {
298 Part::Instruction(i) => {
299 self.instr_set.instruction_to_constant(context, i).is_some()
300 }
301 Part::ConditionCode(_) | Part::Constant(_) => true,
302 };
303 bool_to_match_result(is_const)
304 }
305 IsPowerOfTwo(id) => {
306 let part = self.left_hand_sides[id.0 as usize];
307 match part {
308 Part::Constant(c) => {
309 let is_pow2 = c.as_int().unwrap().is_power_of_two();
310 bool_to_match_result(is_pow2)
311 }
312 Part::Instruction(i) => {
313 let c = self
314 .instr_set
315 .instruction_to_constant(context, i)
316 .ok_or(Else)?;
317 let is_pow2 = c.as_int().unwrap().is_power_of_two();
318 bool_to_match_result(is_pow2)
319 }
320 Part::ConditionCode(_) => unreachable!("IsPowerOfTwo on a condition code"),
321 }
322 }
323 BitWidth(id) => {
324 let part = self.left_hand_sides[id.0 as usize];
325 let bit_width = match part {
326 Part::Instruction(i) => self.instr_set.instruction_result_bit_width(context, i),
327 Part::Constant(Constant::Int(_, w)) | Part::Constant(Constant::Bool(_, w)) => {
328 w.fixed_width().unwrap_or_else(|| {
329 self.instr_set.instruction_result_bit_width(context, root)
330 })
331 }
332 Part::ConditionCode(_) => panic!("BitWidth on condition code"),
333 };
334 debug_assert!(
335 bit_width != 0,
336 "`InstructionSet` implementors must uphold the contract that \
337 `instruction_result_bit_width` returns one of 1, 8, 16, 32, 64, or 128"
338 );
339 Ok(unsafe { NonZeroU32::new_unchecked(bit_width as u32) })
340 }
341 FitsInNativeWord(id) => {
342 let native_word_size = self.instr_set.native_word_size_in_bits(context);
343 debug_assert!(native_word_size.is_power_of_two());
344
345 let part = self.left_hand_sides[id.0 as usize];
346 let fits = match part {
347 Part::Instruction(i) => {
348 let size = self.instr_set.instruction_result_bit_width(context, i);
349 size <= native_word_size
350 }
351 Part::Constant(c) => {
352 let root_width = self.instr_set.instruction_result_bit_width(context, root);
353 let size = c.bit_width(root_width);
354 size <= native_word_size
355 }
356 Part::ConditionCode(_) => panic!("FitsInNativeWord on condition code"),
357 };
358 bool_to_match_result(fits)
359 }
360 Eq(a, b) => {
361 let part_a = self.left_hand_sides[a.0 as usize];
362 let part_b = self.left_hand_sides[b.0 as usize];
363 let eq = match (part_a, part_b) {
364 (Part::Instruction(inst), Part::Constant(c1))
365 | (Part::Constant(c1), Part::Instruction(inst)) => {
366 match self.instr_set.instruction_to_constant(context, inst) {
367 Some(c2) => c1 == c2,
368 None => false,
369 }
370 }
371 (a, b) => a == b,
372 };
373 bool_to_match_result(eq)
374 }
375 IntegerValue(id) => {
376 let part = self.left_hand_sides[id.0 as usize];
377 match part {
378 Part::Constant(c) => {
379 let x = c.as_int().ok_or(Else)?;
380 let id = self.peep_opt.integers.already_interned(x).ok_or(Else)?;
381 Ok(id.into())
382 }
383 Part::Instruction(i) => {
384 let c = self
385 .instr_set
386 .instruction_to_constant(context, i)
387 .ok_or(Else)?;
388 let x = c.as_int().ok_or(Else)?;
389 let id = self.peep_opt.integers.already_interned(x).ok_or(Else)?;
390 Ok(id.into())
391 }
392 Part::ConditionCode(_) => unreachable!("IntegerValue on condition code"),
393 }
394 }
395 BooleanValue(id) => {
396 let part = self.left_hand_sides[id.0 as usize];
397 match part {
398 Part::Constant(c) => {
399 let b = c.as_bool().ok_or(Else)?;
400 bool_to_match_result(b)
401 }
402 Part::Instruction(i) => {
403 let c = self
404 .instr_set
405 .instruction_to_constant(context, i)
406 .ok_or(Else)?;
407 let b = c.as_bool().ok_or(Else)?;
408 bool_to_match_result(b)
409 }
410 Part::ConditionCode(_) => unreachable!("IntegerValue on condition code"),
411 }
412 }
413 ConditionCode(id) => {
414 let part = self.left_hand_sides[id.0 as usize];
415 let cc = part.as_condition_code().ok_or(Else)?;
416 let cc = cc as u32;
417 debug_assert!(cc != 0);
418 Ok(unsafe { NonZeroU32::new_unchecked(cc) })
419 }
420 MatchOp::Nop => Err(Else),
421 })();
422 log::trace!("Evaluated match operation: {:?} = {:?}", match_op, result);
423 result
424 }
425
426 pub fn apply_one(
436 &mut self,
437 context: &mut TInstructionSet::Context,
438 root: TInstructionSet::Instruction,
439 ) -> Option<TInstructionSet::Instruction> {
440 log::trace!("PeepholeOptimizer::apply_one");
441
442 self.backtracking_states.clear();
443 self.actions.clear();
444 self.right_hand_sides.clear();
445 self.left_hand_sides.clear();
446
447 self.left_hand_sides.push(Part::Instruction(root));
449
450 let mut r#final = None;
451
452 let mut query = self.peep_opt.automata.query();
453 loop {
454 log::trace!("Current state: {:?}", query.current_state());
455 log::trace!(
456 "self.left_hand_sides = {:#?}",
457 self.left_hand_sides.iter().enumerate().collect::<Vec<_>>()
458 );
459
460 if query.is_in_final_state() {
461 log::trace!("Found a match at state {:?}", query.current_state());
468 r#final = Some((query.current_state(), self.actions.len()));
469 }
470
471 if query.has_transition_on(&Err(Else)) {
477 self.backtracking_states.push((
478 query.current_state(),
479 self.actions.len(),
480 self.left_hand_sides.len(),
481 ));
482 }
483
484 let match_op = match query.current_state_data() {
485 None => break,
486 Some(op) => op,
487 };
488
489 let input = self.eval_match_op(context, root, *match_op);
490
491 let actions = if let Some(actions) = query.next(&input) {
492 actions
493 } else if r#final.is_some() {
494 break;
495 } else if let Some((state, actions_len, lhs_len)) = self.backtracking_states.pop() {
496 query.go_to_state(state);
497 self.actions.truncate(actions_len);
498 self.left_hand_sides.truncate(lhs_len);
499 query
500 .next(&Err(Else))
501 .expect("backtracking states always have `Else` transitions")
502 } else {
503 break;
504 };
505
506 self.actions.extend(actions.iter().copied());
507 }
508
509 let (final_state, actions_len) = match r#final {
512 Some(f) => f,
513 None => {
514 log::trace!("No optimizations matched");
515 return None;
516 }
517 };
518
519 self.actions.truncate(actions_len);
523 query.go_to_state(final_state);
524 let final_actions = query.finish().expect("should be in a final state");
525 self.actions.extend(final_actions.iter().copied());
526 self.eval_actions(context, root);
527
528 let result = self.right_hand_sides.pop().unwrap();
532 let new_root = self.instr_set.replace_instruction(context, root, result);
533 Some(new_root)
534 }
535
536 pub fn apply_all(
539 &mut self,
540 context: &mut TInstructionSet::Context,
541 mut inst: TInstructionSet::Instruction,
542 ) {
543 loop {
544 if let Some(new_inst) = self.apply_one(context, inst) {
545 inst = new_inst;
546 } else {
547 break;
548 }
549 }
550 }
551}