From bdb6bfd840a18ceb1dcff92177f4d5a54e1385e7 Mon Sep 17 00:00:00 2001
From: Russel Arbore <russel.jma@gmail.com>
Date: Thu, 16 Jan 2025 12:49:47 -0800
Subject: [PATCH] take action on SLF lattice

---
 hercules_opt/src/pass.rs | 33 +++++++++++++++++++++
 hercules_opt/src/slf.rs  | 63 ++++++++++++++++++++++++++++++++++++++--
 juno_frontend/src/lib.rs |  2 ++
 3 files changed, 95 insertions(+), 3 deletions(-)

diff --git a/hercules_opt/src/pass.rs b/hercules_opt/src/pass.rs
index d24b6563..1e7104ce 100644
--- a/hercules_opt/src/pass.rs
+++ b/hercules_opt/src/pass.rs
@@ -25,6 +25,7 @@ pub enum Pass {
     PhiElim,
     Forkify,
     ForkGuardElim,
+    SLF,
     WritePredication,
     Predication,
     SROA,
@@ -470,6 +471,38 @@ impl PassManager {
                     }
                     self.clear_analyses();
                 }
+                Pass::SLF => {
+                    self.make_def_uses();
+                    self.make_reverse_postorders();
+                    self.make_typing();
+                    let def_uses = self.def_uses.as_ref().unwrap();
+                    let reverse_postorders = self.reverse_postorders.as_ref().unwrap();
+                    let typing = self.typing.as_ref().unwrap();
+                    for idx in 0..self.module.functions.len() {
+                        let constants_ref =
+                            RefCell::new(std::mem::take(&mut self.module.constants));
+                        let dynamic_constants_ref =
+                            RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
+                        let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
+                        let mut editor = FunctionEditor::new(
+                            &mut self.module.functions[idx],
+                            FunctionID::new(idx),
+                            &constants_ref,
+                            &dynamic_constants_ref,
+                            &types_ref,
+                            &def_uses[idx],
+                        );
+                        slf(&mut editor, &reverse_postorders[idx], &typing[idx]);
+
+                        self.module.constants = constants_ref.take();
+                        self.module.dynamic_constants = dynamic_constants_ref.take();
+                        self.module.types = types_ref.take();
+
+                        println!("{}", self.module.functions[idx].name);
+                        self.module.functions[idx].delete_gravestones();
+                    }
+                    self.clear_analyses();
+                }
                 Pass::WritePredication => {
                     self.make_def_uses();
                     let def_uses = self.def_uses.as_ref().unwrap();
diff --git a/hercules_opt/src/slf.rs b/hercules_opt/src/slf.rs
index efbeae81..981a0cce 100644
--- a/hercules_opt/src/slf.rs
+++ b/hercules_opt/src/slf.rs
@@ -54,7 +54,10 @@ impl Semilattice for SLFLattice {
     }
 
     fn bottom() -> Self {
-        panic!()
+        let mut subvalues = BTreeMap::new();
+        // The empty indices set overlaps with all possible indices sets.
+        subvalues.insert(Box::new([]) as Box<[Index]>, None);
+        SLFLattice { subvalues }
     }
 }
 
@@ -63,7 +66,7 @@ impl Semilattice for SLFLattice {
  * known values inside collections and replaces reads of those values with the
  * values directly.
  */
-pub fn slf(editor: &mut FunctionEditor, reverse_postorder: &Vec<NodeID>) {
+pub fn slf(editor: &mut FunctionEditor, reverse_postorder: &Vec<NodeID>, typing: &Vec<TypeID>) {
     // First, run a dataflow analysis that looks at known values inside
     // collections. Thanks to the value semantics of Hercules IR, this analysis
     // is relatively simple and straightforward.
@@ -95,9 +98,63 @@ pub fn slf(editor: &mut FunctionEditor, reverse_postorder: &Vec<NodeID>) {
                 // Start with the indices of the `collect` input.
                 let mut value = inputs[0].clone();
 
+                // Any indices sets that overlap with `indices` become `None`,
+                // since we no longer know what's stored there.
+                for (other_indices, subvalue) in value.subvalues.iter_mut() {
+                    if indices_may_overlap(other_indices, indices) {
+                        *subvalue = None;
+                    }
+                }
+
+                // Track `data` at `indices`.
+                value.subvalues.insert(indices.clone(), Some(data));
+
                 value
             }
-            _ => SLFLattice::top(),
+            _ => SLFLattice::bottom(),
         }
     });
+
+    // Second, look for reads where the indices set either:
+    // 1. Equal the indices of a known sub-value. Then, the read can be replaced
+    //    by the known sub-value.
+    // 2. Otherwise, if the indices set doesn't overlap with any known or
+    //    unknown sub-value, then the read can be replaced by a zero constant.
+    // 3. Otherwise, the read can't be replaced.
+    // Keep track of which nodes we've already replaced, since a sub-value we
+    // knew previously may be the ID of an old node replaced previously.
+    let mut replacements = BTreeMap::new();
+    for id in editor.node_ids() {
+        let Node::Read {
+            collect,
+            ref indices,
+        } = editor.func().nodes[id.idx()]
+        else {
+            continue;
+        };
+        let subvalues = &lattice[collect.idx()].subvalues;
+
+        if let Some(sub_value) = subvalues.get(indices)
+            && let Some(mut known) = *sub_value
+        {
+            while let Some(replacement) = replacements.get(&known) {
+                known = *replacement;
+            }
+            editor.edit(|mut edit| {
+                edit = edit.replace_all_uses(id, known)?;
+                edit.delete_node(id)
+            });
+            replacements.insert(id, known);
+        } else if !subvalues
+            .keys()
+            .any(|other_indices| indices_may_overlap(other_indices, indices))
+        {
+            editor.edit(|mut edit| {
+                let zero = edit.add_zero_constant(typing[id.idx()]);
+                let zero = edit.add_node(Node::Constant { id: zero });
+                edit = edit.replace_all_uses(id, zero)?;
+                edit.delete_node(id)
+            });
+        }
+    }
 }
diff --git a/juno_frontend/src/lib.rs b/juno_frontend/src/lib.rs
index cfaf7a26..46d34891 100644
--- a/juno_frontend/src/lib.rs
+++ b/juno_frontend/src/lib.rs
@@ -182,6 +182,8 @@ pub fn compile_ir(
     add_pass!(pm, verify, WritePredication);
     add_pass!(pm, verify, PhiElim);
     add_pass!(pm, verify, DCE);
+    add_pass!(pm, verify, SLF);
+    add_pass!(pm, verify, DCE);
     add_pass!(pm, verify, Predication);
     add_pass!(pm, verify, DCE);
     add_pass!(pm, verify, CCP);
-- 
GitLab