gollvm: fix exception handling stmt bugs

Revise Llvm_backend::exception_handler_statement(), which was not
correct as implemented. Specifically, if an exception is thrown by
something in the catch clause, the catch block for it needs to store
the exception and make a note that the exception took place... then
after the "finally" block has executed, the cleanup code should
rethrow the exception (if there was one) as opposed to unconditionally
returning.

Change-Id: Ia63297cad1bc601f9ab5066ad879b1bc5739efd1
Reviewed-on: https://go-review.googlesource.com/56410
Reviewed-by: Cherry Zhang <cherryyz@google.com>
diff --git a/llvm-gofrontend/go-llvm-bnode.cpp b/llvm-gofrontend/go-llvm-bnode.cpp
index 7e4d0db..a99cc98 100644
--- a/llvm-gofrontend/go-llvm-bnode.cpp
+++ b/llvm-gofrontend/go-llvm-bnode.cpp
@@ -336,7 +336,7 @@
 
 Bvariable *Bnode::var() const
 {
-  assert(flavor() == N_Var);
+  assert(flavor() == N_Var || flavor() == N_ExcepStmt);
   return u.var;
 }
 
@@ -844,13 +844,16 @@
                                       Bstatement *body,
                                       Bstatement *onexception,
                                       Bstatement *finally,
+                                      Bvariable *finTempVar,
                                       Location loc)
 {
   assert(body);
   assert(onexception);
   assert(finally);
+  assert(finTempVar);
   std::vector<Bnode *> kids = { body, onexception, finally };
   Bstatement *rval = new Bstatement(N_ExcepStmt, func, kids, loc);
+  rval->u.var = finTempVar;
   return archive(rval);
 }
 
@@ -893,8 +896,6 @@
 
 }
 
-
-
 Bblock *BnodeBuilder::mkBlock(Bfunction *func,
                               const std::vector<Bvariable *> &vars,
                               Location loc)
diff --git a/llvm-gofrontend/go-llvm-bnode.h b/llvm-gofrontend/go-llvm-bnode.h
index 6fe4dcd..f3e9615 100644
--- a/llvm-gofrontend/go-llvm-bnode.h
+++ b/llvm-gofrontend/go-llvm-bnode.h
@@ -199,7 +199,7 @@
  private:
   std::vector<Bnode *> kids_;
   union {
-    Bvariable *var;
+    Bvariable *var;  // filled in only for Var and ExcepStmt nodes
     Bfunction *func; // filled in only for fcn constants, calls, conditionals
     SwitchDescriptor *swcases;
     int indices; // for composite expressions, index to BnodeBuilder's indexvecs_
@@ -310,6 +310,7 @@
                           Bstatement *body,
                           Bstatement *onexception,
                           Bstatement *finally,
+                          Bvariable *finTempVar,
                           Location loc);
   Bstatement *mkSwitchStmt(Bfunction *func,
                            Bexpression *swvalue,
diff --git a/llvm-gofrontend/go-llvm.cpp b/llvm-gofrontend/go-llvm.cpp
index 0368fee..a9f2e27 100644
--- a/llvm-gofrontend/go-llvm.cpp
+++ b/llvm-gofrontend/go-llvm.cpp
@@ -1688,9 +1688,14 @@
   assert(func == except_stmt->function());
   assert(!finally_stmt || func == finally_stmt->function());
 
+  std::string tname(namegen("finvar"));
+  Bvariable *tvar = func->localVariable(tname, boolType(),
+                                        false, location);
+  tvar->markAsTemporary();
   Bstatement *excepst = nbuilder_.mkExcepStmt(func, bstat,
                                               except_stmt,
-                                              finally_stmt, location);
+                                              finally_stmt, tvar,
+                                              location);
   return excepst;
 }
 
@@ -2372,6 +2377,11 @@
   std::pair<llvm::Instruction*, llvm::BasicBlock *>
   postProcessInst(llvm::Instruction *inst,
                   llvm::BasicBlock *curblock);
+  llvm::Value *populateCatchPadBlock(llvm::BasicBlock *catchpadbb,
+                                     Bvariable *finvar);
+  llvm::BasicBlock *populateFinallyBlock(llvm::BasicBlock *finBB,
+                                         Bvariable *finvar,
+                                         llvm::Value *extmp);
   DIBuildHelper *dibuildhelper() const { return dibuildhelper_; }
   Llvm_linemap *linemap() { return be_->linemap(); }
 
@@ -2384,7 +2394,7 @@
   std::vector<llvm::BasicBlock*> padBlockStack_;
   std::set<llvm::AllocaInst *> temporariesDiscovered_;
   std::vector<llvm::AllocaInst *> newTemporaries_;
-  llvm::BasicBlock* finallyBlock_;
+  llvm::BasicBlock *finallyBlock_;
   Bstatement *cachedReturn_;
   bool emitOrphanedCode_;
 };
@@ -2731,11 +2741,6 @@
   Bexpression *defcallex = defst->getDeferStmtDeferCall();
   Bexpression *undcallex = defst->getDeferStmtUndeferCall();
 
-  // Finish bb (see the comments for Llvm_backend::function_defer_statement
-  // as to why we use this name).
-  llvm::BasicBlock *finbb =
-      llvm::BasicBlock::Create(context_, be_->namegen("finish"), func);
-
   // Landing pad to which control will be transferred if an exception
   // is thrown when executing "undcallex".
   llvm::BasicBlock *padbb =
@@ -2744,6 +2749,12 @@
   // Catch BB will contain checkdefer (defercall) code.
   llvm::BasicBlock *catchbb =
       llvm::BasicBlock::Create(context_, be_->namegen("catch"), func);
+
+  // Finish bb (see the comments for Llvm_backend::function_defer_statement
+  // as to why we use this name).
+  llvm::BasicBlock *finbb =
+      llvm::BasicBlock::Create(context_, be_->namegen("finish"), func);
+
   if (curblock)
     llvm::BranchInst::Create(finbb, curblock);
   curblock = finbb;
@@ -2778,6 +2789,131 @@
   return contbb;
 }
 
+// Populate the landing pad used to catch exceptions thrown from
+// the catch code from an exceptions handling statement (pad "P2"
+// in the diagram in the header comment for GenBlocks::genExcep).
+
+llvm::Value *GenBlocks::populateCatchPadBlock(llvm::BasicBlock *catchpadbb,
+                                              Bvariable *finvar)
+{
+  LIRBuilder builder(context_, llvm::ConstantFolder());
+  builder.SetInsertPoint(catchpadbb);
+
+  // Create landing pad instruction.
+  llvm::Type *eht = be_->landingPadExceptionType();
+  llvm::LandingPadInst *caughtResult =
+      builder.CreateLandingPad(eht, 0, be_->namegen("ex2"));
+  caughtResult->setCleanup(true);
+
+  // Create temporary into which caught result will be stored
+  std::string tag(be_->namegen("ehtmp"));
+  llvm::AllocaInst *ai = new llvm::AllocaInst(eht, 0, tag);
+  temporariesDiscovered_.insert(ai);
+  newTemporaries_.push_back(ai);
+
+  // Store caught result into temporary.
+  builder.CreateStore(caughtResult, ai);
+
+  // Emit "finally = false" into catch pad BB following pad inst
+  builder.SetInsertPoint(catchpadbb);
+  llvm::Value *fval = be_->boolean_constant_expression(false)->value();
+  builder.CreateStore(fval, finvar->value());
+
+  return ai;
+}
+
+// At end of finally BB, emit:
+//
+//   if (finok)
+//     return
+//   else
+//     resume EXC
+//
+// where EXC is the exception that caused control to flow
+// into the catch pad bb.
+//
+// In addition, this routine creates the shared return statement
+// (if there is a cached return) and fixes up the finally stmt
+// resume block.
+
+llvm::BasicBlock *GenBlocks::populateFinallyBlock(llvm::BasicBlock *finBB,
+                                                  Bvariable *finvar,
+                                                  llvm::Value *extmp)
+{
+  LIRBuilder builder(context_, llvm::ConstantFolder());
+  builder.SetInsertPoint(finBB);
+  llvm::Function *func = function()->function();
+  llvm::BasicBlock *finResBB =
+      llvm::BasicBlock::Create(context_, be_->namegen("finres"), func);
+  llvm::BasicBlock *finRetBB =
+      llvm::BasicBlock::Create(context_, be_->namegen("finret"), func);
+  std::string lname(be_->namegen("fload"));
+  llvm::LoadInst *finvarload = builder.CreateLoad(finvar->value(), lname);
+  llvm::Value *tval = be_->boolean_constant_expression(true)->value();
+  llvm::Value *cmp = builder.CreateICmp(llvm::CmpInst::Predicate::ICMP_EQ,
+                                        finvarload, tval,
+                                        be_->namegen("icmp"));
+  builder.CreateCondBr(cmp, finRetBB, finResBB);
+
+  // Populate return block
+  if (cachedReturn_ != nullptr) {
+    llvm::BasicBlock *curblock = finRetBB;
+    Bexpression *re = cachedReturn_->getReturnStmtExpr();
+    llvm::BasicBlock *bb = walkExpr(curblock, re);
+    assert(curblock == bb);
+    cachedReturn_ = nullptr;
+  }
+
+  // Populate resume block
+  builder.SetInsertPoint(finResBB);
+  std::string ename(be_->namegen("excv"));
+  llvm::LoadInst *exload = builder.CreateLoad(extmp, ename);
+  builder.CreateResume(exload);
+
+  return finRetBB;
+}
+
+// Create the needed control flow for an exception handler statement.
+// At a high level, we want something that looks like:
+//
+//    try { body }
+//    catch { exceptioncode }
+//    finally { finallycode }
+//
+// This means creating the following landing pads + control flow:
+//
+//    { /* begin body */
+//      <any calls are converted to invoke instructions,
+//       targeting landing pad P1>
+//      /* end body */
+//    }
+//    goto finok;
+//    pad P1:
+//      goto catch;
+//    catch: {
+//      /* begin exceptioncode */
+//      <any calls are converted to invoke inst,
+//       targeting landing pad P2>
+//      /* end exceptioncode */
+//    }
+//    goto finok;
+//    pad P2:
+//      store exception -> ehtmp
+//      finvar = false
+//      goto finally;
+//    finok:
+//      finvar = true
+//    finally:
+//    { /* begin finallycode */
+//      <calls here are left as is>
+//      /* end finallycode */
+//    }
+//    if (finvar)
+//      <normal return>
+//    else
+//      rethrow exception ehtmp
+//
+
 llvm::BasicBlock *GenBlocks::genExcep(Bstatement *excepst,
                                       llvm::BasicBlock *curblock)
 {
@@ -2794,26 +2930,52 @@
   assert(ifexception);
   Bstatement *finally = excepst->getExcepStmtFinally(); // may be null
 
-  // Create a landing pad block. This pad will be where control
-  // will arrive if an exception is thrown within the "body" code.
-  llvm::BasicBlock *padbb =
-      llvm::BasicBlock::Create(context_, be_->namegen("pad"), func);
+  // This temp will track what sort of action is needed at the end of
+  // of the "finally" clause.  For this variable, a value of 'true'
+  // signals a normal return, and a value of 'false' indicates that
+  // there was an additional exception thrown during execution of the
+  // "ifexception" code (as opposed to an exception thrown during
+  // execution of "body") that needs to be passed to unwind/resume
+  // after the "finally" code.
+  Bvariable *finvar = excepst->var();
+  assert(finvar);
+
+  // Create a "finok" BB; this block will set the "finally" variable
+  // to true and then jump to the finally block.
+  std::string fbbname(be_->namegen("finok"));
+  llvm::BasicBlock *finokBB =
+      llvm::BasicBlock::Create(context_, fbbname, func);
 
   // Create a "finally" BB, corresponding to where control will wind
   // up once both the body and (possibly) the exception clause are
   // complete. This will also be where we'll place any code in the
   // "finally" statement above.
   std::string cbbname(be_->namegen("finally"));
-  llvm::BasicBlock *contbb =
+  llvm::BasicBlock *contBB =
       llvm::BasicBlock::Create(context_, cbbname, func);
 
-  // Not expecting to see nested exception statements, assert if
-  // this crops up.
+  // Emit an assignment "finally = true" into the finok block,
+  // then a jump to the finally block.
+  {
+    LIRBuilder builder(context_, llvm::ConstantFolder());
+    builder.SetInsertPoint(finokBB);
+    llvm::Value *tval = be_->boolean_constant_expression(true)->value();
+    builder.CreateStore(tval, finvar->value());
+    llvm::BranchInst::Create(contBB, finokBB);
+  }
+
+  // Create a landing pad block. This pad will be where control
+  // will arrive if an exception is thrown within the "body" code.
+  llvm::BasicBlock *padbb =
+      llvm::BasicBlock::Create(context_, be_->namegen("pad"), func);
+
+  // Not expecting to see nested exception statements, assert if this
+  // crops up.
   assert(padBlockStack_.empty());
 
   // If there is a finally block, then record the block for purposes
   // of return handling.
-  finallyBlock_ = (finally ? contbb : nullptr);
+  finallyBlock_ = (finally ? finokBB : nullptr);
   assert(!cachedReturn_);
 
   // Push first pad block onto stack. The presence of a non-empty pad
@@ -2830,16 +2992,7 @@
   // If the body block ended without a return, then insert a jump
   // to the continue / or finally clause.
   if (curblock)
-    llvm::BranchInst::Create(contbb, curblock);
-
-  // Emit landing pad inst in pad block, followed by branch to catch bb.
-  llvm::LandingPadInst *padinst =
-      llvm::LandingPadInst::Create(be_->landingPadExceptionType(),
-                                   0, be_->namegen("ex"), padbb);
-  padinst->addClause(llvm::Constant::getNullValue(be_->llvmPtrType()));
-    llvm::BasicBlock *catchbb =
-      llvm::BasicBlock::Create(context_, be_->namegen("catch"), func);
-  llvm::BranchInst::Create(catchbb, padbb);
+    llvm::BranchInst::Create(finokBB, curblock);
 
   // Create second pad block. Here the idea is that if an exception
   // is thrown as a result of any call within the catch ("ifexception" code)
@@ -2847,33 +3000,46 @@
   llvm::BasicBlock *catchpadbb =
       llvm::BasicBlock::Create(context_, be_->namegen("catchpad"), func);
 
+  // Block containing catch code
+  llvm::BasicBlock *catchbb =
+      llvm::BasicBlock::Create(context_, be_->namegen("catch"), func);
+
+  // Emit landing pad inst in pad block, followed by branch to catch bb.
+  {
+    LIRBuilder builder(context_, llvm::ConstantFolder());
+    builder.SetInsertPoint(padbb);
+    llvm::LandingPadInst *padinst =
+        builder.CreateLandingPad(be_->landingPadExceptionType(),
+                                 0, be_->namegen("ex"));
+    padinst->addClause(llvm::Constant::getNullValue(be_->llvmPtrType()));
+    llvm::BranchInst::Create(catchbb, padbb);
+  }
+
   // Push second pad, walk exception stmt, then pop the pad.
   padBlockStack_.push_back(catchpadbb);
   auto bb = walk(ifexception, catchbb);
   if (bb)
-    llvm::BranchInst::Create(contbb, bb);
+    llvm::BranchInst::Create(finokBB, bb);
   padBlockStack_.pop_back();
 
   // Return handling now complete.
   finallyBlock_ = nullptr;
 
-  // Fix up second pad block, followed by branch to continue block.
-  llvm::LandingPadInst *padinst2 =
-      llvm::LandingPadInst::Create(be_->landingPadExceptionType(),
-                                   0, be_->namegen("ex2"), catchpadbb);
-  padinst2->addClause(llvm::Constant::getNullValue(be_->llvmPtrType()));
-  llvm::BranchInst::Create(contbb, catchpadbb);
+  // Create landing pad instruction in the second pad block. For this
+  // landing pad, we want to capture the exception object into a
+  // temporary, and set "finally = false" to indicate that an exception
+  // was thrown.
+  llvm::Value *extmp = populateCatchPadBlock(catchpadbb, finvar);
+  llvm::BranchInst::Create(contBB, catchpadbb);
 
   // Handle finally statement where applicable.
-  curblock = contbb;
+  curblock = contBB;
   if (finally != nullptr) {
     curblock = walk(finally, curblock);
-    if (cachedReturn_ != nullptr) {
-      Bexpression *re = cachedReturn_->getReturnStmtExpr();
-      llvm::BasicBlock *bb = walkExpr(curblock, re);
-      assert(curblock == bb);
-      cachedReturn_ = nullptr;
-    }
+
+    // Augment the end of the finally block with an if/jump to
+    // return or resume, and populate the return/resume blocks.
+    curblock = populateFinallyBlock(curblock, finvar, extmp);
   }
 
   return curblock;
diff --git a/unittests/BackendCore/BackendStmtTests.cpp b/unittests/BackendCore/BackendStmtTests.cpp
index 01b6204..69b31db 100644
--- a/unittests/BackendCore/BackendStmtTests.cpp
+++ b/unittests/BackendCore/BackendStmtTests.cpp
@@ -519,10 +519,6 @@
   store i8 0, i8* %x
   br label %finish.0
 
-finish.0:                                         ; preds = %catch.0, %entry
-  invoke void @deferreturn(i8* nest undef, i8* %x)
-          to label %cont.0 unwind label %pad.0
-
 pad.0:                                            ; preds = %finish.0
   %ex.0 = landingpad { i8*, i32 }
           catch i8* null
@@ -532,6 +528,10 @@
   call void @checkdefer(i8* nest undef, i8* %x)
   br label %finish.0
 
+finish.0:                                         ; preds = %catch.0, %entry
+  invoke void @deferreturn(i8* nest undef, i8* %x)
+          to label %cont.0 unwind label %pad.0
+
 cont.0:                                           ; preds = %finish.0
   ret void
 }
@@ -611,20 +611,28 @@
   const char *exp = R"RAW_RESULT(
 define void @baz(i8* nest %nest.0) #0 personality i32 (i32, i32, i64, i8*, i8*)* @__gccgo_personality_v0 {
 entry:
+  %ehtmp.0 = alloca { i8*, i32 }
   %x = alloca i64
+  %finvar.0 = alloca i8
   store i64 0, i64* %x
   %call.0 = invoke i64 @id(i8* nest undef, i64 99)
           to label %cont.0 unwind label %pad.0
 
+finok.0:                                          ; preds = %cont.2, %cont.1
+  store i8 1, i8* %finvar.0
+  br label %finally.0
+
+finally.0:                                        ; preds = %catchpad.0, %finok.0
+  call void @ohstopit(i8* nest undef)
+  %fload.0 = load i8, i8* %finvar.0
+  %icmp.0 = icmp eq i8 %fload.0, 1
+  br i1 %icmp.0, label %finret.0, label %finres.0
+
 pad.0:                                            ; preds = %cont.0, %entry
   %ex.0 = landingpad { i8*, i32 }
           catch i8* null
   br label %catch.0
 
-finally.0:                                        ; preds = %catchpad.0, %cont.2, %cont.1
-  call void @ohstopit(i8* nest undef)
-  ret void
-
 cont.0:                                           ; preds = %entry
   store i64 %call.0, i64* %x
   invoke void @plark(i8* nest undef)
@@ -632,19 +640,28 @@
 
 cont.1:                                           ; preds = %cont.0
   store i64 123, i64* %x
+  br label %finok.0
+
+catchpad.0:                                       ; preds = %catch.0
+  %ex2.0 = landingpad { i8*, i32 }
+          cleanup
+  store { i8*, i32 } %ex2.0, { i8*, i32 }* %ehtmp.0
+  store i8 0, i8* %finvar.0
   br label %finally.0
 
 catch.0:                                          ; preds = %pad.0
   invoke void @plix(i8* nest undef)
           to label %cont.2 unwind label %catchpad.0
 
-catchpad.0:                                       ; preds = %catch.0
-  %ex2.0 = landingpad { i8*, i32 }
-          catch i8* null
-  br label %finally.0
-
 cont.2:                                           ; preds = %catch.0
-  br label %finally.0
+  br label %finok.0
+
+finres.0:                                         ; preds = %finally.0
+  %excv.0 = load { i8*, i32 }, { i8*, i32 }* %ehtmp.0
+  resume { i8*, i32 } %excv.0
+
+finret.0:                                         ; preds = %finally.0
+  ret void
 }
    )RAW_RESULT";
 
@@ -763,25 +780,31 @@
   const char *exp = R"RAW_RESULT(
 define i64 @baz(i8* nest %nest.0, i64 %p0) #0 personality i32 (i32, i32, i64, i8*, i8*)* @__gccgo_personality_v0 {
 entry:
+  %ehtmp.0 = alloca { i8*, i32 }
   %p0.addr = alloca i64
   %ret = alloca i64
+  %finvar.0 = alloca i8
   store i64 %p0, i64* %p0.addr
   store i64 0, i64* %ret
   %call.0 = invoke i64 @splat(i8* nest undef, i64 99)
           to label %cont.0 unwind label %pad.0
 
-pad.0:                                            ; preds = %entry
-  %ex.0 = landingpad { i8*, i32 }
-          catch i8* null
-  br label %catch.0
+finok.0:                                          ; preds = %cont.1, %else.0, %then.0
+  store i8 1, i8* %finvar.0
+  br label %finally.0
 
-finally.0:                                        ; preds = %catchpad.0, %cont.1, %else.0, %then.0
+finally.0:                                        ; preds = %catchpad.0, %finok.0
   %call.2 = call i64 @splat(i8* nest undef, i64 987)
   %icmp.1 = icmp eq i64 %call.2, 2
   %zext.1 = zext i1 %icmp.1 to i8
   %trunc.1 = trunc i8 %zext.1 to i1
   br i1 %trunc.1, label %then.1, label %else.1
 
+pad.0:                                            ; preds = %entry
+  %ex.0 = landingpad { i8*, i32 }
+          catch i8* null
+  br label %catch.0
+
 cont.0:                                           ; preds = %entry
   %icmp.0 = icmp eq i64 %call.0, 88
   %zext.0 = zext i1 %icmp.0 to i8
@@ -790,25 +813,27 @@
 
 then.0:                                           ; preds = %cont.0
   store i64 22, i64* %ret
-  br label %finally.0
+  br label %finok.0
 
 else.0:                                           ; preds = %cont.0
   %p0.ld.0 = load i64, i64* %p0.addr
   store i64 %p0.ld.0, i64* %ret
+  br label %finok.0
+
+catchpad.0:                                       ; preds = %catch.0
+  %ex2.0 = landingpad { i8*, i32 }
+          cleanup
+  store { i8*, i32 } %ex2.0, { i8*, i32 }* %ehtmp.0
+  store i8 0, i8* %finvar.0
   br label %finally.0
 
 catch.0:                                          ; preds = %pad.0
   %call.1 = invoke i64 @splat(i8* nest undef, i64 13)
           to label %cont.1 unwind label %catchpad.0
 
-catchpad.0:                                       ; preds = %catch.0
-  %ex2.0 = landingpad { i8*, i32 }
-          catch i8* null
-  br label %finally.0
-
 cont.1:                                           ; preds = %catch.0
   store i64 %call.1, i64* %ret
-  br label %finally.0
+  br label %finok.0
 
 then.1:                                           ; preds = %finally.0
   store i64 9, i64* %ret
@@ -816,11 +841,20 @@
   ret i64 %ret.ld.3
 
 fallthrough.1:                                    ; preds = %else.1
-  %ret.ld.1 = load i64, i64* %ret
-  ret i64 %ret.ld.1
+  %fload.0 = load i8, i8* %finvar.0
+  %icmp.2 = icmp eq i8 %fload.0, 1
+  br i1 %icmp.2, label %finret.0, label %finres.0
 
 else.1:                                           ; preds = %finally.0
   br label %fallthrough.1
+
+finres.0:                                         ; preds = %fallthrough.1
+  %excv.0 = load { i8*, i32 }, { i8*, i32 }* %ehtmp.0
+  resume { i8*, i32 } %excv.0
+
+finret.0:                                         ; preds = %fallthrough.1
+  %ret.ld.1 = load i64, i64* %ret
+  ret i64 %ret.ld.1
 }
    )RAW_RESULT";