halyavin created this revision.
Herald added a reviewer: EricWF.

This change removes dependency on Microsoft STL implementation but adds 
dependency on VC runtime implementation. We will need to evolve and add 
#ifdef's to this code as new versions of Visual Studio runtime will come out.

Tested on x86 and x64, llvm clang 5.0, Visual Studio 2015 runtime.


https://reviews.llvm.org/D40071

Files:
  include/exception
  src/support/runtime/exception_pointer_msvc.ipp

Index: src/support/runtime/exception_pointer_msvc.ipp
===================================================================
--- src/support/runtime/exception_pointer_msvc.ipp
+++ src/support/runtime/exception_pointer_msvc.ipp
@@ -8,84 +8,237 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include <stdio.h>
-#include <stdlib.h>
+#include <atomic>
+#include <cstdint>
+#include <cstring>
+#include <malloc.h>
+#include <windows.h> // For RtlPcToFileHeader function
 
-#if !defined(_CRTIMP2_PURE)
-#define _CRTIMP2_PURE __declspec(dllimport)
+struct EHCatchableType {
+  uint32_t properties;
+  int32_t type_info;
+  uint32_t non_virtual_adjustment;
+  uint32_t offset_to_virtual_base_ptr;
+  uint32_t virtual_base_table_index;
+  uint32_t size;
+  int32_t copy_function;
+};
+
+struct EHCatchableTypeArray {
+  uint32_t catchable_types;
+  // It is variable size but we only need the first element of this array
+  int32_t array_of_catchable_types[1];
+};
+
+struct EHThrowInfo {
+  uint32_t attributes;
+  int32_t unwind;
+  int32_t forward_compat;
+  int32_t catchable_type_array;
+};
+
+struct EHParameters {
+  uint32_t magic_number;
+  void* exception_object;
+  EHThrowInfo* throw_info;
+#ifdef _M_AMD64
+  uintptr_t throw_image_base;
 #endif
+};
 
-#if !defined(__CLRCALL_PURE_OR_CDECL)
-#define __CLRCALL_PURE_OR_CDECL __cdecl
+struct EHExceptionRecord {
+  uint32_t exception_code;
+  uint32_t exception_flags;
+  void* exception_record;
+  void* exception_address;
+  uint32_t number_of_parameters;
+  EHParameters parameters;
+};
+
+// defined in vcruntime<ver>.dll
+extern "C" EHExceptionRecord** __current_exception();
+
+// This is internal compiler definition for MSVC but not for clang.
+// We use our own EHThrowInfo because _ThrowInfo doesn't match actual
+// compiler-generated structures in 64-bit mode.
+#ifdef __clang__
+struct _ThrowInfo;
+// defined in vcruntime<ver>.dll
+_LIBCPP_NORETURN extern "C" void __stdcall _CxxThrowException(
+    void* __exc, _ThrowInfo* __throw_info);
 #endif
 
-_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL __ExceptionPtrCreate(void*);
-_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL __ExceptionPtrDestroy(void*);
-_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL __ExceptionPtrCopy(void*,
-                                                              const void*);
-_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL
-__ExceptionPtrAssign(void*, const void*);
-_CRTIMP2_PURE bool __CLRCALL_PURE_OR_CDECL
-__ExceptionPtrCompare(const void*, const void*);
-_CRTIMP2_PURE bool __CLRCALL_PURE_OR_CDECL
-__ExceptionPtrToBool(const void*);
-_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL __ExceptionPtrSwap(void*, void*);
-_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL
-__ExceptionPtrCurrentException(void*);
-[[noreturn]] _CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL
-__ExceptionPtrRethrow(const void*);
-_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL
-__ExceptionPtrCopyException(void*, const void*, const void*);
+namespace {
+struct ExceptionPtr {
+  void* exception_object;
+  const EHThrowInfo* throw_info;
+  std::atomic<size_t> counter;
+#ifdef _M_AMD64
+  PVOID image_base;
+#endif
+  template <class T>
+  T convert(int32_t offset) {
+#ifdef _M_AMD64
+    uintptr_t value = reinterpret_cast<uintptr_t>(image_base) +
+                      static_cast<uintptr_t>(offset);
+#else
+    uintptr_t value = static_cast<uintptr_t>(offset);
+#endif
+    T res;
+    static_assert(
+        sizeof(value) == sizeof(res),
+        "Can only convert to pointers or pointers to member functions");
+    memcpy(&res, &value, sizeof(value));
+    return res;
+  }
 
-namespace std {
+  void copy(void* dst, const void* src, const EHCatchableType* exc_type) {
+    struct Temp {};
+    constexpr uint32_t virtual_base = 4;
+    if (exc_type->copy_function == 0) {
+      memcpy(dst, src, exc_type->size);
+    } else if (exc_type->properties & virtual_base) {
+      auto copy_constructor =
+          convert<void (Temp::*)(const void*, int)>(exc_type->copy_function);
+      ((Temp*)dst->*copy_constructor)(src, 1);
+    } else {
+      auto copy_constructor =
+          convert<void (Temp::*)(const void*)>(exc_type->copy_function);
+      ((Temp*)dst->*copy_constructor)(src);
+    }
+  }
 
-exception_ptr::exception_ptr() _NOEXCEPT { __ExceptionPtrCreate(this); }
-exception_ptr::exception_ptr(nullptr_t) _NOEXCEPT { __ExceptionPtrCreate(this); }
+  EHCatchableType* exception_type() {
+    return convert<EHCatchableType*>(
+        convert<EHCatchableTypeArray*>(throw_info->catchable_type_array)
+            ->array_of_catchable_types[0]);
+  }
 
-exception_ptr::exception_ptr(const exception_ptr& __other) _NOEXCEPT {
-  __ExceptionPtrCopy(this, &__other);
-}
-exception_ptr& exception_ptr::operator=(const exception_ptr& __other) _NOEXCEPT {
-  __ExceptionPtrAssign(this, &__other);
-  return *this;
-}
+  ExceptionPtr(const void* exception_object_, const EHThrowInfo* throw_info_)
+      : exception_object(nullptr), throw_info(throw_info_), counter(1) {
+#ifdef _M_AMD64
+    RtlPcToFileHeader(
+        reinterpret_cast<PVOID>(const_cast<EHThrowInfo*>(throw_info)),
+        &image_base);
+#endif
+    EHCatchableType* exc_type = exception_type();
+    this->exception_object = malloc(exc_type->size);
+    if (this->exception_object == nullptr) {
+      throw std::bad_alloc();
+    }
+    copy(exception_object, exception_object_, exc_type);
+  }
 
-exception_ptr& exception_ptr::operator=(nullptr_t) _NOEXCEPT {
-  exception_ptr dummy;
-  __ExceptionPtrAssign(this, &dummy);
-  return *this;
-}
+  ~ExceptionPtr() {
+    if (throw_info->unwind && exception_object) {
+      struct Temp {};
+      auto destructor = convert<void (Temp::*)()>(throw_info->unwind);
+      ((Temp*)exception_object->*destructor)();
+    }
+    free(exception_object);
+  }
 
-exception_ptr::~exception_ptr() _NOEXCEPT { __ExceptionPtrDestroy(this); }
+  static ExceptionPtr bad_alloc;
+  static ExceptionPtr bad_exception;
+};
 
-exception_ptr::operator bool() const _NOEXCEPT {
-  return __ExceptionPtrToBool(this);
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Waddress-of-temporary"
+#endif
+
+ExceptionPtr ExceptionPtr::bad_alloc(
+    &std::bad_alloc(),
+    reinterpret_cast<const EHThrowInfo*>(__GetExceptionInfo(std::bad_alloc())));
+
+ExceptionPtr
+ExceptionPtr::bad_exception(&std::bad_exception(),
+                            reinterpret_cast<const EHThrowInfo*>(
+                                __GetExceptionInfo(std::bad_exception())));
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+} // namespace
+
+namespace std {
+
+exception_ptr::exception_ptr(const exception_ptr& __other) _NOEXCEPT
+    : __ptr_(__other.__ptr_) {
+  if (__ptr_) {
+    reinterpret_cast<ExceptionPtr*>(__ptr_)->counter.fetch_add(1);
+  }
 }
 
-bool operator==(const exception_ptr& __x, const exception_ptr& __y) _NOEXCEPT {
-  return __ExceptionPtrCompare(&__x, &__y);
+exception_ptr& exception_ptr::
+operator=(const exception_ptr& __other) _NOEXCEPT {
+  auto before = __ptr_;
+  __ptr_ = __other.__ptr_;
+  if (__ptr_) {
+    reinterpret_cast<ExceptionPtr*>(__ptr_)->counter.fetch_add(1);
+  }
+  if (before) {
+    if (reinterpret_cast<ExceptionPtr*>(before)->counter.fetch_sub(1) == 1) {
+      delete reinterpret_cast<ExceptionPtr*>(before);
+    }
+  }
+  return *this;
 }
 
-
-void swap(exception_ptr& lhs, exception_ptr& rhs) _NOEXCEPT {
-  __ExceptionPtrSwap(&rhs, &lhs);
+exception_ptr::~exception_ptr() _NOEXCEPT {
+  if (__ptr_) {
+    if (reinterpret_cast<ExceptionPtr*>(__ptr_)->counter.fetch_sub(1) == 1) {
+      delete reinterpret_cast<ExceptionPtr*>(__ptr_);
+    }
+  }
 }
 
-exception_ptr __copy_exception_ptr(void* __except, const void* __ptr) {
-  exception_ptr __ret = nullptr;
-  if (__ptr)
-    __ExceptionPtrCopyException(&__ret, __except, __ptr);
-  return __ret;
+exception_ptr __copy_exception_ptr(void* exception_object,
+                                   const void* throw_info) {
+  ExceptionPtr* ptr;
+  try {
+    ptr = new ExceptionPtr(exception_object,
+                           reinterpret_cast<const EHThrowInfo*>(throw_info));
+  } catch (const std::bad_alloc&) {
+    ptr = &ExceptionPtr::bad_alloc;
+    ptr->counter.fetch_add(1);
+  } catch (...) {
+    ptr = &ExceptionPtr::bad_exception;
+    ptr->counter.fetch_add(1);
+  }
+  exception_ptr res;
+  memcpy(&res, &ptr, sizeof(ptr));
+  return res;
 }
 
 exception_ptr current_exception() _NOEXCEPT {
-  exception_ptr __ret;
-  __ExceptionPtrCurrentException(&__ret);
-  return __ret;
+  EHExceptionRecord** record = __current_exception();
+  if (*record && !std::uncaught_exception()) {
+    return __copy_exception_ptr((*record)->parameters.exception_object,
+                                (*record)->parameters.throw_info);
+  }
+  return exception_ptr();
 }
 
 _LIBCPP_NORETURN
-void rethrow_exception(exception_ptr p) { __ExceptionPtrRethrow(&p); }
+void rethrow_exception(exception_ptr p) {
+  if (!p) {
+    throw std::bad_exception();
+  }
+  ExceptionPtr* exc_ptr = reinterpret_cast<ExceptionPtr*>(p.__ptr_);
+  EHCatchableType* exc_type = exc_ptr->exception_type();
+  // _CxxThrowException doesn't call free on exception object so we must
+  // allocate it on the stack.
+  void* dst = _alloca(exc_type->size);
+  exc_ptr->copy(dst, exc_ptr->exception_object, exc_type);
+  auto throw_info = reinterpret_cast<_ThrowInfo*>(
+      const_cast<EHThrowInfo*>(exc_ptr->throw_info));
+  // For some reason clang doesn't call p destructor during unwinding.
+  // So we must clear it ourselves.
+  p = nullptr;
+  _CxxThrowException(dst, throw_info);
+}
 
 nested_exception::nested_exception() _NOEXCEPT : __ptr_(current_exception()) {}
 
Index: include/exception
===================================================================
--- include/exception
+++ include/exception
@@ -134,8 +134,6 @@
 _LIBCPP_FUNC_VIS exception_ptr current_exception() _NOEXCEPT;
 _LIBCPP_NORETURN _LIBCPP_FUNC_VIS void rethrow_exception(exception_ptr);
 
-#ifndef _LIBCPP_ABI_MICROSOFT
-
 class _LIBCPP_TYPE_VIS exception_ptr
 {
     void* __ptr_;
@@ -158,10 +156,19 @@
     bool operator!=(const exception_ptr& __x, const exception_ptr& __y) _NOEXCEPT
         {return !(__x == __y);}
 
+    friend _LIBCPP_FUNC_VIS void swap(exception_ptr& __x, exception_ptr& __y) _NOEXCEPT
+    {
+        void* __tmp = __x.__ptr_;
+        __x.__ptr_ = __y.__ptr_;
+        __y.__ptr_ = __tmp;
+    }
+
     friend _LIBCPP_FUNC_VIS exception_ptr current_exception() _NOEXCEPT;
     friend _LIBCPP_FUNC_VIS void rethrow_exception(exception_ptr);
 };
 
+#ifndef _LIBCPP_ABI_MICROSOFT
+
 template<class _Ep>
 exception_ptr
 make_exception_ptr(_Ep __e) _NOEXCEPT
@@ -183,39 +190,7 @@
 
 #else // _LIBCPP_ABI_MICROSOFT
 
-class _LIBCPP_TYPE_VIS exception_ptr
-{
-#if defined(__clang__)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunused-private-field"
-#endif
-    void* __ptr1_;
-    void* __ptr2_;
-#if defined(__clang__)
-#pragma clang diagnostic pop
-#endif
-public:
-    exception_ptr() _NOEXCEPT;
-    exception_ptr(nullptr_t) _NOEXCEPT;
-    exception_ptr(const exception_ptr& __other) _NOEXCEPT;
-    exception_ptr& operator=(const exception_ptr& __other) _NOEXCEPT;
-    exception_ptr& operator=(nullptr_t) _NOEXCEPT;
-    ~exception_ptr() _NOEXCEPT;
-    _LIBCPP_EXPLICIT operator bool() const _NOEXCEPT;
-};
-
-_LIBCPP_FUNC_VIS
-bool operator==(const exception_ptr& __x, const exception_ptr& __y) _NOEXCEPT;
-
-inline _LIBCPP_INLINE_VISIBILITY
-bool operator!=(const exception_ptr& __x, const exception_ptr& __y) _NOEXCEPT
-    {return !(__x == __y);}
-
-_LIBCPP_FUNC_VIS void swap(exception_ptr&, exception_ptr&) _NOEXCEPT;
-
 _LIBCPP_FUNC_VIS exception_ptr __copy_exception_ptr(void *__except, const void* __ptr);
-_LIBCPP_FUNC_VIS exception_ptr current_exception() _NOEXCEPT;
-_LIBCPP_NORETURN _LIBCPP_FUNC_VIS void rethrow_exception(exception_ptr p);
 
 // This is a built-in template function which automagically extracts the required
 // information.
@@ -229,6 +204,7 @@
 }
 
 #endif // _LIBCPP_ABI_MICROSOFT
+
 // nested_exception
 
 class _LIBCPP_EXCEPTION_ABI nested_exception
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to