Instrumented local variables and implemented runtime.

BUG=https://bugs.chromium.org/p/nativeclient/issues/detail?id=4374
R=kschimpf@google.com

Review URL: https://codereview.chromium.org/2095763002 .
diff --git a/src/IceASanInstrumentation.cpp b/src/IceASanInstrumentation.cpp
index f4b47e1..83ebc19 100644
--- a/src/IceASanInstrumentation.cpp
+++ b/src/IceASanInstrumentation.cpp
@@ -24,10 +24,12 @@
 
 #include <sstream>
 #include <unordered_map>
+#include <vector>
 
 namespace Ice {
 
 namespace {
+
 constexpr SizeT RzSize = 32;
 const std::string RzPrefix = "__$rz";
 const llvm::NaClBitcodeRecord::RecordVector RzContents =
@@ -42,6 +44,9 @@
 
 } // end of anonymous namespace
 
+ICE_TLS_DEFINE_FIELD(std::vector<InstCall *> *, ASanInstrumentation,
+                     LocalDtors);
+
 // Create redzones around all global variables, ensuring that the initializer
 // types of the redzones and their associated globals match so that they are
 // laid out together in memory.
@@ -126,38 +131,95 @@
 // Check for an alloca signaling the presence of local variables and add a
 // redzone if it is found
 void ASanInstrumentation::instrumentFuncStart(LoweringContext &Context) {
-  auto *FirstAlloca = llvm::dyn_cast<InstAlloca>(Context.getCur());
-  if (FirstAlloca == nullptr)
-    return;
+  if (ICE_TLS_GET_FIELD(LocalDtors) == nullptr)
+    ICE_TLS_SET_FIELD(LocalDtors, new std::vector<InstCall *>());
 
-  constexpr SizeT Alignment = 4;
-  InstAlloca *RzAlloca = createLocalRz(Context, RzSize, Alignment);
-
-  // insert before the current instruction
-  InstList::iterator Next = Context.getNext();
-  Context.setInsertPoint(Context.getCur());
-  Context.insert(RzAlloca);
-  Context.setNext(Next);
-}
-
-void ASanInstrumentation::instrumentAlloca(LoweringContext &Context,
-                                           InstAlloca *Instr) {
-  auto *VarSizeOp = llvm::dyn_cast<ConstantInteger32>(Instr->getSizeInBytes());
-  SizeT VarSize = (VarSizeOp == nullptr) ? RzSize : VarSizeOp->getValue();
-  SizeT Padding = Utils::OffsetToAlignment(VarSize, RzSize);
-  constexpr SizeT Alignment = 1;
-  InstAlloca *Rz = createLocalRz(Context, RzSize + Padding, Alignment);
-  Context.insert(Rz);
-}
-
-InstAlloca *ASanInstrumentation::createLocalRz(LoweringContext &Context,
-                                               SizeT Size, SizeT Alignment) {
   Cfg *Func = Context.getNode()->getCfg();
-  Variable *Rz = Func->makeVariable(IceType_i32);
-  Rz->setName(Func, nextRzName());
-  auto *ByteCount = ConstantInteger32::create(Ctx, IceType_i32, Size);
-  auto *RzAlloca = InstAlloca::create(Func, Rz, ByteCount, Alignment);
-  return RzAlloca;
+  bool HasLocals = false;
+  LoweringContext C;
+  C.init(Context.getNode());
+  std::vector<Inst *> Initializations;
+  Constant *InitFunc =
+      Ctx->getConstantExternSym(Ctx->getGlobalString("__asan_poison"));
+  Constant *DestroyFunc =
+      Ctx->getConstantExternSym(Ctx->getGlobalString("__asan_unpoison"));
+
+  InstAlloca *Cur;
+  ConstantInteger32 *VarSizeOp;
+  while (
+      (Cur = llvm::dyn_cast<InstAlloca>(iteratorToInst(C.getCur()))) &&
+      (VarSizeOp = llvm::dyn_cast<ConstantInteger32>(Cur->getSizeInBytes()))) {
+    HasLocals = true;
+
+    // create the new alloca that includes a redzone
+    SizeT VarSize = VarSizeOp->getValue();
+    Variable *Dest = Cur->getDest();
+    SizeT RzPadding = RzSize + Utils::OffsetToAlignment(VarSize, RzSize);
+    auto *ByteCount =
+        ConstantInteger32::create(Ctx, IceType_i32, VarSize + RzPadding);
+    constexpr SizeT Alignment = 8;
+    auto *NewVar = InstAlloca::create(Func, Dest, ByteCount, Alignment);
+
+    // calculate the redzone offset
+    Variable *RzLocVar = Func->makeVariable(IceType_i32);
+    RzLocVar->setName(Func, nextRzName());
+    auto *Offset = ConstantInteger32::create(Ctx, IceType_i32, VarSize);
+    auto *RzLoc = InstArithmetic::create(Func, InstArithmetic::Add, RzLocVar,
+                                         Dest, Offset);
+
+    // instructions to poison and unpoison the redzone
+    constexpr SizeT NumArgs = 2;
+    constexpr Variable *Void = nullptr;
+    constexpr bool NoTailcall = false;
+    auto *Init = InstCall::create(Func, NumArgs, Void, InitFunc, NoTailcall);
+    auto *Destroy =
+        InstCall::create(Func, NumArgs, Void, DestroyFunc, NoTailcall);
+    Init->addArg(RzLocVar);
+    Destroy->addArg(RzLocVar);
+    auto *RzSizeConst = ConstantInteger32::create(Ctx, IceType_i32, RzPadding);
+    Init->addArg(RzSizeConst);
+    Destroy->addArg(RzSizeConst);
+
+    Cur->setDeleted();
+    C.insert(NewVar);
+    ICE_TLS_GET_FIELD(LocalDtors)->emplace_back(Destroy);
+    Initializations.emplace_back(RzLoc);
+    Initializations.emplace_back(Init);
+
+    C.advanceCur();
+    C.advanceNext();
+  }
+
+  C.setInsertPoint(C.getCur());
+
+  // add the leftmost redzone
+  if (HasLocals) {
+    Variable *LastRz = Func->makeVariable(IceType_i32);
+    LastRz->setName(Func, nextRzName());
+    auto *ByteCount = ConstantInteger32::create(Ctx, IceType_i32, RzSize);
+    constexpr SizeT Alignment = 8;
+    auto *RzAlloca = InstAlloca::create(Func, LastRz, ByteCount, Alignment);
+
+    constexpr SizeT NumArgs = 2;
+    constexpr Variable *Void = nullptr;
+    constexpr bool NoTailcall = false;
+    auto *Init = InstCall::create(Func, NumArgs, Void, InitFunc, NoTailcall);
+    auto *Destroy =
+        InstCall::create(Func, NumArgs, Void, DestroyFunc, NoTailcall);
+    Init->addArg(LastRz);
+    Destroy->addArg(LastRz);
+    Init->addArg(RzAlloca->getSizeInBytes());
+    Destroy->addArg(RzAlloca->getSizeInBytes());
+
+    ICE_TLS_GET_FIELD(LocalDtors)->emplace_back(Destroy);
+    C.insert(RzAlloca);
+    C.insert(Init);
+  }
+
+  // insert initializers for the redzones
+  for (Inst *Init : Initializations) {
+    C.insert(Init);
+  }
 }
 
 void ASanInstrumentation::instrumentCall(LoweringContext &Context,
@@ -214,6 +276,15 @@
   Context.setNext(Next);
 }
 
+void ASanInstrumentation::instrumentRet(LoweringContext &Context, InstRet *) {
+  InstList::iterator Next = Context.getNext();
+  Context.setInsertPoint(Context.getCur());
+  for (InstCall *RzUnpoison : *ICE_TLS_GET_FIELD(LocalDtors)) {
+    Context.insert(RzUnpoison);
+  }
+  Context.setNext(Next);
+}
+
 void ASanInstrumentation::instrumentStart(Cfg *Func) {
   Constant *ShadowMemInit =
       Ctx->getConstantExternSym(Ctx->getGlobalString("__asan_init"));
@@ -224,4 +295,10 @@
   Func->getEntryNode()->getInsts().push_front(Call);
 }
 
+// TODO(tlively): make this more efficient with swap idiom
+void ASanInstrumentation::finishFunc(Cfg *Func) {
+  (void)Func;
+  ICE_TLS_GET_FIELD(LocalDtors)->clear();
+}
+
 } // end of namespace Ice
diff --git a/src/IceASanInstrumentation.h b/src/IceASanInstrumentation.h
index 2cf5c59..de250d4 100644
--- a/src/IceASanInstrumentation.h
+++ b/src/IceASanInstrumentation.h
@@ -31,7 +31,9 @@
   ASanInstrumentation &operator=(const ASanInstrumentation &) = delete;
 
 public:
-  ASanInstrumentation(GlobalContext *Ctx) : Instrumentation(Ctx), RzNum(0) {}
+  ASanInstrumentation(GlobalContext *Ctx) : Instrumentation(Ctx), RzNum(0) {
+    ICE_TLS_INIT_FIELD(LocalDtors);
+  }
   void instrumentGlobals(VariableDeclarationList &Globals) override;
 
 private:
@@ -40,15 +42,15 @@
                                 VariableDeclaration *RzArray,
                                 SizeT &RzArraySize,
                                 VariableDeclaration *Global);
-  InstAlloca *createLocalRz(LoweringContext &Context, SizeT Size,
-                            SizeT Alignment);
   void instrumentFuncStart(LoweringContext &Context) override;
-  void instrumentAlloca(LoweringContext &Context, InstAlloca *Instr) override;
   void instrumentCall(LoweringContext &Context, InstCall *Instr) override;
+  void instrumentRet(LoweringContext &Context, InstRet *Instr) override;
   void instrumentLoad(LoweringContext &Context, InstLoad *Instr) override;
   void instrumentStore(LoweringContext &Context, InstStore *Instr) override;
   void instrumentAccess(LoweringContext &Context, Operand *Op, SizeT Size);
   void instrumentStart(Cfg *Func) override;
+  void finishFunc(Cfg *Func) override;
+  ICE_TLS_DECLARE_FIELD(std::vector<InstCall *> *, LocalDtors);
   bool DidInsertRedZones = false;
   std::atomic<uint32_t> RzNum;
 };
diff --git a/src/IceCfg.cpp b/src/IceCfg.cpp
index a113b95..c2b4d06 100644
--- a/src/IceCfg.cpp
+++ b/src/IceCfg.cpp
@@ -645,16 +645,19 @@
   getTarget()->lowerArguments();
 }
 
-void Cfg::sortAndCombineAllocas(CfgVector<Inst *> &Allocas,
+void Cfg::sortAndCombineAllocas(CfgVector<InstAlloca *> &Allocas,
                                 uint32_t CombinedAlignment, InstList &Insts,
                                 AllocaBaseVariableType BaseVariableType) {
   if (Allocas.empty())
     return;
   // Sort by decreasing alignment.
-  std::sort(Allocas.begin(), Allocas.end(), [](Inst *I1, Inst *I2) {
-    auto *A1 = llvm::dyn_cast<InstAlloca>(I1);
-    auto *A2 = llvm::dyn_cast<InstAlloca>(I2);
-    return A1->getAlignInBytes() > A2->getAlignInBytes();
+  std::sort(Allocas.begin(), Allocas.end(), [](InstAlloca *A1, InstAlloca *A2) {
+    uint32_t Align1 = A1->getAlignInBytes();
+    uint32_t Align2 = A2->getAlignInBytes();
+    if (Align1 == Align2)
+      return A1->getNumber() > A2->getNumber();
+    else
+      return Align1 > Align2;
   });
   // Process the allocas in order of decreasing stack alignment.  This allows
   // us to pack less-aligned pieces after more-aligned ones, resulting in less
@@ -746,6 +749,8 @@
   bool HasLargeAlignment = false;
   bool HasDynamicAllocation = false;
   for (Inst &Instr : EntryNode->getInsts()) {
+    if (Instr.isDeleted())
+      continue;
     if (auto *Alloca = llvm::dyn_cast<InstAlloca>(&Instr)) {
       uint32_t AlignmentParam = Alloca->getAlignInBytes();
       if (AlignmentParam > StackAlignment)
@@ -769,6 +774,8 @@
     if (Node == EntryNode)
       continue;
     for (Inst &Instr : Node->getInsts()) {
+      if (Instr.isDeleted())
+        continue;
       if (llvm::isa<InstAlloca>(&Instr)) {
         // Allocations outside the entry block require a frame pointer.
         HasDynamicAllocation = true;
@@ -784,13 +791,15 @@
   // Collect the Allocas into the two vectors.
   // Allocas in the entry block that have constant size and alignment less
   // than or equal to the function's stack alignment.
-  CfgVector<Inst *> FixedAllocas;
+  CfgVector<InstAlloca *> FixedAllocas;
   // Allocas in the entry block that have constant size and alignment greater
   // than the function's stack alignment.
-  CfgVector<Inst *> AlignedAllocas;
+  CfgVector<InstAlloca *> AlignedAllocas;
   // Maximum alignment used by any alloca.
   uint32_t MaxAlignment = StackAlignment;
   for (Inst &Instr : EntryNode->getInsts()) {
+    if (Instr.isDeleted())
+      continue;
     if (auto *Alloca = llvm::dyn_cast<InstAlloca>(&Instr)) {
       if (!llvm::isa<Constant>(Alloca->getSizeInBytes()))
         continue;
diff --git a/src/IceCfg.h b/src/IceCfg.h
index e3f29c7..c656961 100644
--- a/src/IceCfg.h
+++ b/src/IceCfg.h
@@ -295,7 +295,7 @@
     BVT_FramePointer,
     BVT_UserPointer
   };
-  void sortAndCombineAllocas(CfgVector<Inst *> &Allocas,
+  void sortAndCombineAllocas(CfgVector<InstAlloca *> &Allocas,
                              uint32_t CombinedAlignment, InstList &Insts,
                              AllocaBaseVariableType BaseVariableType);
   void findRematerializable();
diff --git a/src/IceInstrumentation.cpp b/src/IceInstrumentation.cpp
index 64a6212..c911a0b 100644
--- a/src/IceInstrumentation.cpp
+++ b/src/IceInstrumentation.cpp
@@ -51,6 +51,8 @@
   std::string FuncName = Func->getFunctionName().toStringOrEmpty();
   if (FuncName == "_start")
     instrumentStart(Func);
+
+  finishFunc(Func);
 }
 
 void Instrumentation::instrumentInst(LoweringContext &Context) {
diff --git a/src/IceInstrumentation.h b/src/IceInstrumentation.h
index 60afef7..3a18542 100644
--- a/src/IceInstrumentation.h
+++ b/src/IceInstrumentation.h
@@ -44,8 +44,10 @@
   virtual void instrumentGlobals(VariableDeclarationList &) {}
   void instrumentFunc(Cfg *Func);
 
+protected:
+  virtual void instrumentInst(LoweringContext &Context);
+
 private:
-  void instrumentInst(LoweringContext &Context);
   virtual void instrumentFuncStart(LoweringContext &) {}
   virtual void instrumentAlloca(LoweringContext &, class InstAlloca *) {}
   virtual void instrumentArithmetic(LoweringContext &, class InstArithmetic *) {
@@ -71,6 +73,7 @@
                                      class InstUnreachable *) {}
   virtual void instrumentStart(Cfg *) {}
   virtual void instrumentLocalVars(Cfg *) {}
+  virtual void finishFunc(Cfg *) {}
 
 protected:
   GlobalContext *Ctx;