[libunwind] 058222b - [libunwind][WebAssembly] Support Wasm EH

Heejin Ahn via cfe-commits cfe-commits at lists.llvm.org
Fri Sep 22 00:39:13 PDT 2023


Author: Heejin Ahn
Date: 2023-09-22T00:35:37-07:00
New Revision: 058222b2316615194c089f2bc68d11341f39d26e

URL: https://github.com/llvm/llvm-project/commit/058222b2316615194c089f2bc68d11341f39d26e
DIFF: https://github.com/llvm/llvm-project/commit/058222b2316615194c089f2bc68d11341f39d26e.diff

LOG: [libunwind][WebAssembly] Support Wasm EH

This adds Wasm-specific libunwind port to support Wasm exception
handling (https://github.com/WebAssembly/exception-handling).

Wasm EH requires `__USING_WASM_EXCEPTIONS__` to be defined. This adds
`Unwind-wasm.c`, which defines libunwind APIs for Wasm. This also adds a
`thread_local` struct of type `_Unwind_LandingPadContext`, which serves
as a medium for input/output data between the user code and the
personality function. How all these work is explained in
https://github.com/WebAssembly/tool-conventions/blob/main/EHScheme.md.
(The doc is old and "You Shouldn't Prune Unreachable Resumes" section
doesn't apply anymore, but otherwise it should be good)

The bulk of these changes was added back in Mar 2020 in
https://github.com/emscripten-core/emscripten/pull/10577 to emscripten
repo and has been used ever since. Now we'd like to upstream this so
that other toolchains that don't use emscripten libraries, e.g., WASI,
can use this too.

Companion patch: D158918

Reviewed By: dschuff, #libunwind, phosek

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

Added: 
    libunwind/src/Unwind-wasm.c

Modified: 
    libunwind/src/config.h

Removed: 
    


################################################################################
diff  --git a/libunwind/src/Unwind-wasm.c b/libunwind/src/Unwind-wasm.c
new file mode 100644
index 000000000000000..35f2f9aeab08048
--- /dev/null
+++ b/libunwind/src/Unwind-wasm.c
@@ -0,0 +1,121 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//
+//  Implements Wasm exception handling proposal
+//  (https://github.com/WebAssembly/exception-handling) based C++ exceptions
+//
+//===----------------------------------------------------------------------===//
+
+#include "config.h"
+#include "unwind.h"
+#include <stdbool.h>
+#include <threads.h>
+
+#ifdef __USING_WASM_EXCEPTIONS__
+
+_Unwind_Reason_Code __gxx_personality_wasm0(int version, _Unwind_Action actions,
+                                            uint64_t exceptionClass,
+                                            _Unwind_Exception *unwind_exception,
+                                            _Unwind_Context *context);
+
+struct _Unwind_LandingPadContext {
+  // Input information to personality function
+  uintptr_t lpad_index; // landing pad index
+  uintptr_t lsda;       // LSDA address
+
+  // Output information computed by personality function
+  uintptr_t selector; // selector value
+};
+
+// Communication channel between compiler-generated user code and personality
+// function
+thread_local struct _Unwind_LandingPadContext __wasm_lpad_context;
+
+/// Calls to this function is in landing pads in compiler-generated user code.
+/// In other EH schemes, stack unwinding is done by libunwind library, which
+/// calls the personality function for each each frame it lands. On the other
+/// hand, WebAssembly stack unwinding process is performed by a VM, and the
+/// personality function cannot be called from there. So the compiler inserts
+/// a call to this function in landing pads in the user code, which in turn
+/// calls the personality function.
+_Unwind_Reason_Code _Unwind_CallPersonality(void *exception_ptr) {
+  struct _Unwind_Exception *exception_object =
+      (struct _Unwind_Exception *)exception_ptr;
+  _LIBUNWIND_TRACE_API("_Unwind_CallPersonality(exception_object=%p)",
+                       (void *)exception_object);
+
+  // Reset the selector.
+  __wasm_lpad_context.selector = 0;
+
+  // Call personality function. Wasm does not have two-phase unwinding, so we
+  // only do the cleanup phase.
+  return __gxx_personality_wasm0(
+      1, _UA_SEARCH_PHASE, exception_object->exception_class, exception_object,
+      (struct _Unwind_Context *)&__wasm_lpad_context);
+}
+
+/// Called by __cxa_throw.
+_LIBUNWIND_EXPORT _Unwind_Reason_Code
+_Unwind_RaiseException(_Unwind_Exception *exception_object) {
+  _LIBUNWIND_TRACE_API("_Unwind_RaiseException(exception_object=%p)",
+                       (void *)exception_object);
+  // Use Wasm EH's 'throw' instruction.
+  __builtin_wasm_throw(0, exception_object);
+}
+
+/// Called by __cxa_end_catch.
+_LIBUNWIND_EXPORT void
+_Unwind_DeleteException(_Unwind_Exception *exception_object) {
+  _LIBUNWIND_TRACE_API("_Unwind_DeleteException(ex_obj=%p)",
+                       (void *)(exception_object));
+  if (exception_object->exception_cleanup != NULL)
+    (*exception_object->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT,
+                                           exception_object);
+}
+
+/// Called by personality handler to alter register values.
+_LIBUNWIND_EXPORT void _Unwind_SetGR(struct _Unwind_Context *context, int index,
+                                     uintptr_t value) {
+  _LIBUNWIND_TRACE_API("_Unwind_SetGR(context=%p, index=%d, value=%lu)",
+                       (void *)context, index, value);
+  // We only use this function to set __wasm_lpad_context.selector field, which
+  // is index 1 in the personality function.
+  if (index == 1)
+    ((struct _Unwind_LandingPadContext *)context)->selector = value;
+}
+
+/// Called by personality handler to get instruction pointer.
+_LIBUNWIND_EXPORT uintptr_t _Unwind_GetIP(struct _Unwind_Context *context) {
+  // The result will be used as an 1-based index after decrementing 1, so we
+  // increment 2 here
+  uintptr_t result =
+      ((struct _Unwind_LandingPadContext *)context)->lpad_index + 2;
+  _LIBUNWIND_TRACE_API("_Unwind_GetIP(context=%p) => %lu", (void *)context,
+                       result);
+  return result;
+}
+
+/// Not used in Wasm.
+_LIBUNWIND_EXPORT void _Unwind_SetIP(struct _Unwind_Context *context,
+                                     uintptr_t value) {}
+
+/// Called by personality handler to get LSDA for current frame.
+_LIBUNWIND_EXPORT uintptr_t
+_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) {
+  uintptr_t result = ((struct _Unwind_LandingPadContext *)context)->lsda;
+  _LIBUNWIND_TRACE_API("_Unwind_GetLanguageSpecificData(context=%p) => 0x%lx",
+                       (void *)context, result);
+  return result;
+}
+
+/// Not used in Wasm.
+_LIBUNWIND_EXPORT uintptr_t
+_Unwind_GetRegionStart(struct _Unwind_Context *context) {
+  return 0;
+}
+
+#endif

diff  --git a/libunwind/src/config.h b/libunwind/src/config.h
index a20bf43272cde33..deb5a4d4d73d467 100644
--- a/libunwind/src/config.h
+++ b/libunwind/src/config.h
@@ -89,7 +89,7 @@
   __asm__(".globl " SYMBOL_NAME(aliasname));                                   \
   __asm__(SYMBOL_NAME(aliasname) " = " SYMBOL_NAME(name));                     \
   _LIBUNWIND_ALIAS_VISIBILITY(SYMBOL_NAME(aliasname))
-#elif defined(__ELF__) || defined(_AIX)
+#elif defined(__ELF__) || defined(_AIX) || defined(__wasm__)
 #define _LIBUNWIND_WEAK_ALIAS(name, aliasname)                                 \
   extern "C" _LIBUNWIND_EXPORT __typeof(name) aliasname                        \
       __attribute__((weak, alias(#name)));


        


More information about the cfe-commits mailing list