[WebAssembly] Place 'try' and 'catch' correctly wrt EH_LABELs

Summary:
After instruction selection phase, possibly-throwing calls, which were
previously invoke, are wrapped in `EH_LABEL` instructions. For example:
```
  EH_LABEL <mcsymbol .Ltmp0>
  CALL_VOID @foo ...
  EH_LABEL <mcsymbol .Ltmp1>
```

`EH_LABEL` is placed also in the beginning of EH pads:
```
bb.1 (landing-pad):
  EH_LABEL <mcsymbol .Ltmp2>
  ...
```

And we'd like to maintian this relationship, so when we place a `try`,
```
  TRY ...
  EH_LABEL <mcsymbol .Ltmp0>
  CALL_VOID @foo ...
  EH_LABEL <mcsymbol .Ltmp1>
```

When we place a `catch`,
```
bb.1 (landing-pad):
  EH_LABEL <mcsymbol .Ltmp2>
  %0:except_ref = CATCH ...
  ...
```

Previously we didn't treat EH_LABELs specially, so `try` was placed
right before a call, and `catch` was placed in the beginning of an EH
pad.

Reviewers: dschuff

Subscribers: sbc100, jgravelle-google, sunfish, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D58914

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@355996 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp b/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp
index 6002885..65cc088 100644
--- a/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp
@@ -543,6 +543,11 @@
       for (const auto &MI : reverse(*Header)) {
         if (MI.isCall()) {
           AfterSet.insert(&MI);
+          // Possibly throwing calls are usually wrapped by EH_LABEL
+          // instructions. We don't want to split them and the call.
+          if (MI.getIterator() != Header->begin() &&
+              std::prev(MI.getIterator())->isEHLabel())
+            AfterSet.insert(&*std::prev(MI.getIterator()));
           break;
         }
       }
diff --git a/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp b/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp
index 6cda1cc..603b717 100644
--- a/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp
@@ -21,7 +21,7 @@
 #include "llvm/MC/MCAsmInfo.h"
 using namespace llvm;
 
-#define DEBUG_TYPE "wasm-exception-prepare"
+#define DEBUG_TYPE "wasm-late-eh-prepare"
 
 namespace {
 class WebAssemblyLateEHPrepare final : public MachineFunctionPass {
@@ -185,9 +185,12 @@
   for (auto &MBB : MF) {
     if (MBB.isEHPad()) {
       Changed = true;
+      auto InsertPos = MBB.begin();
+      if (InsertPos->isEHLabel()) // EH pad starts with an EH label
+        ++InsertPos;
       unsigned DstReg =
           MRI.createVirtualRegister(&WebAssembly::EXCEPT_REFRegClass);
-      BuildMI(MBB, MBB.begin(), MBB.begin()->getDebugLoc(),
+      BuildMI(MBB, InsertPos, MBB.begin()->getDebugLoc(),
               TII.get(WebAssembly::CATCH), DstReg);
     }
   }
@@ -255,7 +258,11 @@
   for (auto *Extract : ExtractInstrs) {
     MachineBasicBlock *EHPad = getMatchingEHPad(Extract);
     assert(EHPad && "No matching EH pad for extract_exception");
-    MachineInstr *Catch = &*EHPad->begin();
+    auto CatchPos = EHPad->begin();
+    if (CatchPos->isEHLabel()) // EH pad starts with an EH label
+      ++CatchPos;
+    MachineInstr *Catch = &*CatchPos;
+
     if (Catch->getNextNode() != Extract)
       EHPad->insert(Catch->getNextNode(), Extract->removeFromParent());
 
@@ -359,8 +366,10 @@
     // with leaf functions, and we don't restore __stack_pointer in leaf
     // functions anyway.
     auto InsertPos = MBB.begin();
-    if (MBB.begin()->getOpcode() == WebAssembly::CATCH)
-      InsertPos++;
+    if (InsertPos->isEHLabel()) // EH pad starts with an EH label
+      ++InsertPos;
+    if (InsertPos->getOpcode() == WebAssembly::CATCH)
+      ++InsertPos;
     FrameLowering->writeSPToGlobal(WebAssembly::SP32, MF, MBB, InsertPos,
                                    MBB.begin()->getDebugLoc());
   }
diff --git a/test/CodeGen/WebAssembly/eh-labels.mir b/test/CodeGen/WebAssembly/eh-labels.mir
new file mode 100644
index 0000000..0152764
--- /dev/null
+++ b/test/CodeGen/WebAssembly/eh-labels.mir
@@ -0,0 +1,46 @@
+# RUN: llc -mtriple=wasm32-unknown-unknown -exception-model=wasm -mattr=+exception-handling -run-pass wasm-late-eh-prepare -run-pass wasm-cfg-stackify %s -o - | FileCheck %s
+
+# This tests 'try' and 'catch' instructions are correctly placed with respect to
+# EH_LABEL instructions.
+--- |
+  target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
+  target triple = "wasm32-unknown-unknown"
+
+  declare i32 @__gxx_wasm_personality_v0(...)
+  declare void @foo()
+  define void @eh_label_test() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
+    ret void
+  }
+...
+
+---
+# CHECK-LABEL: eh_label_test
+name: eh_label_test
+liveins:
+  - { reg: '$arguments' }
+body: |
+  bb.0:
+    ; TRY should be before EH_LABEL wrappers of throwing calls
+    ; CHECK:      TRY
+    ; CHECK-NEXT: EH_LABEL
+    ; CHECK-NEXT: CALL_VOID @foo
+    ; CHECK-NEXT: EH_LABEL
+    successors: %bb.1, %bb.2
+    EH_LABEL <mcsymbol .Ltmp0>
+    CALL_VOID @foo, implicit-def dead $arguments, implicit $sp32, implicit $sp64
+    EH_LABEL <mcsymbol .Ltmp1>
+
+  bb.1 (landing-pad):
+  ; predecessors: %bb.0
+    successors: %bb.2
+    ; CATCH should be after an EH_LABEL at the beginning of an EH pad
+    ; CHECK:      EH_LABEL
+    ; CHECK-NEXT: CATCH
+    EH_LABEL <mcsymbol .Ltmp2>
+    dead %0:i32 = EXTRACT_EXCEPTION_I32 implicit-def dead $arguments
+    CATCHRET %bb.2, %bb.0, implicit-def dead $arguments
+
+  bb.2:
+  ; predecessors: %bb.0, %bb.1
+    RETURN_VOID implicit-def dead $arguments
+...