build under older LLVM's, handle rust mangling
diff --git a/enzyme/Enzyme/GradientUtils.cpp b/enzyme/Enzyme/GradientUtils.cpp
index 4159e2a..43c857b 100644
--- a/enzyme/Enzyme/GradientUtils.cpp
+++ b/enzyme/Enzyme/GradientUtils.cpp
@@ -127,6 +127,10 @@
 llvm::cl::opt<bool>
     EnzymePrintDiffUse("enzyme-print-diffuse", cl::init(false), cl::Hidden,
                        cl::desc("Print differential use analysis"));
+
+llvm::cl::opt<std::string>
+    EnzymeRustDeallocName("rust-dealloc-name", cl::init(""), cl::Hidden,
+                            cl::desc("Name of Rust deallocation function"));
 }
 
 SmallVector<unsigned int, 9> MD_ToCopy = {
@@ -9474,6 +9478,7 @@
                                     GradientUtils *gutils) {
   assert(isAllocationFunction(allocationfn, TLI));
 
+#if LLVM_VERSION_MAJOR >= 17
   std::string demangledName = llvm::demangle(allocationfn);
   if (demangledName == "__rustc::__rust_alloc" || demangledName == "__rustc::__rust_alloc_zeroed" ) {
     Type *VoidTy = Type::getVoidTy(tofree->getContext());
@@ -9482,10 +9487,26 @@
     Type *inTys[3] = {IntPtrTy, RustSz, RustSz};
 
     auto FT = FunctionType::get(VoidTy, inTys, false);
+    if (EnzymeRustDeallocName == "") {
+      // Rust's (de)alloc names aren't stable. We expect rustc to set them 
+      // for us, but if it fails to do so we instead search for it here.
+      for (auto &F : *builder.GetInsertBlock()->getParent()->getParent()) {
+        auto demangledName = llvm::demangle(F.getName());
+        if (demangledName == "__rustc::__rust_dealloc") {
+          EnzymeRustDeallocName = F.getName();
+          break;
+        }
+      }
+      if (EnzymeRustDeallocName == "") {
+        // If we can't find it, use the raw __rust_dealloc as a fallback.
+        // FIXME: Make this a hard error once we pass the right name from rustc.
+        EnzymeRustDeallocName = "__rust_dealloc";
+      }
+    }
     Value *freevalue = builder.GetInsertBlock()
                            ->getParent()
                            ->getParent()
-                           ->getOrInsertFunction("__rust_dealloc", FT)
+                           ->getOrInsertFunction(EnzymeRustDeallocName, FT)
                            .getCallee();
     Value *vals[3];
     vals[0] = builder.CreatePointerCast(tofree, IntPtrTy);
@@ -9509,6 +9530,7 @@
       builder.Insert(freecall);
     return freecall;
   }
+#endif
   if (allocationfn == "julia.gc_alloc_obj" ||
       allocationfn == "jl_gc_alloc_typed" ||
       allocationfn == "ijl_gc_alloc_typed" ||
diff --git a/enzyme/Enzyme/LibraryFuncs.h b/enzyme/Enzyme/LibraryFuncs.h
index 299d9d6..8291e0a 100644
--- a/enzyme/Enzyme/LibraryFuncs.h
+++ b/enzyme/Enzyme/LibraryFuncs.h
@@ -56,9 +56,11 @@
     return true;
   if (name == "__size_returning_new_experiment")
     return true;
+#if LLVM_VERSION_MAJOR >= 17
   std::string demangledName = llvm::demangle(name.str());
   if (demangledName == "__rustc::__rust_alloc" || demangledName == "__rustc::__rust_alloc_zeroed")
     return true;
+#endif
   if (name == "julia.gc_alloc_obj" || name == "jl_gc_alloc_typed" ||
       name == "ijl_gc_alloc_typed")
     return true;
@@ -210,8 +212,12 @@
   assert(isAllocationFunction(funcName, TLI));
 
   // Don't re-zero an already-zero buffer
+#if LLVM_VERSION_MAJOR >= 17
     std::string demangledName = llvm::demangle(funcName.str());
-  if (funcName == "calloc" || demangledName == "__rustc::__rust_alloc_zeroed")
+  if (demangledName == "__rustc::__rust_alloc_zeroed")
+    return;
+#endif
+  if (funcName == "calloc")
     return;
 
   Value *allocSize = argValues[0];