Skip to content

Commit cfc43e1

Browse files
committed
chore(interpreter): make places first-class IR values
Makes more invalid states unrepresentable. The lowerer needs to be refactored, TBD.
1 parent ebe143c commit cfc43e1

4 files changed

Lines changed: 256 additions & 186 deletions

File tree

interpreter/src/ir.rs

Lines changed: 77 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ pub mod lower;
1414
#[cfg(test)]
1515
mod tests;
1616

17-
use std::fmt::{self, Debug, Display, Formatter};
17+
use std::{
18+
fmt::{self, Debug, Display, Formatter},
19+
ops::Deref,
20+
};
1821

1922
use parser::{BuiltinFunction, Command, Redirection};
2023

@@ -41,13 +44,13 @@ pub enum Instruction {
4144
Negation { dest: Reg, arg: Arg, ty: ArgTy },
4245
ToInt { dest: Reg, arg: Arg, ty: ArgTy },
4346
Negative { dest: Reg, arg: Arg, ty: ArgTy },
44-
IncrementPost { dest: Reg, arg: Arg, ty: ArgTy },
45-
DecrementPost { dest: Reg, arg: Arg, ty: ArgTy },
46-
IncrementPre { dest: Reg, arg: Arg, ty: ArgTy },
47-
DecrementPre { dest: Reg, arg: Arg, ty: ArgTy },
47+
IncrementPost { dest: Reg, arg: Arg, ty: PlaceTy },
48+
DecrementPost { dest: Reg, arg: Arg, ty: PlaceTy },
49+
IncrementPre { dest: Reg, arg: Arg, ty: PlaceTy },
50+
DecrementPre { dest: Reg, arg: Arg, ty: PlaceTy },
4851
CopyP { dest: Reg, arg: Arg, ty: ArgTy },
4952
CopyS { dest: Reg, arg: Arg, ty: ArgTy },
50-
CopyA { dest: Reg, arg: Arg, ty: ArgTy },
53+
CopyA { dest: Reg, arg: Arg, ty: PlaceTy },
5154

5255
// Binary operations
5356
Eq { dest: Reg, lhs: Arg, rhs: Arg, tyr: ArgTy, tyl: ArgTy },
@@ -67,11 +70,11 @@ pub enum Instruction {
6770
Concat { dest: Reg, lhs: Arg, rhs: Arg, tyr: ArgTy, tyl: ArgTy },
6871

6972
// Intrinsic operations
70-
StoreS { dest: Reg, ty_place: ArgTy, var: NonLocal, arg: Arg, ty: ArgTy },
73+
StoreS { dest: Reg, ty_place: PlaceTy, var: NonLocal, arg: Arg, ty: ArgTy },
7174
StoreR { dest: Reg, src: Arg, arg: Arg, ty: ArgTy, tys: ArgTy },
72-
StoreA { dest: Reg, lhs: Arg, rhs: Arg, start: Reg, end: Reg, tyl: ArgTy, tyr: ArgTy },
73-
LoadA { dest: Reg, arg: Arg, start: Reg, end: Reg, ty: ArgTy },
74-
LoadM { dest: Reg, arg: Arg, start: Reg, end: Reg, ty: ArgTy },
75+
StoreA { dest: Reg, lhs: Arg, rhs: Arg, start: Reg, end: Reg, tyl: PlaceTy, tyr: ArgTy },
76+
LoadA { dest: Reg, arg: Arg, start: Reg, end: Reg, ty: PlaceTy },
77+
LoadM { dest: Reg, arg: Arg, start: Reg, end: Reg, ty: PlaceTy },
7578
IntrinsicCall { dest: Reg, start: Reg, end: Reg, fun: BuiltinFunction },
7679
OutputCall { start: Reg, end: Reg, cmd: Command, redir: Option<Redirection> },
7780
UserCall { dest: Reg, start: Reg, end: Reg, name: NonLocal },
@@ -106,6 +109,7 @@ pub union Arg {
106109
}
107110

108111
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
112+
#[repr(u8)]
109113
pub enum ArgTy {
110114
Reg,
111115
Imm,
@@ -117,6 +121,22 @@ pub enum ArgTy {
117121
IaVal,
118122
}
119123

124+
#[derive(Clone, Copy)]
125+
#[repr(u8)]
126+
pub enum PlaceTy {
127+
Reg = ArgTy::Reg as u8,
128+
Rec = ArgTy::Rec as u8,
129+
UsVal = ArgTy::UsVal as u8,
130+
UaVal = ArgTy::UaVal as u8,
131+
IsVal = ArgTy::IsVal as u8,
132+
IaVal = ArgTy::IaVal as u8,
133+
}
134+
135+
const _: () = {
136+
assert!(size_of::<PlaceTy>() == size_of::<ArgTy>());
137+
assert!(align_of::<PlaceTy>() == align_of::<ArgTy>());
138+
};
139+
120140
impl Instruction {
121141
fn set_label(&mut self, label: Label) {
122142
match self {
@@ -187,6 +207,48 @@ impl Instruction {
187207
}
188208
}
189209

210+
impl From<PlaceTy> for ArgTy {
211+
#[inline(always)]
212+
fn from(value: PlaceTy) -> Self {
213+
match value {
214+
PlaceTy::Reg => Self::Reg,
215+
PlaceTy::Rec => Self::Rec,
216+
PlaceTy::UsVal => Self::UsVal,
217+
PlaceTy::UaVal => Self::UaVal,
218+
PlaceTy::IsVal => Self::IsVal,
219+
PlaceTy::IaVal => Self::IaVal,
220+
}
221+
}
222+
}
223+
224+
/// Deref polymorphism is not _that_ bad.
225+
impl Deref for PlaceTy {
226+
type Target = ArgTy;
227+
228+
#[inline(always)]
229+
fn deref(&self) -> &Self::Target {
230+
// SAFETY: The target is declared as a strict superset with equal repr.
231+
unsafe { &*(&raw const *self).cast::<Self::Target>() }
232+
}
233+
}
234+
235+
impl TryFrom<ArgTy> for PlaceTy {
236+
type Error = ();
237+
238+
#[inline(always)]
239+
fn try_from(value: ArgTy) -> Result<Self, Self::Error> {
240+
match value {
241+
ArgTy::Rec => Ok(Self::Rec),
242+
ArgTy::Reg => Ok(Self::Reg),
243+
ArgTy::UsVal => Ok(Self::UsVal),
244+
ArgTy::UaVal => Ok(Self::UaVal),
245+
ArgTy::IsVal => Ok(Self::IsVal),
246+
ArgTy::IaVal => Ok(Self::IaVal),
247+
_ => Err(()),
248+
}
249+
}
250+
}
251+
190252
impl Debug for Instruction {
191253
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
192254
write!(f, "0x{:032x}", self.to_bytes())
@@ -207,11 +269,14 @@ impl Display for Instruction {
207269
| Self::ToInt { dest, arg, ty }
208270
| Self::Negative { dest, arg, ty }
209271
| Self::CopyS { dest, arg, ty }
210-
| Self::CopyA { dest, arg, ty }
211272
| Self::CopyP { dest, arg, ty } => {
212273
write!(f, "{dest} <- {op}")?;
213274
fmt_arg(f, arg, ty, " ")
214275
}
276+
Self::CopyA { dest, arg, ty } => {
277+
write!(f, "{dest} <- {op}")?;
278+
fmt_arg(f, arg, ty, " ")
279+
}
215280
Self::Eq { dest, lhs, rhs, tyl, tyr }
216281
| Self::NEq { dest, lhs, rhs, tyl, tyr }
217282
| Self::Gt { dest, lhs, rhs, tyl, tyr }
@@ -232,6 +297,7 @@ impl Display for Instruction {
232297
fmt_arg(f, rhs, tyr, ", ")
233298
}
234299
Self::StoreS { dest, ty_place, var, arg, ty } => {
300+
let ty_place = ArgTy::from(*ty_place);
235301
write!(f, "{dest} <- {op} {ty_place}({var})")?;
236302
fmt_arg(f, arg, ty, ", ")
237303
}

interpreter/src/ir/lower.rs

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use smallvec::SmallVec;
1818
use crate::{
1919
CodeRange,
2020
ir::{
21-
ArgTy, Instruction, IxWidth, Label, NonLocal, Reg, RegWidth,
21+
Instruction, IxWidth, Label, NonLocal, PlaceTy, Reg, RegWidth,
2222
lower::utils::{CallConv, LinearRegRange, Operand, RegAlloc, RtType, TypedArg, var_index},
2323
},
2424
vm::{Consts, Function, SymbolTable, types::Value},
@@ -579,6 +579,7 @@ impl<'a> CodeGen<'a> {
579579
if matches!(place, Place::Record(_) | Place::Variable(_)) =>
580580
{
581581
let (arg, ty) = this.load_place(dest, place).into();
582+
let ty = ty.try_into().unwrap();
582583
match op {
583584
UnaryPlaceOperator::IncrementL => {
584585
this.emit(Instruction::IncrementPre { dest, arg, ty });
@@ -667,10 +668,11 @@ impl<'a> CodeGen<'a> {
667668
this.regs.free_many(range);
668669
}
669670
&ExprNode::ChainedIndex(var, ref indices) => {
670-
let (mut arg, mut ty) = match &var {
671+
let (mut arg, ty) = match &var {
671672
Variable::User(ident) => TypedArg::new_ua(this, ident).into(),
672673
var => TypedArg::new_ia(var).into(),
673674
};
675+
let mut ty = ty.try_into().unwrap();
674676

675677
for (i, index) in indices.iter().enumerate() {
676678
let range = this.gen_call_convention(RtType::Scalar, index);
@@ -681,7 +683,8 @@ impl<'a> CodeGen<'a> {
681683
Instruction::LoadM { dest, arg, start, end, ty }
682684
};
683685
this.emit(instr);
684-
(arg, ty) = TypedArg::new_reg(dest).into();
686+
let (arg_n, ty_n) = TypedArg::new_reg(dest).into();
687+
(arg, ty) = (arg_n, ty_n.try_into().unwrap());
685688
this.regs.free_many(range);
686689
}
687690
}
@@ -709,9 +712,11 @@ impl<'a> CodeGen<'a> {
709712
let (start, end) = range.as_range();
710713
if let Variable::User(ident) = var {
711714
let (arg, ty) = TypedArg::new_ua(self, ident).into();
715+
let ty = ty.try_into().unwrap();
712716
self.emit(Instruction::LoadA { dest, arg, ty, start, end });
713717
} else {
714718
let (arg, ty) = TypedArg::new_ia(var).into();
719+
let ty = ty.try_into().unwrap();
715720
self.emit(Instruction::LoadA { dest, arg, ty, start, end });
716721
}
717722
self.regs.free_many(range);
@@ -731,14 +736,15 @@ impl<'a> CodeGen<'a> {
731736
Place::Variable(Variable::User(ident)) => {
732737
let t_arg = TypedArg::new_us(self, ident);
733738
let (var, ty_place) = t_arg.into();
739+
let ty_place = ty_place.try_into().unwrap();
734740

735741
if t_arg.as_reg().is_some_and(|reg| reg == dest) {
736742
return; // Value already on destination.
737743
}
738744

739745
let store = match ty_place {
740-
ArgTy::Reg => Instruction::CopyP { dest, arg, ty },
741-
ArgTy::UsVal => {
746+
PlaceTy::Reg => Instruction::CopyP { dest, arg, ty },
747+
PlaceTy::UsVal => {
742748
Instruction::StoreS { dest, ty_place, var: unsafe { var.sym }, arg, ty }
743749
}
744750
_ => unreachable!(),
@@ -747,10 +753,11 @@ impl<'a> CodeGen<'a> {
747753
}
748754
Place::Variable(var) => {
749755
let var = var_index(var);
750-
self.emit(Instruction::StoreS { dest, ty_place: ArgTy::IsVal, var, arg, ty });
756+
self.emit(Instruction::StoreS { dest, ty_place: PlaceTy::IsVal, var, arg, ty });
751757
}
752758
Place::Index(Variable::User(ident), index) => {
753759
let (lhs, tyl) = TypedArg::new_ua(self, ident).into();
760+
let tyl = tyl.try_into().unwrap();
754761
let (rhs, tyr) = src.into();
755762
let range = self.gen_call_convention(RtType::Scalar, index);
756763
let (start, end) = range.as_range();
@@ -759,31 +766,35 @@ impl<'a> CodeGen<'a> {
759766
}
760767
Place::Index(var, index) => {
761768
let (lhs, tyl) = TypedArg::new_ia(var).into();
769+
let tyl = tyl.try_into().unwrap();
762770
let (rhs, tyr) = src.into();
763771
let range = self.gen_call_convention(RtType::Scalar, index);
764772
let (start, end) = range.as_range();
765773
self.emit(Instruction::StoreA { dest, lhs, rhs, start, end, tyl, tyr });
766774
self.regs.free_many(range);
767775
}
768776
&Place::ChainedIndex(var, ref indices) => {
769-
let (mut arg, mut ty) = match &var {
777+
let (mut arg, ty) = match &var {
770778
Variable::User(ident) => TypedArg::new_ua(self, ident).into(),
771779
var => TypedArg::new_ia(var).into(),
772780
};
781+
let mut ty = ty.try_into().unwrap();
773782
let last = indices.len() - 1;
774783

775784
for index in &indices[..last] {
776785
let range = self.gen_call_convention(RtType::Scalar, index);
777786
let (start, end) = range.as_range();
778787
self.emit(Instruction::LoadM { dest, arg, start, end, ty });
779-
(arg, ty) = TypedArg::new_reg(dest).into();
788+
let (arg_n, ty_n) = TypedArg::new_reg(dest).into();
789+
(arg, ty) = (arg_n, ty_n.try_into().unwrap());
780790
self.regs.free_many(range);
781791
}
782792

783793
let range = self.gen_call_convention(RtType::Scalar, &indices[last]);
784794
let (start, end) = range.as_range();
785795
let (lhs, tyl) = TypedArg::new_reg(dest).into();
786796
let (rhs, tyr) = src.into();
797+
let tyl = tyl.try_into().unwrap();
787798
self.emit(Instruction::StoreA { dest, lhs, rhs, start, end, tyl, tyr });
788799
self.regs.free_many(range);
789800
}
@@ -878,7 +889,9 @@ impl<'a> CodeGen<'a> {
878889
let (arg, ty) = this.load_place(dest, &Place::Variable(var)).into();
879890
let instr = match rt_ty {
880891
RtType::Scalar => Instruction::CopyS { dest, arg, ty },
881-
RtType::Array => Instruction::CopyA { dest, arg, ty },
892+
RtType::Array => {
893+
Instruction::CopyA { dest, arg, ty: ty.try_into().unwrap() }
894+
}
882895
RtType::Any => Instruction::CopyP { dest, arg, ty },
883896
};
884897
this.emit(instr);

0 commit comments

Comments
 (0)