From 1a26190c5372bc05d56c54cd889dcb3a42d258d5 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Thu, 23 Jan 2025 14:06:44 -0600
Subject: [PATCH 1/8] Detect mixing of field and other reads/writes and panic
 for now

---
 hercules_opt/src/sroa.rs      | 46 +++++++++++++++++++++++++++++------
 juno_scheduler/src/default.rs |  4 +--
 2 files changed, 40 insertions(+), 10 deletions(-)

diff --git a/hercules_opt/src/sroa.rs b/hercules_opt/src/sroa.rs
index 6461ad71..97280cfe 100644
--- a/hercules_opt/src/sroa.rs
+++ b/hercules_opt/src/sroa.rs
@@ -33,6 +33,8 @@ use crate::*;
  *
  * - Read: the read node reads primitive fields from product values - these get
  *   replaced by a direct use of the field value
+ *   A read can also extract a product from an array or sum; the value read out
+ *   will be broken into individual fields (by individual reads from the array)
  *
  * - Write: the write node writes primitive fields in product values - these get
  *   replaced by a direct def of the field value
@@ -57,12 +59,11 @@ pub fn sroa(editor: &mut FunctionEditor, reverse_postorder: &Vec<NodeID>, types:
     let func = editor.func();
 
     for node in reverse_postorder {
-        match func.nodes[node.idx()] {
+        match &func.nodes[node.idx()] {
             Node::Phi { .. }
             | Node::Reduce { .. }
             | Node::Parameter { .. }
             | Node::Constant { .. }
-            | Node::Write { .. }
             | Node::Ternary {
                 first: _,
                 second: _,
@@ -70,8 +71,38 @@ pub fn sroa(editor: &mut FunctionEditor, reverse_postorder: &Vec<NodeID>, types:
                 op: TernaryOperator::Select,
             } if editor.get_type(types[&node]).is_product() => product_nodes.push(*node),
 
-            Node::Read { collect, .. } if editor.get_type(types[&collect]).is_product() => {
-                product_nodes.push(*node)
+            Node::Write { collect, data, indices } => {
+                if editor.get_type(types[&collect]).is_product() {
+                    if !indices.iter().all(|i| i.is_field()) {
+                        // For a write, we will split the write into two pieces
+                        // splitting at the first index that's not a field
+                        todo!("Mixing array index and fields in a write not supported");
+                    } else {
+                        product_nodes.push(*node);
+                    }
+                } else if editor.get_type(types[&data]).is_product() {
+                    // If we're writing a product into a non-product (either here or from the
+                    // writes that can be generated by splitting above) we should replace the write
+                    // by a sequence of writes that read each field of the product and write them
+                    // into the collection, then those write nodes can be ignored for SROA but the
+                    // reads will be handled by SROA
+                    todo!("Writing a product into a non-product is not supported");
+                }
+            }
+            Node::Read { collect: _, indices } => {
+                if indices.iter().all(|i| i.is_field()) {
+                    product_nodes.push(*node);
+                } else if indices.iter().any(|i| i.is_field()) {
+                    // For a read, we split the read into a series of reads where each piece has
+                    // either only field reads or no field reads. Those with fields are the only
+                    // ones considered during SROA but any read that whose collection is not a
+                    // product but produces a product (i.e. if there's an array of products) then
+                    // following the read we replae the read that produces a product by reads of
+                    // each field and add that information to the node map for the rest of SROA
+                    // (this produces some reads that mix types of indices, since we only read
+                    // leaves but that's okay since those reads are not handled by SROA)
+                    todo!("Mixing array index and fields in a read is not supported");
+                }
             }
 
             // We add all calls to the call/return list and check their arguments later
@@ -516,8 +547,7 @@ impl<T: std::fmt::Debug> IndexTree<T> {
                     IndexTree::Node(ts) => ts[i].lookup_idx(idx, n + 1),
                 }
             } else {
-                // TODO: This could be hit because of an array inside of a product
-                panic!("Error handling lookup of field");
+                panic!("Cannot process a mix of fields and other read indices; pre-processing should prevent this");
             }
         } else {
             self
@@ -548,7 +578,7 @@ impl<T: std::fmt::Debug> IndexTree<T> {
                     }
                 }
             } else {
-                panic!("Error handling set of field");
+                panic!("Cannot process a mix of fields and other read indices; pre-processing should prevent this");
             }
         } else {
             IndexTree::Leaf(val)
@@ -579,7 +609,7 @@ impl<T: std::fmt::Debug> IndexTree<T> {
                     }
                 }
             } else {
-                panic!("Error handling set of field");
+                panic!("Cannot process a mix of fields and other read indices; pre-processing should prevent this");
             }
         } else {
             val
diff --git a/juno_scheduler/src/default.rs b/juno_scheduler/src/default.rs
index 8274b81a..d32491bd 100644
--- a/juno_scheduler/src/default.rs
+++ b/juno_scheduler/src/default.rs
@@ -33,9 +33,9 @@ pub fn default_schedule() -> ScheduleStmt {
         DCE,
         PhiElim,
         DCE,
-        CRC,
+        //CRC,
         DCE,
-        SLF,
+        //SLF,
         DCE,
         Inline,
         /*DeleteUncalled,*/
-- 
GitLab


From 399f54061e63e5384b3f0f09820ea8a97c7af8a9 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Thu, 23 Jan 2025 16:24:11 -0600
Subject: [PATCH 2/8] Handle mixed field and array writes

---
 hercules_opt/src/sroa.rs | 131 ++++++++++++++++++++++++++++++++++-----
 1 file changed, 117 insertions(+), 14 deletions(-)

diff --git a/hercules_opt/src/sroa.rs b/hercules_opt/src/sroa.rs
index 97280cfe..a81f294e 100644
--- a/hercules_opt/src/sroa.rs
+++ b/hercules_opt/src/sroa.rs
@@ -56,10 +56,8 @@ pub fn sroa(editor: &mut FunctionEditor, reverse_postorder: &Vec<NodeID>, types:
     // for the call's arguments or the return's value
     let mut call_return_nodes: Vec<NodeID> = vec![];
 
-    let func = editor.func();
-
     for node in reverse_postorder {
-        match &func.nodes[node.idx()] {
+        match &editor.func().nodes[node.idx()] {
             Node::Phi { .. }
             | Node::Reduce { .. }
             | Node::Parameter { .. }
@@ -71,25 +69,98 @@ pub fn sroa(editor: &mut FunctionEditor, reverse_postorder: &Vec<NodeID>, types:
                 op: TernaryOperator::Select,
             } if editor.get_type(types[&node]).is_product() => product_nodes.push(*node),
 
-            Node::Write { collect, data, indices } => {
-                if editor.get_type(types[&collect]).is_product() {
-                    if !indices.iter().all(|i| i.is_field()) {
-                        // For a write, we will split the write into two pieces
-                        // splitting at the first index that's not a field
-                        todo!("Mixing array index and fields in a write not supported");
+            Node::Write {
+                collect,
+                data,
+                indices,
+            } => {
+                let (fields_write, write_prod_into_non) = {
+                    let mut fields = vec![];
+                    let mut remainder = vec![];
+
+                    let mut indices = indices.iter();
+                    while let Some(idx) = indices.next() {
+                        if idx.is_field() {
+                            fields.push(idx.clone());
+                        } else {
+                            remainder.push(idx.clone());
+                            remainder.extend(indices.cloned());
+                            break;
+                        }
+                    }
+
+                    if fields.is_empty() {
+                        if editor.get_type(types[data]).is_product() {
+                            (None, Some(*node))
+                        } else {
+                            (None, None)
+                        }
+                    } else if remainder.is_empty() {
+                        (Some(*node), None)
                     } else {
-                        product_nodes.push(*node);
+                        let collect = *collect;
+                        let data = *data;
+
+                        // We need to find the type of the collection that will be extracted from
+                        // the collection being modified when we read it at the fields index
+                        let after_fields_type = type_at_index(editor, types[&collect], &fields);
+
+                        let mut fields_write = None;
+                        let mut remainder_write = None;
+                        editor.edit(|mut edit| {
+                            let read_inner = edit.add_node(Node::Read {
+                                collect,
+                                indices: fields.clone().into(),
+                            });
+                            types.insert(read_inner, after_fields_type);
+                            product_nodes.push(read_inner);
+
+                            let rem_write = edit.add_node(Node::Write {
+                                collect: read_inner,
+                                data,
+                                indices: remainder.into(),
+                            });
+                            types.insert(rem_write, after_fields_type);
+                            remainder_write = Some(rem_write);
+
+                            let complete_write = edit.add_node(Node::Write {
+                                collect,
+                                data: rem_write,
+                                indices: fields.into(),
+                            });
+                            types.insert(complete_write, types[&collect]);
+                            fields_write = Some(complete_write);
+
+                            edit = edit.replace_all_uses(*node, complete_write)?;
+                            edit.delete_node(*node)
+                        });
+                        let fields_write = fields_write.unwrap();
+                        let remainder_write = remainder_write.unwrap();
+
+                        if editor.get_type(types[&data]).is_product() {
+                            (Some(fields_write), Some(remainder_write))
+                        } else {
+                            (Some(fields_write), None)
+                        }
                     }
-                } else if editor.get_type(types[&data]).is_product() {
-                    // If we're writing a product into a non-product (either here or from the
-                    // writes that can be generated by splitting above) we should replace the write
+                };
+
+                if let Some(node) = fields_write {
+                    product_nodes.push(node);
+                }
+
+                if let Some(node) = write_prod_into_non {
+                    // If we're writing a product into a non-product we need to replace the write
                     // by a sequence of writes that read each field of the product and write them
                     // into the collection, then those write nodes can be ignored for SROA but the
                     // reads will be handled by SROA
                     todo!("Writing a product into a non-product is not supported");
                 }
             }
-            Node::Read { collect: _, indices } => {
+            Node::Read {
+                collect: _,
+                indices,
+            } => {
                 if indices.iter().all(|i| i.is_field()) {
                     product_nodes.push(*node);
                 } else if indices.iter().any(|i| i.is_field()) {
@@ -688,6 +759,38 @@ impl<T: std::fmt::Debug> IndexTree<T> {
     }
 }
 
+// Given the editor, type of some collection, and a list of indices to access that type at, returns
+// the TypeID of accessing the collection at the given indices
+fn type_at_index(editor: &FunctionEditor, typ: TypeID, idx: &[Index]) -> TypeID {
+    let mut typ = typ;
+    for index in idx {
+        match index {
+            Index::Field(i) => {
+                let Type::Product(ref ts) = *editor.get_type(typ) else {
+                    panic!("Accessing a field of a non-product type; did typechecking succeed?");
+                };
+                typ = ts[*i];
+            }
+            Index::Variant(i) => {
+                let Type::Summation(ref ts) = *editor.get_type(typ) else {
+                    panic!(
+                        "Accessing a variant of a non-summation type; did typechecking succeed?"
+                    );
+                };
+                typ = ts[*i];
+            }
+            Index::Position(pos) => {
+                let Type::Array(elem, ref dims) = *editor.get_type(typ) else {
+                    panic!("Accessing an array position of a non-array type; did typechecking succeed?");
+                };
+                assert!(pos.len() == dims.len(), "Read mismatch array dimensions");
+                typ = elem;
+            }
+        }
+    }
+    return typ;
+}
+
 // Given a product value val of type typ, constructs a copy of that value by extracting all fields
 // from that value and then writing them into a new constant
 // This process also adds all the read nodes that are generated into the read_list so that the
-- 
GitLab


From 69fda48b148d5893d29504cdeba54db6871f2d08 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Thu, 23 Jan 2025 16:29:49 -0600
Subject: [PATCH 3/8] Re-enable CRC, now breaking SROA again

---
 juno_scheduler/src/default.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/juno_scheduler/src/default.rs b/juno_scheduler/src/default.rs
index d32491bd..3d2ccbb2 100644
--- a/juno_scheduler/src/default.rs
+++ b/juno_scheduler/src/default.rs
@@ -33,7 +33,7 @@ pub fn default_schedule() -> ScheduleStmt {
         DCE,
         PhiElim,
         DCE,
-        //CRC,
+        CRC,
         DCE,
         //SLF,
         DCE,
-- 
GitLab


From 71a69ae2cdc1598e80be3bf04a149da3430a161b Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Thu, 23 Jan 2025 17:15:29 -0600
Subject: [PATCH 4/8] Handle mixed indices for reads in SROA

---
 hercules_opt/src/sroa.rs | 95 +++++++++++++++++++++++++++++++++-------
 1 file changed, 79 insertions(+), 16 deletions(-)

diff --git a/hercules_opt/src/sroa.rs b/hercules_opt/src/sroa.rs
index a81f294e..16af0866 100644
--- a/hercules_opt/src/sroa.rs
+++ b/hercules_opt/src/sroa.rs
@@ -74,6 +74,8 @@ pub fn sroa(editor: &mut FunctionEditor, reverse_postorder: &Vec<NodeID>, types:
                 data,
                 indices,
             } => {
+                // For a write, we may need to split it into two pieces if the it contains a mix of
+                // field and non-field indices
                 let (fields_write, write_prod_into_non) = {
                     let mut fields = vec![];
                     let mut remainder = vec![];
@@ -98,6 +100,7 @@ pub fn sroa(editor: &mut FunctionEditor, reverse_postorder: &Vec<NodeID>, types:
                     } else if remainder.is_empty() {
                         (Some(*node), None)
                     } else {
+                        // Here we perform the split into two writes
                         let collect = *collect;
                         let data = *data;
 
@@ -157,22 +160,82 @@ pub fn sroa(editor: &mut FunctionEditor, reverse_postorder: &Vec<NodeID>, types:
                     todo!("Writing a product into a non-product is not supported");
                 }
             }
-            Node::Read {
-                collect: _,
-                indices,
-            } => {
-                if indices.iter().all(|i| i.is_field()) {
-                    product_nodes.push(*node);
-                } else if indices.iter().any(|i| i.is_field()) {
-                    // For a read, we split the read into a series of reads where each piece has
-                    // either only field reads or no field reads. Those with fields are the only
-                    // ones considered during SROA but any read that whose collection is not a
-                    // product but produces a product (i.e. if there's an array of products) then
-                    // following the read we replae the read that produces a product by reads of
-                    // each field and add that information to the node map for the rest of SROA
-                    // (this produces some reads that mix types of indices, since we only read
-                    // leaves but that's okay since those reads are not handled by SROA)
-                    todo!("Mixing array index and fields in a read is not supported");
+            Node::Read { collect, indices } => {
+                // For a read, we split the read into a series of reads where each piece has either
+                // only field reads or no field reads. Those with fields are the only ones
+                // considered during SROA but any read whose collection is not a product but
+                // produces a product (i.e. if there's an array of products) then following the
+                // read we replae the read that produces a product by reads of each field and add
+                // that information to the node map for the rest of SROA (this produces some reads
+                // that mix types of indices, since we only read leaves but that's okay since those
+                // reads are not handled by SROA)
+                let indices = indices
+                    .chunk_by(|i, j| i.is_field() && j.is_field())
+                    .collect::<Vec<_>>();
+
+                let (field_reads, non_fields_produce_prod) = {
+                    if indices.len() == 0 {
+                        // If there are no indices then there were no indices originally, this is
+                        // only used with clones of arrays
+                        (vec![], vec![])
+                    } else if indices.len() == 1 {
+                        // If once we perform chunking there's only one set of indices, we can just
+                        // use the original node
+                        if indices[0][0].is_field() {
+                            (vec![*node], vec![])
+                        } else if editor.get_type(types[node]).is_product() {
+                            (vec![], vec![*node])
+                        } else {
+                            (vec![], vec![])
+                        }
+                    } else {
+                        let mut field_reads = vec![];
+                        let mut non_field = vec![];
+
+                        // To construct the multiple reads we need to track the current collection
+                        // and the type of that collection
+                        let mut collect = *collect;
+                        let mut typ = types[&collect];
+
+                        let indices = indices
+                            .into_iter()
+                            .map(|i| i.into_iter().cloned().collect::<Vec<_>>())
+                            .collect::<Vec<_>>();
+                        for index in indices {
+                            let is_field_read = index[0].is_field();
+                            let field_type = type_at_index(editor, typ, &index);
+
+                            editor.edit(|mut edit| {
+                                collect = edit.add_node(Node::Read {
+                                    collect,
+                                    indices: index.into(),
+                                });
+                                typ = field_type;
+                                Ok(edit)
+                            });
+
+                            if is_field_read {
+                                field_reads.push(collect);
+                            } else if editor.get_type(typ).is_product() {
+                                non_field.push(collect);
+                            }
+                        }
+
+                        // Replace all uses of the original read (with mixed indices) with the
+                        // newly constructed reads
+                        editor.edit(|mut edit| {
+                            edit = edit.replace_all_uses(*node, collect)?;
+                            edit.delete_node(*node)
+                        });
+
+                        (field_reads, non_field)
+                    }
+                };
+
+                product_nodes.extend(field_reads);
+
+                for node in non_fields_produce_prod {
+                    todo!("Reading a product out of a non-product is not suppoprted");
                 }
             }
 
-- 
GitLab


From 0cb3ab9f0c60a67644a78eedda5c3f41f96dc371 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Thu, 23 Jan 2025 17:16:01 -0600
Subject: [PATCH 5/8] Re-enable all of default pass pipeline

---
 juno_scheduler/src/default.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/juno_scheduler/src/default.rs b/juno_scheduler/src/default.rs
index 3d2ccbb2..8274b81a 100644
--- a/juno_scheduler/src/default.rs
+++ b/juno_scheduler/src/default.rs
@@ -35,7 +35,7 @@ pub fn default_schedule() -> ScheduleStmt {
         DCE,
         CRC,
         DCE,
-        //SLF,
+        SLF,
         DCE,
         Inline,
         /*DeleteUncalled,*/
-- 
GitLab


From e30228218d96cb561cf692792631f702cfb491a5 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Thu, 23 Jan 2025 17:23:40 -0600
Subject: [PATCH 6/8] Add test for reading and writing products into/out-of
 arrays

---
 juno_samples/antideps/src/antideps.jn | 15 ++++++++++++++-
 juno_samples/antideps/src/main.rs     |  4 ++++
 juno_scheduler/src/default.rs         |  3 ++-
 3 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/juno_samples/antideps/src/antideps.jn b/juno_samples/antideps/src/antideps.jn
index 738ee6da..f40640d2 100644
--- a/juno_samples/antideps/src/antideps.jn
+++ b/juno_samples/antideps/src/antideps.jn
@@ -121,4 +121,17 @@ fn read_chains(input : i32) -> i32 {
   sub[1] = 99;
   arrs.0[1] = 99;
   return result + sub[1] - arrs.0[1];
-}
\ No newline at end of file
+}
+
+#[entry]
+fn array_of_structs(input: i32) -> i32 {
+  let arr : (i32, i32)[2];
+  let sub = arr[0];
+  sub.1 = input + 7;
+  arr[0] = sub;
+  arr[0].1 = input + 3;
+  let result = sub.1 + arr[0].1;
+  sub.1 = 99;
+  arr[0].1 = 99;
+  return result + sub.1 - arr[0].1;
+}
diff --git a/juno_samples/antideps/src/main.rs b/juno_samples/antideps/src/main.rs
index 6e5ed7a3..2f1e8efc 100644
--- a/juno_samples/antideps/src/main.rs
+++ b/juno_samples/antideps/src/main.rs
@@ -27,6 +27,10 @@ fn main() {
         let output = read_chains(2).await;
         println!("{}", output);
         assert_eq!(output, 14);
+
+        let output = array_of_structs(2).await;
+        println!("{}", output);
+        assert_eq!(output, 14);
     });
 }
 
diff --git a/juno_scheduler/src/default.rs b/juno_scheduler/src/default.rs
index 8274b81a..77ae7f23 100644
--- a/juno_scheduler/src/default.rs
+++ b/juno_scheduler/src/default.rs
@@ -35,11 +35,12 @@ pub fn default_schedule() -> ScheduleStmt {
         DCE,
         CRC,
         DCE,
-        SLF,
+        //SLF,
         DCE,
         Inline,
         /*DeleteUncalled,*/
         InterproceduralSROA,
+        Xdot,
         SROA,
         PhiElim,
         DCE,
-- 
GitLab


From c15d42d2e9ebdb0b9198940dd106f04cc64c8764 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Thu, 23 Jan 2025 21:20:33 -0600
Subject: [PATCH 7/8] Handle reading/writing products from/to non-products

---
 hercules_opt/src/sroa.rs      | 61 ++++++++++++++++++++++++++++-------
 juno_scheduler/src/default.rs |  1 -
 2 files changed, 49 insertions(+), 13 deletions(-)

diff --git a/hercules_opt/src/sroa.rs b/hercules_opt/src/sroa.rs
index 16af0866..66d11d69 100644
--- a/hercules_opt/src/sroa.rs
+++ b/hercules_opt/src/sroa.rs
@@ -74,6 +74,9 @@ pub fn sroa(editor: &mut FunctionEditor, reverse_postorder: &Vec<NodeID>, types:
                 data,
                 indices,
             } => {
+                let data = *data;
+                let collect = *collect;
+
                 // For a write, we may need to split it into two pieces if the it contains a mix of
                 // field and non-field indices
                 let (fields_write, write_prod_into_non) = {
@@ -92,8 +95,8 @@ pub fn sroa(editor: &mut FunctionEditor, reverse_postorder: &Vec<NodeID>, types:
                     }
 
                     if fields.is_empty() {
-                        if editor.get_type(types[data]).is_product() {
-                            (None, Some(*node))
+                        if editor.get_type(types[&data]).is_product() {
+                            (None, Some((*node, collect, remainder)))
                         } else {
                             (None, None)
                         }
@@ -101,13 +104,11 @@ pub fn sroa(editor: &mut FunctionEditor, reverse_postorder: &Vec<NodeID>, types:
                         (Some(*node), None)
                     } else {
                         // Here we perform the split into two writes
-                        let collect = *collect;
-                        let data = *data;
-
                         // We need to find the type of the collection that will be extracted from
                         // the collection being modified when we read it at the fields index
                         let after_fields_type = type_at_index(editor, types[&collect], &fields);
 
+                        let mut inner_collection = None;
                         let mut fields_write = None;
                         let mut remainder_write = None;
                         editor.edit(|mut edit| {
@@ -117,11 +118,12 @@ pub fn sroa(editor: &mut FunctionEditor, reverse_postorder: &Vec<NodeID>, types:
                             });
                             types.insert(read_inner, after_fields_type);
                             product_nodes.push(read_inner);
+                            inner_collection = Some(read_inner);
 
                             let rem_write = edit.add_node(Node::Write {
                                 collect: read_inner,
                                 data,
-                                indices: remainder.into(),
+                                indices: remainder.clone().into(),
                             });
                             types.insert(rem_write, after_fields_type);
                             remainder_write = Some(rem_write);
@@ -137,11 +139,15 @@ pub fn sroa(editor: &mut FunctionEditor, reverse_postorder: &Vec<NodeID>, types:
                             edit = edit.replace_all_uses(*node, complete_write)?;
                             edit.delete_node(*node)
                         });
+                        let inner_collection = inner_collection.unwrap();
                         let fields_write = fields_write.unwrap();
                         let remainder_write = remainder_write.unwrap();
 
                         if editor.get_type(types[&data]).is_product() {
-                            (Some(fields_write), Some(remainder_write))
+                            (
+                                Some(fields_write),
+                                Some((remainder_write, inner_collection, remainder)),
+                            )
                         } else {
                             (Some(fields_write), None)
                         }
@@ -152,12 +158,42 @@ pub fn sroa(editor: &mut FunctionEditor, reverse_postorder: &Vec<NodeID>, types:
                     product_nodes.push(node);
                 }
 
-                if let Some(node) = write_prod_into_non {
+                if let Some((write_node, collection, index)) = write_prod_into_non {
+                    let node = write_node;
                     // If we're writing a product into a non-product we need to replace the write
                     // by a sequence of writes that read each field of the product and write them
                     // into the collection, then those write nodes can be ignored for SROA but the
                     // reads will be handled by SROA
-                    todo!("Writing a product into a non-product is not supported");
+
+                    // The value being written must be the data and so must be a product
+                    assert!(editor.get_type(types[&data]).is_product());
+                    let fields = generate_reads(editor, types[&data], data);
+
+                    let mut collection = collection;
+                    let collection_type = types[&collection];
+
+                    fields.for_each(|field: &Vec<Index>, val: &NodeID| {
+                        product_nodes.push(*val);
+                        editor.edit(|mut edit| {
+                            collection = edit.add_node(Node::Write {
+                                collect: collection,
+                                data: *val,
+                                indices: index
+                                    .iter()
+                                    .chain(field)
+                                    .cloned()
+                                    .collect::<Vec<_>>()
+                                    .into(),
+                            });
+                            types.insert(collection, collection_type);
+                            Ok(edit)
+                        });
+                    });
+
+                    editor.edit(|mut edit| {
+                        edit = edit.replace_all_uses(node, collection)?;
+                        edit.delete_node(node)
+                    });
                 }
             }
             Node::Read { collect, indices } => {
@@ -165,7 +201,7 @@ pub fn sroa(editor: &mut FunctionEditor, reverse_postorder: &Vec<NodeID>, types:
                 // only field reads or no field reads. Those with fields are the only ones
                 // considered during SROA but any read whose collection is not a product but
                 // produces a product (i.e. if there's an array of products) then following the
-                // read we replae the read that produces a product by reads of each field and add
+                // read we replace the read that produces a product by reads of each field and add
                 // that information to the node map for the rest of SROA (this produces some reads
                 // that mix types of indices, since we only read leaves but that's okay since those
                 // reads are not handled by SROA)
@@ -210,6 +246,7 @@ pub fn sroa(editor: &mut FunctionEditor, reverse_postorder: &Vec<NodeID>, types:
                                     collect,
                                     indices: index.into(),
                                 });
+                                types.insert(collect, field_type);
                                 typ = field_type;
                                 Ok(edit)
                             });
@@ -235,7 +272,7 @@ pub fn sroa(editor: &mut FunctionEditor, reverse_postorder: &Vec<NodeID>, types:
                 product_nodes.extend(field_reads);
 
                 for node in non_fields_produce_prod {
-                    todo!("Reading a product out of a non-product is not suppoprted");
+                    field_map.insert(node, generate_reads(editor, types[&node], node));
                 }
             }
 
@@ -892,7 +929,7 @@ fn reconstruct_product(
 }
 
 // Given a node val of type typ, adds nodes to the function which read all (leaf) fields of val and
-// returns a list of pairs of the indices and the node that reads that index
+// returns an IndexTree that tracks the nodes reading each leaf field
 fn generate_reads(editor: &mut FunctionEditor, typ: TypeID, val: NodeID) -> IndexTree<NodeID> {
     let res = generate_reads_at_index(editor, typ, val, vec![]);
     res
diff --git a/juno_scheduler/src/default.rs b/juno_scheduler/src/default.rs
index 77ae7f23..3d2ccbb2 100644
--- a/juno_scheduler/src/default.rs
+++ b/juno_scheduler/src/default.rs
@@ -40,7 +40,6 @@ pub fn default_schedule() -> ScheduleStmt {
         Inline,
         /*DeleteUncalled,*/
         InterproceduralSROA,
-        Xdot,
         SROA,
         PhiElim,
         DCE,
-- 
GitLab


From 993d3a48f5fbbe931a5a2d894e6ac343f495ca83 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Thu, 23 Jan 2025 21:21:44 -0600
Subject: [PATCH 8/8] Restore pass order

---
 juno_scheduler/src/default.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/juno_scheduler/src/default.rs b/juno_scheduler/src/default.rs
index 3d2ccbb2..8274b81a 100644
--- a/juno_scheduler/src/default.rs
+++ b/juno_scheduler/src/default.rs
@@ -35,7 +35,7 @@ pub fn default_schedule() -> ScheduleStmt {
         DCE,
         CRC,
         DCE,
-        //SLF,
+        SLF,
         DCE,
         Inline,
         /*DeleteUncalled,*/
-- 
GitLab