xdy/
ir.rs

1//! # Intermediate representation
2//!
3//! The intermediate representation (IR) of `xDy` expresses a simple register
4//! transfer language. It supports [immediate](AddressingMode::Immediate) and
5//! [register](AddressingMode::Register) [addressing modes](AddressingMode).
6//! There are two types of register: [`i32`](RegisterIndex) and
7//! [rolling record](RollingRecordIndex). The
8//! [program counter](ProgramCounter) is a special register that holds the
9//! [function](Function)-relative index of the currently executing instruction.
10//! The result register is a special register that holds the result of the
11//! current evaluation; it is written by the [return](Return) instruction.
12//! The [evaluator](Evaluator) executes a target function's
13//! [instructions](Instruction) sequentially. There are no control flow
14//! instructions, making the IR purely functional.
15
16use std::{
17	cell::{Ref, RefCell},
18	collections::{BTreeSet, HashMap, HashSet},
19	fmt::{Display, Formatter}
20};
21
22#[cfg(feature = "serde")]
23use serde::{Deserialize, Serialize};
24
25#[cfg(doc)]
26use crate::{Evaluator, Function, RollingRecord};
27
28////////////////////////////////////////////////////////////////////////////////
29//                              Instruction set.                              //
30////////////////////////////////////////////////////////////////////////////////
31
32/// Instruction: Roll an inclusive range.
33#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
34#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
35pub struct RollRange
36{
37	/// The destination rolling record for the results.
38	pub dest: RollingRecordIndex,
39
40	/// The start of the range.
41	pub start: AddressingMode,
42
43	/// The end of the range.
44	pub end: AddressingMode
45}
46
47impl Display for RollRange
48{
49	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result
50	{
51		write!(f, "{} <- roll range {}:{}", self.dest, self.start, self.end)
52	}
53}
54
55/// Instruction: Roll a set of standard dice.
56#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
57#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
58pub struct RollStandardDice
59{
60	/// The destination rolling record for the results.
61	pub dest: RollingRecordIndex,
62
63	/// The number of dice to roll.
64	pub count: AddressingMode,
65
66	/// The number of faces on each die.
67	pub faces: AddressingMode
68}
69
70impl Display for RollStandardDice
71{
72	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result
73	{
74		write!(
75			f,
76			"{} <- roll standard dice {}D{}",
77			self.dest, self.count, self.faces
78		)
79	}
80}
81
82/// Instruction: Roll a set of custom dice.
83#[derive(Debug, Clone, Hash, PartialEq, Eq)]
84#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
85pub struct RollCustomDice
86{
87	/// The destination rolling record for the results.
88	pub dest: RollingRecordIndex,
89
90	/// The number of dice to roll.
91	pub count: AddressingMode,
92
93	/// The faces of each die in the homogeneous set.
94	pub faces: Vec<i32>
95}
96
97impl RollCustomDice
98{
99	/// Answer the number of distinct faces of the custom die.
100	///
101	/// # Returns
102	/// The number of distinct faces.
103	pub fn distinct_faces(&self) -> usize
104	{
105		self.faces.iter().collect::<HashSet<_>>().len()
106	}
107}
108
109impl Display for RollCustomDice
110{
111	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result
112	{
113		write!(f, "{} <- roll custom dice {}D[", self.dest, self.count)?;
114		let mut first = true;
115		for face in &self.faces
116		{
117			if !first
118			{
119				write!(f, ", ")?;
120			}
121			write!(f, "{}", face)?;
122			first = false;
123		}
124		write!(f, "]")
125	}
126}
127
128/// Instruction: Drop the lowest dice from a rolling record. This simply
129/// involves marking the lowest dice as dropped, so that they are not included
130/// in the sum computed by [SumRollingRecord]. A negative count will put back
131/// lowest dice that were previously dropped. The number of dice to drop is
132/// clamped to the number of dice in the record.
133#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
134#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
135pub struct DropLowest
136{
137	/// The [rolling record](RollingRecord) from which to drop dice.
138	pub dest: RollingRecordIndex,
139
140	/// The number of dice to drop.
141	pub count: AddressingMode
142}
143
144impl Display for DropLowest
145{
146	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result
147	{
148		write!(
149			f,
150			"{} <- drop lowest {} from {}",
151			self.dest, self.count, self.dest
152		)
153	}
154}
155
156/// Instruction: Drop the highest dice from a rolling record. This simply
157/// involves marking the highest dice as dropped, so that they are not included
158/// in the sum computed by [SumRollingRecord]. A negative count will put back
159/// highest dice that were previously dropped. The number of dice to drop is
160/// clamped to the number of dice in the record.
161#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
162#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
163pub struct DropHighest
164{
165	/// The [rolling record](RollingRecord) from which to drop dice.
166	pub dest: RollingRecordIndex,
167
168	/// The number of dice to drop.
169	pub count: AddressingMode
170}
171
172impl Display for DropHighest
173{
174	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result
175	{
176		write!(
177			f,
178			"{} <- drop highest {} from {}",
179			self.dest, self.count, self.dest
180		)
181	}
182}
183
184/// Instruction: Compute the result of a [rolling record](RollingRecord),
185/// ignoring any dice marked as dropped by [DropLowest] or [DropHighest].
186/// The summation is clamped to the range of an `i32`.
187#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
188#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
189pub struct SumRollingRecord
190{
191	/// The destination register for the result.
192	pub dest: RegisterIndex,
193
194	/// The source rolling record to compute.
195	pub src: RollingRecordIndex
196}
197
198impl Display for SumRollingRecord
199{
200	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result
201	{
202		write!(f, "{} <- sum rolling record {}", self.dest, self.src)
203	}
204}
205
206/// Instruction: Add two values together, saturating on overflow.
207#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
208#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
209pub struct Add
210{
211	/// The destination register for the result.
212	pub dest: RegisterIndex,
213
214	/// The first operand.
215	pub op1: AddressingMode,
216
217	/// The second operand.
218	pub op2: AddressingMode
219}
220
221impl Display for Add
222{
223	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result
224	{
225		write!(f, "{} <- {} + {}", self.dest, self.op1, self.op2)
226	}
227}
228
229/// Instruction: Subtract one value from another, saturating on overflow.
230#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
231#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
232pub struct Sub
233{
234	/// The destination register for the result.
235	pub dest: RegisterIndex,
236
237	/// The first operand.
238	pub op1: AddressingMode,
239
240	/// The second operand.
241	pub op2: AddressingMode
242}
243
244impl Display for Sub
245{
246	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result
247	{
248		write!(f, "{} <- {} - {}", self.dest, self.op1, self.op2)
249	}
250}
251
252/// Instruction: Multiply two values together, saturating on overflow.
253#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
254#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
255pub struct Mul
256{
257	/// The destination register for the result.
258	pub dest: RegisterIndex,
259
260	/// The first operand.
261	pub op1: AddressingMode,
262
263	/// The second operand.
264	pub op2: AddressingMode
265}
266
267impl Display for Mul
268{
269	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result
270	{
271		write!(f, "{} <- {} * {}", self.dest, self.op1, self.op2)
272	}
273}
274
275/// Instruction: Divide one value by another, saturating on overflow. Treat
276/// division by zero as zero.
277#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
278#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
279pub struct Div
280{
281	/// The destination register for the result.
282	pub dest: RegisterIndex,
283
284	/// The first operand.
285	pub op1: AddressingMode,
286
287	/// The second operand.
288	pub op2: AddressingMode
289}
290
291impl Display for Div
292{
293	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result
294	{
295		write!(f, "{} <- {} / {}", self.dest, self.op1, self.op2)
296	}
297}
298
299/// Instruction: Compute the remainder of dividing one value by another,
300/// saturating on overflow. Treat division by zero as zero.
301#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
302#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
303pub struct Mod
304{
305	/// The destination register for the result.
306	pub dest: RegisterIndex,
307
308	/// The first operand.
309	pub op1: AddressingMode,
310
311	/// The second operand.
312	pub op2: AddressingMode
313}
314
315impl Display for Mod
316{
317	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result
318	{
319		write!(f, "{} <- {} % {}", self.dest, self.op1, self.op2)
320	}
321}
322
323/// Instruction: Compute the exponentiation of one value by another,
324/// saturating on overflow.
325#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
326#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
327pub struct Exp
328{
329	/// The destination register for the result.
330	pub dest: RegisterIndex,
331
332	/// The first operand.
333	pub op1: AddressingMode,
334
335	/// The second operand.
336	pub op2: AddressingMode
337}
338
339impl Display for Exp
340{
341	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result
342	{
343		write!(f, "{} <- {} ^ {}", self.dest, self.op1, self.op2)
344	}
345}
346
347/// Instruction: Compute the negation of a value, saturating on overflow.
348#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
349#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
350pub struct Neg
351{
352	/// The destination register for the result.
353	pub dest: RegisterIndex,
354
355	/// The operand.
356	pub op: AddressingMode
357}
358
359impl Display for Neg
360{
361	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result
362	{
363		write!(f, "{} <- -{}", self.dest, self.op)
364	}
365}
366
367/// Instruction: Return a value from the function.
368#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
369#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
370pub struct Return
371{
372	/// The return value.
373	pub src: AddressingMode
374}
375
376impl Display for Return
377{
378	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result
379	{
380		write!(f, "return {}", self.src)
381	}
382}
383
384/// An instruction in the intermediate representation of the dice language.
385#[derive(Debug, Clone, Hash, PartialEq, Eq)]
386#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
387pub enum Instruction
388{
389	RollRange(RollRange),
390	RollStandardDice(RollStandardDice),
391	RollCustomDice(RollCustomDice),
392	DropLowest(DropLowest),
393	DropHighest(DropHighest),
394	SumRollingRecord(SumRollingRecord),
395	Add(Add),
396	Sub(Sub),
397	Mul(Mul),
398	Div(Div),
399	Mod(Mod),
400	Exp(Exp),
401	Neg(Neg),
402	Return(Return)
403}
404
405impl Instruction
406{
407	/// Get the destination for the instruction, if any.
408	///
409	/// # Returns
410	/// The destination, or `None` if the instruction has no destination.
411	pub fn destination(&self) -> Option<AddressingMode>
412	{
413		match self
414		{
415			Self::RollRange(inst) => Some(inst.dest.into()),
416			Self::RollStandardDice(inst) => Some(inst.dest.into()),
417			Self::RollCustomDice(inst) => Some(inst.dest.into()),
418			Self::DropLowest(inst) => Some(inst.dest.into()),
419			Self::DropHighest(inst) => Some(inst.dest.into()),
420			Self::SumRollingRecord(inst) => Some(inst.dest.into()),
421			Self::Add(inst) => Some(inst.dest.into()),
422			Self::Sub(inst) => Some(inst.dest.into()),
423			Self::Mul(inst) => Some(inst.dest.into()),
424			Self::Div(inst) => Some(inst.dest.into()),
425			Self::Mod(inst) => Some(inst.dest.into()),
426			Self::Exp(inst) => Some(inst.dest.into()),
427			Self::Neg(inst) => Some(inst.dest.into()),
428			Self::Return(_) => None
429		}
430	}
431
432	/// Get the sources for the instruction, if any.
433	///
434	/// # Returns
435	/// The sources, or an empty vector if the instruction has no sources.
436	pub fn sources(&self) -> Vec<AddressingMode>
437	{
438		match self
439		{
440			Self::RollRange(inst) => vec![inst.start, inst.end],
441			Self::RollStandardDice(inst) => vec![inst.count, inst.faces],
442			Self::RollCustomDice(inst) => vec![inst.count],
443			Self::DropLowest(inst) => vec![inst.dest.into(), inst.count],
444			Self::DropHighest(inst) => vec![inst.dest.into(), inst.count],
445			Self::SumRollingRecord(inst) => vec![inst.src.into()],
446			Self::Add(inst) => vec![inst.op1, inst.op2],
447			Self::Sub(inst) => vec![inst.op1, inst.op2],
448			Self::Mul(inst) => vec![inst.op1, inst.op2],
449			Self::Div(inst) => vec![inst.op1, inst.op2],
450			Self::Mod(inst) => vec![inst.op1, inst.op2],
451			Self::Exp(inst) => vec![inst.op1, inst.op2],
452			Self::Neg(inst) => vec![inst.op],
453			Self::Return(inst) => vec![inst.src]
454		}
455	}
456
457	/// Create an instruction to roll a range of dice.
458	///
459	/// # Parameters
460	/// - `dest`: The destination rolling record.
461	/// - `start`: The start of the range.
462	/// - `end`: The end of the range.
463	///
464	/// # Returns
465	/// The instruction.
466	#[inline]
467	pub const fn roll_range(
468		dest: RollingRecordIndex,
469		start: AddressingMode,
470		end: AddressingMode
471	) -> Self
472	{
473		Self::RollRange(RollRange { dest, start, end })
474	}
475
476	/// Create an instruction to roll standard dice.
477	///
478	/// # Parameters
479	/// - `dest`: The destination rolling record.
480	/// - `count`: The number of dice to roll.
481	/// - `faces`: The number of faces on each die.
482	///
483	/// # Returns
484	/// The instruction.
485	#[inline]
486	pub const fn roll_standard_dice(
487		dest: RollingRecordIndex,
488		count: AddressingMode,
489		faces: AddressingMode
490	) -> Self
491	{
492		Self::RollStandardDice(RollStandardDice { dest, count, faces })
493	}
494
495	/// Create an instruction to roll custom dice.
496	///
497	/// # Parameters
498	/// - `dest`: The destination rolling record.
499	/// - `count`: The number of dice to roll.
500	/// - `faces`: The number of faces on each die.
501	///
502	/// # Returns
503	/// The instruction.
504	#[inline]
505	pub const fn roll_custom_dice(
506		dest: RollingRecordIndex,
507		count: AddressingMode,
508		faces: Vec<i32>
509	) -> Self
510	{
511		Self::RollCustomDice(RollCustomDice { dest, count, faces })
512	}
513
514	/// Create an instruction to drop the lowest value from a rolling record.
515	///
516	/// # Parameters
517	/// - `dest`: The destination rolling record.
518	/// - `count`: The number of dice to drop.
519	///
520	/// # Returns
521	/// The instruction.
522	#[inline]
523	pub const fn drop_lowest(
524		dest: RollingRecordIndex,
525		count: AddressingMode
526	) -> Self
527	{
528		Self::DropLowest(DropLowest { dest, count })
529	}
530
531	/// Create an instruction to drop the highest value from a rolling record.
532	///
533	/// # Parameters
534	/// - `dest`: The destination rolling record.
535	/// - `count`: The number of dice to drop.
536	///
537	/// # Returns
538	/// The instruction.
539	#[inline]
540	pub const fn drop_highest(
541		dest: RollingRecordIndex,
542		count: AddressingMode
543	) -> Self
544	{
545		Self::DropHighest(DropHighest { dest, count })
546	}
547
548	/// Create an instruction to sum the values in a rolling record.
549	///
550	/// # Parameters
551	/// - `dest`: The destination register.
552	/// - `src`: The rolling record to sum.
553	///
554	/// # Returns
555	/// The instruction.
556	#[inline]
557	pub const fn sum_rolling_record(
558		dest: RegisterIndex,
559		src: RollingRecordIndex
560	) -> Self
561	{
562		Self::SumRollingRecord(SumRollingRecord { dest, src })
563	}
564
565	/// Create an instruction to add two values.
566	///
567	/// # Parameters
568	/// - `dest`: The destination register.
569	/// - `op1`: The first operand.
570	/// - `op2`: The second operand.
571	///
572	/// # Returns
573	/// The instruction.
574	#[inline]
575	pub const fn add(
576		dest: RegisterIndex,
577		op1: AddressingMode,
578		op2: AddressingMode
579	) -> Self
580	{
581		Self::Add(Add { dest, op1, op2 })
582	}
583
584	/// Create an instruction to subtract two values.
585	///
586	/// # Parameters
587	/// - `dest`: The destination register.
588	/// - `op1`: The first operand.
589	/// - `op2`: The second operand.
590	///
591	/// # Returns
592	/// The instruction.
593	#[inline]
594	pub const fn sub(
595		dest: RegisterIndex,
596		op1: AddressingMode,
597		op2: AddressingMode
598	) -> Self
599	{
600		Self::Sub(Sub { dest, op1, op2 })
601	}
602
603	/// Create an instruction to multiply two values.
604	///
605	/// # Parameters
606	/// - `dest`: The destination register.
607	/// - `op1`: The first operand.
608	/// - `op2`: The second operand.
609	///
610	/// # Returns
611	/// The instruction.
612	#[inline]
613	pub const fn mul(
614		dest: RegisterIndex,
615		op1: AddressingMode,
616		op2: AddressingMode
617	) -> Self
618	{
619		Self::Mul(Mul { dest, op1, op2 })
620	}
621
622	/// Create an instruction to divide two values.
623	///
624	/// # Parameters
625	/// - `dest`: The destination register.
626	/// - `op1`: The first operand.
627	/// - `op2`: The second operand.
628	///
629	/// # Returns
630	/// The instruction.
631	#[inline]
632	pub const fn div(
633		dest: RegisterIndex,
634		op1: AddressingMode,
635		op2: AddressingMode
636	) -> Self
637	{
638		Self::Div(Div { dest, op1, op2 })
639	}
640
641	/// Create an instruction to compute the remainder of two values.
642	///
643	/// # Parameters
644	/// - `dest`: The destination register.
645	/// - `op1`: The first operand.
646	/// - `op2`: The second operand.
647	///
648	/// # Returns
649	/// The instruction.
650	#[inline]
651	pub const fn r#mod(
652		dest: RegisterIndex,
653		op1: AddressingMode,
654		op2: AddressingMode
655	) -> Self
656	{
657		Self::Mod(Mod { dest, op1, op2 })
658	}
659
660	/// Create an instruction to compute the exponentiation of two values.
661	///
662	/// # Parameters
663	/// - `dest`: The destination register.
664	/// - `op1`: The first operand.
665	/// - `op2`: The second operand.
666	///
667	/// # Returns
668	/// The instruction.
669	#[inline]
670	pub const fn exp(
671		dest: RegisterIndex,
672		op1: AddressingMode,
673		op2: AddressingMode
674	) -> Self
675	{
676		Self::Exp(Exp { dest, op1, op2 })
677	}
678
679	/// Create an instruction to negate a value.
680	///
681	/// # Parameters
682	/// - `dest`: The destination register.
683	/// - `op`: The source operand.
684	///
685	/// # Returns
686	/// The instruction.
687	#[inline]
688	pub const fn neg(dest: RegisterIndex, op: AddressingMode) -> Self
689	{
690		Self::Neg(Neg { dest, op })
691	}
692
693	/// Create an instruction to return a value from the function.
694	///
695	/// # Parameters
696	/// - `src`: The return value.
697	///
698	/// # Returns
699	/// The instruction.
700	#[inline]
701	pub const fn r#return(src: AddressingMode) -> Self
702	{
703		Self::Return(Return { src })
704	}
705}
706
707impl Display for Instruction
708{
709	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result
710	{
711		match self
712		{
713			Self::RollRange(inst) => write!(f, "{}", inst),
714			Self::RollStandardDice(inst) => write!(f, "{}", inst),
715			Self::RollCustomDice(inst) => write!(f, "{}", inst),
716			Self::DropLowest(inst) => write!(f, "{}", inst),
717			Self::DropHighest(inst) => write!(f, "{}", inst),
718			Self::SumRollingRecord(inst) => write!(f, "{}", inst),
719			Self::Add(inst) => write!(f, "{}", inst),
720			Self::Sub(inst) => write!(f, "{}", inst),
721			Self::Mul(inst) => write!(f, "{}", inst),
722			Self::Div(inst) => write!(f, "{}", inst),
723			Self::Mod(inst) => write!(f, "{}", inst),
724			Self::Exp(inst) => write!(f, "{}", inst),
725			Self::Neg(inst) => write!(f, "{}", inst),
726			Self::Return(inst) => write!(f, "{}", inst)
727		}
728	}
729}
730
731impl From<RollRange> for Instruction
732{
733	fn from(inst: RollRange) -> Self { Self::RollRange(inst) }
734}
735
736impl From<RollStandardDice> for Instruction
737{
738	fn from(inst: RollStandardDice) -> Self { Self::RollStandardDice(inst) }
739}
740
741impl From<RollCustomDice> for Instruction
742{
743	fn from(inst: RollCustomDice) -> Self { Self::RollCustomDice(inst) }
744}
745
746impl From<DropLowest> for Instruction
747{
748	fn from(inst: DropLowest) -> Self { Self::DropLowest(inst) }
749}
750
751impl From<DropHighest> for Instruction
752{
753	fn from(inst: DropHighest) -> Self { Self::DropHighest(inst) }
754}
755
756impl From<SumRollingRecord> for Instruction
757{
758	fn from(inst: SumRollingRecord) -> Self { Self::SumRollingRecord(inst) }
759}
760
761impl From<Add> for Instruction
762{
763	fn from(inst: Add) -> Self { Self::Add(inst) }
764}
765
766impl From<Sub> for Instruction
767{
768	fn from(inst: Sub) -> Self { Self::Sub(inst) }
769}
770
771impl From<Mul> for Instruction
772{
773	fn from(inst: Mul) -> Self { Self::Mul(inst) }
774}
775
776impl From<Div> for Instruction
777{
778	fn from(inst: Div) -> Self { Self::Div(inst) }
779}
780
781impl From<Mod> for Instruction
782{
783	fn from(inst: Mod) -> Self { Self::Mod(inst) }
784}
785
786impl From<Exp> for Instruction
787{
788	fn from(inst: Exp) -> Self { Self::Exp(inst) }
789}
790
791impl From<Neg> for Instruction
792{
793	fn from(inst: Neg) -> Self { Self::Neg(inst) }
794}
795
796impl From<Return> for Instruction
797{
798	fn from(inst: Return) -> Self { Self::Return(inst) }
799}
800
801impl TryFrom<Instruction> for RollRange
802{
803	type Error = ();
804
805	fn try_from(inst: Instruction) -> Result<Self, Self::Error>
806	{
807		match inst
808		{
809			Instruction::RollRange(inst) => Ok(inst),
810			_ => Err(())
811		}
812	}
813}
814
815impl TryFrom<Instruction> for RollStandardDice
816{
817	type Error = ();
818
819	fn try_from(inst: Instruction) -> Result<Self, Self::Error>
820	{
821		match inst
822		{
823			Instruction::RollStandardDice(inst) => Ok(inst),
824			_ => Err(())
825		}
826	}
827}
828
829impl TryFrom<Instruction> for RollCustomDice
830{
831	type Error = ();
832
833	fn try_from(inst: Instruction) -> Result<Self, Self::Error>
834	{
835		match inst
836		{
837			Instruction::RollCustomDice(inst) => Ok(inst),
838			_ => Err(())
839		}
840	}
841}
842
843impl TryFrom<Instruction> for DropLowest
844{
845	type Error = ();
846
847	fn try_from(inst: Instruction) -> Result<Self, Self::Error>
848	{
849		match inst
850		{
851			Instruction::DropLowest(inst) => Ok(inst),
852			_ => Err(())
853		}
854	}
855}
856
857impl TryFrom<Instruction> for DropHighest
858{
859	type Error = ();
860
861	fn try_from(inst: Instruction) -> Result<Self, Self::Error>
862	{
863		match inst
864		{
865			Instruction::DropHighest(inst) => Ok(inst),
866			_ => Err(())
867		}
868	}
869}
870
871impl TryFrom<Instruction> for SumRollingRecord
872{
873	type Error = ();
874
875	fn try_from(inst: Instruction) -> Result<Self, Self::Error>
876	{
877		match inst
878		{
879			Instruction::SumRollingRecord(inst) => Ok(inst),
880			_ => Err(())
881		}
882	}
883}
884
885impl TryFrom<Instruction> for Add
886{
887	type Error = ();
888
889	fn try_from(inst: Instruction) -> Result<Self, Self::Error>
890	{
891		match inst
892		{
893			Instruction::Add(inst) => Ok(inst),
894			_ => Err(())
895		}
896	}
897}
898
899impl TryFrom<Instruction> for Sub
900{
901	type Error = ();
902
903	fn try_from(inst: Instruction) -> Result<Self, Self::Error>
904	{
905		match inst
906		{
907			Instruction::Sub(inst) => Ok(inst),
908			_ => Err(())
909		}
910	}
911}
912
913impl TryFrom<Instruction> for Mul
914{
915	type Error = ();
916
917	fn try_from(inst: Instruction) -> Result<Self, Self::Error>
918	{
919		match inst
920		{
921			Instruction::Mul(inst) => Ok(inst),
922			_ => Err(())
923		}
924	}
925}
926
927impl TryFrom<Instruction> for Div
928{
929	type Error = ();
930
931	fn try_from(inst: Instruction) -> Result<Self, Self::Error>
932	{
933		match inst
934		{
935			Instruction::Div(inst) => Ok(inst),
936			_ => Err(())
937		}
938	}
939}
940
941impl TryFrom<Instruction> for Mod
942{
943	type Error = ();
944
945	fn try_from(inst: Instruction) -> Result<Self, Self::Error>
946	{
947		match inst
948		{
949			Instruction::Mod(inst) => Ok(inst),
950			_ => Err(())
951		}
952	}
953}
954
955impl TryFrom<Instruction> for Exp
956{
957	type Error = ();
958
959	fn try_from(inst: Instruction) -> Result<Self, Self::Error>
960	{
961		match inst
962		{
963			Instruction::Exp(inst) => Ok(inst),
964			_ => Err(())
965		}
966	}
967}
968
969impl TryFrom<Instruction> for Neg
970{
971	type Error = ();
972
973	fn try_from(inst: Instruction) -> Result<Self, Self::Error>
974	{
975		match inst
976		{
977			Instruction::Neg(inst) => Ok(inst),
978			_ => Err(())
979		}
980	}
981}
982
983impl TryFrom<Instruction> for Return
984{
985	type Error = ();
986
987	fn try_from(inst: Instruction) -> Result<Self, Self::Error>
988	{
989		match inst
990		{
991			Instruction::Return(inst) => Ok(inst),
992			_ => Err(())
993		}
994	}
995}
996
997////////////////////////////////////////////////////////////////////////////////
998//                             Addressing modes.                              //
999////////////////////////////////////////////////////////////////////////////////
1000
1001/// The addressing mode for instructions. An addressing mode specifies how to
1002/// access the operand of an instruction. The operand can be an immediate
1003/// constant or a register. Registers are identified by their index into the
1004/// frame's register set.
1005#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1006#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1007pub enum AddressingMode
1008{
1009	/// The addressing mode is an immediate constant.
1010	Immediate(Immediate),
1011
1012	/// The addressing mode is a register.
1013	Register(RegisterIndex),
1014
1015	/// The addressing mode is a rolling record.
1016	RollingRecord(RollingRecordIndex)
1017}
1018
1019impl Display for AddressingMode
1020{
1021	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result
1022	{
1023		match self
1024		{
1025			Self::Immediate(imm) => write!(f, "{}", imm),
1026			Self::Register(reg) => write!(f, "{}", reg),
1027			Self::RollingRecord(rec) => write!(f, "{}", rec)
1028		}
1029	}
1030}
1031
1032/// An immediate constant value.
1033#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1034#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1035pub struct Immediate(pub i32);
1036
1037impl Display for Immediate
1038{
1039	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result
1040	{
1041		write!(f, "{}", self.0)
1042	}
1043}
1044
1045impl std::ops::Add for Immediate
1046{
1047	type Output = Self;
1048
1049	fn add(self, rhs: Self) -> Self::Output { Self(self.0 + rhs.0) }
1050}
1051
1052impl std::ops::Sub for Immediate
1053{
1054	type Output = Self;
1055
1056	fn sub(self, rhs: Self) -> Self::Output { Self(self.0 - rhs.0) }
1057}
1058
1059impl std::ops::Mul for Immediate
1060{
1061	type Output = Self;
1062
1063	fn mul(self, rhs: Self) -> Self::Output { Self(self.0 * rhs.0) }
1064}
1065
1066impl std::ops::Div for Immediate
1067{
1068	type Output = Self;
1069
1070	fn div(self, rhs: Self) -> Self::Output { Self(self.0 / rhs.0) }
1071}
1072
1073impl std::ops::Rem for Immediate
1074{
1075	type Output = Self;
1076
1077	fn rem(self, rhs: Self) -> Self::Output { Self(self.0 % rhs.0) }
1078}
1079
1080impl std::ops::Neg for Immediate
1081{
1082	type Output = Self;
1083
1084	fn neg(self) -> Self::Output { Self(-self.0) }
1085}
1086
1087impl From<usize> for Immediate
1088{
1089	fn from(immediate: usize) -> Self { Self(immediate as i32) }
1090}
1091
1092impl From<Immediate> for i32
1093{
1094	fn from(immediate: Immediate) -> Self { immediate.0 }
1095}
1096
1097impl From<Immediate> for AddressingMode
1098{
1099	fn from(immediate: Immediate) -> Self { Self::Immediate(immediate) }
1100}
1101
1102/// The index of a register in the frame's register set.
1103#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
1104#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1105pub struct RegisterIndex(pub usize);
1106
1107impl Display for RegisterIndex
1108{
1109	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result
1110	{
1111		write!(f, "@{}", self.0)
1112	}
1113}
1114
1115impl From<usize> for RegisterIndex
1116{
1117	fn from(reg: usize) -> Self { Self(reg) }
1118}
1119
1120impl From<RegisterIndex> for usize
1121{
1122	fn from(reg: RegisterIndex) -> Self { reg.0 }
1123}
1124
1125impl From<RegisterIndex> for AddressingMode
1126{
1127	fn from(reg: RegisterIndex) -> Self { Self::Register(reg) }
1128}
1129
1130/// The index of a rolling record in the frame's rolling record set.
1131#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
1132#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1133pub struct RollingRecordIndex(pub usize);
1134
1135impl Display for RollingRecordIndex
1136{
1137	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result
1138	{
1139		write!(f, "⚅{}", self.0)
1140	}
1141}
1142
1143impl From<usize> for RollingRecordIndex
1144{
1145	fn from(rec: usize) -> Self { Self(rec) }
1146}
1147
1148impl From<RollingRecordIndex> for usize
1149{
1150	fn from(rec: RollingRecordIndex) -> Self { rec.0 }
1151}
1152
1153impl From<RollingRecordIndex> for AddressingMode
1154{
1155	fn from(rec: RollingRecordIndex) -> Self { Self::RollingRecord(rec) }
1156}
1157
1158impl TryFrom<AddressingMode> for Immediate
1159{
1160	type Error = ();
1161
1162	fn try_from(value: AddressingMode) -> Result<Self, Self::Error>
1163	{
1164		match value
1165		{
1166			AddressingMode::Immediate(imm) => Ok(imm),
1167			_ => Err(())
1168		}
1169	}
1170}
1171
1172impl TryFrom<AddressingMode> for RegisterIndex
1173{
1174	type Error = ();
1175
1176	fn try_from(value: AddressingMode) -> Result<Self, Self::Error>
1177	{
1178		match value
1179		{
1180			AddressingMode::Register(reg) => Ok(reg),
1181			_ => Err(())
1182		}
1183	}
1184}
1185
1186impl TryFrom<AddressingMode> for RollingRecordIndex
1187{
1188	type Error = ();
1189
1190	fn try_from(value: AddressingMode) -> Result<Self, Self::Error>
1191	{
1192		match value
1193		{
1194			AddressingMode::RollingRecord(rec) => Ok(rec),
1195			_ => Err(())
1196		}
1197	}
1198}
1199
1200/// A program counter, which references an instruction in a compiled function.
1201/// As there are no control flow instructions in the dice language, the program
1202/// counter is not really an addressing mode, per se, but is still useful for
1203/// various tools.
1204#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
1205pub struct ProgramCounter(pub usize);
1206
1207impl Display for ProgramCounter
1208{
1209	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result
1210	{
1211		write!(f, "*{}", self.0)
1212	}
1213}
1214
1215impl From<usize> for ProgramCounter
1216{
1217	fn from(pc: usize) -> Self { Self(pc) }
1218}
1219
1220impl From<ProgramCounter> for usize
1221{
1222	fn from(pc: ProgramCounter) -> Self { pc.0 }
1223}
1224
1225/// Provides the ability to allocate a new instance of the receiver. This is
1226/// useful for generating new register and rolling record indices.
1227pub trait CanAllocate
1228{
1229	/// Allocate a new instance of the receiver.
1230	///
1231	/// # Returns
1232	/// The newly allocated instance.
1233	fn allocate(&mut self) -> Self;
1234}
1235
1236impl CanAllocate for RegisterIndex
1237{
1238	fn allocate(&mut self) -> Self
1239	{
1240		let register = *self;
1241		self.0 += 1;
1242		register
1243	}
1244}
1245
1246impl CanAllocate for RollingRecordIndex
1247{
1248	fn allocate(&mut self) -> Self
1249	{
1250		let record = *self;
1251		self.0 += 1;
1252		record
1253	}
1254}
1255
1256impl CanAllocate for ProgramCounter
1257{
1258	fn allocate(&mut self) -> Self
1259	{
1260		let pc = *self;
1261		self.0 += 1;
1262		pc
1263	}
1264}
1265
1266////////////////////////////////////////////////////////////////////////////////
1267//                          Instruction visitation.                           //
1268////////////////////////////////////////////////////////////////////////////////
1269
1270/// A visitor for instructions in the intermediate representation of the dice
1271/// language.
1272///
1273/// # Type Parameters
1274/// - `E`: The type of error that the visitor may return.
1275pub trait InstructionVisitor<E>
1276{
1277	/// Visit a range roll instruction.
1278	///
1279	/// # Parameters
1280	/// - `inst`: The instruction to visit.
1281	///
1282	/// # Errors
1283	/// An error if the visitor fails to visit the instruction.
1284	fn visit_roll_range(&mut self, inst: &RollRange) -> Result<(), E>;
1285
1286	/// Visit a standard dice roll instruction.
1287	///
1288	/// # Parameters
1289	/// - `inst`: The instruction to visit.
1290	///
1291	/// # Errors
1292	/// An error if the visitor fails to visit the instruction.
1293	fn visit_roll_standard_dice(
1294		&mut self,
1295		inst: &RollStandardDice
1296	) -> Result<(), E>;
1297
1298	/// Visit a custom dice roll instruction.
1299	///
1300	/// # Parameters
1301	/// - `inst`: The instruction to visit.
1302	///
1303	/// # Errors
1304	/// An error if the visitor fails to visit the instruction.
1305	fn visit_roll_custom_dice(
1306		&mut self,
1307		inst: &RollCustomDice
1308	) -> Result<(), E>;
1309
1310	/// Visit a drop lowest instruction.
1311	///
1312	/// # Parameters
1313	/// - `inst`: The instruction to visit.
1314	///
1315	/// # Errors
1316	/// An error if the visitor fails to visit the instruction.
1317	fn visit_drop_lowest(&mut self, inst: &DropLowest) -> Result<(), E>;
1318
1319	/// Visit a drop highest instruction.
1320	///
1321	/// # Parameters
1322	/// - `inst`: The instruction to visit.
1323	///
1324	/// # Errors
1325	/// An error if the visitor fails to visit the instruction.
1326	fn visit_drop_highest(&mut self, inst: &DropHighest) -> Result<(), E>;
1327
1328	/// Visit a sum rolling record instruction.
1329	///
1330	/// # Parameters
1331	/// - `inst`: The instruction to visit.
1332	///
1333	/// # Errors
1334	/// An error if the visitor fails to visit the instruction.
1335	fn visit_sum_rolling_record(
1336		&mut self,
1337		inst: &SumRollingRecord
1338	) -> Result<(), E>;
1339
1340	/// Visit an addition instruction.
1341	///
1342	/// # Parameters
1343	/// - `inst`: The instruction to visit.
1344	///
1345	/// # Errors
1346	/// An error if the visitor fails to visit the instruction.
1347	fn visit_add(&mut self, inst: &Add) -> Result<(), E>;
1348
1349	/// Visit a subtraction instruction.
1350	///
1351	/// # Parameters
1352	/// - `inst`: The instruction to visit.
1353	///
1354	/// # Errors
1355	/// An error if the visitor fails to visit the instruction.
1356	fn visit_sub(&mut self, inst: &Sub) -> Result<(), E>;
1357
1358	/// Visit a multiplication instruction.
1359	///
1360	/// # Parameters
1361	/// - `inst`: The instruction to visit.
1362	///
1363	/// # Errors
1364	/// An error if the visitor fails to visit the instruction.
1365	fn visit_mul(&mut self, inst: &Mul) -> Result<(), E>;
1366
1367	/// Visit a division instruction.
1368	///
1369	/// # Parameters
1370	/// - `inst`: The instruction to visit.
1371	///
1372	/// # Errors
1373	/// An error if the visitor fails to visit the instruction.
1374	fn visit_div(&mut self, inst: &Div) -> Result<(), E>;
1375
1376	/// Visit a modulo instruction.
1377	///
1378	/// # Parameters
1379	/// - `inst`: The instruction to visit.
1380	///
1381	/// # Errors
1382	/// An error if the visitor fails to visit the instruction.
1383	fn visit_mod(&mut self, inst: &Mod) -> Result<(), E>;
1384
1385	/// Visit an exponentiation instruction.
1386	///
1387	/// # Parameters
1388	/// - `inst`: The instruction to visit.
1389	///
1390	/// # Errors
1391	/// An error if the visitor fails to visit the instruction.
1392	fn visit_exp(&mut self, inst: &Exp) -> Result<(), E>;
1393
1394	/// Visit a negation instruction.
1395	///
1396	/// # Parameters
1397	/// - `inst`: The instruction to visit.
1398	///
1399	/// # Errors
1400	/// An error if the visitor fails to visit the instruction.
1401	fn visit_neg(&mut self, inst: &Neg) -> Result<(), E>;
1402
1403	/// Visit a return instruction.
1404	///
1405	/// # Parameters
1406	/// - `inst`: The instruction to visit.
1407	///
1408	/// # Errors
1409	/// An error if the visitor fails to visit the instruction.
1410	fn visit_return(&mut self, inst: &Return) -> Result<(), E>;
1411}
1412
1413/// Provides the ability to apply a [visitor](InstructionVisitor).
1414///
1415/// # Type Parameters
1416/// - `V`: The type of visitor to apply.
1417/// - `E`: The type of error that can occur while visiting.
1418pub trait CanVisitInstructions<V, E>
1419where
1420	V: InstructionVisitor<E>
1421{
1422	/// Apply the specified [visitor](InstructionVisitor) to the receiver.
1423	///
1424	/// # Parameters
1425	/// - `visitor`: The visitor to apply.
1426	///
1427	/// # Errors
1428	/// An error if one occurs while visiting the receiver.
1429	fn visit(&self, visitor: &mut V) -> Result<(), E>;
1430}
1431
1432impl<V, E> CanVisitInstructions<V, E> for Instruction
1433where
1434	V: InstructionVisitor<E>
1435{
1436	fn visit(&self, visitor: &mut V) -> Result<(), E>
1437	{
1438		match self
1439		{
1440			Instruction::RollRange(inst) => visitor.visit_roll_range(inst),
1441			Instruction::RollStandardDice(inst) =>
1442			{
1443				visitor.visit_roll_standard_dice(inst)
1444			},
1445			Instruction::RollCustomDice(inst) =>
1446			{
1447				visitor.visit_roll_custom_dice(inst)
1448			},
1449			Instruction::DropLowest(inst) => visitor.visit_drop_lowest(inst),
1450			Instruction::DropHighest(inst) => visitor.visit_drop_highest(inst),
1451			Instruction::SumRollingRecord(inst) =>
1452			{
1453				visitor.visit_sum_rolling_record(inst)
1454			},
1455			Instruction::Add(inst) => visitor.visit_add(inst),
1456			Instruction::Sub(inst) => visitor.visit_sub(inst),
1457			Instruction::Mul(inst) => visitor.visit_mul(inst),
1458			Instruction::Div(inst) => visitor.visit_div(inst),
1459			Instruction::Mod(inst) => visitor.visit_mod(inst),
1460			Instruction::Exp(inst) => visitor.visit_exp(inst),
1461			Instruction::Neg(inst) => visitor.visit_neg(inst),
1462			Instruction::Return(inst) => visitor.visit_return(inst)
1463		}
1464	}
1465}
1466
1467////////////////////////////////////////////////////////////////////////////////
1468//                           Register dependencies.                           //
1469////////////////////////////////////////////////////////////////////////////////
1470
1471/// Analyzes the dependencies between instructions.
1472#[derive(Debug, Clone, PartialEq, Eq)]
1473pub struct DependencyAnalyzer<'inst>
1474{
1475	// The function being analyzed.
1476	instructions: &'inst [Instruction],
1477
1478	/// The program counter. Always set to the correct value before visiting an
1479	/// instruction.
1480	pc: ProgramCounter,
1481
1482	/// The register and rolling record writers, indexed by target. When the
1483	/// function is in static single assignment (SSA) form, then each register
1484	/// and rolling record writer will only have one writer.
1485	writers: HashMap<AddressingMode, BTreeSet<ProgramCounter>>,
1486
1487	/// The register and rolling record readers, indexed by source.
1488	readers: HashMap<AddressingMode, BTreeSet<ProgramCounter>>,
1489
1490	/// The transitive register and rolling record dependencies, as a map from
1491	/// registers to all instructions that contribute to them.
1492	transitive_dependencies:
1493		RefCell<Option<HashMap<AddressingMode, BTreeSet<ProgramCounter>>>>,
1494
1495	/// The transitive register and rolling record dependents, as a map from
1496	/// registers to all instructions that consume from them.
1497	transitive_dependents:
1498		RefCell<Option<HashMap<AddressingMode, BTreeSet<ProgramCounter>>>>
1499}
1500
1501impl<'inst> DependencyAnalyzer<'inst>
1502{
1503	/// Analyze the dependencies between instructions in the specified function
1504	/// body.
1505	///
1506	/// # Parameters
1507	/// - `instructions`: The function body to analyze.
1508	///
1509	/// # Returns
1510	/// The populated dependency analyzer.
1511	pub fn analyze(instructions: &'inst [Instruction]) -> Self
1512	{
1513		let mut analyzer = Self {
1514			instructions,
1515			pc: ProgramCounter::default(),
1516			writers: HashMap::new(),
1517			readers: HashMap::new(),
1518			transitive_dependencies: RefCell::default(),
1519			transitive_dependents: RefCell::default()
1520		};
1521		// Visit each instruction in the function body.
1522		for instruction in analyzer.instructions
1523		{
1524			instruction.visit(&mut analyzer).unwrap();
1525			analyzer.pc.allocate();
1526		}
1527		analyzer
1528	}
1529
1530	/// Answer the instructions being analyzed.
1531	///
1532	/// # Returns
1533	/// The requested instructions.
1534	pub fn instructions(&self) -> &'inst [Instruction] { self.instructions }
1535
1536	/// Answer the direct writers of registers and rolling records, indexed by
1537	/// target.
1538	///
1539	/// # Returns
1540	/// The requested map.
1541	pub fn writers(&self)
1542	-> &HashMap<AddressingMode, BTreeSet<ProgramCounter>>
1543	{
1544		&self.writers
1545	}
1546
1547	/// Answer the direct readers of registers and rolling records, indexed by
1548	/// source.
1549	///
1550	/// # Returns
1551	/// The requested map.
1552	pub fn readers(&self)
1553	-> &HashMap<AddressingMode, BTreeSet<ProgramCounter>>
1554	{
1555		&self.readers
1556	}
1557
1558	/// Compute the transitive dependencies of registers and rolling records, as
1559	/// a map from registers to all instructions that contribute to them.
1560	///
1561	/// # Returns
1562	/// The requested map.
1563	pub fn transitive_dependencies(
1564		&self
1565	) -> Ref<'_, HashMap<AddressingMode, BTreeSet<ProgramCounter>>>
1566	{
1567		// If a cached value is available, then return it.
1568		if self.transitive_dependencies.borrow().is_some()
1569		{
1570			return Ref::map(self.transitive_dependencies.borrow(), |x| {
1571				x.as_ref().unwrap()
1572			})
1573		}
1574		let mut transitive_dependencies =
1575			HashMap::<AddressingMode, BTreeSet<ProgramCounter>>::new();
1576		// This is a worklist algorithm. Start with the direct writers of each
1577		// register and rolling record, and then add all of the writers of each
1578		// source register, and so on, until no new dependencies are found.
1579		for (dest, writers) in &self.writers
1580		{
1581			let mut all_dependencies = BTreeSet::new();
1582			let mut to_visit = writers.clone();
1583			while let Some(pc) = to_visit.pop_first()
1584			{
1585				if all_dependencies.insert(pc)
1586				{
1587					// This instruction contributes to the register or rolling
1588					// record and it hasn't been encountered yet. Enqueue all of
1589					// the writers of its sources to the worklist.
1590					let dependency = &self.instructions[pc.0];
1591					dependency.sources().iter().for_each(|src| {
1592						if let Some(writers) = self.writers.get(src)
1593						{
1594							// Only registers and rolling records that do not
1595							// represent parameters or external variables
1596							// have writers.
1597							to_visit.extend(writers.iter().clone());
1598						}
1599					});
1600				}
1601			}
1602			transitive_dependencies.insert(*dest, all_dependencies);
1603		}
1604		*self.transitive_dependencies.borrow_mut() =
1605			Some(transitive_dependencies);
1606		Ref::map(self.transitive_dependencies.borrow(), |x| {
1607			x.as_ref().unwrap()
1608		})
1609	}
1610
1611	/// Compute the transitive dependents of registers and rolling records, as
1612	/// a map from registers to all instructions that consume from them.
1613	///
1614	/// # Returns
1615	/// The requested map.
1616	pub fn transitive_dependents(
1617		&self
1618	) -> Ref<'_, HashMap<AddressingMode, BTreeSet<ProgramCounter>>>
1619	{
1620		// If a cached value is available, then return it.
1621		if self.transitive_dependents.borrow().is_some()
1622		{
1623			return Ref::map(self.transitive_dependents.borrow(), |x| {
1624				x.as_ref().unwrap()
1625			})
1626		}
1627		let mut transitive_dependents =
1628			HashMap::<AddressingMode, BTreeSet<ProgramCounter>>::new();
1629		for (dest, readers) in &self.readers
1630		{
1631			let mut all_dependents = BTreeSet::new();
1632			let mut to_visit = readers.clone();
1633			while let Some(pc) = to_visit.pop_first()
1634			{
1635				if all_dependents.insert(pc)
1636				{
1637					// This instruction consumes from the register or rolling
1638					// record and it hasn't been encountered yet. Enqueue all of
1639					// the readers of its destination to the worklist.
1640					let dependency = &self.instructions[pc.0];
1641					if let Some(dest) = dependency.destination()
1642					{
1643						if let Some(readers) = self.readers.get(&dest)
1644						{
1645							// Only registers and rolling records that do not
1646							// represent parameters or external variables have
1647							// readers.
1648							to_visit.extend(readers.iter().clone());
1649						}
1650					}
1651				}
1652			}
1653			transitive_dependents.insert(*dest, all_dependents);
1654		}
1655		*self.transitive_dependents.borrow_mut() = Some(transitive_dependents);
1656		Ref::map(self.transitive_dependents.borrow(), |x| x.as_ref().unwrap())
1657	}
1658}
1659
1660impl InstructionVisitor<()> for DependencyAnalyzer<'_>
1661{
1662	fn visit_roll_range(&mut self, inst: &RollRange) -> Result<(), ()>
1663	{
1664		self.writers
1665			.entry(inst.dest.into())
1666			.or_default()
1667			.insert(self.pc);
1668		self.readers.entry(inst.start).or_default().insert(self.pc);
1669		self.readers.entry(inst.end).or_default().insert(self.pc);
1670		Ok(())
1671	}
1672
1673	fn visit_roll_standard_dice(
1674		&mut self,
1675		inst: &RollStandardDice
1676	) -> Result<(), ()>
1677	{
1678		self.writers
1679			.entry(inst.dest.into())
1680			.or_default()
1681			.insert(self.pc);
1682		self.readers.entry(inst.count).or_default().insert(self.pc);
1683		self.readers.entry(inst.faces).or_default().insert(self.pc);
1684		Ok(())
1685	}
1686
1687	fn visit_roll_custom_dice(
1688		&mut self,
1689		inst: &RollCustomDice
1690	) -> Result<(), ()>
1691	{
1692		self.writers
1693			.entry(inst.dest.into())
1694			.or_default()
1695			.insert(self.pc);
1696		self.readers.entry(inst.count).or_default().insert(self.pc);
1697		Ok(())
1698	}
1699
1700	fn visit_drop_lowest(&mut self, inst: &DropLowest) -> Result<(), ()>
1701	{
1702		self.writers
1703			.entry(inst.dest.into())
1704			.or_default()
1705			.insert(self.pc);
1706		self.readers
1707			.entry(inst.dest.into())
1708			.or_default()
1709			.insert(self.pc);
1710		self.readers.entry(inst.count).or_default().insert(self.pc);
1711		Ok(())
1712	}
1713
1714	fn visit_drop_highest(&mut self, inst: &DropHighest) -> Result<(), ()>
1715	{
1716		self.writers
1717			.entry(inst.dest.into())
1718			.or_default()
1719			.insert(self.pc);
1720		self.readers
1721			.entry(inst.dest.into())
1722			.or_default()
1723			.insert(self.pc);
1724		self.readers.entry(inst.count).or_default().insert(self.pc);
1725		Ok(())
1726	}
1727
1728	fn visit_sum_rolling_record(
1729		&mut self,
1730		inst: &SumRollingRecord
1731	) -> Result<(), ()>
1732	{
1733		self.writers
1734			.entry(inst.dest.into())
1735			.or_default()
1736			.insert(self.pc);
1737		self.readers
1738			.entry(inst.src.into())
1739			.or_default()
1740			.insert(self.pc);
1741		Ok(())
1742	}
1743
1744	fn visit_add(&mut self, inst: &Add) -> Result<(), ()>
1745	{
1746		self.writers
1747			.entry(inst.dest.into())
1748			.or_default()
1749			.insert(self.pc);
1750		self.readers.entry(inst.op1).or_default().insert(self.pc);
1751		self.readers.entry(inst.op2).or_default().insert(self.pc);
1752		Ok(())
1753	}
1754
1755	fn visit_sub(&mut self, inst: &Sub) -> Result<(), ()>
1756	{
1757		self.writers
1758			.entry(inst.dest.into())
1759			.or_default()
1760			.insert(self.pc);
1761		self.readers.entry(inst.op1).or_default().insert(self.pc);
1762		self.readers.entry(inst.op2).or_default().insert(self.pc);
1763		Ok(())
1764	}
1765
1766	fn visit_mul(&mut self, inst: &Mul) -> Result<(), ()>
1767	{
1768		self.writers
1769			.entry(inst.dest.into())
1770			.or_default()
1771			.insert(self.pc);
1772		self.readers.entry(inst.op1).or_default().insert(self.pc);
1773		self.readers.entry(inst.op2).or_default().insert(self.pc);
1774		Ok(())
1775	}
1776
1777	fn visit_div(&mut self, inst: &Div) -> Result<(), ()>
1778	{
1779		self.writers
1780			.entry(inst.dest.into())
1781			.or_default()
1782			.insert(self.pc);
1783		self.readers.entry(inst.op1).or_default().insert(self.pc);
1784		self.readers.entry(inst.op2).or_default().insert(self.pc);
1785		Ok(())
1786	}
1787
1788	fn visit_mod(&mut self, inst: &Mod) -> Result<(), ()>
1789	{
1790		self.writers
1791			.entry(inst.dest.into())
1792			.or_default()
1793			.insert(self.pc);
1794		self.readers.entry(inst.op1).or_default().insert(self.pc);
1795		self.readers.entry(inst.op2).or_default().insert(self.pc);
1796		Ok(())
1797	}
1798
1799	fn visit_exp(&mut self, inst: &Exp) -> Result<(), ()>
1800	{
1801		self.writers
1802			.entry(inst.dest.into())
1803			.or_default()
1804			.insert(self.pc);
1805		self.readers.entry(inst.op1).or_default().insert(self.pc);
1806		self.readers.entry(inst.op2).or_default().insert(self.pc);
1807		Ok(())
1808	}
1809
1810	fn visit_neg(&mut self, inst: &Neg) -> Result<(), ()>
1811	{
1812		self.writers
1813			.entry(inst.dest.into())
1814			.or_default()
1815			.insert(self.pc);
1816		self.readers.entry(inst.op).or_default().insert(self.pc);
1817		Ok(())
1818	}
1819
1820	fn visit_return(&mut self, inst: &Return) -> Result<(), ()>
1821	{
1822		self.readers.entry(inst.src).or_default().insert(self.pc);
1823		Ok(())
1824	}
1825}
1826
1827////////////////////////////////////////////////////////////////////////////////
1828//                                   Tests.                                   //
1829////////////////////////////////////////////////////////////////////////////////
1830
1831#[cfg(test)]
1832mod test
1833{
1834	use std::collections::BTreeSet;
1835
1836	use pretty_assertions::assert_eq;
1837
1838	use crate::{
1839		AddressingMode, DependencyAnalyzer, Function, Immediate, Instruction,
1840		ProgramCounter, RegisterIndex
1841	};
1842
1843	/// Answer the function `x: 1 + {x} + 1 + {x} + 1 + {x}`, which is used by
1844	/// the tests in this module.
1845	///
1846	/// # Returns
1847	/// The function described above.
1848	fn function() -> Function
1849	{
1850		// x: 1 + {x} + 1 + {x} + 1 + {x}
1851		// =
1852		// Function(x@0) r#6 ⚅#0   (pc)
1853		//   @1 <- 1 + @0         #0
1854		//   @2 <- @1 + 1         #1
1855		//   @3 <- @2 + @0        #2
1856		//   @4 <- @3 + 1         #3
1857		//   @5 <- @4 + @0        #4
1858		//   return @5            #5
1859		Function {
1860			parameters: vec!["x".to_string()],
1861			externals: Vec::new(),
1862			register_count: 6,
1863			rolling_record_count: 0,
1864			instructions: vec![
1865				Instruction::add(
1866					1.into(),
1867					Immediate(1).into(),
1868					RegisterIndex(0).into()
1869				),
1870				Instruction::add(
1871					2.into(),
1872					RegisterIndex(1).into(),
1873					Immediate(1).into()
1874				),
1875				Instruction::add(
1876					3.into(),
1877					RegisterIndex(2).into(),
1878					RegisterIndex(0).into()
1879				),
1880				Instruction::add(
1881					4.into(),
1882					RegisterIndex(3).into(),
1883					Immediate(1).into()
1884				),
1885				Instruction::add(
1886					5.into(),
1887					RegisterIndex(4).into(),
1888					RegisterIndex(0).into()
1889				),
1890				Instruction::r#return(RegisterIndex(5).into()),
1891			]
1892		}
1893	}
1894
1895	/// Ensure that direct dependency analysis produces the correct writer map.
1896	#[test]
1897	fn test_analyze_writers()
1898	{
1899		let function = function();
1900		let analyzer = DependencyAnalyzer::analyze(&function.instructions);
1901		assert_eq!(
1902			analyzer.writers(),
1903			&[
1904				(
1905					AddressingMode::Register(RegisterIndex(1)),
1906					BTreeSet::from([ProgramCounter(0)])
1907				),
1908				(
1909					AddressingMode::Register(RegisterIndex(2)),
1910					BTreeSet::from([ProgramCounter(1)])
1911				),
1912				(
1913					AddressingMode::Register(RegisterIndex(3)),
1914					BTreeSet::from([ProgramCounter(2)])
1915				),
1916				(
1917					AddressingMode::Register(RegisterIndex(4)),
1918					BTreeSet::from([ProgramCounter(3)])
1919				),
1920				(
1921					AddressingMode::Register(RegisterIndex(5)),
1922					BTreeSet::from([ProgramCounter(4)])
1923				)
1924			]
1925			.into()
1926		);
1927	}
1928
1929	/// Ensure that direct dependency analysis produces the correct reader map.
1930	#[test]
1931	fn test_analyze_readers()
1932	{
1933		let function = function();
1934		let analyzer = DependencyAnalyzer::analyze(&function.instructions);
1935		assert_eq!(
1936			analyzer.readers(),
1937			&[
1938				(
1939					AddressingMode::Immediate(Immediate(1)),
1940					BTreeSet::from([
1941						ProgramCounter(0),
1942						ProgramCounter(1),
1943						ProgramCounter(3)
1944					])
1945				),
1946				(
1947					AddressingMode::Register(RegisterIndex(0)),
1948					BTreeSet::from([
1949						ProgramCounter(0),
1950						ProgramCounter(2),
1951						ProgramCounter(4)
1952					])
1953				),
1954				(
1955					AddressingMode::Register(RegisterIndex(1)),
1956					BTreeSet::from([ProgramCounter(1)])
1957				),
1958				(
1959					AddressingMode::Register(RegisterIndex(2)),
1960					BTreeSet::from([ProgramCounter(2)])
1961				),
1962				(
1963					AddressingMode::Register(RegisterIndex(3)),
1964					BTreeSet::from([ProgramCounter(3)])
1965				),
1966				(
1967					AddressingMode::Register(RegisterIndex(4)),
1968					BTreeSet::from([ProgramCounter(4)])
1969				),
1970				(
1971					AddressingMode::Register(RegisterIndex(5)),
1972					BTreeSet::from([ProgramCounter(5)])
1973				),
1974			]
1975			.into()
1976		);
1977	}
1978
1979	/// Ensure that transitive dependency analysis produces the correct
1980	/// dependencies map.
1981	#[test]
1982	fn test_dependencies()
1983	{
1984		let function = function();
1985		let analyzer = DependencyAnalyzer::analyze(&function.instructions);
1986		let dependencies = analyzer.transitive_dependencies();
1987		assert_eq!(
1988			*dependencies,
1989			[
1990				(
1991					AddressingMode::Register(RegisterIndex(1)),
1992					BTreeSet::from([ProgramCounter(0)])
1993				),
1994				(
1995					AddressingMode::Register(RegisterIndex(2)),
1996					BTreeSet::from([ProgramCounter(0), ProgramCounter(1)])
1997				),
1998				(
1999					AddressingMode::Register(RegisterIndex(3)),
2000					BTreeSet::from([
2001						ProgramCounter(0),
2002						ProgramCounter(1),
2003						ProgramCounter(2)
2004					])
2005				),
2006				(
2007					AddressingMode::Register(RegisterIndex(4)),
2008					BTreeSet::from([
2009						ProgramCounter(0),
2010						ProgramCounter(1),
2011						ProgramCounter(2),
2012						ProgramCounter(3)
2013					])
2014				),
2015				(
2016					AddressingMode::Register(RegisterIndex(5)),
2017					BTreeSet::from([
2018						ProgramCounter(0),
2019						ProgramCounter(1),
2020						ProgramCounter(2),
2021						ProgramCounter(3),
2022						ProgramCounter(4)
2023					])
2024				)
2025			]
2026			.into()
2027		);
2028	}
2029
2030	/// Ensure that transitive dependency analysis produces the correct
2031	/// dependents map.
2032	#[test]
2033	fn test_dependents()
2034	{
2035		let function = function();
2036		let analyzer = DependencyAnalyzer::analyze(&function.instructions);
2037		let dependents = analyzer.transitive_dependents();
2038		assert_eq!(
2039			*dependents,
2040			[
2041				(
2042					AddressingMode::Immediate(Immediate(1)),
2043					BTreeSet::from([
2044						ProgramCounter(0),
2045						ProgramCounter(1),
2046						ProgramCounter(2),
2047						ProgramCounter(3),
2048						ProgramCounter(4),
2049						ProgramCounter(5)
2050					])
2051				),
2052				(
2053					AddressingMode::Register(RegisterIndex(0)),
2054					BTreeSet::from([
2055						ProgramCounter(0),
2056						ProgramCounter(1),
2057						ProgramCounter(2),
2058						ProgramCounter(3),
2059						ProgramCounter(4),
2060						ProgramCounter(5)
2061					])
2062				),
2063				(
2064					AddressingMode::Register(RegisterIndex(1)),
2065					BTreeSet::from([
2066						ProgramCounter(1),
2067						ProgramCounter(2),
2068						ProgramCounter(3),
2069						ProgramCounter(4),
2070						ProgramCounter(5)
2071					])
2072				),
2073				(
2074					AddressingMode::Register(RegisterIndex(2)),
2075					BTreeSet::from([
2076						ProgramCounter(2),
2077						ProgramCounter(3),
2078						ProgramCounter(4),
2079						ProgramCounter(5)
2080					])
2081				),
2082				(
2083					AddressingMode::Register(RegisterIndex(3)),
2084					BTreeSet::from([
2085						ProgramCounter(3),
2086						ProgramCounter(4),
2087						ProgramCounter(5)
2088					])
2089				),
2090				(
2091					AddressingMode::Register(RegisterIndex(4)),
2092					BTreeSet::from([ProgramCounter(4), ProgramCounter(5)])
2093				),
2094				(
2095					AddressingMode::Register(RegisterIndex(5)),
2096					BTreeSet::from([ProgramCounter(5)])
2097				)
2098			]
2099			.into()
2100		);
2101	}
2102}