passes: implement clobber-non-live mode

In the statepoint pass implement clobber-non-live mode, which
will insert instructions clobbering all the stack slots that are
considered dead at each statepoint. This is to help debugging
liveness issues.

Change-Id: I96dcf51c3a2b8275cab00468f1aa5997580abe99
Reviewed-on: https://go-review.googlesource.com/c/155498
Reviewed-by: Than McIntosh <thanm@google.com>
diff --git a/passes/GoStatepoints.cpp b/passes/GoStatepoints.cpp
index 041be22..20ee31c 100644
--- a/passes/GoStatepoints.cpp
+++ b/passes/GoStatepoints.cpp
@@ -93,9 +93,8 @@
 static cl::opt<std::string> PrintFunc("gogc-print-func", cl::Hidden,
                                       cl::init(""));
 
-// TODO: make sure ClobberNonLive work. In the original code it
-// is to clobber non-relocated pointers. We'll need a different
-// mechanism.
+// At each statepoint, clobber all the stack slots that are considered
+// dead, for debugging purposes.
 static cl::opt<bool> ClobberNonLive("gogc-clobber-non-live",
                                     cl::Hidden, cl::init(false));
 
@@ -335,6 +334,7 @@
 static void findLiveSetAtInst(Instruction *inst, GCPtrLivenessData &Data,
                               SetVector<Value *> &AddrTakenAllocas,
                               StatepointLiveSetTy &out,
+                              SetVector<Value *> &AllAllocas,
                               DefiningValueMapTy &DVCache);
 
 // TODO: Once we can get to the GCStrategy, this becomes
@@ -406,11 +406,13 @@
                           GCPtrLivenessData &OriginalLivenessData,
                           SetVector<Value *> &AddrTakenAllocas, CallSite CS,
                           PartiallyConstructedSafepointRecord &Result,
+                          SetVector<Value *> &AllAllocas,
                           DefiningValueMapTy &DVCache) {
   Instruction *Inst = CS.getInstruction();
 
   StatepointLiveSetTy LiveSet;
-  findLiveSetAtInst(Inst, OriginalLivenessData, AddrTakenAllocas, LiveSet, DVCache);
+  findLiveSetAtInst(Inst, OriginalLivenessData, AddrTakenAllocas,
+                    LiveSet, AllAllocas, DVCache);
 
   if (PrintLiveSet) {
     dbgs() << "Live Variables at " << *Inst << ":\n";
@@ -1569,12 +1571,20 @@
     SetVector<Value *> &BadLoads,
     DefiningValueMapTy &DVCache) {
   GCPtrLivenessData OriginalLivenessData;
+
+  // Find all allocas.
+  SetVector<Value *> AllAllocas;
+  if (ClobberNonLive)
+    for (Instruction &I : F.getEntryBlock())
+      if (isa<AllocaInst>(I) && hasPointer(I.getType()->getPointerElementType()))
+        AllAllocas.insert(&I);
+
   computeLiveInValues(DT, F, OriginalLivenessData, AddrTakenAllocas,
                       ToZero, BadLoads, DVCache);
   for (size_t i = 0; i < records.size(); i++) {
     struct PartiallyConstructedSafepointRecord &info = records[i];
     analyzeParsePointLiveness(DT, OriginalLivenessData, AddrTakenAllocas,
-                              toUpdate[i], info, DVCache);
+                              toUpdate[i], info, AllAllocas, DVCache);
   }
 }
 
@@ -1871,6 +1881,16 @@
 
   zeroAmbiguouslyLiveSlots(F, ToZero, AddrTakenAllocas);
 
+  // In clobber-non-live mode, delete all lifetime markers, as the
+  // inserted clobbering may be beyond the original lifetime.
+  if (ClobberNonLive)
+    for (Instruction &I : instructions(F))
+      if (CallInst *CI = dyn_cast<CallInst>(&I))
+        if (Function *Fn = CI->getCalledFunction())
+          if (Fn->getIntrinsicID() == Intrinsic::lifetime_start ||
+              Fn->getIntrinsicID() == Intrinsic::lifetime_end)
+            ToDel.insert(&I);
+
   // Delete dead Phis.
   for (Instruction *I : ToDel)
     I->eraseFromParent();
@@ -2723,9 +2743,14 @@
 #endif
 }
 
+// Compute the set of values live at Inst, store the result in Out.
+//
+// Side effect: in clobber-non-live mode, the clobbering instructions
+// are inserted here.
 static void findLiveSetAtInst(Instruction *Inst, GCPtrLivenessData &Data,
                               SetVector<Value *> &AddrTakenAllocas,
                               StatepointLiveSetTy &Out,
+                              SetVector<Value *> &AllAllocas,
                               DefiningValueMapTy &DVCache) {
   BasicBlock *BB = Inst->getParent();
 
@@ -2755,5 +2780,23 @@
         LiveOut.remove(V);
     }
 
+  // Clobber all non-live allocas.
+  if (ClobberNonLive) {
+    SetVector<Value *> ToClobber(AllAllocas);
+    ToClobber.set_subtract(LiveOut);
+    if (!ToClobber.empty()) {
+      IRBuilder<> Builder(Inst);
+      Type *Int8Ty = IntegerType::get(Inst->getModule()->getContext(), 8);
+      const DataLayout &DL = Inst->getModule()->getDataLayout();
+      Value *Bad = ConstantInt::get(Int8Ty, 0xff);
+      for (Value *Alloca : ToClobber) {
+        unsigned Siz =
+            DL.getTypeStoreSize(Alloca->getType()->getPointerElementType());
+        Builder.CreateMemSet(Alloca, Bad, Siz, 0);
+        //dbgs() << "clobber " << *Alloca << " at " << *Inst << "\n";
+      }
+    }
+  }
+
   Out.insert(LiveOut.begin(), LiveOut.end());
 }