1use std::fmt;
16
17const _: () = assert!(std::mem::size_of::<CfgInst>() <= 48);
25const _: () = assert!(std::mem::size_of::<CfgInstData>() <= 32);
26
27use gruel_air::{EnumId, StructId, Type};
28use gruel_span::Span;
29use lasso::{Key, Spur};
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub struct Place {
49 pub base: PlaceBase,
51 pub proj_start: u32,
53 pub proj_len: u32,
55}
56
57impl Place {
58 #[inline]
60 pub const fn local(slot: u32) -> Self {
61 Self {
62 base: PlaceBase::Local(slot),
63 proj_start: 0,
64 proj_len: 0,
65 }
66 }
67
68 #[inline]
70 pub const fn param(slot: u32) -> Self {
71 Self {
72 base: PlaceBase::Param(slot),
73 proj_start: 0,
74 proj_len: 0,
75 }
76 }
77
78 #[inline]
80 pub const fn is_simple(&self) -> bool {
81 self.proj_len == 0
82 }
83
84 #[inline]
86 pub const fn as_local(&self) -> Option<u32> {
87 if self.proj_len == 0 {
88 match self.base {
89 PlaceBase::Local(slot) => Some(slot),
90 PlaceBase::Param(_) => None,
91 }
92 } else {
93 None
94 }
95 }
96
97 #[inline]
99 pub const fn as_param(&self) -> Option<u32> {
100 if self.proj_len == 0 {
101 match self.base {
102 PlaceBase::Param(slot) => Some(slot),
103 PlaceBase::Local(_) => None,
104 }
105 } else {
106 None
107 }
108 }
109}
110
111impl fmt::Display for Place {
112 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113 match self.base {
114 PlaceBase::Local(slot) => write!(f, "${}", slot)?,
115 PlaceBase::Param(slot) => write!(f, "%{}", slot)?,
116 }
117 if self.proj_len > 0 {
118 write!(
119 f,
120 "[{}..{}]",
121 self.proj_start,
122 self.proj_start + self.proj_len
123 )?;
124 }
125 Ok(())
126 }
127}
128
129#[derive(Debug, Clone, Copy, PartialEq, Eq)]
131pub enum PlaceBase {
132 Local(u32),
134 Param(u32),
136}
137
138#[derive(Debug, Clone, Copy, PartialEq, Eq)]
143pub enum Projection {
144 Field {
149 struct_id: StructId,
150 field_index: u32,
151 },
152 Index { array_type: Type, index: CfgValue },
157}
158
159#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
161pub struct BlockId(pub(crate) u32);
162
163impl BlockId {
164 #[inline]
166 pub const fn from_raw(index: u32) -> Self {
167 Self(index)
168 }
169
170 #[inline]
172 pub const fn as_u32(self) -> u32 {
173 self.0
174 }
175}
176
177impl fmt::Display for BlockId {
178 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
179 write!(f, "bb{}", self.0)
180 }
181}
182
183#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
185pub struct CfgValue(u32);
186
187impl CfgValue {
188 #[inline]
190 pub const fn from_raw(index: u32) -> Self {
191 Self(index)
192 }
193
194 #[inline]
196 pub const fn as_u32(self) -> u32 {
197 self.0
198 }
199}
200
201impl fmt::Display for CfgValue {
202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203 write!(f, "v{}", self.0)
204 }
205}
206
207#[derive(Debug, Clone)]
209pub struct CfgInst {
210 pub data: CfgInstData,
211 pub ty: Type,
212 pub span: Span,
213}
214
215#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
217pub enum CfgArgMode {
218 #[default]
220 Normal,
221 Inout,
223 Borrow,
225}
226
227impl From<gruel_air::AirArgMode> for CfgArgMode {
228 fn from(mode: gruel_air::AirArgMode) -> Self {
229 match mode {
230 gruel_air::AirArgMode::Normal => CfgArgMode::Normal,
231 gruel_air::AirArgMode::Inout => CfgArgMode::Inout,
232 gruel_air::AirArgMode::Borrow => CfgArgMode::Borrow,
233 }
234 }
235}
236
237#[derive(Debug, Clone, Copy)]
239pub struct CfgCallArg {
240 pub value: CfgValue,
242 pub mode: CfgArgMode,
244}
245
246impl CfgCallArg {
247 pub fn is_inout(&self) -> bool {
249 self.mode == CfgArgMode::Inout
250 }
251
252 pub fn is_borrow(&self) -> bool {
254 self.mode == CfgArgMode::Borrow
255 }
256
257 pub fn is_by_ref(&self) -> bool {
259 matches!(self.mode, CfgArgMode::Inout | CfgArgMode::Borrow)
260 }
261}
262
263#[derive(Debug, Clone)]
268pub enum CfgInstData {
269 Const(u64),
271
272 FloatConst(u64),
274
275 BoolConst(bool),
277
278 StringConst(u32),
280
281 Param {
283 index: u32,
284 },
285
286 BlockParam {
289 index: u32,
290 },
291
292 Add(CfgValue, CfgValue),
294 Sub(CfgValue, CfgValue),
295 Mul(CfgValue, CfgValue),
296 Div(CfgValue, CfgValue),
297 Mod(CfgValue, CfgValue),
298
299 Eq(CfgValue, CfgValue),
301 Ne(CfgValue, CfgValue),
302 Lt(CfgValue, CfgValue),
303 Gt(CfgValue, CfgValue),
304 Le(CfgValue, CfgValue),
305 Ge(CfgValue, CfgValue),
306
307 BitAnd(CfgValue, CfgValue),
309 BitOr(CfgValue, CfgValue),
310 BitXor(CfgValue, CfgValue),
311 Shl(CfgValue, CfgValue),
312 Shr(CfgValue, CfgValue),
313
314 Neg(CfgValue),
316 Not(CfgValue),
317 BitNot(CfgValue),
318
319 Alloc {
322 slot: u32,
323 init: CfgValue,
324 },
325 Load {
327 slot: u32,
328 },
329 Store {
331 slot: u32,
332 value: CfgValue,
333 },
334 ParamStore {
336 param_slot: u32,
337 value: CfgValue,
338 },
339
340 PlaceRead {
346 place: Place,
347 },
348
349 PlaceWrite {
354 place: Place,
355 value: CfgValue,
356 },
357
358 Call {
362 name: Spur,
364 args_start: u32,
366 args_len: u32,
368 },
369
370 Intrinsic {
373 name: Spur,
375 args_start: u32,
377 args_len: u32,
379 },
380
381 StructInit {
385 struct_id: StructId,
386 fields_start: u32,
388 fields_len: u32,
390 },
391 FieldSet {
392 slot: u32,
393 struct_id: StructId,
394 field_index: u32,
395 value: CfgValue,
396 },
397 ParamFieldSet {
399 param_slot: u32,
401 inner_offset: u32,
403 struct_id: StructId,
404 field_index: u32,
405 value: CfgValue,
406 },
407
408 ArrayInit {
413 elements_start: u32,
415 elements_len: u32,
417 },
418 IndexSet {
420 slot: u32,
421 array_type: Type,
423 index: CfgValue,
424 value: CfgValue,
425 },
426 ParamIndexSet {
428 param_slot: u32,
430 array_type: Type,
432 index: CfgValue,
434 value: CfgValue,
436 },
437
438 EnumVariant {
441 enum_id: EnumId,
442 variant_index: u32,
443 },
444
445 EnumCreate {
449 enum_id: EnumId,
450 variant_index: u32,
451 fields_start: u32,
453 fields_len: u32,
455 },
456
457 EnumPayloadGet {
460 base: CfgValue,
462 variant_index: u32,
464 field_index: u32,
466 },
467
468 IntCast {
473 value: CfgValue,
475 from_ty: Type,
477 },
478
479 FloatCast {
482 value: CfgValue,
484 from_ty: Type,
486 },
487
488 IntToFloat {
491 value: CfgValue,
493 from_ty: Type,
495 },
496
497 FloatToInt {
501 value: CfgValue,
503 from_ty: Type,
505 },
506
507 Drop {
511 value: CfgValue,
512 },
513
514 StorageLive {
518 slot: u32,
519 },
520
521 StorageDead {
525 slot: u32,
526 },
527}
528
529#[derive(Debug, Clone, Copy)]
537pub enum Terminator {
538 Goto {
541 target: BlockId,
542 args_start: u32,
544 args_len: u32,
546 },
547
548 Branch {
551 cond: CfgValue,
552 then_block: BlockId,
553 then_args_start: u32,
555 then_args_len: u32,
557 else_block: BlockId,
558 else_args_start: u32,
560 else_args_len: u32,
562 },
563
564 Switch {
567 scrutinee: CfgValue,
569 cases_start: u32,
571 cases_len: u32,
573 default: BlockId,
575 },
576
577 Return { value: Option<CfgValue> },
579
580 Unreachable,
583
584 None,
587}
588
589#[derive(Debug, Clone)]
591pub struct BasicBlock {
592 pub id: BlockId,
594 pub params: Vec<(CfgValue, Type)>,
596 pub insts: Vec<CfgValue>,
598 pub terminator: Terminator,
600 pub preds: Vec<BlockId>,
602}
603
604impl BasicBlock {
605 pub fn new(id: BlockId) -> Self {
607 Self {
608 id,
609 params: Vec::new(),
610 insts: Vec::new(),
611 terminator: Terminator::None,
612 preds: Vec::new(),
613 }
614 }
615}
616
617#[derive(Debug)]
619pub struct Cfg {
620 blocks: Vec<BasicBlock>,
622 pub entry: BlockId,
624 return_type: Type,
626 values: Vec<CfgInst>,
628 extra: Vec<CfgValue>,
631 call_args: Vec<CfgCallArg>,
634 switch_cases: Vec<(i64, BlockId)>,
637 projections: Vec<Projection>,
640 num_locals: u32,
642 num_params: u32,
644 fn_name: String,
646 param_modes: Vec<bool>,
648 param_types: Vec<Type>,
652}
653
654impl Cfg {
655 pub fn new(
657 return_type: Type,
658 num_locals: u32,
659 num_params: u32,
660 fn_name: String,
661 param_modes: Vec<bool>,
662 param_types: Vec<Type>,
663 ) -> Self {
664 Self {
665 blocks: Vec::new(),
666 entry: BlockId(0),
667 return_type,
668 values: Vec::new(),
669 extra: Vec::new(),
670 call_args: Vec::new(),
671 switch_cases: Vec::new(),
672 projections: Vec::new(),
673 num_locals,
674 num_params,
675 fn_name,
676 param_modes,
677 param_types,
678 }
679 }
680
681 #[inline]
683 pub fn return_type(&self) -> Type {
684 self.return_type
685 }
686
687 #[inline]
689 pub fn num_locals(&self) -> u32 {
690 self.num_locals
691 }
692
693 #[inline]
701 pub fn alloc_temp_local(&mut self) -> u32 {
702 let slot = self.num_locals;
703 self.num_locals += 1;
704 slot
705 }
706
707 #[inline]
709 pub fn num_params(&self) -> u32 {
710 self.num_params
711 }
712
713 #[inline]
715 pub fn fn_name(&self) -> &str {
716 &self.fn_name
717 }
718
719 #[inline]
721 pub fn is_param_inout(&self, slot: u32) -> bool {
722 self.param_modes
723 .get(slot as usize)
724 .copied()
725 .unwrap_or(false)
726 }
727
728 #[inline]
730 pub fn param_modes(&self) -> &[bool] {
731 &self.param_modes
732 }
733
734 #[inline]
738 pub fn param_type(&self, slot: u32) -> Option<Type> {
739 self.param_types.get(slot as usize).copied()
740 }
741
742 pub fn new_block(&mut self) -> BlockId {
744 let id = BlockId(self.blocks.len() as u32);
745 self.blocks.push(BasicBlock::new(id));
746 id
747 }
748
749 #[inline]
751 pub fn get_block(&self, id: BlockId) -> &BasicBlock {
752 &self.blocks[id.0 as usize]
753 }
754
755 #[inline]
757 pub fn get_block_mut(&mut self, id: BlockId) -> &mut BasicBlock {
758 &mut self.blocks[id.0 as usize]
759 }
760
761 pub fn add_inst(&mut self, inst: CfgInst) -> CfgValue {
763 let value = CfgValue::from_raw(self.values.len() as u32);
764 self.values.push(inst);
765 value
766 }
767
768 #[inline]
770 pub fn get_inst(&self, value: CfgValue) -> &CfgInst {
771 &self.values[value.0 as usize]
772 }
773
774 #[inline]
776 pub fn get_inst_mut(&mut self, value: CfgValue) -> &mut CfgInst {
777 &mut self.values[value.0 as usize]
778 }
779
780 #[inline]
782 pub fn value_count(&self) -> usize {
783 self.values.len()
784 }
785
786 pub fn push_extra(&mut self, values: impl IntoIterator<Item = CfgValue>) -> (u32, u32) {
790 let start = self.extra.len() as u32;
791 self.extra.extend(values);
792 let len = self.extra.len() as u32 - start;
793 (start, len)
794 }
795
796 #[inline]
798 pub fn get_extra(&self, start: u32, len: u32) -> &[CfgValue] {
799 &self.extra[start as usize..(start + len) as usize]
800 }
801
802 pub fn push_call_args(&mut self, args: impl IntoIterator<Item = CfgCallArg>) -> (u32, u32) {
806 let start = self.call_args.len() as u32;
807 self.call_args.extend(args);
808 let len = self.call_args.len() as u32 - start;
809 (start, len)
810 }
811
812 #[inline]
814 pub fn get_call_args(&self, start: u32, len: u32) -> &[CfgCallArg] {
815 &self.call_args[start as usize..(start + len) as usize]
816 }
817
818 pub fn push_switch_cases(
822 &mut self,
823 cases: impl IntoIterator<Item = (i64, BlockId)>,
824 ) -> (u32, u32) {
825 let start = self.switch_cases.len() as u32;
826 self.switch_cases.extend(cases);
827 let len = self.switch_cases.len() as u32 - start;
828 (start, len)
829 }
830
831 #[inline]
833 pub fn get_switch_cases(&self, start: u32, len: u32) -> &[(i64, BlockId)] {
834 &self.switch_cases[start as usize..(start + len) as usize]
835 }
836
837 pub fn push_projections(&mut self, projs: impl IntoIterator<Item = Projection>) -> (u32, u32) {
841 let start = self.projections.len() as u32;
842 self.projections.extend(projs);
843 let len = self.projections.len() as u32 - start;
844 (start, len)
845 }
846
847 #[inline]
849 pub fn get_projections(&self, start: u32, len: u32) -> &[Projection] {
850 &self.projections[start as usize..(start + len) as usize]
851 }
852
853 #[inline]
855 pub fn get_place_projections(&self, place: &Place) -> &[Projection] {
856 self.get_projections(place.proj_start, place.proj_len)
857 }
858
859 pub fn make_place(
864 &mut self,
865 base: PlaceBase,
866 projs: impl IntoIterator<Item = Projection>,
867 ) -> Place {
868 let (proj_start, proj_len) = self.push_projections(projs);
869 Place {
870 base,
871 proj_start,
872 proj_len,
873 }
874 }
875
876 #[inline]
882 pub fn get_goto_args(&self, term: &Terminator) -> &[CfgValue] {
883 match term {
884 Terminator::Goto {
885 args_start,
886 args_len,
887 ..
888 } => self.get_extra(*args_start, *args_len),
889 _ => panic!("get_goto_args called on non-Goto terminator"),
890 }
891 }
892
893 #[inline]
899 pub fn get_branch_then_args(&self, term: &Terminator) -> &[CfgValue] {
900 match term {
901 Terminator::Branch {
902 then_args_start,
903 then_args_len,
904 ..
905 } => self.get_extra(*then_args_start, *then_args_len),
906 _ => panic!("get_branch_then_args called on non-Branch terminator"),
907 }
908 }
909
910 #[inline]
916 pub fn get_branch_else_args(&self, term: &Terminator) -> &[CfgValue] {
917 match term {
918 Terminator::Branch {
919 else_args_start,
920 else_args_len,
921 ..
922 } => self.get_extra(*else_args_start, *else_args_len),
923 _ => panic!("get_branch_else_args called on non-Branch terminator"),
924 }
925 }
926
927 pub fn add_inst_to_block(&mut self, block: BlockId, inst: CfgInst) -> CfgValue {
929 let value = self.add_inst(inst);
930 self.blocks[block.0 as usize].insts.push(value);
931 value
932 }
933
934 pub fn add_block_param(&mut self, block: BlockId, ty: Type) -> CfgValue {
936 let param_index = self.blocks[block.0 as usize].params.len() as u32;
937 let inst = CfgInst {
938 data: CfgInstData::BlockParam { index: param_index },
939 ty,
940 span: Span::new(0, 0),
941 };
942 let value = self.add_inst(inst);
943 self.blocks[block.0 as usize].params.push((value, ty));
944 value
945 }
946
947 pub fn set_terminator(&mut self, block: BlockId, term: Terminator) {
949 self.blocks[block.0 as usize].terminator = term;
950 }
951
952 pub fn blocks(&self) -> &[BasicBlock] {
954 &self.blocks
955 }
956
957 #[inline]
959 pub fn block_count(&self) -> usize {
960 self.blocks.len()
961 }
962
963 pub fn block_ids(&self) -> impl Iterator<Item = BlockId> {
965 (0..self.blocks.len() as u32).map(BlockId)
966 }
967
968 pub fn compute_predecessors(&mut self) {
970 for block in &mut self.blocks {
972 block.preds.clear();
973 }
974
975 let mut edges: Vec<(BlockId, BlockId)> = Vec::new();
977 for block in &self.blocks {
978 match &block.terminator {
979 Terminator::Goto { target, .. } => {
980 edges.push((block.id, *target));
981 }
982 Terminator::Branch {
983 then_block,
984 else_block,
985 ..
986 } => {
987 edges.push((block.id, *then_block));
988 edges.push((block.id, *else_block));
989 }
990 Terminator::Switch {
991 cases_start,
992 cases_len,
993 default,
994 ..
995 } => {
996 for (_, target) in self.get_switch_cases(*cases_start, *cases_len) {
997 edges.push((block.id, *target));
998 }
999 edges.push((block.id, *default));
1000 }
1001 Terminator::Return { .. } | Terminator::Unreachable | Terminator::None => {}
1002 }
1003 }
1004
1005 for (from, to) in edges {
1007 self.blocks[to.0 as usize].preds.push(from);
1008 }
1009 }
1010}
1011
1012impl fmt::Display for Cfg {
1013 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1014 writeln!(
1015 f,
1016 "cfg {} (return_type: {}) {{",
1017 self.fn_name,
1018 self.return_type.name()
1019 )?;
1020 for block in &self.blocks {
1021 write!(f, " {}:", block.id)?;
1022 if !block.params.is_empty() {
1023 write!(f, "(")?;
1024 for (i, (val, ty)) in block.params.iter().enumerate() {
1025 if i > 0 {
1026 write!(f, ", ")?;
1027 }
1028 write!(f, "{}: {}", val, ty.name())?;
1029 }
1030 write!(f, ")")?;
1031 }
1032 writeln!(f)?;
1033
1034 if !block.preds.is_empty() {
1036 write!(f, " ; preds: ")?;
1037 for (i, pred) in block.preds.iter().enumerate() {
1038 if i > 0 {
1039 write!(f, ", ")?;
1040 }
1041 write!(f, "{}", pred)?;
1042 }
1043 writeln!(f)?;
1044 }
1045
1046 for &val in &block.insts {
1048 let inst = self.get_inst(val);
1049 write!(f, " {} : {} = ", val, inst.ty.name())?;
1050 self.fmt_inst_data(f, &inst.data)?;
1051 writeln!(f)?;
1052 }
1053
1054 write!(f, " ")?;
1056 match &block.terminator {
1057 Terminator::Goto {
1058 target,
1059 args_start,
1060 args_len,
1061 } => {
1062 write!(f, "goto {}", target)?;
1063 let args = self.get_extra(*args_start, *args_len);
1064 if !args.is_empty() {
1065 write!(f, "(")?;
1066 for (i, arg) in args.iter().enumerate() {
1067 if i > 0 {
1068 write!(f, ", ")?;
1069 }
1070 write!(f, "{}", arg)?;
1071 }
1072 write!(f, ")")?;
1073 }
1074 }
1075 Terminator::Branch {
1076 cond,
1077 then_block,
1078 then_args_start,
1079 then_args_len,
1080 else_block,
1081 else_args_start,
1082 else_args_len,
1083 } => {
1084 write!(f, "branch {}, {}", cond, then_block)?;
1085 let then_args = self.get_extra(*then_args_start, *then_args_len);
1086 if !then_args.is_empty() {
1087 write!(f, "(")?;
1088 for (i, arg) in then_args.iter().enumerate() {
1089 if i > 0 {
1090 write!(f, ", ")?;
1091 }
1092 write!(f, "{}", arg)?;
1093 }
1094 write!(f, ")")?;
1095 }
1096 write!(f, ", {}", else_block)?;
1097 let else_args = self.get_extra(*else_args_start, *else_args_len);
1098 if !else_args.is_empty() {
1099 write!(f, "(")?;
1100 for (i, arg) in else_args.iter().enumerate() {
1101 if i > 0 {
1102 write!(f, ", ")?;
1103 }
1104 write!(f, "{}", arg)?;
1105 }
1106 write!(f, ")")?;
1107 }
1108 }
1109 Terminator::Switch {
1110 scrutinee,
1111 cases_start,
1112 cases_len,
1113 default,
1114 } => {
1115 write!(f, "switch {} [", scrutinee)?;
1116 let cases = self.get_switch_cases(*cases_start, *cases_len);
1117 for (i, (val, target)) in cases.iter().enumerate() {
1118 if i > 0 {
1119 write!(f, ", ")?;
1120 }
1121 write!(f, "{} => {}", val, target)?;
1122 }
1123 write!(f, "], default: {}", default)?;
1124 }
1125 Terminator::Return { value } => {
1126 if let Some(value) = value {
1127 write!(f, "return {}", value)?;
1128 } else {
1129 write!(f, "return")?;
1130 }
1131 }
1132 Terminator::Unreachable => {
1133 write!(f, "unreachable")?;
1134 }
1135 Terminator::None => {
1136 write!(f, "<no terminator>")?;
1137 }
1138 }
1139 writeln!(f)?;
1140 writeln!(f)?;
1141 }
1142 writeln!(f, "}}")
1143 }
1144}
1145
1146impl Cfg {
1147 fn fmt_inst_data(&self, f: &mut fmt::Formatter<'_>, data: &CfgInstData) -> fmt::Result {
1148 match data {
1149 CfgInstData::Const(v) => write!(f, "const {}", v),
1150 CfgInstData::FloatConst(bits) => write!(f, "const {}", f64::from_bits(*bits)),
1151 CfgInstData::BoolConst(v) => write!(f, "const {}", v),
1152 CfgInstData::StringConst(idx) => write!(f, "string_const @{}", idx),
1153 CfgInstData::Param { index } => write!(f, "param {}", index),
1154 CfgInstData::BlockParam { index } => write!(f, "block_param {}", index),
1155 CfgInstData::Add(lhs, rhs) => write!(f, "add {}, {}", lhs, rhs),
1156 CfgInstData::Sub(lhs, rhs) => write!(f, "sub {}, {}", lhs, rhs),
1157 CfgInstData::Mul(lhs, rhs) => write!(f, "mul {}, {}", lhs, rhs),
1158 CfgInstData::Div(lhs, rhs) => write!(f, "div {}, {}", lhs, rhs),
1159 CfgInstData::Mod(lhs, rhs) => write!(f, "mod {}, {}", lhs, rhs),
1160 CfgInstData::Eq(lhs, rhs) => write!(f, "eq {}, {}", lhs, rhs),
1161 CfgInstData::Ne(lhs, rhs) => write!(f, "ne {}, {}", lhs, rhs),
1162 CfgInstData::Lt(lhs, rhs) => write!(f, "lt {}, {}", lhs, rhs),
1163 CfgInstData::Gt(lhs, rhs) => write!(f, "gt {}, {}", lhs, rhs),
1164 CfgInstData::Le(lhs, rhs) => write!(f, "le {}, {}", lhs, rhs),
1165 CfgInstData::Ge(lhs, rhs) => write!(f, "ge {}, {}", lhs, rhs),
1166 CfgInstData::BitAnd(lhs, rhs) => write!(f, "bit_and {}, {}", lhs, rhs),
1167 CfgInstData::BitOr(lhs, rhs) => write!(f, "bit_or {}, {}", lhs, rhs),
1168 CfgInstData::BitXor(lhs, rhs) => write!(f, "bit_xor {}, {}", lhs, rhs),
1169 CfgInstData::Shl(lhs, rhs) => write!(f, "shl {}, {}", lhs, rhs),
1170 CfgInstData::Shr(lhs, rhs) => write!(f, "shr {}, {}", lhs, rhs),
1171 CfgInstData::Neg(v) => write!(f, "neg {}", v),
1172 CfgInstData::Not(v) => write!(f, "not {}", v),
1173 CfgInstData::BitNot(v) => write!(f, "bit_not {}", v),
1174 CfgInstData::Alloc { slot, init } => write!(f, "alloc ${} = {}", slot, init),
1175 CfgInstData::Load { slot } => write!(f, "load ${}", slot),
1176 CfgInstData::Store { slot, value } => write!(f, "store ${} = {}", slot, value),
1177 CfgInstData::ParamStore { param_slot, value } => {
1178 write!(f, "param_store %{} = {}", param_slot, value)
1179 }
1180 CfgInstData::PlaceRead { place } => {
1181 write!(f, "place_read ")?;
1182 self.fmt_place(f, place)
1183 }
1184 CfgInstData::PlaceWrite { place, value } => {
1185 write!(f, "place_write ")?;
1186 self.fmt_place(f, place)?;
1187 write!(f, " = {}", value)
1188 }
1189 CfgInstData::Call {
1190 name,
1191 args_start,
1192 args_len,
1193 } => {
1194 write!(f, "call @{}(", name.into_usize())?;
1196 let args = self.get_call_args(*args_start, *args_len);
1197 for (i, arg) in args.iter().enumerate() {
1198 if i > 0 {
1199 write!(f, ", ")?;
1200 }
1201 match arg.mode {
1202 CfgArgMode::Inout => write!(f, "inout {}", arg.value)?,
1203 CfgArgMode::Borrow => write!(f, "borrow {}", arg.value)?,
1204 CfgArgMode::Normal => write!(f, "{}", arg.value)?,
1205 }
1206 }
1207 write!(f, ")")
1208 }
1209 CfgInstData::Intrinsic {
1210 name,
1211 args_start,
1212 args_len,
1213 } => {
1214 write!(f, "intrinsic @{}(", name.into_usize())?;
1216 let args = self.get_extra(*args_start, *args_len);
1217 for (i, arg) in args.iter().enumerate() {
1218 if i > 0 {
1219 write!(f, ", ")?;
1220 }
1221 write!(f, "{}", arg)?;
1222 }
1223 write!(f, ")")
1224 }
1225 CfgInstData::StructInit {
1226 struct_id,
1227 fields_start,
1228 fields_len,
1229 } => {
1230 write!(f, "struct_init #{} {{", struct_id.0)?;
1231 let fields = self.get_extra(*fields_start, *fields_len);
1232 for (i, field) in fields.iter().enumerate() {
1233 if i > 0 {
1234 write!(f, ", ")?;
1235 }
1236 write!(f, "{}", field)?;
1237 }
1238 write!(f, "}}")
1239 }
1240 CfgInstData::FieldSet {
1241 slot,
1242 struct_id,
1243 field_index,
1244 value,
1245 } => {
1246 write!(
1247 f,
1248 "field_set ${}.#{}.{} = {}",
1249 slot, struct_id.0, field_index, value
1250 )
1251 }
1252 CfgInstData::ParamFieldSet {
1253 param_slot,
1254 inner_offset,
1255 struct_id,
1256 field_index,
1257 value,
1258 } => {
1259 write!(
1260 f,
1261 "param_field_set %{}+{}.#{}.{} = {}",
1262 param_slot, inner_offset, struct_id.0, field_index, value
1263 )
1264 }
1265 CfgInstData::ArrayInit {
1266 elements_start,
1267 elements_len,
1268 } => {
1269 write!(f, "array_init [")?;
1270 let elements = self.get_extra(*elements_start, *elements_len);
1271 for (i, elem) in elements.iter().enumerate() {
1272 if i > 0 {
1273 write!(f, ", ")?;
1274 }
1275 write!(f, "{}", elem)?;
1276 }
1277 write!(f, "]")
1278 }
1279 CfgInstData::IndexSet {
1280 slot,
1281 array_type,
1282 index,
1283 value,
1284 } => {
1285 write!(
1286 f,
1287 "index_set ${}({})[{}] = {}",
1288 slot,
1289 array_type.name(),
1290 index,
1291 value
1292 )
1293 }
1294 CfgInstData::ParamIndexSet {
1295 param_slot,
1296 array_type,
1297 index,
1298 value,
1299 } => {
1300 write!(
1301 f,
1302 "param_index_set %{}({})[{}] = {}",
1303 param_slot,
1304 array_type.name(),
1305 index,
1306 value
1307 )
1308 }
1309 CfgInstData::EnumVariant {
1310 enum_id,
1311 variant_index,
1312 } => {
1313 write!(f, "enum_variant #{}::{}", enum_id.0, variant_index)
1314 }
1315 CfgInstData::EnumCreate {
1316 enum_id,
1317 variant_index,
1318 fields_start,
1319 fields_len,
1320 } => {
1321 let fields = self.get_extra(*fields_start, *fields_len);
1322 let field_strs: Vec<String> = fields.iter().map(|v| format!("{}", v)).collect();
1323 write!(
1324 f,
1325 "enum_create #{}::{}({})",
1326 enum_id.0,
1327 variant_index,
1328 field_strs.join(", ")
1329 )
1330 }
1331 CfgInstData::EnumPayloadGet {
1332 base,
1333 variant_index,
1334 field_index,
1335 } => {
1336 write!(
1337 f,
1338 "enum_payload_get {} variant={} field={}",
1339 base, variant_index, field_index
1340 )
1341 }
1342 CfgInstData::IntCast { value, from_ty } => {
1343 write!(f, "intcast {} from {}", value, from_ty.name())
1344 }
1345 CfgInstData::FloatCast { value, from_ty } => {
1346 write!(f, "floatcast {} from {}", value, from_ty.name())
1347 }
1348 CfgInstData::IntToFloat { value, from_ty } => {
1349 write!(f, "int_to_float {} from {}", value, from_ty.name())
1350 }
1351 CfgInstData::FloatToInt { value, from_ty } => {
1352 write!(f, "float_to_int {} from {}", value, from_ty.name())
1353 }
1354 CfgInstData::Drop { value } => {
1355 write!(f, "drop {}", value)
1356 }
1357 CfgInstData::StorageLive { slot } => {
1358 write!(f, "storage_live ${}", slot)
1359 }
1360 CfgInstData::StorageDead { slot } => {
1361 write!(f, "storage_dead ${}", slot)
1362 }
1363 }
1364 }
1365
1366 fn fmt_place(&self, f: &mut fmt::Formatter<'_>, place: &Place) -> fmt::Result {
1368 match place.base {
1370 PlaceBase::Local(slot) => write!(f, "${}", slot)?,
1371 PlaceBase::Param(slot) => write!(f, "param%{}", slot)?,
1372 }
1373
1374 let projections = self.get_place_projections(place);
1376 for proj in projections {
1377 match proj {
1378 Projection::Field {
1379 struct_id,
1380 field_index,
1381 } => {
1382 write!(f, ".#{}.{}", struct_id.0, field_index)?;
1383 }
1384 Projection::Index { array_type, index } => {
1385 write!(f, "({})[{}]", array_type.name(), index)?;
1386 }
1387 }
1388 }
1389
1390 Ok(())
1391 }
1392}
1393
1394#[cfg(test)]
1395mod tests {
1396 use super::*;
1397
1398 #[test]
1399 fn test_block_id_size() {
1400 assert_eq!(std::mem::size_of::<BlockId>(), 4);
1401 }
1402
1403 #[test]
1404 fn test_cfg_value_size() {
1405 assert_eq!(std::mem::size_of::<CfgValue>(), 4);
1406 }
1407
1408 #[test]
1409 fn test_cfg_inst_size() {
1410 let cfg_inst_size = std::mem::size_of::<CfgInst>();
1413 let cfg_inst_data_size = std::mem::size_of::<CfgInstData>();
1414
1415 assert!(
1418 cfg_inst_size <= 48,
1419 "CfgInst grew beyond 48 bytes: {}",
1420 cfg_inst_size
1421 );
1422 assert!(
1423 cfg_inst_data_size <= 32,
1424 "CfgInstData grew beyond 32 bytes: {}",
1425 cfg_inst_data_size
1426 );
1427 }
1428
1429 #[test]
1430 fn test_terminator_size() {
1431 let size = std::mem::size_of::<Terminator>();
1435 assert!(size <= 40, "Terminator is {} bytes, expected <= 40", size);
1436 }
1437
1438 #[test]
1439 fn test_create_cfg() {
1440 let mut cfg = Cfg::new(Type::I32, 0, 0, "test".to_string(), vec![], vec![]);
1441 let entry = cfg.new_block();
1442 cfg.entry = entry;
1443
1444 let const_val = cfg.add_inst_to_block(
1445 entry,
1446 CfgInst {
1447 data: CfgInstData::Const(42),
1448 ty: Type::I32,
1449 span: Span::new(0, 2),
1450 },
1451 );
1452
1453 cfg.set_terminator(
1454 entry,
1455 Terminator::Return {
1456 value: Some(const_val),
1457 },
1458 );
1459
1460 assert_eq!(cfg.block_count(), 1);
1461 }
1462}